Engagement: ErikEvenson/galaxy#1397 (LEG-2 live-dock freshness wall)
Reviews: implementer SP1 gist d0466bd3028ddea4ac9a49306d6c3f04 ([implementer-publish] on #1397)
Reviewer prior: 20b2d5d7 (§2-B framing, risks i/ii/iii)
HEAD verified: c5a7e750d376641ab2b6c6299cb574cb9792daf9 (main, clean)
Artifact: SP1 design + control-test-plan review (no code, no cluster mutation)
Ball after this: IMPLEMENTER (SP2 — implement B-minimal + run CT-1/2/3, control-test-first, CT-3 dispositive)
RATIFY SP1. Design verified against code (high confidence). CT-3 is honest as specified — ratified with two explicit sharpenings (S1, S2 below) that make it exercise the actual adaptive-Δ code path rather than a proxy. B-minimal ratified as the smallest extension and confirmed milestone-compatible. Contingency-C stays parked for post-CT-3-RED, not now. SP2 routed to the implementer with one added hard condition (R7 crash-watchdog).
The implementer did the right thing: FEASIBLE-PENDING-MEASUREMENT, one dispositive gate isolated
(CT-3 / risk i), no force-fit GREEN. Concur.
| SP1 claim | Verified | Evidence |
|---|---|---|
| forward-prop primitive exists, Δ=90 s | ✅ | planner.py:6730-6739 _propagate_test_particle_via_oracle(r_ship,v_ship, SCHEDULE_SNAPSHOT_DEFERRAL_S,…), ship + target |
| oracle built with horizon covering Δ | ✅ | planner.py:6696 horizon_s = max_planning_horizon_s + SCHEDULE_SNAPSHOT_DEFERRAL_S |
| same 28-body integration, NOT lossy extrapolation | ✅ strong | planner.py:2492 _propagate_test_particle_via_oracle calls physics_core.simulate_trajectory_encke with no burns; docstring: "bit-identical to a no-thrust ship integrated by the cost function over the same horizon" — the same Encke + perturber-trajectory the cost function uses. This is the load-bearing "not lossy" claim; confirmed at the kernel. |
| Δ is already fully parameterized — only the value is literal | ✅ | oracle-horizon (:6696), ship/tgt pre-prop (:6731,:6736), cost-slice offset post_prep_offset_idx = round(Δ/sub_dt) (:6773), consumer anchor new_start = request_gt + OFFSET (consumer.py:548-551) — all read the literal; making Δ per-request touches exactly these + the executor mirror |
| #1104/#1106 is why 90 s error ≈ 0 today | ✅ | planner.py:6716-6729 comment: pre-prop replaced two-body _kepler_step (5c) with oracle N-body prop, eliminating the 137 km #1104 drift; anchor consumer.py:548-575 = request.game_time + OFFSET (#1106) |
lock constant is game-engine state.py:664, not planner.py:597 |
✅ (SP1 refinement correct) | state.py:664 PLANNING_LOCK_TIMEOUT_REAL_S = 120.0; acquire forces time_scale→1.0 (state.py:699-705); cleanup prunes >120 s + restores saved scale (tick_loop.py:1316-1346) |
slack mechanism slack ≈ Δ − wall under the freeze |
✅ | producer stale-flag consumer.py:559 slack_s ≤ SCHEDULE_SLACK_FLOOR_S(0.0) → stale_on_arrival=True; game-time on both sides (time-scale-invariant) |
| #1378 refuse-to-arm gate to PRESERVE | ✅ | schedule_executor.py:200 is_stale_on_arrival, used :1425 → routes _STALE_REVALIDATE_REASON :1442 |
| three-copy deferral literal (single-source hazard) | ✅ | consumer.py:34 SCHEDULE_PUBLISH_OFFSET_S=90.0, planner.py:1222 SCHEDULE_SNAPSHOT_DEFERRAL_S=90.0, schedule_executor.py:173 SCHEDULE_DEFERRAL_BUDGET_S=90.0 — R4 per-request-field fix is right; precedent request.expected_time_scale (consumer.py:504-510) confirmed |
| ~16.4 km approach gate is geometry-scaled | ✅ | gates.py:52-62 approach_entry = max(APPROACH_ENTRY_FLOOR_M, APPROACH_ENTRY_FRACTION × 2π·target_a) |
Mechanism ratified: lever (B) = (1) carry Δ as one per-request deferral_s field (retires the
three-copy literal), (2) thread it through the four already-parameterized planner/consumer sites +
the executor mirror, (3) raise the lock timeout ≥ Δ so time_scale=1.0 spans the whole solve →
game_elapsed ≈ wall → slack ≈ Δ − wall > 0. This is a magnitude-extension of a shipping
primitive, not a new subsystem. Confidence: high — every load-bearing site checked at the kernel,
not the comment.
CT-3 decides feasible-vs-contingency-C, so it must be honest: independent ground truth, actual Δ, actual gate. Verdict: honest as specified, with two sharpenings to lock in.
The concern the tests-must-use-independent-ground-truth rule guards against: CT-3 comparing the planner's oracle against the planner re-running its own oracle. That is not what CT-3 does. I traced both integration paths:
- Forward-prop under test (planner):
_propagate_test_particle_via_oracle→simulate_trajectory_enckeagainst a frozen, one-shot pre-sampled perturber trajectory (oracle built once atrequest.game_time). - Ground truth (live game-engine — the authority the executor trusts): the tick loop advances
bodies via
physics_core.propagate_bodies_leapfrogplusreanchor_bodiesper-tick secular-phase correction (nbody.py:157), and advances the chaser via_update_orbital_object_encke→encke_substep_loopagainst those live-coupled, reanchored bodies, sub-stepping atMAX_PHYSICS_DT (0.5 s), re-resolving the Encke reference body per tick (simulation.py:926-1065).
These are genuinely divergent inputs over Δ, even though both bottom out on shared Rust kernels:
- frozen pre-sampled field vs live-coupled field — the oracle's perturber trajectory does NOT
get the live per-tick
reanchor_bodiessecular-phase nudge; the live engine does. Over 90 s this divergence is ~0 (the #1104/#1106 result); over ~500 s the un-reanchored leapfrog phase-walk is a primary error source — exactly risk (i)/(iii). - per-tick Encke reference re-resolution vs the planner's fixed one-shot frame.
- sub-step granularity — live
MAX_PHYSICS_DT=0.5 svs oraclesub_dt.
So CT-3's error ‖r_pred − r_actual‖ measures the real frozen-field-vs-live-coupled divergence at
the extended Δ. Not self-referential — ratified. (a) satisfied.
CT-3 runs at the dockable geometry and full Δ (~500 s). S1 (make explicit): the oracle in CT-3
MUST be built at horizon_s = max_planning_horizon_s + Δ (the §3.3 / planner.py:6696 adaptive-Δ
horizon), i.e. perturber_traj_pos must span the full ~500 s. If CT-3 reuses a 90-s-horizon oracle
the propagation clamps/runs out of samples and measures garbage. Building at horizon ≥ Δ makes CT-3
exercise the actual adaptive-Δ code path — which doubles as a smoke-test of the SP2 change, not a
proxy. (b) satisfied under S1.
Bar = the geometry-scaled ~16.4 km approach gate (gates.py:52). Correct. S2 (make explicit): the
live-engine ground-truth advance over Δ must be a pure no-thrust coast (no burns fire in
[t0, t0+Δ] — true by construction: the schedule hasn't armed; burns start at start_game_time = t0+Δ) and must be deterministic (paused cluster, fixed-dt ticks, no RNG — the DE seed is
planner-side only; the sim leapfrog+Encke is deterministic). Both conditions make r_actual@Δ the
reproducible authority and apples-to-apples with the no-burn test-particle prop. (c) satisfied under S2.
CT-3 disposition: honest and dispositive as specified; ratified with S1 (oracle horizon ≥ Δ) and S2 (deterministic pure-coast ground-truth advance) folded in. RED (error ≥ gate) → risk (i) fatal → HALT-and-publish contingency-C to Erik; do not tune Δ upward to chase it (that is the third-tweak trap R8, and Δ-chasing cannot fix a stale-in-position snapshot).
Ratify B-minimal for SP2. It is milestone-compatible. Here is the load-bearing reasoning:
The #1397 milestone is a continuous unobserved from-pad run. The apparent objection to B-minimal is its "~500 s real-time game freeze." That phrasing overstates the cost — and the correction is the whole call:
-
time_scale=1.0is NOT a pause; it is de-acceleration. A pause is_cached_paused→ the tick batch breaks and no ticks fire (tick_loop.py).time_scaleonly governs game-seconds per wall-second. At 1.0 the sim keeps ticking — bodies advance, the chaser keeps coasting, ~500 game- seconds still elapse over the ~500 s wall window. Game-time never stops. The run stays continuous. That is precisely why the forward-anchor pre-props the chaser forward by Δ≈500 s — because the chaser really does move through those 500 s. Continuity — the milestone's actual semantic — is preserved. -
The freeze is an already-shipped, already-milestone-accepted behavior — B-minimal only extends its duration.
acquire_planning_lockalready forcestime_scale→1.0for up to 120 s on every planning solve today (state.py:699-705, the #1104 compute-time-deviation bound). B-minimal introduces no new mechanism; it raises the timeout so the existing 1× window covers the ~500 s dockable solve instead of capping at 120 s. Since the demonstration is unobserved, the only visible effect is that the sim doesn't fast-forward during the single LEG-2 dockable solve — no observer is watching it not-accelerate, and no tick is skipped. -
B-minimal preserves #1378 trivially —
schedule_executor.pyuntouched; the snapshot stays honest by freeze AND forward-prop. Confidence: high on milestone-compatibility.
B-structural (decouple lock from time_scale, run at saved Nx during compute) is correctly deferred:
it re-opens the #1104 137 km exposure (Δ must cover wall × time_scale, and the operator can change
time_scale mid-solve — Δ becomes coupled to a live, mutable quantity), i.e. the risk-(ii) re-arch.
Adopt B-structural only if a future campaign genuinely needs the sim to keep accelerating
through the dockable solve. For the #1397 from-pad demo it buys nothing the milestone needs. Call:
B-minimal now; B-structural parked as a named follow-on, not a blocker.
Ratified scope for SP2, control-test-first:
-
Implement B-minimal: (a) add
request.deferral_s(per-request Δ, submitter-tagged by solve- class — dockable ≈ 550-600 s, fast ≈ 90 s; single source, retires the three-copy literal, mirrors theexpected_time_scaletag pattern); (b) thread Δ through the four planner/consumer sites (§3.3)- the
schedule_executor.py:173mirror + the determinism guard test; (c) raisePLANNING_LOCK_TIMEOUT_REAL_S≥ Δ for the dockable class only.
- the
-
R7 is a hard condition, not optional (I verified it): the 120-s prune in
_planning_lock_cleanup_loop(tick_loop.py:1316-1346) is also the planner-crash recovery ("a hard planner crash leaves the lock held forever"). Raising the timeout to ~600 s must NOT defeat crash recovery. SP2 must keep a separate hard crash-watchdog distinct from the freshness Δ — e.g. a planner-thread liveness heartbeat, ormax(Δ, wall_ceiling)+bufferas a distinct crash timeout — so a genuinely hung planner is still reclaimed. Do not let the freshness budget swallow the crash budget. -
Run the control tests, CT-3 dispositive: CT-1 (fresh-AND-dockable flip stale→fresh solely by Δ), CT-2 (#1378 stale-fire PRESERVED under the parameterized-Δ path —
test_freshness_gate_1378stays GREEN), CT-3 with S1 + S2 folded in (frozen-oracle prop vs live-engine deterministic pure-coast advance at horizon ≥ Δ, vs the 16.4 km gate). CT-3 GREEN → the live dock (coordinator's step, trips the sudo gate — operator auth). CT-3 RED → HALT-and-publish contingency-C to Erik (C1 offline-dock+segment-rescope / C2 fund-lock-re-arch); do not Δ-chase (R8). -
Third-tweak guard (R8): Δ is a new structural mechanism, not a knob re-tune — first adjustment is clean. But a further adjustment of Δ or the lock timeout inside SP2 fires the third-tweak rule → HALT + publish rather than tune.
No escalation. Design is sound, CT-3 can be made honest (and is, under S1/S2), B-minimal is milestone-safe. Contingency-C remains parked for a CT-3 RED, per the implementer's own gate.
- Reviewer artifact only — no galaxy-repo edits, no cluster mutation.
- Every load-bearing SP1 site independently verified at HEAD
c5a7e750d(§2), incl. the "not lossy" claim at thesimulate_trajectory_enckekernel and the CT-3 ground-truth independence at both integration paths. - CT-3 scrutinized against tests-must-use-independent-ground-truth — confirmed independent (frozen pre-sampled field vs live reanchored coupled field), not self-referential.
- Fully-qualified
ErikEvenson/galaxy#NNNNrefs throughout. - B-minimal/B-structural adjudicated with an explicit milestone-semantics argument (continuity preserved; 1× ≠ pause).
- Did NOT self-close #1397. Ball → implementer for SP2.