docs(spec): manifest.runtime trust-tier text states publish-gate-only enforcement truthfully - #13479
Conversation
… enforcement truthfully The `manifest.loading` tombstone redirected upgrading authors to the plugin trust tier as something "which are enforced". Measured, nothing in this repo dispatches on the tier — its only reads are two CLI progress lines that echo the value — so the redirection pointed at a key with no local enforcement: ADR-0049 false compliance with a shipped migration message attached. The truthful statement is a split, now carried in all four places the claim ships (tombstone prescription, PluginRuntime enum describe, manifest.runtime field describe, ADR-0087 D3 entry -> protocol-upgrade-guide): - enforced at the cloud marketplace publish gate (unverified publisher requesting the `node` tier -> HTTP 422 + forced manual review), which is why the key is not retired; - not enforced at load, so a locally installed plugin is not isolated by the tier it declares. Text only: no schema shape and no accept/reject change (check:authorable-surface green, accept-set pinned). The permissions half of the tombstone sentence is preserved verbatim and rides with the permissions card. Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 126 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 760e15935f8320bdb09a1589b7a0a3232acb881c && git checkout 760e15935f8320bdb09a1589b7a0a3232acb881c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8fcd0816dadc10c66aa7a5d2fa5d8b57e7844344 15963192715b505a2b2a82c9430e683ee0a80a47 && git checkout -B drift-repro 8fcd0816dadc10c66aa7a5d2fa5d8b57e7844344 && git merge --no-ff 15963192715b505a2b2a82c9430e683ee0a80a47
node scripts/docs-audit/affected-docs.mjs --json 8fcd0816dadc10c66aa7a5d2fa5d8b57e7844344
|
Fixes #11330
Executes the maintainer ruling of 2026-08-30 (verbatim「同意」): option B now — make the text true. Option A (enforce the tier at load) is a v18 direction and is deliberately not built here; option C (retire the key) was ruled out, because the cloud marketplace publish gate is a real consumer.
The defect
manifest.loading's tombstone redirected every upgrading author, verbatim:Measured, that redirection pointed at a key this repo does not enforce at all. An author who followed the prescription got the same nothing they were being warned about, one key over — ADR-0049 false compliance with a shipped migration message attached.
Premise re-check (all three hold, re-measured on
origin/mainthis run)git grep -n "which are enforced" origin/main -- packages/spec/src/kernel/manifest.zod.tsmanifest.zod.ts:569git grep -nE "runtime === '(sandbox|node|worker)'" origin/main -- packages/ | grep -v testliveness/README.md,liveness/manifest.jsonquoting the pattern); restricted topackages/**/*.ts(x)/jsthe count is 0. Control: the sibling keypackagingdoes dispatch (cli/src/commands/plugin/build.ts:158,202), so the probe is not blind to this file.plugin-permission-audit.ts— 422 +requiresManualReview). ⛔ No new cloud read taken; this board has no cloud access.The only reads of the tier remain two CLI progress lines that echo the value (
plugin/build.ts:127,plugin/publish.ts:94).What changed — text only
The truthful statement is a split, and both halves ship together everywhere the claim appears:
nodetier is hard-rejected (HTTP 422) and forced to manual review.Corrected in all four places the claim is published:
packages/spec/src/kernel/manifest.zod.ts— theloadingtombstone prescriptionpackages/spec/src/kernel/manifest.zod.ts— thePluginRuntimeenum describe + TSDocpackages/spec/src/kernel/manifest.zod.ts— themanifest.runtimefield describe + TSDocpackages/spec/src/migrations/entries/semantic/17.plugin-manifest-loading-retired.ts— the ADR-0087 D3 entryPlus the
packages/spec/liveness/ledger row and README narrative, brought to the same reading (the row's"READ TWICE, ENFORCED NOWHERE"headline and itsSCOPE LIMIT — cloud unmeasuredclause are both falsified by the #12400 cloud reading).⭐ Coordination pin with #11333 — the permissions half is untouched
The ruling splits that one tombstone sentence: the trust-tier half is this card, the permission declarations half rides with #11333. This PR corrects only its own half and preserves the permissions clause verbatim —
the permission declarations, which are enforced. A dedicated pin test asserts it is still there, so when #11333 corrects it the handoff shows up as a red pin rather than a silent overwrite.Declared scope extension (bounded in-place fix)
Item 4 and the liveness files are outside the ruling's named landing point (
manifest.zod.ts) and are declared here rather than done quietly. They are the same claim shipped a second time: the D3 entry renders intodocs/protocol-upgrade-guide.md, so fixing only the schema would have left the upgrade guide still calling the tier a surface "the platform actually enforces" — a half-fix that leaves the repo self-contradictory on a security-shaped claim. Boundary scan:git grep -rln "which are enforced"→ the remaining hits arepackages/spec/CHANGELOG.md(a different subject —skill.active, unrelated) and the regeneratedcontent/docs/references/**. No open PR touches any file in this diff (checked against the full open-PR file set).Verification
Full log excerpts are in the report comment on #11330. Union re-run on the final commit
15963192:pnpm --filter @objectstack/spec check:generated→✓ All 14 generated artifacts are up to date.(exit 0)pnpm --filter @objectstack/spec test→Test Files 443 passed | 1 skipped,Tests 11815 passed | 1 skipped(exit 0)pnpm --filter @objectstack/spec typecheck→ exit 0, includingcheck:test-typecheck: OK — @objectstack/spec's test layer compiles(so the new test file is genuinely covered, not silently excluded)scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, from the real change set):check:doc-authoring,check:doc-anchors,check:docs-single-h1,check:doc-security-posture,check:changeset-gate-self-tests,check:pm-half-states,check:objectui-changeset,check:test-source-alias,check:spec-parsed-alias,check:empty-state,check:llms-txt,check:skill-examples,check:role-word,check:docs-audit-scope,check:quick-reference-counts,check:docs-redirects— all exit 0.node scripts/check-nul-bytes.mjs→ OK, plus a control-byte self-scan over the diff.PREREQUISITE NOT MET(unbuilt@objectstack/lint/@objectstack/client-react) — recorded as NOT MEASURED, not as failures; both were rebuilt and re-run to a real green.Byte-invariance
This is a text change and the accepted key set is byte-identical:
check:authorable-surfaceis green andpackages/spec/authorable-surface/**is absent from the diff. A pin test asserts the behaviour directly —node/sandbox/workerall still parse, unset still parses,'isolated'is still refused.Reverse verification (predicted direction: RED)
Restored the retracted claim into the tombstone on top of the commit, with the mutation proven on disk before reading any result — HEAD blob
88c8f73dvs mutatedcf9c6e38, injected-marker count1 → 0, restored-claim count0 → 1. The tombstone pin went red (Tests 1 failed | 5 passed); the five describe/behaviour pins correctly stayed green, since the ablation reverted only the tombstone. Restore verified by state —git diff HEADempty — not by an exit code. The pins resolve through relative source imports (./manifest.zod), so this ablation acts onsrc/directly and has nodist/leg to rebuild.Review posture
Clause ② path limb fires (
packages/spec/src/**), so this PR parks as DRAFT withneeds:contract-reviewon both carriers; the review chain owns enqueue. The content limb reads as not firing — no accept/reject behaviour moves — but that is the reviewer's call, not mine, and the evidence for it is the byte-invariance section above.One question I could not settle inside a text card, flagged for the review chain: the liveness row still carries
"status": "dead". That is now only true of load-side dispatch in objectstack+objectui — the key has a real cross-repo consumer. I left the verdict alone and corrected the prose to say so explicitly, becauseliverequires an evidence pointer the scan resolves to a local file and cloud's gate is not local. The ledger may want a vocabulary for "enforced in a sibling repo"; that is its own decision, not this one.Generated by Claude Code