Repository navigation
spec: manifest.zod.ts integrity TSDoc must stop asserting an unpack-time verification nobody performs (spec half of #13563) #16333
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationpriority:p2Medium: important, M3Medium: important, M3
on Sep 6, 2026 Claim:
- session:
session_01T6HeZvT9wdSJD1ZxJb5Eno(PM dispatch,domain:specexecution seat) - branch:
claude/issue-16333-manifest-integrity-tsdoc-unpack - dispatched: 2026-09-07T07:10Z
The dev inherits this claim and the assignee. ⛔ It posts no second
Claim:, and never writes the assignee field.
PM pre-dispatch readings, anchored to
3335bd35eaf47a5b2aed9120e217dafd242ff7e1⚠️ origin/mainmoves under sibling fetches in this container — never anchor by name.1. The card's line anchor has DRIFTED
The card cites
manifest.zod.ts:129-131for theintegrityTSDoc. At this sha,:120-140isPluginRuntimeSchema's describe andPluginPackagingSchema; the integrity docblock (Per-file content digests of the packaged artifact (ADR-0025 §3.2,) begins around:139.⛔ Re-locate by symbol, not by line number, and report what you actually find.
2.
⚠️ The governed-surface half is LIVE and UNRESOLVED — this changes how the PR landsThe card says: "If the ADR-0025 prose or a liveness ledger row states step 5 as done, correct it in the same PR (ADR edits are governed surface — the PR lands by human merge)."
That condition is not vacuous.
docs/adr/0025-plugin-package-distribution.md(521 lines) carries unpack prose at:118,:171,:236— "5. Materialize. Unpack to a per-environment, per-plugin dir" — and:448.⛔ I have not judged whether that prose asserts step 5 as done rather than as designed. That reading is the dev's, and it decides the PR's landing path:
- ADR untouched ⇒ ordinary
packages/specPR ⇒ this seat verifies, flips ready, arms, and the merge queue lands it. - ADR touched ⇒ governed surface. ⛔ This seat may not approve it and may not merge it, and ⛔ will not arm it. It lands by human merge only (
os-zhuangorhotlong). That is not a delay to route around — it is the rule.
⇒ Report which path you are on, explicitly and early. If it is the governed path, say so in the PR body's first paragraph so no reviewer mistakes it for an armable PR.
3. Serial constraint — clear
No in-flight claim in this lane names
packages/spec/src/kernel/manifest.zod.tsordocs/adr/0025-*: #15867 holdsai/knowledge-source.zod.ts, #16293 holdscontracts/ai-service.ts+ai/tool.zod.ts+ the error ledger.⚠️ #16327 landed manifest-surface prose two hours ago (merged, so no live conflict) — but it meansmanifest.zod.tsneighbours have moved recently; rebase awareness rather than a blocker.
Generated by Claude Code
- session:
os-dev-report
{ "issue": 16333, "status": "done", "branch": "claude/issue-16333-manifest-integrity-tsdoc-unpack", "pr": "https://github.com/objectstack-ai/objectstack/pull/16532", "premise_still_valid": true, "summary": "LANDING PATH: ORDINARY packages/spec PR, NOT governed — safe for the PM seat to verify, flip ready and arm. ADR-0025 is deliberately untouched because its prose states step 5 as DESIGNED, not done: its Status line already records 'the install flow (3.5) ... remain unimplemented', and all four unpack mentions are design mood (:118 the section-2 Decision, :171 the bundled packaging strategy, :236 step 5 of the numbered design list, :448 a section-5 projected consequence). Section 3.5 in fact CORROBORATES the correction: steps 4-7 assign download-and-verify, materialize, register and load to the installing host, never to the control plane, so the old TSDoc contradicted the ADR and the new one agrees with it. Rewrote BOTH integrity docblocks in packages/spec/src/kernel/manifest.zod.ts: PluginIntegritySchema, re-located by symbol to :139-145 (the card's :129-131 had drifted, as PM flagged), AND a SECOND docblock the card's line cite does not reach — the integrity field's own TSDoc inside ManifestSchema at :748-753 — which carried the same false attribution; fixing only the first would have left the claim shipping. Both now state that the map is computed by 'os plugin build' and self-checked by the 'os plugin publish' preflight (#13464), that unpack-time re-verification is NOT implemented, that it is owned by the future runtime loader (ADR-0025 3.5 steps 4-7) and NOT by the control plane, plus a one-line pointer to #11331. LIVENESS LEDGER: no row states step 5 as done, but two carried the same wrong owner, which acceptance criterion 'ADR / ledger text agrees with the TSDoc' does not allow to stand — liveness/manifest.json's integrity note said the leg 'remains the cloud control plane's obligation', and liveness/README.md's manifest row said 'nothing computes or checks them' (stale since #13464). Both corrected. 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. The README Notes cell is hand-written by design — readme-table.mts generates the row set and counts and states the Notes content is deliberately never generated — so this is not a hand-edited generated artifact. IN-REPO MEASUREMENT at 3335bd35eaf47a5b2aed9120e217dafd242ff7e1: computeIntegrity has one production caller (plugin/build.ts); verifyIntegrity has one (the publish preflight); there is NO 'os plugin install' (the command dir holds build/publish/sign only); readTarGz's only production caller is that same publish path reading back its own output. CLOUD: not read, cloned or edited — out of reach for this session; the cloud-side reading is cited as INHERITED from #13563 and labelled as such in the PR body, never re-measured here. Assignee arrived already set by the PM dispatch and was never written by me; no second Claim was posted.", "tests": "BASE pinned to the literal sha 3335bd35eaf47a5b2aed9120e217dafd242ff7e1 (equal to origin/main at worktree creation); every proof anchored to it, never to the moving ref. os-regen-merge.sh deliberately NOT used. Final 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 4 changed files, none generated. Nothing needed regenerating because TSDoc feeds no artifact — only .describe() does, and no .describe() was touched. FOOTPRINT (card asked, tracked vs gitignored): TRACKED 309 files = api-surface 17 + export-origins 17 + authorable-surface 14 + declaration-map 14 + authorable-defaults 13 + 4 singletons + authorable-surface.base.json + docs/protocol-upgrade-guide.md (6) + content/docs/references 228. GITIGNORED: packages/spec/json-schema/ = 1576 files on disk (.gitignore:63). The sibling card's '~14' is exactly the authorable-surface shard count, missing four other sharded dirs, the singletons, the 228-file references tree and the whole 1576-file gitignored tree. CLAUSE 2 = no, proven by TWO-DIRECTION ABLATION with NO REBUILD between mutation and check: the stated confound is real (build regenerates the gates' own baselines), so I mutated the CHECKED-IN BASELINE rather than the source, which needs no rebuild at all. Each leg proved its mutation reached disk via git hash-object differing from the HEAD blob (empty/equal hash treated as FAILURE, not as nothing-to-compare), restored with 'git checkout HEAD -- PATH' (never bare checkout), and proved restoration by the hash returning to the HEAD blob plus an empty 'git diff HEAD'; the script carried a trap on EXIT INT TERM with absolute paths. Results, all exit 1 with OPPOSITE diagnostics per gate: check:api-surface drop-a-real-export => '0 breaking (removed/narrowed), 1 added'; check:api-surface add-a-phantom-export => '1 breaking (removed/narrowed), 0 added'; check:authorable-surface drop kernel/ValidationWarning:message => 'authorable-surface/ is out of date (1 key(s) not recorded)'; check:authorable-surface add kernel/OsAblationPhantomDef:phantomProp => '1 authorable key(s) disappeared from the contract'. ONE LEG DISCARDED AS VACUOUS RATHER THAN BANKED: my first authorable add-leg used the malformed key 'OsAblationPhantom.key', and the gate's red was 'cannot shard ... has no category segment' — that 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. CLEAN VERDICTS (exit code captured before any pipe, via redirect-then-capture): check:api-surface exit 0 'public API surface + factory signatures unchanged'; check:authorable-surface exit 0 'authorable-defaults/ verified against upstream 3335bd35eaf4 — 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; dispatch-gates.mjs --ran reconciliation = 69 derived families, 69 run, 0 UNRUN; 'pnpm lint' repo-wide (eslint . --no-inline-config) exit 0 in 77s, so the whole-repo scan is a REAL reading here, not a declared narrowing; check:nul-bytes green plus a direct control-character scan over the three edited files with zero matches. NOT MEASURED (never a pass, never a finding): 'pnpm check:dual-build-cjs-loads' exits 3 PREREQUISITE NOT MET — it reads built output for ~87 packages and only the spec closure was built; 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 prerequisite was cheap, so @objectstack/formula and @objectstack/lint were built and it was re-run to a real exit 0. All heavy build/test ran through scripts/pm/os-verify-lock.sh with a stable slot; verdicts read off the printed VERDICT line, never a bare dollar-question-mark. UPSTREAM: origin/main advanced 6 commits since BASE; none touches any of my 4 paths, so no merge was taken and the anchoring is intact. A read-back of the PR caught that my changeset commit had not been pushed; it was pushed and get_files now confirms all 4 files including the changeset.", "mcp_calls": "6 — REST was 403 for this container ('GitHub access is not enabled for this session'), so the card body and the claim comment were read through the zero-quota public-repo page payload channel and all git work went over plain git (push works; the container's credentials are asymmetric). The 6 MCP calls are: create_pull_request, search_issues (the one dedup search, declared channel switch from the 403'd REST probe), issue_write (the finding), pull_request_read get, pull_request_read get_files, add_issue_comment (this one).", "open_questions": [], "out_of_scope_findings": [ "filed as #16533: five source comments in packages/cli and packages/core still call unpack-time manifest.integrity re-verification the cloud control plane's obligation (osplugin.ts:18 and :47, plugin/publish.ts:116, core/security/index.ts:38, core/security/plugin-artifact-integrity.ts:11 — the last being the verifier's own module docblock, which explains the module's portability BY the wrong owner). Not fixed here: editing them pulls packages/cli and packages/core into the verification surface of a prose-only PR, which the bounded in-place-fix exemption does not cover. Dedup search ran with a lit positive control (the known-present #16333 came back), and no existing card covers these sites." ] }
Generated by Claude Code
Spec half of #13563, split by director ruling (decision batch #58, 2026-09-06, option C — break the documentary cross-reference now; the runtime loader (option B) is the eventual home of ADR-0025 §3.5 step 5).
Why
packages/spec/src/kernel/manifest.zod.ts:129-131says per-filemanifest.integrityre-verification at unpack is the cloud control plane's obligation. The cloud repo'splugin-artifact.ts:9-11anddocs/design/plugin-distribution-cloud.mdsay it is done by the runtime at unpack. Neither side unpacks anything today (#13563 measured: the.ospluginblob is write-only on cloud, no tar/zlib dependency in either repo,verifyIntegrity's only production caller is the publisher self-check from #13464). Two published texts each point at the other, so an agent or a spec consumer reading either learns that a verification exists.Scope
integrityTSDoc inmanifest.zod.tsto state what is true: the digest is computed and self-checked by the publisher (os plugin publish, Verifymanifest.integrityat publish preflight — the framework leg of #11331 (does not discharge the unpack ruling) #13464); 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.manifest.integritydeclares per-file artifact digests the spec says the runtime re-verifies at unpack — nothing computes them and nothing checks them #11331 so the enforce leg is findable when the loader is built.Out of scope
Acceptance
manifest.zod.tsno longer asserts a cloud-side unpack verification