Skip to content

Commit 5c41965

Browse files
committed
docs(qa): round 12 — platform-core: install-local's flag and artifact, two knownGaps lines
QA run #24779 checklist-accuracy findings 9, 10 and 11 (wave anchor #24579 round 12, claim 6286008102). Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude <noreply@anthropic.com>
1 parent b47fccc commit 5c41965

1 file changed

Lines changed: 30 additions & 10 deletions

File tree

‎docs/qa/platform-checklist/areas/platform-core.json‎

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@
11961196
"title": "Documentation audience gating (book + doc): the tree endpoint and the doc read layer gate on ONE semantics — public/org/permissionSet, fail-closed — and an unclaimed doc never leaks through a public book",
11971197
"since": "v14.1",
11981198
"status": "active",
1199-
"revision": 2,
1199+
"revision": 3,
12001200
"priority": "P1",
12011201
"surface": "api",
12021202
"personas": [
@@ -1213,7 +1213,8 @@
12131213
],
12141214
"knownGaps": [
12151215
"Stock showcase authors ONE book with `audience: 'public'` — `showcase_manual` (examples/app-showcase/src/system/books/index.ts) — and none with `audience: { permissionSet: '<name>' }`. Verify against the installed corpus FIRST (GET /api/v1/meta/book and read each item's `audience`): the public clauses run on `showcase_manual`; if no permissionSet-gated book exists, the permissionSet clauses are blocked(fixture) and MUST be recorded as such (or staged per knownGaps[1]) — do NOT score them from the `org` default alone, which is the one audience every authenticated caller passes and therefore proves nothing about the gate.",
1216-
"Authoring a book/doc at runtime to create the fixture is acceptable only if the run records that it did so and tears it down; the audience is read off the stored row, so a runtime-authored book exercises the same resolver."
1216+
"Authoring a book/doc at runtime to create the fixture is acceptable only if the run records that it did so and tears it down; the audience is read off the stored row, so a runtime-authored book exercises the same resolver.",
1217+
"acceptance[2]'s last sentence, an unknown future audience shape also denies, has no pin: the audienceAllows cases in packages/spec/src/system/book.test.ts drive public, org / unset and { permissionSet } (the fail-closed case included), never a shape outside that vocabulary (QA run #24779). The branch is audienceAllows' final `return false` (book.zod.ts, 'unknown future shape → fail closed'): a run reads it from source and records that half not pin-scored, never as a pass carried by the permissionSet case"
12171218
]
12181219
},
12191220
"steps": [
@@ -1299,6 +1300,12 @@
12991300
"date": "2026-10-19",
13001301
"change": "fixtures.knownGaps[0] and acceptance[3] (clause, verify) corrected — two checklist-accuracy findings of the 18.0 QA sweep (QA run #24557, platform-core.docs-audience-gate; wave anchor #24579 §3). knownGaps[0] said stock showcase authors neither a public nor a permissionSet book; it ships the public `showcase_manual` (examples/app-showcase/src/system/books/index.ts), so only the permissionSet half is a fixture gap. acceptance[3] said a lesser persona's tree is a subset of admin's; audienceAllows (packages/spec/src/system/book.zod.ts) admits a permissionSet doc for holders only and has no admin bypass, so for those books the run's admin non-holder saw less than a holder — the subset relation is holder vs non-holder. acceptance[2]'s unknown-shape half (unreachable live, no pin) is a fixture/pin gap left on #24579 §4",
13011302
"ref": "#24632"
1303+
},
1304+
{
1305+
"revision": 3,
1306+
"date": "2026-10-20",
1307+
"change": "knownGaps[2] is new — checklist-accuracy finding of QA run #24779 (wave anchor #24579 round 12): no pin covers acceptance[2]'s 'unknown future audience shape → deny' branch. Read at base 307ce4c4b4: book.test.ts's audienceAllows cases drive the three declared shapes only. The item carries no automated.ref, so there is no ref to narrow. No clause changed",
1308+
"ref": "claude/issue-24579-v18-sweep-round12"
13021309
}
13031310
]
13041311
},
@@ -1843,7 +1850,7 @@
18431850
"title": "Marketplace install-local: the wiring matrix mounts the right arms per OS_CLOUD_URL, the air-gapped inline install works with NO control plane, reseed/purge touch exactly the sample rows, every mutating door is manage_metadata-gated both sides, and the listing projects installedBy/storageDir per principal",
18441851
"since": "v17",
18451852
"status": "active",
1846-
"revision": 5,
1853+
"revision": 6,
18471854
"priority": "P1",
18481855
"surface": "api",
18491856
"personas": [
@@ -1856,7 +1863,7 @@
18561863
"app": "showcase",
18571864
"requires": [
18581865
"TWO isolated boots of the same app: boot A = plain `objectstack dev` with NO OS_CLOUD_URL (resolveCloudUrl() falls back to DEFAULT_CLOUD_URL — truthy — so the CLOUD arm mounts: proxy + install-local + cloud-connection + runtime-config); boot B = the same line with OS_CLOUD_URL=off exported (the offline arm: install-local pinned to the 'off' control plane + runtime-config ONLY — nothing that dials out mounts). The matrix is decided by Serve.planMarketplaceWiring, pure and testable (packages/cli/src/commands/serve.ts). ⚠️ On showcase the boot-A cloud arm is the HOST's, not the CLI's: examples/app-showcase/objectstack.config.ts mounts MarketplaceProxyPlugin, MarketplaceInstallLocalPlugin and CloudConnectionPlugin itself whenever resolveCloudUrl() is truthy, and RuntimeConfigPlugin always, and planMarketplaceWiring checks presence — so on boot A the CLI wires none of the four, and on boot B its offline arm adds install-local alone (pinned to the 'off' control plane) beside the host's runtime-config. The observable route matrix is the same either way",
1859-
"a compiled package artifact for the inline install: pnpm -C examples/app-crm build → dist/objectstack.json (the air-gapped path the #8343 fix exists for — `handleInstall`'s inline branch never reads the cloud URL). Both boots that install run from a SCRATCH COPY of the app, never the shared tree: install-local caches the manifest under the working directory's .objectstack/installed-packages (packages/cloud-connection/src/local-manifest-source.ts#DEFAULT_INSTALLED_PACKAGES_DIR, resolved against process.cwd()), which a per-boot OS_HOME / OS_STORAGE_LOCAL_ROOT does not redirect (RUNNER.md environment facts)",
1866+
"a compiled package artifact for the inline install: examples/app-crm/dist/objectstack.json. The tree's own `pnpm build` writes it (the root turbo build runs @objectstack/example-crm's `objectstack build` script), and the file is gitignored (examples/*/dist/), so only an unbuilt tree lacks it; then `pnpm -C examples/app-crm build` writes it. A run under a no-rebuild brief installs the existing file, as QA run #24779 did (the air-gapped path the #8343 fix exists for — `handleInstall`'s inline branch never reads the cloud URL). Both boots that install run from a SCRATCH COPY of the app, never the shared tree: install-local caches the manifest under the working directory's .objectstack/installed-packages (packages/cloud-connection/src/local-manifest-source.ts#DEFAULT_INSTALLED_PACKAGES_DIR, resolved against process.cwd()), which a per-boot OS_HOME / OS_STORAGE_LOCAL_ROOT does not redirect (RUNNER.md environment facts)",
18601867
"admin credentials passed to the CLI door (--email/--password or OS_RUNTIME_EMAIL/OS_RUNTIME_PASSWORD) — the runtime refuses an unauthenticated install with a 401 that names exactly that remedy (packages/cli/src/commands/package/install.ts)"
18611868
],
18621869
"knownGaps": [
@@ -1867,12 +1874,12 @@
18671874
"steps": [
18681875
"boot A (no OS_CLOUD_URL), as admin: GET /api/v1/runtime/config and record features.marketplace + features.installLocal; probe the mounts — GET /api/v1/marketplace/install-local (authed) → 200, and the browse namespace answers as a mounted passthrough (a non-404 that reflects the upstream's reachability, not this kernel's routing). Capture the boot banner's Marketplace plugin line",
18691876
"boot B (OS_CLOUD_URL=off), as admin: GET /api/v1/runtime/config → still served, now reporting installLocal:true AND marketplace:false (#8389 discovery + #8356 route-table derivation); GET /api/v1/marketplace/install-local → 200 []; a browse-namespace probe (e.g. GET /api/v1/marketplace/packages) → 404 (the proxy did NOT mount); GET /api/v1/cloud-connection/status → 404 (cloud-connection did NOT mount)",
1870-
"build the artifact: pnpm -C examples/app-crm build; confirm dist/objectstack.json exists",
1877+
"locate the artifact examples/app-crm/dist/objectstack.json, which the tree's `pnpm build` already wrote (fixtures.requires[1]); run `pnpm -C examples/app-crm build` only when it is absent, and record the manifest's id and version",
18711878
"air-gapped install against boot B: `os package install ./examples/app-crm/dist/objectstack.json` with admin credentials — the CLI signs in, then POSTs the manifest inline to /api/v1/marketplace/install-local (install.ts); capture the success output and the installed entry",
18721879
"confirm the install registered: GET /api/v1/meta/app lists the CRM app; a CRM object answers on /api/v1/data; GET /api/v1/marketplace/install-local as admin shows the entry WITH installedBy, and storageDir on the listing envelope (data.storageDir — one value for the whole ledger, not a per-entry field)",
18731880
"per-principal projection (#9011): repeat the GET as the plain member — 200, the entry listed, but installedBy ABSENT from the entry and storageDir ABSENT from the envelope; repeat anonymous — 401",
1874-
"reseed/purge on the installed entry: create ONE user-authored row in an installed CRM object and record its id; POST /api/v1/marketplace/install-local/:manifestId/purge-sample-data as admin — capture {deleted, skipped, errors}; re-read: every manifest-declared seed id is gone, the user-authored row SURVIVES (the purge deletes only ids declared in the cached manifest's seed datasets — marketplace-install-local-plugin.ts); restart boot B against the same DB and confirm the purged rows do NOT come back (sampleDataPurged persisted on the ledger entry)",
1875-
"POST …/:manifestId/reseed-sample-data as admin — the sample rows return and sampleDataPurged flips back off",
1881+
"reseed/purge on the installed entry: create ONE user-authored row in an installed CRM object and record its id; POST /api/v1/marketplace/install-local/:manifestId/purge-sample-data as admin — capture {deleted, skipped, errors}; re-read: every manifest-declared seed id is gone, the user-authored row SURVIVES (the purge deletes only ids declared in the cached manifest's seed datasets — marketplace-install-local-plugin.ts); restart boot B against the same DB and confirm the purged rows do NOT come back. Read the flag where each side keeps it: the purge answer and the listing entry carry withSampleData:false (the listing derives it per request from the caller's own rows and serves no sampleDataPurged key, #21775), while sampleDataPurged:true is persisted in the ledger file STORAGEDIR/MANIFESTID.json (the admin listing's data.storageDir; local-manifest-source.ts), which the rehydrate-time healer reads",
1882+
"POST …/:manifestId/reseed-sample-data as admin — the sample rows return, the listing entry reads withSampleData:true again, and the ledger file's sampleDataPurged reads false",
18761883
"capability gate both sides (#8976): as the plain member, POST install-local (any body), DELETE …/:manifestId, POST reseed, POST purge — each 403 naming the manage_metadata requirement; anonymous on each → 401; the admin's same calls succeeded above",
18771884
"DELETE /api/v1/marketplace/install-local/:manifestId as admin → 200 {success:true, data:{manifestId, cleanups, note}}; WITHOUT restarting, re-read: a CRM object's data route answers 404 and GET /meta/app no longer lists the CRM app. Then restart once and re-read — the package stays gone (the ledger entry was removed first, so rehydrate does not bring it back)"
18781885
],
@@ -1904,7 +1911,7 @@
19041911
{
19051912
"clause": "purge deletes EXACTLY the manifest-declared sample rows: every seed-dataset id gone, the user-authored row untouched, already-deleted ids counted as skipped, and the emptiness is DURABLE — sampleDataPurged persists so the rehydrate-time healer does not re-seed on the next restart",
19061913
"oracle": "api",
1907-
"verify": "the purge response counts + the post-purge reads (user row present, seed ids absent) + the post-restart re-read (still absent); reseed then restores them and flips the flag back (marketplace-install-local-plugin.ts rehydrate guard, flag write)",
1914+
"verify": "the purge response counts + the post-purge reads (user row present, seed ids absent) + the post-restart re-read (still absent); reseed then restores them and flips the flag back — sampleDataPurged is read in the ledger file, and the listing entry's withSampleData, derived from the caller's rows, follows the rows (marketplace-install-local-plugin.ts rehydrate guard, flag write, handleList)",
19081915
"evidence": "purge response + before/after/post-restart row reads"
19091916
},
19101917
{
@@ -1966,6 +1973,12 @@
19661973
"date": "2026-10-19",
19671974
"change": "personas[1], steps[3] and fixtures.requires[0] corrected — three checklist-accuracy findings of the 18.0 QA sweep (QA run #24557, platform-core.marketplace-install-local-lifecycle; wave anchor #24579 §3). The plain member cannot be a fresh sign-up: the stock audience posture is invite_only (plugin-auth audience-posture.ts), so the run created the member through the admin create-user door. steps[3] read `install./examples` (a missing space). On showcase the cloud arm of boot A, and runtime-config on both boots, are mounted by the host config (examples/app-showcase/objectstack.config.ts), and Serve.planMarketplaceWiring wires only what the host did not — the matrix the clauses score is unchanged. No acceptance clause changed",
19681975
"ref": "#24632"
1976+
},
1977+
{
1978+
"revision": 6,
1979+
"date": "2026-10-20",
1980+
"change": "fixtures.requires[1], steps[2], steps[6]–[7] and acceptance[4].verify — checklist-accuracy findings of QA run #24779 (wave anchor #24579 round 12). The listing serves withSampleData, derived per request from the caller's rows (#21775), never sampleDataPurged, which lives in the ledger file (local-manifest-source.ts, marketplace-install-local-plugin.ts handleList at base 307ce4c4b4). The artifact step now says what a runner needs: the existing examples/app-crm/dist/objectstack.json the tree's build writes, rebuilt only when absent. No clause changed",
1981+
"ref": "claude/issue-24579-v18-sweep-round12"
19691982
}
19701983
]
19711984
},
@@ -2691,7 +2704,7 @@
26912704
"title": "Seed mode matrix (insert/update/upsert/replace/ignore): each mode does exactly its verb through the ONE SeedLoaderService, no-op replays skip instead of churning, ignore reports skips — and `replace` is DECLARED delete-all-then-insert but IMPLEMENTED as bare insert (no caller clears the table): the posture is recorded, never ticked as scoped deletion",
26922705
"since": "v15",
26932706
"status": "active",
2694-
"revision": 2,
2707+
"revision": 3,
26952708
"priority": "P1",
26962709
"surface": "api",
26972710
"personas": ["seeded admin (admin@objectos.ai / admin123)"],
@@ -2703,7 +2716,8 @@
27032716
],
27042717
"knownGaps": [
27052718
"the publish response's seedApplied summary surfaces inserted/updated/errors but not per-row skip detail — the `ignore says so` clause therefore reads the skip from effect (counts + untouched values) plus the loader's summary where surfaced, not from a UI report",
2706-
"`insert` mode's schema comment says 'fail on duplicate', but the loader does NO matching in insert mode (no existing-records preload) — the failure only materializes where the object itself carries a unique constraint; on an unconstrained scratch object an insert replay simply duplicates rows. The matrix records both shapes honestly"
2719+
"`insert` mode's schema comment says 'fail on duplicate', but the loader does NO matching in insert mode (no existing-records preload) — the failure only materializes where the object itself carries a unique constraint; on an unconstrained scratch object an insert replay simply duplicates rows. The matrix records both shapes honestly",
2720+
"after insert mode has duplicated a natural key (acceptance[3]'s unconstrained shape), upsert and update bind each key to ONE of the duplicates and leave the other unconverged, silently: loadExistingRecords (packages/metadata-protocol/src/seed-loader.ts) keys an unordered find by externalId, so the last row read wins — the NEWER duplicate on QA run #24779's SQLite boot, which upsert rewrote while the original kept its value. No clause scores it: record it beside acceptance[3] as an observation, never as a FAIL of acceptance[0] or negative[0]"
27072721
]
27082722
},
27092723
"steps": [
@@ -2794,6 +2808,12 @@
27942808
"date": "2026-10-16",
27952809
"change": "fixtures.requires[0], steps[0] and [6] and acceptance[4].verify corrected (checklist-accuracy findings of QA run #23939, wave #23878; steps[6] from this card's re-run). A seed body refuses a `name` key (SeedSchema is strict; the URL names the seed) and a scratch object needs a sharingModel (its publish is refused, rule security-owd-unset), so the fixture as written could not be authored; steps[6]'s replay ran after steps 2–4 had rewritten A's rows, so its first replay legitimately writes — the step now scores the second replay, which the re-run measured at 0 inserted / 0 updated; and the verify text pinned decideWriteAction to a source line that has since moved — it now cites the symbols only. Not loosened: every mode clause and the replace posture are scored as before",
27962810
"ref": "#24036"
2811+
},
2812+
{
2813+
"revision": 3,
2814+
"date": "2026-10-20",
2815+
"change": "knownGaps[2] is new — QA run #24779's observation on acceptance[3] (wave anchor #24579 round 12): after insert mode duplicated a natural key, upsert bound the newer duplicate and left the original unconverged. Read at base 307ce4c4b4 in seed-loader.ts loadExistingRecords, which keys an unordered find by externalId (last row wins). No clause changed",
2816+
"ref": "claude/issue-24579-v18-sweep-round12"
27972817
}
27982818
]
27992819
},

0 commit comments

Comments
 (0)