Skip to content

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

Description

@os-zhuang

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-131 says per-file manifest.integrity re-verification at unpack is the cloud control plane's obligation. The cloud repo's plugin-artifact.ts:9-11 and docs/design/plugin-distribution-cloud.md say it is done by the runtime at unpack. Neither side unpacks anything today (#13563 measured: the .osplugin blob 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

Out of scope

Acceptance

  • manifest.zod.ts no longer asserts a cloud-side unpack verification
  • ADR / ledger text agrees with the TSDoc
  • changeset present

Activity

  1. self-assigned this
    on Sep 7, 2026
  2. huangyiirene commented on Sep 7, 2026

    @huangyiirene
    Collaborator

    Claim:

    • session: session_01T6HeZvT9wdSJD1ZxJb5Eno (PM dispatch, domain:spec execution 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/main moves 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-131 for the integrity TSDoc. At this sha, :120-140 is PluginRuntimeSchema's describe and PluginPackagingSchema; 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 lands

    The 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/spec PR ⇒ 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-zhuang or hotlong). 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.ts or docs/adr/0025-*: #15867 holds ai/knowledge-source.zod.ts, #16293 holds contracts/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 means manifest.zod.ts neighbours have moved recently; rebase awareness rather than a blocker.


    Generated by Claude Code

  3. huangyiirene commented on Sep 7, 2026

    @huangyiirene
    Collaborator

    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

  4. removed their assignment
    on Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions