Skip to content

Commit 4c0b22b

Browse files
os-litantclaude
andauthored
fix(runtime): strip read-time decorations before the route-level seed apply's closed parse (#16162)
* fix(runtime): strip read-time decorations before the route-level seed apply's closed parse `POST /packages/:id/publish-drafts` reads each just-published `seed` body back through `protocol.getMetaItem` and hands it to `SeedLoaderRequestSchema`. That read exits through `decorateMetadataItem`, which stamps `_diagnostics` on every body whose type has a registered schema, and `SeedSchema` is closed — so the door refused the document it had just served, on a 200, as `seedApplied.error`: zero rows loaded and the author told their seed body failed spec validation. The direction is settled by the contract, not by judgement. `METADATA_READ_DECORATIONS` declares `_diagnostics` a key the read path derives and attaches to the response, and its module names "any re-parse of a served document" as a consumer that must strip. `_packageId` is deliberately NOT a member and `SeedSchema` accepts it via `MetadataProtectionFields` — measured on the real producer, the served body carries both and the schema refuses exactly one. So the fix is the declared helper, not a widened schema and not the export path's blanket underscore strip, which would drop provenance this schema allowlists on purpose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * chore(gates): re-anchor the system-context census and record the new engine double Both are mechanical regenerations the gates asked for by name, not hand edits: - `check:check-system-context-census` reported pure LINE ROT — the three `domains/packages.ts` anchors on `content/docs/permissions/system-context.mdx` each moved by exactly the +9 lines this branch's import block added. Repaired with the gate's own `--fix`; it now reports OK over 105 sites and 140 anchors. - `check:engine-double-contract` reported the new test file's engine double as RETAINED-but-unrecorded on all three scanned verbs. Regenerated with `--write` (736 rows, 3 added, 0 lost), so the pin protects the file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * chore(gates): re-anchor the system-context census after merging origin/main The merge brought main's re-anchoring of `system-context.mdx` and this branch's own +9-line import block in `domains/packages.ts`. `merge=os-regen` resolved the page with exit 0 and no markers while silently keeping one side, so the page had to be regenerated from the merged tree rather than text-merged. `pnpm gen:system-context-census` (= `check-system-context-census.mjs --fix`) rewrote 3 anchors, all `domains/packages.ts`, all by exactly +9: `:241`->`:250`, `:274`->`:283`, `:543`->`:552`. Delta is line-anchor numbers only: with every digit run normalized the page is byte-identical before and after, at the same 423 lines and the same 65 rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 159dbad commit 4c0b22b

5 files changed

Lines changed: 497 additions & 3 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/runtime": patch
3+
---
4+
5+
The package-publish door's route-level seed apply can consume the platform's own read-back envelope again.
6+
7+
`POST /packages/:id/publish-drafts` reads each just-published `seed` body back through `protocol.getMetaItem` before handing it to the seed loader. That read exits through `decorateMetadataItem`, which stamps `_diagnostics` on every body whose metadata type has a registered schema — `seed` has one — and `SeedSchema` has been closed since protocol 17. So the door refused the document it had just been served: `unrecognized_keys: ["_diagnostics"]`, minted as a 422 and delivered on a **200** as `seedApplied.error`. Zero rows loaded, and the author was told their seed body failed spec validation when nothing about it was wrong.
8+
9+
The read-back is now passed through `stripReadDecorations` at the unwrap — the same helper, for the same reason, that the dataset query, the cold-boot flow bind and `saveMetaItem`'s verbatim persist already call. `METADATA_READ_DECORATIONS` is the declared list of keys the read path derives from a document and attaches to the *response*, so removing them restores the document the author actually wrote.
10+
11+
Nothing is widened to accept them: `SeedLoaderRequestSchema` stays closed, and the publish response keeps its declared shape. The strip is deliberately **not** a blanket `startsWith('_')` sweep — the ADR-0010 protection envelope (`_packageId`, `_provenance`, …) is not a read decoration, and the metadata schemas allowlist it precisely so a served document keeps its provenance when it is parsed again.
12+
13+
Only protocols that do not self-apply seeds inside `publishPackageDrafts` reach this path; the shipping protocol self-applies and was never affected.

content/docs/permissions/system-context.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,10 @@ The largest single consumer — **17 of the 105 sites**.
160160
| 49 | Action `requiredPermissions` bypassed | runtime | Get: engine self-invocation runs any action | `action-execution.ts:401` |
161161
| 50 | `manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability | `domains/meta.ts:471`, `:874`, `rest-server.ts:5319`, `:6766`, `:7014`, `:7445`, `:7638` |
162162
| 51 | The shared metadata-write verdict itself returns `allowed` | metadata-core | Get: the one function all of row 50's doors consult answers yes before any capability is examined | `meta-write-capability.ts:134` |
163-
| 52 | Anonymous-deny seam satisfied on the domain dispatchers and the package/federation routes | runtime, rest | Get: passes with no `userId` | `domains/actions.ts:421`, `domains/ai.ts:60`, `domains/automation.ts:989`, `domains/meta.ts:232`, `domains/security.ts:78`, `domains/packages.ts:543`, `external-datasource-routes.ts:302`, `package-routes.ts:97` |
163+
| 52 | Anonymous-deny seam satisfied on the domain dispatchers and the package/federation routes | runtime, rest | Get: passes with no `userId` | `domains/actions.ts:421`, `domains/ai.ts:60`, `domains/automation.ts:989`, `domains/meta.ts:232`, `domains/security.ts:78`, `domains/packages.ts:552`, `external-datasource-routes.ts:302`, `package-routes.ts:97` |
164164
| 53 | MCP principal check satisfied | runtime | Get: MCP surface reachable with no user | `domains/mcp.ts:61` |
165165
| 54 | Package REST route capability gate bypassed | rest | Get: package read/write over REST without `manage_metadata` / `studio.access` / `setup.access` | `package-routes.ts:102` |
166-
| 55 | Package domain capability gates bypassed | runtime | Get: package management and package-inventory reads without the capability | `domains/packages.ts:241`, `:274` |
166+
| 55 | Package domain capability gates bypassed | runtime | Get: package management and package-inventory reads without the capability | `domains/packages.ts:250`, `:283` |
167167
| 56 | Activation write / authoring refusals do not fire | runtime | Get: activation artifacts writable and authorable without the activation-authoring capability | `activation-gate.ts:138`, `:189` |
168168
| 57 | Automation run-state read, flow-authoring write and unrelated-screen read all pass | runtime | Get: run state, flow writes and screen reads with no grant | `domains/automation.ts:254`, `:545`, `:635` |
169169
| 58 | Audience-binding suggestion recording skipped | plugin-security | Lose: install-time suggestions are not recorded for system callers | `suggested-audience-bindings.ts:703` |

0 commit comments

Comments
 (0)