Skip to content

feat(rate-of-closure): bind optimizer to full flight - #4289

Merged
dieterolson merged 12 commits into
feat/4197-capability-observerfrom
feat/4197-capability-flight-evaluator
Aug 9, 2026
Merged

feat(rate-of-closure): bind optimizer to full flight#4289
dieterolson merged 12 commits into
feat/4197-capability-observerfrom
feat/4197-capability-flight-evaluator

Conversation

@d-sorgcodexagent

Copy link
Copy Markdown
Contributor

Summary

  • bind player-capability-profile/v1 and capability requests to the real Waterloo/Penner forward-flight model in Python and React
  • validate units, clubs, safe bounds, physical domains, sampling, target-frame signs, and explicit per-club spin provenance before integration
  • return the complete available ball-flight-result/v1 scalar set with typed complete/nonconverged/failed outcomes
  • centralize gyro-projected spin-axis tilt across result, impact, and variation producers
  • add a shared 16-metric parity fixture and update the root, campaign, Rate, specification, and contract handoffs

Contract decisions

  • no hidden global driver spin fallback; three-variable profiles require a sourced spin default for every requested club
  • positive spin-axis tilt means fade/right in the target frame
  • Python records adaptive SciPy RK45 and React records fixed-step RK4; parity uses published tolerance bands, not bitwise identity
  • this post-impact evaluator cannot fabricate no_impact

Validation

  • Python shared flight suite: 138 passed, 4 optional-Rust skips
  • React suite: 97 files, 597 tests passed
  • Ruff check and format, targeted mypy, TypeScript type-check, zero-warning ESLint: passed
  • Vite production build: passed, 176 modules transformed
  • independent final review: no P0/P1/P2 blockers
  • staged diff/secret pattern/structural budget checks: passed

Stack

This draft intentionally targets feat/4197-capability-observer at 49612946138b1021f80c9f8d2a4d06f1610825db (PR #4283). Do not retarget or merge it out of order.

Progresses #4197. The issue remains open for the PyQt6/React authoring, persistence, worker, observation visualization, and release work.

dieterolson and others added 4 commits August 8, 2026 06:16
…n workspaces

Expose the qualified Waterloo/Penner capability optimizer as a primary
Shot Optimizer module in both clients: strict cross-runtime
capability-optimization-workflow/v1 authoring and persistence,
off-UI-thread execution with truthful progress and cooperative
cancellation, complete scalar-ensemble/v1 cohort retention, ranked
alternatives, stage-qualified scatter axes, bounded raw-row paging,
spreadsheet-safe CSV, and stable JSON. The React panel is lazy-loaded
so the main Vite chunk stays under the 500 kB warning threshold, and
saved v1 layouts reveal newly registered modules. Handoff documents are
updated in this implementation commit per fleet policy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t reveal

Add PyQt workflow/worker/GUI suites and React workflow, worker-client,
run, panel, and results tests for the Shot Optimizer slice; extend
primary-navigation coverage so saved layouts reveal newly registered
modules and the new tab registers exactly once in both clients.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… gates

Add the SPEC 1.14.6 change-log row, document the end-user workflow in
the capability optimization spec, and update the campaign handoff with
the recovered slice and its verified gate evidence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No material source change; handoff pointer update only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dieterolson and others added 4 commits August 8, 2026 10:30
…cess

The dict-splat construction of CapabilityWorkflowInputs failed the
hosted delta-mypy gate because **dict[str, float] cannot be proven
against the integer fields. Build the snapshot with explicit typed
keyword arguments instead; behavior is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Handoff update for implementation commit 101020b per fleet policy;
no source change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The prior note wrongly framed the 3.10 StrEnum ImportError as a
stack-wide defect to fix in source. requires-python is >=3.11 and only
quality-gate and tests (3.11) are required checks, so the 3.10 lane is
a stale-matrix artifact. No source change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The observation contract has only complete/no_impact/failed members, so a
horizon nonconvergence is normalized into failed. Reporting a bare
"failed N" reads as numerical breakage when the retained reason says the
flight simply did not reach the ground inside the configured horizon.
Both clients now append the retained reason codes with counts, which is
data the adapter already carries per row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dieterolson

Copy link
Copy Markdown
Collaborator

Defect found from the stacked child (#4294): the TypeScript evaluator throws untyped exceptions on ~12.5% of a default driver search.

While verifying the Shot Optimizer UI against this evaluator, the React runtime reported 12 of 96 samples as evaluator_exception where Python reported zero. Deterministic reproduction against the default workflow document:

Python  : complete 72 | failed 24 (all no_ground_crossing_before_max_time)
React   : complete 66 | failed 18 (no_ground_crossing_before_max_time) | failed 12 (evaluator_exception)

Root cause, from wrapping the evaluator to un-suppress the message:

RangeError: timeS must be nonnegative
  at validateInputs (src/rate_of_closure/web/src/model/ballFlightMetrics.ts:110)
  sample = {ball_speed: 83.24, launch_angle: -4.32, launch_direction: 9.21}
  sample = {ball_speed: 85.84, launch_angle: -3.17, launch_direction: 10.25}

Every failing sample has a negative launch angle at high ball speed — legal inside the profile's declared safe bounds (launch_angle spans -10°..45°), so the search reaches them normally. The Python path handles the same samples without throwing.

Why this matters beyond the count:

  • It contradicts this slice's own spec text: "Metric sets, frames, signs, and typed statuses are parity contracts" — the same physical sample yields a typed status in Python and an untyped exception in TypeScript.
  • It also contradicts "Contract and programming errors surface instead of being silently counted as ordinary failed trials" — a RangeError from a validator is precisely a contract/programming error, and it is currently being absorbed into the ordinary failed cohort.
  • ballFlightMetrics.ts was last touched by c280407d4 (this PR's head), so it belongs to this slice rather than the observer parent or the UI child.

Not fixing it from the child branch — that would pull a parent-owned file into #4294's diff and cross the stack. Flagging it here so this slice can decide whether the trajectory builder should not be emitting a negative timeS for descending-launch cases, or whether the validator's precondition is wrong.

Reproduction note: the 12 exceptions are invisible in the compact result, which only exposes failed_count. They became visible because #4294 now prints the retained reason_code values alongside the counts.

dieterolson and others added 3 commits August 8, 2026 21:15
A descending launch starts at z=0 and is already below ground once the
t > dt guard permits the crossing check, so the interpolation ratio went
negative and extrapolated the crossing to a negative sample time. That
raised RangeError: timeS must be nonnegative, which the observation
layer absorbed as an untyped evaluator_exception on 12 of 96 samples of
a default driver search while Python reported none. Clamping the ratio
to [0, 1] keeps the crossing inside the detecting step, so these samples
report nonconverged and match the Python runtime.

Also builds _lerp_vector's 3-tuple explicitly instead of carrying an
unused type: ignore, and coerces _curve to float, clearing the hosted
delta-mypy gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brings in the evaluator branch descending-launch and delta-mypy repairs.
Resolves the two documentation conflicts by keeping both slices: SPEC.md
retains the 1.14.6 workspace row above the amended 1.14.5 evaluator row
at header version 1.14.6, and the tool handoff keeps the workspace
completion record ahead of the evaluator repair entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(rate-of-closure): matched PyQt6/React capability optimization workspaces
@dieterolson
dieterolson merged commit 9bbb98e into feat/4197-capability-observer Aug 9, 2026
15 checks passed
d-sorgcodexagent Bot pushed a commit that referenced this pull request Aug 9, 2026
The per-slice stacked-branch cautions described the stack while it was
open and now read as active constraints, so state plainly that #4283,
#4289, and #4294 are merged onto this branch, how they were folded, what
was verified here (1426 Python, 624 React), and that the incomplete CI
matrix reflects 25 of 28 org runners being offline rather than failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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