Skip to content

feat(rate-of-closure, #4103): port the flight integrator (the last blocking symbol) - #4542

Merged
dieterolson merged 1 commit into
mainfrom
feat/roc-flight-integrator
Aug 18, 2026
Merged

feat(rate-of-closure, #4103): port the flight integrator (the last blocking symbol)#4542
dieterolson merged 1 commit into
mainfrom
feat/roc-flight-integrator

Conversation

@dieterolson

Copy link
Copy Markdown
Collaborator

The last and hardest of the ten symbols that gated the React migration. I deferred this twice — in #4533 and #4541 — because main's in-file RK4 loop carries #4518's ground-crossing guard, and a naive swap would revert it.

What changes

  • flight.ts gains AngularFlightPoint, FlightSimulationOptions, simulateFlightWithOptions
  • FlightResult.trajectory widens from FlightPoint[] to AngularFlightPoint[]
  • The in-file RK4 loop moves to a new flightIntegrator.ts; both entry points delegate to it, so there is one integrator rather than two that can drift
  • Nine bindings the moved loop was the only user of are dropped from flight.ts

Why the swap does not revert #4518

main's guard:

if (pos[2] >= 0.0 && nextPos[2] < 0.0 && t > dt) {

The && t > dt was added because a descending launch starts below zero, tGround can land before zero, and a negative-time trajectory point makes the metric contract raise RangeError — surfacing a bad launch as an error rather than the nonconverged result it is.

The integrator's contact test:

if (currentGap <= 0 || nextGap > 0) return null;

with terminalGapM defaulting to position[2]. It requires currentGap > 0 strictly, so a launch starting at ground level yields a gap of zero and never records a crossing. That is structurally the same protection, expressed as a state condition rather than a time check.

This is not argued from the code alone. All 1,420 tests on main pass unchanged with the integrator swapped in — including wind.test.ts (the Python/TS parity tolerance from #4513) and the ball_flight_metrics_golden_v1 and inverse_flight_solver_golden_v1 Python-parity fixtures. Those goldens are the strongest available evidence that trajectory output is unchanged.

One new bound, no behaviour change

The integrator checks Math.ceil(maxTime / step) > MAX_FLIGHT_INTEGRATION_STEPS (50,000) upfront, keeping synchronous UI-thread RK4 work finite. The default 10 s at a 1 ms step is 10,000 steps, so no existing caller is affected.

What it unlocks

flightGroundTransfer (+260) and simulationTypes (+83) both need the angular state at landing that AngularFlightPoint carries. Their 15 tests land with them.

Two main-owned tests restored (again)

TorqueProfilePanel.test.tsx and PrimaryViewTabs.test.tsx are reverted to main's versions. Both are purely additive by line count yet fail against main's unchanged components — the recurring trap in this migration, and the reason the prune script now refuses to delete files present on origin/main.

Verification

  • tsc --noEmit — clean
  • eslint . — clean
  • vitest run1,435 passed across 176 files, 0 failed
  • npm run build — production Vite build succeeds
  • Staged diff: 6 files, zero deletions outside the integrator move
  • SPEC.md: exactly one insertion (row 1.17.36)

🤖 Generated with Claude Code

The last and hardest of the ten symbols blocking the React migration.
flight.ts gains AngularFlightPoint, FlightSimulationOptions and
simulateFlightWithOptions; FlightResult.trajectory widens to
AngularFlightPoint[]; and the in-file RK4 loop moves into a new
flightIntegrator.ts that both entry points delegate to, so there is one
integrator rather than two that can drift.

This was deferred twice because main's loop carries #4518's ground-crossing
guard and a naive swap would revert it. It does not: the integrator's
contact test requires currentGap > 0 strictly, so a launch starting at
ground level yields a gap of zero and never records a crossing --
structurally the same protection && t > dt provides.

That is not argued from the code alone. All 1,420 tests on main pass
unchanged with the integrator swapped in, including wind.test.ts and the
ball_flight_metrics_golden_v1 and inverse_flight_solver_golden_v1
Python-parity fixtures.

The integrator adds an upfront MAX_FLIGHT_INTEGRATION_STEPS = 50_000 bound
keeping synchronous UI-thread RK4 work finite; the default 10s at a 1ms
step is 10,000 steps, so no existing caller changes behaviour.

Unlocks flightGroundTransfer and simulationTypes, which need the angular
state at landing; their 15 tests land with them.

Verified: tsc clean, eslint clean, 1,435 tests across 176 files, production
build succeeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@dieterolson
dieterolson enabled auto-merge (squash) August 18, 2026 14:38
@dieterolson
dieterolson merged commit 571cfef into main Aug 18, 2026
15 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant