Skip to content

docs(spec): manifest.integrity TSDoc stops asserting an unpack verification nobody performs - #16532

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-16333-manifest-integrity-tsdoc-unpack
Sep 7, 2026
Merged

huangyiirene merged 2 commits into
mainfrom
claude/issue-16333-manifest-integrity-tsdoc-unpack

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Landing path: ORDINARY packages/spec PR — this diff touches NO governed surface, so it is armable in the usual way and does not need a human merge.

docs/adr/0025-plugin-package-distribution.md is deliberately not edited; the reasoning is in "Why the ADR needed no edit" below. Checked mechanically against the register rather than recalled: today's GOVERNED_SURFACES is docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md, and the four changed paths hit none of them.

Fixes #16333

Clause-②: no — prose only. No export added, removed or narrowed; no authorable key moved; no accept-set movement. Proven with a two-direction ablation below rather than read off a bare green.

What was wrong

The integrity TSDoc said per-file re-verification at unpack is the cloud control plane's obligation. The cloud repo's documentary half says it is the runtime's. Neither side unpacks anything, so two published texts each pointed at the other and a reader of either learned that a verification exists when none does. Because this text ships inside @objectstack/spec's .d.ts, the wrong claim reached every consumer that hovered the field.

Measured in this checkout at 3335bd35eaf47a5b2aed9120e217dafd242ff7e1:

  • computeIntegrity is defined at packages/cli/src/utils/osplugin.ts and has exactly one production caller, packages/cli/src/commands/plugin/build.ts.
  • verifyIntegrity has exactly one production caller, the os plugin publish preflight (packages/cli/src/commands/plugin/publish.ts).
  • There is no os plugin install — packages/cli/src/commands/plugin/ holds build.ts, publish.ts, sign.ts and nothing else.
  • The archive reader readTarGz's only production caller is that same publish path, reading back its own output. Nothing in this repo unpacks an installed artifact.

Cloud's half is not re-measured here; this session has no access to that repo. The cloud-side reading is inherited from #13563 and labelled as inherited, and cloud's documentary half stays with #13563 by the same ruling.

The line anchor had drifted

The card cites manifest.zod.ts:129-131. At the anchor sha that range is PluginRuntimeSchema's describe and PluginPackagingSchema. Re-located by symbol, the docblock is at :139-145, and there is a second one the card's line cite does not reach: the integrity field's own TSDoc inside ManifestSchema, at :748-753, carrying the same false attribution. Both are rewritten — acceptance asks that manifest.zod.ts no longer assert a cloud-side unpack verification, and one of the two would have left the claim shipping.

What the TSDoc says now

Why the ADR needed no edit

The card makes ADR correction conditional on its prose stating step 5 as done. It does not — it states it as designed:

  • The ADR's own Status line already reads: "the install flow (§3.5) ... remain unimplemented". That is the only implementation-status assertion in the file, and it is correct.
  • The four unpack mentions are all design mood: :118 is the §2 Decision statement, :171 describes the bundled packaging strategy, :236 is step 5 of the §3.5 numbered design list, and :448 is a §5 projected consequence. None claims the step is built.
  • §3.5 also corroborates the correction: its steps 4-7 assign download-and-verify, materialize, register and load to the installing host, never to the control plane. The old TSDoc contradicted the ADR; the new one agrees with it.

So the ADR is left alone rather than touched for thoroughness, and this PR stays off the governed path.

Liveness ledger

The card names the ledger alongside the ADR. No row states step 5 as done — but two carried the same wrong owner, which acceptance ("ADR / ledger text agrees with the TSDoc") does not allow to stand:

The row's status (dead) and verifiedAt (2026-08-30) are deliberately unchanged. This is a prose correction, not a re-measurement, and moving verifiedAt would falsely claim a fresh liveness reading.

Verification

Base 3335bd35eaf47a5b2aed9120e217dafd242ff7e1; all readings below taken at head 9fd63b161d.

Generated artifacts — zero movement. pnpm --filter @objectstack/spec check:generated (after a real build) reports "All 15 generated artifacts are up to date". git diff --name-only BASE..HEAD lists exactly the four changed files, none of them generated. Nothing was hand-edited and nothing needed regenerating: TSDoc feeds no artifact, only .describe() does, and no .describe() was touched.

Measured generated-artifact footprint, since the card asks for it tracked vs gitignored:

tree files
api-surface/ 17 tracked
export-origins/ 17 tracked
authorable-surface/ 14 tracked
declaration-map/ 14 tracked
authorable-defaults/ 13 tracked
4 singletons + authorable-surface.base.json + docs/protocol-upgrade-guide.md 6 tracked
content/docs/references/ 228 tracked
packages/spec/json-schema/ 1576 gitignored (.gitignore:63)

Tracked total is 309, not "~14" — that figure is exactly the authorable-surface/ shard count, which misses the other four sharded directories, the singletons, the 228-file references tree, and the whole 1576-file gitignored json-schema/ tree.

Clause ② — two-direction ablation, no rebuild between mutation and check. The confound is real: pnpm --filter @objectstack/spec build regenerates the gates' own baselines, so a mutate-build-check leg comes back falsely green. Avoided by mutating the checked-in baseline instead of the source, which needs no rebuild at all. Each leg proved its mutation reached disk by git hash-object differing from the HEAD blob, restored with git checkout HEAD -- PATH, and proved restoration by the hash returning to the HEAD blob.

gate leg verdict exit
check:api-surface drop a real export from the baseline 0 breaking (removed/narrowed), 1 added 1
check:api-surface add a phantom export to the baseline 1 breaking (removed/narrowed), 0 added 1
check:authorable-surface drop kernel/ValidationWarning:message authorable-surface/ is out of date (1 key(s) not recorded) 1
check:authorable-surface add kernel/OsAblationPhantomDef:phantomProp 1 authorable key(s) disappeared from the contract 1

The two legs of each gate produce opposite diagnostics, so this is a real two-direction proof and not merely two reds.

One leg was discarded as vacuous rather than banked: a first attempt at the authorable-surface add-direction used the key OsAblationPhantom.key, which the gate rejected as malformed ("cannot shard ... has no category segment"). That red graded my fixture's shape, not the ratchet, so the leg was re-run with a well-formed key and only the re-run is reported above.

Clean verdicts on the unmutated tree, exit codes captured before any pipe:

  • check:api-surface — exit 0, "public API surface + factory signatures unchanged"
  • check:authorable-surface — exit 0, "authorable-defaults/ verified against upstream 3335bd3 — 1211 default(s) unchanged"

Tests, typecheck, gates.

  • pnpm --filter @objectstack/spec test — 483 files / 13136 tests passed, lock verdict command-exit 0
  • pnpm --filter @objectstack/spec typecheck — exit 0
  • node scripts/pm/dispatch-gates.mjs --ran — 69 derived families, 69 run, 0 UNRUN
  • pnpm lint (repo-wide eslint . --no-inline-config) — exit 0, so the whole-repo scan is a real reading here, not a narrowing
  • pnpm check:nul-bytes green, plus a direct control-character scan over the three edited files: no matches

One family is NOT MEASURED, not passed. pnpm check:dual-build-cjs-loads exits 3 — PREREQUISITE NOT MET, because it reads built output for ~87 packages and only the @objectstack/spec closure was built here. Its own text says "This is NOT a pass: nothing was measured." CI builds everything and will measure it. (check:doc-formula-expressions also exited 3 at first; that one was cheap to satisfy, so @objectstack/formula and @objectstack/lint were built and it was re-run to a real exit 0.)

Out of scope, filed separately

The same false attribution lives at five more sites outside this card's scope, in packages/cli and packages/core. They are deliberately not touched here: editing them would pull two more packages into the verification surface for a prose card. Filed for triage rather than ridden along.


Generated by Claude Code

The `integrity` TSDoc asserted that per-file re-verification at unpack is
the cloud control plane's obligation. The cloud repo's own docs assert it is
the runtime's. Neither side unpacks anything, so two published texts pointed
at each other and a reader of either learned that a verification exists.

Rewrite both `integrity` docblocks in `manifest.zod.ts` to state what is
true: the map is computed by `os plugin build` and self-checked by the
`os plugin publish` preflight; unpack-time re-verification is NOT
implemented and is owned by the future runtime loader (ADR-0025 §3.5
steps 4-7), not by the control plane. The enforce leg points at #11331.

Correct the same attribution in the liveness ledger row and its README note
so the ledger text agrees with the TSDoc. The row's `status` (dead) and
`verifiedAt` are untouched: this is a prose correction, not a re-measurement.

ADR-0025 needs no edit — its status line already records the install flow
(§3.5) as unimplemented, and §3.5 assigns steps 4-7 to the installing host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 1 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/manifest.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/getting-started/quick-reference.mdx (via ManifestSchema (symbol, a top-level const object))
  • content/docs/plugins/development.mdx (via ManifestSchema (symbol, a top-level const object))
  • content/docs/protocol/kernel/plugin-spec.mdx (via ManifestSchema (symbol, a top-level const object))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via ManifestSchema (symbol, a top-level const object))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/manifest.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2a2f39b72a4354faf9dc4003da4337e7e25409e3 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 57af7ef2e261e9386ec628543fcd953e2811b465 — the merge of head 9fd63b161dc831a0ca15f73f5d6d945d41140480 into base 2a2f39b72a4354faf9dc4003da4337e7e25409e3, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 57af7ef2e261e9386ec628543fcd953e2811b465 && git checkout 57af7ef2e261e9386ec628543fcd953e2811b465
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2a2f39b72a4354faf9dc4003da4337e7e25409e3 9fd63b161dc831a0ca15f73f5d6d945d41140480 && git checkout -B drift-repro 2a2f39b72a4354faf9dc4003da4337e7e25409e3 && git merge --no-ff 9fd63b161dc831a0ca15f73f5d6d945d41140480

node scripts/docs-audit/affected-docs.mjs --json 2a2f39b72a4354faf9dc4003da4337e7e25409e3

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 2a2f39b72a4354faf9dc4003da4337e7e25409e3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@huangyiirene
huangyiirene marked this pull request as ready for review September 7, 2026 08:50
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 7, 2026
Merged via the queue into main with commit b60f48b Sep 7, 2026
36 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-16333-manifest-integrity-tsdoc-unpack branch September 7, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: manifest.zod.ts integrity TSDoc must stop asserting an unpack-time verification nobody performs (spec half of #13563)

2 participants