Skip to content

Correct DESIGN's bash-AST confined-vocab citation: two of four names resolve to nothing - #7496

Merged
briansrls merged 28 commits into
mainfrom
session/deep-eagle-728
Jul 31, 2026
Merged

briansrls merged 28 commits into
mainfrom
session/deep-eagle-728

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

DESIGN's shell→intent row declares an enforceable invariant and names the confined bash-AST vocabulary as bash_build, bash_command_fold_serialize, the ShellStmt/ShellWord/ShellProgram coproduct, and serialize_bash.

Two of those four name nothing in the tree.

cited reality
bash_build ✓ module v2.extdeps.languages.bash_build
bash_command_fold_serialize ✓ module v2.workflow.bash_command_fold_serialize
ShellStmt/ShellWord/ShellProgram coproduct ✗ no such types — every bash_build_* fn returns generic Node
serialize_bash ✗ no definition anywhere; only mentions inside note strings

Both were dissolved by the typed-WitnessBin migration (Phase D, 2026-07-14). The dag/tools/* transport notes record that dissolution explicitly — "The former ShellProgram builders … dissolved with the typed WitnessBin path" — and this row did not follow.

The wall was never affected

v2.lens.realization_vocabulary_containment confines by module, not by these type names:

  • target_ast_vocab_module_prefixes = v2.extdeps.languages.bash, v2.workflow.bash
  • target_ast_vocab_modules = extdeps.languages.typescript.program

The lens is live and enrolled (roster_registry, and a lens_unit entry in commit_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-resolution rather 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.md regenerated through dag/tools/generated_artifact_gate.dag main_wet; re-run produces zero further change (byte-idempotent fixed point).
  • Source edit and regenerated artifact are both at the tip; verified again after merging origin/main.
  • Diff is one line of design_document.dag plus 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-resolution needs 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

gunbc-ci-auto-heal and others added 27 commits July 30, 2026 07:30
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>
… 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>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review July 31, 2026 13:17
@gunbai-bot gunbai-bot Bot changed the title gunbc oracle Correct DESIGN's bash-AST confined-vocab citation: two of four names resolve to nothing Jul 31, 2026
@briansrls
briansrls merged commit 480cf8d into main Jul 31, 2026
9 of 10 checks passed
@briansrls
briansrls deleted the session/deep-eagle-728 branch July 31, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant