Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module gunbc.recurring_failure_mode.a_roster_edit_judged_against_the_base_tip_not_the_merge_base

import std.types { NonEmptyStr }
import gunbc.recurring_failure_mode { RecurringFailureMode }

data a_roster_edit_judged_against_the_base_tip_not_the_merge_base: RecurringFailureMode = RecurringFailureMode {
identity: "a_roster_edit_judged_against_the_base_tip_not_the_merge_base" as NonEmptyStr,

receipts: [
"INVALID STATE: a required-floor judgment of a ROSTER EDIT reads the base side at the base ref's TIP, while the floor's declared comparison is a merge-base one. A branch that edits the roster and then falls behind main is judged as if it authored every roster change main made after the branch point, in reverse. The change it is charged with is not in its diff, and the merge that would land it does not contain that change either: git's three-way merge keeps main's later edit to rows the branch did not touch.",

"OCCURRENCES, 2026-10-05, two PRs from one parent lane in one hour. gunbc#13333 (clever-raven-680) and gunbc#13341 (sunny-ant-198) each edited v2.workflow.floor_unimported_bare_provider_debt_roster. Main then retired roadmap_launch_deployment_receipt rows. Both floors went red with RosterRetirementChanged (Retired -> ActiveDebt) on rows neither diff touched, and both went green after merging origin/main with no change to the fix. Each lane spent a diagnosis round on a red its diff did not cause.",

"THE MECHANISM, read from the code, not inferred from the symptom. v1_compiler.cli_run required_floor_runner interface_consumer_planning matches floor_diff_comparison_readout and, on the FreezeBaselineComparison MergeBase arm, resolves `git merge-base base head` before reading the base tree; its comment states the floor's window is the relation the affected-set diff was taken under. Two other readers take floor_diff_comparison_readout()?.base(), the tip, and they read DIFFERENT inputs there. (a) unimported_bare_provider_gate reads the UNIMPORTED-BARE-PROVIDER ROSTER at the tip, through v2.workflow.floor_unimported_bare_provider_debt unimported_bare_provider_roster_at_base (git.Core.Show), and judges the roster edit against it; this is the reader the two occurrences above hit. (b) The cost-debt edit-budget arm of the claim planner passes the tip to cost_debt_changed_witness_ceilings, which runs v2.workflow.floor_cost_debt_edit cost_debt_changed_witness_ceilings_at_base over the changed WITNESS FILE'S SOURCE at that ref, not a roster, to classify the edit and so pick the grandfathered or new-witness budget. Its stale-base consequence is a different one: a witness file main edited after the branch point is classified as if this change made the reverse edit. That is unobserved so far, and reachable by the same mechanism. (c) A third reader, of a third input: gunbc#13332's cost_debt_admitted_identities reads COST-ROSTER MEMBERSHIP at the base side to decide what a change admits; at its head 103a8ea847 it read the tip through base() as well (see HARM AND RUNG). So one readout yields two different base commits in one run, depending on which consumer asks.",

"HARM AND RUNG. As observed, it is a LOUD false red: the line stops, typed and located, on a fact the diff did not author. That is §5-shaped but mis-aimed, and the cost is real: diagnosis rounds, and a standing incentive to merge main reflexively instead of reading the refusal. The SILENT arm is reachable in the same class through an admission reading. A roster whose base-tip membership decides what a change ADMITS (gunbc#13332's cost_debt_admitted_identities, at its head 103a8ea847, reads floor_diff_comparison_readout()?.base() the same way) charges a behind branch with admitting every row main retired since the branch point. That is spurious work at best, and a refusal attributed to the wrong change at worst. Found at rung 1, mitigated only by authors merging main.",

"WHY THIS IS NOT stale_claim_survives_its_own_correct_edit. That row is a dependent assertion left un-updated after a correct premise edit. Here no assertion is stale; the base READ is taken at the wrong commit, so a correct roster and a correct rule still disagree.",

"CEILING 3, structurally guaranteed, with the reason: FreezeBaselineComparison already carries the relation as a closed coproduct, so the base commit a roster judgment reads is decidable from the readout alone. Carrying the RESOLVED base commit on the readout, and deleting the bare base() accessor as a route to a tree, leaves no consumer able to read the tip under a merge-base relation. It does not reach 4 because a consumer handed a commit can still pass a different one to git; the construction removes the accessor that invites it, not git's ability to read any ref.",

"NEXT-RUNG TRIGGER (a CAPABILITY, not an artifact), classified CAN CLIMB NOW BUT UNBUILT: every required-floor consumer that reads a base-side tree obtains the base COMMIT from one resolution of floor_diff_comparison_readout that applies the MergeBase arm, so interface planning, the unimported-bare-provider roster edit, the cost-debt edit budget and cost-debt admission read the same commit in one run. A per-site merge-base call added to one gate does not discharge this row; it fixes one site and leaves the accessor standing for the next.",

"RECOGNITION RULE AND REVIEW TELL: a refusal naming rows the diff did not touch, which clears after merging main with no change to the diff, is this class until shown otherwise. In review, grep `.base()` on the comparison readout: every caller that reads a tree with it, rather than printing it, is a member.",

"DISCHARGED, 2026-10-05 (crisp-raven-17, gunbc#13347), by the trigger above, for all three inputs. THE CONSTRUCTION. v1_compiler.cli_run comparison_window resolve is the one place the relation is read for a tree: the base ref under two-dot, the departure point under merge-base. When it cannot resolve it refuses FreezeBaselineUnobservable or FreezeBaselineUnrelatedHistory and never degrades to a ref. It returns ResolvedComparisonWindow, whose base side is a BaseTreeCommit. That is a newtype whose field is private to that module, so resolve is its only constructor; ResolvedComparisonWindow's fields are private too. v1_compiler.cli_run required_floor_runner FloorBaseTree resolves the window at most once per floor run and lends it to every base-tree reader: interface_consumer_planning; unimported_bare_provider_gate, for input (a), the roster, through unimported_bare_provider_edit_refusals and unimported_bare_provider_roster_source_at_base; the cost-debt edit budget in run_required_floor, for input (b), the changed witness file's source, through cost_debt_changed_witness_ceilings; cost-debt admission, for input (c), cost-roster membership, through cost_debt_admitted_identities and cost_debt_base_tree_extract, the git archive of the base tree the base roster is evaluated over; and two tip readers this row had not named, floor_base_test_decl_census and floor_base_file_read, the second reached through non_fold_residue_changed_row_subjects. collect_frozen_path_deferral_additions_for uses the same resolver. FreezeBaselineComparison base() is deleted, and base_ref() remains for printing. EVIDENCE, IN ORDER OF STRENGTH. (1) THE READER INTERFACE, rung 3, structurally guaranteed at that interface. unimported_bare_provider_roster_source_at_base, unimported_bare_provider_edit_refusals, cost_debt_changed_witness_ceilings and cost_debt_base_tree_extract take a BaseTreeCommit, not a string, so the substitution a review raised does not compile: passing the readout's base_ref() as a string into the roster read. floor_base_file_read and floor_base_test_decl_census take FloorBaseTree. The limit is precise, and it is why this is ceiling 3, not 4: a consumer can still hand any ref to git outside these readers, or add a new reader that takes a string. namespace_baseline reconstruct_base_index still takes a string, because it is a general utility with other callers; the floor's only call passes FloorBaseTree's commit. That substitution was executed once as a one-off probe on this branch, and rustc refused it with E0308 (expected `&BaseTreeCommit`, found `&String`). The probe is not enrolled anywhere, because the crate has no compile-fail harness, so after this change the wall rests on the signatures. (2) THE EXECUTED CONTROL, at the selection boundary. required_floor_runner floor_base_tree_tests floor_base_tree_is_the_departure_point_under_merge_base_and_the_base_under_two_dot builds a real history (the branch departs at P, then main moves to C) and drives FloorBaseTree. Under merge-base every base-tree reader is handed P; before this change, the readers read C. Under two-dot the base tree is C, and an unrelated history refuses FreezeBaselineUnrelatedHistory under merge-base. It runs git and nothing else, so it fits the v1 unit lane's per-test budget. At this head that lane is the rust-unit-tests job of .github/workflows/witnesses.yml, which the aggregate job needs; that job, not this sentence, is the standing of the control. WHAT IT REPLACED, recorded rather than silently dropped: this PR first carried two real-history controls that drove the selection through the judgment: roster_edit_is_judged_at_the_merge_base_not_the_base_tip, through unimported_bare_provider_edit_refusals, and gunbc#13332's cost_debt_row_retired_at_the_tip_but_carried_at_the_merge_base_is_not_admitted, through cost_debt_admitted_by_fold. Both passed locally at head 165da6683e. gunbc#13452 then deleted every v1 Rust test over 100ms that evaluates the .dag corpus, the second of those among them, so this change follows that ruling rather than re-adding them. The judgments are the .dag's own and are claimed there. For input (c) the join, meaning that the reader receives the selected commit, is executed by the cost-debt RED below. For (a) and (b) it rests on the reader signatures in (1), which take BaseTreeCommit or FloorBaseTree. Input (b) shares the commit and type, and has no discriminating control of its own. (3) NO .dag CONTROL IS REACHABLE for the defect itself. Which commit is the base side is decided on the host, and a .dag claim receives a roster already read. A claim over v2.workflow.floor_unimported_bare_provider_debt's edit judgment would only re-check the judgment, which v2.test.claim.floor_unimported_bare_provider_debt_test edit_refuses_a_rewritten_cause_and_a_reverted_retirement already executes on the floor. Its next-rung trigger is the window resolution moving into v2.workflow.floor_diff_observe. INPUT (c), COVERED AFTER gunbc#13332 LANDED. That PR carried its own per-site arm, cost_debt_comparison_base_commit, which this row's trigger says does not discharge it. As agreed with its author (crisp-ram-667), the PR that landed second converted it: the helper is deleted, cost_debt_admitted_identities takes FloorBaseTree, and the base-tree archive takes the BaseTreeCommit. cost_debt_roster_head_closure reads only the head tree and is not a base reader. That PR's RED, cost_debt_row_retired_at_the_tip_but_carried_at_the_merge_base_is_not_admitted, was deleted with the slow v1 tests by gunbc#13452. A fix may not land after its discriminating RED is gone, so the RED is re-established at a grain that runs, required_floor_runner floor_base_tree_tests cost_debt_row_retired_at_the_tip_but_carried_at_the_merge_base_is_not_admitted. It drives cost_debt_admitted_at_base_tree, the selection cost_debt_admitted_identities runs, through FloorBaseTree over a real history, with the roster read by git show and the .dag fold's set difference supplied, so no corpus is evaluated. A row main retired after the branch point is not admitted; the POSITIVE CONTROL, a row the branch really adds, is admitted. MUTANT, executed remotely at gunbc#13347 head 401bd7249a: FloorBaseTree resolving a two-dot window (the tip) instead of the comparison's own makes this RED fail, admitting [t.retired], and makes the selection control fail too, base tree = tip rather than departure point. Unmutated, both pass in 0.05s. No floor helper resolves its own merge base any more.",
],
evidence: [],
}
Loading