Skip to content

Commit 615c085

Browse files
docs(qa): key the permission-matrix publish refusal on code NO_DRAFT, not a retired message spelling (#19843)
Fixes #19705 Clause-②: no ## What `docs/qa/platform-checklist/areas/access-security.json`, item `access-security.permission-matrix-edit-loop` (P1): clause 2 and its `verify` quoted the nothing-to-publish refusal by a bracketed lowercase opener inside the message text. That opener was retired from every metadata-protocol refusal in `7a25a3ee9c` (PR #19683), so a checker following the clause verbatim against a live answer recorded a false failure. Per the card's fix shape (adopted by triage): the clause and verify now key on the **code axis**: HTTP 404 whose body carries `code: 'NO_DRAFT'`, and say explicitly to judge the refusal on status + `code`, never on message prose. Pass/fail semantics are unchanged. - `revision` 2 -> 3, with a new append-only `history` entry (revision 3, ref #19705). The revision-2 history entry that quotes the old spelling is untouched (history is append-only). - New `source` row: `packages/metadata-protocol/src/sys-metadata-repository.ts#promoteDraft`, the producer. ## Runtime site (verified on `origin/main` 71ef221) - Producer: `SysMetadataRepository.promoteDraft` (`packages/metadata-protocol/src/sys-metadata-repository.ts`) throws message `No pending draft exists for TYPE/NAME — nothing to publish.` with `code = 'NO_DRAFT'`, `status = 404`. No bracketed opener. - Route: `POST /meta/:type/:name/publish` in `packages/rest/src/rest-server.ts` routes the throw through `handleRouteError`; the wire body shape `{ error, code: 'NO_DRAFT' }` is pinned in `rest-4xx-message-truncation.test.ts` and `rest-5xx-message-sanitization.test.ts`. - Status and code match the card. Absence of the opener family is pinned by `protocol.bracketed-refusal-opener-absence.test.ts`. ## Carrier re-derivation over `docs/qa/**` Instrument: `git grep -ni no_draft -- docs/qa` plus a family census `git grep -noE '[0-9]{3} \[[a-z][a-z_]+\]' -- docs/qa`, at 71ef221 (base). Radius: every tracked file under `docs/qa/`. Lit control: `git grep -nic invalid_metadata -- docs/qa` answers 3 hits in each of two area files, so the instrument reaches both files. - Before: `no_draft` 3 lines, all in `access-security.json` (clause, verify, revision-2 history). - After (db81a89): 1 line, the immutable revision-2 history entry. No other `docs/qa/**` carrier of the retired `no_draft` spelling exists. ## Gates (all at `db81a89b`) `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 13 commands; all 13 run, reconciled with `--ran` (13 derived, 13 run, 0 NOT-MEASURED, derived from recorded exit codes). All exit 0. `check:doc-formula-expressions` first exited 3 (PREREQUISITE NOT MET, formula/lint unbuilt, nothing measured); after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` it re-ran exit 0. `check:platform-checklist` anchor counts: symbol anchors before 620/638 resolved against 310 cited sources, after 621/639 against 311 (the one new source row resolves); residual 18 unchanged; 17 file floors held; line citations 0 before and after. No floor or ledger touched. ## Changeset None. Docs/QA-only: `docs/qa/**` ships in no package's `files[]` (`git grep -l docs/qa -- '**/package.json'` = 0 hits; positive control: the `files` arrays exist and list `dist`). This is `skip-changeset` territory; per the dispatch, no label write was attempted from this seat. ## Acceptance notes - `docs/qa/platform-checklist/areas/studio-authoring.json` is off-limits for this card by the triage ruling. Its `[invalid_metadata]` lines have already been re-spelled on `main` (its item revision 3, dated 2026-09-23). It still carries `422 [writable_package_required]` in one clause and one source row, for the metadata-protocol write door, which no longer emits that opener (`SysMetadataRepository` throws `WRITABLE_PACKAGE_REQUIRED` / 422 with plain prose). Same false-failure class as this card; reported for the seat to file or attach to #19709, not edited here. - `packages/runtime/src/domains/packages.ts` still opens two refusals with a bracketed restatement of their declared code (`[writable_package_required]` and `[duplicate_source_not_a_base]`). The absence pin covers only `metadata-protocol`. Reported, not edited here (outside `docs/qa/**`). - `packages/rest/src/rest-server.ts`: the comment above the publish route still reads `404 [no_draft] when nothing to publish`. This is a code comment, not a refusal, so it goes here as a note only. - `packages/client` JSDoc carrying the same spelling is #19704 (a `domain:cli` card). #19704 is not addressed here. --- _Generated by [Claude Code](https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6bf7f1c commit 615c085

1 file changed

Lines changed: 6 additions & 4 deletions

File tree

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,7 @@
17801780
"title": "Editing the permission matrix and publishing flips an affected persona's live API access; revoke flips it back; assign/unassign moves access",
17811781
"since": "v16",
17821782
"status": "active",
1783-
"revision": 2,
1783+
"revision": 3,
17841784
"priority": "P1",
17851785
"surface": "mixed",
17861786
"personas": [
@@ -1813,9 +1813,9 @@
18131813
"evidence": "the baseline trace"
18141814
},
18151815
{
1816-
"clause": "the matrix edit saves and takes effect: flipping the verb in the console answers 2xx on PUT /api/v1/meta/permission/S and the published set carries the new verb. The publish CALL is 2xx only when the save was staged as a draft (?mode=draft); a plain PUT — which is what the console editor issues — lands state:'active' directly, and POST .../publish then answers 404 [no_draft] because nothing is pending. Record WHICH path the console took",
1816+
"clause": "the matrix edit saves and takes effect: flipping the verb in the console answers 2xx on PUT /api/v1/meta/permission/S and the published set carries the new verb. The publish CALL is 2xx only when the save was staged as a draft (?mode=draft); a plain PUT — which is what the console editor issues — lands state:'active' directly, and POST .../publish then answers 404 with code NO_DRAFT because nothing is pending — judge that refusal on the status and the code field, never on the message text, which is human language and free to change. Record WHICH path the console took",
18171817
"oracle": "api",
1818-
"verify": "PUT 2xx and GET /api/v1/meta/permission/S (published) shows delete:true on showcase_task. The publish call is 2xx after a ?mode=draft save, or 404 [no_draft] after a plain active-state PUT (rest-server.ts mounts POST /meta/:type/:name/publish for every type and answers 404 [no_draft] when there is nothing to promote) — either is a pass provided the published set carries the verb",
1818+
"verify": "PUT 2xx and GET /api/v1/meta/permission/S (published) shows delete:true on showcase_task. The publish call is 2xx after a ?mode=draft save, or HTTP 404 whose body carries code: 'NO_DRAFT' after a plain active-state PUT (rest-server.ts mounts POST /meta/:type/:name/publish for every type; SysMetadataRepository.promoteDraft throws status 404 / code NO_DRAFT when there is nothing to promote) — either is a pass provided the published set carries the verb. Read the code field, not the error message: the message is prose, and a quoted message spelling goes stale without the refusal changing",
18191819
"evidence": "PUT + publish + GET"
18201820
},
18211821
{
@@ -1862,6 +1862,7 @@
18621862
"objectui packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx (type=permission editor; client.save → PUT /api/v1/meta/permission/<name>; object CRUD+VAMA+lifecycle + field R/W)",
18631863
"packages/spec/src/security/permission.zod.ts#PermissionSetSchema (PermissionSetSchema)",
18641864
"packages/rest/src/rest-route-ledger.ts#REST_ROUTE_LEDGER (PUT /api/v1/meta/:type/:name saveItem; POST .../publish publishItem — re-pointed #18104: saveItem is a CLIENT METHOD name, carried in this file only inside a string value)",
1865+
"packages/metadata-protocol/src/sys-metadata-repository.ts#promoteDraft (the publish-with-nothing-pending refusal: status 404, code NO_DRAFT)",
18651866
"examples/app-showcase/src/security/permission-sets.ts",
18661867
"cross-ref access-security.crud-permission-matrix + access-security.readonly-package-locks-studio, ADR-0090 D1/D5, ADR-0033"
18671868
],
@@ -1872,7 +1873,8 @@
18721873
"change": "new — permission-matrix WRITE/edit loop: flip a verb in the console matrix → publish → affected persona's live API access flips, revoke flips back, assign/unassign moves access (enforcement + lock covered by sibling items)",
18731874
"ref": "claude/platform-test-checklist-ocwugl"
18741875
},
1875-
{ "revision": 2, "date": "2026-08-18", "change": "corrected clause 1, which demanded a 2xx from POST /api/v1/meta/permission/S/publish. That route is mounted type-generically, but it answers 404 [no_draft] when no pending draft exists — and the console's permission save is a plain PUT that lands state:'active' directly, so there is nothing to promote. As written the clause was unsatisfiable on the path the item's own fixture block describes (#9453 CF-5). The clause now keys on the published set carrying the verb and records which save path was taken", "ref": "#9386" }
1876+
{ "revision": 2, "date": "2026-08-18", "change": "corrected clause 1, which demanded a 2xx from POST /api/v1/meta/permission/S/publish. That route is mounted type-generically, but it answers 404 [no_draft] when no pending draft exists — and the console's permission save is a plain PUT that lands state:'active' directly, so there is nothing to promote. As written the clause was unsatisfiable on the path the item's own fixture block describes (#9453 CF-5). The clause now keys on the published set carrying the verb and records which save path was taken", "ref": "#9386" },
1877+
{ "revision": 3, "date": "2026-09-23", "change": "re-keyed clause 2 and its verify on the refusal's code axis: the nothing-to-publish answer is now stated as HTTP 404 with code NO_DRAFT instead of quoting a bracketed lowercase opener inside the message. That opener was retired from every metadata-protocol refusal in 7a25a3ee9c (error is human language, code is the machine token), so a checker matching the old quoted spelling against a live answer recorded a false failure. Pass/fail semantics are unchanged; the producer is now cited in source", "ref": "#19705" }
18761878
]
18771879
},
18781880
{

0 commit comments

Comments
 (0)