docs(spec): the environment artifact's checksum describe states its own coverage boundary (the metadata block only); grantedPermissions states it sits outside the digest by design (#14993) - #16008
Conversation
…ry on the keys it governs
`EnvironmentArtifactSchema.checksum` inherited the shared `Sha256DigestSchema`
describe ("SHA-256 digest (64 hex chars)"), which says what the value is but
nothing about what it covers. The digest is computed over the canonical JSON
serialization of the `metadata` block only, so every other envelope key —
`grantedPermissions` included — rides outside it, and nothing on the surface
said so.
Two describe edits, no contract change:
- `checksum` gets its own field-level `.describe()` (the shared
`Sha256DigestSchema` describe is untouched, so the other digest fields still
inherit it): what it covers first, then what it does not.
- `grantedPermissions`' describe gains one appended sentence group: it sits
beside `metadata`, outside the digest, and integrity of the granted set rests
on the carrier — the artifact is environment-local and control-plane served
(ADR-0003 / cloud ADR-0007).
The accept set of `EnvironmentArtifactSchema` does not move: same keys, same
value schemas, same absent-vs-`{}` semantics.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
`check:doc-authoring` refuses an internal issue id inside `.describe()` prose — that text projects into `content/docs/references/**` and the generated skill artifacts, where a citation-shaped `#NNNN` resolves to nothing for the reader who sees it. The gate's own prescribed route is an adjacent `//` comment, so the card anchor moves there and the describe keeps only the customer-resolvable references (ADR-0003 / cloud ADR-0007). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
…s it The file already pins describe TEXT (the `grantedPermissions` key description is asserted clause by clause, because the manifest-`id`-not-`package_id` assumption is documented nowhere else that a producer reads). The digest coverage boundary is the same kind of fact: no mechanism to pin — `checksum` is computed and verified by the control plane, this envelope only carries it — so the sentence IS the surface, and a sentence with no pin rots silently. Six assertions: `checksum`'s own describe names the `metadata` block as what it covers and says nothing else on the envelope is under the digest; the shared `Sha256DigestSchema` describe is unchanged and is a different instance, so the key-level text did not relabel every other digest field; the key still accepts and rejects exactly what the shared digest does; and both descriptions carry no internal issue id, the negative pin `check:doc-authoring` asks for when a test twin pins customer-facing text. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
…ecksum-coverage-describe
📓 Docs Drift CheckThis PR changes 1 package(s): 1 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 — 129 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 57e62f4b481590f688dc75026805fd17c7700b44 && git checkout 57e62f4b481590f688dc75026805fd17c7700b44
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 51d59e4c9affe28922c4b4fea86cd3ac186c487f 641354355edf8f59de35812ffa43270ea114bfbf && git checkout -B drift-repro 51d59e4c9affe28922c4b4fea86cd3ac186c487f && git merge --no-ff 641354355edf8f59de35812ffa43270ea114bfbf
node scripts/docs-audit/affected-docs.mjs --json 51d59e4c9affe28922c4b4fea86cd3ac186c487f
|
checksum states its own coverage boundary
checksum states its own coverage boundary|
CI note from the dispatching seat — the one red check on Generated by Claude Code |
…ecksum-coverage-describe
Fixes #14993
The environment artifact envelope carries a
checksum, and until now nothing on the surface said what that digest covers. The field inherited the sharedSha256DigestSchemadescribe — "SHA-256 digest (64 hex chars)" — which states what the value is and nothing about its scope. The digest is computed over the canonical JSON serialization of themetadatablock only, so every other key on the envelope rides outside it, the install-timegrantedPermissionsconsent set (#14865) included.The ruling this executes
Recorded by the director seat on the card (comment 5548641412, 2026-09-05T02:09Z; maintainer verbatim 「同意」 to decision batch #41 item 3), recorded on the premise that the carrier is trusted end-to-end:
Nothing mechanical moves: no key is added or removed, no value schema changes, no digest input changes, and the accept set of
EnvironmentArtifactSchemais byte-for-byte the same set it was. Option 2b — an additive sibling digest, if the maintainer states the carrier is not trusted — stays recorded on the card as the flip, and is deliberately not this PR's.The two describes as landed
checksumgains its own field-level describe. The sharedSha256DigestSchemadescribe is untouched, so the other digest fields in the repo still inherit it:grantedPermissionsgains one appended sentence group; its five existing clauses (the manifest-idkeying, thesys_package_installationsource, the enforcer consumer, absent versus{}) are unchanged:The existing
⚠ A STRINGdocblock note onchecksumstays as it was; the describe is additive.One declared deviation from the ruling's letter
The ruling asked the
grantedPermissionssentence group to point at the card.pnpm check:doc-authoringrefuses an internal issue id inside.describe()prose, and reds on exactly that string:Its rationale is the same premise the ruling rests on — describe prose projects into
content/docs/references/**and the generated skill artifacts, where a citation-shaped id points at nothing for the reader who sees it — and its prescribed route is an adjacent//comment. So the card anchor moved to a two-line comment beside the key, and the describe keeps only the customer-resolvable references (ADR-0003 / cloud ADR-0007). The gate is green either way; this note exists so the divergence from the ruling text is read rather than discovered.Regeneration: measured, and nothing moved
pnpm --filter @objectstack/spec gen:docs— 230 files generated, working tree clean afterwards. The reference pages render the module docblock and the top-level exported schema symbols; a field-level describe on a key inside thelazySchemaobject is not projected, so neithercontent/docs/references/system/environment-artifact.mdxnor the cloud mirror moves.pnpm --filter @objectstack/spec check:generated— all 15 generated artifacts up to date,check:docsandcheck:authorable-surfaceamong them. Thejson-schema.manifestand authorable-surface shards did not move.content/docs/is hand-edited by this PR, and nothing undercontent/docs/releases/is touched at all.Verification
Run against the final commit,
af3041c9(this branch merged withorigin/mainatf7db8f4fd):pnpm --filter @objectstack/spec test— 478 files, 12844 passed, 0 failed.pnpm --filter @objectstack/spec typecheck— clean;check:test-typecheckholds at 54 files / 261 errors / 145 pinned signatures, unmoved.pnpm lint(eslint . --no-inline-config, whole repo) — exit 0.node scripts/pm/dispatch-gates.mjs --commandsover this diff: 70 exit 0. The two exceptions arecheck:dual-build-cjs-loadsandcheck:type-check-debt, both exit 3 / PREREQUISITE NOT MET — each needs a whole-workspace build before it measures anything, which is CI's run, not a local one. Neither is a finding.Tests 4 failed | 29 passed); the mutation and its restoration are both proven on disk by blob hash, andgit diff HEADis empty afterwards.Tests
Six assertions were added, not invented: the file already pins describe text — the
grantedPermissionsdescription is asserted clause by clause, because the manifest-id-not-package_idassumption is documented nowhere else a producer reads. The coverage boundary is the same kind of fact. There is no digest mechanism to pin here (the control plane computes and verifies it; this envelope only carries the value), so the sentence is the surface, and an unpinned sentence rots silently.The new pins assert that
checksum's describe names themetadatablock and states what it excludes; that the sharedSha256DigestSchemadescribe is unchanged and a different instance, so the key-level text did not relabel every other digest field; that the key still accepts and rejects exactly what the shared digest does; and that neither description carries an internal issue id — the negative pincheck:doc-authoringasks for whenever a test twin pins customer-facing text.Changeset
One,
@objectstack/specpatch — describe text only, no behaviour change.Generated by Claude Code