Rate of Closure remainder: club builder, impact tensor, flight, multi-view workspace, ground and web companion (consolidates 43 PRs) - #4466
Open
dieterolson wants to merge 619 commits into
Conversation
added 30 commits
August 11, 2026 01:36
This was referenced Aug 14, 2026
…rors My first local run passed because the verification venv had no PyQt6 or matplotlib, so mypy resolved those imports to Any under --ignore-missing-imports. CI has them installed and infers precise types, which inverted several diagnostics. The venv now carries CI's dependency set, and the local run reproduces CI exactly (23 -> 0). * Casts around Qt getters are replaced by annotated locals, which are correct whether the stubs return Any or a precise type, so this cannot flip again. * Keyword-splat dicts (course_scene style, plot_wizard common) are declared dict[str, Any] instead of being inferred as dict[str, object]. * One shared attribute had two incompatible declarations after the mixin union: the file-commands mixin declared _navigation_settings as QSettings while the navigation mixin declared the NavigationSettings protocol. Both mixins, the compositor and SimulationTab now use the protocol, which is all any of them needs (value/setValue) and which QSettings satisfies structurally. 144 PyQt tests over the affected surfaces pass.
dieterolson
disabled auto-merge
August 14, 2026 10:11
dieterolson
enabled auto-merge (squash)
August 14, 2026 10:11
added 5 commits
August 14, 2026 12:59
…solidation Three conflicts, all in the Bolt/Palette surface #4438 just landed: * motion_tabs.py — the two branches extracted *different* things from the same module: #4438 pulled the view code into motion_view.MotionViewMixin, while this branch pulled the palette and geometry helpers into motion_helpers. Kept both. The file is now based on main's post-extraction version, imports build_motion_colors/chain_path_length from motion_helpers instead of redefining them inline, and drops the rendering import that became unused. 1093 -> 1074 lines, so no duplicated helper and no module-budget pressure. * torque_utils.py and test_sidekick_ux_hardening.py — resolved to main's versions. The two torque implementations are behaviourally identical (both evaluate ascending coefficients; the merged pendulum test pins 2 + 3t), so main's Bolt form wins rather than re-imposing this branch's TorquePolynomial call. shared/python/swing_sim/torque_profiles still ships and is still consumed by the rate-of-closure torque workspace features. test_action_audit keeps timezone.utc with the noqa anchored on its own line, and the anchored grep for a top-level datetime UTC import is empty.
rate_of_closure.web_companion.runtime and web_authority.child both do a plain module-level 'import uvicorn', and tests/rate_of_closure imports them, so the whole pytest lane aborts at collection without it. uvicorn was declared only in pyproject's optional rate-of-closure-web extra, which CI does not install; CI installs from requirements.txt. Same class as the filelock gap, and the same failure that just took #4447 red. Found by walking the import graph from tests/ through first-party modules and diffing the unguarded external roots against requirements.txt plus the explicit pip lines in ci-standard.yml. The only other new-in-this-branch hits were starlette (a fastapi dependency CI installs) and mpl_toolkits (part of matplotlib), both already satisfied.
…e SPEC row The doc-union strategy that kept every family's SPEC and handoff content also concatenated every family's handoff narrative, leaving AGENT_HANDOFF.md at 4872 lines and src/rate_of_closure/AGENT_HANDOFF.md at 5091 — the exact opposite of the documented contract for those two files (current-state only, <=150 lines, history in git). Rewritten from the consolidated tree's real state at 85 and 101 lines, keeping what a next agent actually needs: the cross-client module-registry contract, the regional-ground plan ownership, the zero-size-canvas rule, the derived release-evidence chain, and the packaging rule that a module-level import in a collected test must be declared in requirements.txt. docs/development/RATE_OF_CLOSURE_CAMPAIGN_HANDOFF.md is deliberately left long: it is a campaign narrative referenced as evidence by the campaign manifest, not one of the two policy-governed handoff files. Adds the dated SPEC 1.14.96 change-log row this consolidation owes under the spec-check contract, and moves Last Spec Update to 2026-08-14 (it still read 2026-08-12 while newer rows had landed).
… contract test Merge conflicts: * .github/workflows/cross-repo-python-integration.yml and its contract test — resolved to main's versions, deliberately NOT unioned. #4480 narrowed the UpstreamDrift sparse scope to `src/shared` and deleted the pinned-submodule step, with a comment explaining that UpstreamDrift moved its consumed packages under src/shared/python and that `pip install -e .` resolves through hatchling's `packages = ["src"]`. Its test asserts set *equality* (`actual == REQUIRED_SPARSE_PATHS`), so re-adding this branch's wider scope or the submodule-init step would fail the very test #4480 fixed. The wide scope is the superseded side, not content worth preserving; nothing else references the removed step. The workflow is now byte-identical to main, keeping #4475's CI_RUNNER_MODE fallback and public-repo hosted routing. * AGENT_HANDOFF.md — kept this branch's policy-compliant rewrite and folded in main's new fact (#4406 drift-transfer diagnostics under UpstreamDrift epic #8551), 89 lines. * SPEC.md — unioned, newest first. canonical numeric JSON: the guard and the test contradicted each other and both arrived from this branch's own fold. Resolved in favour of the guard, and the test rewritten to assert the narrowed contract. Evidence that the strict default is the intended contract, not an accident: * The guard appears in three coordinated places — the float path, the integer path, and a named opt-out (`canonical_numeric_json_extended_floats`) whose docstring says "beyond the safe range while retaining safe integers". * A dedicated facade, src/rate_of_closure/variation/canonical_numeric_json.py, re-exports the extended encoder under the plain name for the one caller that needs it (capability-observation payloads carrying declared 1e20/1e21 magnitudes). That is how a contract gets narrowed while an established caller keeps working. * The strict encoder's only production consumer is regional_ground_authority_status.py — the browser-facing authority job-status wire, where a value above 2**53-1 would reach the browser as a different number. * The old test's own second case already asserted the +/-(2**53-1) boundary, i.e. the file half-agreed with the guard. The failing case was a pre-guard regression guard ("preserves established... domain") that the narrowing superseded. The default guard is untouched: loosening it would make every cross-runtime payload silently accept values JavaScript cannot represent, which is a contract that cannot fail. The test now asserts (a) the default path fails closed for out-of-range floats, (b) the opt-in path still serializes that domain exactly with no exponent notation, and (c) the opt-in path still refuses unsafe integers. canonical_numeric_float stays strict: its production consumers (ground contract_types, ground_playback_comparison) carry physical magnitudes far below the limit, so an unused opt-out would be speculative. Not fixed here: test_wind.py's abs=1e-12 cross-client tolerance is #4482's (~1600 ULP of accumulation under a different libm, not one rounding).
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidates the remaining 43 open
src/rate_of_closurePRs into one branch. They alledit the same tree, so separate PRs would conflict with one another and multiply CI by
~43x. Every stack was folded top-down (each child into its own parent, then the parent
upward) so no parent is left a slice behind, and the two non-tip carriers the lead flagged
are explicitly included: #4171 (
8067ca79aCAD export artifact validation, absent fromtip #4172) and #4309 (
f9afa9d96strict ground result playback, absent from tip #4312).bash verify_coverage.sh consolidated/rate-closure-remainder-2026-08-13 <all 43>reportssafe=43 NOT-covered=0— closing every superseded PR drops nothing.What this carries
Correctness findings fixed here (not merge mechanics)
These are defects the merge exposed. Each was measured, not assumed.
from datetime import UTCimports (rate_of_closure/ui/pyqt6/torque_profile_controller.py,tests/unit/sidekick/agent/test_action_audit.py) arrived with feat: Swing–Impact–Ball-Flight Simulation Platform (consolidated — epic #4103) #4119's chain.datetime.UTCdoes not exist on Python 3.10, which the test matrix still runs;
mainhas onlyversion-gated imports. Both reverted to
timezone.utc, and the# noqa: UP017isre-anchored onto the physical line that uses it so a format pass cannot displace it again.
KeyError: 'regional_surfaces', 78tests). The workspace-file family hard-coded a 10-entry PyQt-to-canonical tab map while
the ground families added three tabs.
CANONICAL_MODULE_IDSnow matches the Reactclient's
PRIMARY_VIEWSexactly (12 shared modules); the PyQt-only Ground Study tab isdeclared unshared, excluded from the cross-client document, and restored in place on
import rather than dropped. Python demanded 10 module ids while React validated 12 — the
two clients disagreed, so no workspace file could round-trip between them.
Matplotlib then built a degenerate axes transform and raised
LinAlgError: Singular matrixfromaxvline. The shared canvas now keeps the figure atleast one pixel, which protects every plot in the compositor rather than one call site.
Restored end to end:
SimulationConfigvalidates a qualified binding,SimulationRuncarries
club_assembly_usage, the pipeline feeds the bound head mass and inertia tensorto the impact solver and reports
not_usedon a miss,ManualSwingSourcedeclares itsselected-head attitude, and the TypeScript twin matches.
PyQt
ControlsPaneland therate_of_closure.clubfacade, so feat(rate_of_closure): export selected clubhead STL #4326-feat(rate-of-closure): bind club assembly to simulation #4341's exports wereunreachable (
club_artifact_uiimported six names the package no longer exposed).rolling_projectionprojected onto the request surface, not the active one — after aregion transition the no-slip projection used the surface the ball had left.
filelockis imported by 15 always-collected test modules but was declared only in anoptional extra, so
tests (3.11)would have failed at collection. Declared inrequirements.txt, which is what CI installs from.four-surface capability manifest was missing the five linked ground specifications
(now 24 active specs / 39 declarations), and carriers recorded a self-referential
head_shawheredocs/release's own contract test requires the immutableevidence_commit_sha.all pass now; the ground-playback comparison overlay and event markers, the chip
forgiveness objective controls, and the tracking-aware camera were reconciled across
families. The toolstrip had two anonymous
role="status"live regions; both now havedistinct accessible names.
.codex-worktreesgitlinks pointing atcommits that exist on no remote. Removed from the index (
git ls-files -s | grep ^160000is empty). The
.gitignoreline landed with CONS-A1.Structural finding worth recording
#4119 shares no development history with the other 42 PRs beyond the 2026-08-06 base: it
is a parallel, pre-refactor implementation of the same subsystem (monolithic
simulation_tab,kinetics,plotting/catalog,App.tsx), whereas the trunk had since split those modules.Its conflicts were therefore resolved toward the trunk's later structure, with #4119's unique
deliverables (the club-assembly/STL slices above) ported forward. The same applies at smaller
scale to the flight family (#4207/#4210 chains), whose files the trunk had already refactored.
Ground playback was implemented twice in parallel (#4390 line and #4305 chain); the
app-wired implementation is kept and the #4305 chain's comparison capability was ported into
it rather than discarded.
Verification performed locally
quality-gatewas reproduced with its exact pins, and every blocking step passes:check+format --checkover all 770changed non-excluded Python files: clean. 98 modules arrived formatted by newer ruff builds
and were normalised — that mismatch is what turned 🎨 Palette & Bolt Suite: Form Submission, Accessibility, SVG & CSV Optimizations #4429's gate red.
MYPYPATH='src;src/python/src'(semicolon: the:in CI's Linux exportparses as one nonexistent directory on Windows and makes mypy pass having checked nothing),
one invocation over all 545 changed non-test modules via
@listfile, numpy<2.4:137 errors -> Success.
bandit (
-ll -ii, 495 files), and the Module Size Budget step insidequality-gate(a dead duplicate helper had pushed
motion_tabs.pyto 1203 lines): all pass.tests/rate_of_closure: 1520 passed. The 30 remaining local failures are allModuleNotFoundError: ntsecuritycon— pywin32 is absent from this box and the code path isos.name == "nt"-guarded, so Linux CI skips it.tsc --noEmitclean, ESLint zero warnings, 1097 vitest tests pass, productionbuild succeeds.
Not included
The four PRs
d-sorgcodexagent[bot]opened during this work (#4451, #4452, #4453, #4465) aredeliberately left out: they are stacked on
codex/4433-club-explorer-camera, which is#4444's head inside the Morris/variation consolidation (#4447), not on any of these five
families. Absorbing them would pull that entire 26-PR chain into this branch and duplicate
content that #4447 already carries. They belong on top of #4447.
Closes #4111
Closes #4136
Closes #4143
Closes #4144
Closes #4148
Closes #4149
Closes #4150
Closes #4159
Closes #4160
Closes #4163
Closes #4164
Closes #4189
Closes #4192
Closes #4193
Closes #4194
Closes #4195
Closes #4196
Closes #4197
Closes #4198
Closes #4199
Closes #4200
Closes #4224
Closes #4225
Closes #4261
Closes #4264
Closes #4272
Closes #4273
Closes #4274
Closes #4275
Closes #4284
Closes #4300
Closes #4379
Closes #4380
Closes #4385
Part of #4103
Part of #4104
Part of #4105
Part of #4106
Part of #4107
Part of #4108
Part of #4109
Part of #4110
Part of #4120
Part of #4125
Part of #4146
Part of #4158
Part of #4180
Part of #4191
Part of #4205
Part of #4218
Part of #4220
Part of #4260
Part of #4267
Part of #4377
Supersedes #4119, #4170, #4171, #4172, #4190, #4204, #4207, #4208, #4210, #4211, #4213,
#4214, #4215, #4216, #4299, #4301, #4303, #4305, #4306, #4307, #4308, #4309, #4312, #4324,
#4326, #4327, #4328, #4329, #4330, #4331, #4333, #4334, #4336, #4338, #4340, #4341, #4343,
#4344, #4348, #4349, #4390, #4391, #4392