Repository navigation
feat(cloud-connection): count an installed app's remaining sample rows the purge's way - #30613
Conversation
Claude-Session: https://claude.ai/code/session_01Utp6CYE3Av1KFxjNwT6qzN Co-authored-by: Claude <[email protected]>
…s the purge's way GET /api/v1/marketplace/install-local/:manifestId/sample-data answers how many rows a purge would delete now (countSeedRows, the purge's matcher with nothing deleted), behind the purge's gate, with a closed empty query set. Claude-Session: https://claude.ai/code/session_01Utp6CYE3Av1KFxjNwT6qzN Co-authored-by: Claude <[email protected]>
…tes that drive the count door Claude-Session: https://claude.ai/code/session_01Utp6CYE3Av1KFxjNwT6qzN Co-authored-by: Claude <[email protected]>
Claude-Session: https://claude.ai/code/session_01Utp6CYE3Av1KFxjNwT6qzN Co-authored-by: Claude <[email protected]>
📓 Docs Drift CheckThis PR changes 1 package(s): 28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f45a2499b2d7f4057068c0f595b1223a65d2476f && git checkout f45a2499b2d7f4057068c0f595b1223a65d2476f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 55d9dd448f00d1498ba3ae830368306aa0dd8d8a 00d0c9d55607411d933cfc506d6f7e86c101839d && git checkout -B drift-repro 55d9dd448f00d1498ba3ae830368306aa0dd8d8a && git merge --no-ff 00d0c9d55607411d933cfc506d6f7e86c101839d
node scripts/docs-audit/affected-docs.mjs --json 55d9dd448f00d1498ba3ae830368306aa0dd8d8a
|
|
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL 维护者速读
席位核对 — ACCEPT · domain:server#2 · head
|
Requested by Bill · project thread
domain:server#2 · os-bill · session_01Utp6CYE3Av1KFxjNwT6qzN
维护者速读
GET /api/v1/marketplace/install-local/:manifestId/sample-data,回答「这个已装应用的示例数据现在还剩多少条」,即此刻点「清除示例数据」会删掉的条数。清除本身一行没动。manage_metadata权限,所以数量和清除结果一定一致。任何查询参数都会被 400 拒绝,GET 请求无论怎么写都删不了数据。回滚即还原本 PR。marketplace-install-local-plugin.ts的handleSampleDataCount;marketplace-install-local-purge.ts的countSeedRows;测试文件末尾新增的GET …/sample-datadescribe。pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2 src/marketplace-install-local-purge.test.ts,预期 33 个全绿,其中GET …/sample-data下 7 个。Fixes #30323
Clause-②: yes (widening)
Metadata-protocol: none
What changes
After a reload, no API said how much sample data an installed app still had. The install answer's
data.seededis gone, the purge has no dry run, and the listing'swithSampleDatastops at the first seed row it finds and answers yes or no. So the console's purge confirm could only give a count for an install made in the same browser session (PR #30537, the ui half of this card).GET /api/v1/marketplace/install-local/:manifestId/sample-data(new, read-only) answers{ success: true, data: { manifestId, count, unidentified } }.countis the number of rowsPOST …/purge-sample-datawould delete now:countSeedRows, which runs the purge's ownmatchSeedRowspass in full (notfirstOnly) and deletes nothing.unidentifiedis the seed records the purge would report inerrorsand leave in place (no usable key, a key two rows carry, an object whose rows could not be read). Each is logged atwarnwith its reason.manage_metadatathrough the sharedrequireInstallCapability(401 / held-session 403 / 403FORBIDDEN), then404for no install, the unreadable-ledger answer, the protocol handshake's422 OS_PROTOCOL_INCOMPATIBLE, then the purge's scope (organizationWallActive+resolveActiveOrgId).200, count: 0and no row is read (ADR-0123 D2 for a tenant-scoped read; the listing answerswithSampleData: falseto the same session), where the purge refuses403. A package with no seed datasets getscount: 0, where the purge answers400 NOTHING_TO_PURGE. No engine or metadata service gets the purge's500 DRIVER_UNAVAILABLE, never a0.GETwith an empty closed query set (SAMPLE_DATA_COUNT_QUERY_PARAMS, AGENTS.md route rule 5):?dryRun=0,?confirm=trueor any other name is refused400 VALIDATION_ERRORwith the shared located sentence (unknownQueryParamRefusalMessagefrom@objectstack/rest), before any row is read.server-onlyrow beside the purge incloud-connection-route-ledger.ts.@objectstack/cloud-connectionnow depends on@objectstack/rest(workspace) for that refusal sentence;pnpm-lock.yamlgains the three importer lines. No cycle:restdoes not depend oncloud-connection, andruntime, whichcloud-connectionalready depends on, already depends onrest.withSampleData; the install door. Nopackages/specchange.Why a separate read, not a count on the listing
The seat ruling asked to measure both and take the smaller. The listing answers every installed package on every visit to Installed Apps, for every signed-in caller, and today reads only until the first seed row (
firstOnly). A count has to read every seeded object's rows for every package on each listing call. The new door does that for one package, when the confirm needs it, behind the purge's gate. A?dryRun=1switch on the purge was rejected because a misspelled switch on aPOSTthat deletes would delete.What "an edited sample record is kept" means
The purge matches a row by its dataset's
externalIdkey. A sample record whose key field was edited is no longer a seed row: it is neither deleted nor counted. A sample record edited only in other fields still carries its seed key: the purge deletes it, and the count includes it. The card's observation ("kept a sample record I had edited") holds for a key edit. The count follows the purge either way, because it is the same matcher; the pins below cover all three cases.Tests
Pins go into the existing harness files as new top-level describes.
marketplace-install-local-purge.test.ts:countSeedRows: counts exactly the rowspurgeSeedRowsthen deletes, deleting nothing itself; a key two rows carry isunidentified, equal to the purge'serrors, and is not counted.GET …/sample-data, through the booted plugin with the realSeedLoaderService:withSampleData: false;200, count 0, no seed row read;count 0, no row read;?dryRun=0and?confirm=true:400 VALIDATION_ERRORnaming the parameter, nothing read, nothing deleted;404; no metadata service:500 DRIVER_UNAVAILABLE;manage_metadata:403 FORBIDDENnaming the capability, no seed row read.marketplace-install-local-sample-data-not-loaded.test.ts: on an entry the rehydrate refused, the count answers the purge's422byte for byte and changes nothing; the loadable entry beside it counts 1.cloud-connection-auth-gate.pin.test.tsandmarketplace-install-local-api-key-account-posture.pin.test.ts: the door joins the census (now six), so a held session and a held owner's API key are refused403with the gate's code, an anonymous caller401, and the un-held operator is served.Acceptance notes
counton any visit.content/docs/deployment/marketplace-handbook.mdxstep 6 names the reseed and purge routes and says both needmanage_metadata, which still holds. Adding the new route there is left to the ui follow-up, when the console shows the count.marketplace-install-local-plugin.ts(told to the seat first; handler and one mount line only, the install door and itsheldByread untouched), the two auth-pin test files whose route census must list the new door,packages/cloud-connection/package.jsonandpnpm-lock.yaml(the@objectstack/restdependency), andpackages/cloud-connection/vitest.config.ts(its source alias).patch, becauseMetadata-protocol: none(AGENTS.md Post-Task Checklist step 3: the level follows the kind; a non-protocol widening shipspatch).Verification
All on head
00d0c9d5(origin/mainad2393f7merged at05371373; the merge touched nopackages/cloud-connectionfile; the dependency closure was rebuilt after it).Tests and typecheck
pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2under the verify lock: Test Files 70 passed (70), Tests 888 passed (888).pnpm --filter @objectstack/cloud-connection typecheck(bothtscprograms, tests included): exit 0.eslint --no-inline-configover the seven changed.tsfiles: exit 0, no finding. The repo-widepnpm lintis left to CI.Ablations (scripts/ablation-replace.mjs, each restored, tree clean after each)
The suites import the plugin and the matcher from
src/, so no rebuild sits between the mutation and the run.countSeedRowspassesfirstOnly: true(the listing's early stop): 2 red, the unit "counts exactly the rows the purge then deletes" and the route "counts N … purge then deletes exactly the count".count: 0answer deleted: 1 red, "wall, no active organization: 200 with count 0".requireInstallCapabilityadmission deleted: 4 red, themanage_metadata403 pin, both held-session census cases and the anonymous 401 case incloud-connection-auth-gate.pin.test.ts.DRIVER_UNAVAILABLEbranch disabled: 1 red, "no metadata service: the purge's 500 DRIVER_UNAVAILABLE, never a 0".Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon00d0c9d5: 75 families, each run with its exit code captured before any pipe, all exit 0.--ran: 75 derived, 75 run, 0 NOT-MEASURED, 0 UNRUN.On the first run
check:test-source-aliaswas red: the new@objectstack/restimport resolved torest/distin the tests. Fixed by an anchored alias inpackages/cloud-connection/vitest.config.ts, as the gate prescribes.Full dogfood battery: n/a. The title has no
!andClause-②has no(narrowing)arm. CI's Dogfood Regression Gate runs on the PR.Generated by Claude Code