fix(api-tox): Codex P1+P2 followups on auto-rollback (PR #34) - #36
Merged
Conversation
Two real bugs Codex caught on the v2.2.0 Phase 1.1 ship.
P1 — `rollback_fired = True` claimed even when undo failed
=========================================================
In `AutoRollbackGuard.__exit__`, pre-fix code set `rollback_fired =
True` whenever `auto_rollback_end` was attempted, regardless of the
handler's actual return value. Failure modes:
* dispatcher raises (caught by `except Exception: pass`)
* handler returns `{"error": "ui.undo.endBlock failed: ..."}`
* handler returns `{"ok": False, "endblock_ok": True,
"undo_error": "..."}` (the dpsk4-baked `handle_auto_rollback_end`
explicitly returns this when `ui.undo.undo()` raises)
* handler returns `{"ok": True}` (partial payload — defensive)
…all of these reported "reverted" to both the user and the LLM
while the network stayed broken. Silent-correctness bug, not a
crash. Worst case: agent's follow-up decisions cascade on a
corrupted graph while the chat UI shows a confident "reverted"
message.
Fix: capture the dispatcher's return value, only set rollback_fired
= True when it's `{"ok": True, "rolled_back": True}`. Every other
shape (raise, ok=False, partial) routes to the degraded hint with a
short `(undo raised: ...)` / `(endBlock raised: ...)` detail clause
appended so the LLM can decide whether to retry or escalate.
P2 — hint dropped when undo block never opened
==============================================
In `Agent._loop`, pre-fix the hint-append condition was:
if (rollback_guard is not None
and getattr(rollback_guard, "rollback_fired", False)
and getattr(rollback_guard, "hint_text", "")
and ...): ...
The middle clause keyed on `rollback_fired`. But in the degraded
path (regression detected but undo block didn't open), the guard
sets `rollback_fired = False` while still populating `hint_text`
specifically to flag the failure. The condition dropped that hint
entirely — leaving the LLM continuing from a broken graph with zero
feedback in exactly the failure mode where it most needed it.
Fix: extracted the inline block into `Agent._apply_rollback_hint`
(testable as a method), condition keys on `hint_text` presence
instead of `rollback_fired`. Both success and degraded paths now
surface the hint to LLM + chat UI uniformly.
Tests
=====
12 new regression tests in `tests/test_tdpilot_api_rollback.py`:
TestCodexP1RollbackFiredAccuracy (5 tests)
- end-handler returns `{"ok": False, "undo_error": ...}`
→ rollback_fired stays False, hint contains "(undo raised: ...)"
- end-handler returns `{"error": "endBlock failed: ..."}`
→ rollback_fired stays False, hint contains "(endBlock raised: ...)"
- dispatcher raises during auto_rollback_end
→ rollback_fired stays False, degraded hint
- end-handler returns `{"ok": True, "rolled_back": True}` (happy path)
→ rollback_fired = True, "reverted" hint (no regression on happy path)
- end-handler returns `{"ok": True}` (partial)
→ rollback_fired stays False (defensive)
TestCodexP2HintAppendInDegradedPath (7 tests)
- hint appends with rollback_fired=False (the actual P2 fix-trigger)
- hint appends with rollback_fired=True (happy path)
- empty hint_text is a no-op
- None guard is a no-op
- empty results_block is a no-op
- list-shaped tool_result.content gets a text-block appended
- on_text raising doesn't break the LLM-side append
Refactor
========
Extracted `Agent._loop`'s hint-append block into `Agent._apply_rollback_hint`
so the P2 path is directly testable without urlopen mocking. Also
clean reuse point for future Phase 1 features that need the same
results_block-augmentation pattern (e.g. Phase 1.3 mid-turn integrity
check might want to fold its findings into the same callsite).
Local sweep: 1772 pytest passed (1760 prior + 12 new). Ruff
format/check clean. Versions in sync. dpsk4 .tox freshness clean
(not touched). API .tox freshness FAIL until rebuilt — that's the
final pre-push step.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bakes the rollback-success-gating fix (P1) and the extracted Agent._apply_rollback_hint method (P2) into the .tox so users get the corrected auto-rollback behaviour at COMP load time. 213,814 → 222,406 bytes (~+8.5KB delta from PR #34's tox — mostly the new private method body + the broadened format_hint). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 tasks
dreamrec
added a commit
that referenced
this pull request
May 11, 2026
* feat(api-tox): v2.2.0 Phase 1.2 — cycle detection in tool chains Second feature of the v2.2.0 reliability phase (Phase 1.1 was auto-rollback; see PR #34 + Codex P1/P2 follow-up in PR #36). Pairs with 1.1 to cover the two high-impact reliability gaps: - 1.1 catches "agent broke things" → undo the batch - 1.2 catches "agent is stuck looping" → break the turn How it works ============ A ``CycleLedger`` is constructed once per turn in ``Agent._loop`` and tracks ``(tool_name, args_hash) -> count``. Each call to ``ledger.record(name, args)`` increments and returns the new count. The agent loop checks against the threshold (default 3) BEFORE dispatching: - count == 1: first time, dispatch. - count == 2: second time, dispatch. - count >= 3: ``CycleDetected`` raised; ``run_turn``'s catch fires ``on_error`` → ``EV_ERROR`` + ``EV_STATE: idle`` in the event stream → red banner in the chat UI with tool name + args preview. The threshold of 3 is conservative on purpose — 2 dispatches give the agent a chance to recover from a one-off failure before declaring the turn stuck. Args identity uses sorted-keys JSON so ``{"a":1,"b":2}`` and ``{"b":2,"a":1}`` collapse to the same key; nested dicts hash recursively; lists are order-sensitive (connect source/dest ordering matters). Disable via env var ``TDPILOT_DISABLE_CYCLE_DETECTION=1``. Implementation ============== - NEW td_component/tdpilot_api_cycle_detector.py — pure-Python: args_hash, CycleLedger, CycleDetected, env-var gate, factory builder. No TouchDesigner imports. - tdpilot_api_agent.py: - New cycle_ledger_factory ctor kwarg. - _loop builds one ledger per turn after model resolution. - Inner per-batch for-loop checks ledger.record() BEFORE on_tool_call/dispatch; raises CycleDetected at the threshold. - CycleDetected is **late-imported** from cycle_detector inside _loop to avoid a circular import (cycle_detector imports AgentError from agent at top-level). Cost: one dict lookup per turn after the import is cached. - tdpilot_api_runtime.py: - New _build_cycle_ledger_factory method honours TDPILOT_DISABLE_CYCLE_DETECTION=1. - AgentRuntime._build_agent passes the factory to Agent(). - build_tdpilot_api_tox.py: - Adds tdpilot_api_cycle_detector to _SOURCE_FILES so it's baked into the API .tox. (Post-#35 the freshness gate auto-imports this same list, so no parallel-list maintenance.) Tests (49 new) ============== - TestArgsHash (8) — order independence, normalization, nested ordering, list-order sensitivity, defensive paths. - TestCycleLedger (10) — threshold validation (>=2), increment, per-key isolation, peek/reset, len semantics. - TestCycleDetectedException (3) — metadata, message formatting, AgentError membership. - TestEnvVarGate (3) — truthy/falsy classification. - TestBuildCycleLedgerFactory (3) — env-disable, fresh instances, custom threshold. - TestUsagePattern (4) — the canonical check-then-dispatch flow. - TestFormatArgsSummary (3) — render rules. - TestAgentLoopCycleIntegration (3) — end-to-end with mocked urlopen, proving the late-import + raise + on_error path actually works in run_turn. Covers: enabled+stuck (raises on 3rd, dispatches 2), disabled-factory (no check, hits turn_budget), distinct-args (never triggers, completes cleanly). Local sweep =========== - pytest: 1821 passed (1772 prior + 49 new). - ruff format + check: clean. - check_versions: in sync at v2.1.5 (no bump on this PR). - check_tox_freshness (dpsk4): fresh — that .tox not touched. - check_tox_api_freshness (chat-pipe): EXPECTED FAIL until the user rebuilds the .tox via the canonical Textport recipe. - check_no_personal_paths: clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: rebuild tdpilot_API.tox for Phase 1.2 cycle detection --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9 tasks
dreamrec
added a commit
that referenced
this pull request
May 11, 2026
First milestone of the v2.2.0→v3.0 roadmap (see docs/ROADMAP.md). Phase 1 ships in full; the chat-pipe agent is now safe to leave unsupervised on complex builds, two failure modes have automatic recovery, and drag-and-go finally works for new users out of the box. Bundled in this release ======================= - 1.1 Auto-rollback on error regression (PR #34 + #36) - 1.2 Cycle detection in tool chains (PR #37) - 1.2.1 Drag-and-go UX polish — Authmode COMP param, auto-save+auto-reload on Apikey change, 401 reconnect banner (PR #38) - 1.2.2 Build scripts auto-mirror .tox into main repo on rebuild, eliminating stale-symlink footgun (PR #39) - PR #35 paired source-file-list refactor (build-script + freshness- check now share a single source of truth) 136 new tests since v2.1.5. Total suite at release: 1848 passing. Headline behaviour change for end users ======================================= Default chat-pipe webserver auth posture changes from token-required to origin-allowlist-only (Authmode=open default). The origin allowlist still rejects cross-origin browser CSRF, so this is safe on TouchDesigner's typical single-user dev/perform usage profile. Users sharing .toe files across machines should set Authmode=token on the COMP for that deployment. The MCP-server tdpilot-dpsk4.tox (port 9985) auth model is UNCHANGED — TD_MCP_SHARED_SECRET still required. Bumps + rebuilt artifacts ========================= All 10 versioned files (7 manifests + 3 doc titles) bumped from 2.1.5 to 2.2.0 (scripts/check_versions.py enforces sync). API_VERSION in td_component/callbacks/_header.py also bumped; this invalidated both .tox source-hashes, both rebuilt inside TouchDesigner via the canonical recipe. Auto-mirror (Phase 1.2.2, just landed) propagated the rebuilt .tox files to the main repo's td_component/ automatically. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dreamrec
added a commit
that referenced
this pull request
May 19, 2026
* fix(api-tox): Codex P1+P2 followups on auto-rollback (PR #34) Two real bugs Codex caught on the v2.2.0 Phase 1.1 ship. P1 — `rollback_fired = True` claimed even when undo failed ========================================================= In `AutoRollbackGuard.__exit__`, pre-fix code set `rollback_fired = True` whenever `auto_rollback_end` was attempted, regardless of the handler's actual return value. Failure modes: * dispatcher raises (caught by `except Exception: pass`) * handler returns `{"error": "ui.undo.endBlock failed: ..."}` * handler returns `{"ok": False, "endblock_ok": True, "undo_error": "..."}` (the dpsk4-baked `handle_auto_rollback_end` explicitly returns this when `ui.undo.undo()` raises) * handler returns `{"ok": True}` (partial payload — defensive) …all of these reported "reverted" to both the user and the LLM while the network stayed broken. Silent-correctness bug, not a crash. Worst case: agent's follow-up decisions cascade on a corrupted graph while the chat UI shows a confident "reverted" message. Fix: capture the dispatcher's return value, only set rollback_fired = True when it's `{"ok": True, "rolled_back": True}`. Every other shape (raise, ok=False, partial) routes to the degraded hint with a short `(undo raised: ...)` / `(endBlock raised: ...)` detail clause appended so the LLM can decide whether to retry or escalate. P2 — hint dropped when undo block never opened ============================================== In `Agent._loop`, pre-fix the hint-append condition was: if (rollback_guard is not None and getattr(rollback_guard, "rollback_fired", False) and getattr(rollback_guard, "hint_text", "") and ...): ... The middle clause keyed on `rollback_fired`. But in the degraded path (regression detected but undo block didn't open), the guard sets `rollback_fired = False` while still populating `hint_text` specifically to flag the failure. The condition dropped that hint entirely — leaving the LLM continuing from a broken graph with zero feedback in exactly the failure mode where it most needed it. Fix: extracted the inline block into `Agent._apply_rollback_hint` (testable as a method), condition keys on `hint_text` presence instead of `rollback_fired`. Both success and degraded paths now surface the hint to LLM + chat UI uniformly. Tests ===== 12 new regression tests in `tests/test_tdpilot_api_rollback.py`: TestCodexP1RollbackFiredAccuracy (5 tests) - end-handler returns `{"ok": False, "undo_error": ...}` → rollback_fired stays False, hint contains "(undo raised: ...)" - end-handler returns `{"error": "endBlock failed: ..."}` → rollback_fired stays False, hint contains "(endBlock raised: ...)" - dispatcher raises during auto_rollback_end → rollback_fired stays False, degraded hint - end-handler returns `{"ok": True, "rolled_back": True}` (happy path) → rollback_fired = True, "reverted" hint (no regression on happy path) - end-handler returns `{"ok": True}` (partial) → rollback_fired stays False (defensive) TestCodexP2HintAppendInDegradedPath (7 tests) - hint appends with rollback_fired=False (the actual P2 fix-trigger) - hint appends with rollback_fired=True (happy path) - empty hint_text is a no-op - None guard is a no-op - empty results_block is a no-op - list-shaped tool_result.content gets a text-block appended - on_text raising doesn't break the LLM-side append Refactor ======== Extracted `Agent._loop`'s hint-append block into `Agent._apply_rollback_hint` so the P2 path is directly testable without urlopen mocking. Also clean reuse point for future Phase 1 features that need the same results_block-augmentation pattern (e.g. Phase 1.3 mid-turn integrity check might want to fold its findings into the same callsite). Local sweep: 1772 pytest passed (1760 prior + 12 new). Ruff format/check clean. Versions in sync. dpsk4 .tox freshness clean (not touched). API .tox freshness FAIL until rebuilt — that's the final pre-push step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: rebuild tdpilot_API.tox for Codex P1+P2 followups Bakes the rollback-success-gating fix (P1) and the extracted Agent._apply_rollback_hint method (P2) into the .tox so users get the corrected auto-rollback behaviour at COMP load time. 213,814 → 222,406 bytes (~+8.5KB delta from PR #34's tox — mostly the new private method body + the broadened format_hint). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dreamrec
added a commit
that referenced
this pull request
May 19, 2026
* feat(api-tox): v2.2.0 Phase 1.2 — cycle detection in tool chains Second feature of the v2.2.0 reliability phase (Phase 1.1 was auto-rollback; see PR #34 + Codex P1/P2 follow-up in PR #36). Pairs with 1.1 to cover the two high-impact reliability gaps: - 1.1 catches "agent broke things" → undo the batch - 1.2 catches "agent is stuck looping" → break the turn How it works ============ A ``CycleLedger`` is constructed once per turn in ``Agent._loop`` and tracks ``(tool_name, args_hash) -> count``. Each call to ``ledger.record(name, args)`` increments and returns the new count. The agent loop checks against the threshold (default 3) BEFORE dispatching: - count == 1: first time, dispatch. - count == 2: second time, dispatch. - count >= 3: ``CycleDetected`` raised; ``run_turn``'s catch fires ``on_error`` → ``EV_ERROR`` + ``EV_STATE: idle`` in the event stream → red banner in the chat UI with tool name + args preview. The threshold of 3 is conservative on purpose — 2 dispatches give the agent a chance to recover from a one-off failure before declaring the turn stuck. Args identity uses sorted-keys JSON so ``{"a":1,"b":2}`` and ``{"b":2,"a":1}`` collapse to the same key; nested dicts hash recursively; lists are order-sensitive (connect source/dest ordering matters). Disable via env var ``TDPILOT_DISABLE_CYCLE_DETECTION=1``. Implementation ============== - NEW td_component/tdpilot_api_cycle_detector.py — pure-Python: args_hash, CycleLedger, CycleDetected, env-var gate, factory builder. No TouchDesigner imports. - tdpilot_api_agent.py: - New cycle_ledger_factory ctor kwarg. - _loop builds one ledger per turn after model resolution. - Inner per-batch for-loop checks ledger.record() BEFORE on_tool_call/dispatch; raises CycleDetected at the threshold. - CycleDetected is **late-imported** from cycle_detector inside _loop to avoid a circular import (cycle_detector imports AgentError from agent at top-level). Cost: one dict lookup per turn after the import is cached. - tdpilot_api_runtime.py: - New _build_cycle_ledger_factory method honours TDPILOT_DISABLE_CYCLE_DETECTION=1. - AgentRuntime._build_agent passes the factory to Agent(). - build_tdpilot_api_tox.py: - Adds tdpilot_api_cycle_detector to _SOURCE_FILES so it's baked into the API .tox. (Post-#35 the freshness gate auto-imports this same list, so no parallel-list maintenance.) Tests (49 new) ============== - TestArgsHash (8) — order independence, normalization, nested ordering, list-order sensitivity, defensive paths. - TestCycleLedger (10) — threshold validation (>=2), increment, per-key isolation, peek/reset, len semantics. - TestCycleDetectedException (3) — metadata, message formatting, AgentError membership. - TestEnvVarGate (3) — truthy/falsy classification. - TestBuildCycleLedgerFactory (3) — env-disable, fresh instances, custom threshold. - TestUsagePattern (4) — the canonical check-then-dispatch flow. - TestFormatArgsSummary (3) — render rules. - TestAgentLoopCycleIntegration (3) — end-to-end with mocked urlopen, proving the late-import + raise + on_error path actually works in run_turn. Covers: enabled+stuck (raises on 3rd, dispatches 2), disabled-factory (no check, hits turn_budget), distinct-args (never triggers, completes cleanly). Local sweep =========== - pytest: 1821 passed (1772 prior + 49 new). - ruff format + check: clean. - check_versions: in sync at v2.1.5 (no bump on this PR). - check_tox_freshness (dpsk4): fresh — that .tox not touched. - check_tox_api_freshness (chat-pipe): EXPECTED FAIL until the user rebuilds the .tox via the canonical Textport recipe. - check_no_personal_paths: clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: rebuild tdpilot_API.tox for Phase 1.2 cycle detection --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dreamrec
added a commit
that referenced
this pull request
May 19, 2026
First milestone of the v2.2.0→v3.0 roadmap (see docs/ROADMAP.md). Phase 1 ships in full; the chat-pipe agent is now safe to leave unsupervised on complex builds, two failure modes have automatic recovery, and drag-and-go finally works for new users out of the box. Bundled in this release ======================= - 1.1 Auto-rollback on error regression (PR #34 + #36) - 1.2 Cycle detection in tool chains (PR #37) - 1.2.1 Drag-and-go UX polish — Authmode COMP param, auto-save+auto-reload on Apikey change, 401 reconnect banner (PR #38) - 1.2.2 Build scripts auto-mirror .tox into main repo on rebuild, eliminating stale-symlink footgun (PR #39) - PR #35 paired source-file-list refactor (build-script + freshness- check now share a single source of truth) 136 new tests since v2.1.5. Total suite at release: 1848 passing. Headline behaviour change for end users ======================================= Default chat-pipe webserver auth posture changes from token-required to origin-allowlist-only (Authmode=open default). The origin allowlist still rejects cross-origin browser CSRF, so this is safe on TouchDesigner's typical single-user dev/perform usage profile. Users sharing .toe files across machines should set Authmode=token on the COMP for that deployment. The MCP-server tdpilot-dpsk4.tox (port 9985) auth model is UNCHANGED — TD_MCP_SHARED_SECRET still required. Bumps + rebuilt artifacts ========================= All 10 versioned files (7 manifests + 3 doc titles) bumped from 2.1.5 to 2.2.0 (scripts/check_versions.py enforces sync). API_VERSION in td_component/callbacks/_header.py also bumped; this invalidated both .tox source-hashes, both rebuilt inside TouchDesigner via the canonical recipe. Auto-mirror (Phase 1.2.2, just landed) propagated the rebuilt .tox files to the main repo's td_component/ automatically. Co-authored-by: Claude Opus 4.7 (1M context) <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.
Summary
Codex's automated review on PR #34 caught two real bugs in the v2.2.0 Phase 1.1 auto-rollback code I shipped earlier today. Both are real silent-correctness issues — neither crashes, both mislead the agent + user — and both have regression tests targeting the exact failure shapes Codex described.
This is the established v2.1.3→v2.1.4→v2.1.5 followup pattern carried into Phase 1.1.
The two bugs
P1 —
rollback_fired = Trueclaimed even when undo failedAutoRollbackGuard.__exit__setrollback_fired = Truewheneverauto_rollback_endwas attempted, regardless of the handler's actual return value. Failure modes:rollback_fired = True+ "reverted" hint (lying)rollback_fired = False+ degraded hint with(undo raised: ...){"error": "ui.undo.endBlock failed: ..."}rollback_fired = False+ degraded hint with(endBlock raised: ...){"ok": False, "undo_error": "..."}rollback_fired = False+ degraded hint with the surfaced error{"ok": True, "rolled_back": True}rollback_fired = True(correct)Worst case pre-fix: agent's follow-up decisions cascade on a corrupted graph while both the chat UI and the LLM's next tool_result show "reverted." Silent-correctness, not a crash — exactly the kind of bug that erodes trust without a clear repro.
P2 — hint dropped when undo block never opened
Agent._loop's inline hint-append condition gated ongetattr(rollback_guard, "rollback_fired", False). In the degraded path (regression detected but undo block never opened — e.g. running outside TD, orauto_rollback_beginreturned an error), the guard setsrollback_fired = Falsewhile still populatinghint_textspecifically to flag the failure. The condition dropped that hint entirely — leaving the LLM continuing from a broken graph in exactly the failure mode where it most needed feedback.Fix: extracted the inline block into
Agent._apply_rollback_hint(testable), condition now keys onhint_textpresence instead ofrollback_fired. Both success and degraded paths now surface the hint to LLM + chat UI uniformly.Refactor
The inline hint-append logic became a method on
Agentso the P2 path is unit-testable without urlopen mocking. Cleaner reuse surface for future Phase 1 features that also want to augment the per-batchresults_block(e.g. Phase 1.3 mid-turn integrity check could fold its findings into the same callsite).Files
Modified (3):
td_component/tdpilot_api_rollback.py—__exit__inspects the dispatcher's return value before claiming success; new_format_end_failurehelper produces the(undo raised: …)/(endBlock raised: …)detail clauses;format_hintgained anend_failure=""keyword for richer degraded-path hints.td_component/tdpilot_api_agent.py— extracted_apply_rollback_hint;_loopcalls it after the per-batch tool_use loop instead of inlining the condition.tests/test_tdpilot_api_rollback.py— 12 new tests across two new classes (TestCodexP1RollbackFiredAccuracy× 5,TestCodexP2HintAppendInDegradedPath× 7).Binary (2):
td_component/tdpilot_API.tox(221,182 → 222,406 bytes),.tox-api-source-hash.json— rebuilt in TD via the canonical Textport recipe.No source files added/removed → no
_SOURCE_FILES/_API_TOX_SOURCE_FILESchanges needed (PR #35's paired-list consolidation means there's only one list now anyway).Test plan
uv run pytest tests/ --ignore=tests/agent_evals -q— 1772 passed (1760 prior + 12 new).uv run --extra dev ruff format --check src tests scripts td_component— clean.uv run --extra dev ruff check src tests scripts td_component— clean.uv run python scripts/check_versions.py— in sync at v2.1.5.uv run python scripts/check_tox_freshness.py— dpsk4 .tox fresh (not touched).uv run python scripts/check_tox_api_freshness.py— API .tox fresh post-rebuild (hash10ba287545a6f421...).bash scripts/check_no_personal_paths.sh— clean.No version bump — Phase 1.1 stays unreleased; this is the same in-progress arc.
Why mock-only and not live-TD verification
The user's TD has
TD_MCP_REQUIRE_AUTH=1so my MCP client hits 401, blocking live verification through the dpsk4 MCP server. The 12 new mock-dispatcher tests model the exact failure shapes Codex flagged — higher coverage than a single hand-driven live test would give. If anything still slips, the Codex review pattern will catch it post-PR.🤖 Generated with Claude Code