Repository navigation
BL-1: seal NormalizedTree behind an admission door so retention cannot reach resolve - #8256
Conversation
OtherAssembleRefusal is the catch-all arm of AssembleRejectionCause, and it carried only a reason symbol. A reason symbol does not say WHICH file refused, so recovering that meant re-lexing an entire import closure by hand — measured on use_site_verdict, whose 15-file closure had to be folded through tokenize one source at a time to learn that dag/std/algebra.dag was the refusing file. The location is carried as a probe-owned projection rather than a bare Locus. Locus admits NodeLocus and PortLocus, which have no manifest serialization; a host emitter that errored on them would abort a 27-module survey over one unrepresentable position. FrontierProbeCauseLocation is serializable by construction and total, and its unavailable arm carries a typed reason so the deficit is counted rather than fabricated or fatal. Location is kept structurally separate from the cause key: two modules refusing for the same reason at different positions are the same detailed cause, so a clustering key reads the cause and not this field. The survey binary is the third consumer and is updated with it — the DAG carrier change alone would have emitted a manifest that no longer typechecks against the new field, which is worse than dropping it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e its variants
The location was a field of OtherAssembleRefusal, which made a position
structurally part of what the refusal IS: two modules refusing for the same
reason at different positions read as different detailed causes at a
clustering key. It now sits beside the cause on AssembleRejectionDetail.
CauseLocationUnavailable { reason: Symbol } is replaced by the closed pair
CauseLocationPort { port } and CauseLocationNodeNotSerializable. A PortLocus
carries real serializable data and no longer degrades to a reason string;
only a Node, which has no manifest rendering, reaches an unavailable arm.
The survey emitter follows the field and the arms, and stays total.
Controls (all executed): each of the three Locus arms reaches its own arm;
the byte range is asserted exactly, so substituting WholeFile reds; and a
two-diagnostic control pins reason and location to the SAME diagnostic
(RED verified by asserting the tail's offset).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fixture was hand-edited to the reshaped carrier, which is exactly what its own note forbids: a hand-written approximation tests the author's idea of the format instead of the emitter's. These bytes are copied verbatim out of a survey run at commit 2880549, tree 932acf8, executable sha256 677c1bf0673c70ef… over src/v2/compiler/use_site_verdict.dag, and the reload half passes against them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eipts frontier_gap_clustering_from_receipts counted blocker CLASSES and then wrote a hardcoded located_reason literal into whichever branch fired, so the verdict reported a reason no receipt had to carry: the one real survey measures ^tokenize_lex_e1_unrecognized_char, which no branch could name. Its HeterogeneousGaps arm reported module_count as the distinct-reason count, which is a count of modules wearing the name of a count of reasons. And count_receipts_with_located_reason, the function that reads the measured axis, had zero callers. Both numbers are now read off the receipts. CommonRoot requires unanimity rather than a plurality, because any plurality bar is a threshold and a threshold is the smuggled heuristic a closed system never needs. An empty survey reports a distinct count of zero rather than becoming a common root. Controls (executed, both REDs verified): a unanimous survey reports the measured reason and counts the receipts carrying it; a mixed survey reports 2 distinct reasons over 3 receipts, where the old arm reported 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These eight files are the one-off measurement instruments behind the A/B finding — the real-closure door probe, the cause and reason readouts, the lex localizer and three tokenize fixtures. They were never meant to land; the PR description says so explicitly. A blanket 'git add -A src/v2' put them in the tree anyway, which is experimental residue and a description that no longer described the diff. They stay local, excluded via .git/info/exclude so the next add cannot repeat this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tally ran unconditionally and its result was discarded on the heterogeneous arm, which needs the distinct count alone. Cheap at survey scale, but a copied fold whose output is thrown away is a cost-shape defect regardless of the realized n, and 'n is small here' is not a time-stable fact. Found by review 51421. All four clustering claims still pass by execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MEASURED, not hypothesised. src/v2/compiler/03_resolve.dag needs 71 source reads against the manifest transport's 64-row inline cap, so the manifest emits SourceRootManifestElided with produced_row_count 0 and an Empty ingest. Handed that, frontier_probe_emit_from_ingest answered ^resolve_module_not_found with cause MissingModuleIdentityUnavailable — a survey row asserting the module is missing when the truth is that its sources were never supplied. Two states, different remedies, and the one reported was the plausible one. frontier_probe_coverage_gate already classifies an elided manifest correctly, but it guards only the discovery path; this entry receives the ingest directly and never consults coverage, so the conflation was reachable from every from_ingest caller. An empty ingest is now a typed, located, counted population refusal naming ^frontier_probe_empty_ingest. Residue, declared rather than closed: emptiness is decidable here, coverage is not, because the entry is not given the SourceRootCoverage — a PARTIAL closure still answers. Dissolve-on is on the carrier: route the coverage receipt into this entry so the refusal becomes an arm of the existing gate. Four claims green by execution; RED verified by disabling the wall, which reds three of them and restores the module-not-found answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tern Every other match site was updated when the field landed; this one kept the two-field pattern. Review 51431 flagged it. The predicted typecheck failure does not occur — the file's five claims were green by execution before this change and are green after — so the language admits the partial pattern. That is the reason to fix it rather than to leave it: a pattern that silently keeps matching when the carrier grows a field is how a match stops covering what its author thought it covered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review 51432 is right that this claim's behavior against the checked-in stub changed. What it was before is the part worth naming: with receipts Empty, clustering asked self_emit_ready_count == module_count, both were 0, and it answered CommonRoot with dominant_located_reason ^probe_stage_emit_ok — a survey with no receipts reporting that every module reached emit. The one claim whose subject IS the clustering verdict was passing vacuously on a fabricated verdict. Receipt-derived clustering answers HeterogeneousGaps for an empty survey, so the claim now reds on the stub. That is the same state its siblings were already in and is the file's declared offline condition, not a CI failure shape: compiler_frontier_per_module_probe_survey_holds compares the receipt count against the 27-row roster, and 0 is not 27. The length check makes THIS claim's red say 'no receipts' explicitly rather than leaving it to read as a clustering disagreement. Verified by execution that the conjunction is false on an empty survey rather than erroring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The observer carried two arms over three reachable states, and the missing arm failed OPEN: every Rejected outcome fell to the else branch and reported BodyLoweringLowered. The instrument used to measure the body-lowering gap was counting failures as successes, so any population read through it undercounted by exactly the refusals. That arm is reachable by construction, not in theory: the rejection- propagation repair deliberately made body_lower_finish_for_normalize propagate genuine rejection through rejected_with_pending, and this classifier then discarded the distinction it created. Recognition had the same fail-open by a second route. It read the diagnostic HEAD alone, while retention travels through diagnostics_merge and rejected_with_pending — both of which place OUTER diagnostics first — so a retained body behind any pending diagnostic also read as lowered. It now scans the whole list. Six claims green by execution. RED verified by restoring the previous classifier: the rejection claim, the rejection-diagnostics claim and the behind-a-pending-diagnostic claim all red, while the three negative controls stay green — so the claims pin these two defects rather than failing broadly. The controls are the load-bearing half: a classifier that answered retained more often would satisfy the positive claims and destroy the count. Two existing consumers gain the third arm. Residue declared on the carrier: recognition still reads an encoding rather than a producer-returned variant, correct for every encoding the single retention producer can emit, with dissolve-on naming the producer change that makes this function the identity. First step of the sequence gunbc.roadmap_authority already declares: honest frontier, then the normalized-tree construction boundary, then the retained population to zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ation probe The stage classifier and reconciliation machinery already existed in gunbc.tools.frontier_ingestion_probe, hardcoded to the 15-member std ingestion roster. Measuring a second population needed the roster to be an argument rather than a second copy of the mechanism — two classifiers are two authorities that can disagree. take_census_over(rows) is the general form; take_frontend_census() is now that call with the module roster. Roster identity derives from the rows PASSED, never the module-level roster, so a receipt cannot claim a denominator it was not measured over — the exact misreading census_receipt_provenance_note exists to prevent, which a parameterised census stamping the default identity would have reintroduced. All 14 existing claims over the instrument still pass. The new probe measures the body-lowering population: the two SH-D shards with measured door receipts, the std members the pre-repair observation recorded as retained plus the one it never re-ran, and the seam's own modules. It is deliberately not the whole corpus, and it carries no aggregate pass/fail — the population IS the finding, and a Boolean over it would be a verdict on subjects not separately established. WHY BEFORE THE WALL: normalize returns Accepted for a tree whose diagnostics carry wrapper-retention, and resolve consumes it. Making retention unconstructible changes which modules normalize accepts, so the newly refusing population must be known before the wall lands rather than discovered from a red tree afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Executed, three-member roster: use_site_verdict NORM_ACCEPTED, materialization_carriers NORM_OTHER, optional NORM_RETAINED. That corrects the shard rows rather than the finding. Neither shard retains as a file — use_site_verdict normalizes clean — so the wrapper-retained residue measured in their door chains comes from OTHER modules in their import closures, of which optional is one confirmed instance. 'The shard is body-lowering blocked' was too coarse: the shard is blocked because its closure contains retaining modules, and the repair subjects are those. The roster is three because ten was executed and ABANDONED at 2h51m and 8.3 GiB resident with no verdict, on a host whose shared 20 GiB slice is reaped largest-task-first — a hazard to other sessions before it was a slow measurement. The instrument's own note already owed a realized execution for exactly this reason. The seven dropped members are unmeasured, not excluded, and the carrier says so. The 10-to-3 shrink is nonlinear (2h51m to ~2min), so a dropped member is pathological rather than merely larger. Which one is NOT established, and guessing the largest file would be a guess dressed as a finding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed text Two defects, both reachable only because an aggregate was authored over a roster this module's own note says carries no aggregate verdict. A refusal became an integer. count_stage_matching answered 0 - 1 for CensusRefusedAtWorld and 0 - 2 for CensusRefusedAtReconciliation, so a typed refusal and a genuine population count inhabited one type: a caller could add, compare or display a failure as a measurement, and -1 reads as a number rather than as a stop. The census already carries typed refusal arms, so this discarded exactly the distinction they exist to preserve. Presentation became semantic authority. The count compared stage_label_of(s) against a target String, but FrontendStage is a closed coproduct — a label rename would change the count with no stage changing, and a label collision would merge distinct stages. Counting belongs to the structural variant or to frontend_stage_eq, never to rendered text. Deleted rather than repaired: the module already states the per-member observation IS the finding and that no aggregate verdict is warranted over subjects that have not each been separately established. The helpers contradicted the stated scope, so there is nothing for a corrected aggregate to mean here. Int and Bool were left import-only and are dropped with them.
The previous revision declared three rows named body_lowering_population_roster while the module's prose said the subjects were ten. That made the planning error this instrument exists to detect representable in its own carrier: seven unmeasured subjects erased, one retained member observed, and a bare count licensing "the retained population is one". Two authorities now. body_lowering_subject_roster is the denominator, carrying exact identities cited rather than asserted — the two SH-D shards, and five std members from the executed table in docs/plans/v2-frontend-std-ingestion-frontier-exact-head.md (logic, optional, diagnostic, occurrence_identity as its NORM_RETAINED rows; node as its NOT RE-OBSERVED row). body_lowering_execution_batch is what one run selected, and it is DERIVED from the roster by label selection rather than re-authored beside it, so a batch row cannot name a path the roster does not. Standing is three states, because observed-or-absent cannot carry this population: a subject dropped for cost, a subject whose run a budget interrupted, and a subject genuinely measured are three epistemic positions with three remedies, and collapsing the first two into absence renders "we did not look" as "there is nothing there". SubjectInterrupted stays separate from SubjectUnmeasured because node's 900s overrun is a real lower bound on cost that an unmeasured subject does not carry; body_lowering_prior_standing gives that arm its producer from the cited historical receipt, declared apart from this run so the two can never be read as one measurement. The population is EIGHT, derived rather than chosen (operator ruling 2026-08-13). Seven exact identities plus one subject for the vacuity bare-expression specimen CLASS, which grounds as a class and not as members: the vacuity note describes "many isolated configurations" and enumerates none, and its optional_absent copy is the same subject as the rostered optional member. Minting three rows would have fabricated two identities to satisfy a prose sentence. The prose "ten" is recorded as retired rather than silently corrected, and the note states that eight is not an authority either — completeness is an identity join, not a count equality.
Four blocking diagnostics, found by compiling the entry rather than by
inspection, and one of them is worth recording because it is a language-layer
trap rather than a typo.
A BRACE INSIDE A PROSE NOTE IS INTERPOLATION SYNTAX. The roster note described a
standing as SubjectUnmeasured{no_committed_specimen_identity}, and the compiler
read the braces as an interpolation and refused an undefined variable — inside a
String literal, at a position no reader would look for code. Escaped as \{, which
is the existing modeled form. This is the class DESIGN records from the
${...}-in-strings incident: the tempting move is to respell the prose around the
obstacle, and the modeled escape already exists.
The other three are empty list literals in fold seeds, which carry no element
type on their own. Written as [] as List<T>, the idiom already used by
frontier_ingestion_probe and normalize_retention_contract_probe_test.
Result: 0 blocking errors on dag/tools/body_lowering_population_probe.dag. The
248 remaining advisories are pre-existing and corpus-wide.
body_lowering_program_receipt was run against the live tree (73s) and returned eight standings. Recorded because compiling is not running, and the join is the part worth proving: eight subjects in and eight standings out establishes that no subject is dropped between roster and receipt, the three observed rows reproduce the earlier batch reading so deriving the batch from the roster did not change what executes, and the specimen class routes to no_committed_specimen_identity rather than to not_selected_into_execution_batch — the distinction the realization coproduct exists to make. The note also states what the run does not establish: the four unmeasured subjects are unobserved, not staged, and node's prior interruption is neither confirmed nor refuted here.
Merges main through 3c533ba rather than through its tip. #8228 landed at 9c5d007, so this drops the BL-0 files this branch was duplicating and reduces the diff to the BL-2 work alone. It deliberately stops one commit short. #8214 (9156b78) introduced in-body source annotations across 13 files, confirmed by compiling dag/std/affine_space.dag against the live tree, so merging main's tip today would import a known compile-clean red into a branch that is currently green — turning an unrelated lane's defect into this PR's failure. 3c533ba is the newest main commit whose tree carries neither the in-body nor the unattached annotation form. The remainder of main merges once that class is repaired.
Review on #8208 noted that stage_label_of paralleled frontier_ingestion_probe's frontend_stage_label and disagreed with it on one variant — NORM_ACCEPTED against ACCEPTED — and judged it non-blocking because it is presentation-only. Taking it anyway: it is a second renderer for one closed coproduct, and it had already drifted, which is the §3 fork rather than a style preference. This module's own note claims there is one stage classifier in the repository and not two; forking the label while importing the classifier honoured that in the half that was harder to get wrong. frontend_stage_label is imported and the fork deleted. Confirmed by execution that the divergence was real and is now gone: census_report prints "use_site_verdict ACCEPTED | materialization_carriers NORM_OTHER | optional NORM_RETAINED", where the forked renderer printed NORM_ACCEPTED for the same stage. The recorded first reading named stages by their rendered label, which would now be stale. It names them by VARIANT instead, and says why: the spelling belongs to the renderer, so a note pinning it goes stale the moment that renderer is edited — the same reason counting may not read labels. Five variant imports were left import-only by the deletion and are dropped; NormalizeRetained stays, since the prior-standing rows construct it.
Review 51759 (REQUEST_CHANGES) found that body_lowering_program_receipt routed EVERY live-file subject through the census-refusal cause. Verified and correct: on CensusRefusedAtWorld, logic, diagnostic, occurrence_identity and node — four subjects this run was never going to execute — were rewritten from "not selected" into "the census refused". That is state-space conflation, and it undid the roster/batch split on the one path where the distinction is least visible. The two states have different remedies: a not-selected subject needs a bigger batch or a cheaper instrument, while a refused census needs the infrastructure repaired, so a reader deciding what to do next was misdirected. More generally it is a failure at one scope attributed to subjects outside that scope — the absorbing fallback wearing an error's name, manufacturing information about subjects nobody looked at. The refusal cause now reaches only selected subjects. A subject outside the batch keeps not_selected_into_execution_batch under refusal exactly as under success, because nothing about it was attempted and its standing does not depend on the census. The specimen class keeps its own structural cause under both. Proven by execution with a discriminating control, not by inspection. census_refusal_does_not_reach_a_non_batch_subject_RED returns false against the previous arm and true against this one; the other three assert that a batch member does receive the cause, that the cause is carried rather than fixed, and that the specimen class is unaffected. The arm takes a cause symbol as input, so it is reachable by ordinary call and needs no broken tree to observe.
…identity row Per-subject censuses (one subject per run) measured six of the seven runnable subjects. Two consequences, plus the reading that keeps them from being misread. THE STALE RECEIPT ROW. occurrence_identity measures NormalizeOther, not the NormalizeRetained the exact-head receipt records. That receipt is explicit that the member was never re-run after the repair, so the cell was unknown rather than confirmed — and it is now known stale. Corrected where the receipt lives rather than only in a message, because a stale cell in a cited authority is the failure that survives on the assumption that someone else checked it. logic independently reproduced what the receipt predicted for it, which is evidence the receipt was right about the mechanism and wrong only about the row it could not re-run. WHAT A STAGE LICENSES IS NOW DERIVED, not left to prose. Leaving NormalizeRetained is not evidence of repair: NormalizeOther is by construction neither retention nor a graft refusal, so it names where a module refuses and never why. ObservedReading projects that — ObservedClean for the one admitting stage, ObservedRefusedCauseNamed where the classifier identified the kind, ObservedRefusedCauseUnclassified for NormalizeOther alone. Derived from the stage rather than stored beside it, so it cannot disagree with the coproduct it reads. A READING EXISTS ONLY WHERE SOMETHING WAS OBSERVED. A first draft of reading_of_standing mapped SubjectUnmeasured and SubjectInterrupted onto the unclassified-refusal arm on the reasoning that all three leave the cause unknown. That is the census-refusal conflation again, in the module that just repaired it: unknown-because-unclassified and unknown-because-unattempted are different unknowns with different remedies. StandingReading keeps them apart. Nine witnesses, all executed. Two are discriminating controls for the classes above: NormalizeOther must not read clean, and an unmeasured subject must have no reading at all. node is not included in any of this. It was SIGKILLed at 346s while every other subject finished between 138s and 292s; the signal is observed and the cause is not established, so it stands interrupted with a fresh lower bound and carries no stage. The six completions sit within a factor of 2.1, so the earlier 2h51m ten-member run is unexplained by them, and node dying rather than finishing left node-is-the-outlier versus superlinear-in-roster-size open.
… can complete The earlier merge deliberately stopped at 3c533ba, one commit short of #8214, because that commit put in-body source annotations across 13 files and merging main's tip would have imported a known compile-clean red. #8235 moved all 87 body-grain annotations to module-item grain and main now carries none, so the reason for stopping short is gone and this takes the remainder. Recorded because the earlier stop was itself declared: a branch that resumes a partial merge should say what condition cleared, not silently catch up.
…t reach resolve normalize computed 'this root's body lowering left no wrapper behind' and had nowhere to put it: NormalizedTree was an alias for Node, so the fact travelled as diagnostics beside a bare Node and was dropped at the first FreeMonoid<Node> carrier before resolve. Restore the carrier through the root-carrying signatures and give it a door. NormalizedTree becomes a sole_constructor record; admit_normalized_tree refuses a root carrying wrapper-retention evidence. The retention recognizer lives once, beside its single producer in v2.std.compilers.body_lowering, and scans the whole diagnostic list -- rejected_with_pending prepends outer diagnostics, so a head-only read is systematically wrong. Rung: accepted refinement with executing refusal, not structural impossibility. Also records the typecheck-blindness receipt on the hollow-alias lane: the conversion reported 0 blocking errors with a record in a Node position AND a raw Node in the NormalizedTree field, and surfaced only under execution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
…lation The ContentHash specimen was characterised as a family member in union position. Read on main, Fnv1a64Structural is the coproduct's PAYLOAD, not a member -- and that is the stronger fact: no alias participates in it, so the class is not a quirk of alias declarations. Both specimens are one shape. The typechecker does not distinguish a wrapper from the thing it wraps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
Review 51940 is right and this was my error. body_lowering_fold already carried body_lowering_diagnostic_is_wrapper_retained and the whole-list body_lowering_any_diagnostic_is_wrapper_retained -- landed by BL-0 -- and I minted a second copy in v2.std.compilers.body_lowering under the SAME names, then pointed the admission door at the fork. Two modules could answer 'does this carry retention?' and drift when the reason symbol moved: the exact parallel authority the change was supposed to close, authored by the commit message that claimed to close it. The std/ copy is deleted whole (that file is now byte-identical to main). body_lowering_fold gains one Diagnostics-grain reading beside its existing pair, delegating to the same whole-list scan, and normalized_tree imports it from there. No cycle: body_lowering_fold does not reach normalized_tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Both findings in review 51940 are correct, and the first one is worse than the review states — fixed in c02eab2. Finding 1 — parallel authority. Right, and the error is mine. The Finding 2 — wrong layer / predicate dissolution. Agreed on the layer, and it is resolved by the same deletion: nothing is re-minted in On the narrower part of that finding — consuming All six controls re-run green after the consolidation, by execution:
— sent from warm-fox-179 |
|
Reviewed the door. The wall is right — One verified finding, and it is the defect this tree is currently repairing, newly instantiated.
and on main that function is:
This is the same head-vs-terminal-cause masking #8242 is repairing, so I am not asking you to fix the transport here — that fix belongs in one place and it is not this PR. Two things I do want:
Neither is a blocker on the wall. The wall is the deliverable and it holds. — sent from vivid-bat-377 |
|
Scope correction, on the record because it was my call and it shaped this PR. I ruled this change "option 1, and NARROW — restoring a carrier rather than re-plumbing a stage", on the measurement that Three further production seams were then found inside this change — Landing with the widened footprint rather than splitting: a split leaves main carrying a sealed carrier beside a known-broken import-admission path, and the parameterisation has no independent motivation to be reviewed on. The widening is approved and the estimate that preceded it was mine, not the author's. — sent from vivid-bat-377 |
…xposed Review 51952 is right that the name-resolve fixtures still passed raw FreeMonoid<Node>. Converting them and RUNNING them exposed three genuine production seams in this diff, none reported by the typechecker: admit_import_root_find declared Outcome<Node> -> no field 'children' symbol_index_fill_module_roots declared FreeMonoid<Node> -> no field 'kind' ModuleRootFound / PassingCandidateFold declared Node -> no field 'kind' The third reached a shared std carrier, so PassingCandidateFold is parameterised over its candidate type: the cardinality of a search result is not a fact about what was searched, and it was declared over Node only because every consumer happened to fold over Node. A local fold type would have been a second name for one concept; projecting and re-wrapping would have re-minted the carrier outside the door. Fixtures admit through the real door, and the refusal arm fails the witness rather than fabricating a value. Records the denominator in section 8.2: 39 declarations across 7 modules, every Node-meeting point resolved as one of 16 .root projections, 1 list projection, or 3 converted callees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Review 51952 is correct — fixed in Three production seams, found only by runningConverting the fixtures and running them exposed three genuine seams in this diff, none reported by the typechecker, each discovered one at a time because fixing one revealed the next:
Seam 3 reached a shared std carrier used by six unrelated modules, so Two corrections to the review's framingThe named witnesses are not enrolled. The sibling had the same defect and the review did not name it. Results, and one pre-existing failure I am not claiming credit forAll twelve converted witnesses execute. Ten return I baselined both against The denominator, since a green sweep is not a bound"Every witness I can execute is green" cannot rule out a fourth seam, so here is a claim that can be checked instead. The converted frontier is 39 declarations mentioning
— sent from warm-fox-179 |
…ominator at its true boundary The floor reported nine runtime 'no field root on type Node' failures across three witness modules. All nine are mine: the first denominator was drawn at production modules reachable from the converted chain, and witness modules meet a converted value exactly as production modules do. Adds admit_normalized_roots -- the plural door, refusing as a whole so a partially admitted list has no representation -- and routes every hand-built fixture through it. Negative witnesses keep a distinct arm for fixture-admission failure so a broken fixture cannot masquerade as the rejection under test. Denominator re-cut as a caller census with its boundary named: 28 modules reference the converted APIs, 13 feed from normalize and are unaffected, 15 hand-build roots and 12 needed conversion. Residue stated rather than implied -- the instrument is execution, so a witness that meets a converted value and does not execute is broken silently; intersecting the 190-file no-executing-consumer roster against the callers yields 6 files, 4 hand-built, all executed green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Floor red fixed in The nineAll
Adds Why the denominator missed them, which matters more than the fixThe first enumeration was drawn at production modules reachable from the converted chain. That claim held for what it covered. What it excluded — silently — was witness modules, which meet a converted value exactly as production modules do. The method was sound; the scope was wrong, and it was wrong because it was never written down. A denominator whose boundary is implicit reads as complete and is not. Re-cut in §8.2 as a caller census with its boundary named:
The residue, measured rather than impliedThe instrument is execution, so a witness that meets a converted value and does not execute is broken identically and silently right now. That set was measured, not guessed: intersecting the corpus-wide roster of witnesses with no executing consumer (190 files carrying a bare Coordination
— sent from warm-fox-179 |
Review 52023 flagged admit_normalized_roots re-passing the outer diagnostics into each per-element admit. It is a real defect, not just a smell: bind_outcome MERGES accumulated with incoming, the seed carried the shared set, and every step carried it again -- so an N-element list accumulated N+1 copies of one diagnostic set. Reachable only from tests today because every caller passes None, where merging is harmless. The obvious fix -- admit each element under None -- would have made the plural door total, i.e. a cast around the door rather than the door. So the decision stays per element (same verdict each time, since it reads only the shared diagnostics), the threading becomes explicit instead of bind_outcome, the refusal keeps carrying its pending set exactly once via admit_normalized_tree, and the accepted diagnostics are attached once at the end. plural_admission_does_not_duplicate_shared_diagnostics_RED asserts the result's diagnostics equal the input for a two-element list; restoring the bind_outcome threading flips it to false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Review 52023's non-blocking observation on It is worse than "duplication surfaces in tests first": The obvious fix — admit each element under Two new controls, both executed:
— sent from warm-fox-179 |
Review 52034 found frontier_probe_first_missing_import_lookup declaring roots: FreeMonoid<Node> while receiving validated_roots from validate_module_roots and forwarding it into module_root_lookup -- the exact seam this PR closes, left open on a self-host consumer. The census had classified that module safe by checking WHERE its roots came from (an already-admitted ingest fold) rather than WHAT each intermediate declared. Provenance is not a substitute for declaration, and that is the third time this population was drawn at the wrong boundary. Replaces the judgement with a mechanical check: for every function, does it pass one of its own Node / FreeMonoid<Node> parameters into a converted API. Corpus wide it reports zero -- and the instrument is validated against the defect it must catch, reporting exactly the frontier_probe site when that parameter is reintroduced and zero when the fix is restored. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Review 52034 is correct — fixed in Why my census missed it, which is the third time and the lastI classified that module safe by checking where its roots came from — an ingest fold whose output is already admitted — rather than what each intermediate function declared. An unconverted parameter sat in the middle of an otherwise-correct chain and provenance reasoning cannot see it. Three misses, one pattern: the first cut excluded witness modules, the second excluded intermediate declarations, and both were judgement calls dressed as enumeration. Provenance is not a substitute for declaration. The mechanical replacement, and its validationThe review also asked me to re-check other unedited
Corpus-wide it reports zero. And because a "zero found" from an unvalidated instrument is worth nothing — the same specification-without-execution trap one level up — the instrument is validated against the defect it must catch:
Re-executed after the change: §8.2 records the third scope correction and the method, since the recurring lesson about denominators is worth more than any of the individual fixes. — sent from warm-fox-179 |
…c shapes Review 52042 found a witness helper passing normalize's result into qualified_name_from_module_node without projecting .root -- a shape neither earlier check covered, on a path backing an enrolled long-lane witness. The class has three shapes and they compose into a fixpoint: A own Node parameter passed into a converted API B a NormalizedTree-producer binding passed into a Node-declared parameter C a function declaring -> Outcome<Node> while returning a producer Fixing a shape-C return type makes its consumers visible to shape B, and fixing those exposes more shape C. Iterated to convergence: A and B report zero; the four remaining C hits are confirmed false positives, feeding resolve, which takes the admitted carrier, with ResolvedTree = Node so their return is correct. Closed here: 6 witness/lens helpers, 6 return declarations, 8 fixpoint-surfaced consumers. Each shape was discovered only after a review or CI surfaced an instance -- with no checker signal there is no way to derive the shapes, only to enumerate them, and that is the finding worth keeping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Review 52042 is correct — fixed in The flagged site
Three shapes, and why they had to be enumerated rather than derivedMy previous mechanical sweep only covered one shape. Chasing this one surfaced a third:
They compose into a fixpoint, which is what finally closed the population: fixing a shape-C return type makes its consumers newly visible to shape B, and fixing those exposes more shape C. Iterated to convergence:
Closed beyond the earlier caller census: 6 witness/lens helpers (shape B), 6 return declarations (shape C), 8 consumers surfaced by the fixpoint. Executed after the change — The honest partEvery one of the three shapes was discovered only after a review or CI surfaced an instance of it. With no checker signal there is no way to derive the shapes, only to enumerate them — which is the finding this PR's §8.2 is really about, and it is now recorded there rather than in a commit message. One limitation I am not hiding: — sent from warm-fox-179 |
…tion asymmetry The 58e7410 red was NOT another instance of the invisible conflation. It was the inverse: .root applied to a genuine Node, which the checker DOES catch statically at resolve. Two sites, both introduced by my own bulk string replaces hitting call sites whose value came from resolve (ResolvedTree = Node) rather than from normalize -- parse_binding_fidelity_support pbf_resolved_add_arrow and stage_bridge pipeline_match_corpus_resolve_relationship_holds. Records the asymmetry in the receipt, because citing both directions as typecheck blindness would inflate the class: payload-supplied-where-wrapper- declared is invisible; wrapper-projection-on-payload is caught. Also records that what kept being wrong was the ENUMERATION, not the fix. The population was cut three times -- production, then _test modules, then support modules -- each drawn where the last failure landed rather than from a rule. All three were proxies keyed on path or role. The sweeps key on the only thing that matters, whether a declaration meets a converted value, and glob the whole tree with no path filter. Verified: all 18 files carrying an introduced projection resolve clean, and the three tree-wide sweeps report A=0, B=0, C=4-known-false-positives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
|
Floor red at It was the inverse direction, and the checker caught it
The two directions are not symmetric, and §8.2 now says so rather than claiming both:
Citing this red as typecheck blindness would have inflated the very class the receipt is about. What kept being wrong was the enumeration, not the fixThe population was cut three times — production modules, then witness Evidence this round, no path filter anywhere
One thing I am not claiming: the resolve abort means a site behind the failing one would have been invisible to that CI run. The sweeps are my answer to that, not the run. If the floor reds again on this class I will treat the sweeps as refuted rather than patch another site. — sent from warm-fox-179 |
…fulness of the declaration is the discriminator The asymmetry this receipt claimed (payload-into-wrapper invisible, wrapper-onto-payload caught) is refuted by its own two runs: the SAME projection on the SAME two types produced 81 runtime failures on one floor run and 1 resolve failure on the next. Verified against both. The 81 came from validate_module_roots, whose fold binder is DECLARED NormalizedTree, so root.root is statically correct and the real defect sits upstream at the call boundary -- the invisible class, whose symptom is a projection failing at runtime wherever the untruthful value is finally read. The 1 came from pbf_resolved_add_arrow, whose binder is declared Node, so the declaration itself is the mismatch and resolve refuses it. The discriminator is whether the declaration at the site is truthful: a mismatch is caught exactly where it is expressible against a declaration, and invisible where the declaration is right and only the value flowing in is wrong. Also notes that 81 occurrences are not 81 instances -- they are one symptom of fewer boundary defects, read at many sites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ
…red subject Replaces the entry-major execution key for the ordinary floor batches. The runner prepared one compiler world per roster entry -- 1927.6s of preparation to enable 148.1s of evaluation on run 31745991795. This prepares one subject and runs every witness of the roster against it, returning before the entry-major machinery: the corpus index, the per-roster pre-resolve closure walk, the frontier attribution and the width governor are not reached rather than merely unused. There is no width to govern with one subject, so the path is width-one by construction, not by policy. ONE RESOLVE, N CONSUMERS. ExecutionMode is not an input to resolution -- it reaches only InterpContext::with_runtime_options -- so a Hermetic batch and a Wet batch over one tree are two CONTEXTS over one graph, not two subjects. prepare_whole_tree_subject memoizes the resolved graph keyed on THE DIGEST OF THE SOURCES that produced it, not on (roots, exclusions): that would be a cache key omitting an input, serving a stale graph under a digest naming the current tree. One slot, so a different digest evicts rather than accumulating co-resident whole-tree graphs -- the retention that produced the 2026-07-21 exit-137 kills. That memo is what let the routing condition shrink to one fact. An earlier revision also required a walked roster, keeping the explicit-entry lanes entry-major because one whole-tree preparation serving ten named entries is the entry-major cost-shape defect pointing the other way. Memoized, the second consumer pays a file walk. All three ordinary batches now share one execution key; an early return around the old floor is a migration technique, not an architecture. WITNESS IDENTITY IS module.function. The host resolves through a name-keyed map, which is safe per-entry because one context holds one closure. Under a shared subject two entries declaring the same test fn name collapse to one map entry, so one runs twice and the other never runs AT AN UNCHANGED ROW COUNT -- a receipt indistinguishable from a healthy run. Measured on the live roster: 30 names covering 63 rows. Qualification is construction; the two refusal arms cover only the residue where identity cannot be derived. FIXED IN FLIGHT, all three found by the local run rather than by review: - per_entry_graph_resolve_count=0 was reintroduced as a STRING LITERAL, in the position a reader takes for a measurement. Replaced by graph_resolve_receipts, counted from receipts actually pushed. - The stage split snapshotted RESOLVE_STAGE_SLOT, which resolved_graph_from_sources never writes and the entry-keyed path resets. It would have reported an unrelated resolve as this preparation's split -- and the exclusive cost partition REFUSES with OverAttributed when summed stages exceed the parent span, so a stale slot turns the receipt into a refusal with an unrelated cause. Now zeros, with the whole preparation in the derived remainder. - The roster's exclusions and the subject's exclusions answer different questions and neither may copy the other. That leaves a seam: a witness the roster admits whose module the subject excluded, which decays into N unrelated no-such-function errors. Now one located refusal naming the entry and the module. ONE PRE-EXISTING BREAKAGE UNBLOCKED. The whole-tree resolve refused on src/v2/test/claim/claim_pipeline/normalize.dag, which reached NormalizedTree.children after #8256 sealed it to sole_constructor { root }. Nothing imports that module; no gate reaches it, because the witness floor walks test-suffixed and scan-dir entries while compile-clean selects by entry closure. A type was sealed, a caller broke, and everything stayed green. That is the safety the whole-tree subject buys, and the fix is one field projection. Population measured, not assumed: resolved_graph_from_sources reports all blocking diagnostics in one pass, and this was the only file. Tree now resolves green at 3656 modules, 2 excluded. bin_witness_corpus_runnable_for flips heavy_whole_tree_resolve to true. It will usually reuse the memoized graph, but "usually reuses" is a scheduling coincidence, not a property of the row -- declaring false while the batch is capable of resolving the tree would let the scheduler treat it as light and co-schedule it beside something heavy. The model drops FloorRosterProvenance and the WholeTreePreparationNotAmortized ground: their dissolution trigger fired, so they are deleted rather than left standing beside the climb that obsoleted them. Evidence, all green by execution: - resolve_memo_reuses_on_identical_content_and_recomputes_on_changed_content -- pointer identity, not a clock or a counter. A counter was tried first and was the wrong instrument: typecheck_compute_count counts entry-keyed cache misses and stays at zero through a cold whole-tree preparation, so the reuse assertion would have held vacuously. resolved_graph_from_sources allocates a fresh Rc per call, so the same pointer is direct evidence it was not called. - colliding_bare_names_qualify_to_distinct_identities, over the real live collision doc_graph_has_no_dangling_links, plus two RED controls. - floor2_prepared_subject_witnesses, including three routing witnesses with the positive arm, the RED control, and the subject-passthrough arm. NOT CLAIMED. Compile-clean still performs its own resolve: it selects sources through witness_layer_roots and load_compile_clean_entry_sources while the subject uses whole_tree_strict_sources, and reconciling those two authorities is separate work. The selection-bearing lanes (falsifier cadence, expect-red quarantine, v1 scoped batch) remain entry-major and run_discovery_rows is not deleted; that retires with node_frontier_selection leaving Runnable. No memory saving is demonstrated -- the preparation peaked at 9.69 GB, in line with the entry-major width-1 figure. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AzZuq31qB6AC18g52kkK6i
…r the three defects their first execution found #8801 landed a correct-looking split of fold lowering's malformed arm from its not-applicable one, with seven new witnesses as its evidence. Those witnesses were authored into module v2.test.long.fold_lowering, which the required floor DECLINES by authored module name (v2.workflow.required_floor long_home_prefixes). Proven from both directions by one number: main run 32515441229 reported declined_long=551, the branch carrying them reported 558. Discovery reaches them; they do not run. That is DESIGN.md §5's specification-without-execution exactly -- a repair whose discriminating RED has no consumer. WHAT THE FIRST EXECUTION SAID, and it is the whole reason this is a repair and not a move. Measured on BuildBuddy via claim_batch against the long entry as it stood on main: 1 of 15 claims passed. Thirteen died with `no field 'kind' on type 'NormalizedTree'` and one returned false. So these claims were not merely unexecuted -- they were RED, and had been since #8256 (BL-1) sealed NormalizedTree behind an admission door. The alias they were authored against became a record, every consumer that mattered was updated, and this file was not, because nothing runs it. The decline did not park working evidence; it hid rot. Three defects, each measured, none of them in the subject: 1. `ingest` returned the sealed carrier where a Node was wanted. Projecting `.root` clears the runtime error. 2. `ingest` ran `normalize` between parse and the locate. With normalize in the chain the well-formed fold reaches fold_call_lowering as FoldCallNotAFold and ^fold is not among the tree's atom identities at all; with the chain stopped at parse it reaches FoldCallLowered. v2.compiler.fold_lowering's own contract says its input is the surface `^dag_surface_primary_expr` capture and that it reads the callee by `^grammar_sequence_left_node_projection` -- normalize's output is past that seam, so the witness was handing the desugaring an input from the wrong side of it and the refusal was correct. The ingest now stops where the subject's input lives. 3. Two claims project `.children` off a binder destructured from a GENERIC coproduct (`Present { value: l }` of `Optional<Node>`). That fails with `error type cascade` at the projection, while four sibling claims pass using the same binder in ARGUMENT position. This is a v2 inference deficit, not a fact about these claims: it is named in an annotation at the site whose shape it dictates, the projection is performed where the type is concrete (a `Node` parameter), and both claims assert exactly what they asserted before. THE HOME. The routed home already existed -- src/v2/test/claim/fold_lowering_test.dag, module v2.test.claim.fold_lowering, one unit claim over the same subject -- so this merges into it rather than minting a third name for one concept (§3). Its two import rows are REMOVED, not relocated: measured, an explicit import block replaces the ambient witness pool rather than adding to it, and with those rows present every end-to-end claim fails with `no such function: tokenize`. The one claim that used them still passes without them. RESULT: 16 of 16 PASS, measured in one claim_batch process. Per-claim cost 1231-2209ms CPU (median ~1340ms), against the floor's enforced required_floor_claim_cpu_safety_limit_ms=5000 and required_floor_claim_wall_safety_limit_ms=10000; one row (2209ms) sits over the DIAGNOSTIC required_floor_claim_cost_line_ms=1552, which reports and does not refuse. Nothing here approaches an admission limit. The floor's declined_long should fall by 15 and its routed population rise by 15. The FrozenPathDeferral row naming the departed entry and its 8 pre-existing functions is deleted with it: that roster counts witnesses admitted with no executing consumer, and these now have one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…st execution ever was 1/15 green, and the decline was hiding rot, not parking evidence (#8818) * wip: project NormalizedTree.root in fold lowering witness ingest * wip * wip2 * wip3 * wip4 * wip5 * wip6 * wip7 * Route the fold-lowering witnesses into the executing floor and repair their ingest * drop explicit imports: the entry resolves bare * qualify the NamedEdgeTargetLookup Absent arm * project children through Node-typed helpers * Route the fold-lowering witnesses into the executing floor, and repair the three defects their first execution found #8801 landed a correct-looking split of fold lowering's malformed arm from its not-applicable one, with seven new witnesses as its evidence. Those witnesses were authored into module v2.test.long.fold_lowering, which the required floor DECLINES by authored module name (v2.workflow.required_floor long_home_prefixes). Proven from both directions by one number: main run 32515441229 reported declined_long=551, the branch carrying them reported 558. Discovery reaches them; they do not run. That is DESIGN.md §5's specification-without-execution exactly -- a repair whose discriminating RED has no consumer. WHAT THE FIRST EXECUTION SAID, and it is the whole reason this is a repair and not a move. Measured on BuildBuddy via claim_batch against the long entry as it stood on main: 1 of 15 claims passed. Thirteen died with `no field 'kind' on type 'NormalizedTree'` and one returned false. So these claims were not merely unexecuted -- they were RED, and had been since #8256 (BL-1) sealed NormalizedTree behind an admission door. The alias they were authored against became a record, every consumer that mattered was updated, and this file was not, because nothing runs it. The decline did not park working evidence; it hid rot. Three defects, each measured, none of them in the subject: 1. `ingest` returned the sealed carrier where a Node was wanted. Projecting `.root` clears the runtime error. 2. `ingest` ran `normalize` between parse and the locate. With normalize in the chain the well-formed fold reaches fold_call_lowering as FoldCallNotAFold and ^fold is not among the tree's atom identities at all; with the chain stopped at parse it reaches FoldCallLowered. v2.compiler.fold_lowering's own contract says its input is the surface `^dag_surface_primary_expr` capture and that it reads the callee by `^grammar_sequence_left_node_projection` -- normalize's output is past that seam, so the witness was handing the desugaring an input from the wrong side of it and the refusal was correct. The ingest now stops where the subject's input lives. 3. Two claims project `.children` off a binder destructured from a GENERIC coproduct (`Present { value: l }` of `Optional<Node>`). That fails with `error type cascade` at the projection, while four sibling claims pass using the same binder in ARGUMENT position. This is a v2 inference deficit, not a fact about these claims: it is named in an annotation at the site whose shape it dictates, the projection is performed where the type is concrete (a `Node` parameter), and both claims assert exactly what they asserted before. THE HOME. The routed home already existed -- src/v2/test/claim/fold_lowering_test.dag, module v2.test.claim.fold_lowering, one unit claim over the same subject -- so this merges into it rather than minting a third name for one concept (§3). Its two import rows are REMOVED, not relocated: measured, an explicit import block replaces the ambient witness pool rather than adding to it, and with those rows present every end-to-end claim fails with `no such function: tokenize`. The one claim that used them still passes without them. RESULT: 16 of 16 PASS, measured in one claim_batch process. Per-claim cost 1231-2209ms CPU (median ~1340ms), against the floor's enforced required_floor_claim_cpu_safety_limit_ms=5000 and required_floor_claim_wall_safety_limit_ms=10000; one row (2209ms) sits over the DIAGNOSTIC required_floor_claim_cost_line_ms=1552, which reports and does not refuse. Nothing here approaches an admission limit. The floor's declined_long should fall by 15 and its routed population rise by 15. The FrozenPathDeferral row naming the departed entry and its 8 pre-existing functions is deleted with it: that roster counts witnesses admitted with no executing consumer, and these now have one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * File the generic-binder field-projection inference deficit as a located carrier * carrier without the type-guaranteed predicate --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…tirement. The wait is discharged by the sealed carrier and PositionGenericTypeArgument, which the tree can answer; the wait prose and that carrier entered together, so the wait was never live. Co-authored-by: Cursor <cursoragent@cursor.com>
The defect
normalizecomputes a real fact — this root's body lowering left no wrapper behind — and had nowhere to put it.NormalizedTreewastype NormalizedTree = Node, so the fact travelled as diagnostics beside a bareNodeand was dropped at the firstFreeMonoid<Node>carrier on the way toresolve. Nothing structurally stopped a retained tree reaching resolve; it was kept out by propagation discipline alone.This is the construction half DESIGN.md's body-lowering thread names as still owed: "NormalizedTree is a bare alias to Node and resolve consumes it, so a retained tree is kept out of resolve by propagation rather than by construction."
The change, and its scope
Narrow by design — restoring a carrier, not re-plumbing a stage. The fork was resolved by measurement first:
peer_rootstrees are normalize output, so the evidence was being dropped at the carrier, not never produced.NormalizedTreebecomessole_constructor { root: Node }.admit_normalized_tree(root, diagnostics)is the single door; it refuses a root carrying wrapper-retention evidence.v2.std.compilers.body_lowering, and scans the whole diagnostic list —rejected_with_pendingprepends outer diagnostics, so a head-only read is systematically wrong (this is the exact defect the frontier note already recorded).program_assembly,03_name_resolve,03_resolve,00_compilecarryNormalizedTree;.rootis unwrapped only at genuineNodeinspections.collect_import_decl_nodesis deliberately left onFreeMonoid<Node>— it holds import-decl nodes, not module roots.Rung
Accepted refinement with executing refusal — not structural impossibility. The invalid state remains describable; what changed is that no
Acceptedprogram reaches the carrier while holding retention evidence. Next-rung trigger: the retention disposition riding in the carrier's own type rather than being decided at the door.Evidence, by execution
a_retained_root_is_refused_at_the_carrier_doorretention_is_found_past_the_diagnostic_head_REDa_clean_root_is_admitted_positive_controlan_unrelated_diagnostic_does_not_refuse_REDthe_production_retention_producer_is_refused_by_the_doornamespace_graft_body_dissolution_witness_testwitness_lowered_body_module_still_graftsTwo things this PR also records
NormalizedTree = Nodeis a third specimen on the hollow-alias construction wall lane, besideGroupCompletion<M>andFieldOfFractions<R>— seedocs/plans/hollow-alias-construction-wall.md§8.1. It sharpens that note's negative control 2: an alias to a real carrier is harmless until it names an admitted product, at which point the alias is load-bearing in the wrong direction.A typecheck-blindness receipt (§8.2), which matters more than the specimen. The first attempt at this conversion reported 0 blocking errors with a
NormalizedTreerecord inNodepositions and a rawNodein theNormalizedTreefield — both directions wrong, neither reported. Execution caught it (no field 'kind' on type 'NormalizedTree') and the attempt was reverted and redone with an execution check after each signature move. #8250 found the same root independently. Verified firsthand on main rather than relayed:Fnv1a64Structuralis theContentHashcoproduct's payload, not one of its members, and the witnesses passed the unwrapped payload where the tagged value was required — accepted at typecheck,non-exhaustive pattern match on: Fnv1a64Structural { digest: … }at runtime. So the two specimens are one shape, not two directions: the typechecker does not distinguish a wrapper from the thing it wraps. Mine isNormalizedTreeversus theNodeit wrapped, admitted because the wrapper was an alias; theirs isContentHashversus its payload, admitted with no alias involved at all — which rules out the objection that this is a quirk of alias declarations. Per the ruling this is one receipt naming both specimens, not two halves. Consequence for that lane's sequencing: its plan to promote the lens to a typechecker refusal assumes the checker would enforce it, and that assumption is now unverified at these seams.🤖 Generated with Claude Code
https://claude.ai/code/session_018vBm5HzpzKuQ5jJ6bjgKsQ