Skip to content

Give the row-cost receipt its CPU column, beside the wall one - #7845

Merged
briansrls merged 16 commits into
mainfrom
session/merry-raven-690-cpu-column
Aug 5, 2026
Merged

briansrls merged 16 commits into
mainfrom
session/merry-raven-690-cpu-column

Conversation

@briansrls

@briansrls briansrls commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #7844 (which is stacked on #7837). Review those first; this diff is the top commit only.

What this is

The last piece of the 2026-08-05 clock-basis ruling that still had a real consumer gap. #7820 renamed eval_ms → eval_wall_ms, #7837 made the row carry both clocks — and nothing printed the second one, so the remedy that second clock exists to discriminate could not be read from the artifact at all.

The tuple becomes a struct, and that is not tidying

Vec<(String, String, u128, u128, u128, String, String)> → WitnessRowCost.

A positional tuple is how eval came to mean "wall" by index and nothing else — the same defect one layer below the ObservationEvent.wall field name this lane retired. It is also exactly what would have turned a second clock into "the third u128".

The column

eval_cpu_ms sits immediately beside eval_wall_ms — asserted by the test, not left to convention — so an author reads the remedy from one file without joining another:

wall cpu what to fix
high high algorithm, or repeated evaluation
high low waiting, I/O, subprocess, scheduling

It is not a second threshold. The witness threshold is stated on wall (operator ruling); CPU is the remedy discriminator, which is the only thing that earns a second clock a column.

An unsampled clock renders unmeasured, never 0

A 0 there would claim the witness used no CPU at all — which reads as the strongest possible remedy signal (pure waiting) about a row for which nothing was measured. That is the fabricated zero the outcome column already closed, one column over.

Green by execution, discrimination proven

witness_row_cost_receipt_distinguishes_unexecuted_from_sub_millisecond gains a wall-only fixture and goes RED when the renderer is changed to unwrap_or(0), failing on the exact fabricated cell:

assertion `left == right` failed: an unsampled cpu clock must report absence, not 0:
1  dag/test/claim/wall_only_test.dag  cpu_clock_not_sampled  4  0  0  0  Done

It also asserts the two clocks are independent carriers — 500µs wall against 300µs cpu, so a producer filling both columns from one figure would be caught — and that the two columns are adjacent in the header.

cargo test --bin claim_executor — 68 passed, 0 failed.

CI note

The ci job on this stack is red on two pre-existing main-red witness failures, not on anything in these diffs. Split and evidence in #7846 (which repairs one half; #7842 / #7843 already cover the other).

Brian Searls and others added 10 commits August 5, 2026 06:10
The operator's 2026-08-05 ruling: recording both clocks is correct; the
defect is a duration whose meaning lives only in its field NAME. This is
the observation-authority half of that ruling (7820A landed the seed half
in #7820).

std.observation gains ClockBasis = CpuClock | WallClock and
TimedMeasurement { basis, elapsed }, and two carriers stop naming their
clock in prose:

  ObservationOutcome.TimedOut  now carries `basis`, so a kill says WHICH
    cap it crossed — a 5000ms thread-CPU fail-stop and a 600000ms wall
    receipt ceiling were previously indistinguishable, and the CI line
    now renders the clock beside both figures.
  ObservationEvent.wall        becomes `durations: List<TimedMeasurement>`.
    A list rather than one tagged duration because the seed already holds
    both clocks for every occurrence (run_claim_measured), and because
    the operator ruled thread CPU is the REMEDY DISCRIMINATOR beside a
    wall threshold: high wall + high CPU is algorithm or repeated
    evaluation, high wall + low CPU is waiting, I/O or scheduling. One
    clock cannot make that call.

A record with `cpu` and `wall` fields would have been the nickname twice,
which is what the operator refused when 7820A's first draft proposed cpu
as a third measured quantity beside wall and rss.

The payoff is a construction wall, not a rename: witness_row_cost_verdict
now takes the observed clock, WitnessRowCostDatedBasis carries its own,
and a cross-clock comparison answers BasisClockMismatch — a fourth arm,
not a Bool collapse, on the same reasoning std.content_hash refuses
cross-family comparison. Comparing a CPU figure against a wall baseline
used to typecheck and answer confidently; four sessions spent an evening
doing exactly that.

Not walled, and said so on the carrier: two entries may share one basis.
The construction that would forbid it is a total function from a closed
sum, which this substrate lacks (Map is a closure, not wire-serializable,
and refused by the determinism gate). So observation_duration_of REFUSES
an ambiguous read with the duplicate as its cause rather than silently
picking the first — rung mechanically preventable, ceiling structurally
impossible, trigger named.

Green by execution with discriminating REDs, all run locally:
  witness_row_cost_projection_holds     PASS; RED when the basis filter
    is removed from observation_clock_lookup (the silent first-match pick)
  witness_row_cost_drift_comparator_holds PASS incl. two new cross-clock
    controls on BOTH sides of the 2x ratio, so the refusal is proven to
    be decided by the clock and not by the magnitude; RED when the
    comparator stops checking it
  floor_component_receipt (15), observation_model (36), ci_render (19),
  tty_render (16), seed_scoped_run (10), crawl_replay (3)  all PASS

ClockBasis is exported vocabulary: gunbc#7831 states WallClock in its own
policy carrier and rebases onto this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The .dag verdict authority now refuses a cross-clock comparison, but the
production falsifier receipt does not reach it: the seed calls only
witness_row_cost_exceeds_basis and rebuilds the verdict arms in Rust, so
the clock never crosses that seam.

That comparison is still correct today — both sides are wall — but for a
reason about the current producers rather than about the mechanism, which
is exactly the state this ruling exists to end. Said on the carrier with
its next trigger, so the rung is not inflated to cover a path the wall
does not stand on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The row's eval term became List<TimedMeasurement>, and the seed's
extractor still called nanosecond_count on it — so production
project_witness_cost_receipt refused with "cannot access field 'count' on
List" while every .dag witness stayed green, because none of them cross
that Rust seam.

Found two ways, independently: by running the existing
production_witness_cost_projection_* Rust tests (2 of 3 failed), and by
review 48651, which prescribed the same remedy. Worth naming why CI would
not have: nextest was removed from CI in 2026-07-11, so those tests run
locally only, and the witness corpus proves the .dag projection rather
than the transport that reads it.

The fix reads ONE clock through the authored projection rather than
walking the list in Rust: witness_cost_clock_nanos calls
std.observation.observation_duration_of, the same accessor every .dag
consumer uses, so the seed cannot grow a second copy of the lookup or of
its ambiguity refusal. Which clock it wants is now a thing it has to say,
and it says wall — the clock the witness threshold is stated on.

An unsampled clock comes back None, never 0, matching the unmeasured-cell
discipline the sibling receipt already keeps. Wall coming back None
refuses: the authored projection already guarantees a wall figure, so its
absence here means the row reached the transport without the one
measurement the receipt exists to carry.

  production_witness_cost_projection_preserves_exact_nanoseconds_and_ordering  ok
  production_witness_cost_projection_preserves_failed_and_refused              ok
  production_witness_cost_projection_refuses_missing_and_duplicate_parent      ok

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…econstruction

The follow-up gunbc#7837's witness_row_cost_clock_mismatch_note declared,
now dissolved in place rather than left as a note.

That PR grew a cross-clock refusal on the .dag comparator, and the
production falsifier cadence could not reach it: the seed called only
witness_row_cost_exceeds_basis, the bare ratio predicate, and rebuilt
DriftExceeded / WithinBasis / BasisAbsent as Rust string literals. Two
defects in one shape — a second representation of WitnessRowCostVerdict,
and a seam the clock never crossed, so a CPU figure against a wall basis
would still have answered confidently. It was correct only because both
sides happened to be wall, which is a fact about the producers and not a
property of the mechanism.

Three parts, and the third is the load-bearing one:

  Both basis files carry a REQUIRED per-row clock cell. An unstated clock
  refuses at parse and an unmodelled one refuses too — never a default to
  wall, which would be right for every row today and silently wrong the
  first time someone seeds from a CPU receipt. The 1176 existing rows are
  stamped wall because that is what they ARE: before the ruling the seed's
  PerformanceReceipt held only wall_nanos.

  The seed calls witness_row_cost_seed_verdict_dated / _undated and
  renders the arm the AUTHORITY named. It constructs no carrier — the
  Optional stays inside .dag and whether a dated basis exists is the map
  lookup the seed already had, which is data presence, not judgement.

  The Rust arm reconstruction is DELETED, not kept beside the carrier. An
  arm added to WitnessRowCostVerdict now reaches the cadence receipt with
  no Rust edit, and BasisClockMismatch is counted on its own line because
  its remedy differs from BasisAbsent's: fix the producer handing over the
  wrong clock, versus seed the missing row.

Both comparators move together — the compile-clean cost drift shares this
parser and authority by design, so leaving it on the old predicate would
have forked what it was built to share.

Green by execution, with the discrimination named:

  drift_verdicts_come_from_the_authority_and_a_cross_clock_basis_refuses
    asserts the cross-clock refusal on BOTH sides of the 2x ratio (21ms
    and 20ms against a 10ms basis), so it is proven decided by the clock
    and not by the magnitude. This is the ONLY test that crosses this Rust
    seam — the .dag witnesses cannot reach it and the parser tests only
    show the cell is read.
  parse_witness_row_cost_basis_line_requires_srv_fleet_arm64  gains a
    cpu-clock positive, a missing-clock refusal, and an unknown-clock
    refusal.
  committed_witness_row_cost_basis_loads_and_excludes_the_censored_row  ok
    over all 1176 migrated rows.

ObservationAbsent stays decided seed-side, deliberately: an absent
observation has no counterpart in the sum, and giving it one would let a
row with no measurement reach a comparator at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review 48672, correctly: the sibling witness-row-cost path reports
clock_mismatch on its [receipt] summary and the compile-clean path did
not, so the two receipts disagreed about whether the state this change
exists to expose is worth surfacing.

The summary line is where a reader looks to see whether a state fired.
A degradation that is counted internally but omitted from the line
someone actually reads is the counting half-done: its frequency is
observable in principle and invisible in practice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The last piece of the 2026-08-05 clock-basis ruling with a real consumer
gap. #7820 renamed eval_ms to eval_wall_ms and #7837 made the row carry
both clocks; nothing yet printed the second one, so the remedy that second
clock exists to discriminate could not be read from the artifact at all.

The seven-wide anonymous tuple becomes WitnessRowCost. That is not tidying:
a positional tuple is how `eval` came to mean "wall" by index and nothing
else — the same defect one layer below the ObservationEvent.wall field name
this lane retired — and it is exactly what would have made a second clock
"the third u128".

eval_cpu_ms sits immediately beside eval_wall_ms, asserted by the test
rather than left to convention, so an author reads the remedy from one file
without joining another: high wall with high CPU is algorithm or repeated
evaluation, high wall with low CPU is waiting, I/O, subprocess or
scheduling. It is not a second threshold — the witness threshold is stated
on wall.

An unsampled clock renders `unmeasured`, never 0. A `0` there would claim
the witness used no CPU at all, which reads as the strongest possible
remedy signal — pure waiting — about a row for which nothing was measured.
That is the fabricated zero the outcome column already closed, one column
over.

Green by execution, with the discrimination proven rather than assumed:

  witness_row_cost_receipt_distinguishes_unexecuted_from_sub_millisecond
    gains a wall-only fixture, and goes RED when the renderer is changed
    to `unwrap_or(0)` — failing on the exact fabricated cell:
      cpu_clock_not_sampled  4  0  0  0  Done
    It also asserts the two clocks are independent carriers (500us wall
    against 300us cpu, so a producer filling both from one figure would
    be caught) and that the two columns are adjacent in the header.
  cargo test --bin claim_executor  68 passed, 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot force-pushed the session/merry-raven-690-cpu-column branch from a66c354 to f7150fd Compare August 5, 2026 09:48
@gunbai-bot gunbai-bot Bot changed the title Witness cost/purpose/admission: retire long/ as authority Give the row-cost receipt its CPU column, beside the wall one Aug 5, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 5, 2026 09:48
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

Brian Searls added 3 commits August 5, 2026 15:37
Two conflicts, both the same shape: main carries the state BEFORE the drift
lane, and this branch stacks on the drift lane.

- witness_row_cost.dag witness_row_cost_clock_mismatch_note: kept the
  past-tense arm. main's arm names 'the Rust arm reconstruction is DELETED'
  as its NEXT TRIGGER; #7844 discharges that trigger, so carrying main's
  hedge forward would understate the rung.
- cli_run.rs: kept the named WitnessRowCost struct and the
  witness_cost_clock_nanos reads over main's positional 7-tuple. The tuple
  is exactly the by-position naming the clock-basis ruling retired.

Verified on the resolved tree: cargo check --workspace --all-targets clean;
witness_row_cost_drift_comparator_holds and witness_row_cost_projection_holds
green by execution.
Three conflicts, all one shape: main's claim_executor.rs reads the row
positionally (row.0 / row.1 / row.2) because #7844 landed without cli_run.rs,
where this branch defines the named WitnessRowCost. Resolved to ours at all
three, which is the only consistent resolution -- our cli_run.rs is what
declares the struct those field names read.

Verified: zero conflict markers tree-wide; cargo check --workspace
--all-targets clean.
@briansrls
briansrls merged commit 1b5c972 into main Aug 5, 2026
5 checks passed
@briansrls
briansrls deleted the session/merry-raven-690-cpu-column branch August 5, 2026 19:42
briansrls pushed a commit that referenced this pull request Aug 5, 2026
Integrate main through #7845 before re-running compile-clean gate.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 5, 2026
…elds

#7845 replaced WitnessRowCost's tuple shape with named fields and inserted
eval_cpu_nanos in the middle, so every positional index in this branch's new code
shifted: .0/.1/.2/.5 are entry/function/eval_wall_nanos/outcome. Merging main
migrated the pre-existing call sites; these six were the residue this branch adds.

The skip test now calls row_measurement_is_absent(&row.outcome) rather than
comparing the outcome string inline. Same behavior — that helper tests exactly
selection-skipped — but it is the existing authority for the question, and the
inline compare was a second copy of it (DESIGN section 3). TimedOut rows still
fall through to be measured, which is deliberate: they are the population this
disclosure exists to surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 5, 2026
…ived from the existing witness row-cost receipt, with promotion-to-refusal as its declared trigger (#7862)

* WIP: Make the 500ms mandatory-migration threshold a counted disclosure derive

* WIP: Make the 500ms mandatory-migration threshold a counted disclosure derive

* Bind the two import-less plan sketches from their definers (unblocks this PR's compile-clean)

Not this PR's defect. dag/gunbc/plans/branch_merge_admission_model.dag and
merge_admission_gate_shape_proposal.dag are the only two of ~46 table-using
modules under dag/gunbc/plans/ carrying no imports at all, so their cross-module
names bind by pool coincidence (DESIGN #6985 Class B). This PR touches
claim_executor.rs, and a non-selectable .rs path forces compile-clean to the
whole-tree baseline, which compiles them; main pushes take the scoped path and
never do.

Same change as #7872 (authored off main). Carried here so this PR can go green
without waiting for that to merge; when it does, the identical lines merge clean.

Verified by execution: both modules compile with 0 blocking errors. Two imports
are required, not one — adding md_helpers alone flips each module from ambient
bare resolution to import-scoped visibility, which then refuses Plan/HasTrigger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Migrate the migration-disclosure receipt to WitnessRowCost's named fields

#7845 replaced WitnessRowCost's tuple shape with named fields and inserted
eval_cpu_nanos in the middle, so every positional index in this branch's new code
shifted: .0/.1/.2/.5 are entry/function/eval_wall_nanos/outcome. Merging main
migrated the pre-existing call sites; these six were the residue this branch adds.

The skip test now calls row_measurement_is_absent(&row.outcome) rather than
comparing the outcome string inline. Same behavior — that helper tests exactly
selection-skipped — but it is the existing authority for the question, and the
inline compare was a second copy of it (DESIGN section 3). TimedOut rows still
fall through to be measured, which is deliberate: they are the population this
disclosure exists to surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls pushed a commit that referenced this pull request Aug 6, 2026
…pt-index long-lane row (#7880)

* WIP: Make the 500ms mandatory-migration threshold a counted disclosure derive

* WIP: Make the 500ms mandatory-migration threshold a counted disclosure derive

* Bind the two import-less plan sketches from their definers (unblocks this PR's compile-clean)

Not this PR's defect. dag/gunbc/plans/branch_merge_admission_model.dag and
merge_admission_gate_shape_proposal.dag are the only two of ~46 table-using
modules under dag/gunbc/plans/ carrying no imports at all, so their cross-module
names bind by pool coincidence (DESIGN #6985 Class B). This PR touches
claim_executor.rs, and a non-selectable .rs path forces compile-clean to the
whole-tree baseline, which compiles them; main pushes take the scoped path and
never do.

Same change as #7872 (authored off main). Carried here so this PR can go green
without waiting for that to merge; when it does, the identical lines merge clean.

Verified by execution: both modules compile with 0 blocking errors. Two imports
are required, not one — adding md_helpers alone flips each module from ambient
bare resolution to import-scoped visibility, which then refuses Plan/HasTrigger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Follow-up to #7872: the std.markdown symbols were still binding by coincidence

review 48984, on the merged #7872, is correct and my completeness claim there was
not. Both plan sketches still referenced MarkdownBlock, TableBlock and AlignNone
with no std.markdown import, so those three kept binding by pool coincidence —
the same #6985 Class B pathology #7872 exists to close, left in place by the
change closing it.

THE BAD INFERENCE, named because it is the trap and not a slip: I read
"0 blocking error(s)" as "every name binds from a declared import". It does not.
Bare resolution still answers for whatever the closure happens to contain, so a
clean compile is precisely the evidence that CANNOT separate a declared binding
from a coincidental one — the compiler is green either way. Only reading the file
against its own import list decides it, which is what the reviewer did and I had
not. Verifying the fix by the same instrument that was blind to the defect is a
DESIGN §5 specification-without-execution failure wearing an execution costume.

Symbol set verified against std.markdown's 34 exports this time rather than
inferred from a clean compile: these two modules use MarkdownBlock, TableBlock and
AlignNone and nothing else from it. Byte-identical to the most common sibling
form, carried by 18 plan modules including the three #7872 cited as precedent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Migrate the migration-disclosure receipt to WitnessRowCost's named fields

#7845 replaced WitnessRowCost's tuple shape with named fields and inserted
eval_cpu_nanos in the middle, so every positional index in this branch's new code
shifted: .0/.1/.2/.5 are entry/function/eval_wall_nanos/outcome. Merging main
migrated the pre-existing call sites; these six were the residue this branch adds.

The skip test now calls row_measurement_is_absent(&row.outcome) rather than
comparing the outcome string inline. Same behavior — that helper tests exactly
selection-skipped — but it is the existing authority for the question, and the
inline compare was a second copy of it (DESIGN section 3). TimedOut rows still
fall through to be measured, which is deliberate: they are the population this
disclosure exists to surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Dissolve the concept-index long-lane row by fixing the witness's cost

The witness's cost is its POOL, and the pool was 638 files.

concept_index_enumeration_pool_roots was "src/v2/test/claim", and
concept_decl_facts parses every .dag under the root on every call, so each
parse-only witness parsed 638 files / ~3.0 MB and grew with every unrelated
module added anywhere beneath it. That is precisely what the SubstrateLongLaneRow
recorded ("corpus-breadth cost grows with unrelated modules in the pool") and what
pushed witness_coproduct_arms_parse_only to 6065ms thread-CPU against the 5000ms
fast-lane budget, rehoming it to test/claim/long/ under #7756.

Breadth was never the subject. Every assertion names ONE concept by qualified
name and checks its extracted fields, so the requirement is a pool CONTAINING
those concepts, and the smallest pool that crosses the boundary is a directory of
its own (operator ruling 2026-08-04: a witness's size is DERIVED from what it
proves, never chosen for verisimilitude). src/v2/test/fixture/concept_index_pool
is that pool.

MEASURED, same host, same binary, toggling only the .dag source:

  witness_coproduct_arms_parse_only   3952ms -> 2ms
  whole 11-witness family, slowest      51ms  (was ~4000ms for parse-only rows)

All 11 pass, including the returned witness. The pool is proven non-vacuous
rather than merely quiet: witness_parse_only_extract_nonempty_in_scoped_pool
asserts the pool is non-empty AND contains the target, and
witness_parse_only_extract_reflects_perturbed_field_type reads RecordPerturb's
beta: Int out of the fixture — so the stale-field RED control cannot be passing
because the pool is empty.

WHY THE TYPES DID NOT ALL MOVE. The two witness families consume different
inputs: concept_decl_facts_live reads the loaded module REGISTRY, concept_decl_facts
reads FILES under a root. The live-registry witnesses keep local declarations
(a locally-declared record IS their subject); the parse-only witnesses read the
fixture pool. That is two inputs to two mechanisms, not one concept forked.

A CHANGE THAT DID NOT EARN ITS PLACE ON THIS WITNESS, kept and labeled rather
than quietly credited: enumerate_concepts_from_decls folded with list_snoc_item,
which is list_append(xs, [item]) and therefore O(n^2) over the decl count. It is
replaced with list_map (fold_list_right + Cons, O(n), order-preserving, identical
output). Measured effect on this witness: 3952ms -> 3795ms, ~4%, inside noise —
because the dominant cost was the host-side pool parse, not the fold. It stays
because it is a proven cost-shape defect on a function enumerate_concepts() also
calls corpus-wide (DESIGN section 6: fixed regardless of the realized n), NOT
because it fixed anything here. My first hypothesis was that this fold WAS the
cost; measurement refuted it, and the pool root was the answer.

Dissolves: the SubstrateLongLaneRow, the long/ file, and
coproduct_arms_parse_only_long_lane_note, all deleted together per the row's own
trigger ("eval lands under gunbc_ci_fast_lane_witness_eval_budget on a measured
cold run and re-enrolls per-PR discovery").

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* WIP: shell -> dag (and TS -> dag)

* Drop the stale concept-index parse-only basis row invalidated by this branch

witness_coproduct_arms_parse_only carried a dated basis of 3850ms from run
30550328673, measured when concept_decl_facts parsed all 638 files under
src/v2/test/claim. Narrowing the pool to the witness family's own directory
takes the same witness to ~1.5ms, so that basis is a measurement of a
different implementation and no longer a reference for this one.

Leaving it is not neutral: the drift comparator is a 2x ratio, so a 3850ms
basis pins this row WithinBasis below 7700ms forever and the drift gate goes
vacuous for it exactly where a future regression would show up. That is the
failure committed_witness_row_cost_basis_loads_and_excludes_the_censored_row
already guards against for the 900794ms censored row, in the same file.

Dropped rather than re-signed: witness_row_cost_basis_host_class_note requires
basis rows to be signed from a cited arm64 srv-fleet run, never a local
measurement, and this container is x86. BasisAbsent is the honest third state
(typed and counted, per witness_row_cost_spine_note) until the next fleet
cadence re-signs it. Drift is falsifier-cadence counted and not merge-refusing,
so this changes no gate verdict. 1190 rows remain, over the >=1000 floor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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