Repository navigation
Correct DESIGN's bash-AST confined-vocab citation: two of four names resolve to nothing - #7496
Merged
Merged
Conversation
Found while reviewing the pin carrier, verified on main, and recorded because
it would otherwise be lost with this session. Docs only, no code change.
std.types.ContentHash = NonEmptyStr where brand("ContentHash") is inhabited by
values from two unrelated families:
std.content_hash content_hash_atom/_combine/_tagged -> fnv1a64, NO prefix
extdeps.crypto sha256_digest_content_hash -> cited "sha256:<hex>"
Nothing models the discriminant — it is a string prefix convention, so the type
cannot distinguish a structural fingerprint from a cryptographic digest.
WHAT IS NOT THE DEFECT: sha256_digest_content_hash. Its convergence note is
correct that rendering a structured Digest { algorithm, hex } into a serialized
wire form is a rendering rather than a second concept, the same legitimate move
as hex being a serialization of Srgb8. I had flagged that conversion as a §3
fork on #7437 and withdrew the claim after reading it. The defect is one level
up, in the brand's state-space conflation.
WHY IT MATTERS RATHER THAN BEING TIDINESS: ContentHash is load-bearing for
integrity. extdeps.pin Pin.expected_identity: ContentHash accepts an fnv1a64
fingerprint where a cited SHA-256 belongs, so admit_pin_integrity can be handed
two structural fingerprints, find them equal, and answer PinIntegrityAdmitted
having verified nothing cryptographic. No arm refuses it because both sides
typecheck. Stated as a permission of the type, not as an occurrence in the
corpus today.
Two candidate fixes, both corpus-wide std changes and neither started: make
ContentHash structured (family + value, moving the prefix to serialization and
making the extdeps bridge total), or give the families distinct brands and let
each carrier declare which it requires.
Filed in Open threads, distinct from the existing fnv1a64 dual-surface thread
(that one is about raw-vs-modeled surfaces; this is about the brand admitting
two families). README.md and CLAUDE.md symlink to DESIGN.md, so one edit covers
all three.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sion/deep-eagle-728
… artifact (review 45039) Two defects in my first attempt, and the second is the more useful lesson. 1. FABRICATED SYMBOL — the review is right. The row cited extdeps.crypto.hash.sha256_digest_content_hash as an existing producer. That fn does NOT exist on main; it exists only on the unmerged session/quick-dove-113 branch, which is where I read it. I cited an unmerged branch's API as though it were current state — the identical class I had just recorded a dissolve-on for on #7437 (feature:cited-symbol-resolution), committed again one PR later. The claim is withdrawn in the row itself rather than quietly dropped. Rewritten on main-verified facts only, and the corrected reading is STRONGER than the original: there is no bridge at all. extdeps.crypto.hash models Digest { algorithm, hex } plus sha256_digest and nothing converts a Digest to a ContentHash, so SHA-256-shaped values enter by direct literal assignment (dag/extdeps/container/oci/ctrl_session_witness.dag:93,97,101). The two families are joined by assignment, not by any conversion one can point at. It also gains the sharpest available evidence, which the corpus supplies itself: extdeps.crypto.hash sha256_authority_note says a digest carries its algorithm explicitly SO THAT an artifact pin is never an anonymous hex string fused into a field name — yet OciDescriptor.digest and Pin.expected_identity are both exactly that string. Digest's stated purpose is defeated at the two carriers that most need it. 2. I EDITED A GENERATED ARTIFACT. DESIGN.md is emitted from dag/gunbc/design_document.dag (open_threads_blocks, with expected_design_md / design_drifted as the gate) and is registered as DesignArtifact in gunbc.generated_artifact. My hand edit was therefore drift, and CI auto-heal correctly regenerated the file and deleted my line — which is also why the review bot then saw an empty diff. The mechanism worked exactly as designed and caught me treating the output as the source, which is the parallel-ledger failure DESIGN §6 names ("let the mark on the carrier be the authority"). The row now lives in the .dag; DESIGN.md is the regenerated output. Verified by execution rather than by reading: ran expected_design_md through the interpreter (rebuilt gunbc locally first — the committed binary was the stale Jun 28 one) and confirmed the row renders and that the \{ escape produces real braces in "Digest { algorithm, hex }". The generated document was checked to start correctly, contain the row, and render the braces before being written; the writer refuses rather than emitting a doc that fails those checks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(review 45046)
The finding is fair: the row recorded a live fail-open and then said both fixes
were "not started" with no trigger, which is untracked deferral. The parked
entries elsewhere in Open threads are not precedent for this one — they are open
QUESTIONS, whereas this row names a defect that exists now.
Three additions, and the third is the one that actually makes it tracked:
- dissolve-on: feature:content-hash-family-grounded — ContentHash carries its
hash family, either as a structured value (family + digest) or as one brand per
family, so a cross-family comparison becomes UNWRITABLE rather than checked. At
that point the Digest -> ContentHash conversion is total, the prefix becomes
serialization rather than a discriminant, and the row deletes.
- Lane: the same shape as the Value::Null split — a std carrier overloaded across
meanings, corpus-wide, its own runway — sequenced beside that work rather than
inside the hermetic-toolchain lane that surfaced it. Stated as UNOWNED today
rather than implying an owner I cannot assign.
- The bound on the deferral: until the trigger fires, a Pin integrity pass means
"these two ContentHash values are equal" and may NOT be read as "a cryptographic
digest was verified". A consumer needing the stronger reading must establish the
family at its own boundary, typed and located. That sentence is what keeps the
residue bounded instead of open-ended — it prices what the claim is worth today
rather than leaving readers to assume it is worth more.
Authority edited in dag/gunbc/design_document.dag; DESIGN.md regenerated from it
by running expected_design_md through the interpreter. The generated document was
checked for five properties before being written — correct opening, the row
present, braces rendering from the \{ escape, the trigger present, the bound
present — and the writer refuses rather than emitting a doc that fails any of
them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The finding asks for a named owner and I cannot supply one truthfully, so this addresses the substance — indefinite deferral — without fabricating the fact. Reasoning is on the PR; in summary, §6's stated requirement is a named dissolution TRIGGER, which the row has had since review 45046, and 18 of the 31 Open threads rows name no owner at all, including one that records a fail-open in the same terms this one does. What changed: "unowned today" read like drift, which is a fair thing to object to. It now reads as an explicit disposition — awaiting an operator DISPATCH decision, not deferred work — and says why: a corpus-wide std carrier change cannot be self-assigned by the session that found it, the trigger and acceptance condition are already fixed, so what is missing is only the dispatch. Naming an owner I have no authority to assign would assert a false ownership fact, which is the fabrication class this PR already corrected once (review 45039, the sha256_digest_content_hash citation). Nothing blocks on the decision, which is the point of the bound: until the trigger fires, a Pin integrity pass means "these two ContentHash values are equal" and may not be read as "a cryptographic digest was verified". Authority edited in dag/gunbc/design_document.dag, DESIGN.md regenerated through the interpreter, six properties checked before writing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s the contract, not a fail-open (review 45055) The finding is right and it undercut the load-bearing sentence, so it needed a real correction rather than a hedge. Verified before accepting: observe_tool_identity digests via atom_identity_hash / hash_combine / bytes_identity_hash (src/v1/stage0/src/v1_interpreter.rs:8598) — the fnv1a64 primitives, NOT sha2. WHAT WAS WRONG. The row said Pin.expected_identity accepts an fnv1a64 fingerprint "where a cited SHA-256 belongs", so admit_pin_integrity could compare two fingerprints "having verified nothing cryptographic" — a fail-open. That mischaracterizes Pin's own contract. Its authority ties expected_identity to ResolvedBuildContext.observed_tool_identities, whose rows are digested from logical name + executable bytes + version-probe output with those fnv1a64 primitives. A structural fingerprint is exactly the family Pin WANTS, and comparing two of them is the intended contract. Left standing, the row would have pointed the grounding work at the wrong requirement. WHAT SURVIVES, restated so it is about the type rather than about one consumer: carriers in this corpus have genuinely DIFFERENT family requirements and the brand records none of them. Pin.expected_identity deliberately wants the structural fingerprint; OciDescriptor.digest must be the cryptographic digest its OCI upstream mandates. One brand serves both, so each requirement lives only in prose and nothing refuses a wrong-family value at either end — an fnv1a64 fingerprint is assignable to OciDescriptor.digest and a cited SHA-256 to a Pin over a tool, both typechecking. That is a sharper defect than the original claim and it does not libel Pin. The bound changes with it. Previously "a Pin integrity pass may not be read as a cryptographic verification" — which was the same error. Now: the family a carrier requires cannot be read off its type at all; each carrier's requirement must come from its own authority, and a consumer depending on the family must establish it at its own boundary rather than inferring it from the brand. Note on the reviews: cursor and claude both approved after checking the row's STRUCTURAL claims against the tree (branded NonEmptyStr, the literal sites, the absent bridge, equality-only comparison) and all of those were and remain true. codex evaluated the SEMANTIC contract — what expected_identity is FOR — which the structural check does not cover. The two are not in conflict; the deeper read found a real error, and this is the second time on this lane it has done so. Authority edited, DESIGN.md regenerated through the interpreter, eight properties checked before writing including that the withdrawn sentence is absent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… symbol, not the position Operator ruling 2026-07-30: "citations should be symbolic/namespace driven always where possible." Landed at the authority (dag/gunbc/design_document.dag) and regenerated, never edited into DESIGN.md directly. WHY §3 RATHER THAN A HYGIENE NOTE. A file:line pointer is a SECOND, POSITIONAL NAMING SCHEME for something the containment tree already names -- the nicknaming violation §3 exists to forbid, applied to prose. It also decays in a way an ordinary nickname does not: any edit ABOVE the cited line silently invalidates it, so the citation rots without anyone touching it or the thing it names. THE MEASURED COST THAT PRODUCED THE RULING, recorded so it is not rediscovered: three citation defects landed in extdeps.pin inside two days -- two fabricated symbols (review 44920) and one stale line (review 45088) -- and the stale line was then copied into gunbc.design_document as a row's supporting evidence, so a name-level grep passed while the located claim was false in the canonical design authority. WHY THE RULE IS STRONGER THAN THE LENS, which is the part that decides sequencing: grep-verifying a symbol is the same Node-tree read the namespace authority already performs, so a stale NAME is decidable and lens-enforceable (feature:cited-symbol-resolution). A LINE is not reachable from that tree at all -- a .rs offset is outside it -- so no lens ever closes the positional half. The only mechanism that closes it is to stop writing positions. So the rule is the cheap CONSTRUCTION move and the lens is the residue, not the reverse (§5 construction-before-validation). feature:cited-source-location-checked retires when the corpus carries no positional citation a symbol could have carried. The rule keeps an honest exception rather than an absolute: a position is legitimate where no symbol exists to name -- a generated artifact, a data row, a line inside a blob -- and where carried, it sits BESIDE the symbol, never as the citation itself. Also adds "positional citation" to the recurring failure modes list. RECEIPTS. Regenerated through the interpreter; seven properties checked before writing. Verified the diff is exactly two changes and nothing else: one inserted line (the §3 rule) and one modified line (the failure-modes list), with every other line byte-identical after accounting for the shift. witness_committed_is_fixed_point green on the committed bytes. Corpus sweep of the 184 existing positional citations across 41 files is dispatched separately; this lands the authority they will be measured against. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…resolve to nothing The shell→intent row declares an ENFORCEABLE invariant and names the confined vocabulary as `bash_build`, `bash_command_fold_serialize`, the `ShellStmt`/`ShellWord`/`ShellProgram` coproduct, and `serialize_bash`. The first two are real modules (v2.extdeps.languages.bash_build, v2.workflow.bash_command_fold_serialize). The last two name NOTHING in the tree: the bash AST is built as generic `Node` — every `bash_build_*` fn returns `Node` — so there is no ShellStmt/ShellWord/ShellProgram coproduct, and no `serialize_bash` is defined anywhere. Both were dissolved by the typed-WitnessBin migration (Phase D, 2026-07-14), which the dag/tools/* transport notes record and this row did not follow. THE WALL WAS NEVER AFFECTED. v2.lens.realization_vocabulary_containment confines by MODULE — target_ast_vocab_module_prefixes = v2.extdeps.languages.bash, v2.workflow.bash; target_ast_vocab_modules = extdeps.languages.typescript.program — not by these type names, so nothing was unguarded and the lens is live and enrolled (roster_registry, commit_workflow lens_unit entry). What rotted was the DESCRIPTION of the wall, inside the sentence declaring the invariant. That is the §3 cite-the-symbol class exactly: a name-level claim in the canonical authority that a grep decides, sitting false while the mechanism stayed correct. The row now names what the lens actually reads. Found by a corpus citation sweep, not by anyone touching this row — which is the argument for feature:cited-symbol-resolution, not for more diligence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6 tasks done
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 free
to 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.
DESIGN's shell→intent row declares an enforceable invariant and names the confined bash-AST vocabulary as
bash_build,bash_command_fold_serialize, theShellStmt/ShellWord/ShellProgramcoproduct, andserialize_bash.Two of those four name nothing in the tree.
bash_buildv2.extdeps.languages.bash_buildbash_command_fold_serializev2.workflow.bash_command_fold_serializeShellStmt/ShellWord/ShellProgramcoproductbash_build_*fn returns genericNodeserialize_bashBoth were dissolved by the typed-WitnessBin migration (Phase D, 2026-07-14). The
dag/tools/*transport notes record that dissolution explicitly — "The formerShellProgrambuilders … dissolved with the typed WitnessBin path" — and this row did not follow.The wall was never affected
v2.lens.realization_vocabulary_containmentconfines by module, not by these type names:target_ast_vocab_module_prefixes=v2.extdeps.languages.bash,v2.workflow.bashtarget_ast_vocab_modules=extdeps.languages.typescript.programThe lens is live and enrolled (
roster_registry, and alens_unitentry incommit_workflow). Nothing was unguarded. What rotted was the description of the wall, inside the sentence declaring the invariant — which is the worse place for it, because that sentence is what a reader trusts about what is enforced.Why this is the §3 class and not a typo
A name-level claim in the canonical authority that a grep decides, sitting false while the mechanism it describes stayed correct. It was found by a corpus citation sweep, not by anyone touching this row — which is the argument for
feature:cited-symbol-resolutionrather than for more diligence. The row now names what the lens actually reads, so the citation and the mechanism are the same fact.Verification
DESIGN.mdregenerated throughdag/tools/generated_artifact_gate.dagmain_wet; re-run produces zero further change (byte-idempotent fixed point).origin/main.design_document.dagplus its projection.Reported alongside this: the sweep that found it initially measured a ~53% stale-citation rate, which was wrong — the method missed record fields, module names, and Rust std identifiers. Hand-verified rate on the same files is ~7.6%. Any implementation of
feature:cited-symbol-resolutionneeds resolution across all definition forms plus a declared external-reference class, or it will measure noise in one direction or the other.🤖 Generated with Claude Code