Uh oh!
There was an error while loading. Please reload this page.
spec(liveness): bring the plugin manifest into the ledger's governed universe - #11338
Merged
Conversation
…universe Seed packages/spec/liveness/manifest.json and add ManifestSchema to SPEC_ONLY_SCHEMAS + GOVERNED, so the ratchet can finally ask who reads the plugin manifest's properties. Extends the universe; no gate weakened. Refs #10728
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. 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): |
os-sam
marked this pull request as ready for review
August 23, 2026 12:24
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 23, 2026
This was referenced Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#10728
Adds
ManifestSchematoSPEC_ONLY_SCHEMAS+GOVERNEDand seedspackages/spec/liveness/manifest.json, so the liveness ratchet can finally ask "who reads this?" about the plugin manifest. This extends the ratchet's universe — a strengthening. No gate was weakened, no manual floor added, no threshold moved.The fork the triage ruling asked about: the escape hatch CAN host it
The scope ruling said to stop and report if
SPEC_ONLY_SCHEMASstructurally could not host the manifest. It can, and this is measured rather than assumed — see the reverse verification below, leg B: with the override entry removed the gate throwsmetadata type 'manifest' has no registered schema, becausegetMetadataTypeSchema('manifest')has nothing to return. The override is the only resolution path, which is exactly the positionqueryandqaare already in.What the gate now measures
livedeadSix containers are drilled (
contributes,capabilities,configuration,navigationContributions,engine,engines);datais a resolved deferral to the governedseedtype rather than a duplicated row set —manifest.data[]isSeedSchema, and the gate enforces key-set equality, so a key added toSeedSchemacannot get a verdict on one side and not the other.Where the rows come from
contributesmembers are Plugin-manifestcontributes.themesdeclares a theme-contribution channel nothing reads — same declared-but-unenforced shape as the retired stackthemescarrier #10627's verdict table, not a fresh measurement — 1 live (kinds), 10 dead. One thing was re-verified rather than copied: Plugin-manifestcontributes.themesdeclares a theme-contribution channel nothing reads — same declared-but-unenforced shape as the retired stackthemescarrier #10627 cited the read site atengine.ts:4499-4501on299b85e9d; on today'smainthe same three lines areengine.ts:4504-4506. The ledger cites the current lines.contributessurface), per-key with controls: the same probe finds real reads of sibling keys —.packaging(cli/src/commands/plugin/build.ts:126),.data(runtime/src/app-plugin.ts:946),.contributes(objectql/src/engine.ts:4504) — so a zero is about the path, not the pattern.../objectuiwas walked for everydeadrow (control:manifest.(id|name|namespace|version)→ 46 hits there, so manifest reads are findable in that repo).cloudis unmeasured, carried unchanged from Plugin-manifestcontributes.themesdeclares a theme-contribution channel nothing reads — same declared-but-unenforced shape as the retired stackthemescarrier #10627 (that repo could not be attached to the measuring session). Everydeadrow is scoped toobjectstack+objectuiand is an enforce-or-remove worklist row, not a licence to delete. Recorded in the ledger's_noteand in each affected row.Dispositions are recorded as PENDING, because they are pending
No
contributestombstone has merged. #10724 (tombstone the nine mechanically-dead members) is open andpm:blockedon the cloud census; #10726 (contributes.routes, recommendation B) routes its removal through #10724. So no row says "retired" — onlyloadingdoes, because its tombstone really landed (#4914). That is deliberate: a ledger note claiming a retirement before the tombstone merges is the stale-note class this lane just spent a day repairing.Reverse verification — does the extension bite?
Predicted direction stated before running; mutation proven on disk in both directions (injected-text and deleted-text hit counts), each leg restored under an
EXIT INT TERMtrap. No build ordistis involved — the gate is run bytsxand imports fromsrc/— so no rebuild is owed.'manifest'fromGOVERNED✗ 1 README state-table row(s) that GOVERNED does not back,✗ 1 row(s) where README.md and state-counts.md disagreemanifest: ManifestSchemafromSPEC_ONLY_SCHEMASError: metadata type 'manifest' has no registered schemamanifest 43 classified (live 22, dead 21), worktree diff emptyLeg A produced one signal beyond the prediction, reported rather than smoothed over:
✗ 1 stale undrilled-container row(s)— themanifest/datadeferral correctly goes stale the momentmanifestleaves the universe.This is also why the PR adds a self-test. Leg A's redness lands on the README row and the count artifact — both repairable by deleting the row and regenerating. Do those three edits together and the gate is green over a ~24-key authoring surface nothing asks about again, which is precisely the state this type was seeded out of. The pin is on membership, not on verdicts, so the ledger's rows stay free to move as the measurement moves.
Findings filed, not fixed
Seeding the ledger is what produced these; none is touched by this PR (all four carry the cloud-measurement precondition):
manifest.runtime— the ADR-0025 §3.6 plugin trust tier — is read only to be echoed in two CLI lines, andloading's own tombstone tells upgrading authors it is enforced #11330 —manifest.runtime, the ADR-0025 §3.6 trust tier, is read only by two CLI lines that echo it; noruntime === 'sandbox'branch exists. Security-shaped becauseloading's own shipped tombstone redirects upgrading authors to it as something "which [is] enforced".manifest.integritydeclares per-file artifact digests the spec says the runtime re-verifies at unpack — nothing computes them and nothing checks them #11331 —manifest.integritydeclares per-file digests the spec says the runtime re-verifies at unpack; nothing computes them and nothing checks them.capabilities,configuration,extensions— have zero reads of the container itself, so all 8 keys beneath them are inert (configuration.properties.secretpromises encryption/masking) #11332 —capabilities/configuration/extensions: zero reads of the container in each case, so all 8 keys beneath are inert.configuration.properties.secretpromises "value is encrypted/masked (e.g. API Keys)".manifest.permissionsis live on its LEGACYstring[]arm only — the structuredPluginPermissionsSchema(services / hooks / network / fs) has zero readers, and new code is told to prefer it #11333 —manifest.permissionsis live on its legacystring[]arm only (the reader is guarded byArray.isArray); the structuredPluginPermissionsSchemathe schema tells new code to prefer has zero readers.Verification
Union re-run at the final commit
f819e4048, both under the shared verify lock (VERDICT command-exit 0):check:liveness→ exit 0, output above.packages/specfull suite → 418 files / 11130 tests passed; the targeted file run names both new cases green (19 passed).node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackon the actual diff, which named more than the dispatch did):check:empty-state,check:strictness-ledger,check:variant-docs,check:merge-driver,check:published-files,check:slot-lookup,check:test-source-alias,check:type-source-resolution,check:nul-bytes,check:query-options-erasure,check:engine-double-contract,check:type-check-coverage,check-ci-filter-parity,check-plugin-teardown-shape,check-affected-docs,pnpm --filter @objectstack/spec typecheck— all exit 0.check-dev-prereqsexits 1 on this worktree, and it is not a finding about this diff — its own output says "1 unmet precondition, not a list of problems" and names 67 of 67 workspace packages as having nodist/, i.e. the worktree was never built. Nothing in this diff produces adist. For the same reasoncheck:type-check-debt --re-measure(which refuses outright on an unbuilt closure) was not run repo-wide; its relevant leg ispnpm --filter @objectstack/spec typecheck, which runstsc --noEmit && check:scripts-typecheck && check:test-typecheckover the package this diff touches and exits 0 — so the new test file type-checks and adds no debt. CI builds before running both.Changeset
None —
skip-changeset, re-derived rather than inherited. The diff is the liveness gate's own tooling plus its ledger; no runtime code, no exported type, and no schema changes (ManifestSchemaitself is untouched). Same shape as PR #11209, which changedpermission.json+proof-registry.mts+ a test and carried no changeset.File surface
Exactly the declared surface —
packages/spec/scripts/liveness/**, the newpackages/spec/liveness/manifest.json, and the ledger index files the gate's own reconciliation requires (README.mdrow + heading, the generatedstate-counts.md). Nopackages/spec/src/**path is touched, so the clause-② path limb does not fire.content/docs/releases/untouched.Generated by Claude Code