From 4cecf6d3395b44799a12744738cd8b2f2b15dd76 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 27 Sep 2026 22:44:10 +0000 Subject: [PATCH 1/4] Spark wet witnesses: supply the off-fleet executor instead of reading the runner's hostname MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fabric_capacity_standing_wet_witness and spark_pair_serving_apply_wet_witness asserted an "off fleet" refusal while observing the executing runner. srv1 is both a CI runner and a declared dashboard host (gunbc.roadmap_dashboard_instance), so on srv1 runners event_log_store_for_host resolves and both claims went red on unrelated PRs; they pass on srv3/srv4. The verdict was a function of runner assignment, not of the code. The executor is now supplied at the interface (DESIGN §3), shown first to have no declared placement, and the real authority-read route still executes from it. The capacity entry is split into fabric_capacity_standing_at so that route is reachable below the hostname observation. Expectations unchanged. Files recurring_failure_mode wet_witness_premise_read_from_the_runner_it_happens_to_land_on. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../instruments/fabric_capacity_standing.dag | 9 +++- ..._from_the_runner_it_happens_to_land_on.dag | 24 ++++++++++ ...ric_capacity_standing_wet_witness_test.dag | 46 +++++++++++++------ ...rk_pair_serving_apply_wet_witness_test.dag | 22 +++++---- 4 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag diff --git a/dag/gunbc/instruments/fabric_capacity_standing.dag b/dag/gunbc/instruments/fabric_capacity_standing.dag index 7b5ddc7e1db..fd22491c622 100644 --- a/dag/gunbc/instruments/fabric_capacity_standing.dag +++ b/dag/gunbc/instruments/fabric_capacity_standing.dag @@ -603,7 +603,14 @@ fn fabric_capacity_standing() -> ProcessExit ExecutorReachKnown { short_hostname: executor, path: _ } => match now_epoch_seconds() { Absent => exit_failure(reason: "fabric capacity standing: the clock could not be read as epoch seconds, so the lease standing of the authority cannot be derived") - Present { value: at } => fabric_capacity_standing_current(current: current_pair_serving_authorities(short_hostname: executor, at: at)) + Present { value: at } => fabric_capacity_standing_at(executor: executor, at: at) } } } + +// THE ENTRY BELOW ITS OBSERVATIONS: the executor and the instant are supplied, and the authority +// is still read from the log that executor resolves. A claim about an executor with no event log +// placement supplies one here instead of depending on which host happened to run it. +fn fabric_capacity_standing_at(executor: String, at: EpochSecs) -> ProcessExit { + fabric_capacity_standing_current(current: current_pair_serving_authorities(short_hostname: executor, at: at)) +} diff --git a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag new file mode 100644 index 00000000000..834a1e0b583 --- /dev/null +++ b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag @@ -0,0 +1,24 @@ +module gunbc.recurring_failure_mode.wet_witness_premise_read_from_the_runner_it_happens_to_land_on + +import std.types { NonEmptyStr } +import std.decl_ref { DeclarationRef, decl_ref } +import gunbc.recurring_failure_mode { RecurringFailureMode } + +data wet_witness_premise_read_from_the_runner_it_happens_to_land_on: RecurringFailureMode = RecurringFailureMode { + identity: "wet_witness_premise_read_from_the_runner_it_happens_to_land_on" as NonEmptyStr, + + receipts: [ + "**a wet witness's premise read from the runner it happens to land on** (a required-floor claim is ABOUT a host of one kind -- here, an executor with no event log placement -- but observes the executing runner's identity instead of supplying the subject, so its expected verdict is true only on the runners that happen to be of that kind; on any other runner it is red with no change to the code).", + + "SPECIMEN (2026-09-27, #12425 and other unrelated PRs). test.claim.spark.fabric_capacity_standing_wet_witness and test.claim.spark.spark_pair_serving_apply_wet_witness observed the runner's hostname and asserted every group's authority read refused 'off fleet'. The floor job passed on srv3/srv4 runners and failed (expected passed, observed failed) on srv1 runners: gunbc.roadmap_dashboard_instance declares a dashboard instance for srv1, so gunbc.fabric_event_log_host event_log_store_for_host RESOLVES there and the premise is false. Not a regression in the read and not flake: the verdict was a function of runner assignment.", + + "WHY IT IS A CLASS: a CI runner pool is not a neutral environment -- runners are fleet hosts, and which one a job lands on is scheduling, not an input the claim declares. The tell is a wet claim whose expectation names a property of the host ('off fleet', 'no dashboard', 'no GPU') while its subject is taken from observe_executor_reach or another observation of the executing machine. DESIGN §3: the subject belongs at the interface as a supplied value.", + + "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from it; the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims. CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", + ], + + evidence: [ + decl_ref(module_path: "gunbc.instruments.fabric_capacity_standing", decl_name: "fabric_capacity_standing_at"), + decl_ref(module_path: "gunbc.fabric_event_log_host", decl_name: "event_log_store_for_host"), + ], +} diff --git a/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag b/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag index f0c203c6be5..7d74b370240 100644 --- a/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag +++ b/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag @@ -2,26 +2,42 @@ module test.claim.spark.fabric_capacity_standing_wet_witness import std.logic { Bool } import std.types { String } +import v2.std.optional { Present, Absent } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import std.process { ExitSuccess, ExitFailure } -import gunbc.instruments.fabric_capacity_standing { fabric_capacity_standing } +import gunbc.fabric_event_log_host { now_epoch_seconds } +import gunbc.roadmap_dashboard_instance { DashboardInstanceForHost, DashboardInstanceHostUnknown, dashboard_instance_for_host } +import gunbc.instruments.fabric_capacity_standing { fabric_capacity_standing_at } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly -// THE LIVE ENTRY, EXECUTED. test.claim.spark.fabric_capacity_standing_witness drives the verdict -// over a SUPPLIED roster so the real group_report producer and its binding run hermetically; this -// file is the other half of DESIGN §3's pairing obligation, the one that cannot be hermetic: the -// entry reads the executor's hostname (shell.Exec.RunArgv, no mock_response), resolves its event -// log layout and reads every claimed group's authority from the log before any standing is -// judged. On the wet lane's runner that read does not reach a log -- the runner is no dashboard -// host, or its remote is unreachable -- so the honest verdict is a refusal that NAMES THE READ: -// every claimed group is reported "serving authority could not be read at ", and no -// group's capacity is stood for. That is the route: deleting the live binding, or letting an -// unreadable log fall back to the source row, changes this text. +// The executor this claim is about: a host with no event log placement. It is SUPPLIED (DESIGN §3, +// a witness discriminates at one interface), because the executor the runner observes is not the +// claim's subject -- srv1 is both a CI runner and a declared dashboard host, so observing it made +// the verdict a function of which runner took the job (gunbc.recurring_failure_mode +// wet_witness_premise_read_from_the_runner_it_happens_to_land_on). +data off_fleet_executor: String = "off-fleet-witness-executor" + +// THE LIVE ENTRY BELOW ITS OBSERVATIONS, EXECUTED. test.claim.spark.fabric_capacity_standing_witness +// drives the verdict over a SUPPLIED roster; this is the other half of DESIGN §3's pairing +// obligation: fabric_capacity_standing_at resolves the supplied executor's event log layout +// through the real host-store derivation and reads every claimed group's authority before any +// standing is judged. The executor is first shown to have no declared placement, so the refusal +// that NAMES THE READ -- "serving authority could not be read at " -- is the route and not +// an accident of the runner. Deleting the log read, or letting an unreadable log fall back to the +// source row, changes this text. test fn the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet() -> Bool { - match fabric_capacity_standing() { - ExitSuccess => false - ExitFailure { code: _, reason: reason } => - string_contains(s: reason, pattern: "group-a: serving authority could not be read at ") + match dashboard_instance_for_host(host: off_fleet_executor) { + DashboardInstanceForHost { instance: _ } => false + DashboardInstanceHostUnknown { host: _ } => + match now_epoch_seconds() { + Absent => false + Present { value: at } => + match fabric_capacity_standing_at(executor: off_fleet_executor, at: at) { + ExitSuccess => false + ExitFailure { code: _, reason: reason } => + string_contains(s: reason, pattern: "group-a: serving authority could not be read at ") + } + } } } diff --git a/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag b/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag index 871afd386c9..26110510ce9 100644 --- a/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag +++ b/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag @@ -5,28 +5,34 @@ import std.types { String, List } import v2.std.optional { Present, Absent } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import gunbc.fabric_event_log_host { now_epoch_seconds } -import gunbc.spark.fabric_reach { ExecutorReachKnown, ExecutorReachUnknown, observe_executor_reach } +import gunbc.roadmap_dashboard_instance { DashboardInstanceForHost, DashboardInstanceHostUnknown, dashboard_instance_for_host } import gunbc.spark.pair_serving_apply { spark_pair_apply_plans, spark_pair_apply_plan_refusals } import gunbc.spark.pair_serving_authority { spark_pair_serving_authorities } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly +// The executor this claim is about: a host with no event log placement, SUPPLIED rather than +// observed (DESIGN §3). srv1 is both a CI runner and a declared dashboard host, so observing the +// runner made the verdict a function of which runner took the job +// (gunbc.recurring_failure_mode wet_witness_premise_read_from_the_runner_it_happens_to_land_on). +data off_fleet_executor: String = "off-fleet-witness-executor" + // THE APPLY SEAM'S LIVE BINDING, EXECUTED. test.claim.spark.spark_pair_serving_apply_witness drives // the plan fold over a SUPPLIED roster; this is the other half of DESIGN §3's pairing obligation: -// spark_pair_apply_plans reads the executor's hostname, resolves its event log layout and reads -// every claimed group's authority from the log before planning anything. On the wet lane's -// runner that read does not reach a log, so every claimed group's plan is a refusal that NAMES +// spark_pair_apply_plans, given an executor with no declared placement (shown first), resolves its event log layout and reads +// every claimed group's authority from the log before planning anything. That read does not +// reach a log, so every claimed group's plan is a refusal that NAMES // THE READ -- "the serving authority could not be read" -- and nothing is planned from the // resting row. Deleting the log read, or letting an unreadable log fall back to the row, turns // these refusals into plans and this claim red. test fn the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet() -> Bool { - match observe_executor_reach() { - ExecutorReachUnknown { cause: _ } => false - ExecutorReachKnown { short_hostname: executor, path: _ } => + match dashboard_instance_for_host(host: off_fleet_executor) { + DashboardInstanceForHost { instance: _ } => false + DashboardInstanceHostUnknown { host: _ } => match now_epoch_seconds() { Absent => false Present { value: at } => { - let plans = spark_pair_apply_plans(executor: executor, at: at) + let plans = spark_pair_apply_plans(executor: off_fleet_executor, at: at) let refusals = spark_pair_apply_plan_refusals(plans: plans) length(plans) == length(spark_pair_serving_authorities) && length(refusals) == length(plans) From 959e0fdb7b33499d906160c96e8b7b77b1e73938 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 28 Sep 2026 01:03:07 +0000 Subject: [PATCH 2/4] Supply the instant too; the two claims are hermetic and leave the wet lane The floor refused both changed witnesses as a route gap: the hermetic changed-witness run reached Clock.UnixSecs. The claims do not depend on the clock (an unresolved store refuses before any lease standing is derived), so `at` is supplied like the executor. With nothing left to observe, the LocalRepoWetLane exclusions (gunbc.ci_layer_roots) and the local_repo_wet_schedule rows are deleted, exactly as those exclusions' dissolution condition named, and the claims run on the hermetic floor. Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/ci/ci_layer_roots.dag | 14 ----------- ..._from_the_runner_it_happens_to_land_on.dag | 2 +- ...ric_capacity_standing_wet_witness_test.dag | 22 ++++++++---------- .../fabric_capacity_standing_witness_test.dag | 2 +- ...rk_pair_serving_apply_wet_witness_test.dag | 23 +++++++++---------- .../spark_pair_serving_apply_witness_test.dag | 2 +- src/v2/workflow/local_repo_wet_terminal.dag | 12 ---------- 7 files changed, 24 insertions(+), 53 deletions(-) diff --git a/dag/gunbc/ci/ci_layer_roots.dag b/dag/gunbc/ci/ci_layer_roots.dag index 363147f23f7..7ebb5d4d967 100644 --- a/dag/gunbc/ci/ci_layer_roots.dag +++ b/dag/gunbc/ci/ci_layer_roots.dag @@ -382,10 +382,6 @@ data excl_local_repo_wet_bash_materialized_reason: String = "real host-effect ex data excl_local_repo_wet_bash_materialized_dissolve: DissolutionCondition = unbound_dissolution(description: "mock_response coverage lands for shell.Exec.Run over a sealed transport script and the emitted-bytes assertions are re-checked under a fixed mock -- which the transport_script_stdin_byte_fidelity note already refuses as vacuous for a byte-fidelity claim -- OR the bin-witness wet class regains an executing per-PR consumer; then these fns re-enroll there, drop off local_repo_wet_schedule, and this row deletes") -data excl_local_repo_wet_executor_identity_reason: String = "real host-effect execution witness whose effects are the EXECUTOR'S OWN IDENTITY AND EVENT-LOG READ: the instrument entry reads the executing host's hostname (shell.Exec.RunArgv, no mock_response), resolves that host's event log layout, and reads each claimed group's serving authority from the log before judging any standing (gunbc.instruments.fabric_capacity_standing fabric_capacity_standing over gunbc.spark.pair_serving_authority_log). No temporary repository is built and nothing is written; on a runner that is no dashboard host the read refuses at the layout step and the entry reports every group unread, which is the route the claim asserts. It is the wet half of the pairing obligation whose hermetic half is test.claim.spark.fabric_capacity_standing_witness over a supplied roster." - -data excl_local_repo_wet_executor_identity_dissolve: DissolutionCondition = unbound_dissolution(description: "mock_response coverage lands for shell.Exec.RunArgv and the event-log head read, so the instrument entry can be driven hermetically with a fixed executor and a fixed partition head; then the claim re-enrolls as an ordinary hermetic discovery row, drops off local_repo_wet_schedule, and this row deletes") - data excl_local_repo_wet_host_probe_reason: String = "real host-effect execution witness whose ONLY effect is a READ-ONLY HOST PATH PROBE (shell.PosixCommandV.Check, i.e. command -v) -- refused by the hermetic envelope (no mock_response for operation Check), so excluded from the discovery corpus and executed by the required floor's local-repo wet lane. THIS IS A DISTINCT ADMITTED EFFECT, NOT THE THROWAWAY-REPOSITORY PROPERTY excl_local_repo_wet_reason NAMES: these fns build no temporary repository and write nothing. What they share with that class, and what admits them to the same lane, is the negative half -- no network, no cargo, no remote host, no install media. THE VERDICT IS HOST-INDEPENDENT ON THE TOOL-PRESENCE AXIS, MEASURED RATHER THAN ASSUMED (2026-09-02, PR #10055): every arm of both fns returns the literal true, and the npm fn was executed on one host in both conditions -- probe found, and probe exit=127 with sh still resolvable -- returning the same verdict in each. So admitting them imports no host-conditional red. The residual red these fns CAN produce is route-unreachability (no shell at all: TypeError failed to execute sh), which is the same reachability risk every member of this lane already carries and is not specific to the probed tool. PROVENANCE CARRIED FORWARD from the bin-wet row this replaces: falsifier Codex materialization reuses the same PosixCommandV.Check.exists path (migrated off shell.Which.Check, crisp-wren-896, PR #8590)." data excl_local_repo_wet_host_probe_dissolve: DissolutionCondition = unbound_dissolution(description: "mock_response coverage lands for shell.PosixCommandV.Check, so observe_host_cli_dependency can be asserted under a fixed mock; then these fns re-enroll as ordinary hermetic discovery rows, drop off local_repo_wet_schedule, and this row deletes") @@ -977,16 +973,6 @@ data witness_exclusion_frontier: List = [ classification: LocalRepoWetLane, reason: excl_local_repo_wet_reason, dissolution: excl_local_repo_wet_dissolve}, - WitnessExclusionRow { - pattern: "fabric_capacity_standing_wet_witness_test.dag", - classification: LocalRepoWetLane, - reason: excl_local_repo_wet_executor_identity_reason, - dissolution: excl_local_repo_wet_executor_identity_dissolve}, - WitnessExclusionRow { - pattern: "spark_pair_serving_apply_wet_witness_test.dag", - classification: LocalRepoWetLane, - reason: excl_local_repo_wet_executor_identity_reason, - dissolution: excl_local_repo_wet_executor_identity_dissolve}, WitnessExclusionRow { pattern: "devboot_text_blob_real_execution_witness_test.dag", classification: LocalRepoWetLane, diff --git a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag index 834a1e0b583..63a4b572336 100644 --- a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag +++ b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag @@ -14,7 +14,7 @@ data wet_witness_premise_read_from_the_runner_it_happens_to_land_on: RecurringFa "WHY IT IS A CLASS: a CI runner pool is not a neutral environment -- runners are fleet hosts, and which one a job lands on is scheduling, not an input the claim declares. The tell is a wet claim whose expectation names a property of the host ('off fleet', 'no dashboard', 'no GPU') while its subject is taken from observe_executor_reach or another observation of the executing machine. DESIGN §3: the subject belongs at the interface as a supplied value.", - "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from it; the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims. CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", + "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and so is the instant, and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from them; with nothing left to observe the claims are hermetic, so their LocalRepoWetLane exclusions in gunbc.ci_layer_roots and their rows in v2.workflow.local_repo_wet_terminal were deleted exactly as those exclusions' dissolution condition named, and they run on the hermetic floor; the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims (their verdict no longer depends on any host fact). CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", ], evidence: [ diff --git a/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag b/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag index 7d74b370240..6f08bbaa131 100644 --- a/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag +++ b/dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag @@ -1,11 +1,9 @@ module test.claim.spark.fabric_capacity_standing_wet_witness import std.logic { Bool } -import std.types { String } -import v2.std.optional { Present, Absent } +import std.types { String, EpochSecs } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import std.process { ExitSuccess, ExitFailure } -import gunbc.fabric_event_log_host { now_epoch_seconds } import gunbc.roadmap_dashboard_instance { DashboardInstanceForHost, DashboardInstanceHostUnknown, dashboard_instance_for_host } import gunbc.instruments.fabric_capacity_standing { fabric_capacity_standing_at } @@ -18,7 +16,11 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // wet_witness_premise_read_from_the_runner_it_happens_to_land_on). data off_fleet_executor: String = "off-fleet-witness-executor" -// THE LIVE ENTRY BELOW ITS OBSERVATIONS, EXECUTED. test.claim.spark.fabric_capacity_standing_witness +// The instant is supplied too: a store that does not resolve refuses before any lease standing is +// derived from it, so the claim does not depend on the clock. +data supplied_at: EpochSecs = 1790000000 + +// THE LIVE ENTRY BELOW ITS OBSERVATIONS, EXECUTED HERMETICALLY. test.claim.spark.fabric_capacity_standing_witness // drives the verdict over a SUPPLIED roster; this is the other half of DESIGN §3's pairing // obligation: fabric_capacity_standing_at resolves the supplied executor's event log layout // through the real host-store derivation and reads every claimed group's authority before any @@ -30,14 +32,10 @@ test fn the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet( match dashboard_instance_for_host(host: off_fleet_executor) { DashboardInstanceForHost { instance: _ } => false DashboardInstanceHostUnknown { host: _ } => - match now_epoch_seconds() { - Absent => false - Present { value: at } => - match fabric_capacity_standing_at(executor: off_fleet_executor, at: at) { - ExitSuccess => false - ExitFailure { code: _, reason: reason } => - string_contains(s: reason, pattern: "group-a: serving authority could not be read at ") - } + match fabric_capacity_standing_at(executor: off_fleet_executor, at: supplied_at) { + ExitSuccess => false + ExitFailure { code: _, reason: reason } => + string_contains(s: reason, pattern: "group-a: serving authority could not be read at ") } } } diff --git a/dag/test/claim/spark/fabric_capacity_standing_witness_test.dag b/dag/test/claim/spark/fabric_capacity_standing_witness_test.dag index d906547e8b7..861bf737243 100644 --- a/dag/test/claim/spark/fabric_capacity_standing_witness_test.dag +++ b/dag/test/claim/spark/fabric_capacity_standing_witness_test.dag @@ -316,7 +316,7 @@ test fn one_refusing_member_refuses_the_whole_standing() -> Bool { // authority log folds to when no transition is recorded -- so the claim stays hermetic while the // real group_report producer and the binding that maps it over the roster both execute. The // live entry, which reads that roster from the event log first, is executed by -// test.claim.spark.fabric_capacity_standing_wet_witness on the local-repo wet lane. +// test.claim.spark.fabric_capacity_standing_wet_witness over a supplied executor with no placement. fn w_resting_current() -> List { map(spark_pair_serving_authorities, a => CurrentAuthorityRead { authority: a, head: HeadAbsent, generation: 0, grant: none, entry_state: none, previous: none, admitted_by: none, transitions: [] as List }) } diff --git a/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag b/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag index 26110510ce9..95b666f6b96 100644 --- a/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag +++ b/dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag @@ -1,10 +1,8 @@ module test.claim.spark.spark_pair_serving_apply_wet_witness import std.logic { Bool } -import std.types { String, List } -import v2.std.optional { Present, Absent } +import std.types { String, List, EpochSecs } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.fabric_event_log_host { now_epoch_seconds } import gunbc.roadmap_dashboard_instance { DashboardInstanceForHost, DashboardInstanceHostUnknown, dashboard_instance_for_host } import gunbc.spark.pair_serving_apply { spark_pair_apply_plans, spark_pair_apply_plan_refusals } import gunbc.spark.pair_serving_authority { spark_pair_serving_authorities } @@ -17,6 +15,10 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // (gunbc.recurring_failure_mode wet_witness_premise_read_from_the_runner_it_happens_to_land_on). data off_fleet_executor: String = "off-fleet-witness-executor" +// The instant is supplied too: a store that does not resolve refuses before any lease standing is +// derived from it, so the claim does not depend on the clock. +data supplied_at: EpochSecs = 1790000000 + // THE APPLY SEAM'S LIVE BINDING, EXECUTED. test.claim.spark.spark_pair_serving_apply_witness drives // the plan fold over a SUPPLIED roster; this is the other half of DESIGN §3's pairing obligation: // spark_pair_apply_plans, given an executor with no declared placement (shown first), resolves its event log layout and reads @@ -29,15 +31,12 @@ test fn the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet match dashboard_instance_for_host(host: off_fleet_executor) { DashboardInstanceForHost { instance: _ } => false DashboardInstanceHostUnknown { host: _ } => - match now_epoch_seconds() { - Absent => false - Present { value: at } => { - let plans = spark_pair_apply_plans(executor: off_fleet_executor, at: at) - let refusals = spark_pair_apply_plan_refusals(plans: plans) - length(plans) == length(spark_pair_serving_authorities) - && length(refusals) == length(plans) - && all(refusals, r => string_contains(s: r, pattern: "the serving authority could not be read")) - } + { + let plans = spark_pair_apply_plans(executor: off_fleet_executor, at: supplied_at) + let refusals = spark_pair_apply_plan_refusals(plans: plans) + length(plans) == length(spark_pair_serving_authorities) + && length(refusals) == length(plans) + && all(refusals, r => string_contains(s: r, pattern: "the serving authority could not be read")) } } } diff --git a/dag/test/claim/spark/spark_pair_serving_apply_witness_test.dag b/dag/test/claim/spark/spark_pair_serving_apply_witness_test.dag index 6bdc6e92b90..0e3f614f67b 100644 --- a/dag/test/claim/spark/spark_pair_serving_apply_witness_test.dag +++ b/dag/test/claim/spark/spark_pair_serving_apply_witness_test.dag @@ -14,7 +14,7 @@ import product.capacity.event_chain { HeadAbsent } // THE CURRENT AUTHORITY IS SUPPLIED: the resting row of every claimed group at generation 0, which // is exactly what the log folds to when no transition has been recorded. The live read of the log -// is executed by test.claim.spark.spark_pair_serving_apply_wet_witness on the local-repo wet lane. +// is executed by test.claim.spark.spark_pair_serving_apply_wet_witness over a supplied executor with no placement. fn w_current() -> List { map(spark_pair_serving_authorities, a => CurrentAuthorityRead { authority: a, head: HeadAbsent, generation: 0, grant: none, entry_state: none, previous: none, admitted_by: none, transitions: [] as List }) } diff --git a/src/v2/workflow/local_repo_wet_terminal.dag b/src/v2/workflow/local_repo_wet_terminal.dag index 9b829861165..9dcc0007002 100644 --- a/src/v2/workflow/local_repo_wet_terminal.dag +++ b/src/v2/workflow/local_repo_wet_terminal.dag @@ -1045,18 +1045,6 @@ fn local_repo_wet_schedule() -> List { function: "a_rerun_resumes_its_own_pending_state_and_restores_the_exact_active_by_real_execution", expectation: ExpectedToHold {} }, - WetScheduledClaim { - identity: WitnessIdentity { module_path: "test.claim.spark.fabric_capacity_standing_wet_witness", function: "the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet" }, - entry: "dag/test/claim/spark/fabric_capacity_standing_wet_witness_test.dag", - function: "the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet", - expectation: ExpectedToHold {} - }, - WetScheduledClaim { - identity: WitnessIdentity { module_path: "test.claim.spark.spark_pair_serving_apply_wet_witness", function: "the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet" }, - entry: "dag/test/claim/spark/spark_pair_serving_apply_wet_witness_test.dag", - function: "the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet", - expectation: ExpectedToHold {} - }, WetScheduledClaim { identity: WitnessIdentity { module_path: "test.claim.spark.pair_serving_d0_real_execution", function: "a_fence_and_a_restore_each_reach_their_terminal_by_real_execution" }, entry: "dag/test/claim/spark/pair_serving_d0_real_execution_witness_test.dag", From b1cfc7568e86ebd3f4e4e50251e06fa565fa7be1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 28 Sep 2026 01:30:23 +0000 Subject: [PATCH 3/4] Admit the two spark route claims in the required gate by name, with a control Deleting their wet-lane rows left them outside required_gate_prefixes and required_gate_authored_modules, so they would have run only when they changed. The two modules join required_gate_authored_modules (named modules, no spark prefix; parent ruling msg_fe97729b option (b)). v2.test.required_floor_gate_selector the_spark_authority_read_route_claims_are_planned_without_changing asserts both real identities are Planned (not PlannedAsChangedWitness) through required_floor_site_disposition; it is in the v2.test. family, so it runs on every required floor, and it goes red locally with either row removed. Co-Authored-By: Claude Opus 5.5 (1M context) --- ..._from_the_runner_it_happens_to_land_on.dag | 2 +- .../required_floor_gate_selector_test.dag | 36 +++++++++++++++++++ src/v2/workflow/required_floor.dag | 12 +++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag index 63a4b572336..70d6bace0f1 100644 --- a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag +++ b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag @@ -14,7 +14,7 @@ data wet_witness_premise_read_from_the_runner_it_happens_to_land_on: RecurringFa "WHY IT IS A CLASS: a CI runner pool is not a neutral environment -- runners are fleet hosts, and which one a job lands on is scheduling, not an input the claim declares. The tell is a wet claim whose expectation names a property of the host ('off fleet', 'no dashboard', 'no GPU') while its subject is taken from observe_executor_reach or another observation of the executing machine. DESIGN §3: the subject belongs at the interface as a supplied value.", - "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and so is the instant, and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from them; with nothing left to observe the claims are hermetic, so their LocalRepoWetLane exclusions in gunbc.ci_layer_roots and their rows in v2.workflow.local_repo_wet_terminal were deleted exactly as those exclusions' dissolution condition named, and they run on the hermetic floor; the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims (their verdict no longer depends on any host fact). CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", + "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and so is the instant, and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from them; with nothing left to observe the claims are hermetic, so their LocalRepoWetLane exclusions in gunbc.ci_layer_roots and their rows in v2.workflow.local_repo_wet_terminal were deleted exactly as those exclusions' dissolution condition named, and the transfer is completed by admitting both modules by name in v2.workflow.required_floor required_gate_authored_modules, so they are PLANNED on every required run and not only when they change (control: v2.test.required_floor_gate_selector the_spark_authority_read_route_claims_are_planned_without_changing, red with either row removed); the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims (their verdict no longer depends on any host fact). CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", ], evidence: [ diff --git a/src/v2/test/required_floor_gate_selector_test.dag b/src/v2/test/required_floor_gate_selector_test.dag index fb777db3f2b..3f1e5e46061 100644 --- a/src/v2/test/required_floor_gate_selector_test.dag +++ b/src/v2/test/required_floor_gate_selector_test.dag @@ -85,3 +85,39 @@ test fn the_match_names_the_kind_that_admitted() -> Bool { } } } + +// THE SPARK AUTHORITY-READ ROUTE CLAIMS ARE PLANNED ON A RUN WHERE THEY DID NOT CHANGE +// (gunbc#12461, parent ruling msg_fe97729b). Supplying their executor and instant made them pure, +// so they left the local-repo wet schedule; this establishes that their real identities are +// PLANNED by the gate itself -- not PlannedAsChangedWitness, not declined as cost debt -- so the +// pairing obligation's real-path half keeps executing where merges are decided. Deleting either +// row from required_gate_authored_modules turns this red on every required run, which a claim +// inside those modules could not do: it would run only when it changed. +test fn the_spark_authority_read_route_claims_are_planned_without_changing() -> Bool { + match required_floor_site_disposition( + module_path: "test.claim.spark.fabric_capacity_standing_wet_witness", + identity: "test.claim.spark.fabric_capacity_standing_wet_witness.the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet" + ) { + Planned => true + DeclinedLongModule { matched_prefix: _ } => false + DeclinedFixtureMember { matched_prefix: _ } => false + DeclinedOutsideRequiredGate => false + DeclinedCostDebt => false + DeclinedOutsideGateClosure => false + DeclinedDiscoveryExcluded { matched_substring: _ } => false + PlannedAsChangedWitness => false + } + && match required_floor_site_disposition( + module_path: "test.claim.spark.spark_pair_serving_apply_wet_witness", + identity: "test.claim.spark.spark_pair_serving_apply_wet_witness.the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet" + ) { + Planned => true + DeclinedLongModule { matched_prefix: _ } => false + DeclinedFixtureMember { matched_prefix: _ } => false + DeclinedOutsideRequiredGate => false + DeclinedCostDebt => false + DeclinedOutsideGateClosure => false + DeclinedDiscoveryExcluded { matched_substring: _ } => false + PlannedAsChangedWitness => false + } +} diff --git a/src/v2/workflow/required_floor.dag b/src/v2/workflow/required_floor.dag index 7dc5363ded2..3b099ca74f4 100644 --- a/src/v2/workflow/required_floor.dag +++ b/src/v2/workflow/required_floor.dag @@ -769,6 +769,16 @@ fn required_gate_prefixes() -> List { // gunbc.live_deploy.served_slice_charge beyond what the runner-capacity rows already carry. The module // declares SubstrateInputsOnly. The first required run after admission is the priced receipt; read // its identity-grained disposition. +// +// THE EIGHTH AND NINTH ROWS ARE THE SPARK AUTHORITY-READ ROUTE CLAIMS (adhoc-0536e80f-be0, gunbc#12461; +// parent ruling msg_fe97729b from eager-owl-205, option (b)). They were scheduled on the local-repo +// wet lane because they observed the executing runner's hostname, and on srv1 -- a declared +// dashboard host -- that premise was false and they went red on unrelated PRs +// (gunbc.recurring_failure_mode wet_witness_premise_read_from_the_runner_it_happens_to_land_on). +// Supplying the executor and the instant made them pure, so they left the wet schedule; without a +// row here they would have run only when they themselves changed, and the pairing obligation's +// real-path half (DESIGN 3) would stop executing where merges are decided. Named modules, not a +// spark prefix. Both declare SubstrateInputsOnly. fn required_gate_authored_modules() -> List { [ "test.claim.runner_capacity_plan_witness", @@ -778,6 +788,8 @@ fn required_gate_authored_modules() -> List { "test.claim.emitted_crate_workspace_witness", "v2.test.claim.body_lowering.statement_let_bind", "test.claim.host_allocation_conservation", + "test.claim.spark.fabric_capacity_standing_wet_witness", + "test.claim.spark.spark_pair_serving_apply_wet_witness", ] } From fd44917a537eaeec860a377a91ffdc762a04bbc4 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 28 Sep 2026 02:30:31 +0000 Subject: [PATCH 4/4] Move the gate-admission control into its own module; leave required_floor_gate_selector as on main Editing v2.test.required_floor_gate_selector made every claim in it a changed witness, and its sibling the_match_names_the_kind_that_admitted assumes the withdrawn v2.test. prefix, so it went red (run 36366239387). The control now lives in v2.test.required_floor_spark_route_admission; the selector file is byte-identical to main. Local claim_batch: PASS with the gate rows, FAIL with both removed. That control is not itself in the required gate, and says so. Co-Authored-By: Claude Opus 5.5 (1M context) --- ..._from_the_runner_it_happens_to_land_on.dag | 2 +- .../required_floor_gate_selector_test.dag | 36 -------------- ...uired_floor_spark_route_admission_test.dag | 47 +++++++++++++++++++ 3 files changed, 48 insertions(+), 37 deletions(-) create mode 100644 src/v2/test/required_floor_spark_route_admission_test.dag diff --git a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag index 70d6bace0f1..dcbf11010ec 100644 --- a/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag +++ b/dag/gunbc/recurring_failure_mode/wet_witness_premise_read_from_the_runner_it_happens_to_land_on.dag @@ -14,7 +14,7 @@ data wet_witness_premise_read_from_the_runner_it_happens_to_land_on: RecurringFa "WHY IT IS A CLASS: a CI runner pool is not a neutral environment -- runners are fleet hosts, and which one a job lands on is scheduling, not an input the claim declares. The tell is a wet claim whose expectation names a property of the host ('off fleet', 'no dashboard', 'no GPU') while its subject is taken from observe_executor_reach or another observation of the executing machine. DESIGN §3: the subject belongs at the interface as a supplied value.", - "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and so is the instant, and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from them; with nothing left to observe the claims are hermetic, so their LocalRepoWetLane exclusions in gunbc.ci_layer_roots and their rows in v2.workflow.local_repo_wet_terminal were deleted exactly as those exclusions' dissolution condition named, and the transfer is completed by admitting both modules by name in v2.workflow.required_floor required_gate_authored_modules, so they are PLANNED on every required run and not only when they change (control: v2.test.required_floor_gate_selector the_spark_authority_read_route_claims_are_planned_without_changing, red with either row removed); the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims (their verdict no longer depends on any host fact). CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", + "REPAIR: the executor is SUPPLIED (a host shown first to have no declared placement through dashboard_instance_for_host), and so is the instant, and the real log-read route -- current_pair_serving_authorities over event_log_store_for_host -- still executes from them; with nothing left to observe the claims are hermetic, so their LocalRepoWetLane exclusions in gunbc.ci_layer_roots and their rows in v2.workflow.local_repo_wet_terminal were deleted exactly as those exclusions' dissolution condition named, and the transfer is completed by admitting both modules by name in v2.workflow.required_floor required_gate_authored_modules, so they are PLANNED on every required run and not only when they change (control: v2.test.required_floor_spark_route_admission the_spark_authority_read_route_claims_are_planned_without_changing, red with either row removed; that control is not itself in the gate); the fabric capacity entry was split into gunbc.instruments.fabric_capacity_standing fabric_capacity_standing_at so its reads-before-judging route is reachable below the hostname observation. RUNG found at 1 (the harm occurred: unrelated PRs blocked). NOW 2 for these two claims (their verdict no longer depends on any host fact). CEILING 2: whether a wet claim reads its subject from the executing machine is not yet a modeled fact of the wet roster. NEXT TRIGGER, capability not artifact: the wet lane schedule (v2.workflow.local_repo_wet_terminal) records, per scheduled claim, which host facts its verdict depends on, and refuses a claim whose expectation depends on a host fact the runner pool does not hold uniformly. FRONTIER: the placed-host arm (a real authority read on a host WITH a placement) is fleet state and is owed by a fleet lane, not the required floor.", ], evidence: [ diff --git a/src/v2/test/required_floor_gate_selector_test.dag b/src/v2/test/required_floor_gate_selector_test.dag index 3f1e5e46061..fb777db3f2b 100644 --- a/src/v2/test/required_floor_gate_selector_test.dag +++ b/src/v2/test/required_floor_gate_selector_test.dag @@ -85,39 +85,3 @@ test fn the_match_names_the_kind_that_admitted() -> Bool { } } } - -// THE SPARK AUTHORITY-READ ROUTE CLAIMS ARE PLANNED ON A RUN WHERE THEY DID NOT CHANGE -// (gunbc#12461, parent ruling msg_fe97729b). Supplying their executor and instant made them pure, -// so they left the local-repo wet schedule; this establishes that their real identities are -// PLANNED by the gate itself -- not PlannedAsChangedWitness, not declined as cost debt -- so the -// pairing obligation's real-path half keeps executing where merges are decided. Deleting either -// row from required_gate_authored_modules turns this red on every required run, which a claim -// inside those modules could not do: it would run only when it changed. -test fn the_spark_authority_read_route_claims_are_planned_without_changing() -> Bool { - match required_floor_site_disposition( - module_path: "test.claim.spark.fabric_capacity_standing_wet_witness", - identity: "test.claim.spark.fabric_capacity_standing_wet_witness.the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet" - ) { - Planned => true - DeclinedLongModule { matched_prefix: _ } => false - DeclinedFixtureMember { matched_prefix: _ } => false - DeclinedOutsideRequiredGate => false - DeclinedCostDebt => false - DeclinedOutsideGateClosure => false - DeclinedDiscoveryExcluded { matched_substring: _ } => false - PlannedAsChangedWitness => false - } - && match required_floor_site_disposition( - module_path: "test.claim.spark.spark_pair_serving_apply_wet_witness", - identity: "test.claim.spark.spark_pair_serving_apply_wet_witness.the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet" - ) { - Planned => true - DeclinedLongModule { matched_prefix: _ } => false - DeclinedFixtureMember { matched_prefix: _ } => false - DeclinedOutsideRequiredGate => false - DeclinedCostDebt => false - DeclinedOutsideGateClosure => false - DeclinedDiscoveryExcluded { matched_substring: _ } => false - PlannedAsChangedWitness => false - } -} diff --git a/src/v2/test/required_floor_spark_route_admission_test.dag b/src/v2/test/required_floor_spark_route_admission_test.dag new file mode 100644 index 00000000000..554618f3675 --- /dev/null +++ b/src/v2/test/required_floor_spark_route_admission_test.dag @@ -0,0 +1,47 @@ +module v2.test.required_floor_spark_route_admission + +import v2.workflow.required_floor { + RequiredFloorDisposition, + Planned, PlannedAsChangedWitness, DeclinedLongModule, DeclinedFixtureMember, + DeclinedOutsideRequiredGate, DeclinedOutsideGateClosure, DeclinedDiscoveryExcluded, + DeclinedCostDebt, + required_floor_site_disposition, +} +import v2.std.logic { Bool } +import v2.std.text { String } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +fn identity_is_planned(module_path: String, identity: String) -> Bool { + match required_floor_site_disposition(module_path: module_path, identity: identity) { + Planned => true + DeclinedLongModule { matched_prefix: _ } => false + DeclinedFixtureMember { matched_prefix: _ } => false + DeclinedOutsideRequiredGate => false + DeclinedCostDebt => false + DeclinedOutsideGateClosure => false + DeclinedDiscoveryExcluded { matched_substring: _ } => false + PlannedAsChangedWitness => false + } +} + +// THE SPARK AUTHORITY-READ ROUTE CLAIMS ARE PLANNED BY THE GATE, NOT ONLY WHEN THEY CHANGE +// (gunbc#12461, lane ruling msg_fe97729b). Supplying their executor and instant made them pure, so +// they left the local-repo wet schedule and were admitted by name in required_gate_authored_modules. +// This asserts their real identities are Planned -- not PlannedAsChangedWitness, not cost debt -- +// and it is red with either row removed. WHERE THIS CLAIM ITSELF RUNS: this module is not in the +// required gate, so it executes when it or its closure's owning module changes, not on every +// required run; enrolling it is the follow-up named in gunbc#12461. It is its own module because +// v2.test.required_floor_gate_selector carries sibling claims that assume the withdrawn v2.test. +// prefix and go red whenever that file is selected as changed. +test fn the_spark_authority_read_route_claims_are_planned_without_changing() -> Bool { + identity_is_planned( + module_path: "test.claim.spark.fabric_capacity_standing_wet_witness", + identity: "test.claim.spark.fabric_capacity_standing_wet_witness.the_live_entry_reads_the_authority_before_judging_and_refuses_off_fleet" + ) + && identity_is_planned( + module_path: "test.claim.spark.spark_pair_serving_apply_wet_witness", + identity: "test.claim.spark.spark_pair_serving_apply_wet_witness.the_apply_seam_reads_the_authority_before_planning_and_refuses_off_fleet" + ) +}