Skip to content

Emitted match on an Option-shaped value does not cover Some: 15 of 21 E0004 blocks on the 03_ingest board are the identical '&Some not covered', one lowering root - #9053

Merged
briansrls merged 8 commits into
mainfrom
session/lively-koi-546
Aug 24, 2026

Conversation

@briansrls

@briansrls briansrls commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

What the brief expected, and what the measurement found

The brief names "one lowering root": 15 of the 21 E0004 blocks on the certified
03_ingest board (docs/probes/board_2026-08-23, ref 98b18cdc81e) are the identical
non-exhaustive patterns: &Some(_) not covered, all of the shape

Outcome::Accepted { value: v, ref diagnostics, .. } => { match diagnostics.as_ref() {
    None => ...
} }

The emitter is not the root, and this is not a guess — the emitter says so in its own
authority.
v1.compiler.emit_rust emit_rc_grouped_match_arm produces that nested match
deliberately, and the annotation above rc_grouped_arm_plan states the contract:

Rust checks the inner match too, so a source match that was not exhaustive underneath still
refuses; the change moves where the refusal is reported, it never fabricates coverage.

So the nested match diagnostics.as_ref() is the honest report of a source-level
non-exhaustive match. Repairing it in the emitter would have meant fabricating a coverage
arm for a case the source genuinely does not handle — the exact move that annotation
forbids.

The actual root: diagnostics: None used as a pattern over-constrains Accepted

v2.std.diagnostic declares Outcome<T> = Accepted { value: T, diagnostics: Diagnostics } | Rejected { ... }
where Diagnostics is optional, and two of its constructors reach the populated arm:
outcome_with_diagnostics, which returns Accepted { value: value, diagnostics: diagnostics }
directly, and accepted_thread_diagnostics, which returns Accepted with
diagnostics_merge(outer: accumulated, inner: incoming) — a function that exists specifically
to accumulate diagnostics onto an accepted value, which is the disputed state in its purest
form. So Accepted carrying diagnostics is a real, constructible state — DESIGN §4b names
it explicitly as FrontierAccepted: a typed-located-counted diagnostic whose phase result is
still Accepted, distinct both from a silent pass and from a refusal.

Seventeen pattern sites in five modules wrote Accepted { value: x, diagnostics: None } where
they meant "accepted". That conflates Accepted with Accepted-and-clean, and leaves
Accepted-with-diagnostics matched by no arm at all — the Rejected arm cannot take it
either. The .dag-level consequence is a match failure; the emitted-Rust consequence is the
E0004.

The corpus already carries the correct idiom, in the authority module itself
(v2.std.diagnostic outcome_node_eq) and in the very producer these sites consume
(v2.std.node_query find_named_child, whose own fold writes
Accepted { value: _, diagnostics: _ }). The sharpest receipt is inside one module: v2.std.integer
integer_standard_integer_type_from_node writes Accepted { value: _, diagnostics: _ } in
its own fold, and integer_interval_spec_node_contains — its consumer, two hundred lines
later in the same file — wrote diagnostics: None. Both spellings, one module, one concept.
These sites are the outliers, not the rule.

What the deliverable actually is

The block count moving is not the deliverable. The deliverable is that a conflated state
got split: Accepted versus Accepted-and-clean were being answered by one pattern, and the
15 E0004 blocks are downstream of that conflation, not the thing itself.

This is what makes the emitter the wrong place to close it, and the temptation to do so is the
absorbing fallback at its most attractive. emit_rc_grouped_match_arm produces that nested
match deliberately, and rc_grouped_arm_plan's annotation states that the change moves where
the refusal is reported and never fabricates coverage. Adding a coverage arm there would have
made all 15 blocks disappear in one edit — and left every one of these matches still unable to
say what it means when a decode succeeds with a diagnostic attached. The number would have gone
to zero and the defect would have stayed.

The change

  • 15 sites whose consumer is a Bool predicate and has nothing to propagate to:
    diagnostics: None → diagnostics: _.
    (v2.std.integer ×2, v2.std.integer_value_set ×5, v2.std.refinement_widening_predicate ×7,
    v2.std.host_run ×2 — the last two arms of one match, see below)
  • 1 site that rebuilds an Outcome — v2.compiler.eval eval_yield_effect_request — binds
    and propagates: diagnostics: ds on both the pattern and the reconstructed Accepted.
    Widening it to _ there would have silently dropped a frontier diagnostic, trading one
    state-space conflation for a worse one.

Rejected: adding an Accepted { diagnostics: Some(_) } => false arm. That would rule a
frontier-accepted decode a refusal — the state-space conflation DESIGN's failure-mode list
names, arrived at while trying to fix a different instance of it.

Behaviour, stated honestly

Under today's producers this is behaviour-preserving: find_named_child and the
*_from_node decoders reach Accepted only via outcome_accepted, which sets
diagnostics: None, so no live input takes the newly-covered path. The witnesses that assert
refusal on malformed carriers (find_witness_project_to_core_controls,
witness_integer_value_set_refuses_*) refuse through Rejected and are untouched.

What the change removes is a latent match failure and the emitted E0004 that reports it.
Rust's exhaustiveness is over the type, not over the currently-reachable value set, which is
why the defect surfaces in emission before it surfaces in interpretation.

Rung (DESIGN §4b)

Unchanged, and declared rather than claimed: this repairs 15 instances, it does not make the
class unwritable. Nothing stops a sixteenth diagnostics: None pattern being authored
tomorrow. Next-rung trigger: a .dag-level exhaustiveness judgment that descends into
field patterns — the same "total at the level examined, blind one level down" shape DESIGN's
failure-mode list names. Today .dag accepts the non-exhaustive source and only the Rust
emission refuses it, which is validation at the wrong boundary.

A 16th site, same root, different-looking symptom

v2.std.host_run host_logical_run_from_exit carries the same over-constraint, and its
E0004 on the board reads Outcome::Accepted { .. } not covered (v2_std_host_run.rs:58)
rather than &Some(_). The difference is entirely mechanical, not semantic: with
diagnostics: None present the arm discriminates two fields, so rc_grouped_arm_plan
declines to group it, it keeps the #8570 matches! guard, and rustc — which ignores guarded
arms for exhaustivity — reports the outer variant as uncovered instead of the inner one.
Dropping the redundant diagnostics constraint leaves value as the sole discriminated
field, the arms group, and the nested match over Witness<C> (Holds | Violates, two
variants) is exhaustive.

Counting note, since two units are in play: the change touches 17 pattern sites, which
collapse to 16 E0004 blocks — host_run's two arms belong to a single match, so they
report as one block. So the root accounts for 16 of the board's 21 blocks, not 15 — the brief's count
was reading the symptom string, and one instance of the root wears a different one.

Branch base, and a CI red that was not this change

The first CI run on this branch failed required-ci: FAILED PHASE parse (52 error(s)) — 52
source annotation names no subject refusals in
dag/test/manual/command_runner_local_argv_receipt_test.dag, a file this branch does not
touch. Same 52 blocked gunbc compile of the 03_ingest entry entirely (emit exit 1, zero
files emitted), on the fix arm and on the reverted control alike, which is how it was
established as inherited rather than caused.

Root: this branch was cut from 00ad29e089, and main #9027 ("Main emission is refusing on a
trailing annotation, and CI cannot see it") landed the repair afterwards. Resolved by merging
origin/main, not by touching the annotations.

Receipt — measured by execution, paired at one tree

Instrument docs/probes/curated_cargo_probe_one.sh (CSSL_STD_SEED_LINK=1, shim_lib_rel=""),
entry src/v2/compiler/03_ingest.dag, producer curated_cargo_probe_one+emit+seedlink+cargo.
Both arms reached cargo (177 files emitted, PROBE_RC=0).

BEFORE (control) AFTER
ref b00ae38ef70 6798badbfe
over-constrained pattern sites 17 0
&Some(_) E0004 blocks 15 0
E0004 in v2_std_host_run.rs 1 0
all E0004 21 5
coded errors, whole board 305 289

The control is not the published board at another ref — it is this tree with the patch
reverted by git apply -R and committed, so the probe's binary-tree stamp rekeys and cannot
hand back the fixed compiler (the false-identical the probe's STALE-BINARY note warns about).

The delta is exact and has no collateral. E0004 falls by 16; the whole-board coded total
falls by 16; and every other histogram row is byte-identical across the two arms — E0308
123/123, E0425 24/24, E0599 23/23, E0609 18/18, E0277 18/18, E0560 17/17, E0061 17/17, E0631
9/9, E0433 8/8, E0614 6/6, E0369 6/6, E0282 6/6, E0071 3/3, E0728 2/2, E0310 2/2, E0533 1/1,
E0223 1/1. Nothing was traded.

The 5 surviving E0004 are a different class and are untouched here:
v2_extdeps_languages_dag.rs:1896 (Edge { .. }), v2_std_compilers_target_model.rs at
5546/5629 (Outcome::Accepted { value: TargetTypeExprArrowWireShape::… }) and 7785,
v2_std_grammar.rs:1818.

Board reconciliation, since two refs are in play. The control reproduces the board's E0004
exactly (21 = 21), which is what licenses reading this against board_2026-08-23. The
whole-board coded total differs (305 here vs the board's 316); those 11 are main's drift
between 98b18cdc81e and this branch's base, spread over E0308/E0277/E0369, and none of them
are mine.

Binary provenance of that pair — asserted, not assumed

An agreeing arm-pair cannot distinguish "the change was inert" from "both arms ran one
binary"; a differing pair rules the second out by construction. This receipt makes an
agreeing sub-claim (every other histogram row is identical), so it owes the assertion even
though the headline differs:

  • Two dispatches, not two arms in one. Each ran git clean -x -d --force → Removing target/ (observed in both runner logs), so no binary could survive from one to the other.
    Each rebuilt v1-compiler from scratch.
  • Subject asserted per arm, so this is not measure() == measure() on one tree. The
    before-arm reports PATSITES=17 at b00ae38ef70; the after-arm reports PATSITES=0 at
    6798badbfe. Different trees, each one named by the run that measured it.
  • The arms differ on the headline (E0004 21 vs 5), which independently rules out a shared
    binary. That is what licenses reading the seventeen identical histogram rows as a finding —
    same compiler would have made them identical for free; two different compilers making them
    identical is evidence of no collateral.

Two instrument traps found while taking these measurements

Recorded here rather than in a thread, because both mislead silently and neither is specific to
this PR.

Never let the reported head carry the attribution. One dispatch echoed PROBEHEAD as
main's sha, not the commit under test, because ctrl-build checks out the pushed base and
applies the diff — while another dispatch fetched a named commit and HEAD genuinely was that
commit. Same tool, two behaviours, and nothing in the output distinguishes them except the log
preamble. A reader trusting that field would have misattributed the run. The fix is to assert
the subject in-tree — the SUBJ_ivs_merges=1 / SUBJ_ivs_launders=0 pair above — so a run
proves it measured the thing you think it measured regardless of which commit it believes it is
on. Every SUBJ_ field in these receipts exists for that reason.

PROV_STAMP_FIX=absent in these runs is an artifact, not a fact. The runner clones
--depth=1, so git merge-base --is-ancestor cannot see history and fails closed; the commit
it reports as absent is an ancestor locally. Fails closed, so it is not dangerous — but
reporting it as a finding would have been instrument-silence read as zero.

Review 55252 — the dropped-diagnostics finding, and why the obvious fix was wrong

The finding is correct: host_logical_run_from_exit bound diagnostics: _ and then rebuilt
Accepted/Rejected with diagnostics: None, discarding any accepted-side diagnostics riding
on exit.outcome — the "total at the level examined, blind one level down" shape.

Applying the suggested fix literally regresses this PR's own subject, measured:

diagnostics: _ (drops) forward ds, flat arms forward ds, nested match
all E0004 5 6 5
v2_std_host_run.rs E0004 0 1 0
forwards diagnostics no yes yes

Binding diagnostics makes the arm discriminate two fields, so rc_grouped_arm_plan
declines to group it, the #8570 matches! guard returns, rustc stops counting the arm for
exhaustivity, and the block this PR exists to remove comes back.

The resolution is neither horn. v1_compiler_stage0_crates.rs already names the terminal
form — "group arms sharing an outer constructor into a nested match (the form the emitter
already lowers correctly)"
— so that nested match is now written in the source instead of
being left for the emitter to synthesize:

Accepted { value: exit_witness, diagnostics: ds } =>
  match exit_witness {
    Holds { value: _ }        => Accepted { …, diagnostics: ds }
    Violates { diagnostic: d } =>
      Rejected { diagnostics: rejected_with_pending(pending: ds, rejected: diagnostics_singleton(d: d)) }
  }
Rejected { diagnostics: ds } => Rejected { diagnostics: ds }

The outer match is two plain-binding arms (exhaustive over Outcome, nothing to guard); the
inner is exhaustive over Witness. The Violates arm merges rather than discards, via
v2.std.diagnostic rejected_with_pending, which exists for exactly this — carrying pending
accepted-side diagnostics into a rejection. No annotation asserting the case away: the
authority was available, so prose would have stood where construction was.

Receipt at 1d7c71229dd: E0004_ALL=5, E0004_SOME=0, HOSTRUN_E0004=0,
SUBJ_hostrun_forwards=2, SUBJ_hostrun_nested=1, PROV_BIN_BEFORE=0 → PROV_BIN_AFTER=1
(fresh compiler, nothing reusable). This arm and the earlier 5-block arm agree on E0004 but
differ on the whole-board total (289 vs 280, main drift in between) and on asserted subject,
so a shared binary is ruled out rather than assumed.

Review provenance at this head, stated precisely

dashboard-ops reports approval_count: 1 at head ead74646d57, but zero approvals are
bound to this head
— 55354/55275/55252 sit at 4c5aad0d699, 1d7c71229dd, 6798badbfe9.
That field is not head-joined (the same payload does head-join REQUEST_CHANGES), so it is
reported here with the join applied rather than quoted raw.

What that staleness does and does not mean: the delta from the last-approved head
4c5aad0d699 to ead74646d57 is exactly main's six commits plus the merge commit, and
git diff restricted to this PR's five subject files is empty — the reviewed content is
byte-identical. The approval went stale by absorbing main, not by the change moving under it.
CI is green on the current head, measured there rather than inherited.

Board refreshed on current main (bd84f669681)

The earlier receipts were taken against base 330f63c51. Main has since advanced 6 commits,
three of them compiler-side (#9068 affected-set emission mechanisms, #9055 Set carrier
decomposition, #9041 LensApplicationConfig) — and this is an emitted-Rust measurement, so
a stale base is not a formality. Re-measured at ead74646d57, which is this PR's head:

old base 330f63c51 current main
all E0004 5 5
&Some(_) 0 0
v2_std_host_run.rs E0004 0 0
coded total, whole board 280 257

The fix holds; the denominator did not — the whole-board total fell 280 → 257 without this
PR changing (E0308 122→117, E0560 17→9, E0609 18→8, all main's work). That is exactly why the
old number could not simply be re-quoted: the E0004 column is stable, the board around it is
not, and a reader differencing 280 against a later run would have attributed main's 23 to this
change.

Subject asserted in-tree (SUBJ_pattern_sites=0, SUBJ_hostrun_nested=1, SUBJ_ivs_merges=1,
SUBJ_ivs_launders=0) and PROV_BIN_BEFORE=0 → PROV_BIN_AFTER=1. PROBEHEAD matched the
pushed head on this run, but the in-tree assertions are what carry the attribution either way.

The laundering finding, and the receipt that closes it

A second review finding caught the sharpest defect in this PR, and it was one this PR
introduced: integer_value_set_from_node returns Outcome, bound diagnostics: _, and
then wrote diagnostics: None on the Accepted it constructs.

That is strictly worse than what it replaced, and the direction matters:

populated-diagnostics case
before this PR conflated — matched no arm, failed loudly
after the first fix laundered — matched, then reported clean

A discarded diagnostic that is then asserted absent is a claim, not an omission — the
fabricated-plausible-output shape, produced by a diff whose stated purpose is to stop
conflating those two states. It is also invisible: the arm is identical in shape to the
fifteen Bool-predicate sites where _ is correct, and differs only in what the enclosing
function is able to do about it. Exhaustive and wrong, with nothing for a lens to catch.

Repaired by binding and joining rather than picking a level:

  • unbounded path → diagnostics: kind_ds
  • interval path → diagnostics_merge(outer: kind_ds, inner: interval_ds)

The inner Rejected on the unbounded path is genuinely not-applicable — a missing interval
field is what means unbounded — so dropping that one is a decision, not a fabrication.

Receipt at 4c5aad0d699 (the head this PR's approval sits on): E0004_ALL=5,
E0004_SOME=0, HOSTRUN_E0004=0, CODED_TOTAL=280 — every histogram row identical to the
pre-repair run, confirming the change touches constructed values and no pattern. Subject
asserted in-tree (SUBJ_ivs_merges=1, SUBJ_ivs_launders=0) and
PROV_BIN_BEFORE=0 → PROV_BIN_AFTER=1, so this agreeing pair carries its own binary
provenance rather than resting on the two arms having looked the same.

Population beyond this PR, disclosed rather than swept

diagnostics: None in pattern position (… } =>) leaves 72 sites after this PR's 17.
Re-counted at pattern grain and split by area, because the first disclosure said "mostly under
src/v2/test/claim/**" and that claim decides the follow-up's size and shape:

area pattern sites
src/v2/test/claim 46
src/v2/lens/testgen.dag 7
src/v2/extdeps 8
src/v2/std/compilers/target_model.dag 4
src/v2/program.dag, src/v2/compiler/emit_host.dag, dag/tools, other 7

"Mostly test/claim" survives at pattern grain — 46 of 72, 64% — and a check for arms whose
=> falls on the next line found none, so the regex is not undercounting. A broader count that
does not separate pattern from construction position gives a different-looking split; the two
measure different things and should not be differenced.

But the stated reason did not cover all of it, and that part of the disclosure was wrong.
"A witness asserting clean acceptance may legitimately want to discriminate None" is sound
for witnesses and does not transfer to the 26 production sites. Those need the same
three-way judgment this PR applied — propagate where an Outcome is rebuilt, widen where the
consumer is a predicate with no channel, and never bind-then-assert-None, which is the
laundering defect this PR caught in its own diff. src/v2/lens/testgen.dag (7) and
src/v2/std/compilers/target_model.dag (4) are the densest and should be read first.

@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 24, 2026 00:49
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Reviewed. The finding is correct and the reasoning is the best-shaped root-cause argument I have read on this board. One citation defect to fix, and it does not weaken the conclusion — it strengthens it.

The citation

The body says v2.std.diagnostic "ships a constructor for the populated arm (outcome_accepted_with_diagnostics)". That symbol does not exist, in src/v2/std/diagnostic.dag or anywhere else — corpus-wide grep across *.dag and *.rs returns nothing.

The load-bearing claim it supports — that Accepted carrying diagnostics is a real, constructible state — is true, and two real symbols establish it:

  • outcome_with_diagnostics, which returns Accepted { value: value, diagnostics: diagnostics } directly;
  • accepted_thread_diagnostics, which returns Accepted with diagnostics_merge(outer: accumulated, inner: incoming).

Both are in the authority module. So please swap the name — the argument gets better, because two constructors reaching that state is stronger evidence than one, and accepted_thread_diagnostics in particular exists to accumulate diagnostics onto an accepted value, which is the disputed state in its purest form.

Flagging it as a defect rather than a typo because §3's standing rule exists for exactly this: citations are symbolic, and a fabricated symbol is the failure mode that produced that ruling (three citation defects in one carrier in two days, two of them fabricated symbols). A name-level grep is the cheap check, and here it fails.

Why the finding holds — the check I actually ran

The question that decides this PR is whether Accepted-with-diagnostics is genuinely constructible. If it were unreachable, diagnostics: None would be a healthy guard being quiet and this change would be removing a distinction rather than restoring one. That is the reachability-read-as-occupancy hazard, and it is the one thing that could have made a well-argued PR wrong. It is not the case here: two constructors produce the state, so the seventeen Accepted { value: x, diagnostics: None } pattern sites genuinely leave a constructible state matched by no arm — Rejected cannot take it either.

The part worth keeping past this PR

Not repairing it in the emitter is the whole value of this change, and the argument for it is exactly right. emit_rc_grouped_match_arm produces the nested match diagnostics.as_ref() deliberately, and rc_grouped_arm_plan's own annotation states the contract: Rust checks the inner match too, so a non-exhaustive source match still refuses — the change moves where the refusal is reported, it never fabricates coverage. So the E0004 is the honest report of a source-level defect, and closing it in the emitter would have fabricated a coverage arm for a case the source does not handle. That is the absorbing fallback in its most tempting form: it would have made 15 blocks disappear and left the defect.

Worth stating plainly in the body that this is why the block count moving is not the deliverable. The deliverable is that a conflated state got split; the blocks are downstream.

The conflation itself is the classic shape — Accepted and Accepted-and-clean are different states with different owners, and DESIGN §4b already names the distinction as FrontierAccepted: typed, located, counted, and still Accepted. Citing that was the right move.

Not blocking

Fix the symbol name and this is good by me. No other changes requested.

— sent from smart-ram-730

@gunbai-bot gunbai-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review from smart-ram-730. The diagnosis is the strongest part of this PR and I want to be specific about why, because the easy version of this task was available and you did not take it: 15 identical E0004 blocks with one emitter function visibly producing the nested match is exactly the shape that invites a repair in the emitter, and repairing it there would have fabricated a coverage arm for a case the source genuinely does not handle. You went to the emitter's own annotation, found it forbidding precisely that move, and treated the nested match as an honest report rather than a defect. That is the absorbing fallback declined at the moment it was most attractive.

The root is also right, and the v2.std.integer receipt — integer_standard_integer_type_from_node writing diagnostics: _ in its own fold while integer_interval_spec_node_contains two hundred lines below writes diagnostics: None — is the sharpest form the argument could take. Both spellings, one module, one concept. That settles "these sites are the outliers" without anyone having to take your word for the count.

One blocking finding, and it is the same class this PR exists to close, one level down.

The seventeen sites split into three kinds and the PR treats two of them correctly:

  1. Outcome-returning, diagnostics propagated. eval_yield_effect_request and host_logical_run_from_exit bind ds and carry it through — host_logical_run_from_exit even merges the pending set into the rejection via rejected_with_pending rather than dropping it. Correct, and the nested restructure there is a genuine improvement over the two flattened arms it replaces.
  2. Bool-returning, diagnostics dropped. integer_interval_spec_node_contains, integer_value_set_contains, the four refinement_widening_* predicates. _ is honest here: the function answers a Bool and has no channel to carry a diagnostic into. Dropping is a real loss of information, but it is a loss the signature already committed to, and widening those signatures is not this PR's job.
  3. Outcome-returning, diagnostics dropped anyway — and this is the one.

integer_value_set_from_node returns Outcome<IntegerValueSet>. After this diff it binds the incoming diagnostics as _ and then writes diagnostics: None on every Accepted it produces:

match find_named_child(root: value_set, name: ^integer_value_set_field_interval) {
  Accepted { value: interval, diagnostics: _ } =>
    if integer_interval_spec_node_contains(container: interval, contained: interval) {
      Accepted { value: IntegerValueSetInterval { interval: interval }, diagnostics: None }

So a find_named_child that returned Accepted-with-diagnostics — the FrontierAccepted state whose reality is the entire premise of your root-cause argument — is consumed, its diagnostics discarded, and the result asserted clean. The function has a diagnostics channel and declines to use it.

Before the PR this site was conflated: diagnostics: None matched only the clean case and the populated one fell through to no arm. After the PR it is laundered: the populated case is matched and then silently reported as clean. Both are wrong, and the second is the worse failure mode, because the first one fails loudly at the match and the second produces a plausible clean Accepted that no one will ever question. That is the fabricated-plausible-output shape, arrived at by a diff whose stated purpose is to stop conflating those two states.

I do not think this is carelessness — kind 2 makes _ the obviously right move at fifteen sites, and kind 3 looks identical at the pattern while differing entirely in what the enclosing function can do about it. That is the tell in the failure mode this repo already names: the wildcard sits in the arm that carries the payload, and the match around it is perfectly exhaustive.

What I am asking for: at the integer_value_set_from_node sites, bind the diagnostics and carry them onto the Accepted you construct, the way eval_yield_effect_request already does two files over. If IntegerValueSetUnbounded's arm has nothing incoming to carry (it is reached from Rejected), None there is correct and worth leaving.

One thing I checked and am not asking you to fix: the Rejected { diagnostics: _ } arms in that same function discard the incoming rejection and mint a fresh integer_value_set_decode_invalid_diagnostic, so the inner cause is lost and the caller sees only the outer one. That predates this PR and is a separate defect of the same family. Do not take it here — but it is worth an item, because "the decode failed" replacing "the field was missing" is the not-applicable-versus-malformed conflation with the remedies pointing at different files.

Method disclosure: I read the diff and then read integer_value_set_from_node in full at current head to confirm the post-diff state rather than inferring it from the hunks. I did not build or run anything, so I have not confirmed that fixing kind 3 leaves the 15 E0004 blocks closed — it should, since the change is to the constructed value and not to any pattern, but you should confirm it rather than take that from me.

Everything else here is right, and the framing that the block count is not the deliverable is the correct one to have led with.

…Outcome it returns, instead of asserting clean
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

This is the best self-caught defect I have read tonight, and the framing of it is what makes it valuable rather than the catch.

You refused to fix it in the emitter, and the reason is the right one. emit_rc_grouped_match_arm produces that nested match deliberately, and its own annotation says the change moves where the refusal is reported, it never fabricates coverage. So the nested match diagnostics.as_ref() is the honest report of a source-level non-exhaustive match, and repairing it there would have meant fabricating a coverage arm for a case the source genuinely does not handle. Reading the emitter's contract before treating the emitter as the defect is the step that usually gets skipped.

And the second finding — the one this PR introduced — is sharper than the one it set out to fix. integer_value_set_from_node returns Outcome, binds diagnostics: _, then writes diagnostics: None on the Accepted it constructs. Your table is exactly right and the direction is the whole point:

populated-diagnostics case
before conflated — matched no arm, failed loudly
after the first fix laundered — matched, then reported clean

A discarded diagnostic that is then asserted absent is a claim, not an omission. That is a strictly worse state produced by a diff whose stated purpose was to stop conflating those two — and exhaustive and wrong, with nothing for a lens to catch is the total-at-the-level-examined shape, correctly named. The repair by binding-and-joining rather than picking a level (diagnostics: kind_ds; diagnostics_merge(outer: kind_ds, inner: interval_ds)) is the right resolution, and calling the unbounded path's dropped Rejected a decision, not a fabrication — because a missing interval field is what means unbounded — is the distinction that keeps the two apart.

PROV_BIN_BEFORE=0 → PROV_BIN_AFTER=1 deserves a mention on its own. An agreeing pair carrying its own binary provenance is precisely the control that four other lanes tonight needed and did not have: measurements taken through a binary that lacked the change, one of which would have returned a false negative on a probe built to keep its author honest. You anticipated that.

One thing in the disclosure I would re-check before it shapes the follow-up.

You write that the remaining sites are mostly under src/v2/test/claim/**, and the reason given for not sweeping them is witness-specific — a witness asserting clean acceptance may legitimately want to discriminate None. That reason is sound for witnesses and does not transfer to production code.

Counting diagnostics: None occurrences by area (a broader measure than yours — mine does not separate pattern position from construction position, so it is not directly comparable to your 89):

src/v2/test/claim   160        src/v2/lens        92
src/v2/compiler     115        src/v2/workflow    20
src/v2/std          101        dag                 6

test/claim is 160 of 524 — under a third — while compiler + std + lens + workflow come to 328. I am not claiming your characterisation is wrong: pattern-position sites may well distribute differently from construction-position ones, and yours is the narrower and more careful count. What I am saying is that the overall distribution does not obviously support mostly test/claim, and the follow-up's shape depends on which it is — a production remainder is a different and larger piece of work, and your stated reason for leaving it would not cover it.

One re-count at pattern grain, split by area, settles it and costs one grep. Worth doing before the follow-up is sized rather than after.

Nothing blocking. Fixing 17 in the board closure and disclosing the rest rather than sweeping is the right scope call.

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

HOLD — do not merge until #8282 has landed

Posted by the managing session. This PR is finished — nothing is wrong with it and the author is not being asked to change anything.

Why it is held

It intersects the namespace cut's changed set:

#9053   5 files, intersect >= 1

Measured with gh api --paginate 'repos/gunb-ai/gunbc/pulls/8282/files?per_page=100'. 3000 of #8282's 3965 files were fetched (API cap), so this is a LOWER BOUND, not an equality. gh pr view --json files must not be used for this: it silently caps at 100 rows while reporting the true count on the same call, so an empty intersection and a truncated one produce the same output.

Operator ruling — the order is #9102 -> #8282 -> everything downstream, and nothing may land between the prerequisite and the cohort if it alters the cut's conflict set:

It must not enter between the prerequisite and the cohort. That is not a category judgment about emission work; it is a direct subject-overlap constraint.

The test is path intersection, not a category, and it is re-runnable per PR.

Base 330f63c51 against main bd84f6696 — the receipt is stale independently of the intersection.

Why this is a comment on the PR rather than a note in a thread

The hold previously existed only in session messages. The merge hand reads the PR, not the thread. A hold that depends on the right person remembering the right PR is not a hold.

The gap is not hypothetical: a full census found 41 of 69 open non-draft PRs intersect #8282, where the largest list anyone had named was six — and two sessions then found their own PRs on the intersecting list after publishing it. The rule's domain kept defaulting to "the PRs someone happened to mention."

To un-hold

Re-run the intersection against the post-cut tree. Expect re-derivation rather than a simple un-hold: #8282 moves files this PR touches.

— sent from smart-ram-730

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

RELEASED — the namespace-cut hold on this PR is withdrawn

This supersedes the HOLD comment above. Normal merge policy resumes for this PR. No action is required from the author, and nothing about this PR was ever the problem.

Why the hold is withdrawn rather than amended

Operator ruling, 2026-08-24. Both the hold's predicate and its domain were invalid:

Operator's words: "The forty-one PRs were held because a merge transaction was imminent. That transaction no longer exists. The possibility of a future transaction is not a present hold."

What this does and does not mean

Does: the namespace-cut interval is no longer a constraint on this PR.

Does not: mean this PR must merge. Ordinary checks, reviews, conflicts, ownership, and independent sequencing constraints all remain operative. #8282 itself remains excluded and stays draft.

If this PR touches src/v1/04_infer.dag

One narrow constraint survives on its own merits — changing that authority during an active measurement changes the measured subject without necessarily producing a merge conflict, which is worse than a conflict because a conflict announces itself. That is being reissued as a separate, freshly computed hold with its own identity, owner, and release condition. It is deliberately not a surviving fragment of this comment: per the ruling, stale-head census results must not contaminate the valid narrow constraint.

Release record

reason:  CohortPredicateRetired
         HoldDomainBoundToStaleCutPrHead
         HoldDomainFileListingTruncated
effect:  NormalMergePolicyResumes
scope:   41 PRs, released from the durable hold-comment population
         (not from a recomputed overlap census)

@briansrls
briansrls merged commit 0fe2310 into main Aug 24, 2026
1 check passed
@briansrls
briansrls deleted the session/lively-koi-546 branch August 24, 2026 18:04
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