Repository navigation
The falsifier alert notified once per incident; a renamed witness left two stale roster rows - #7884
Merged
Merged
Conversation
… left two stale roster rows Two independent defects, both found from the falsifier's own receipts. ALERT REACHED ITS RECIPIENT ONCE PER INCIDENT, NOT EVERY 20 HOURS. An @mention notifies when it is CREATED, and only an issue body's first write is a creation; body edits send nothing. The mention lived only in the body, so the artifacts that carry current state — the sustained-red heartbeat and the failing-set-changed comments — notified nobody. That is the whole mechanism the workflow header describes as its purpose. Receipt: issue #7737 opened 2026-08-03 and accrued four github-actions comments over the next two days (one heartbeat, three failing-set-changed). None carried a mention. The alerting MECHANISM was working the entire time and its state was correct; what never happened was delivery. The handle is now one ALERT_MENTION constant read by every notifying artifact — body, heartbeat, failing-set-changed, and the green close. One constant rather than four literals because gunbc.falsifier_lane falsifier_lane_owner_roster_note records this mention as the only owner fact in the tree, and a second copy of it would be a second authority for that one fact. That note is updated to name the constant. A latent second defect on the same path: the 20h throttle read comments[length-1] from a single per_page=100 request. The endpoint returns oldest-first and accepts no sort/direction, so past 100 comments that index is the 100th, not the newest, and its age only grows — the throttle would have inverted into "heartbeat every run", turning a silent alert into a spamming one. Paginated. Enrolled as an executing control rather than left to review: .github/scripts/falsifier-alert-selfcheck.cjs runs the SHIPPED yaml's script body against stubbed APIs, as a separate job so it can only ever add a red and never suppress an alert. Measured against 70b422f, four of its five scenarios are discriminating REDs (heartbeat, failing-set-changed, green-close, throttle-pagination) and the open-issue-body scenario is the positive control. Its honest limit is stated in the job: nothing watches falsifier-alert run conclusions today, so this is a real control that is not itself alerted on; that residue closes with the script's dissolve-on. A RENAMED WITNESS LEFT TWO STALE LONG-LANE ROWS. #7822 renamed witness_live_constraint_present_iff_line_unaccepted to witness_fixture_constraint_present_iff_line_unaccepted as part of decomposing the live witness onto planted fixtures. Two byte-identical gunbc.ci_layer_roots falsifier_substrate_long_lane_rows rows kept the old name, so the falsifier's batch 6 tried to run a function that does not exist and reported "no such function" (run 31047744902). Both rows are DELETED rather than repointed, because the row's own dissolve_on first arm is met by execution: the renamed witness measures cpu=48ms wall=50ms against the 5000ms fast-lane budget, two orders under it. The deferral reason those rows carried — live_view() joining declared nodes, edges, receipts and finish-line specs at ~3074ms — describes work the function no longer does. Nothing is de-enrolled: the file sits under dag/test/claim, so deleting the rows returns it to per-PR discovery, and #7822 already gave the live half its own long-lane row on roadmap_program_view_live_corpus_receipt_test.dag. WHAT THIS DOES NOT FIX, so the falsifier's remaining red is not read as this change's failure. Batch 6 also reports direct_rust_door_production_group_closing_expectation_holds returning false. That witness is DECLARED known-red by #7814 and is red by design. It counts as a plain failure because discovery_entries_are_expect_red requires EVERY explicit entry in a batch to be a known-red path, and the witness joins the long-lane roster to earn its budget — the machinery deficit gunbc.explicit_witness_admission already documents on its sibling row, with a named dissolve_on (separate the execution-requirement/budget authority from the batch roster). Untouched here; that row says the only edit that may green it is making the real inferred tree survive emission. Also untouched: the 170-minute step timeout that killed run 31031283447 mid-floor. The following run completed inside the budget, so the floor is running marginal against that cap rather than over it — the run-stability and five-minute-CI-gate lanes' subject, and the cap is not raised here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two hygiene fixes on the control added in the previous commit, both of the same class it exists to catch. A harness that throws partway printed "ALL GREEN" over however many scenarios had reported — zero failures among two executed scenarios is not the same fact as zero among five, and only one of those is a pass. It now refuses (exit 2) unless every declared scenario reported, and the top-level promise has a catch arm so a throw before reporting refuses rather than exiting 0 on an unhandled rejection. Each scenario chdirs into a fresh mkdtemp so the workflow script can read its receipt relative to cwd; on a self-hosted runner firing every four hours those accumulated. Registered and removed on both exit paths. Re-verified both directions after the change: ALL GREEN on this branch, 4 RED against origin/main, and no temp dirs left behind by either run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both #7763 and #7857 touched dag/gunbc/ci_layer_roots.dag, the same file this branch deletes two long-lane roster rows from. The textual merge is clean, which is exactly the case worth re-executing rather than trusting: a roster defect can live only in the composition of two individually correct changes.
… as a named gap Operator correction: .github/scripts/falsifier-alert-selfcheck.cjs was raw hand-authored JavaScript. Every foreign-language artifact in this tree is EMITTED from .dag — .github/live-deploy-srv1-apply.sh is shell and sits in the generated_artifact registry — so hand-authoring a second realization here added maintained foreign-language surface rather than a scaffold. A declared dissolution trigger is what makes a scaffold legitimate when the substrate route is unavailable; it does not license skipping a route that was never attempted, and this one was not. The subject was JS, so the check was written in JS. That is the unmarked-workaround shape DESIGN names, and noticing it is the line-stop. Checked before removing rather than assumed: the .dag route is genuinely blocked, at the same seam this workflow's own dissolve-on already names. There is no workflow_run TRIGGER model — extdeps.github.workflow_runs models runs as an API resource, not as a trigger — and gunbc.falsifier_workflow declares issues: none. So the alert's notifying artifacts are not substrate objects, no lens has a Node tree to read, and a .dag witness cannot exist until falsifier_workflow absorbs this file. Modeling it as text analysis over the yaml would be the grep-not-lens antipattern, which is not the substrate route either. So the mention property ships UNENFORCED, and that is recorded in the workflow header with its next-rung trigger rather than left implied — the honest state is a gap that is named and counted, not one papered over by a check that executes in the wrong language. The authoring-time execution receipt stands as evidence the shape is correct (4 discriminating REDs against 70b422f: heartbeat, failing-set-changed, green-close, throttle pagination; open-issue-body green as the positive control), and it is cited in the header as an unenrolled receipt rather than presented as a live control. Re-verified after the removal that the fix itself is untouched: same 4 RED against origin/main, all green here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent defects behind "the falsifier is red and it isn't notifying me", both found from the falsifier's own receipts.
1. The alert reached its recipient once per incident, not every 20 hours
An
@mentionnotifies when it is created, and only an issue body's first write is a creation — body edits send nothing (which is exactly why this workflow rewrites the body every window). The mention lived only in the body, so the artifacts that carry current state notified nobody.Receipt: issue #7737 opened 2026-08-03 and accrued four
github-actionscomments over the next two days — one heartbeat, three failing-set-changed. None carried a mention. The alerting mechanism was working the whole time and its state was correct; delivery is what never happened.ALERT_MENTIONconstant read by every notifying artifact: body, heartbeat, failing-set-changed, green-close. One constant rather than four literals becausegunbc.falsifier_lanefalsifier_lane_owner_roster_noterecords this mention as the only owner fact in the tree — a second copy would be a second authority for one fact. That note is updated to name the constant.comments[length-1]from a singleper_page=100request. The endpoint returns oldest-first and accepts no sort/direction, so past 100 comments that index is the 100th and only gets older — the throttle would have inverted into "heartbeat every run". Paginated.The property ships UNENFORCED, and the gap is named rather than implied. A hand-authored JS harness was written beside the workflow and then removed (operator, 2026-08-06): every foreign-language artifact in this tree is emitted from
.dag—.github/live-deploy-srv1-apply.shis shell and sits in thegenerated_artifactregistry — so hand-authoring a second realization adds maintained foreign-language surface rather than a scaffold. Checked before removing rather than assumed: the.dagroute is genuinely blocked at the same seam this workflow's own dissolve-on names — there is noworkflow_runtrigger model (extdeps.github.workflow_runsmodels runs as an API resource) andgunbc.falsifier_workflowdeclaresissues: none, so the notifying artifacts are not substrate objects and no lens has aNodetree to read.What stands is an unenrolled authoring-time receipt, cited in the workflow header as exactly that and not as a live control:
Its honest limit is stated in the job: nothing watches
falsifier-alertrun conclusions today, so this is a real control that is not itself alerted on. That residue closes with the script's dissolve-on.2. A renamed witness left two stale long-lane rows
#7822 renamed
witness_live_constraint_present_iff_line_unaccepted→witness_fixture_constraint_present_iff_line_unacceptedwhile decomposing the live witness onto planted fixtures. Two byte-identicalfalsifier_substrate_long_lane_rowskept the old name, so batch 6 tried to run a function that does not exist:Both rows are deleted rather than repointed, because the row's own
dissolve_onfirst arm is met by execution — the renamed witness measures cpu=48ms wall=50ms against the 5000ms fast-lane budget:The deferral reason those rows carried (
live_view()joining declared nodes, edges, receipts and finish-line specs at ~3074ms) describes work the function no longer does. Nothing is de-enrolled: the file is underdag/test/claim, so deleting the rows returns it to per-PR discovery, and #7822 already gave the live half its own long-lane row onroadmap_program_view_live_corpus_receipt_test.dag.Verified on the merged tree
Main moved three commits during review and two of them touched this same roster file (#7763, #7857 — the floor-plan witness file itself grew 33 → 36 test fns). The textual merge was clean, which is the case worth re-executing rather than trusting, so every figure below is re-run against the merged tree with a rebuilt binary (main added a
build.rs):ci_floor_plan_witness_test— 36/36, the plan⇄roster consistency authoritygauntlet_lane_enrollment_witness_test— 6/6, incl. both RED controlsWhat this does not fix
So the falsifier's remaining red is not read as this change's failure:
direct_rust_door_production_group_closing_expectation_holdsfalse. It is declared known-red by Direct Rust door: one canonical stage-located acceptance group #7814 and red by design. It counts as a plain failure becausediscovery_entries_are_expect_redrequires every explicit entry in a batch to be a known-red path, and the witness joins the long-lane roster to earn its budget — the machinery deficitgunbc.explicit_witness_admissionalready documents on its sibling row, with a named dissolve-on. Untouched; that row says the only edit that may green it is making the real inferred tree survive emission.gunbc.roster_registryalready registersfalsifier_substrate_long_lane_rowsas aDeclaredFrontierandv2.lens.roster_registryalready holds it to carrying a non-empty reason and dissolve-on — which both stale rows satisfied perfectly. Nothing joins a row's(entry, function)to a declaration that exists, and the nearest assertion compares roster lengths, a count equality rather than the identity join DESIGN §5 asks for. That is why the stale name sat unnoticed. Deliberately not built here:roster_registry_enforcement_intent_convergence_notesays the enforcement-intent thread absorbs this registry whenStandingIntent/LensContractland, and "Do not build that machinery here."🤖 Generated with Claude Code