refactor: retire the legacy timeboxing agent, and the 34 modules only it reached - #444
Merged
hugolytics merged 13 commits intoSep 11, 2026
Conversation
Fails today on two of four handoff configurations and on the second DM turn -- the redirect route and the handoff fall-through still send to the agent directly. Green once the sends are closed (Task 2) and the registration is gone (Task 5). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two sends still addressed timeboxing_agent directly: the redirect route,
which every second DM turn takes once a session is open, and the handoff
fall-through, reached whenever no session channel is configured or the
user is already in it. Both now continue on the kernel. A retired
registration would have turned each into Exception('Recipient not
found') rendered as a warning.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two sends still addressed timeboxing_agent directly: the redirect route,
which every second DM turn takes once a session is open, and the handoff
fall-through, reached whenever no session channel is configured or the
user is already in it. Both now continue on the kernel. A retired
registration would have turned each into Exception('Recipient not
found') rendered as a warning.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The stage and submit cards' dispatchers sent to timeboxing_agent inside a broad except that rewrote the card to 'please try again'. With the agent gone that would have been a transient glitch forever. One handler now answers all seven action ids honestly. Deviation from the task brief: build_text_section_block and four sibling block-builder functions lived in the two modules this task deletes, and TimeboxingFlowAgent (agent.py, not yet deleted -- that is Task 5) still imported all five to render its own cards. Moved all five verbatim into messages.py instead of just the one retired_cards.py needs, and repointed agent.py's two import lines there, so the legacy agent keeps working unmodified until its own deletion. Also touched the README/AGENTS docs that named the deleted modules or the now-dead action-id dispatch, so they do not describe code that no longer exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…pped Legacy capped consecutive no-change refine passes at three (9eb333e); the kernel's NeedsAnotherTurn had no cap, and _another_turn's own docstring named the loop it could not see. The streak is read from the outcome kinds the snapshot already records, so nothing new is stored. _advance_request in tests/doubles/timeboxing.py gains an interaction_id= keyword (default unchanged) because InMemoryPlanningSessionRepository.save replays a repeated interaction_id instead of re-running the planner, which would have flattened every scripted turn in the new test onto one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Off everywhere since 2026-08-22, registered unconditionally at startup with nothing routed to it. The harness covers all five stages, the confirm gate (stronger: deny by default), Undo, and the migrated constraint store; the one behaviour it lacked landed in the previous commit. The reachability tool reports zero orphans after the cut; the identifier 'timeboxing_agent' stays where it is a handoff target, a persona key, or a session-key prefix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
kg_constraint_client's rows now go through the durable-store adapter the harness actually reads; the tmbx patch-order test replays the model's own dump, which is the only serialiser left on that path. The reachability, dangling-import, coverage-diff and seam-taxonomy tools move into scripts/dev/tests/ so the retirement's gates are reproducible. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, taxonomy output path un-hardcoded Four fixes from Task 7 review: covdiff.py's source-root argument is now required and it refuses to run when fewer than half the before-report's paths resolve under it, so a wrong or missing root can no longer misfile every regression as "deleted" in silence. adapters/calendar/models.py has zero importers and zero tests since 67489cd deleted its last caller (test_sync_engine.py), so its orphans.py KEPT_DESPITE_UNREACHABLE entry was false; the module and the entry are both gone. taxonomy.py's per-test JSON path is now an optional argument defaulting beside the script instead of a hardcoded scratchpad path from one session, and the seam summary always prints to stdout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Its writers were agent.py and a review modal only legacy posted. After the retirement the table is permanently empty and the harness path re-read it after every turn -- a no-op dragging get_constraint_store through four handler signatures. ConstraintStore now names one class in the repo, the memory server's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The offence walker judges every assignment shape reaching a private attribute (plain, tuple/list-unpacking, starred, augmented, annotated), not just plain assignment, so the allowlist stays a true ceiling. Each offence is keyed by its target's own line:col, not the statement's line, so two dishonest targets sharing one line (tuple unpacking, chained assignment) are counted separately rather than one hiding the other. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…code as live Two root calendar docs now note McpCalendarClient's removal and tmbx/calendar/ as its replacement; core/README.md and the timeboxing package's README/AGENTS stop describing the deleted TimeboxingFlowAgent coordinator, GraphFlow orchestration, sync engine, patcher, and Notion-backed constraint plumbing as live, and instead say the Stage 1 elicitation loop and durable constraint-memory backends (now read by tasks' defaults memory and runtime.py's startup checks) are what remains; slack_bot/README.md explains the two rows commit 6f93212 removed instead of leaving the removal silent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nest coverage list The constraint-review buttons on legacy thread roots had no handler at all once constraint_review.py went -- the exact dead-button failure the retired-cards handler was written to end. Ten ids now, not seven. docs/architecture still called the deleted agent the primary planner; tests/README undercounted the deleted tests; a ticket told a reader to run six files that do not exist; the docs ticket said Open for work already landed. Fixed, and the docs ticket now covers docs/architecture too. The coverage classification is corrected in the branch's favour: the schedular calendar models are dead code (their only consumer is never registered), most of durable_constraint_store's lost lines are callers nothing calls, and the one ordinary-day loss is contracts.py:51 -- an all-day event's date. Ticketed with durable_constraint_store.py:506. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Problem
TimeboxingFlowAgent— 8,936 of its own lines, plus the 34 modules only itreached — has been unreachable from any live entry point since
_timebox_backend()started answeringharnessunlessFF_TIMEBOX_BACKEND=legacy, a variable set nowhere outsidetests/conftest.py. It was still registered on the AutoGen runtimeunconditionally, still instantiated in the running bot with nothing routed
to it, and four live
send_messagecall sites still addressed it directly— each one a bare
Exception("Recipient not found")waiting for the day theclass was deleted without those sends being closed first. This PR closes
those four sends, ports the one behaviour the harness genuinely lacked (a
refine-loop no-progress cap legacy had and the harness's uncapped
NeedsAnotherTurndid not), then deletes the class, the constraint-reviewsurface only it wrote to, and every module reachable only through it —
across thirteen commits, each task-reviewed, with the deletion landing only after the sends were closed and the cap was ported.
Measured numbers
src/: 74 files changed, 442 insertions(+), 21,830 deletions(-) — 56 modules deleted(
git diff --stat 31e88b5..HEAD -- src/)tests/: 100 files changed, 902 insertions(+), 15,295 deletions(-) — 77 test files deleted (67 in the retirement commit)(
git diff --stat 31e88b5..HEAD -- tests/)environmental failure —
test_the_deployed_profile_matches_the_repository— on both sides)budget)
scripts/dev/tests/orphans.py: 0 newly orphaned modulesscripts/dev/tests/dangling.py 42d9eb2..HEAD: 54 deleted modules, 0surviving
src/importers-m slowstill collects 89 tests (untouched by this cut)allowlist: 34 files, 149 offences (24
__new__, 125 private writes) —this is the starting point Project 2 (test-suite composability) shrinks
from, not the "few dozen" the original spec estimated
Also in the fix wave after the whole-branch review
Three constraint-review button ids from legacy thread roots had no handler once
constraint_review.pywent — the exact dead-button failure the retired-cards handler exists to end. Ten ids are retired now, not seven.docs/architecture/agents.mdno longer calls the deleted class the primary planner.Two spec deviations from
docs/superpowers/specs/2026-09-08-test-suite-composability-design.mdmcp_clients.pyscope. The spec said the whole file goes. Measured:only
McpCalendarClientandCalendarDaySnapshotleave it —ConstraintMemoryClientstays, because it is the defaulttasks_defaults_memory_backendand has tests of its own that havenothing to do with the legacy agent.
settings.timeboxing_memory_backend. The spec said this setting andits validator go with
agent.py. Measured: kept — it is also read byruntime.py's graphiti startup checks and by tasks' defaults memory;only
agent.py's own branch on it was deleted.(A third correction, not a deviation in behaviour: Project 2's "Injection —
none" section listed
ConstraintMemoryClientas deleted in project 1 —corrected to say it survives, and that its
workbench=seam — measured0.5s → 17.9s without it — is the one project 2 actually adds. The spec's
"allowlist shrinks to
TBPlan" line now states the measured 34-file/149-offence starting point it shrinks from.)
Coverage: three real regressions, named rather than explained away
Per-line coverage diff against PR #396 (
covdiff.py, root-argument bugfixed and re-verified in Task 7) found several surviving files that
lost covered lines a now-deleted test used to reach. Two of the original
five candidates are dead code, not regressions — their only consumer,
CalendarEventWorkerAgent(agents/schedular/agent.py:~901), is neverregistered in
runtime.py(git grep -n CalendarEventWorkerAgentreturnsonly its own definition), so nothing live reaches these lines at all. Most
of
durable_constraint_store.py's lost lines are the same shape — callersnothing calls. What's left is three real, live-code-now-untested lines:
agents/timeboxing/durable_constraint_store.py:506— theif not rows and search_namesre-query, an ordinary-day path (not anedge case): the rest of the file's lost lines (326-329, 334-336, 357,
539, 561) are
get_store_infoandcallable(...)fallbacks the liveclient never takes — dead code, not a regression
debug/diag.py:46-48— theexcept Exceptionarm ofwith_timeout,imported by four live agents; the surviving test only covers the
TimeoutErrorarmcontracts.py:51— the one ordinary-day loss: an all-day event arrivesas
{"date": ...}rather than{"dateTime": ...}, andhaunt/reconcile.pyimports
contractsfor exactly this parsedurable_constraint_store.py:506andcontracts.py:51are ticketed —tickets/recover_two_lines_the_retirement_left_untested.md— rather thanfixed in this pass;
debug/diag.py:46-48is an accepted known gap.Dead-code cleanup candidates the retirement exposed (not fixed this round)
Named but not removed — a source change, out of scope for a docs/test-
retirement PR:
llm/factory.py:151-158— thecalendar_submitterandtimebox_patcherbranches; their only users (
submitter.py,patching.py) are deletedtmbx/journal/store.py:31-34—journal_sessionmaker; its only callerwas the deleted
agent.py:339agents/schedular/models/calendar.py:185, 246, 351-352, 367-371, 392-402, 408-411andagents/schedular/models/core.py:143—_parse_event_type,_parse_time, the_check_time_combovalidator, thecolorIdfield:CalendarEventWorkerAgent, their only consumer, is never registered inruntime.pyagents/timeboxing/durable_constraint_store.py:325-329, 334-336, 357—get_store_info(zero callers) and thecallable(...)fallbacks thelive client never takes
(Three more class-(a) findings from the same coverage diff —
adapters/calendar/models.py,tool_result_models.py,constraint_review.py— were already deleted in this branch, Tasks 6–7.)Follow-up tickets filed
tickets/harness_bounded_brief_says_what_it_withheld.md— new. Theharness puts every applicable constraint row into the brief today (nothing
withheld), but the day the brief is bounded, nothing makes the truncation
speak — legacy rediscovered that rule three times (3dea6ae, e0c1f30,
Journal: ACCEPTED is the fallback, so an unattended auto-commit becomes a fake approval label #177).
tickets/test_suite_docs_after_legacy_retirement.md— new, filed anddispatched to a sonnet subagent per
CLAUDE.md; its commit is foldedinto this branch. Brought
tests/README.md's "What's out" section, thetwo superseded root calendar docs, the timeboxing agent's
README.md+AGENTS.md,core/README.md, andslack_bot/README.mdback in linewith what the retirement actually deleted.
tickets/skeleton_pre_generation.md— status line updated (not new): theconfirm/undo wiring it describes was retired with the legacy agent on
this branch; Undo lives on
ff_harness_undo/handlers.act_harness_undonow.
tickets/recover_two_lines_the_retirement_left_untested.md— new. Thetwo real coverage regressions the corrected classification leaves
(
durable_constraint_store.py:506,contracts.py:51), each with theone test that would cover it. Not blocking.
The live-drive checklist — for Hugo, not for an agent
This is the one gate the suite cannot stand in for: commit
dda88f4records every unit test passing while the live bot went to legacy. The
how-to is in the memory note "Driving a live Slack timeboxing session".
/timeboxin the timeboxing channel opens a session root with theworking card as first reply
off, and a session root appears in the timeboxing channel (or where I
typed, if none is configured) — no
:warning: Exception: Recipient not foundthread
the card says so
the retired-flow sentence
🤖 Generated with Claude Code