Skip to content

docs(agent-handoff): Restore Root and Pendulum Handoffs to Current State - #4514

Merged
dieterolson merged 1 commit into
mainfrom
docs/handoff-refresh-2026-08-16
Aug 17, 2026
Merged

docs(agent-handoff): Restore Root and Pendulum Handoffs to Current State#4514
dieterolson merged 1 commit into
mainfrom
docs/handoff-refresh-2026-08-16

Conversation

@dieterolson

Copy link
Copy Markdown
Collaborator

The root and src/pendulum_simulator handoff docs had drifted far enough to actively misdirect the next agent. The root doc had also outgrown its own policy by 18x.

The size problem — archived, not deleted

CLAUDE.md requires handoff docs to be current-state only and ≤150 lines, with history in git rather than a changelog inside the file. The root doc had instead accumulated 137 dated entries across 2,708 lines.

Every entry is moved verbatim to docs/agent_handoff_archive/2026-08_tools_root_handoff_log.md. The archived body is byte-identical to the original (165,356 characters, 137 entries — verified programmatically), so the large deletion in the diff is a move, not a loss. The live doc is now 132 lines and carries an explicit do-not against appending dated entries again.

The accuracy problem

The root doc said PR #4119 was open with auto-merge armed, and #4124/#4129 were open drafts stacked on it. Actually:

Claimed Reality
#4119 open, auto-merge armed Closed without merging
#4124 open, draft-for-review Merged
#4129 open, draft-for-review Merged
#4147 first Club Builder slice active Closed; landed via #4157
#4155 active CI repair Closed
epics #4142, #4433 Absent entirely

Delivery has since shifted from long stacked PRs to scoped consolidations rebuilt onto current main. The doc now records the seven live epics, and — most usefully for whoever picks this up — that 39 of the 58 open PRs are codex/4142-* / codex/4433-* drafts superseded by merged #4473, whose own description states it supersedes that draft chain. They should be triaged and closed, not rebased. The four open consolidations (#4446, #4447, #4449, #4466) are the real queue.

The pendulum doc described #4406 as active on research/shoulder-velocity-drift-transfer. #4406 is closed, shipped via consolidation #4450. It now records what landed and keeps the scientific boundary as an explicit do-not: the Drift Transfer tab fails closed for the triple and golfer tiers, and the double model's proximal link rate must not be relabelled as an anatomical quantity. It also cross-references the UpstreamDrift #8684 qualification state — including the finite-ground result where the preregistered screen admitted 0 of 384 cells, so a moving base is not a free upgrade for those tiers.

Also recorded

  • The --regenerate-api-baseline trap: it blesses existing drift rather than only your change.
  • Delta CI's changed-file selection hides accumulated whole-tree debt.
  • Four pre-existing ruff format --check failures under src/data_processing/data_processor/python/tests/. They are on main, are not caused by this diff (which is three markdown files plus a SPEC row), and should not be silently absorbed into an unrelated PR.

Gates

  • ruff check: clean
  • ruff format --check: only the four pre-existing failures noted above; this PR touches no Python
  • SPEC.md §12: dated row added (1.17.11 — deliberately not 1.17.10, which the old handoff text already attributed to the 2026-08-15 consolidation)
  • Both handoff docs within the 150-line policy: root 132, pendulum 112

🤖 Generated with Claude Code

Both documents had drifted far enough to actively misdirect the next agent,
and the root document had also outgrown its own policy by 18x.

CLAUDE.md requires handoff docs to be current-state only and at most 150
lines, with history in git. The root doc had instead accumulated 137 dated
entries across 2,708 lines. Those entries are moved verbatim to
docs/agent_handoff_archive/2026-08_tools_root_handoff_log.md, not deleted —
the archived body is byte-identical to the original (165,356 chars, 137
entries), so no working-tree context is lost. The live doc is now 132 lines
and carries an explicit do-not against appending dated entries again.

Content corrections. The root doc presented PR #4119 as open with auto-merge
armed and #4124/#4129 as open drafts stacked on it; in fact #4119 closed
without merging and #4124/#4129 both merged. Epics #4142 and #4433 were absent
entirely. Delivery has shifted from stacked PRs to scoped consolidations
rebuilt onto current main.

It now records the seven live epics, the four open consolidations (#4446,
#4447, #4449, #4466) as the real queue, and the fact that 39 of 58 open PRs
are codex/4142-* / codex/4433-* drafts superseded by merged #4473 — whose own
description says so. Those should be triaged and closed, not rebased.

The pendulum doc described issue #4406 as active on branch
research/shoulder-velocity-drift-transfer. #4406 is closed and shipped via
consolidation #4450. It now records what landed, keeps the fail-closed
triple/golfer tier boundary as an explicit do-not, and points at the
UpstreamDrift #8684 qualification state — including the finite-ground result
where the preregistered screen admitted 0 of 384 cells, so a moving base is
not a free upgrade for the higher tiers.

Also recorded: the four pre-existing ruff format failures under
src/data_processing/ that are on main and should not be absorbed into an
unrelated PR, and the --regenerate-api-baseline trap.

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 merged commit a0143c1 into main Aug 17, 2026
14 of 15 checks passed
dieterolson pushed a commit that referenced this pull request Aug 17, 2026
…e-base wall

Repo policy is to update the handoff with every PR, and two things changed
since #4514 landed.

The 39 codex/4142-* / codex/4433-* drafts are now closed as superseded by
merged #4473, which says so in its own description. Open PRs went 58 -> 21 and
drafts 39 -> 0. Their work is verifiably on main: the Morris chain as
application/morris/{host,client,contracts}.py, the #4433 chain as the flight,
putting, plot, visual-state and tab-audit modules. Their branches are left
intact, so the guidance is now reopen-rather-than-rebase — those branches have
diverged far enough that merging one would remove current main content.

#4466 turns out not to be mergeable at all. After #4473 squash-merged, the
shared merge-base collapses to a commit predating src/rate_of_closure/, making
every overlapping file a both-added conflict with no common ancestor (281 of
them). #4517 is landing it as standalone slices instead, starting with
swing_sim.ground. The other consolidations should expect the same wall, so the
roadmap now says slice rather than merge.

Also added a do-not against per-file Python version guards, pointing at the
conftest floor guard that replaces them.

Handoff remains within the 150-line policy at 144.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dieterolson pushed a commit that referenced this pull request Aug 17, 2026
…e-base wall

Repo policy is to update the handoff with every PR, and two things changed
since #4514 landed.

The 39 codex/4142-* / codex/4433-* drafts are now closed as superseded by
merged #4473, which says so in its own description. Open PRs went 58 -> 21 and
drafts 39 -> 0. Their work is verifiably on main: the Morris chain as
application/morris/{host,client,contracts}.py, the #4433 chain as the flight,
putting, plot, visual-state and tab-audit modules. Their branches are left
intact, so the guidance is now reopen-rather-than-rebase — those branches have
diverged far enough that merging one would remove current main content.

#4466 turns out not to be mergeable at all. After #4473 squash-merged, the
shared merge-base collapses to a commit predating src/rate_of_closure/, making
every overlapping file a both-added conflict with no common ancestor (281 of
them). #4517 is landing it as standalone slices instead, starting with
swing_sim.ground. The other consolidations should expect the same wall, so the
roadmap now says slice rather than merge.

Also added a do-not against per-file Python version guards, pointing at the
conftest floor guard that replaces them.

Handoff remains within the 150-line policy at 144.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dieterolson pushed a commit that referenced this pull request Aug 17, 2026
…e-base wall

Repo policy is to update the handoff with every PR, and two things changed
since #4514 landed.

The 39 codex/4142-* / codex/4433-* drafts are now closed as superseded by
merged #4473, which says so in its own description. Open PRs went 58 -> 21 and
drafts 39 -> 0. Their work is verifiably on main: the Morris chain as
application/morris/{host,client,contracts}.py, the #4433 chain as the flight,
putting, plot, visual-state and tab-audit modules. Their branches are left
intact, so the guidance is now reopen-rather-than-rebase — those branches have
diverged far enough that merging one would remove current main content.

#4466 turns out not to be mergeable at all. After #4473 squash-merged, the
shared merge-base collapses to a commit predating src/rate_of_closure/, making
every overlapping file a both-added conflict with no common ancestor (281 of
them). #4517 is landing it as standalone slices instead, starting with
swing_sim.ground. The other consolidations should expect the same wall, so the
roadmap now says slice rather than merge.

Also added a do-not against per-file Python version guards, pointing at the
conftest floor guard that replaces them.

Handoff remains within the 150-line policy at 144.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dieterolson added a commit that referenced this pull request Aug 17, 2026
… at It (#4516)

* fix(ci, tests): enforce the two-tier Python floor instead of guessing at it

The `tests (3.10)` lane was running code that requires 3.11, which produced
failures that looked like defects and were not.

This repo is deliberately two-tier. The root distribution declares
requires-python = ">=3.11" (classifiers 3.11/3.12, mypy python_version 3.11),
while ten sub-packages and Rust crates declare >=3.10 and ship 3.10 wheels from
six maturin workflows. The 3.10 matrix lane exists to validate those
sub-packages. But it ran the whole suite, including root-package code such as
src/p1am_control_system and src/shared/python.

Two failures came from exactly that mismatch:

- test_deployment_hardening.py imported tomllib, stdlib only on 3.11+, aborting
  collection for the entire session (1,218 collected, zero executed).
- test_estop_shutdown_safe_state.py timed out because asyncio.wait_for was
  reimplemented on asyncio.timeouts in 3.11 with different cancellation
  propagation.

Neither is a 3.10 support bug. Both are 3.11-only code being run on 3.10.

conftest.py now resolves the nearest pyproject.toml for any collection path,
reads its declared requires-python floor, and skips collection when the running
interpreter is below it. The floor is read from the declarations rather than
hardcoded, so adding a sub-package or moving a floor needs no edit here.
requires-python is regex-parsed on purpose: tomllib does not exist on the very
interpreter the guard has to run on. It fails closed at (3, 11) when nothing
parses. Verified as a strict no-op on 3.11+ — collection is identical at
10,382/10,414 with and without the guard.

CLAUDE.md advertised a flat "Python 3.10+" that the root distribution rejects.
It now states the real contract, names the ten 3.10 distributions, explains
what the 3.10 lane is for, and says not to add per-file version guards because
the conftest handles it.

tests/test_python_version_contract.py locks the declarations together:
requires-python against the mypy target and the classifiers, the CI matrix
against the lowest declared floor, CLAUDE.md against the root floor, and a
regression guard proving root-package tests are excluded below the floor while
3.10 sub-packages are still collected. The interpreter version is faked so
those guards run on every lane, not only the one that cannot execute them.

This supersedes the separate investigation into the e-stop shutdown timeout:
that test should not run on 3.10 at all, and no shutdown-safety code needed to
change.

The pytest.importorskip added to test_deployment_hardening.py in #4515 is now
redundant but retained deliberately as defense in depth — if this guard ever
regresses, a bare import there takes down the whole session again.

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

* docs(agent-handoff): record the closed draft chain and the #4466 merge-base wall

Repo policy is to update the handoff with every PR, and two things changed
since #4514 landed.

The 39 codex/4142-* / codex/4433-* drafts are now closed as superseded by
merged #4473, which says so in its own description. Open PRs went 58 -> 21 and
drafts 39 -> 0. Their work is verifiably on main: the Morris chain as
application/morris/{host,client,contracts}.py, the #4433 chain as the flight,
putting, plot, visual-state and tab-audit modules. Their branches are left
intact, so the guidance is now reopen-rather-than-rebase — those branches have
diverged far enough that merging one would remove current main content.

#4466 turns out not to be mergeable at all. After #4473 squash-merged, the
shared merge-base collapses to a commit predating src/rate_of_closure/, making
every overlapping file a both-added conflict with no common ancestor (281 of
them). #4517 is landing it as standalone slices instead, starting with
swing_sim.ground. The other consolidations should expect the same wall, so the
roadmap now says slice rather than merge.

Also added a do-not against per-file Python version guards, pointing at the
conftest floor guard that replaces them.

Handoff remains within the 150-line policy at 144.

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

* fix(tests): check each sub-package at its own floor, not the global minimum

The 3.11 lane failed on the previous commit with "src/movement_optimizer
declares 3.10 support but the guard would exclude it from the 3.10 lane". The
guard was not the problem; the test was wrong by construction.

test_sub_package_tests_still_run_on_the_lower_lane faked the interpreter to the
*minimum* floor across every lower-floored package, then asserted that every one
of them would still be collected. That only holds when they all declare the same
floor. A single package declaring lower than the rest makes the assertion fail
for packages that are being excluded entirely correctly — a 3.10 package is
supposed to be excluded on 3.9.

Each package is now checked at its own declared floor, which is the property
actually worth guarding and is independent of what anything else declares.

Two related changes:

_below_declared_floor now takes the interpreter version as an optional
parameter instead of the tests patching sys.version_info. That patch was
process-global and read by unrelated library code, which under xdist meant a
test could perturb workers running beside it.

test_conftest_reads_each_package_declared_floor now checks every sub-package
rather than two hand-picked ones, and reports all mismatches with their paths.
Path resolution is the part most likely to differ across platforms, so a failure
should name the package instead of surfacing as a confusing assertion further
downstream.

Also added test_nested_distributions_do_not_widen_their_parent_tree. The
diagnostic turned up a real wrinkle: psa_package declares >=3.10 while living
inside src/shared/python, which is root-package code. The guard handles it
correctly — the nested distribution gets 3.10 and its parent tree stays at
3.11 — but nothing was pinning that, and a regression there would quietly let
the lower lane start collecting root code again.

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

* ci: allow empty collection on lanes below the root Python floor

The floor guard works, and that turned out to be the problem for this PR's own
3.10 lane: pytest exited 5, "no tests collected".

On a sub-floor lane the guard excludes root-package code, including the
top-level tests/ tree. A change that touches only root-package tests therefore
has nothing to run there, and pytest treats an empty selection as an error. The
lane failed while behaving exactly as designed.

The tests step now tolerates exit code 5, but only on lanes below the root
requires-python floor, which is read from pyproject.toml rather than hardcoded
so it tracks the declaration. On the required lane exit 5 still fails, which is
what keeps the issue #3324 vacuous-selection protection meaningful — that guard
exists to catch typo'd or broken test selection, and blanket-tolerating exit 5
everywhere would defeat it.

Verified before pushing, since this is a CI workflow change and the previous two
rounds of surprises here were self-inflicted:

- version comparison: 3.9 and 3.10 allow empty collection, 3.11 and 3.12 do not
- tolerance: exit 5 returns 0 when allowed, and passes through unchanged when not
- the workflow YAML parses and the extracted run block passes bash -n

Deliberately avoided a python heredoc inside the indented YAML block; the floor
is parsed with grep instead, because heredoc terminators in a YAML block scalar
are sensitive to how the block is de-indented.

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

* ci: drop the 3.10 lane from ci-standard instead of propping it up

Replaces the exit-5 tolerance from the previous commit, which was treating a
symptom.

Two facts settle it. Every core_tests entry is root-package code — all of them
are tests/** or src/shared/python/sidekick/tests/** — so once the floor guard
correctly excludes root-package code, the 3.10 lane can never run a single core
test. And 3.10 compatibility for the ten sub-packages that actually claim it is
already proven elsewhere: each crate's maturin-*.yml runs a build + parity gate
across 3.10/3.11/3.12 that builds the wheel, installs it, and asserts both that
the extension imports and that the native backend is selected.

So the lane was not testing 3.10 support. It was running root-package code on an
interpreter that cannot execute it, and the only reason it ever looked
meaningful was that a collection abort masked the emptiness. Tolerating empty
collection would have institutionalised a lane that consumes a runner and
reports nothing on every PR.

ci-standard now runs ["3.11", "3.12"], starting at the root floor. The exit-5
tolerance is reverted, and the now-dead `!= "3.10"` sidekick guard is removed.

Two contract tests replace the weaker matrix check:

test_ci_matrix_starts_at_the_root_floor fails if a lane is ever added below the
root floor, with an error that points at the maturin workflows as the correct
home for lower interpreters.

test_lower_floor_packages_keep_a_workflow_that_exercises_them fails if a
sub-package declares a floor below the root and no workflow outside ci-standard
actually runs that interpreter. This is the guard against the real risk in this
change: dropping the lane must not leave a 3.10 claim with nothing behind it.
It passes today, which is what makes the removal safe rather than assumed.

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

* fix(tests): select sub-package pyprojects by git tracking, not a denylist

The 3.11 lane failed on a vendored dependency:

  .cargo-home/registry/src/.../pyo3-0.24.2/pyo3-runtime/pyproject.toml
  declares >=3.7 but no workflow outside ci-standard runs that interpreter

That file is pyo3's own packaging metadata sitting in the cargo registry cache.
It is a third-party artifact, not a claim this repository makes, and the
directory does not exist locally — it is materialised only on CI runners, which
is why the test passed here and failed there.

The helper was enumerating pyproject.toml by rglob and filtering with a
directory denylist (.venv, node_modules, target, build, dist, .git). A denylist
is unbounded: every new tool cache is another entry nobody adds until it breaks
a build. .cargo-home was simply the first one to appear.

Selection is now by `git ls-files`, which answers "is this ours?" directly and
cannot drift as new caches appear. It returns exactly the eleven real
sub-package pyprojects and nothing else. If git is unavailable the tests skip
rather than silently degrading to a weaker check.

This fixes all four tests that share the helper, not just the one that failed.

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

---------

Co-authored-by: codex-scheduled <codex-scheduled@users.noreply.github.com>
Co-authored-by: Claude Opus 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