Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 9 additions & 0 deletions crates/engine/src/analysis/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,15 @@ fn map_delta<K: Ord + Copy>(
/// normalized. It does not consult a board predicate, but it DOES require the frames it
/// compares to be homogeneous in turn position, which is what "homogeneous" above now means
/// in full.
///
/// ⚠ SCOPE OF THAT REQUIREMENT β€” what this function READS, versus where the frames' sameness
/// comes from. It reads exactly two things: `ResourceVector::snapshot(&f.normalized)` and
/// `window_scope_from_cover_frames(..).phase_invariant`, and `phase_invariant` is
/// `turn_number` + `phase` + `extra_phases.is_empty()`. The sampler gate that mints the frames
/// also makes them homogeneous in `waiting_for`/`priority_player`, but THIS function never
/// looks at those two β€” basis A does, via `loop_states_equal_modulo_resources` β‡’
/// `loop_states_equal` β‡’ `impl PartialEq for GameState`. Do not cite `ring_delta_signature` as
/// the consumer of either field.
pub(crate) fn ring_delta_signature(state: &GameState) -> Option<(u32, ResourceVector)> {
let frames = state.loop_detect_ring.len();
// 2k + 1 with k >= 1.
Expand Down
82 changes: 68 additions & 14 deletions crates/engine/src/game/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1921,11 +1921,12 @@ fn bounded_cycle_offer(

// (4) CERTIFICATION β€” two bases, first match wins, NEVER combined.
//
// Basis A is a fifth copy of the ring `find_map` scan (`:481` the `On` reconcile, `:668`
// Path B, `:710` Path C, `:808` `find_live_loop_winner`). Recorded, not hidden: the repo
// already made this call at `find_live_loop_winner`'s own doc β€” "a deliberate, isolated
// copy … the `On` arm stays VERBATIM (byte-identity gate)" β€” and retargeting the four
// shipped walks would edit byte-identity-gated paths inside a feature commit. Newest
// Basis A is a fifth copy of the ring `find_map` scan. The other four are NAMED, not
// numbered β€” the `On` reconcile, Path B, Path C, `find_live_loop_winner` β€” because the
// coordinates that stood here had ROTTED onto unrelated code. Grep the symbols. Recorded,
// not hidden: `find_live_loop_winner`'s own doc already made this call β€” "a deliberate,
// isolated copy … the `On` arm stays VERBATIM (byte-identity gate)" β€” and retargeting the
// four shipped walks would edit byte-identity-gated paths inside a feature commit. Newest
// prior first: the most recent recurrence is the least extrapolation.
//
// TWO PARALLEL VECS OVER ONE INDEX SPACE. They are built from the same `VecDeque` in the
Expand Down Expand Up @@ -11174,14 +11175,47 @@ fn apply_action(
// and recomputes `priority_player` through `turn_control`'s authorized-submitter
// resolver β€” and it now runs BEFORE the record, exactly as the settle sampler in
// `pass_priority_once_with_pipeline` does. So a frame minted here carries the same
// `waiting_for`/`priority_player` pair a settle frame carries, and that homogeneity
// is load-bearing: `impl PartialEq for GameState` compares both fields and
// `normalize_for_loop` neutralizes neither, so a mixed ring would break
// `ring_delta_signature`'s turn-position conjunct. BLAST RADIUS IS THE RING ONLY β€”
// `apply_action_boundary` re-syncs the returned `wf` before the result leaves the
// engine, so the settled state is unchanged. MEASURED before the reorder: a
// `debug_assert_eq!` census on both fields reported 0 divergences over 18,486 lib +
// 4,487 integration rows, so this replaces a coincidence with a guarantee.
// `waiting_for`/`priority_player` pair a settle frame carries.
//
// WHICH CONSUMER THAT HOMOGENEITY IS FOR β€” and it is NOT `ring_delta_signature`. The
// claim #7005's first commit shipped ("a heterogeneous ring breaks `ring_delta_signature`'s
// turn-position conjunct, because `impl PartialEq for GameState` compares both
// fields") is FALSE at source: that function's body reads only
// `ResourceVector::snapshot(&f.normalized)` and
// `window_scope_from_cover_frames(..).phase_invariant`, and `phase_invariant` is
// `turn_number` + `phase` + `extra_phases.is_empty()`. Neither field is in it.
//
// The consumer that DOES read them is BASIS A β€” the ring scans that call
// `analysis::resource::loop_states_equal_modulo_resources(prior, state)` with `prior`
// a ring frame's `normalized` half and `state` the LIVE board. That chains to
// `types::game_state::loop_states_equal` β‡’ `impl PartialEq for GameState`, which
// compares BOTH `waiting_for` and `priority_player`, and neither `normalize_for_loop`
// nor `project_out_resources` neutralizes either. An answer-beat frame carrying an
// un-synced pair therefore compares UNEQUAL against a live board that has been
// synced, and basis A silently misses the recurrence the frame was minted to certify.
//
// BLAST RADIUS. Nothing this leaves in `state` survives to a consumer unrecomputed:
// `finish_action_boundary` runs the SAME `sync_waiting_for` over `result.waiting_for`
// (`:1171`) and copies the outcome back into the result (`:1189`), and the reorder
// never changes `ActionResult.waiting_for` itself. That is an argument about
// RE-DERIVATION, not reachability, because `apply_action_boundary` is not the only
// route: `inject_pinned_answer`'s three dispatches and `drive_loop_action_iteration`'s
// ten reach `apply_action` directly, and
// `apply_interaction_pre_reconciliation_for_life_safety` returns `raw.result` without
// ever calling `finish_action_boundary` (`apply_action_boundary_core`'s own comment at
// `:1119` records it). All three drive a CLONE β€” `drive_one_shortcut_cycle`'s `work`,
// the drive's `clone`, `preview_candidate_life_safety`'s `preview` β€” never the settled
// board. MEASURED pre-reorder by an instrumented `debug_assert_eq!` census over the
// full lib + integration corpus (per-site counts in PR #7005's history; one unit =
// one `record_loop_detect_sample` invocation): 0 at EITHER sampler where the sync
// changed either field, so this replaces a coincidence with a guarantee.
//
// ONE CONSEQUENCE OF USING THE SYNCHRONIZER RATHER THAN A RAW CLONE:
// `normalize_legacy_attach_waiting_for` can now edit `state.waiting_for` on this
// path, so it may differ from the returned `ActionResult.waiting_for`, where the raw
// clone made the two exactly equal. Benign β€” the boundary re-normalizes at `:1171`
// and copies back at `:1189`, and `inject_pinned_answer` fails closed on every prompt
// kind it has no pin producer for.
sync_waiting_for(state, &wf);
if answering_forced_window
&& !in_simulation_probe()
Expand Down Expand Up @@ -15457,7 +15491,27 @@ mod stage2_injector_tests {
// sha256 `a6d7f2f9d1e15de5…5cb032`, matching `117baa6a1:engine.rs:11549`, and
// still inside `begin_pending_trigger_target_selection` (opens :11400 here,
// :11271 at `b654513cb`).
"game/engine.rs:11549".to_string(),
//
// THIS PR (the basis-A prose correction), ON TOP OF `a6d1a0e62`: `:11549 β‡’
// :11583`, +34. LOCAL, not upstream β€” the CI-vs-local diagnosis in the header
// does not apply. engine.rs's entire delta this round is THREE COMMENT HUNKS
// and nothing else. Two sit ABOVE this producer β€” `@@ -1924,5 +1924,6 @@` in
// `bounded_cycle_offer` (+1: the rotted `:481/:668/:710/:808` sibling-scan
// coordinates replaced by symbol names) and `@@ -11177,8 +11178,41 @@` in
// `apply_action` (+33) β€” summing to +34, and 11549 + 34 = 11583 exactly. The
// third is THIS drift entry, below the producer, which therefore cannot move
// it (its own size is deliberately not arithmetic here β€” a self-counting
// entry restates itself every edit). A comment round cannot mint a prompt,
// and the census agrees β€” the two asserts above this one fired GREEN on the
// run that caught this (total still 37, partition still 5/7/25) and the other
// four entries did not move (`effects/` is untouched by this commit;
// `scoped_library_search.rs:452` re-read and sha256-confirmed in place).
// Identity re-established rather than assumed: line :11583 is byte-identical
// by sha256 (`8a544e878d3e77fb…`, the SAME prefix this log recorded for
// `:11549`) to `a6d1a0e62:engine.rs:11549`, and it is still inside
// `begin_pending_trigger_target_selection`, which moved by the same +34
// (opens :11400 β‡’ :11434).
"game/engine.rs:11583".to_string(),
],
"the five production producers, NAMED: the CR 603.5 gate in `resolve_chain_body` \
plus the two repeated-optional-payment drivers, the per-player acceptance cursor \
Expand Down
Loading
Loading