Skip to content

General orchestration intent to Bash emit fold over grammar rows - #5828

Merged
briansrls merged 28 commits into
mainfrom
session/orchestration-bash-coverage
Jun 26, 2026
Merged

briansrls merged 28 commits into
mainfrom
session/orchestration-bash-coverage

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session fierce-crane-753.
Pushing to session/orchestration-bash-coverage advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

briansrls and others added 27 commits June 25, 2026 15:05
…mmar row

orch_emit_pipeline composes per-construct grammar rows by emitting each child
intent to Bash and binding its string into the parent production (orch_seq2),
then emitting the parent — every string comes from emit() over a grammar row, no
translate-stage change. Fold lives in v2.workflow (compiler consumer), rows stay
in bash.dag. Witnesses prove a 2-step Pipeline emits newline-joined by execution
plus a discriminating space-joined perturbation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d (general grammar rows)

Add general grammar rows (Assign/If/AndOr/Pipe/WithRedir/EnvPrefixed/Heredoc
+ program set_e wrapper) and their emit_shell_stmt / emit_shell_program arms,
each byte-identical vs the program.dag serialize oracle with discriminating
teeth. 25 witnesses green by execution under both source-roots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… grounded in real build_step_transport fragments

Add the two nameable bash constructs the build_step_transport migration needs
(path-redirect, negation prefix) as permanent program.dag model variants, with
disposable serialize arms for totality. emit_redir recursively emits the
RedirToFile path word; bash_stmt_negation grammar row emits the '! ' prefix.
Witnesses ground the new operators in the REAL harness spellings
('> "$PROBE_BIN"', '! (...)') — NOT a fresh serialize arm (anti-circular).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rminism for multi-orch_raw-token rows); keep grammar union coverage + RedirToFile/Negation + lex-completeness fixes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ge env-bearing; strengthen held-welds witness

GAP 1 (§2 keystone fork): orchestration_emit's orch_emit_run no longer
stands beside the general shell_emit fold unexplained. Env-FREE Run now
lowers to RawLine(command) and delegates to emit_shell_stmt (single
orch_raw green path, proven byte-identical by
orch_run_empty_env_delegates_to_shell_emit). Env-BEARING Run lowers to
EnvPrefixed (multi-orch_raw) which hits the emit-kernel straddle, so it
stays on orch_run_emitted_command (orch_emit_run_env_welded) under a new
on-carrier dissolution trigger sibling to the held-welds one. Delegate
the green part, stage the blocked part.

GAP 2: bash_held_welds_dissolution_trigger_recorded_holds strengthened
from a !="" proxy to string_contains teeth — asserts the trigger names
each held weld + the kernel-debt straddle condition + DISSOLVES WHEN.
Goes red if the marker is edited to drop them; the held welds' continued
existence is already proven by the bash_emit_*_holds consumer tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Design final-sign (by my own execution) on head 4fb13ca929 — the full keystone+coverage arc is design-complete.

Verified: GAP1 (4/4 PASS on orchestration_emit_test.dag, both source-roots) — env-free Run now delegates to emit_shell_stmt (orchestration_emit imports shell_emit; real delegation), env-bearing Run stays welded under the on-carrier orch_run_env_reduction_dissolution_trigger naming the same EnvPrefixed kernel straddle. GAP2 — held-welds witness strengthened to string_contains asserting each held-weld name + 'straddle' + 'DISSOLVES WHEN' (real teeth; verified the marker contains all). Oracle clean (0 foreign), deletion hygiene clean (exit welds deleted 0 refs, held welds present 15/30/15 refs). Honest bound: bash_emit_command_test.dag won't resolve locally (>9min, the heavy-resolve that OOMs CI), so GAP2's new witness is verified by inspection (deterministic) and its other tests are pre-existing/unchanged green.

Remaining merge gates are outside the design: a 2nd distinct approval provider, and CI green — currently blocked on the runner-OOM infra (the gunbc ci floor process is SIGKILL'd / exit 137 on memory, even after #5827/#5829 and on retriggers), not content. The one outstanding debt is the emit-kernel String↔List-Int-codepoint straddle (surfaced to operator), which dissolves the held welds + the env reduction when fixed.

— sent from neat-fox-547

gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
…tor verdict, not free debt

review 55258 is right and the correction is worth more than the file. The runner's header claimed
that composing an existing scaffold "adds no new debt beyond the one it composes". That is
self-authorized dissolution, which DESIGN.md names as a failure mode by that name, and it was
wrong on its own terms: a second removable unit is a second obligation that whoever deletes the
first must also find, and a trigger the author writes is a lifecycle fact rather than permission
to create the debt.

So the header now says what it is -- a hand-authored shell orchestration scaffold, DESIGN §6's
out-of-band-actuation tell -- names the concrete dissolution trigger (#5828 bash-emit or a modeled
cssl_probe transport, at which point the file is deleted rather than ported), and states that it
lands only on an operator verdict on this exact exception. That verdict is requested, not
asserted, and it is external to this diff by construction.

Deletion is a live option and costs nothing that has already been measured: the fifteen logs,
their sha256s, the probe rows and the join are committed data, and curated_cargo_probe_one.sh
takes a board without this file. What deletion costs is the mechanised roster read -- the only
non-diligence defence against the enumeration that produced three wrong populations in this fleet
in one evening. The board README now says so where it tells a reader to run it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A67J8mYHNENb4yndcBrMD5
briansrls added a commit that referenced this pull request Aug 24, 2026
…stinct site (#9064)

* Board the frontier by authority, and weight roots by distinct site rather than by summed board

Two probe instruments, no production change.

curated_cargo_board_cohort.sh boards the frontier roster in one process at one ref, reading BOTH
the module list and each row's shim_lib_rel from
dag/tools/self_host_module_behavioral_transport_roster.dag. Enumerating by glob, by grep on a
field name, or by hand each produced a wrong-but-plausible population in this fleet inside one
evening; the authoritative enumeration is mechanised here rather than left to diligence. It
refuses a non-roster module, refuses a discarded log, and exits non-zero naming every board that
line-stopped, so a partial cohort cannot be reported as the frontier.

cross_module_mechanism_weighting.py joins those boards. Its point is that summed manifestations
are not a defect count across modules: closures overlap, so one emitted defect is counted once
per door it is visible through, and the sum rises when more entries are boarded with nothing
changed in the tree. It reports distinct sites and breadth instead, at two declared
site-identity strictnesses, because strict identity assumes an emitted file has the same bytes
in every closure and that is precisely the assumption that can be wrong quietly.

Root vocabulary is borrowed, not minted: cross-code identity from rustc_mechanism_classify, the
E0308 15-root view from e0308_classify_sites carried as a code-local projection column. Rows
with no established cross-code discriminator stay UNCLASSIFIED rather than being filled in from
the error code.

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

* Make the cohort's ref and population an assertion the join executes, not the caller's diligence

The weighting is a comparison, so its failure mode is not a weak measurement but a contaminant:
a board taken at an unasserted ref is indistinguishable afterwards from a genuine per-module
difference, and a board that line-stopped is indistinguishable from a module with nothing to
report. Both are the denominator failing quietly, one through the ref and one through the
population.

--rows/--expect-sha refuse unless every board's own probe row declares the ref the caller
declared, and unless the modules present as rows are exactly the modules present as logs. The two
flags are one assertion and are refused apart, because half of it asserts nothing. Both refusals
were executed against a planted wrong-SHA row and against a half-given flag pair.

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

* The frontier boarded fifteen ways: the shared floor is real, and every summed root size is 7x too big

Fifteen of the sixteen roster modules boarded in one cohort at 6a59c1a (origin/main
1ed0205 plus two probe files the compiler does not read), and joined. Until now every root the
repair queue is ranked on was traced from one entry.

WHAT THE JOIN ANSWERS. Module closures overlap almost completely, so a per-module board cannot
tell "one defect seen from fifteen doors" from "fifteen defects", and those have opposite
staffing consequences. Deduplicating manifestations by site identity across the cohort separates
them: 1709 manifestations resolve to 298 distinct sites, and on the eleven shim-free boards
92.3% of distinct sites appear on two or more modules. That confirms the standing H1 hypothesis
in the root-partition plan by the exact falsifier it named -- real error texts, intersection
measured rather than inferred from histogram similarity -- so the plan's H1 row is updated in
place rather than left as an open question with the answer sitting in a probe.

The reassuring half does not extend to magnitudes. Summing per-module figures overstates by 7.0x,
and diagnostics-per-site varies five-fold between roots, so the inflation is not uniform and
cannot be divided out. PRIMITIVE_REPR_FORK is four distinct sites carrying 104 manifestations;
ABSENT_CLONE_BOUND is 22 sites carrying 129. Ranked by diagnostics they look comparable; ranked
by what a repair edits they differ by a factor of five.

TWO CROSS-CODE ROOTS ARE ESTABLISHED, both invisible to a per-code partition: PRIMITIVE_REPR_FORK
spans E0308 and E0369 (the same emitted repr decision reported as a mismatch where a value
crosses and as an unsupported operator where one is applied), MAP_CARRIER_FORK spans E0308 and
E0560 (the E0560 half reads as an unrelated "struct has no field" family that no E0308 repair
would close). 217 of 298 sites stay UNCLASSIFIED rather than being filled in from their codes.

WHAT IS QUARANTINED RATHER THAN COUNTED. The four shim-bearing boards install a lane lib.rs that
declares only its own lane's modules, so the rest of the closure refuses as unresolved crate-root
paths: all 60 E0432 and all 16 E0608 manifestations sit on those four boards. That population is
labelled SHIM_CLOSURE_GAP_instrument -- a property of how the board was taken, not a root -- and
those boards' remaining unique sites are explicitly not claimed as module properties until they
are re-boarded shim-free.

THE SIXTEENTH MODULE IS ABSENT, NOT ZERO. program_assembly refuses before emitting with 26 hard
name-resolution diagnostics in its own source. The absence is declared into the join, which
refuses unless every board that produced a row also produced a log and every declared absence
carries a reason; a cohort that quietly boards 15 of 16 and reports a weighting over "the
frontier" is the empty-observation narrow.

The fifteen cargo logs ship with their sha256s so this population can be re-partitioned without
a rebuild; re-running the committed join over the committed logs reproduces the summary and the
root table byte-identically.

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

* Classify the cohort runner honestly: a new scaffold awaiting an operator verdict, not free debt

review 55258 is right and the correction is worth more than the file. The runner's header claimed
that composing an existing scaffold "adds no new debt beyond the one it composes". That is
self-authorized dissolution, which DESIGN.md names as a failure mode by that name, and it was
wrong on its own terms: a second removable unit is a second obligation that whoever deletes the
first must also find, and a trigger the author writes is a lifecycle fact rather than permission
to create the debt.

So the header now says what it is -- a hand-authored shell orchestration scaffold, DESIGN §6's
out-of-band-actuation tell -- names the concrete dissolution trigger (#5828 bash-emit or a modeled
cssl_probe transport, at which point the file is deleted rather than ported), and states that it
lands only on an operator verdict on this exact exception. That verdict is requested, not
asserted, and it is external to this diff by construction.

Deletion is a live option and costs nothing that has already been measured: the fifteen logs,
their sha256s, the probe rows and the join are committed data, and curated_cargo_probe_one.sh
takes a board without this file. What deletion costs is the mechanised roster read -- the only
non-diligence defence against the enumeration that produced three wrong populations in this fleet
in one evening. The board README now says so where it tells a reader to run it.

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

* State the sizing rule where the next person sizing emitter work will stand

Requested by smart-ram-730 and relayed by the lane manager, with the placement argued rather than
assumed: the 7.0x inflation and the five-fold sites-per-manifestation variance are PLANNING
errors -- they corrupt which root you pick and how you rank roots against each other -- so they
belong in the board a future sizer opens first, not re-quoted at nine repair lanes already past
planning on one traced root each, whose two-arm measurements are unit-agnostic. The one exception
is a lane whose acceptance test is an exact count, and the section says so rather than
contradicting it silently.

The rule ships with its own falsifying example beside it, because a rule alone is easy to misread:
PRIMITIVE_REPR_FORK at 4 sites / 104 manifestations against ABSENT_CLONE_BOUND at 22 / 129 --
comparable ranked by diagnostics, five-fold apart ranked by what a repair edits, and the ordering
reverses between the two units. That is the case that shows why the inflation cannot be divided
out after the fact.

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

* Delete the shell runner and put its roster read in the join, where it cannot be skipped

The runner needed an operator verdict and no one in this tree can supply one -- DESIGN puts
scaffold approval external to the diff precisely because an author who writes a scaffold can
equally write a row claiming it was approved, and a sibling session ruling on it is that same
defect one level out. Rather than hold a finished board behind a decision it does not need, the
runner is deleted: it was a composed convenience whose absence costs nothing measured, and DESIGN
prices a throwaway at authoring plus review plus maintenance plus deletion plus review of the
deletion against a benefit bounded by its lifetime.

What the runner actually carried was not convenience: it was the mechanised enumeration of the
cohort from the roster authority, which is the defence against the failure this lane exists to
avoid -- a glob, a field-name grep and a hand list each produced a population close enough to pass
a glance and wrong in the denominator, and a wrong denominator does its damage inside a comparison
where it is afterwards indistinguishable from a real per-module difference.

So that check moved into cross_module_mechanism_weighting.py as --roster, which reads the roster
and refuses unless the cohort is exactly the membership it declares. This is a better home than
the runner had: a runner is consulted only by whoever chooses to run it, while every published
weighting passes through the join. Executed both ways -- the cohort passes with all sixteen rows
accounted for, and dropping one module's board and row is REFUSED by name.

The board is unchanged. Its README now documents boarding a module with the in-tree
curated_cargo_probe_one.sh and passing that row's own shim_lib_rel, and says where the roster
check went and why.

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

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 27, 2026
… to a typed carrier

DESIGN 4c: an ordinary String declaration whose sole purpose is commentary is
misplaced data. The two prose notes this PR added move to standalone leading //
annotation blocks on their module-scope service declarations.

DESIGN 3/6: the expanded hand-authored shell now carries a typed
DissolutionCondition naming bash-emit (#5828 / shell-to-intent Phase 2) rather
than prose, matching gunbc.ci_spec's existing rows. Per the
gunbc.githooks_pre_push_emit precedent this edits the failure arm of an EXISTING
transport and adds no new emission site.

The split is what 4c asks for: rationale to the annotation channel, machine-
consumed facts to a typed carrier.
briansrls added a commit that referenced this pull request Aug 28, 2026
…dered as success with empty output (#9518)

* A pipeline reports its LAST stage's status, so a failed entropy draw rendered as success with empty output

Three shell transports ended in a pipe, so the exit status came from the stage
that runs last rather than the stage that knows. Their `nonzero` arms could
never fire for a producer failure.

  extdeps.entropy  Urandom.ReadBytes         head -c N /dev/urandom | base64 -w 0
  extdeps.shell    Find.FilesAndSymlinksWithMode   find ... -printf ... | sort
  extdeps.shell    Find.FilesByNameSorted          find ... | sort

The entropy one is the severe case and is not a listing problem: base64
succeeds on empty input, so a failing `head` is reported as EXIT 0 WITH EMPTY
OUTPUT -- a successful entropy draw that drew nothing, on the one operation
whose fabricated output is indistinguishable from the real one by inspection.
The find pair produced a confident empty listing for a failed traversal, which
both callers digest into a tree identity or manifest.

MEASURED, not inferred from shape:
  sh -c 'find /nonexistent -type f | sort'            -> exit 0
  sh -c 'head -c 16 /nonexistent | base64 -w 0'       -> exit 0
  printf '' | base64 -w 0                             -> exit 0

`set -o pipefail` is NOT the remedy: /bin/sh is dash on the runners this
executes on and aborts on it rather than accepting it (measured). The status is
captured from the producing stage, following the in-tree exemplar
extdeps.rust.rustc (`...; rc=$?; rm -rf "$d"; exit $rc`). The entropy bytes
travel through a file rather than a shell variable because command substitution
strips NULs and would corrupt the draw.

Behaviour: output byte-identical (A/B over a 661-line listing); the only change
is the failing arm, from 0 to the producer's status. Each script executed in
both directions -- happy path exit 0 with correct output, failing producer
nonzero.

NOT REPAIRED, needs its own owner: Urandom.ReadPassword builds components with
unchecked `$(tr ... | head -c1)` substitutions, so a failing component silently
yields a SHORTER password rather than a refusal. That is a different and more
serious defect than the pipe status and a redesign of the generator rather than
an argv repair; recorded in the module note.

Found by crisp-cat-384 (from their own permanently-green listing arm) and
routed via deep-ant-102; neither could take it without widening their ruling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Escape the literal brace in the status note: {count} in prose is interpolation syntax

The note explaining the pipeline-exit-status defect quoted the old argv as
`head -c {count} /dev/urandom | base64 -w 0`. Inside a String, {count} IS
interpolation, so the compiler resolved it as a variable and refused with
'undefined variable count' at entropy.dag:43:160 -- failing both the parse
phase and the floor.

The language already carries the \{ escape for exactly this (locked by
test.claim.string_brace_escape_witness_test), so the fix is the escape, not
a respelling of the quoted argv. The two remaining {count} occurrences are
in transport argv where interpolation is intended.

* Address review 56984: rationale to the annotation channel, shell debt to a typed carrier

DESIGN 4c: an ordinary String declaration whose sole purpose is commentary is
misplaced data. The two prose notes this PR added move to standalone leading //
annotation blocks on their module-scope service declarations.

DESIGN 3/6: the expanded hand-authored shell now carries a typed
DissolutionCondition naming bash-emit (#5828 / shell-to-intent Phase 2) rather
than prose, matching gunbc.ci_spec's existing rows. Per the
gunbc.githooks_pre_push_emit precedent this edits the failure arm of an EXISTING
transport and adds no new emission site.

The split is what 4c asks for: rationale to the annotation channel, machine-
consumed facts to a typed carrier.

* Address review 57015: the shell dissolution trigger cited a fabricated symbol

The trigger named shell.Find.ListDirs and shell.Find.ListFilesByGlob. Neither is
right, and they are wrong in two different ways:

  ListFilesByGlob DOES NOT EXIST. No operation of that name is declared anywhere
  in the service. It was fabricated.

  ListDirs exists but is an unchanged DIRECT-ARGV operation -- argv is a plain
  ["find", "{path}", ...] vector with no shell, no pipeline, and no expanded
  string program. It is not scaffold this PR introduced and carries no debt.

The operations that actually carry the medium-as-string shell are
FilesAndSymlinksWithMode and FilesByNameSorted -- the only two whose argv is an
`sh -c` program capturing the producing stage's status through a temporary file,
which is the exact construction the trigger exists to dissolve. Both are named now
and both resolve.

WHY THIS IS THE SEVERE CLASS RATHER THAN A TYPO. A dissolution trigger is the
receipt that admitted debt has a declared end. Pointed at a fabricated symbol it
retires against nothing, and pointed at an unchanged operation it would have
retired when a wall was built for scaffold that operation never had -- so the
scaffold this PR does introduce would have kept its receipt while losing its
subject. The debt reads as covered in exactly the state where it is not, which is
worse than an uncited scaffold, because an uncited one still ranks for work.

It is also DESIGN section 3's cite-the-symbol rule violated in the direction that
rule is hardest to catch: a name that resolves nowhere is decidable by lookup, and
I did not perform the lookup before authoring it. Both files' triggers are now
grep-resolved against their own declarations -- entropy.dag's Urandom.ReadBytes /
ReadPassword were checked by the same pass and were already correct, so this is a
one-file defect and not a habit across the PR.

---------

Co-authored-by: Brian Searls <briansrls@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Brian Searls <briansearls1@gmail.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 7, 2026
…render

review 61958, both findings verified and correct.

THE BACKSTOP NO LONGER DERIVED THE JOB IT GUARDS. It was a hand-kept sum of
per-step budgets, and this branch inserted a fourth step into the build job with
its own 5-minute budget without adding a term -- so the job backstop could fire
while every step was still inside its declared budget, and it would have read as
a hung build rather than as a miscount. Rather than add the missing term, the sum
now FOLDS the step list: fleet_converge_build_job_steps() is bound once and
consumed by both the job's `steps:` and the backstop, so adding a step and adding
its budget are the same act and the two cannot disagree. Construction where a
check would have been the alternative; emitted backstop moves 65 -> 70.

THE HAND-SHELL GATE CARRIED NO SCAFFOLD MARKER. The dispatch-admission step is a
concat-built program handed to a foreign executor's `run:`, which is exactly the
shape gunbc.ci_spec ci_release_bins_pack_shell_emit_dissolution_trigger names,
and DESIGN section 6 is explicit that a MISSING dissolution condition makes the
finding more severe rather than less. Added
fleet_converge_reset_observer_admission_shell_emit_dissolution_trigger, following
the sibling's form including emitting the description into the script as a
comment, and naming the same trigger (bash-emit, #5828 / shell->intent Phase 2).

AND A COST FINDING OF MY OWN, from CI on ae71183 rather than the review: BOTH
witnesses I added in the previous commit were interrupted before verdict at
507ms and 509ms against the 500ms line. The measurement says rendering this
workflow -- or constructing its five jobs -- is roughly the entire claim budget
by itself, so splitting the org-admin-secret claim into its own identity was
right on SUBJECT and wrong on COST: it rendered the workflow a second time for
one extra string test. Both repaired at the mechanism rather than by budget:

  - the build-job witness folds fleet_converge_build_job_steps(), the same single
    authority the job consumes, instead of indexing fleet_converge_workflow --
    one job's steps instead of five jobs, same question answered.
  - the org-admin-secret claim moved into the witness that ALREADY renders the
    workflow, which now asserts both absences over one render. Two demands, one
    frame as their least common ancestor, so the first value is carried.

Net renders in the file are unchanged at two; my additions now cost no render at
all. Both PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe
briansrls pushed a commit that referenced this pull request Sep 8, 2026
…10739)

* A reset receipt that cannot say a host returned when nobody looked

The Mt. Collins canary receipt states its own limit: "one job on one boot --
nothing here shows a second attempt starting cleanly after a first, which is
what a runner host actually has to do". This is the carrier for that missing
property, and its whole design turns on one distinction: "the controller
accepted the reset" and "the host came back" are two facts, not one.

So there is no duration field outside the arm that observed both ends. The
down and back instants exist only inside HostObservedDownThenBack; every other
arm yields std.observation MeasuredUnavailable carrying its cause. A zero
downtime is not guarded against, it is unwritable -- which matters because zero
sorts to the top of any ranking of outage times.

THE OBSERVER CANNOT BE THE SUBJECT, BY CONSTRUCTION RATHER THAN BY CHECK. A job
running on the host it resets dies at the reset and cannot observe the return;
what it wrote before dying is exactly the empty value that then reads as a
positive answer. Every host-scoped converge mode today is an if-gated step in a
job pinned to the selected host, so adding this mode the ordinary way IS the
failure. The dispatch input is therefore inverted: it carries the OBSERVER
label, runs-on consumes it directly, and the subject is not an input at all --
the entry learns it from the machine it is running on. There is no form field
in which the invalid pairing can be stated. sole_constructor is enforced across
module boundaries (measured: a witness spelling a pairing literal was refused),
so the attempt cannot be assembled around a pairing nobody minted either.

THE ROSTER IS AN IDENTITY JOIN, NOT A LIST. A host is a reset subject exactly
when oob_boot_handoff_admission -- the fold the actuation arm already runs
before it writes -- admits its observation against its capability row. The
evidence that authorizes the act produces the membership, so the roster cannot
drift from the capability it describes. It has one member today because exactly
one host has ever landed a BmcAccessObservation.

Four reds that were each a green reached for the wrong reason:

- an accepted command with the host never down (the receipt that carries only
  the acknowledgement renders this as success)
- a subject already down at issuance -- satisfies both watches in order and
  fabricates a complete reading of a reset that moved nothing
- a down observation predating the issuance instant, so a stale liveness fact
  cannot testify to this reset
- an observer that never converged its own hostname: it differs from the
  subject exactly as surely as a correct peer does, so the difference is real
  and establishes nothing

The last of those is why observer_self_identity takes three names. The order of
its two questions is load-bearing and has its own witness: a runner labelled as
a peer but living on the subject chassis fails both predicates at once, and
asking "have I converged?" first would report the substitution this check
exists to catch as an ordinary maintenance problem.

The second-attempt property is a relation over two receipts, computed. Two
attempts sharing an artifact path, or carrying identical digests, or hashed
under different families, all refuse -- appending to an earlier artifact changes
the bytes its digest names, invalidating a receipt while leaving it looking
valid. And the receipt splits from the observation because a run cannot honestly
digest its own evidence: the transcript is not finished until the run is.

Also: fleet_converge_enrolled_host_options is now derived from fleet_hosts()
rather than spelled a second time, and mtcollins1 is named in
fleet_intent_network and allocated a hostname -- naming is not enrollment, and
it joins no executor roster.

Not yet executed. The credential prelude is absent because authoring it means
naming a pinned secret version nobody has authorized; until then the job
dispatches, runs, and refuses on the missing path before any controller write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* Name a timeout row that exists, and size the back-watch to the step that runs it

The emission refused on an undefined `gunbc_ci_fleet_step_timeout_minutes`; the
row this job's step wants is the one already imported here.

Checking its value found the worse defect. The two watch bounds were round
numbers picked without noticing that a sample costs different amounts on each
side: waiting for the host to go DOWN the probe succeeds and returns at once,
so a sample is about the one-second sleep, while waiting for it to come BACK the
probe fails and carries ConnectTimeout=5, so a sample is about six seconds. Six
hundred back-samples is an hour inside a thirty-minute step -- a watch the
platform kills before the fold can return, and a killed job writes nothing,
which is the empty value that reads as a positive answer. The bound is now
derived from the step budget rather than chosen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* A declared refusal nothing could construct read as coverage

Review 61729, both findings correct and the second showed a comment of mine
asserting a refusal that did not happen.

NoPeerAvailable was declared, matched in the emission gate, and constructed
NOWHERE. The state it names is reachable: the selections fold mapped over the
peer roster, so a subject with an EMPTY peer roster contributed ZERO selections
-- and zero selections is not a refused selection, it is nothing at all. The
subject then stopped being offered with no diagnostic anywhere, which is exactly
the absorbing fallback the paragraph directly above the type forbids. A declared
cause nothing can emit is worse than an absent one, because the type reads as
coverage.

And the note on the option list claimed "an unmintable one yields an EMPTY list
that the emission gate below refuses on", which was false: an empty pairings
list is DispatchAssignment, not DispatchAssignmentRefused, so the gate passed
and would have serialized a reset_observer choice input with zero options -- a
mode nobody can select, which is a capability removed silently rather than a
diagnostic about one.

Fixed at the mechanism rather than the comment. An empty peer roster now
CONSTRUCTS NoPeerAvailable instead of falling through a map with nothing to
iterate, and an empty subject roster refuses on its own new cause -- nothing to
iterate is not the same as nothing refused, and both routes to an empty dispatch
form have to be loud. The gate additionally refuses an empty admitted list,
which is belt to that braces and says so.

Three witnesses: the no-peer route, the no-subject route, and the discriminating
red that no route yields an admitted-but-empty assignment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The new refusal cause made three exhaustive matches non-exhaustive

Which is the wall doing its job: adding NoResetSubjectRostered to the cause
coproduct reds every match that claimed to cover it, so no witness can silently
keep asserting a partition that no longer holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* One path row for the writer and the step that prints it

Review 61747. The invoke stamped receipt_rel "target/host-reset-return.txt" and
gunbc_invoke_receipt_steps turns that into a post-run cat of that exact path,
while the wet exit wrote target/host-reset-return-<attempt>.txt. So a SUCCESSFUL
reset would have exited 0 and then failed on a file nobody wrote: a green
inverted into a red by a second spelling of one path, and it would have fired
the first time credentials existed.

The attempt label is out of the filename, which is what made the fork unfixable
rather than merely wrong -- the attempt is derived at run time from the machine
the job landed on, so emit time cannot name the file. Two attempts are two
dispatches with two workspaces and two uploads, so the run already distinguishes
them; the label bought nothing on disk and cost the agreement. It moves into the
receipt line, where it was always doing its work. The never-append discipline is
untouched: it governs the COMMITTED artifacts a receipt names by digest.

Also the second finding, and it is the same class this branch was already caught
by once: a comment in the run module still said gunbc_fleet_hostname_allocations
carried no Mt. Collins row and that the first attempt would refuse there. This
branch adds that row. The claim was stale the moment it landed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The label and the machine were one reading, so the corroboration was decorative

Review 61761, and it is the sharpest finding on this branch. The entry keyed
itself on `hostname -s`, so the label that decided WHERE the job ran and the
name the machine ANSWERS TO were the same instrument. A runner labelled srv1 but
living on the subject chassis, or a host that never converged and reports
"ubuntu", then failed the assignment lookup as "not an assigned observer" and
NEVER REACHED the identity check at all -- so ObserverIsTheSubjectMachine and
ObserverHostnameNotConverged were reachable only from fixtures while this branch
claimed them as the wall protecting the hardware. Specification-without-execution
on the production route, and citing the wall while the acceptance path cannot
express its red is rung inflation (DESIGN §4b meta-obligation 1).

The dispatch observer label is now carried into the process by its own env row
and the assignment is keyed on it, leaving the machine's own hostname as purely
the corroborating second reading. Neither instrument can produce the other's
evidence, which is what two observers was supposed to mean and was not.

The entry deliberately refuses rather than falling back to its hostname when the
label is absent: the fallback IS the defect, so an arm that restores it under a
missing variable would re-open the hole in the failure path.

Also the ci_spec target note still said the entry takes the credential path as a
single String argument. It takes none. Third stale claim of this shape on this
branch; the rule they add up to is that a sentence naming a state is a claim to
be re-measured, not description.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* Prose in a carrier no fold reads is dead data, not a frontier

Review 61775. host_reset_return_executed_receipt_frontier was a NonEmptyStr
whose only content was the §3c frontier paragraph -- nothing imported it and no
fold read it. §4c: an ordinary String declaration whose sole purpose is
commentary is misplaced or dead data. §3c: a data row no fold reads is the
dangling state, and the same trigger was already stated in the annotation
directly above it, so the row was a second unconsumed authority for one fact.

Dropped, annotation kept. Deliberately NOT re-homed as a DissolutionCondition:
nothing folds frontiers today, so minting one here would move the same dangling
declaration into a better-looking type and satisfy the letter of the finding
while leaving its substance exactly where it was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The refusal receipt discarded the typed cause three claims said it carried

review 61786: `reset_return_verdict` computes a `ResetReturnRefusalCause`, and
the only production serialization path then dropped it — the receipt line said
`verdict=refused` and nothing else, while the annotation, the exit reason, and
the upload step that exists to make refusals readable all claimed the cause was
there. A reader of the artifact could not tell an observer-is-subject refusal
from an out-of-band issuance from a controller refusal, which are three
different remedies.

`reset_return_refusal_cause_wire` and `reset_observer_refusal_cause_wire` render
one arm per cause; the pairing arm carries its inner cause through rather than
flattening it. Both the receipt line and the exit reason now render it. Three
witnesses discriminate ARMS — that no two causes render the same string, and
that a pairing refusal's inner cause survives — rather than checking the string
is non-empty, which would have been green on a constant.

Also: five annotations sat inside `fleet_converge_host_reset_return_step`'s body,
which the corpus parse phase refuses at module-item grain (the resolve path my
local runs take does not reach that check). Hoisted above the declaration, and
the workflow projection regenerated from the fixed authority — the drift the
`heal-generated-artifacts` job reported at a715229.

46/46 witnesses PASS locally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* Two annotations claimed more than the code does

review 61814, both findings verified and both correct.

`gunbc_ci_host_reset_return_target`'s annotation said the credential "arrives as
a path by the same route" as the observer label. It does not. `git grep -n
GUNBC_HOST_RESET_BMC_CREDENTIAL_FILE` returns exactly one module -- the entry
that reads it -- and no step in `fleet_converge_host_reset_return_job`
materializes it, so the emitted mode can today only reach the first refusal arm.
That is the class this PR has now corrected three times: an annotation asserting
a route as landed, which DESIGN section 4c says is never evidence a machine
claim holds. The clause now states the frontier instead, names the Spark prelude
whose shape the materialization will take, and says why the step is not written
here: the secret resource name is an operator fact, and inventing a plausible
one would emit a step that fetches nothing and writes an empty file -- a worse
state than the honest refusal, because the file would then exist.

The second finding: the two-paragraph block describing the CROSS PRODUCT was a
verbatim duplicate sitting on `reset_observer_selections_for`, which is the
PER-SUBJECT selection. One paragraph, two homes, one of them the wrong function.
Removed the copy; the `NoPeerAvailable` rationale that genuinely belongs to that
fold stays.

While fixing the first item I wrote two citations to symbols that do not exist
and caught them by grep before committing, which is the same defect one layer
down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* A refused pairing did not stop the power cycle

review 61872, both findings verified, and the first is the most serious defect
this lane has had.

A REFUSED PAIRING FELL THROUGH TO THE CONTROLLER WRITE. `host_reset_return_attempt`
computed the selection, then gated only on the identity, so an
`ObserverSelectionRefused` -- `ObserverIsSubject`, `SubjectNotRostered`,
`ObserverNotRostered`, `NoPeerAvailable` -- reached `oob_boot_handoff` and
power-cycled the machine, after which the receipt rendered `pairing-not-minted`
ABOUT A RUN THAT HAD ALREADY WRITTEN TO A CONTROLLER. The selection is the arm
carrying `ObserverIsSubject`, so the wall this module advertises against
resetting your own host was sitting BEHIND the write it exists to prevent. It
was unreachable today only because `subject_for_observer` pre-filters -- safety
coming from the caller, not from the arm the module presents as the wall, and
the next caller does not inherit it. This is the same distinction the module
already states for the identity check and did not apply to the selection.

ONE AUTHORITY FOR "MAY THIS RUN WRITE". The second finding: the gate returned a
Bool over ObserverSelfIdentity while `reset_return_verdict` independently matched
the same coproduct to per-arm causes. One fact, two authorities, and the Bool was
the lossy one -- it knew an arm was fatal but not why, so the caller invented a
sentence the verdict already owned. `observer_identity_permits_issuance` is
dissolved into `observer_identity_issuance_block`, which returns the refusal
cause itself; the verdict now reads that classifier instead of re-deriving it,
and `reset_issuance_block` composes selection-then-identity for the gate.

Evidence: `every_selection_refusal_blocks_issuance_before_any_controller_write`
covers all four selection causes; `an_admitted_pairing_with_a_distinct_identity_is_the_only_unblocked_shape`
is the discriminator, since a gate that blocked everything would pass the first;
`the_issuance_block_and_the_verdict_name_the_same_identity_cause` holds the two
readings identical. Discriminating red measured: routing the refused-selection
arm back through the identity check alone turns the first witness FAIL, restoring
turns it PASS.

Also, three witnesses in workflow_dispatch_input_witness_test that main's floor
flagged and that ARE mine: the jobs list is an ordered exact partition and I
inserted host-reset-return third, and the mode vocabulary count moved to sixteen.
Pinned the new job with its `needs: [build]` edge -- it power-cycles its subject,
so a stale binary there would drive real hardware from a model nobody reviewed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* A witness answering two questions could not finish either

The floor on a5f2768 refused with one blocker of mine, and the log names the
mechanism exactly:

  INTERRUPTED-BEFORE-VERDICT
  test.claim.workflow_dispatch_input_witness.fleet_converge_org_actions_mode_binds_secret_and_modeled_entry_holds
  raised_by=cpu_deadline cpu_at_least=501ms/500ms wall_at_least=503ms/8000ms

That witness is enrolled cost debt and normally WITHHELD; because this branch
edits the file it executes for its verdict, and it was interrupted one
millisecond over the line, so it produced no verdict at all.

THE DEFECT IS TWO SUBJECTS IN ONE WITNESS, and the cost is the consequence. Seven
conjuncts ask about the org-actions INVOKE STRING; the eighth asked whether the
whole emitted workflow carries a repository secret, which made the witness emit
all 64KB of YAML to answer a question the other seven do not need. Adding a job
to the workflow grew that emission enough to cross the line. Split on the subject
boundary: the workflow-wide claim is now
`the_fleet_converge_workflow_carries_no_org_admin_repository_secret`. Widening
the 500ms line to fit a witness answering two questions would have been the wrong
direction -- the roster was asking for this split, not for more budget.

Separately, `fleet_converge_apply_step_binds_plan_hash_to_env_not_shell_literal`
emitted the entire workflow TWICE for two conjuncts about the same rendered text.
Bound once. That is not a cache: both demands share the witness frame as their
least common ancestor, so this is removing an authored duplicate.

All three PASS locally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The watch paced itself with a string on the line upstream had marked for retirement

review 61938, verified and correct. The watch slept via
`sleep.Delay.Seconds(seconds: "1" as NonEmptyStr)` -- a flat scalar naming a
domain unit where std.measure already carries one. What makes it more than a
style point is that extdeps.tools.sleep does not merely OFFER the carrier: it
declares the flat-scalar form's own dissolution binding,
`sleep_delay_seconds_flat_scalar_dissolution_trigger: Disposition = Scaffold {
bind: ... sleep_delay_seconds_second_carrier_projection }`. So the call I wrote
was minting a fresh spelling of a concept that already has an authority, on the
exact construction the upstream module had marked for retirement. DESIGN section
2: net concepts must not grow by re-invention; section 3: each fact lives in one
place. gunbc.live_deploy.readiness is the consuming precedent, and I agree with
the review that the one sibling doing the same thing is evidence of debt rather
than precedent -- the projection landed after it.

Paced through the carrier, with the duration modelled as
`host_reset_watch_sample_cadence: Second = second(count: 1)` beside the watch
bounds rather than inline. That placement is the second half of the repair: the
sample counts are DERIVED from the emitted step budget by multiplying by this
cadence, and while the cadence was a string at the call site that arithmetic was
carried in prose with nothing tying the two together.

Witnesses re-run across the change: PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* An unnamed observer queued a runner that does not exist

review 61950, verified and correct. reset_observer is required:false because it
is meaningful for exactly one mode, and its value is interpolated into the reset
job's runs-on. A dispatch of mode=host_reset_return omitting it -- which an API
or `gh workflow run` dispatch does by supplying "" -- passes the job's if: gate
and asks for labels ["self-hosted","linux","arm64",""]. No runner carries an
empty label, so the job QUEUES until the 105-minute backstop and dies with no
diagnostic, no receipt and no upload; the upload step never runs, so not even its
if-no-files-found: error fires. A silent stall standing exactly where the rest of
this mode puts a loud refusal, and the one pairing state reachable at dispatch
time was the one that neither refused nor ran.

THE REFUSAL CANNOT LIVE IN THE RESET JOB, whose runs-on is resolved before any of
its steps exist -- a step there is unreachable in precisely the state it would
diagnose. It lives in `build`: generic labels, always schedulable, and needed by
every host-scoped job, so the line stops before anything is scheduled rather than
after something has queued.

NOT A DEFAULT, which was the review's other arm. Defaulting the observer makes an
omitted field silently select a machine and POWER-CYCLE ITS PAIRED SUBJECT. A
default is right for a preference and wrong for an effect nobody asked for: the
dispatcher who omits the field has not chosen a subject, and inventing one is the
fabricated-plausible-output arm of section 5, not a convenience. Refusing costs a
re-dispatch; defaulting costs a reboot.

Evidence: `an_unnamed_reset_observer_refuses_in_the_always_schedulable_job` checks
BOTH conjuncts of the guard, because one firing on the mode alone would refuse
every correct reset dispatch and one testing emptiness alone would refuse every
other mode -- either would satisfy a witness that only looked for the error
string. `the_build_job_carries_the_reset_observer_dispatch_admission` holds that
the step is actually in the job, since a step function nothing calls is the
dangling-declaration shape and would pass its own witness while never running.
Discriminating red measured: dropping the mode conjunct turns the first FAIL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The backstop derives the job it guards; and my split doubled a 500ms render

review 61958, both findings verified and correct.

THE BACKSTOP NO LONGER DERIVED THE JOB IT GUARDS. It was a hand-kept sum of
per-step budgets, and this branch inserted a fourth step into the build job with
its own 5-minute budget without adding a term -- so the job backstop could fire
while every step was still inside its declared budget, and it would have read as
a hung build rather than as a miscount. Rather than add the missing term, the sum
now FOLDS the step list: fleet_converge_build_job_steps() is bound once and
consumed by both the job's `steps:` and the backstop, so adding a step and adding
its budget are the same act and the two cannot disagree. Construction where a
check would have been the alternative; emitted backstop moves 65 -> 70.

THE HAND-SHELL GATE CARRIED NO SCAFFOLD MARKER. The dispatch-admission step is a
concat-built program handed to a foreign executor's `run:`, which is exactly the
shape gunbc.ci_spec ci_release_bins_pack_shell_emit_dissolution_trigger names,
and DESIGN section 6 is explicit that a MISSING dissolution condition makes the
finding more severe rather than less. Added
fleet_converge_reset_observer_admission_shell_emit_dissolution_trigger, following
the sibling's form including emitting the description into the script as a
comment, and naming the same trigger (bash-emit, #5828 / shell->intent Phase 2).

AND A COST FINDING OF MY OWN, from CI on ae71183 rather than the review: BOTH
witnesses I added in the previous commit were interrupted before verdict at
507ms and 509ms against the 500ms line. The measurement says rendering this
workflow -- or constructing its five jobs -- is roughly the entire claim budget
by itself, so splitting the org-admin-secret claim into its own identity was
right on SUBJECT and wrong on COST: it rendered the workflow a second time for
one extra string test. Both repaired at the mechanism rather than by budget:

  - the build-job witness folds fleet_converge_build_job_steps(), the same single
    authority the job consumes, instead of indexing fleet_converge_workflow --
    one job's steps instead of five jobs, same question answered.
  - the org-admin-secret claim moved into the witness that ALREADY renders the
    workflow, which now asserts both absences over one render. Two demands, one
    frame as their least common ancestor, so the first value is carried.

Net renders in the file are unchanged at two; my additions now cost no render at
all. Both PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The annotation still described the design review 61761 removed

review 61989, verified and correct. The fleet-converge annotation and the PR body
both said gunbc.host_reset_return_run "learns [the subject] from the machine it
is running on." That is the collapsed form review 61761 already removed from the
CODE, left standing in the prose beside it: the entry reads the CARRIED observer
label out of the environment and refuses rather than falling back to its own
hostname.

WHY THIS IS NOT A WORDING SLIP. Keying the entry on `hostname -s` made the label
that decided WHERE the job ran and the name the machine ANSWERS TO one reading,
so a runner labelled srv1 but living on the subject chassis, or a host reporting
"ubuntu" because it never converged, failed the assignment lookup as "not an
assigned observer" and never reached the identity check at all -- leaving
ObserverIsTheSubjectMachine and ObserverHostnameNotConverged reachable only from
fixtures while the module cited them as the wall protecting the hardware. One
fact with two incompatible spellings is DESIGN section 3's meaning fork, and here
the stale spelling is the one a later worker would most plausibly act on, because
it describes a simpler design. Acting on it would re-derive the subject from the
machine and disarm the label-versus-hostname corroboration the hardware depends
on.

Corrected in the annotation and in the PR body, both pointing at the argument in
gunbc.host_reset_return above reset_return_verdict_of_mint rather than restating
it a third time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* A witness rename is a silent de-enrollment from the cost-debt roster

CI on a2c6d6b, not a review. My previous commit merged two workflow-wide absence
claims into one witness to stop rendering the workflow twice -- right on cost --
and RENAMED the witness while doing it, which was wrong on identity and produced
both halves of the failure:

  INTERRUPTED-BEFORE-VERDICT ...the_rendered_workflow_carries_neither_a_pasted_heal_sha_nor_an_org_admin_secret
    raised_by=cpu_deadline cpu_at_least=523ms/500ms
  BLOCKING ...fleet_converge_workflow_has_no_heal_revalidation_paste_through is enrolled in
    v2.workflow.floor_cost_debt but the tree DECLARES NO SUCH IDENTITY

WHAT I HAD WRONG ABOUT THE 500ms LINE, which is worth stating because I acted on
the wrong model twice. It is not a budget these witnesses can meet: the floor
reports marginal_cpu_ms of 1264, 1398 and 1399 for the siblings in this same file
that construct or render the workflow, and they pass. It is the threshold above
which a claim must be ENROLLED as declared cost debt, after which the line is
observed and published rather than gating. So the fix was never to shave
milliseconds -- the org-actions witness at 501ms and this one at 523ms were both
telling me they had left an enrolled identity, not that they were slightly too
slow.

The enrollment is keyed by fully qualified witness name, so a rename silently
de-enrolls. Name restored; the merged conjunct rides along on the enrolled
identity, and the annotation now says the name is load-bearing so the next reader
does not tidy it.

The two identities this branch genuinely adds stay unenrolled on purpose: both
were built to touch one step and one step list rather than the workflow, and the
a2c6d6b run confirms they complete inside the line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The run_id field carried a sentence instead of the run

review 61998, verified and correct, and it is mine twice over: I wrote that
string, and I wrote the annotation defending it.

The only production ResetIssuanceRoute carried

  run_id: "unbound: the run identifier is not read by this entry; the uploading run is the binding"

so every real receipt this mode would ever mint carried prose in a field whose
type says it names the run that issued a power cycle. That is section 4c's
misplaced data in the one field a reader needs to get from the artifact back to
the run that drove the machine.

THE ANNOTATION ARGUED AGAINST A CHOICE NOBODY OFFERED. It defended the sentence
on the grounds that an interpolation-shaped literal would render as a real-looking
identifier naming no run -- true, and irrelevant, because the modeled reading
already exists: extdeps.github.actions_environment github_run_id_variable_name
models the variable, gunbc.actions_run_binding actions_variable_read performs the
read with an absent-versus-set-but-empty distinction, and this very step already
proves the env route works because it hands the observer label over the same way.
I had hit the interpolation parse error early, concluded the reading was
unavailable, and never revisited it once the env route was built.

AN UNIDENTIFIED RUN NOW REFUSES BEFORE ISSUANCE. run_id is mandatory and there is
no honest value for it, so the read is its own outcome: ResetIssuanceRouteRead is
RouteEstablished or RouteUnidentified, and the entry exits on the second before
any controller write. An optional field was the other arm and is worse: it would
let the power cycle proceed and mint a receipt that cannot support the one claim
this carrier exists to make -- that the reset was unattended and went through
converge. A receipt no reader can trace to its run does not establish that, so the
gap has to stop the line rather than be recorded after it. Same ordering as the
observer-identity and pairing walls, for the same reason.

No witness added for the unidentified arm: with run_id mandatory and the read
returning a coproduct, there is no way to author a RouteUnidentified that yields a
route, so a check would be permanently green by construction -- section 4b's
decoration, worse than absent because it would be cited as coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The refusal the upload step exists to carry was never written

review 62016, verified and correct. Five arms refuse before any pairing is
minted -- no credential path, no observer label, an unidentified run, an observer
the assignment does not place, a subject with no roster row -- and every one of
them exited without touching host_reset_return_receipt_path. The only write was
inside host_reset_return_exit, which those arms never reach.

Meanwhile the upload step runs `if: always()` with `if-no-files-found: error`,
and its own annotation states the property: "THE RECEIPT IS UPLOADED ON FAILURE
TOO ... the refused case is the one somebody needs to read, and an upload
conditioned on success discards the evidence exactly then." So on every refusal
this mode can actually reach today, the artifact did not exist and the run's
final red named the MISSING FILE rather than the missing credential -- the
located diagnostic displaced by an artifact-plumbing error. A claim in the
carrier the code did not hold, which is the shape this branch has now caught in
itself four times.

THE PREFLIGHT CAUSES ARE THEIR OWN VOCABULARY, not new ResetReturnRefusalCause
arms. That type answers for an attempt that was MINTED: each of its causes
presupposes a pairing, a route and an observation. A preflight refusal has none
of those, and widening the verdict type with causes reset_return_verdict can
never produce would leave those match arms unreachable while reading as coverage
-- the same defect review 61829 found in this module, deliberately not repeated.

attempt=none-minted is the honest field rather than a placeholder: there is no
attempt label and no subject to name at that point, and writing "unknown" would
put a row in the receipt claiming a subject a reader could sort on.

Evidence: w_preflight_refusals_do_not_collapse_into_one_another holds all five
pairwise distinct, since an operator has to tell an unnamed observer from an
unidentified run and they are different remedies at different layers;
w_a_preflight_refusal_names_the_variable_or_host_it_is_about holds that each
carries the variable or host to act on, which a class-only wire would not.
Discriminating red measured: collapsing one arm to a constant turns the second
witness FAIL, restoring turns it PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The job that reboots a machine belonged to no mutation domain

review 62030, verified and correct, and it is the most consequential finding on
this branch after the refused-pairing one.

fleet_converge_host_reset_return_job() landed with concurrency: none while being
the most mutating job in the workflow -- it power-cycles a physical host. Every
other host-touching job carries fleet_host_mutation_concurrency_group_expression,
whose own annotation states the rule and gives this mode's exact subject as the
reason: ONE MUTATION DOMAIN PER HOST, SHARED BY EVERY JOB THAT CONVERGES OR READS
BACK THAT HOST (RLM-2 ruling section 9), because a readback can otherwise observe
another transaction's mutation. A section 3b divergence in the leasing/locking
domain with nothing anywhere stating a reason, which is the only red that section
admits.

THE FABRICATION IT ADMITTED IS THE ONE THIS MODULE EXISTS TO REFUSE. Two
dispatches naming DIFFERENT observers resolve to the SAME subject; B's power
cycle lands inside A's watch window and A mints HostObservedDownThenBack for a
return it did not cause. The pre-issuance baseline cannot catch it, because B
issues while the subject is still up during A's handoff -- so it arrives by the
one route SubjectNotUpBeforeIssuance is blind to, and the receipt carries a
measured downtime for someone else's reboot. Every wall in this module aims at
that class and the workflow left the door open beside them.

THE GROUP KEYS ON THE SUBJECT, WHICH THE DISPATCH DOES NOT CARRY. The
observer-to-subject map is emitted from the same assignment the option list comes
from and indexed by the dispatch input, so it is derived rather than spelled: a
pairing the mint refused cannot appear in it, and an unmapped observer yields an
empty subject that cannot collide with a real host's domain. Emitted group
resolves to gunbc-host-mutation-mtcollins1 -- the same domain a mode=apply
against that host takes.

WHAT IS NOT COVERED, AND SAYING SO IS THE POINT. GitHub admits one group per job
and this job needs two domains: the subject's, and the observer's, since an apply
naming the observer can reboot the watcher mid-watch. The subject's is taken
because a fabricated down-then-back is a WRONG receipt while a rebooted observer
is a LOST one -- that failure is loud, this one would not have been. The residual
is not closable here; its trigger is already named in fleet_workflow_steps, which
records that the host lease exists, is witnessed, and is not yet acquired by this
workflow. A lease holds two hosts where a group holds one.

Evidence: the_reset_job_shares_the_mutation_domain_of_its_subject, whose last
conjunct is the discriminating one -- a group keyed on the OBSERVER would satisfy
"has a group" while admitting exactly the collision, since the observers differ
and the subject does not. Discriminating red measured: replacing the derived
expression with the observer-keyed one turns it FAIL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* Two failure-mode rows, both filed as records rather than repairs

The classes this lane produced, filed separately because they have different
recognition rules and folding them would give one row two.

declared_refusal_unreachable_from_its_own_constructor: a refusal arm whose only
constructor implies the state that makes the arm unreachable. Three instances in
gunbc#10739 alone -- NoPeerAvailable never constructed because the fold mapped
over an empty roster and contributed zero selections rather than one refusal; the
observer-identity arms fixture-only because the entry keyed on `hostname -s` and
failed the assignment lookup first; and SubjectWasNotUpBeforeIssuance, the purest
form, where the precondition and the shadowing state are the SAME FACT, so care
at the construction site cannot help. Ceiling is mechanically preventable: arm
reachability from a named entry is decidable over the Node tree the namespace
authority already reads.

enforcement_gate_positioned_after_the_effect_it_guards: a refusal computed before
the effect and enforced after it. Two instances -- the identity check that
power-cycled the machine the job was standing on and then reported the refusal,
and, after that repair, a refused PAIRING still falling through to the controller
write. The second is the instructive one: its arm was unreachable only because a
CALLER pre-filtered, so the module presented as its wall an arm whose safety came
from somewhere else. Ceiling is structurally guaranteed -- the effect can require
the guard's permitting value as an argument, so an unpermitted call has no form.

BOTH ROWS STATE THAT NOTHING IS NOW HARDER TO DO THAN BEFORE. They are records,
not repairs; both classes stand UNREMEDIED at mitigatable and may not be cited as
coverage. That is a_written_row_is_not_a_firing_mechanism's obligation, and it
applies to these two as much as to any others -- discharging it by saying so
plainly rather than by implying a climb. Each carries a next-rung trigger naming
a CAPABILITY (an arm-reachability reader; an effect-ordering reader) with what it
must be sufficient for, not an artifact that would contribute to one.

The interim practice that did fire, offered as practice and not as mechanism:
mutate the guard and re-run the witness. A fixture-shaped witness stays green
under `if false`; every repair in that lane was confirmed by a measured red there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* chore: regenerate drifted generated artifacts (ci auto-heal)

Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md declared_refusal_unreachable_from_its_own_constructor
Ledger-Rows-Repaired: docs/design-failure-modes.md enforcement_gate_positioned_after_the_effect_it_guards
Ledger-Repair-Judged: docs/design-rung-drops.md

* The concurrency-group map was hand-built JSON, one function from the gate that class exists to close

review 62097, verified and correct. fleet_converge_reset_subject_map_json built
its object from string literals and fed it to fromJSON, while
extdeps.languages.json.emit already carries json_object, json_kv, json_string and
serialize_json -- and this module already consumes the YAML sibling, so reaching
for the JSON one is the same move rather than a new dependency. Section 2: net
concepts must not grow by re-invention.

THE FAILURE IT REMOVES IS THE ONE THIS WORKFLOW ALREADY LEARNED. Nothing in
NonEmptyStr or HostIdentity forbids a label carrying a quote or a backslash;
escaping was unmodeled, so such a label emitted malformed JSON, fromJSON failed at
JOB-SETUP time before any step existed, and the reset job died with no receipt and
no upload. That is the silent-stall class the dispatch-admission gate was added to
close in this same file, reintroduced one function away from it by the fix for the
concurrency finding. serialize_json escapes through escape_json_string, so the
emitter owns that fact and nothing here restates it.

THE FOLD NOW TAKES ITS PAIRINGS AS AN ARGUMENT, and that is the load-bearing half
rather than a tidy-up. Reading the assignment data row directly, the only witness
available would have restated what the emitter does and been green by
construction -- section 4b's decoration, worse than absent because it gets cited
as coverage. Split so a hostile label can be handed in:
a_reset_subject_map_escapes_a_label_that_would_break_the_json mints a pairing
whose observer carries a quote and holds that the rendering escapes it and does
not contain the raw broken form. Discriminating red measured: restoring the
concat body turns that witness FAIL.

The emitted workflow is byte-identical, which is the expected result -- today's
labels need no escaping, so this is behaviour-preserving for current data and
differs only where the old form was wrong.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* Rebuild the dispatch gate modeled; the scaffold bought nothing

The onboarding re-read against docs/plans/scaffold-admission-doctrine.md found a
defect in my own head, and it is one that survives being green and approved.

fleet_converge_reset_observer_admission_shell_emit_dissolution_trigger was a
NET-NEW ACTIVE dissolution obligation with no operator verdict on that population,
which the doctrine calls a hard reject: marked-but-unapproved debt is refused.
The api-review APPROVE does not cover it -- approval is external to the diff
precisely because an author who can write a scaffold can equally write a row
claiming it was approved -- and review 62127 reasoned that the marker "meets the
wall as stated", which answers the LIFECYCLE question (how would this disappear)
and not the ADMISSION one (may it exist at all). The doctrine separates those
deliberately and I had answered only the first.

THE DOCTRINE DECIDED IT WITHOUT A JUDGEMENT CALL: the default landing state is
the final construction, a scaffold is an approved EXCEPTION reached for when the
construction is unavailable, and here it was available. So the scaffold's whole
benefit was saving ONE BUILD on a dispatch that was already wrong, against
authoring, review, maintenance, deletion and review of the deletion.

WHAT REPLACED IT. gunbc.host_reset_return admit_reset_dispatch is a three-arm
fold over the two dispatch inputs, and host_reset_return_dispatch_admission_wet
is the entry the build job invokes through the ordinary gunbc run step, taking
both values by environment. The gate moved AFTER the release build because it
needs the binary; the safety property is unchanged, since the reset job needs
`build` either way and is never scheduled in the state this refuses.

IT REFUSES WITH A CAUSE RATHER THAN SKIPPING, which the parent flagged before I
built it: a gate that silently declines to run reads as a pass to anyone looking
at the job list, so the not-this-mode arm exits successfully and SAYS SO, and the
unnamed-observer arm exits non-zero with the located diagnostic.

Evidence, now over ARMS rather than over rendered shell text: the admission
witness holds all three arms, its last two conjuncts discriminating -- a gate
refusing on the mode alone would reject every correct reset dispatch, one
ignoring the mode would reject every other mode. A new witness holds that a
whitespace-only observer is unnamed too, which the shell `-z` caught for free and
a modeled comparison does not unless it trims. Discriminating red measured:
making the fold mode-blind turns the admission witness FAIL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

* The Bool over a coproduct came back one function later

review 62169, verified and correct, and the sharp part is that this module had
already made this exact correction.

subject_was_down_before_issuance was a Bool over HostReturnObservation: one arm
true, every other arm false. That re-encodes a variant test the sum already
carries and then throws away what it learned, so the caller minted
SubjectWasNotUpBeforeIssuance a second time -- one fact with two authorities.
It is the same shape as observer_identity_permits_issuance, which review 61872
had me dissolve into observer_identity_issuance_block returning the cause itself.
I applied that lesson to the identity wall and reintroduced the shape at the
already-down wall in the very next commit.

observation_issuance_block now returns ResetReturnRefusalCause?, the verdict
binds the cause it is given rather than re-minting one, and the Bool is deleted
with no marker: there is no debt to mark, the construction was simply available.

Witnesses unchanged and passing -- the already-down witness, its
not-attempted discriminator, and the down-then-back positive control. The
discriminating red still bites at the new shape: making the block never fire
turns w_a_subject_already_down_at_issuance_refuses_on_its_own_cause FAIL.

Worth recording because it is the second instance and the roster now carries the
class: a repair applied at one site is not applied at its siblings unless
somebody looks for them. The identity fix and this one were one commit apart, in
the same file, with the same reviewer-supplied reasoning in front of me.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiMCBr1aMNXBza2BWJHaRe

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 23, 2026
…edown script carries its bash-emit marker (review 70414)

plan_jit_mint and dispatch_jit_mint take a MicrovmRunnerGroupStanding instead of a
bare id; RunnerGroupRestrictionUnobserved refuses (JitMintRefusedRunnerGroupUnrestricted
-> DispatchRefusedRunnerGroupUnrestricted). The slot controller passes the unobserved
arm, so the block microvm_jit_runner_group_restriction_frontier states is now the
match itself. New red witness: an otherwise-authorized mint refuses on it.
microvm_shakedown_known_task_script gains its dissolution row naming bash-emit (#5828),
rendered as the script's first line like its ci_spec siblings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 27, 2026
…ssible on the bash_build node route today

Review on bf218e3: C1 turned an AVAILABLE emission route into a missing prerequisite.
v2.extdeps.languages.bash_build nodes emitted by v2.workflow.bash_emit bash_emit_stmts
already carry typed quoted words, command substitution, assignment, pipe, if/test,
redirect and || true; set/umask/export/trap are ordinary commands on it (#12422 retired
the pack runner that way). The 11 runner rows and the fci1/tools headers now say the
migration is unauthored, not blocked. C5 (background job), C6 (POSIX sh dialect) and C7
(transport argv is a service-declaration literal) remain the genuine gaps. The pack row
is restored to main so #12422's deletion lands cleanly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 28, 2026
…host effect via the bash emitter (#5828) (review 72000)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 28, 2026
…d hold/step scope with #12430's capability names ([C5 background-hold]; the bash_build route for the credential runner); regenerate

Co-Authored-By: Claude Opus 5.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