Repository navigation
Do not start the 65-minute floor on a branch already known to be red - #7882
Conversation
The ci job named only [build, regen] as prerequisites and was not gated on
the generated-artifact heal result. A branch whose ci.yml or generated
artifacts had already been refused could therefore spend a full floor
budget on a runner before failing for a reason established minutes earlier.
That happened repeatedly on 2026-08-05.
ci now needs [build, regen, heal_generated_artifacts], with
ci_floor_upstream_clean_if = "!failure() && !cancelled()".
The expression is load-bearing, not decoration. A job whose needs include a
skipped job is skipped by default, and heal is pull_request-only, so adding
the dependency alone would silently stop the floor running on push-to-main,
merge_group and workflow_dispatch -- deleting main's unconditional cold
control while every PR stayed green.
Four cases, all deliberate:
pull_request, no drift heal exits 0, floor runs (heal is parallel
with regen, so no wall time is added)
pull_request, author-commit heal exits 1, floor skipped instead of
required proving what the drift gate would refuse
pull_request, heal repaired heal exits 1 after SupersededByHealedHead,
so the floor no longer runs against a head
that is already obsolete
push / merge_group / dispatch heal skipped, no failure, floor runs as before
Skipping the floor is not a fail-open. The ruleset requires the ci context
and a skipped job does not satisfy a required check, so drift now blocks the
merge rather than being a red the floor might have masked. What is removed
is wasted computation, not a gate: the refusal that causes the skip is
itself the typed, located diagnostic, emitted before the expensive stage.
Witnesses, green by execution, and the pair is the point. Proven
discriminating by temporarily setting the expression to success():
floor_job_still_runs_when_heal_is_skipped FAILS while
floor_job_waits_on_the_heal_preflight still PASSES -- which is exactly the
silent regression shape, dependency intact and condition wrong.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review 49102 is correct and this was my error. ci_workflow.dag declared needs: [build, regen, heal_generated_artifacts] with a job-level if, while the committed ci.yml carried neither -- an internally inconsistent PR that would have failed ci_yaml_parse_witness, since expected_ci_yml() serializes the model. The cause was mechanical rather than a decision: the gating work was in this worktree when the WIP auto-commit picked it up and pushed the .dag half, while the regenerated ci.yml was discarded by a local reset moments later. The two halves were split across a push boundary. The fix is removal, not regeneration. That change is not in this PR's scope and already exists as #7882, authored on main with its own witnesses and a proven RED control. Regenerating ci.yml here would have made this PR self-consistent by duplicating another PR's change, which is the worse resolution of the two. The three files are restored to cfea75a, the last head where this branch carried only the heal-artifact fix. Regen after the restore produces no ci.yml drift, which is the check that model and artifact now agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…les from the path roster (#7830) * WIP: import -> namespace (import deletion) * chore: regenerate drifted generated artifacts (ci auto-heal) * Namespace closure: three prerequisite rows in front of reference-derived-closure namespace-reference-derived-closure carried a six-capability set-difference closing contract in which every row read Unavailable, while the node itself was the only dispatchable thing in the lane -- one startable row standing for three separable pieces of work with different substrates. The cut follows the contract's own triggers, which already record what each capability waits on: namespace-structural-observations 4 caps, P2aStructuralCandidateProducer7515 namespace-cross-file-provenance 1 cap, P2aReferenceDependencyProjection7515 namespace-pool-independence 1 cap, P2aPoolIndependentDependencyProjection7515 The first two are parallel -- no dependency runs between the same-file rules and the cross-file projection. Pool independence depends on cross-file provenance because a differential needs a projector to perturb. No new identity was minted for integration or for the census. namespace-reference-derived-closure keeps its durable identity and now denotes the aggregate handback (its first_slice moved; its boundary, which still covers all six, did not). namespace-ambiguity-discharge keeps its identity and its existing dependency on the closure node. The three new rows are deliberately ExecutionContractUnspecified. Each first_slice names authoring its own closing check as its first act, per v1_lane_binding_survey_note: the contract follows the witness, never precedes it. The derived closing-contract tasks for the two startable rows are the honest fail-closed state, not a gap; pool-independence gets none because it is dependency-held, and could not carry one anyway -- its differential compares an output shape its prerequisite has not yet produced. Also adds roadmap-receipt-continuity (roadmap-runtime, off the namespace spine): a receipt that was valid and is now absent refuses unless an explicit revocation names the exact node, the exact record, a reason and its disposition. Motivated by #7739, where a branch spent real effort reconstructing an acceptance record that already existed because nothing refused when it went missing. Distinct from startable_nodes_missing_closing_contract, which finds nodes that never had a check rather than accepted state that vanished. Evidence, by execution on this tree: - roadmap_authority_test: 42/42 witnesses PASS - generated_artifact_drift_test: 7/7 PASS - ROADMAP.md regenerated via main_wet on dag/tools/generated_artifact_gate.dag; the projection shows reference-derived-closure requiring the two new prerequisites, and derived closing-contract tasks appearing for exactly the two startable unbound rows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * WIP: import -> namespace (import deletion) * Remove stray empty file committed by WIP auto-commit An empty file named 'true' was created in the worktree by a shell-quoting mishap while sending dashboard messages, then picked up and committed by the WIP auto-commit process. It is not on main and carries no content. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * WIP: import -> namespace (import deletion) * Regenerate ci.yml: heal repair artifact publishes its hidden payload Author-committed because the GitHub App lacks workflows:write, so the heal job can detect this drift but can never push the fix. Emitted from the ci_workflow.dag change in this branch; the value is derived from author_commit_required_committed_artifact_paths(), not hard-coded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Correct the incident enumeration in the hidden-files note The first draft cited "#7770 twice, #7791, #7835, #7857". Two errors: #7770 hit the author-commit-required class three times on 2026-08-05 (ci.yml at 04:39 and 17:42, falsifier.yml at 06:34), and #7835 was only a prospective warning, never a confirmed incident — the real fourth PR is #7772. Verified against this lane's dispatch receipts rather than recall, and the count is now stated as a floor rather than a census. A wrong enumeration inside a canonical carrier is the citation class DESIGN §3 names, so it gets the same bar as any other cited fact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Regenerate ci.yml against merged main (author-committed) The merge brought in 52 commits of main, and the generated-artifact merge driver keeps this side's copy for generated paths, so the merged ci.yml was this branch's pre-merge bytes: it was missing main's new registered plans and stage0 modules in AUTHORED_CONFLICTS, the heal commit's --no-verify, several step ids, and a test-negation respelling. Regenerated from the authority rather than hand-merged. The output now differs from origin/main by exactly one line — the include-hidden-files key this branch adds — which is the check that the regeneration is correct rather than a plausible-looking text merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * WIP: import -> namespace (import deletion) * WIP: import -> namespace (import deletion) * Remove the floor/heal gating model change: it belongs to #7882, not here review 49102 is correct and this was my error. ci_workflow.dag declared needs: [build, regen, heal_generated_artifacts] with a job-level if, while the committed ci.yml carried neither -- an internally inconsistent PR that would have failed ci_yaml_parse_witness, since expected_ci_yml() serializes the model. The cause was mechanical rather than a decision: the gating work was in this worktree when the WIP auto-commit picked it up and pushed the .dag half, while the regenerated ci.yml was discarded by a local reset moments later. The two halves were split across a push boundary. The fix is removal, not regeneration. That change is not in this PR's scope and already exists as #7882, authored on main with its own witnesses and a proven RED control. Regenerating ci.yml here would have made this PR self-consistent by duplicating another PR's change, which is the worse resolution of the two. The three files are restored to cfea75a, the last head where this branch carried only the heal-artifact fix. Regen after the restore produces no ci.yml drift, which is the check that model and artifact now agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…light-gate # Conflicts: # .github/workflows/ci.yml # dag/test/claim/ci_heal_job_witness_test.dag
The merge brought main's floor-peak wet-witness receipt emitter; the generated-artifact merge driver keeps this side for generated paths, so ci.yml had to be re-projected rather than text-merged. Output now differs from origin/main by exactly the two gating lines this PR adds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sion The net diff of session/loyal-ram-550 against main was ONE file, ci.yml, and every hunk was a REVERT of content main already has: - ci job timeout-minutes 110 -> 100 - floor step timeout-minutes 75 -> 65 - deletion of the whole ci_floor_wet_witness_row_outcome_receipt_if block None of that was authored. It is the residue of regenerating ci.yml from a tree that was behind main with a stale local binary — the same defect I diagnosed and repaired on the #7882 worktree, which this branch never received. Merging it would have silently lowered main's floor budget and deleted a live receipt. Repaired by RESTORE, not regeneration. The .dag model on this branch is byte-identical to main's, so regenerating can only reproduce main's ci.yml; running the generator again would risk re-introducing the same stale-binary drift that caused the regression. Taking main's artifact is the same result with none of the risk. Net diff of this branch against main is now empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…al_if to gunbc.ci_heal_credential #7766 moved ci_regen_heal_if and the heal job's credential facts out of gunbc.ci_workflow_expressions into gunbc.ci_heal_credential, and the author-commit path roster into gunbc.heal_push_plan. Resolved by taking main's structure verbatim in all three files and re-adding only this PR's own contribution: ci_floor_upstream_clean_if stays in gunbc.ci_workflow_expressions (it is a floor-gating expression, not a heal-credential fact) and is imported where the ci job and its witnesses need it. ci.yml is regenerated in a following commit, not resolved by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ci-floor-preflight-gate
…g lines Main advanced three times during this resolution (#7766 heal-credential recut, #7880, #7874, then 3c60c89). The first regeneration in this worktree ran on top of a stale ci.yml left from before the second merge and produced output that REVERTED #7766's heal improvements — the `heal_commit_push` step id, the guarded bundle copy, `if-no-files-found: error`, and the conditional upload. That artifact was discarded, not committed: ci.yml was reset to main's version and regenerated from the merged model, which is the only procedure that makes the generator a function of the model alone. Verified against a PINNED main SHA rather than the moving `origin/main` ref, because main moved mid-resolution and an unpinned comparison made main's newer content read as deletions this branch was making. Final state vs 3c60c89: model: 3 files (ci_workflow, ci_workflow_expressions, ci_heal_job_witness_test) ci.yml: needs: [build, regen, heal_generated_artifacts] if: "!failure() && !cancelled()" Witnesses: 27/27 PASS, including floor_job_waits_on_the_heal_preflight and floor_job_still_runs_when_heal_is_skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review 49309 (cursor/composer-2.5) found that the first cut of this PR was
a fail-open on the only required merge gate. Confirmed on three independent
legs before changing anything:
1. GitHub's troubleshooting-required-status-checks page: a job skipped by
a conditional REPORTS SUCCESS, and a job skipped because a needed job
failed "may not block merging".
2. The live default-branch ruleset (id 16178731) names exactly one
required context: `ci`. heal_generated_artifacts is not required.
3. Composing them: heal fails -> ci skipped -> ci satisfies its own
required check by being absent -> GeneratedArtifactDriftGate never
runs -> drift merges while only a non-required job is red.
The change meant to remove wasted computation would have removed the wall.
WHAT CHANGED. The job condition is now `!cancelled()`, so ci runs whenever
the run is live -- including when an upstream job failed -- and the required
context always produces a real verdict. The refusal moved into an executing
first step that reads needs.<job>.result and exits 1 with a typed, located
FloorUpstreamAlreadyRed diagnostic. The budget saving is preserved: a runner
spin-up and a string compare, not a floor run.
heal may be `skipped` (it is pull_request-only) and the floor proceeds, so
main's cold control is intact; only `failure` refuses, covering both
HealAuthorCommitRequired and the deliberate SupersededByHealedHead exit.
MY NOTE WAS THE WORSE HALF AND IS RETRACTED IN PLACE. It asserted "a skipped
job does not satisfy a required check". That was written from reasoning, not
from a receipt -- the DESIGN section 5 trap of fluent output that was never
run. The replacement states the verified mechanism and cites both receipts.
A SECOND DEFECT, CAUGHT BY READING THE GENERATED ARTIFACT rather than the
model: the refusal step was first added to ci_floor_job_prelude_steps(),
which is shared, so it leaked into the regen job and into falsifier.yml. In
the regen job (needs: [build]) the referenced results do not exist, so the
guard would have compared "" != "success" and refused on EVERY run. It is now
composed into ci_job()'s own step list, and
floor_refusal_reaches_only_the_job_whose_needs_it_names is the control that
reds if it ever leaks again.
Witnesses: 30/30 PASS. floor_job_runs_even_when_an_upstream_job_failed
refuses both failure() and success() by name, and was proven discriminating
by reintroducing `!failure() && !cancelled()` and observing exit 1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
So the change intended to remove wasted computation would have removed the wall. That is a DESIGN §5 fail-open and the review's hard-reject bar applies. The fix is the one the review named. The job condition is now The note was the worse half of the defect and is retracted in place. It asserted "a skipped job does not satisfy a required check". I wrote that from reasoning rather than from a receipt — precisely the §5 trap of fluent output that was never run. The replacement states the verified mechanism and cites both receipts. A second defect, which the review did not raise and I caught by reading the generated artifact rather than the model: I first added the refusal to Witnesses 30/30 PASS. — sent from loyal-ram-550 |
…ci-floor-preflight-gate
The prior witness folded over ci_job().steps and asserted the refusal existed SOMEWHERE. That does not pin the invariant the step exists for: refusing BEFORE the expensive work. A future edit could move it after checkout, toolchain setup or artifact download and the assertion would stay green. floor_refusal_is_exactly_the_first_step now matches list_head(ci_job().steps) and requires it to BE the refusal step. Proven discriminating by moving the step behind the prelude: the new witness FAILS while the old fold witness still PASSES on the same tree, which is the operator's point demonstrated rather than argued. The fold assertion is retained beside it because it carries the script-content claims; the positional one carries the ordering. The generated ci.yml is not separately asserted because the drift gate already pins ci.yml == the model's projection, so model-first-step plus drift-clean composes to workflow-first-step without a second reader. Witnesses: 31/31 PASS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What this does
Stops the ~65-minute floor from starting on a branch an upstream job has already established is red — without weakening the merge gate.
cineedsbuild,regen,heal_generated_artifacts!cancelled(), so upstream failure does not implicitly skip the requiredciverdicthealis allowed to beskippedand the floor still runs: heal intentionally runs only for same-repository, non-Dependabot pull requests, so push, merge-group, dispatch, fork and Dependabot cases legitimately skip it. Onlyfailurerefuses — covering bothHealAuthorCommitRequiredand the deliberateSupersededByHealedHeadexit after heal auto-pushes a repair.Retracted first design — recorded because the note in-tree retracts it
The first cut set the job condition to
!failure() && !cancelled(), so an upstream failure skippedci. That was a fail-open on the only required merge gate, found byreview 49309. Verified on three legs:16178731) names exactly one required context —ci.heal_generated_artifactsis not required.ciskipped-as-success while only the non-required heal job was red, soGeneratedArtifactDriftGateand merge-admission stamping could be bypassed.The retracted note also claimed "a skipped job does not satisfy a required check". That is false and is retracted in place in
ci_floor_upstream_red_refusal_note, with both receipts cited. It was written from reasoning rather than from a receipt.Witnesses (31/31 PASS)
floor_job_waits_on_the_heal_preflight— the dependencyfloor_job_runs_even_when_an_upstream_job_failed— condition is!cancelled(), and bothfailure()andsuccess()are refused by name. Polarity is inverted from the first revision on purpose: the dangerous edit is no longer "simplify tosuccess()" but "reintroducefailure()", which reads as a tightening and is in fact the hole. Proven discriminating by reintroducing!failure() && !cancelled()-> exit 1.floor_refusal_is_exactly_the_first_step—list_head(ci_job().steps)must BE the refusal step, so it cannot drift behind checkout/toolchain/download. Proven discriminating by moving the step behind the prelude: this witness fails while the existence-only fold still passes.floor_refusal_reaches_only_the_job_whose_needs_it_names— the step was first added to the sharedci_floor_job_prelude_steps()and leaked into theregenjob andfalsifier.yml; inregen(needs: [build]) the referenced results do not exist, so it would have compared"" != "success"and refused on every run. Caught by reading the generated artifact; this control reds if it leaks again.floor_refusal_tolerates_a_skipped_heal_but_not_a_failed_one— the heal-vs-siblings asymmetry.The generated
ci.ymlis not separately asserted for ordering because the drift gate already pinsci.yml== the model's projection, so model-first-step plus drift-clean composes.Diff vs main
4 files.
ci.ymldelta isneeds: [build, regen, heal_generated_artifacts],if: "!cancelled()", and the refusal step.Test plan
claim_batch --source-root dag --source-root src/v2 --entry dag/test/claim/ci_heal_job_witness_test.dag— 31/31 PASS, plus the two discriminating REDs above run by hand and observed to exit 1.