Skip to content

Bind fleet-converge to capability closure; widen the step role from a coproduct to a relation - #8734

Closed
briansrls wants to merge 6 commits into
mainfrom
fleet-converge-capability-binding
Closed

briansrls wants to merge 6 commits into
mainfrom
fleet-converge-capability-binding

Conversation

@briansrls

@briansrls briansrls commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Corrected body. I briefly replaced this PR's description with text belonging to a different branch — the annotation restore, now #8735. The diff here was never the restore; this body now describes what is actually in it.

Extends the merged #8702 closure to fleet-converge.yml — the workflow that actually carries the hand-written cargo resolve circuit.

The role model was wrong, and binding a second workflow proved it

#8702 shipped StepCapabilityRole as a three-variant coproduct: provides or consumes or neutral. The fleet prelude does not fit:

  • setup-rust-toolchain installs cargo, rustc and rustfmt in one action.
  • The rustup pin step consumes rustup and provides the resolved cargo binding.

Squeezing that into the coproduct would have required marking the rustc-consuming step Neutral — hiding a real dependency to fit my model, which is the failure this whole program is about. So the role became a record of two lists, and neutral became the empty relation rather than a distinct variant.

Worth stating plainly: a model that fits one specimen is not yet a model. The first bound job happened to need exactly one capability and I generalised from it.

The constraint this writes down

Two prelude steps consume a capability an earlier one installs, and until now that fact lived only inside a diagnostic string:

"::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"

The dependency was real, load-bearing, and invisible to everything except a human reading the error it would print if it broke. It is now structural.

Single authority

ci_prelude_bound_steps_with_checkout is one list of rows carrying both the Step and its capability role. ci_prelude_steps_with_checkout and ci_prelude_annotations_with_checkout are both projections of it, so they cannot drift. Three inline step literals were extracted to named functions so both projections share one authority.

Evidence

Eight witnesses, each falsified by a different wrong implementation:

witness verdict
no-provider job refuses true
provider-first job admitted true
provider-after-consumer refuses true
live witness-floor job closes true
a step cannot satisfy its own requirement true
one step can provide several capabilities true
a capability outside the provided list still refuses true
live fleet-converge build job closes true

The middle three are new claims made expressible by the record model — a step declaring both lists would always close if provides were granted before consumes was checked, which is the vacuous answer.

fleet-converge.yml emission byte-identical at 18736. Emission refuses rather than fabricating YAML when closure fails.

Rung

Two jobs bound. Other workflow emitters can still assemble bare Steps and bypass closure entirely — that remains the next trigger.

🤖 Generated with Claude Code

Brian Searls and others added 3 commits August 21, 2026 05:33
… to a relation

WIP commit before merging main -- full message on the PR.
…r_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>
@gunbai-bot gunbai-bot Bot changed the title shell -> dag Restore what my stale-branch squash silently reverted in witness_floor_workflow Aug 21, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 21, 2026 05:43
@gunbai-bot gunbai-bot Bot changed the title Restore what my stale-branch squash silently reverted in witness_floor_workflow Bind fleet-converge to capability closure; widen the step role from a coproduct to a relation Aug 21, 2026
@gunbai-bot

gunbai-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Verification against current main, and one cross-PR dependency worth stating.

Re-verified after merging origin/main into this branch (my branch point was ~8 commits stale, and the byte comparison below is exactly the check that catches that):

check result
8 capability witnesses all true
fleet-converge.yml emission IDENTICAL 18736 == 18736
witnesses.yml emission DIFFERS 1437 vs 1457

The witnesses.yml difference is pre-existing on main and is NOT introduced by this PR. Measured on clean origin/main with this branch's changes stashed — main emits 1437 against a committed 1457, with the same one-line diff:

committed:  - name: "Required CI: parse, regen, regen determinism, behavioral receipt, witness floor"
emitted:    - name: "Required CI: parse, regen, regen determinism, witness floor"

Cause: my own merged #8702 reverted that line from a stale branch. Fixed in #8737, which restores the module text so it agrees with the committed artifact again — no regeneration needed.

Merge order: #8737 before this PR. If this one lands first the drift simply persists unchanged (this branch neither causes nor worsens it), but the reverse order means #8737's byte-identity proof is measured against a tree this PR has already touched.

This branch touches witness_floor_workflow.dag only for the role-model migration — CapabilityNeutral → capability_neutral, ProvidesCapability {…} → provides_only(…), and the same for consumes. Seven lines, none of which affect emitted bytes; the emission delta above comes entirely from the pre-existing revert.

— sent from eager-crane-282

@gunbai-bot

gunbai-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

CI failure at 0ebae58 diagnosed — not introduced by this PR, and not fixable from this branch.

The failing phase is the witness floor:

required-ci: floor refused: subject=fccee536c5b1dedd modules_resolved=3760 modules_excluded=4
dag/gunbc/fleet_desired_admission.dag:169:7: error: non-exhaustive match: missing variant(s) FleetDesiredAbsent, FleetDesiredUndecodable

dag/gunbc/fleet_desired_admission.dag is untouched by this PR (git diff origin/main HEAD -- <that file> is empty). The break came in with #8701, which split FleetDesiredRevision from two variants into four in gunbc.fleet_main_revision without answering the two new arms at the fleet_desired_admission_decide consumer. This merge commit simply pulled that main state in.

Main's own floor confirms it, by headSha:

run head conclusion
04:45 033647789f success
05:23 73c62cad34 (#8701) failure
05:26 2249df7daa failure
05:29 e9e42f2f24 failure

Main has been red since #8701 and remains red; every open PR that merges main inherits it.

#8738 (fix/main-fleet-desired-admission-exhaustive) is already the fix and is open and MERGEABLE. I am not duplicating it here.

Merge order, unchanged apart from the new head: #8738 → #8737 → this PR. (#8737 restores the annotation block my earlier #8702 clobbered via a stale branch; the witnesses.yml 1437-vs-1457 drift on this branch is that same pre-existing defect, also not this PR's.)

@gunbai-bot

gunbai-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Rebased onto current main (608248c15e), which carries #8744 — the fix for the fleet_desired_admission non-exhaustive match that was reddening every PR's witness floor. The CI failure reported here earlier was that, and it is gone from this branch's base.

One further change, to close a hazard rather than to add content: this branch now also merges restore-clobbered-receipt-annotation (#8737). Both PRs edit dag/gunbc/witness_floor_workflow.dag — this one at the imports and the bound-step rows (~397–419), #8737 at ~345 and ~435. They do not overlap, but they are adjacent, and the last time I let two edits to this file land in sequence from a branch that predated one of them, the squash silently reverted 15 lines and I had to open #8737 to put them back. Carrying the restoration here makes the drop unrepresentable in either merge order: if #8737 lands first the replay is a no-op, and if it lands second the block is already present.

Verified after the merge: witness_floor_workflow.dag is 548 lines on this branch, matching #8737, against 533 on main.

— sent from eager-crane-282

@gunbai-bot

gunbai-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Superseded by #8796, which carries this work unchanged along with the rest of the srv3 stack and the fleet-converge capability binding. Consolidated at the operator's request (too many open PRs).

Verified before closing: every declaration, module and witness file this PR introduced is present on srv3-consolidated, and that branch is based on current main rather than on the older base this one carried.

Nothing is dropped. Review history stays here; the diff to review is now #8796.

@gunbai-bot gunbai-bot Bot closed this Aug 21, 2026
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