Skip to content

Commit d97b665

Browse files
docs(qa): author the v18 line's capabilities into the platform checklist (48 since:v18 items) (#23901)
Fixes #23879 Clause-②: no ## What this does The platform checklist carried no `since: "v18"` item, so `node scripts/checklist-select.mjs since:v18` resolved to nothing and a v18 release sweep re-ran only the P0 smoke. This PR authors **48 `since: "v18"` items across 14 area files** — QA data only, no package touched. Premise re-measured before writing: on origin/main `94114c1f`, `since:v18 --json` returned `[]` (exit 1, "nothing matched"), and `.changeset/pre/` held 714 consumed changesets (29 major, 363 minor, 322 patch). On this head the same selector resolves **48 runnable items**. ### How the delta was enumerated Six independent read-only passes (SWEEP.md method, scoped to the v18 delta rather than a whole-ledger re-sweep): - four over the 392 major/minor changesets (about 150 capability rows, grouped); - one over the 322 patch changesets (82 that add or change runner-observable behaviour; the other ~240 are fixes of existing behaviour or text); - one over the ADR-0087 step-18 registries (`MAJOR_18_CONVERSIONS` 72, `RETIRED_KEYS_BY_MAJOR[18]` 239, `RETIRED_DEFS_BY_MAJOR[18]` 170, 389 `18.*` semantic entries) and the landed ADR-0131 stages. Rows were grouped into one item per capability family (each item lists its contributing changesets in `source`). Every item carries its permanent pins in `automated.ref` plus live steps against a booted showcase. Every pin path and cited source file was checked to exist; all 87 new symbol anchors resolve (`symbol anchors: 822/832`, the 10 being the existing named residual). | area | new items | |---|---| | access-security | 9 | | identity-auth | 5 | | automation · cli | 5 each | | api-backend · platform-core · records-forms | 4 each | | studio-authoring | 3 | | approvals · attachments-storage · integration-system | 2 each | | ai · dashboards · i18n | 1 each | P0: `identity-auth.default-organization-boot-invariant`, `access-security.walled-environment-metadata-operator-only`, `platform-core.v18-boot-refusals`, `cli.organization-ownership-ceremony` (the 17 → 18 upgrade end to end — nothing in the ledger exercised it before). ### No item — pure retirements already pinned A retirement is refused at parse with its tombstone, so its honest answer is the pin, not an item: - element flat data-binding keys: `packages/spec/src/ui/element-flat-binding-retirement.test.ts` - manifest `permissions` string list: `packages/spec/src/kernel/manifest-permissions-string-list.test.ts` - the four catalog row objects: `packages/qa/dogfood/test/wall-catalog-refusal.dogfood.test.ts` - `sys_view_definition`: `packages/metadata-protocol/src/plugin.metadata-objects-retired.test.ts` - `sys_approval_token` generic door: `packages/plugins/plugin-approvals/src/sys-approval-token-generic-door.integration.test.ts` - email template `category` / `isSystem`: `packages/spec/src/system/email-template-category-is-system-retirement.test.ts` - view `provider: 'api'`: `packages/spec/src/ui/view-data-api-provider-retirement.test.ts` - document-family schemas: `packages/spec/src/data/document-schemas-retirement.test.ts` - `allowOrgOverride` → `allowEnvironmentOverlay`: `packages/spec/src/kernel/metadata-type-registry-allow-org-override-retirement.test.ts` - legacy `OBJECTSTACK_METADATA_WRITABLE`: `packages/rest/src/rest-meta-managed-seal-hatch.test.ts` - storage scope `public` (also asserted live in `attachments-storage.upload-limits-scope-and-ownership`): `packages/services/service-storage/src/upload-scope-vocabulary.test.ts` - `sys_comment.reactions` column (also live in `records-forms.comment-reactions-and-edit-stamps`): `packages/plugins/plugin-audit/src/sys-comment-reactions-door.integration.test.ts` - `externalSharingModel`, guest recipient, the i18n plugin's own routes, bare `unique: true` on an index, and the 23421 / 23487 / 23488 / 23543 retirements: pins listed per row in the dev report on #23879. The rest of the 72 step-18 conversions are covered generically by `api-backend.enforce-or-remove-authoring-gates` / `api-backend.retired-def-refusal` and the fixture replay in `packages/spec/src/conversions/conversions.test.ts`. ## Run — `checklist-test since:v18` on this branch Subject: framework `94114c1f` (the code is origin/main; this branch changes ledger data only), fresh showcase boot on its own port and ephemeral DB, `Tenancy: single`. Run sequentially by one runner; the per-item verdict table is in the dev report on #23879, and the seat files the `qa-run` record and the defect cards from it. - **Tier 1 — every pin in `automated.ref`, 228 files across 32 packages: all pass** (e.g. spec 24 files / 1546 tests, runtime 34 / 590, rest 20 / 802, objectql 23 / 402, qa/dogfood 15 / 181, cli 25 / 330 plus its one `*.e2e.test.ts`, which packages/cli collects only under `OS_TEST_TIERS=nightly` and which passes there, 2 tests). - **Tier 2 — live API legs**: 1 item PASS, 45 PARTIAL (pins pass, live legs not all driven, or walled / scratch-app / console legs not provisioned this round), **2 FAIL**: - `access-security.anonymous-floor-v18-doors` — anonymous `GET /api/v1/keys` answers 405, not the 401 the 23225 contract states (twice, fresh calls). - `access-security.grant-table-write-contract` — an inverted validity window is refused as an unevaluable predicate (`Unknown variable: previous`), never with the `validity_window_order` rule's own message (twice). - **Console legs: blocked(environment).** `pnpm objectui:build` fails at the pinned `.objectui-sha` `daeb77fc`: `[MISSING_EXPORT] "checkPageComponentViewData" is not exported by packages/spec/dist/ui/index.mjs` — the export was deleted in a35f7a6 (#23784) while the pinned objectui still imports it. The first live pass also disproved four things in my own first draft of the items; those were corrected before any record pinned their revision (a view name is dotted `OBJECT.NAME`; the `/meta` version token is served only on the `?state=draft` read; stock self-registration is closed, so the member is created by the admin and lands held; the dispatcher `/ui` domain is not mounted over HonoServer). ## Gates Local, on head `2d410efa`, from `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (exit codes captured before any pipe): all 13 exit 0 — `check-ci-filter-parity`, `check-closing-keyword-parity` (+ `--self-test`), `check-comment-mask-corpus`, `check:doc-formula-expressions`, `check:cross-package-test-inputs`, `check:doc-authoring`, `check:driver-memory-census`, `check:gitlink-declared`, `check:nul-bytes`, `check:platform-checklist`, `check:refd-timer-probe`, `check:watch-hint-literal`. `--ran` reconciliation: 13 derived, 13 run, 0 NOT-MEASURED (a derived zero). **Changeset:** none — `docs/qa/**` sits outside every package's published `files`, so nothing publishes (`skip-changeset`). **Size:** 3,532 changed lines (all additions, all JSON), over the 3,000-line threshold, so this lands by an authorized review or a human merge, not the queue on green alone. ## Acceptance notes - **Stale existing items — not edited here** (#23878 is running the existing items at the same time, and a revision bump mid-run would orphan its pinned revisions). Each contradicts v18 behaviour: - `cli.migrate-meta-codemod` — still asserts that no `--write` exists (it ships, `packages/cli/test/migrate-meta-write.test.ts`), and still names protocol major 17; - `access-security.capability-declaration-lifecycle` — "bootstraps into sys_capability" (retired); - `automation.trigger-type-matrix`, `automation.time-relative-trigger`, `platform-core.notification-center` — assume scheduled work fires on a stock boot; on v18 it is off unless `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true` (this run's boot log says so); - `attachments-storage.attach-requires-parent-edit` — an outsider who cannot read the parent now gets 404 `RECORD_NOT_FOUND`, not 403; - `approvals.approver-resolution-matrix` — `admin_rescue` now opens on the org administrators; - `records-forms.validation-rule-type-matrix` — warning hits now come back in `warnings[]`; - `cli.scaffold-first-run` — skills now sync from `@objectstack/skills`; - `automation.trigger-type-matrix` also defers the body-only hook refusal that has since landed; - `platform-core.shell-nav-personalization` — drag reorder of the app menu was removed (probably stale; not checked against its steps); - `api-backend.date-range-preset-matrix` — rolling presets now end on today. - **Readings carried from the seat post, verified, not touched** (my edits do not reach those items): `records-forms.concurrent-edit-conflict` cites `packages/rest/src/approvals-routes.ts#mapping` for `CONCURRENT_UPDATE` → 409, but that map has no `CONCURRENT_UPDATE` row (the mapping lives in `rest-server.ts`); `access-security`'s record-shares source text says the family is "registered inline, under no registrar of its own", while `packages/rest/src/sharing-routes.ts` documents and serves it under `RestServer#registerSharingEndpoints`. - ADR-0131 C8 has not landed: `SqlDriver` `applyTenantScope` still carries `.orWhereNull(field)`. The ADR puts that removal on the v18 line after C7, which has landed. - `POST /api/v1/keys` drops an unrecognised body key without a word (a camelCase `expiresAt` 400 days out mints a 90-day key and answers 201); the response echoes the real `expires_at`, and the drop is in the safe direction. - Pins by registry id: 12 of the 72 step-18 conversions and 297 of the 389 `18.*` semantic entries are named by no test (excluding the 169-id guidance roster); two retirements have no pin found at all (the discard-overlay route, `os meta resync`). Not in this PR: running the existing items (#23878); fixing what the run found (one card each, filed by the seat from the dev report). --- _Generated by [Claude Code](https://claude.ai/code/session_016NNWeTTi2eHhhrgDq9sJF8)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent cf07168 commit d97b665

14 files changed

Lines changed: 3535 additions & 0 deletions

‎docs/qa/platform-checklist/areas/access-security.json‎

Lines changed: 666 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,74 @@
792792
"ref": "#22073"
793793
}
794794
]
795+
},
796+
{
797+
"id": "ai.mcp-coded-refusals-and-runtime-exposure",
798+
"title": "MCP on v18: a coded refusal on the data tools, list_actions and the record resource renders {error:{code,message,status}} (not the bare message), both MCP doors honour a runtime-authored object's apiEnabled:false including after a restart, and run_action withholds PERMISSION_DENIED details",
799+
"since": "v18",
800+
"status": "active",
801+
"revision": 1,
802+
"priority": "P2",
803+
"surface": "api",
804+
"personas": [
805+
"seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot"
806+
],
807+
"fixtures": {
808+
"app": "showcase",
809+
"requires": [
810+
"a fresh boot of the showcase: `pnpm dev -- --fresh -p PORT --seed-admin` (ephemeral DB; the seeded admin is printed)",
811+
"MCP enabled on the boot (ai.mcp-http-surface names the switch)",
812+
"a runtime-authored scratch object (PUT /api/v1/meta/object on a scratch boot) with apiEnabled:false"
813+
]
814+
},
815+
"steps": [
816+
"call the MCP get_record tool for a missing id; call query_records on the apiEnabled:false object",
817+
"restart the boot (same file DB) and repeat",
818+
"call run_action on an action the caller may not run"
819+
],
820+
"acceptance": [
821+
{
822+
"clause": "coded MCP refusals and runtime-object exposure — the permanent pins pass at the run subject sha (RUNNER rule 6: cite their output, never re-derive by hand)",
823+
"oracle": "test",
824+
"verify": "run each file named in automated.ref with its package's vitest (`pnpm --filter PKG exec vitest run --maxWorkers=2 FILE`, never through a bare `--`); every named file reports ≥1 test and 0 failed. A file that reports 0 tests or is not found is NOT a pass — it is a stale ref, fix the item",
825+
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
826+
},
827+
{
828+
"clause": "get_record for a missing id renders {error:{code:\"RECORD_NOT_FOUND\",status:404}}",
829+
"oracle": "api",
830+
"verify": "tool result body",
831+
"evidence": "the excerpt"
832+
},
833+
{
834+
"clause": "query_records on the runtime-authored apiEnabled:false object is refused before and after restart, and a deleted object leaves list_objects",
835+
"oracle": "api",
836+
"verify": "tool results across the restart",
837+
"evidence": "the excerpts"
838+
},
839+
{
840+
"clause": "run_action PERMISSION_DENIED carries no internal details",
841+
"oracle": "api",
842+
"verify": "tool result keys",
843+
"evidence": "the excerpt"
844+
}
845+
],
846+
"automated": {
847+
"kind": "unit",
848+
"ref": "packages/mcp/src/plugin.record-resource-exposure.test.ts + packages/mcp/src/mcp-action-refusal-details.test.ts + packages/objectql/src/plugin-runtime-object-metadata-copy.test.ts"
849+
},
850+
"source": [
851+
"packages/mcp/src/mcp-http-tools.ts",
852+
"packages/mcp/src/stdio-data-bridge.ts#enforceApiExposure",
853+
"v18 changesets (.changeset/pre/, consumed into 18.0.0-next.*): 23509-mcp-coded-refusal, 23661-runtime-object-metadata-copy, 23689-env-kernel-runtime-object-copy, 23529-mcp-action-tools-withhold-permission-details"
854+
],
855+
"history": [
856+
{
857+
"revision": 1,
858+
"date": "2026-10-16",
859+
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
860+
"ref": "#23879"
861+
}
862+
]
795863
}
796864
]
797865
}

‎docs/qa/platform-checklist/areas/api-backend.json‎

Lines changed: 314 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,6 +1447,153 @@
14471447
"history": [
14481448
{ "revision": 1, "date": "2026-08-30", "change": "initial — the inbox's keyboard surface (documented in the page's own header and advertised by its hint bar) had no checklist coverage; the load-bearing halves are that r/a land the same server transition as the buttons and that quick-decidability gates hotkeys off rows whose decisions require typed outputs", "ref": "#sweep-2026-08-30" }
14491449
]
1450+
},
1451+
{
1452+
"id": "approvals.request-page-reply-and-reader-visibility",
1453+
"title": "Approval requests on v18: the request record page serves record:approval_decision and a Reply action that lands in its timeline, `enable.approvalsVisibleToReaders` lets a plain record reader see the record's requests read-only (and only then), the generic /data door serves request rows by approvals visibility, and titles shown to a reader use only what that reader may read",
1454+
"since": "v18",
1455+
"status": "active",
1456+
"revision": 1,
1457+
"priority": "P1",
1458+
"surface": "mixed",
1459+
"personas": [
1460+
"seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot",
1461+
"org member — created by the admin with POST /api/v1/auth/admin/create-user (stock showcase self-registration is closed: audience posture invite_only answers sign-up 403 SELF_REGISTRATION_CLOSED); joins the Default Organization as `member`, not platform admin, and lands mustChangePassword:true, i.e. a HELD session until it changes its password (POST /api/v1/auth/change-password)"
1462+
],
1463+
"fixtures": {
1464+
"app": "showcase",
1465+
"requires": [
1466+
"a fresh boot of the showcase: `pnpm dev -- --fresh -p PORT --seed-admin` (ephemeral DB; the seeded admin is printed)",
1467+
"a pending showcase approval request the member is NOT an approver on",
1468+
"for the reader leg: a scratch object (or showcase object via the scratch app) with enable.approvalsVisibleToReaders:true"
1469+
],
1470+
"knownGaps": [
1471+
"the console clause needs the vendored console — blocked(environment) while the .objectui-sha pin does not build against current spec (see records-forms.v18-semantic-defaults)"
1472+
]
1473+
},
1474+
"steps": [
1475+
"GET /api/v1/meta/page/sys_approval_request_detail and find record:approval_decision",
1476+
"as the pending approver POST /api/v1/approvals/requests/ID/comment {body}; read the timeline",
1477+
"as the member (non-participant) GET /api/v1/data/sys_approval_request/ID and list with a total",
1478+
"flip approvalsVisibleToReaders on the scratch object; as a plain reader list that record's requests; flip it off and repeat without restart"
1479+
],
1480+
"acceptance": [
1481+
{
1482+
"clause": "request page, reply, reader opt-in, generic-door read gate and reader-scoped titles — the permanent pins pass at the run subject sha (RUNNER rule 6: cite their output, never re-derive by hand)",
1483+
"oracle": "test",
1484+
"verify": "run each file named in automated.ref with its package's vitest (`pnpm --filter PKG exec vitest run --maxWorkers=2 FILE`, never through a bare `--`); every named file reports ≥1 test and 0 failed. A file that reports 0 tests or is not found is NOT a pass — it is a stale ref, fix the item",
1485+
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
1486+
},
1487+
{
1488+
"clause": "the request page carries record:approval_decision and the reply lands in the timeline (pending approver or submitter only)",
1489+
"oracle": "api",
1490+
"verify": "page body + timeline read",
1491+
"evidence": "excerpts"
1492+
},
1493+
{
1494+
"clause": "a non-participant's by-id read on /data/sys_approval_request → 404 and list totals exclude it",
1495+
"oracle": "api",
1496+
"verify": "status + total",
1497+
"evidence": "answers"
1498+
},
1499+
{
1500+
"clause": "the reader sees requests read-only with no actions only while the flag is on; the inbox is not widened",
1501+
"oracle": "api",
1502+
"verify": "list per flag state",
1503+
"evidence": "two lists"
1504+
},
1505+
{
1506+
"clause": "the console decision panel renders on the request page",
1507+
"oracle": "screenshot",
1508+
"verify": "request page after load",
1509+
"evidence": "one-line description"
1510+
}
1511+
],
1512+
"traps": [
1513+
"wrong-persona",
1514+
"stale-console-bundle"
1515+
],
1516+
"automated": {
1517+
"kind": "unit",
1518+
"ref": "packages/plugins/plugin-approvals/src/sys-approval-request-page.test.ts + packages/spec/src/ui/component-record-approval-decision-22472.test.ts + packages/plugins/plugin-approvals/src/record-reader-opt-in.integration.test.ts + packages/plugins/plugin-approvals/src/request-child-read-gate.integration.test.ts + packages/plugins/plugin-approvals/src/subject-title-reader-fls.integration.test.ts"
1519+
},
1520+
"source": [
1521+
"packages/plugins/plugin-approvals/src/sys-approval-request.page.ts",
1522+
"packages/plugins/plugin-approvals/src/request-read-gate.ts#bindRequestReadGate",
1523+
"packages/plugins/plugin-approvals/src/approval-service.ts",
1524+
"v18 changesets (.changeset/pre/, consumed into 18.0.0-next.*): 22472-spec-record-approval-decision, 22473-approval-request-page, 22560-platform-objects-approvals-visible-to-readers-form-row, 22560-plugin-approvals-declared-record-reader-tier, 22560-spec-approvals-visible-to-readers, 22559-approval-request-read-gate, 22589-approval-sibling-read-gates, 23594-subject-title-reader-fls, 23618-approval-title-family-served-fields, 23637-lookup-target-title-row-access"
1525+
],
1526+
"history": [
1527+
{
1528+
"revision": 1,
1529+
"date": "2026-10-16",
1530+
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
1531+
"ref": "#23879"
1532+
}
1533+
]
1534+
},
1535+
{
1536+
"id": "approvals.act-door-and-admin-rescue-slate",
1537+
"title": "Approvals on v18: the email action door /approvals/act works through the dispatcher exactly as the self-hosted mount (GET renders, POST redeems, PUT 405 Allow: GET, POST, a dead link a 200 page, a policy-held session not 403'd before the token), and an approval over an unstaffed position opens on the org administrators as ordinary approvers",
1538+
"since": "v18",
1539+
"status": "active",
1540+
"revision": 1,
1541+
"priority": "P1",
1542+
"surface": "api",
1543+
"personas": [
1544+
"seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot",
1545+
"org member — created by the admin with POST /api/v1/auth/admin/create-user (stock showcase self-registration is closed: audience posture invite_only answers sign-up 403 SELF_REGISTRATION_CLOSED); joins the Default Organization as `member`, not platform admin, and lands mustChangePassword:true, i.e. a HELD session until it changes its password (POST /api/v1/auth/change-password)"
1546+
],
1547+
"fixtures": {
1548+
"app": "showcase",
1549+
"requires": [
1550+
"a fresh boot of the showcase: `pnpm dev -- --fresh -p PORT --seed-admin` (ephemeral DB; the seeded admin is printed)",
1551+
"an approval email action token (dev mail transport capture) for a pending request",
1552+
"an approval process whose approver is a position nobody holds (scratch process, default onEmptyApprovers)"
1553+
]
1554+
},
1555+
"steps": [
1556+
"GET /api/v1/approvals/act?token=T; POST the same; PUT the same; GET with a consumed token",
1557+
"submit a record into the unstaffed process; as the org admin GET /api/v1/approvals/requests (My Pending); decide it"
1558+
],
1559+
"acceptance": [
1560+
{
1561+
"clause": "the dispatcher act door and the admin_rescue slate — the permanent pins pass at the run subject sha (RUNNER rule 6: cite their output, never re-derive by hand)",
1562+
"oracle": "test",
1563+
"verify": "run each file named in automated.ref with its package's vitest (`pnpm --filter PKG exec vitest run --maxWorkers=2 FILE`, never through a bare `--`); every named file reports ≥1 test and 0 failed. A file that reports 0 tests or is not found is NOT a pass — it is a stale ref, fix the item",
1564+
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
1565+
},
1566+
{
1567+
"clause": "GET renders the action page, POST redeems, PUT → 405 with Allow: GET, POST, a dead token → a 200 page (not an error envelope)",
1568+
"oracle": "api",
1569+
"verify": "status + headers per verb",
1570+
"evidence": "four lines"
1571+
},
1572+
{
1573+
"clause": "the unstaffed request appears in each org admin's pending list and the decision is recorded via_override:false",
1574+
"oracle": "api",
1575+
"verify": "pending list + decision record",
1576+
"evidence": "excerpts"
1577+
}
1578+
],
1579+
"automated": {
1580+
"kind": "dogfood",
1581+
"ref": "packages/runtime/src/domains/approvals-act.test.ts + packages/plugins/plugin-approvals/src/action-page-member.integration.test.ts + packages/plugins/plugin-approvals/src/admin-rescue-named-slate.integration.test.ts + packages/qa/dogfood/test/approval-admin-rescue-slate.dogfood.test.ts"
1582+
},
1583+
"source": [
1584+
"packages/runtime/src/domains/approvals.ts",
1585+
"packages/core/src/security/auth-gate.ts",
1586+
"packages/plugins/plugin-approvals/src/approval-service.ts",
1587+
"v18 changesets (.changeset/pre/, consumed into 18.0.0-next.*): 22575-spec-approvals-action-page-member, 22577-approvals-act-allowlist, 22578-approvals-action-page-member, 22824-admin-rescue-named-slate"
1588+
],
1589+
"history": [
1590+
{
1591+
"revision": 1,
1592+
"date": "2026-10-16",
1593+
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it; approvals.approver-resolution-matrix still says admin_rescue is decidable only via the privileged override — stale against 22824 (reported, not edited here)",
1594+
"ref": "#23879"
1595+
}
1596+
]
14501597
}
14511598
]
14521599
}

0 commit comments

Comments
 (0)