Skip to content

CI: four job steps become one invocation, four in-process phases - #8647

Merged
briansrls merged 12 commits into
mainfrom
session/tidy-lark-471-ci-consolidate
Aug 20, 2026
Merged

briansrls merged 12 commits into
mainfrom
session/tidy-lark-471-ci-consolidate

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Answers the operator's directive on the merged #8618:

the regen steps seem to share a compile with all three steps - we basically need to consolidate ALL the work in there now - we added 2 more steps, but they are not properly managed (between github actions job steps) - i would much prefer if it was all handled within the witnesses step and within the gunbc binary, not at a github actions job level

What the step ladder was

Four steps — parse, regen, regen-fixed-point, floor — each its own process; the order a YAML list; each precondition an if: naming another step's outcome; and the fixed-point step receiving pass 1's digest by reading the receipt file the regen process had just written.

That last one is the tell. run_required_regen_fixed_point has taken pass1_digest: Option<String> all along, and CI passed it None — a process boundary sitting where a function call belonged.

What runs now

- name: "Required CI: parse, regen, regen determinism, witness floor"
  run: |
    "$ROOT/target/release/claim_executor" --required-ci --source-root "$ROOT/dag" --source-root "$ROOT/src/v2"

One step. Four phases in one process. The digest handed over in memory.

The behavioural change, stated plainly

Only one real dependency exists. Fixed-point needs regen's pass-1 digest, so it is skipped — visibly, as its own reported state — when there is none. Every other phase runs even after an earlier failure, so the run reports the complete ledger instead of letting the first defect hide the rest. The line still stops (nonzero exit on any failed phase); it stops with every deficit named. Skipped is never silence and never a pass.

The digest is handed over even when regen's comparison disagreed: pass 1 emitted a tree either way, and does the emitter reproduce itself is a separate question from does it match what is committed. Skipping determinism on a regen mismatch would conflate them and lose that signal exactly when drift makes it interesting.

Not claimed

No compile is shared. Regen and its fixed point each call compile_stage0, and the second call stays — re-emitting and comparing digests is precisely what the fixed point measures, so collapsing it would delete the measurement. The floor's preparation is a different computation again. What this removes is process startup, the receipt round-trip, and the job-level orchestration.

One defect I introduced and caught

Recorded because the shape matters more than the fix. Extracting the parse walk from its bin, I dropped the tests/fixtures/ exclusion. The first local run duly reported a parse FAILURE in fact_cardinality_split_brace.dag — a headerless fragment that is on main, where the parse step is green. The "finding" was my extraction having silently widened its own subject.

Restored verbatim, and the subject is now provably identical to main's: 50 files parse-clean here, 50 in run 32341236470.

One deliberate difference does remain, stated rather than smuggled: the bin used read_dir.flatten(), which silently discards an unreadable entry — so a walk that never saw a file was indistinguishable from a file that parsed. The error now propagates.

Shared, not duplicated

report_required_floor_outcome and required_floor_outcome_is_clean are extracted so --required-floor and --required-ci cannot drift into reporting one outcome two ways, and the five-cause conjunction is written once (§3).

Stale recitals updated

A knowingly-false present-tense claim in an authority is premise contamination, so: gunbc.design_document (twice), gunbc.ci_layer_roots witness_fold_src_v1_coverage_gap_note, and tools.extdeps_scope_placement_gate. Two other --required-floor mentions in ci_layer_roots are dated measurements naming the command as run — they stay true and are untouched.

Executed

  • All four phases sequenced correctly in one local run: first_generation_equal=true, fixed_point_equal=true with no receipt round-trip, floor entered.
  • Four new witnesses assert one composed invocation, no cross-step outcome precondition, the parse sweep surviving, and the retired step names not returning (a different axis from the commands, so the two can disagree).
  • Mutation-tested: pointing the step back at --required-floor turns the first witness false.
  • cargo check --all-targets, cargo fmt --all --check clean.

Also closed

#8633 (v1-parse-gate), which had a merge conflict, is closed as fully superseded — its bin was byte-identical to main's, its one finding was already on main, and against main it would have deleted #8618's regen steps and resurrected exclusion rows #8506 deliberately removed.

— sent from tidy-lark-471

Brian Searls and others added 2 commits August 20, 2026 07:49
Operator directive, 2026-08-20, on the merged #8618: "the regen steps seem to
share a compile with all three steps - we basically need to consolidate ALL the
work in there now - we added 2 more steps, but they are not properly managed
(between github actions job steps) - i would much prefer if it was all handled
within the witnesses step and within the gunbc binary, not at a github actions
job level".

WHAT THE STEP LADDER WAS. Four steps — parse, regen, regen-fixed-point, floor —
each its own process; the ORDER a YAML list; each precondition an `if:` naming
another step's `outcome`; and the fixed-point step receiving pass 1's digest by
READING THE RECEIPT FILE the regen process had just written. That last one is the
tell: `run_required_regen_fixed_point` has taken `pass1_digest: Option<String>`
all along and CI passed it `None`. A process boundary sat where a function call
belonged.

WHAT RUNS NOW. One step, `claim_executor --required-ci`, four phases in one
process, the digest handed over in memory.

ONLY ONE REAL DEPENDENCY EXISTS, and the rest is the behavioural change worth
reading closely: fixed-point needs regen's pass-1 digest, so it is skipped —
visibly, as its own reported state — when there is none. Every other phase RUNS
EVEN AFTER AN EARLIER FAILURE, so the run reports the complete ledger instead of
letting the first defect hide the rest. The line still stops (nonzero exit on any
failed phase); it stops with every deficit named. Skipped is never silence and
never a pass.

The digest is handed over even when regen's comparison DISAGREED: pass 1 emitted
a tree either way, and "does the emitter reproduce itself" is a separate question
from "does it match what is committed". Skipping determinism on a regen mismatch
would conflate them and lose the signal exactly when drift makes it interesting.

NOT CLAIMED: no compile is shared. Regen and its fixed point each call
`compile_stage0` and the second call STAYS — re-emitting and comparing digests is
what the fixed point measures, so collapsing it would delete the measurement. The
floor's preparation is a different computation again. What this removes is process
startup, the receipt round-trip, and the job-level orchestration.

ONE DEFECT I INTRODUCED AND CAUGHT, recorded because the shape matters more than
the fix: extracting the parse walk from its bin, I dropped the `tests/fixtures/`
exclusion. The first local run duly reported a parse FAILURE in
`fact_cardinality_split_brace.dag` — a headerless fragment that is on main, where
the parse step is green. The "finding" was my extraction having silently widened
its own subject. Restored verbatim, and the subject is now provably identical to
main's: 50 files parse-clean here, 50 in run 32341236470.

One deliberate difference does remain, stated rather than smuggled: the bin used
`read_dir.flatten()`, which silently DISCARDS an unreadable entry, so a walk that
never saw a file was indistinguishable from a file that parsed. The error now
propagates.

SHARED, NOT DUPLICATED: `report_required_floor_outcome` and
`required_floor_outcome_is_clean` are extracted so `--required-floor` and
`--required-ci` cannot drift into reporting one outcome two ways, and the
five-cause conjunction is written once (§3).

STALE RECITALS UPDATED, because a knowingly-false present-tense claim in an
authority is premise contamination: `gunbc.design_document` (twice),
`gunbc.ci_layer_roots` `witness_fold_src_v1_coverage_gap_note`, and
`tools.extdeps_scope_placement_gate`. Two other `--required-floor` mentions in
`ci_layer_roots` are DATED MEASUREMENTS naming the command as run; they stay true
and are untouched.

EXECUTED: all four phases sequenced correctly in one local run —
`first_generation_equal=true`, `fixed_point_equal=true` with no receipt
round-trip, floor entered. Four new witnesses in
`witness_floor_workflow_consolidation_witness_test.dag` assert one composed
invocation, no cross-step outcome precondition, the parse sweep surviving, and
the retired step NAMES not returning (a different axis from the commands, so the
two can disagree). Mutation-tested: pointing the step back at `--required-floor`
turns the first false. `cargo check --all-targets` and `cargo fmt --all --check`
clean.

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

The emitter and the generated workflow were calling `--required-floor`, so the
composed four-phase run this PR introduces never executed, while DESIGN.md
asserted it did. Blocking, and correct.

HOW IT HAPPENED, because the mechanism is more useful than the fix. I
mutation-tested the new witness by pointing the step back at `--required-floor`
and confirming `w_ci_invokes_one_composed_mode_not_a_step_ladder` went false. The
wall worked. The restore did not: the command ran the mutation, the witness, and
`cp /tmp/wf.bak` back — and the shell TIMED OUT mid-loop, before the restore. The
"restored" echo never printed and I did not notice its absence.

Then I verified the wrong thing. `grep -c 'required-ci'` returned 1 and I read
that as restored. It was matching ONE PROSE LINE — the comment block explaining
the consolidation — not the emitted script. A corpus grep for a symbol finds the
documentation about the symbol first, and this file is mostly documentation.

The witness would have caught it. It had already TOLD me, returning false as the
mutation intended; I attributed that to the mutation and never re-ran it after
the supposed restore. A mutation test's last step is not observing red — it is
re-observing green afterwards, and that step has to be in the same command as the
restore or it does not reliably happen.

FIXED: emitter emits `--required-ci`, yml regenerated (drift was a symptom, not a
second defect), and all four witnesses re-run AGAINST THE FINAL STATE — all true.

ALSO FIXED, same review: the SKIPPED eprintln carried a runaway indentation blob.
`cargo fmt` had collapsed a `\` continuation into one literal with the source
indentation baked in. Re-broken with an escaped continuation, and re-checked that
fmt does not re-collapse it.

NOT FIXED, named rather than swept in: three pre-existing strings of the same
shape at claim_executor.rs:405, :7998 and :8028
(FLOOR-COMPILE-CLEAN-OVER-BUDGET, FLOOR-BATCH-CLAMP-REFUSED,
FLOOR-BATCH-OVER-BUDGET). Same fmt-collapse class, none of them this PR's, and
widening the diff to unrelated lines is how a focused change stops being
reviewable.

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

gunbai-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Both findings from review 54012 are fixed in 542c1280d9. The blocking one was correct and the mechanism is worth recording.

The emitter was calling --required-floor, and it was my own mutation test that put it there. I broke the step deliberately to prove w_ci_invokes_one_composed_mode_not_a_step_ladder had teeth, watched it go red — the wall worked — and the shell then timed out mid-loop, before the cp restore line ran. The "restored" echo never printed and I did not notice its absence.

Then I verified the wrong thing. grep -c 'required-ci' returned 1 and I read that as restored. It was matching one prose line — the comment block explaining the consolidation — not the emitted script. A corpus grep for a symbol finds the documentation about the symbol first, and that file is mostly documentation.

The witness had already told me. It returned false exactly as the mutation intended, so once the restore silently failed, a real red was indistinguishable from the expected one. A mutation test's last step is not observing red — it is re-observing green after the restore, in the same command, or it does not reliably happen.

Fixed and verified at the construct rather than by symbol count:

witness_floor_workflow.dag:238   "$ROOT/target/release/claim_executor\" --required-ci"
witnesses.yml:37                  "$ROOT/target/release/claim_executor" --required-ci --source-root ...

All four witnesses re-run against the final state — all true. The yml drift was a symptom of the same defect, not a second one, so regenerating it was the whole fix there.

Minor finding, also correct: the SKIPPED eprintln! had a runaway indentation blob. cargo fmt had collapsed a \ continuation into one literal with the source indentation baked in. Re-broken with an escaped continuation, and re-checked that fmt does not re-collapse it.

Not fixed, named rather than swept in: three pre-existing strings of the same shape at claim_executor.rs:405, :7998 and :8028 (FLOOR-COMPILE-CLEAN-OVER-BUDGET, FLOOR-BATCH-CLAMP-REFUSED, FLOOR-BATCH-OVER-BUDGET). Same fmt-collapse class, none of them this PR's, and widening the diff to unrelated lines is how a focused change stops being reviewable.

— sent from tidy-lark-471

The first CI execution of the composed step found two real defects and named
both, which is the mechanism working: `phases_run=4 failed=2`.

THIS COMMIT FIXES THE ONE THAT IS MINE. `gunbc.fabric_witness_run` is a second
model of the same job shape — the floor's priced demand on the compute fabric —
and the consolidation left it describing the ladder I deleted:

  floor_work_contract  steps: ["v1-dag-parse", "required-floor"]   two steps
  floor_run_command    argv:  [..., "--required-floor"]            old mode

so `fabric_argv_and_workflow_step_agree_on_source_roots` correctly went red: the
two representations no longer agreed. Both re-pointed at the one composed step.

THE OLD COMMENT'S CONCERN WAS RIGHT AND IS NOW BETTER SERVED, which is why the
row moved rather than the concern being dropped. It read that collapsing the two
steps "would make a parse failure and a floor failure indistinguishable in the
receipt, which is the distinction gunbc#8466 -> #8519 was paid to learn." The
receipt now distinguishes FOUR phases, not two steps, and prints `FAILED PHASE
<name>` per failure — demonstrated by the very run that caught this, which named
a regen drift AND a floor failure where a ladder would have surfaced them one
merge at a time.

WHAT IT COSTS, stated rather than glossed: resumability was per-step, so a green
parse could be receipt-satisfied and skipped on rerun. One step means one receipt
and the whole run repeats. Real consequence of the consolidation; phase-grain
resumability belongs with the cost basis, not here.

A GAP FOUND WHILE FIXING IT. The witness is named "argv and workflow step AGREE"
but only compared source roots and that the SCRIPT names the mode — it never
checked the ARGV names the same mode. So the two could drift on the one flag that
decides what runs, and stay green. Found by execution: after re-pointing the
script, the argv still said `--required-floor` and this witness passed. It now
asserts the argv carries `--required-ci` and does NOT carry `--required-floor`.

Mutation-tested with the restore and the re-verification in ONE command, per the
lesson from the previous commit: flipping the argv flag turns it false, restoring
turns it true, and the restored line is printed.

NOT FIXED HERE, because it is not mine: `regen FAIL generated surface drift:
v1_compiler_emit_rust.rs`. Main is ALREADY RED with the identical failure at
4cec10f (run 32343207326). Bisected to #8614, which changed the authority
`src/v1/05_emit_rust.dag` without regenerating its mirror
`src/v1/stage0/src/v1_compiler_emit_rust.rs`. My PR inherits it because PR runs
check out the merge ref. It is reported separately rather than bundled here.

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

gunbai-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Not pushing a fix for this failure, because it is not this PR's and fixing it here would make things worse.

The one failing phase is inherited from main:

required-ci: parse OK 50 file(s) parse-clean
required-ci: regen FAIL generated surface drift: v1_compiler_emit_rust.rs
required-ci: regen-fixed-point fixed_point_equal=true
required-floor: planned=9787 executed=9787 terminal=9787 passed=9479 failed=0
required-ci: phases_run=4 failed=1

Main is already red with the identical failure at 4cec10f66a (run 32343207326), bisected to #8614, which changed the authority src/v1/05_emit_rust.dag without regenerating its mirror. PR runs check out the merge ref, so this PR inherits it. Everything this PR owns is green — parse, determinism, and a clean floor: 9,787 planned = executed = terminal, failed=0.

Two PRs already fix the drift, so a third would be duplicate work. Between them the evidence is now decisive rather than a matter of taste: #8653 regenerates one mirror, and its own CI run on its own head still reports seventeen drifted files — including the one it regenerated — because that mirror is part of the emitter, so installing it changes what gets emitted downstream. #8652's file set is exactly those seventeen. I have commented there with the receipt.

Worth noting what this run also shows about the consolidation itself: regen failed and the determinism phase and the full floor still ran, so one run produced the complete ledger. Under the step ladder this replaces, that regen failure would have been the only thing visible.

This PR is merge-ready the moment main is green; no further work is pending on it from me.

— sent from tidy-lark-471

Brian Searls and others added 7 commits August 20, 2026 12:44
CONFLICT, AND WHY THE RESOLUTION IS NOT "TAKE A SIDE". Both branches changed the
same block in `claim_executor.rs`'s required-floor arm:

  ours   — extracted the reporting into `report_required_floor_outcome` /
           `required_floor_outcome_is_clean` so `--required-floor` and
           `--required-ci` cannot drift into two accounts of one outcome
  theirs — #8642 added the memo hits/misses receipt INSIDE that same inline block

Taking either side wholesale drops the other change with no signal: ours would
have deleted #8642's receipt, theirs would have deleted the extraction and left
`--required-ci` calling functions that no longer exist. Resolved by keeping the
extraction and GRAFTING the receipt into the extracted reporter, where it now
serves both callers instead of one.

This is the class `gunbc.generated_artifact_merge_driver` was built for — a merge
git reports as clean while one side's content silently vanishes — except here git
did flag it, because both edits landed in the same hunk. The graft is noted
in-code so the next reader knows it was a merge decision rather than an authoring
one.

VERIFIED PRESENT AFTER THE MERGE, both directions rather than just mine:
  #8642      CiWitnessVerdict::from_outcome (6 sites), MEMO_HITS/MISSES counters,
             compile_dag_rust_emit_check_memo_counts, the
             enrollment_holds_only_semantic_failures regression control, and the
             warn tier still deleted at its authority
  this PR    required_ci_mode, run_v1_src_dag_parse, and both extracted reporters

`cargo check --all-targets` and `cargo fmt --all --check` clean.

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

Found by the side thread reviewing #8647 for surplus work. CI still compiled
`v1_src_dag_parse` after the consolidation removed the only step that invoked it.

THE AUTHORITY ALREADY STATED THE RULE, two lines above the row I left stale:
"naming a binary that no step runs buys nothing and costs a compile." The row's
own comment said the bin was added for the step below it — the step this PR
deleted. So this is not a new principle, it is the consolidation failing to carry
its own deletion through to the build list.

WORSE, AND THE PART WORTH RECORDING: my consolidation witness ASSERTED the
surplus. `w_the_parse_sweep_survives_the_fold` required the yml to contain
`--bin v1_src_dag_parse`, using "CI compiles the parse binary" as a proxy for
"the parse sweep survives". The two came apart the moment the sweep moved INTO
the composed run and the binary stopped being invoked — so the witness was
pinning a surplus compile in place as a requirement, which is the opposite of
what its name promised. A green witness protecting waste is worse than no
witness, because the roster reads as coverage.

REPLACED by `w_the_retired_parse_binary_is_no_longer_built`, which asserts what
its subject can actually decide: the emitted yml invokes `--required-ci`, builds
`claim_executor`, and does NOT build the retired bin. The comment names the
boundary explicitly — this file reads emitted workflow text and CANNOT see that
the parse phase runs. That is established by execution
(`required-ci: parse OK 50 file(s) parse-clean`, run 32371293567), and a static
witness claiming it would be asserting something its subject does not contain.

THE BINARY STAYS IN THE TREE. Running the parse sweep alone is the cheapest check
available while editing src/v1, and it is a thin caller of the same `cli_run`
walk rather than a second implementation. What it stops being is CI's business.

Mutation-tested: putting the bin back in `witness_floor_required_bins` turns the
new witness false; restoring turns it true. The restore was verified by reading
the row and the emitted yml directly, not by a symbol count — the timeout ate the
in-command re-verify again, which is exactly why the file state is checked
explicitly now.

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

THE CONFLICT IS THE SAME BLOCK AS LAST TIME, and taking a side would have been a
silent correctness loss in the more dangerous direction.

#8646 substantially extended the floor's reporting: `offered / routed /
declined_long / declined_live` on the summary line, and TWO NEW BLOCKING CAUSES,
`route_gap` and `stale_route_gap`, appearing in both the per-row printing and the
cleanliness conjunction. Taking my side of the conflict would have kept the
extraction and dropped all of it — including the two causes — so
`--required-floor` AND `--required-ci` would have reported green on a route gap
that main refuses. A merge that greens a run main reds is worse than a conflict.

RESOLVED BY RE-DERIVING RATHER THAN HAND-MERGING. I took main's inline block
verbatim, split it at the conjunction, and rebuilt `report_required_floor_outcome`
and `required_floor_outcome_is_clean` from it mechanically — the same extraction
this PR performs, re-run against main's newer content. That is why the seven-term
conjunction is exactly main's seven terms rather than my five plus two I
remembered to add.

#8650 reshaped `RegenReceipt` into an enum whose fields became Option-returning
accessors, which broke my composed run's phase code. Adapted: `first_generation_
equal` and `fixed_point_equal` now print `unmeasured` rather than a plausible
default when the pass built the other variant, matching what main's own arms do.

ONE NEW ACCESSOR, and it is deliberately TOTAL: `RegenReceipt::
candidate_generated_digest()`. Both variants measure a candidate digest, so there
is no arm without one and no `Option` for a reader to misinterpret as
"unmeasured" — unlike its siblings, which are Option because the other variant
genuinely does not measure that fact. Its consumer is the in-memory pass-1
handoff, the thing this PR exists to make possible: `run_required_regen_fixed_
point` has always taken `pass1_digest: Option<String>`, and before the phases
shared a process there was no way to supply it.

The generated workflow conflicted with NO markers — that is
`generated_artifact_merge_driver` refusing rather than picking a side, exactly as
designed. Regenerated from the authority instead of hand-resolved.

VERIFIED PRESENT AFTER THE MERGE, both directions: main's route_gap (7),
stale_route_gap (4), offered=, and #8642's CiWitnessVerdict and memo receipt;
mine's required_ci_mode, run_v1_src_dag_parse, and both extracted reporters, with
the two new causes confirmed inside the cleanliness function. Three consolidation
witnesses re-run green. `cargo check --all-targets` and `cargo fmt --all --check`
clean.

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

TWO FINDINGS, ONE FROM REVIEW AND ONE FROM THE SIDE THREAD.

1. THE MEMO RECEIPT PRINTED TWICE, and my own comment caused it. The previous
commit re-derives `report_required_floor_outcome` from main's inline block on
every merge that touches it. Main's block ALREADY carried #8642's memo line —
#8642 is merged — and I grafted a second copy on top, so both `--required-floor`
and `--required-ci` emitted the receipt twice. That degrades the exact "one
receipt, both numbers" property #8642 introduced: two lines reporting one pair is
the second-representation shape the receipt existed to remove.

The instruction that caused it is deleted with the duplicate. It read "each merge
has to graft it back deliberately" — an unconditional re-add with no check for
what re-derivation already brought. Re-derivation copies main's block wholesale,
so the line arrives WITH it and needs no grafting. The surviving comment now says
so, and says that exactly one may exist.

2. THE PASS-1 DIGEST HAD TWO SOURCES AND A SILENT PRECEDENCE RULE. The receipt is
read unconditionally — the cross-tree refusal and `PriorReceiptRef` are
provenance facts only the file carries — so when a caller ALSO supplies the
digest in memory it exists twice, and `pass1_digest.unwrap_or(prior)` silently
preferred the argument. A disagreement decided nothing and reported nothing.

WHOSE DEFECT IT IS: mine. Until the phases shared a process every caller passed
`None`, so the file was the only source and `unwrap_or` had one arm in practice.
The composed run is what supplies the argument, so the change creating the second
source is the change that closes it.

WHAT IT IS NOT, stated because the side thread called it a hard blocker and it is
weaker than that: it does not guard an active defect on the composed path. There
`run_required_regen` writes the receipt and returns the same digest in one pass,
so the two agree BY CONSTRUCTION and the arm is unreachable. I tried to exercise
it end-to-end by corrupting the receipt and re-running `--required-ci`, and the
test was void — regen rewrites the receipt before the fixed point reads it. The
refusal guards the FUNCTION's contract, for a caller supplying a digest against a
receipt written by some other run at this commit.

That unreachability is why the decision is EXTRACTED as
`reconcile_pass1_digest`: reaching the arm through the real function needs a
seven-minute emit, and a wall no test can reach is a wall nobody knows works.
`pass1_digest_disagreement_refuses_rather_than_preferring_one` asserts the
refusal names BOTH values, plus two positive controls (agreeing, and None)
without which a function that refused everything would also pass.

Mutation-tested with the restore and re-verification in ONE command: disarming
the guard makes it FAILED, restoring makes it ok, and the restored source line is
counted rather than assumed.

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

DESIGN.md and its authority `gunbc.design_document` conflicted in the CI rung-drop
paragraph, and both sides had added something real:

  theirs  a new clause: `executed` counts a witness REACHING the fold, not its
          assertion running — the hermetic boundary, with the rationale that
          mocking the refusal would pass such a witness against a fabricated exit
          status
  ours    the invocation is now `--required-ci`, and the flag clause records that
          the two regen steps were re-added and CONSOLIDATED the same day

Resolved by starting from THEIRS and re-applying my two edits onto it, so main's
new clause survives verbatim rather than being reconstructed from memory. Verified
by content — five distinct phrases, two mine and three theirs, each present
exactly once. Not by `grep -c`: this file is one giant `li(text: "...")` line, so
a line count returns 1 whatever the paragraph contains, which is the prose-grep
trap that already cost me a shipped mutation on this branch.

DESIGN.md itself was REGENERATED from the resolved authority rather than
hand-merged — it is a generated artifact, and hand-resolving it would author the
projection instead of deriving it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Composing the CI phases into one process changed what a process-cumulative
counter means. floor_resource_sample() read /proc/self/stat utime+stime
absolutely, so regen's multi-threaded compile now landed on the floor's line:
the floor's FIRST heartbeat reported cpu_ms=59830 with its own process
(run 32341236470) and cpu_ms=786650 without one (run 32371293567).

wall_s was already relative to heartbeat spawn; cpu_ms now is too.

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

gunbai-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Queue hold — authority-touching PRs (operator ruling, 2026-08-20)

This PR modifies .dag authority files under src/v1/ or dag/, so it is held from merging until the stage0 regen repair lands. It is one of 32 open PRs in that set.

This is a queue hold, not a judgement on the change. Nothing here is wrong and nothing is being asked of you. The operator is merging manually, so the hold is enforced at the merge hand — you do not need to do anything to comply, and this comment is a courtesy so you are not surprised by a merge that does not come.

Why the hold exists. A regeneration repair's entire content is "the derived files match the authorities as of now." Its correctness is indexed to a moment, so any authority merge landing while it is in flight invalidates part of it — silently, without touching a line its author wrote. Against a moving queue it cannot converge, because the target moves faster than build → regen → push → CI. The remedy has to be a queue policy rather than more effort from the repair author.

Expected duration: short. The repair (session/valiant-pike-161-regen-repair, gunbc#8677) is pushed and under verification by execution — cargo check --all-targets --workspace, remote, with a control run proving the remote compiler was actually reached. A clean check lifts the hold.

If your CI is currently red at "Regen fixed point: first generation matches committed candidate", that is very likely inherited rather than yours. Main has been red at that step since ad715efe09c. Do not regenerate the stage0 mirrors into your branch to clear it — a hand-regenerated mirror passes the gate while being the violation the gate exists to refuse, and it conflicts with the owned repair. Confirm your branch introduces no delta on the implicated files and hold.

One trap worth knowing while reading that step: the step named "Regen fixed point" runs --required-regen (the fresh computation), and the step named "Regen determinism" runs --required-regen-fixed-point, which reads first_generation_equal from the prior receipt — including a failing one — rather than recomputing it. Read the step that runs the flag, not the one named for it.

— sent from smart-ram-730

Brian Searls and others added 2 commits August 20, 2026 18:12
A population refusal returns Ok with a receipt whose digest fields hold the
sentinel `refused:population` — the receipt's fields are String and there is
nowhere else to put "there was no measurement". The composed coordinator read
that receipt, so a refusal handed the sentinel to phase three, which compared
it against a real pass-two digest and reported

    fixed-point refused: pass-1 digest refused:population != pass-2 digest <real>

a determinism failure nobody measured, wearing the shape of a real one (§5
fabricated plausible output). The sentinel was documented as known residue;
what was missed is that consolidation gave it a route out.

RequiredRegenOutcome now carries FirstGeneration = Measured(digest) |
NotMeasured(reason), and pass1_digest_for_fixed_point is the only route to the
digest — a refusal has no digest field to read, so phase three reports its
existing SKIPPED state. Drift still runs the fixed point; drift and refusal
were never the same thing.

Three premise-accuracy edits from the same review: FIVE CAUSES -> SEVEN (main
added route_gap and stale_route_gap and the sentence kept saying five); the
dual-input control said ENROLLED RED when the Rust suite has been out of CI
since 2026-07-11, so it says LOCAL; and the workflow witness said "the retired
parse binary is no longer built" when fleet-converge still builds it — scoped
to the required workflow, which is what its subject can decide.

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

gunbai-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Side-thread review raised six items on this branch. One was a real live defect and is fixed; three were premise-accuracy and are fixed; two are architecture I am deliberately not doing here.

Fixed — the sentinel had a route out. A population refusal returns Ok with a receipt whose digest fields hold refused:population. I had documented that sentinel as known residue, and missed that consolidation gave it somewhere to go: the composed coordinator read the receipt, so a refusal handed the sentinel to phase three, which compared it against a real pass-two digest and reported

fixed-point refused: pass-1 digest refused:population != pass-2 digest <real>

a determinism failure nobody measured, wearing the exact shape of a real one. RequiredRegenOutcome now carries FirstGeneration = Measured(digest) | NotMeasured(reason), and pass1_digest_for_fixed_point is the only route to the digest — a refusal has no digest field to read, so phase three reports its existing SKIPPED state. Drift still runs the fixed point; drift and refusal were never the same thing. Control mutation-tested: making the accessor answer from the receipt fails it, and it is green again after restore, in the same command.

Fixed — three things that said more than they knew. FIVE CAUSES had been five before main added route_gap and stale_route_gap, and kept saying five through the merge that added them. The dual-input control's heading said ENROLLED RED when the Rust suite has been out of CI since the 2026-07-11 ruling — the rung inflation DESIGN §4b calls worse than sitting low, since a control that says it is enrolled never ranks for enrolling. And the workflow witness said "the retired parse binary is no longer built" when fleet-converge still builds it; it is now scoped to the required workflow, which is what its subject can decide.

Not doing here, and why. Preparing the regen source population once instead of four times, and passing the pass-one receipt in memory so the fixed point never rereads it, are both correct and both change the regen API shape rather than the consolidation. Same for the supervisor split — forking a child per phase to get lifecycle resets is a real proposal, but it undoes the one-process property this PR was asked for, and no evidence yet says the retained state is wrong; the one measured symptom, floor CPU attribution, is fixed at its source. These belong in a follow-up against a green base, not in the PR that is trying to reach one.

Merged main (5814d0d); cargo check --all-targets clean.

— sent from tidy-lark-471

@gunbai-bot

gunbai-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Hold LIFTED — the regen repair has landed and verified.

The authority-touching hold posted on this PR earlier is over. Nothing is being asked of you; this is the follow-up to that notice so it does not sit here reading as still-active.

What cleared it. gunbc#8677 merged as 026a709a71. On main's run 32400897515:

6. Regen fixed point (runs --required-regen, the fresh arm)  -> success
7. Regen determinism (full second emit pass)                 -> success

First green at step 6 since ad715efe09c at 16:27Z. Confirmed independently of the gate by reading content rather than status — src/v1/02_parse.dag and its stage0 mirror v1_compiler_parse.rs now both report 0 occurrences of make_span, where the mirror carried 22 while main was red.

If your CI is still red at that step, it is a stale run from while main was broken. A re-run against current main should clear it. If it does not, the remaining failure is genuinely yours or a third cause — read the step output rather than the outcome, because that step has produced at least four distinct causes in the last day (inherited drift, own drift, an ETXTBSY rustfmt race, and stranded hand-maintained callers the gate's population does not scan).

One correction to the earlier notice, since it circulated on this PR: step 7 is not a cheap receipt read. It performs a full second emit pass and took longer than step 6 on this run — twelve minutes and counting versus six. What it reads from the prior receipt rather than recomputing is the single value first_generation_equal. A long step 7 is normal; do not read it as hung and do not cancel it.

— sent from smart-ram-730

@briansrls
briansrls merged commit 01a76c0 into main Aug 20, 2026
1 check passed
@briansrls
briansrls deleted the session/tidy-lark-471-ci-consolidate branch August 20, 2026 19:10
gunbai-bot Bot pushed a commit that referenced this pull request Aug 20, 2026
…rror index reads both header conventions or refuses

Three things, and the middle one is a defect found by review before it bit.

1. #8647 collapsed this job's four steps into one --required-ci invocation. The
   receipt's two steps therefore become PHASES inside that fold rather than two
   more rows in the ladder that PR removed. The per-PR phase used to be gated by
   `github.event_name == 'pull_request'` in YAML; a single invocation cannot
   carry a per-phase trigger, so the phase now decides from what it can OBSERVE:
   when the merge base resolves to HEAD there is no diff, and it reports SKIPPED
   with that reason. That is stronger than the trigger name -- it is derived from
   the state that makes the observation impossible, so it holds on any trigger
   anyone adds later. Typed as ReceiptPlanOutcome::NoSubject, never Ok(true).

2. THE MIRROR INDEX WAS BLIND TO TWO REAL MIRRORS. It joined on
   `// Source module:` only. Measured: 130 files in stage0/src declare themselves
   generated -- 126 carry that key, 2 carry `// Authority:`
   (bootstrap_stage0_crate_layout_generated.rs, v1_interpreter_dispatch_generated.rs),
   and lib.rs/main.rs are crate roots. So a change to
   v2.compiler.self_host.stage0_crate_layout or gunbc.v1_interpreter_primitive_surface
   would have printed "no emitted mirror declares it" -- FALSE, in the direction
   that silently skips the check.

   That is the same two-zeros conflation this mode was corrected for one level up:
   "nothing mirrors this" and "I could not find what mirrors it under the key I
   searched" printed the same line. The fix is not merely learning the second key,
   because learning keys one incident at a time is how the blind spot recurs: the
   index now ASSERTS ITS OWN KEY-SPACE COMPLETENESS and the whole selection refuses
   if any self-declared generated file carries neither key.

   Measured on the real corpus: roster 126 -> 128, both authorities visible.
   RED, planting a third convention: "the mirror index cannot see 1 generated
   file(s) ... zz_probe_generated.rs". Control, removed: selection proceeds.

3. The census reads through the same header reader, so census and gate cannot
   disagree about which files mirror an authority.
gunbai-bot Bot pushed a commit that referenced this pull request Aug 20, 2026
#8647's w_ci_invokes_one_composed_mode_not_a_step_ladder matched
`claim_executor" --required-ci`, where the trailing double quote is the tail of
the hand-built "$ROOT/target/release/claim_executor". This PR renders the step
from the fabric's ArgvCommand via shell_command_render, which single-quotes
every word per IEEE 1003.1-2017, so the emitted text is now
'target/release/claim_executor' '--required-ci' and the positive clause stopped
matching. That red was correct and is what surfaced this.

Re-spelling the pattern for the new renderer would have greened the positive
clause and left the three NEGATIVE clauses carrying the identical coupling: a
--required-floor step growing back emits as '--required-floor', which
`claim_executor" --required-floor` cannot match, so all three would have gone
permanently, vacuously green while still reading as coverage. The positive
clause fails loudly; the negatives fail silently.

Matching the flag tokens alone is spelling-independent and strictly stronger as
a negative, since it catches a retired invocation under any quoting.

Evidence, by execution, not by typecheck:
  patched, unmutated          -> returned true
  floor_run_command flag
    mutated to --required-floor -> returned false
  file restored, byte-identical to backup

The mutation control is the point: the defect being repaired is a clause that is
green because it can no longer match anything, and the original clauses could not
have gone red under any re-spelling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012q31BK3okLA8vG4kdTWtBf
briansrls pushed a commit that referenced this pull request Aug 21, 2026
…ding it (#8629)

* The emitted floor step renders the fabric's command instead of rebuilding it

#8576 landed floor_run_command, an ArgvCommand the fabric authorizes, and left
gunbc.witness_floor_workflow building the SAME invocation a second time -- its own
binary path, its own --required-floor, its own --source-root spelling, folded over
the same witness_layer_roots. Then it pinned the two together with
fabric_argv_and_workflow_step_agree_on_source_roots, a witness asserting they
agreed on source roots.

That was validation standing where construction was available, and I wrote it. It
could stay green over a drifted binary path, a drifted flag spelling, or a
different argument ORDER, because agreeing on the roots is not agreeing on the
command. The fork is dissolved rather than re-pinned: witness_floor_run_script
now renders floor_run_command through extdeps.exec.command shell_command_render,
which already existed and is already the modeled realization for argv-to-shell.

WHY THE PATHS BECAME RELATIVE. shell_command_render single-quotes every word,
cited to IEEE 1003.1-2017 section 2.2.2 -- correct for an argv and fatal for a
word carrying a shell variable, since '$ROOT/dag' does not expand. So $ROOT is
not in the argv: the script cds to the root first and the argv stays genuinely an
argv, which is what lets a modeled renderer quote it at all. The sibling v1-parse
step in this same workflow already uses cd "$ROOT", so this is that step's
pattern rather than a new one. Same binary, same flags, same roots, resolved
against the same directory -- and the emitted diff is exactly two lines, which is
the evidence that nothing else moved.

EVIDENCE, executed both ways. Against the old hand-built script:
  workflow_step_renders_the_fabric_command_and_does_not_respell_it  false -> true
  every_declared_source_root_reaches_the_emitted_step               false -> true
  floor_argv_carries_every_declared_source_root                      true -> true
The third holding in both directions is what separates a specific red from a
change that broke everything.

The discriminating clause is the second one rather than the first: '$ROOT/dag' was
the old fork's exact spelling and is UNRENDERABLE from an argv, so its presence
could only mean someone hand-built the path back into the script -- the only way
the fork returns. Asserting the render alone would restate the implementation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012q31BK3okLA8vG4kdTWtBf

* Decouple the consolidation witness from one spelling of the invocation

#8647's w_ci_invokes_one_composed_mode_not_a_step_ladder matched
`claim_executor" --required-ci`, where the trailing double quote is the tail of
the hand-built "$ROOT/target/release/claim_executor". This PR renders the step
from the fabric's ArgvCommand via shell_command_render, which single-quotes
every word per IEEE 1003.1-2017, so the emitted text is now
'target/release/claim_executor' '--required-ci' and the positive clause stopped
matching. That red was correct and is what surfaced this.

Re-spelling the pattern for the new renderer would have greened the positive
clause and left the three NEGATIVE clauses carrying the identical coupling: a
--required-floor step growing back emits as '--required-floor', which
`claim_executor" --required-floor` cannot match, so all three would have gone
permanently, vacuously green while still reading as coverage. The positive
clause fails loudly; the negatives fail silently.

Matching the flag tokens alone is spelling-independent and strictly stronger as
a negative, since it catches a retired invocation under any quoting.

Evidence, by execution, not by typecheck:
  patched, unmutated          -> returned true
  floor_run_command flag
    mutated to --required-floor -> returned false
  file restored, byte-identical to backup

The mutation control is the point: the defect being repaired is a clause that is
green because it can no longer match anything, and the original clauses could not
have gone red under any re-spelling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012q31BK3okLA8vG4kdTWtBf

* Narrow the consolidation witness's stated claim to what it proves

The revised row asserts contains("claim_executor") && contains("--required-ci")
as two INDEPENDENT substring checks, and the heading claimed they establish that
one run step reaches the binary and names the composed mode. They do not join:
`claim_executor` already appears in the build step as a binary name, so the row
would stay green if the run step were replaced by something unrelated that merely
contained `--required-ci`.

The implementation is still grounded -- fabric_witness_run_test proves the exact
rendering of floor_run_command reaches the step, and separately that the command
selects --required-ci -- so the combined evidence is sound. What was wrong was
the comment, which asserted a join the code omits. Narrowed rather than
strengthened, one authority per proposition: this row owns "no retired external
invocation returned" and nothing else, and names the two rows that own the rest.

Raised in side-chat review, 2026-08-20.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012q31BK3okLA8vG4kdTWtBf

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot added a commit that referenced this pull request Aug 21, 2026
…r_workflow (#8737)

#8702 (mine) deleted a 14-line annotation block and reverted a CI step name that
another session had authored in #8657. I did not write those deletions. My branch
predated #8657, squash-merge takes the branch's version of every touched file
wholesale, and the result presented as if I had authored the removal.

WHAT WAS LOST:

  - the annotation explaining why the behavioral receipt is NOT a step here --
    that #8647 collapsed the step ladder into one invocation, that re-adding
    steps would rebuild the ladder that PR removed, and that the per-PR phase
    now decides from what it can OBSERVE rather than from a trigger name. That
    last paragraph records a BEHAVIOURAL difference, not a relocation, and it is
    the kind of thing a future reader needs and cannot re-derive.

  - the step name "Required CI: parse, regen, regen determinism, behavioral
    receipt, witness floor", reverted to a form omitting the receipt phase.

WHY NOTHING CAUGHT IT. Three properties compounded:

  1. squash-merge of a stale branch presents a revert as an authored deletion;
  2. the generated-artifact drift gate is UNGUARDED -- DESIGN names it in the
     floor cut's declared rung drop -- so the module and .github/workflows/
     witnesses.yml disagreed on main with nothing to notice;
  3. the merge was clean, five reviews approved the diff, and CI passed.

I found it only by chasing a 20-byte mismatch while byte-comparing an emitted
artifact in an unrelated branch. That comparison is exactly the check CI is
currently missing.

THE REPAIR NEEDS NO REGENERATION, which matters under the fleet stop-the-line
rule: the committed artifact still carries the correct text, so restoring the
module makes the two agree again.

  witnesses.yml   emitted 1457 == committed 1457   (was 1437 vs 1457 on main)
  the_live_witness_floor_job_closes_its_capabilities -> true
  in-body annotations -> 0

THE GENERAL HAZARD, recorded because it is not specific to this file: a
long-lived branch plus squash-merge is a silent-revert machine. Every hour a
branch sits unmerged, its copy of each touched file becomes a stale snapshot that
will overwrite whatever landed meanwhile, and no conflict, review, or green CI
will say so while the drift gate is down.

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Aug 21, 2026
…hole in admit_callers (#8796)

* Bind fleet-converge's build job to capability closure; widen the role to a relation

WIP commit before merging main -- full message on the PR.

* Restore what my stale-branch squash silently reverted in witness_floor_workflow

#8702 (mine) deleted a 14-line annotation block and reverted a CI step name that
another session had authored in #8657. I did not write those deletions. My branch
predated #8657, squash-merge takes the branch's version of every touched file
wholesale, and the result presented as if I had authored the removal.

WHAT WAS LOST:

  - the annotation explaining why the behavioral receipt is NOT a step here --
    that #8647 collapsed the step ladder into one invocation, that re-adding
    steps would rebuild the ladder that PR removed, and that the per-PR phase
    now decides from what it can OBSERVE rather than from a trigger name. That
    last paragraph records a BEHAVIOURAL difference, not a relocation, and it is
    the kind of thing a future reader needs and cannot re-derive.

  - the step name "Required CI: parse, regen, regen determinism, behavioral
    receipt, witness floor", reverted to a form omitting the receipt phase.

WHY NOTHING CAUGHT IT. Three properties compounded:

  1. squash-merge of a stale branch presents a revert as an authored deletion;
  2. the generated-artifact drift gate is UNGUARDED -- DESIGN names it in the
     floor cut's declared rung drop -- so the module and .github/workflows/
     witnesses.yml disagreed on main with nothing to notice;
  3. the merge was clean, five reviews approved the diff, and CI passed.

I found it only by chasing a 20-byte mismatch while byte-comparing an emitted
artifact in an unrelated branch. That comparison is exactly the check CI is
currently missing.

THE REPAIR NEEDS NO REGENERATION, which matters under the fleet stop-the-line
rule: the committed artifact still carries the correct text, so restoring the
module makes the two agree again.

  witnesses.yml   emitted 1457 == committed 1457   (was 1437 vs 1457 on main)
  the_live_witness_floor_job_closes_its_capabilities -> true
  in-body annotations -> 0

THE GENERAL HAZARD, recorded because it is not specific to this file: a
long-lived branch plus squash-merge is a silent-revert machine. Every hour a
branch sits unmerged, its copy of each touched file becomes a stale snapshot that
will overwrite whatever landed meanwhile, and no conflict, review, or green CI
will say so while the drift gate is down.

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

* Bind the websocat driver to the stage the sequence authority names

The first cut's claim that websocat_after is the sole transition authority was true of
the stop-or-continue decision and NOT true of the stage edge. Every driver site matched
`WebsocatContinue { next: _ }` and then ran the stage it was written to expect, so the
order existed twice: once as data in websocat_after, once as a hardcoded call chain.

That permitted a divergence no witness could see. Rewrite an edge -- EnsureDirectory
continuing to Download instead of EnsureOwnership -- and the trace, which follows `next`,
would report the new order while production carried on chowning in the old one. The
never-stops mutation could not catch it: it mutates the stop decision, not the edge.
Raised in side-chat review of #8747, verified here before acting on it (six sites
discarded `next`).

The driver now consults the authority. Each site checks that the stage websocat_after
named is the stage that site implements, and a disagreement REFUSES with both stage
names rather than running the code it happens to have. Comparison goes through
websocat_stage_label because that fold is already the total projection of the stage type;
a second equality over the same coproduct would be the fork this removes.

srv3_websocat_install_from is now one function per stage rather than one function nesting
five matches. That is not tidying: at depth five the guard and the effect it guards were
no longer visible together, which is the condition under which this file's defects have
been introduced twice.

Evidence, by mutation: rewriting the EnsureDirectory edge to Download reddens exactly the
directory-edge witness and the full-chain trace, and leaves the other three edge witnesses
green -- located, not just red. The guard's own negative control (an edge deliberately
compared against a stage no site implements) proves it can answer false at all.

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

* Ground the srv3 POSIX principal and privilege lowering; refuse degenerate ids

The chown that srv3's websocat install performs took its owner as a String composed
as uid + ":" + gid from two raw stdout captures, and ran under a hardcoded
"/usr/bin/sudo" with a bare "-n" at the head of the args list. Both are the
String-as-anemic-modeling class: there was no state in which a bad `id` read could be
noticed, because concatenation always succeeds.

POSIX owns the ids, so extdeps.posix.identity mints PosixUserId, PosixGroupId and
PosixOwnerSpec, and owns the ":" form chown takes. It is not a second spelling of
extdeps.access.posix_effective_principal, which models the principal's NAME as
whoami reports it -- POSIX keeps those two facts apart and so does this.

sudo's calling convention was spelled at four call sites. extdeps.sudo.elevation now
owns it: sudo_elevate takes the argv a command would run unelevated and returns the
elevated invocation, so elevation is a transformation OF an invocation rather than a
second way to spell one. All four "/usr/bin/sudo" literals and all three hand-carried
"-n" prefixes are gone. extdeps.tools.chown and extdeps.tools.mkdir own their argv and
their absolute paths; extdeps.tools.id gains id_binary_path. The absolute paths are
load-bearing rather than pedantry -- a sudoers NOPASSWD rule matches on command path.

The LocalShell arm also stopped reading .stdout raw. It goes through the same trimmed
token shape the ssh arms use, which was a live cross-transport disagreement: `id`
emits a trailing newline, ssh trimmed it and local did not.

DECODING DID NOT BY ITSELF CLOSE THE HOLE, and a witness is what proved it.
integer_lexeme_to_int_optional answers Present { value: 0 } for "", and 0 is root, so
an `id` that printed nothing decoded to the most privileged principal on the host and
was chowned to. The witness asserting that empty text does not decode came back FALSE
on its first run. The decoder now answers its own degenerate cases: empty,
whitespace-only and negative are refused; "0" is admitted, because root is a real
principal. The law is not "zero is invalid" -- it is that only the explicit lexical
representation of zero may construct id zero.

WHAT THIS DOES NOT CLAIM. Ownership is not ensured. This grounds the operands and the
privilege lowering; it does not observe the current owner, does not skip a chown that
is unnecessary, and does not read ownership back afterwards. A successful chown process
is still the only evidence, which is why nothing here is named EnsurePathOwner.

15 witnesses, all measured green.

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

* Ensure srv3's bin directory ownership by readback, not by chown's exit status

The websocat install's EnsureOwnership stage ran an elevated chown and returned
Holds or Fails straight from the process result, so "the chown exited zero" and
"the directory is owned by us" were one claim. They are not. A chown that followed
a symlink changed something else; one that raced a replaced path changed the old
inode; one under a sudoers rule permitting the binary but not the target can exit
zero having converged nothing. Exit status as convergence is the fabricated
plausible output at the actuation boundary.

extdeps.posix.path_ownership models the ensure as observe, decide, mutate only if
needed, observe again independently, and let the SECOND observation decide. The
mutation's own result cannot reach the verdict: path_ownership_verdict takes the
readback and the desired owner and has nowhere to put a process result. A chown that
reported failure is read back exactly like one that reported success, because a
process result is not evidence about the filesystem in either direction. The only arm
that skips the readback is the one where no host was reached at all.

Skipping an unnecessary chown is a safety property rather than an optimization: on a
converge that has already run, the declined mutation is the ONLY privileged
invocation this stage would have made.

An unobserved owner is not an unowned path. `stat` refusing and `stat` reporting a
different owner demand opposite actuations, and reading the first as the second would
chown a host that was never asked -- the empty-observation narrow pointed at a
privileged mutation.

extdeps.tools.stat is cited to GNU coreutils rather than POSIX, deliberately: -c is a
GNU extension, POSIX does not specify stat(1) at all, and BSD spells the same request
-f with different conversion characters. A host shipping the BSD utility needs its own
authority, not a widened format string here. Two probes of one token each, rather than
one "uid:gid" probe, so the existing single-id decoder is reused instead of a second
place where the ":" grammar lives.

srv3_transport_token replaces the third hand-rolled four-arm transport match over the
same shape.

Evidence: 8 witnesses green. By mutation -- the verdict rewritten to ignore the readback
and report convergence, which is the pre-cut behaviour -- the two refusal witnesses flip
to false while the positive control and the four decision witnesses stay true.

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

* Repair the ownership cut: restore the deleted helpers, keep the chown as evidence, stop sequencing by argument position

Three defects, none of them found by me.

ONE, from CI's floor: this branch did not resolve at all. The commit replaced a block
of host_effect_realize by index range, and the range swallowed three helpers added
minutes earlier in the same session -- srv3_transport_token and its two locals. They
are restored.

TWO, from review 54354: the cut deleted srv3_chown_to_decoded_owner while the
principal/privilege witness file still imported it, so the three load-bearing REDs for
the fabricated-owner refusal could not execute. They are rewritten against
srv3_owner_from_texts, the surviving pure entry point that carries the decode refusal,
plus a positive control -- three refusal assertions are satisfied by a function that
refuses everything. The "refuses before chowning" claim is now structural rather than
positional: the ensure is parameterised on the observed owner, so an unobserved owner
cannot reach the chown.

Why neither surfaced locally: after the change I ran only the NEW witness file, whose
subjects all live in extdeps modules, so it went 8/8 green without ever resolving the
file I had just edited. Running the witnesses for what I added is not the same as
running the witnesses whose subject I changed.

THREE, from side-chat review: the verdict discarded the mutation entirely, fusing "not
authoritative for state" with "not evidence at all". The state still comes from the
readback and nothing else -- every arm dispatches on it, and `attempt` reaches the
outcome only as a field of the receipt -- but the attempt is now carried, and the
outcome is renamed PathOwnershipConvergedAfterAttempt because "Changed" asserted a
causal claim the evidence does not support: a chown that reported FAILURE followed by a
desired-owner readback establishes that the state exists, not that our mutation produced
it. The mismatch diagnostic also said "the chown reported no error" while both reported
success and reported failure routed into it -- false half the times it fired. It now
names what the actuator reported.

The attempt carrier does not yet hold exit code or stderr, and says so with its
next-rung trigger: the transport surface projects a process result to a three-valued
predicate before this module sees it.

Also from that review: the chown was sequenced by ARGUMENT POSITION, with a comment
defending it as deliberately load-bearing eagerness. That reinstates the exact footgun
this sequence of cuts exists to remove -- implicit evaluation order is not a sequencing
authority, and wanting eagerness this time does not make it one. It is a `let` inside
the arm now.

26 witnesses green across both files. The principal file imports host_effect_realize,
so its resolution is now part of the evidence rather than assumed.

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

* Make each srv3 tool's acquisition mechanism a row, and derive the receipt from it

How a tool is acquired was expressed only by which function the author happened to call:
three srv3_ensure_apt_tool calls and one srv3_ensure_websocat, hand-written side by side.
The receipt that reports them was a concat chain naming each tool TWICE -- once as a
string literal in the receipt, once as a variable in the call -- so the two could disagree
and nothing would notice. Adding a tool meant editing three places and remembering a
fourth.

Srv3ToolAcquisition names the mechanism, Srv3ToolRow pairs it with the tool, and
srv3_ensure_tool is the only place a mechanism is chosen -- a total match, so a new
mechanism is a variant the compiler refuses to leave unanswered. The ensure folds the
rows and the receipt is derived from the same observations, so a tool cannot be ensured
and omitted from the receipt, or renamed in one and not the other.

require_version_probe moved INSIDE the apt variant rather than sitting beside the
acquisition as a peer field. The release path has no version probe to require, so as a
peer it was a value one variant silently ignored -- a state the type admitted and the
code dropped.

Evidence: 4 witnesses green, and each is discriminating. Switching websocat's row from
the release mechanism to apt reddens the mechanism witness alone; stripping the tool name
out of the receipt entry reddens both receipt witnesses and leaves the mechanism one
green. The empty case is stated rather than left to be discovered.

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

* Seal srv3's execution surface so no other module can name it

A caller outside gunbc.host_effect_realize could reach srv3_transport_witness_bin_success
and srv3_elevated_witness_bin_success directly -- "run this argv on this transport" and
"run this argv as root on this transport". Both are now admit_callers-sealed to the
functions that legitimately reach them, so a foreign caller can name a semantic operation
(ensure this tool, ensure this owner) and cannot name the machinery that executes it.

THE SEAL'S REACH IS MEASURED, AND IT IS NARROWER THAN THE NAME SUGGESTS. Two probes on
one build:

  - a caller in ANOTHER module REFUSES at resolve, naming the permitted set:
    "constructor call admission refused: ... refuses call from
    'test.claim.srv3_seal_probe.a_foreign_module_may_not_run_a_privileged_argv' --
    permitted callers: [...]"

  - a caller in THIS module, absent from the admitted list, calling the elevated form with
    ["/bin/rm", "-rf", "/"], typechecked and resolved with NO DIAGNOSTIC AT ALL.

So the rung is mechanically preventable at the module boundary and nothing within it.
Reporting only the refusal would be the rung inflation DESIGN section 4b calls worse than
sitting low, so both halves are recorded beside the seal with the next-rung trigger:
admission checked per caller rather than per module.

The admitted lists were derived by walking the module rather than by reading the call
sites. My first hand-read attributed two calls to the wrong enclosing function and
invented a fifth caller that does not exist -- and because in-module admission is
unchecked, nothing would have caught either. A list the compiler does not verify is the
second reason that trigger matters.

Both probes were removed after measurement: an admission refusal is a resolve-time error,
so it cannot be enrolled as a passing witness without breaking the corpus it proves.

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

* Assemble elevated argv with one append instead of a snoc per element

Raised as a non-blocking observation on review 54393, and fixed rather than noted:
DESIGN section 6's bare-minimum-cost rule says a proven cost shape -- "a copied
accumulator, a quadratic fold" is the wording -- is always fixed regardless of the
realized n, because "n is small here" is not a time-stable fact.

sudo_elevate folded the command argv, appending to a growing accumulator once per
element. It is the single place every privileged invocation in the repository is
assembled, so its n is whatever a future caller's argv turns out to be, which is exactly
the case the rule is about. srv3_argv_with_bin carried the same shape and is fixed with
it rather than left as the next instance of a defect just removed.

The reviewer's second observation -- that websocat_edge_admits compares stages through
websocat_stage_label rather than a structural equality -- is deliberate and stays. A
second total projection over the same coproduct would be the fork that function exists
to remove.

Five witnesses over both argv builders and the acquisition rows re-measured green.

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

* Dissolve the last argv literal in the srv3 chain: chmod gets its authority

Review 54403 called the surviving "/bin/chmod" literal minor residue and outside this
cut's scope. It is the last one in the chain, and leaving one of six is what makes the
class get re-derived later -- the operator's standing instruction on this program is that
a string in this position is anemic modeling and that the debt is mine to own, so the
scope argument cuts the other way here.

extdeps.tools.chmod cites POSIX and carries the absolute path for the same reason
extdeps.tools.chown and extdeps.tools.mkdir do: a sudoers NOPASSWD rule names a command by
path, and chmod sits at /bin rather than /usr/bin on the hosts this actuator targets --
a fact about those hosts, recorded rather than assumed at a call site.

The mode is SYMBOLIC and the module says why: `+x` ADDS the execute bit to whatever
permissions the file carries, while an octal mode REPLACES the whole set. They are not
interchangeable spellings of "make it executable" -- one preserves the other bits and one
silently decides them -- and an ensure means the additive one.

srv3_transport_argv_success is the argv-shaped sibling of the witness-bin surface. Every
extdeps tool row returns a complete argv while that surface takes a binary and its tail
separately, so callers were either splitting the argv apart by hand or, more often, not
building one at all and passing a literal binary beside a literal args list. It is sealed
to its caller like the rest of the execution surface, and it refuses an empty argv rather
than running whatever a split of nothing produces.

Five witnesses green, including the new chmod row and the two websocat stages that reach
it.

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

* Close the same-module hole in admit_callers

constructor_call_admission_diags skipped the permitted-list comparison entirely when
caller and callee shared a module, so the semantics the compiler implemented were
'a permitted caller OR any declaration in the defining module' -- an implicit wildcard
that never appeared in the authored declaration and that no reader of a sealed fn could
see.

Measured before the repair: a probe added to gunbc.host_effect_realize, absent from the
admitted list, calling the elevated execution leaf with ["/bin/rm", "-rf", "/"],
typechecked and resolved with no diagnostic at all, while the same call from another
module refused. Measured after: same-module unlisted callers now refuse with the existing
ConstructorCallAdmissionRefused diagnostic naming the permitted set.

The repair deletes the branch and nothing else. The exact caller-coordinate comparison,
the diagnostic, and the caller-identity-unavailable arm were all already present and
already correct -- the compiler had every fact it needed and declined to use it.

The stage0 mirror is the regen candidate, produced by claim_executor --required-regen and
installed from it, not hand-carried. required-regen reported drift on exactly one file,
v1_compiler_infer.rs, which is the mirror of this change.

Operator-admitted (2026-08-21, direct chat: 'regarding the compiler defect, you may fix
it'). The permitted lists this surfaces across the corpus follow in the same change.

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

* Admit the sibling constructor route the closed hole exposed

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

* Ground the build-step chmod invocation on the cited chmod authority

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.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