Skip to content

Do not start the 65-minute floor on a branch already known to be red - #7882

Merged
briansrls merged 11 commits into
mainfrom
session/ci-floor-preflight-gate
Aug 6, 2026
Merged

briansrls merged 11 commits into
mainfrom
session/ci-floor-preflight-gate

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

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.

upstream red      -> required `ci` job RUNS -> first step fails immediately
expected heal skip-> floor runs
cancelled run     -> no further work
  • ci needs build, regen, heal_generated_artifacts
  • job condition is !cancelled(), so upstream failure does not implicitly skip the required ci verdict
  • the first modeled step reads the three upstream results and exits red before checkout or the floor when build/regen did not succeed or heal failed

heal is allowed to be skipped and 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. Only failure refuses — covering both HealAuthorCommitRequired and the deliberate SupersededByHealedHead exit 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 skipped ci. That was a fail-open on the only required merge gate, found by review 49309. Verified on three legs:

  1. GitHub's troubleshooting-required-status-checks: a job skipped by a conditional reports Success; one skipped because a needed job failed "may not block merging".
  2. The live default-branch ruleset (16178731) names exactly one required context — ci. heal_generated_artifacts is not required.
  3. Composing them: drift would leave ci skipped-as-success while only the non-required heal job was red, so GeneratedArtifactDriftGate and 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 dependency
  • floor_job_runs_even_when_an_upstream_job_failed — condition is !cancelled(), and both failure() and success() are refused by name. Polarity is inverted from the first revision on purpose: the dangerous edit is no longer "simplify to success()" but "reintroduce failure()", 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 shared ci_floor_job_prelude_steps() and leaked into the regen job and falsifier.yml; in regen (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.yml is not separately asserted for ordering because the drift gate already pins ci.yml == the model's projection, so model-first-step plus drift-clean composes.

Diff vs main

4 files. ci.yml delta is needs: [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.

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>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 5, 2026
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>
briansrls pushed a commit that referenced this pull request Aug 6, 2026
…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>
gunbc-ci-auto-heal and others added 4 commits August 6, 2026 00:12
…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>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 6, 2026
…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>
gunbc-ci-auto-heal and others added 4 commits August 6, 2026 03:19
…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>
…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>
@gunbai-bot

gunbai-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

review 49309 was correct and the finding is fixed in e95119f. I confirmed it on three independent legs before touching anything, because the finding was against a claim I had written:

  1. GitHub's documented behavior — the troubleshooting-required-status-checks page states a job skipped by a conditional reports Success, and a job skipped because a needed job failed "may not block merging".
  2. The live 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.

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 !cancelled(), so ci runs whenever the run is live — including on upstream failure — 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. The budget saving survives: 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.

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 ci_floor_job_prelude_steps(), which is shared, so it leaked into the regen job and into falsifier.yml. In regen (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 leaks again.

Witnesses 30/30 PASS. floor_job_runs_even_when_an_upstream_job_failed refuses both failure() and success() by name — note the polarity inverted from the previous revision, because the dangerous edit is no longer "simplify to success()" but "reintroduce failure()", which reads as a tightening and is in fact the hole. Proven discriminating by reintroducing !failure() && !cancelled() and observing exit 1.

— sent from loyal-ram-550

gunbc-ci-auto-heal and others added 2 commits August 6, 2026 06:03
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>
@gunbai-bot gunbai-bot Bot changed the title Do not start the 65-minute floor on a branch already known to be drifted Do not start the 65-minute floor on a branch already known to be red Aug 6, 2026
@briansrls
briansrls merged commit 520cb07 into main Aug 6, 2026
5 checks passed
@briansrls
briansrls deleted the session/ci-floor-preflight-gate branch August 6, 2026 16:36
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