Skip to content

Latest commit

 

History

History
637 lines (505 loc) · 33 KB

File metadata and controls

637 lines (505 loc) · 33 KB

TDPilot DPSK4 v2.5 — Implementation Plan (Cold-Start Executable)

Theme: Agent self-awareness + safety + distribution polish.

Authored: 2026-05-18 (post v2.4.0 ship + dreamrec/TDPilot v1.6.16 upstream audit).

Status: SHIPPED 2026-05-19 as v2.5.0 → v2.5.4. See docs/plans/README.md v2.5 retrospective, the post-2.5.3 audit retrospective, and the v2.5.4 hardening retrospective for the as-shipped scope (8 phases + 3 live-audit patches + 1 hardening release). This plan file is preserved as the historical pre-ship blueprint; phase statuses below are marked SHIPPED for archival accuracy but do not edit further.

For the receiving agent: Drop this file into a fresh Claude Code session inside the TDPilot DPSK4 repo and say "execute v2.5". Everything below is self-contained — no prior session memory required. Each phase has a unique id, a status field, pre-flight checks, validation gates, and resume instructions. Edit the status field as you complete phases so the next session knows where to start.


0. Bootstrap context

Current state (at plan authoring)

  • Latest shipped: v2.4.0 (commits 41dbaaa + 8f087cd + a105fea on main, 2026-05-13). 105 MCP tools. 2000 tests passing.
  • Source of v2.5 themes: docs/plans/v2.4_IMPLEMENTATION_PLAN.md + upstream dreamrec/TDPilot v1.6.16 (2026-05-18).
  • Branch: start a fresh branch off main: git checkout -b claude/v2.5.0-dev.
  • Worktree: if continuing from .claude/worktrees/<slug>/ of a prior session, verify git status is clean before starting any phase.

Critical derived artifacts (per CLAUDE.md)

  1. td_component/tdpilot-dpsk4.tox — rebuild in TD when any _TOX_SOURCE_FILES listed in build_export_mcp_tox.py changes.
  2. td_component/tdpilot_API.tox — rebuild in TD when any _API_TOX_SOURCE_FILES listed in build_tdpilot_api_tox.py changes.
  3. Tool countEXPECTED_MIN_TOOL_COUNT in src/td_mcp/release_gates.py. Currently 105. v2.5 adds 5 new MCP tools → 110. Bump and update all user-facing copies (README.md, npm/README.md, plugin_README.md, docs/, skills/, CHANGELOG, marketplace.json description, npm/package.json description, GitHub repo description).
  4. CHANGELOG.md — per-phase entries during dev; clean v2.5.0 entry on release.
  5. Seven version manifestspyproject.toml, src/td_mcp/__init__.py, .claude-plugin/plugin.json, .claude-plugin/marketplace.json, npm/package.json, mcp/manifest.json, td_component/mcp_webserver_callbacks.py::API_VERSION. Enforced by scripts/check_versions.py.
  6. tdpilot.plugin — rebuild via uv run python scripts/build_plugin_zip.py at release time.
  7. GitHub Release — after git push origin v2.5.0, also run gh release create v2.5.0. Per feedback memory.

Operating philosophy

Same as v2.4: cost is not a constraint; maximize intelligence per turn. v2.5 layers ADDITIVE features — no breaking changes to existing chat-pipe sessions. All new features ship with feature-flags / Menu COMP params for opt-out.

Naming discipline

Use tdpilot-dpsk4 (npm), ~/.tdpilot-dpsk4/api/ (config dir), ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env (MCP env file). NEVER tdpilot-deepseek* or parent-fork tdpilot. Pinned by tests/test_release_critical_names.py.


1. Phase overview

Phase ID Item Effort Tox rebuild Status
v2.5.1-activity-log Activity log ring buffer + td_get_activity_log + _read_journal hints 3 days Both SHIPPED v2.5.0
v2.5.2-ocr OCR sidecar on Phase B vision pipeline ([ocr] extras) 4 days MCP only SHIPPED v2.5.0 (td_ocr_image)
v2.5.3-tool-approval Approvalmode COMP param + runtime gate + chat UI 3 days API only SHIPPED v2.5.0
v2.5.4-auth-fallback TD-side MCP auth fallback file (~/.tdpilot-dpsk4/.tdpilot-dpsk4.env enforcement) 1 day None SHIPPED v2.5.0 (maybe_migrate_env_to_file)
v2.5.5-td-release-card TD 2025.32820 release card markdown 1 hour None SHIPPED (pre-v2.5)
v2.5.6-npm-stdio npm wrapper stdout/stderr separation (Claude Desktop compat) 1 day None SHIPPED v2.5.0 (stdio-discipline AST contract test)
v2.5.7-check-updates td_check_for_updates read-only tool 2 days MCP only SHIPPED v2.5.0
v2.5.8-log-receiver (Stretch) MCP-stderr tail to TD Table DAT 2 days API only SHIPPED v2.5.0 (re-scoped to td_get_traces trace viewer)

Total scope: 8 phases, ~16 days of work, ~3 calendar weeks with discipline.

Phase 1–4 are core; 5–7 are quick polish; 8 is stretch. If schedule slips, drop 8 first.

Subagent strategy: phases 4, 5, 6 (quick polish) are independent — dispatch in parallel via separate agent sessions when convenient. Phases 1, 2, 3 are sequential within their domain but independent across domains (1 = observability, 2 = vision, 3 = safety) so they CAN run in parallel agents.


2. Phase v2.5.1 — Activity log + journal hints

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.1-activity-log Tox rebuild: Both .tox files (chat-pipe needs ring; MCP needs tool)

Why coupled

The activity log produces the data; journal hints consume it. Splitting them creates a half-feature window where the log exists but nothing reads it. Ship together in a single PR.

Pre-flight checks

git status                                                      # clean tree
git rev-parse --abbrev-ref HEAD                                 # on claude/v2.5.1-activity-log or main
uv run python scripts/check_versions.py                         # baseline green
uv run pytest tests/ --ignore=tests/agent_evals -x -q           # baseline ~2000 pass
grep EXPECTED_MIN_TOOL_COUNT src/td_mcp/release_gates.py        # should be 105

Files to create

  • src/td_mcp/observability/__init__.py — module marker
  • src/td_mcp/observability/activity_log.pyActivityRecord dataclass + ActivityRing class (collections.deque(maxlen=200)-backed) + per-turn reset hook
  • src/td_mcp/registry/observability_tools.py — registers td_get_activity_log via the existing tool_registry pattern (mirror the td_foo example at line ~683)
  • tests/test_v25_activity_log.py — ring FIFO eviction, filter, since-ts
  • tests/test_v25_journal_hints.py — threshold logic, args_hash uses B-010 deep-canonical

Files to modify

  • src/td_mcp/tool_registry.py — append ActivityRecord on every dispatch in the wrapper. Wrap tool return values with _read_journal when (tool_name, args_hash) count ≥ 2 this turn.
  • src/td_mcp/release_gates.py — bump EXPECTED_MIN_TOOL_COUNT: int = 106
  • td_component/tdpilot_api_runtime.py — chat-pipe variant of ring buffer + same journal-hint wrap on tool results
  • td_component/tdpilot_api_runtime.py — refine B-007 protocol point 6 in SYSTEM_PROMPT_BASE to acknowledge journal hints (so the LLM sees both static guidance + runtime hint)
  • td_component/build_tdpilot_api_tox.py::_API_TOX_SOURCE_FILES — verify tdpilot_api_runtime.py already in tuple (it is)
  • CHANGELOG.md — Unreleased section, add v2.5.1-activity-log entry
  • docs/API_REFERENCE.md (or equivalent) — document td_get_activity_log

Key data shapes

@dataclass(frozen=True, slots=True)
class ActivityRecord:
    ts: float                                                   # time.monotonic()
    tool_name: str
    args_hash: str                                              # reuse deep-canonical hash from B-010
    duration_ms: int
    result_kind: Literal["ok", "error", "no_change"]
    error_msg: Optional[str] = None

# Tool result wrapper
{
    "data": {...actual tool result...},
    "_read_journal": {
        "call_count": 3,
        "state_unchanged": True,
        "hint": "You've called td_get_errors with these exact args 3 times this turn with no change. Consider switching strategy — see protocol point 6."
    }
}

Threshold logic

  • count == 2: low-friction nudge in hint text
  • count == 3: stronger reinforcement referencing protocol point 6 (B-007)
  • count == 4: cycle-detect throws (existing B-010 behavior; unchanged)

Tool surface

@mcp.tool()
async def td_get_activity_log(
    limit: int = 50,
    tool_filter: Optional[str] = None,
    since_ts: Optional[float] = None,
) -> dict:
    """Return recent agent tool-call activity (200-entry ring buffer)."""

Tests (13 total)

  • 5 in test_v25_activity_log.py: FIFO eviction at 201st entry, filter-by-tool, since-ts cutoff, result_kind classification, JSON-serializable
  • 8 in test_v25_journal_hints.py: count=2 nudge, count=3 reinforcement, count=4 cycle-detect path unchanged, args_hash deep-canonical regression (list-order independence per B-010), per-turn ring reset, hint absent when count=1, hint format stable, chat-pipe variant matches MCP variant

Validation gates

grep "EXPECTED_MIN_TOOL_COUNT: int = 106" src/td_mcp/release_gates.py    # bumped
uv run pytest tests/test_v25_activity_log.py tests/test_v25_journal_hints.py -v
uv run pytest tests/ --ignore=tests/agent_evals -x -q                    # no regressions; ~2013 pass
uv run python scripts/check_versions.py                                  # only release_gates.py changed; versions still in sync
uv run python scripts/check_tox_freshness.py                             # would fail until user rebuilds .tox
uv run python scripts/check_tox_api_freshness.py                         # would fail until user rebuilds .tox

Risks + mitigations

  • Risk: Ring buffer memory growth on long sessions. Mitigation: deque(maxlen=200) caps; ActivityRecord uses slots=True.
  • Risk: Hash collision masking real loops. Mitigation: reuse B-010 deep-canonical (already collision-tested).
  • Risk: Hint text bloats system prompt size. Mitigation: hints are in tool RESULT not system prompt; per-turn reset.

Resume from here

After this PR merges, mark v2.5.1-activity-log status as completed in this file's overview table. Next phase: v2.5.2-ocr (independent — can start in parallel).


3. Phase v2.5.2 — OCR sidecar on Phase B vision pipeline

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.2-ocr Tox rebuild: MCP .tox only (vision pipeline lives MCP-side)

Why

v2.4 Phase B injects screenshots into the LLM via Anthropic-compat vision blocks. Agent can SEE but can't reliably READ on-screen text (LLM vision is weak on small/cluttered numerals). OCR pre-extracts text so the agent gets both the image AND a transcript.

Engine decision

PaddleOCR (production-grade, CPU+GPU, multilingual, well-maintained). Optional via pyproject extras: pip install tdpilot-dpsk4[ocr]. Subprocess sidecar pattern so the 400MB model doesn't bloat the MCP server process.

Pre-flight checks

ls src/td_mcp/vision/                                          # __init__.py, monitor.py, streamer.py exist (Phase B)
grep EXPECTED_MIN_TOOL_COUNT src/td_mcp/release_gates.py       # should be 106 (after v2.5.1)

Files to create

  • src/td_mcp/vision/ocr.py — subprocess manager: spawn, JSON-lines protocol over stdin/stdout, lazy-init, 5-minute idle kill, restart-on-crash
  • src/td_mcp/vision/ocr_worker.py — standalone worker script: paddleocr.PaddleOCR(use_angle_cls=True, lang='en'), accept image paths on stdin, emit JSON results on stdout
  • tests/test_v25_ocr.py — fixture PNG with known text, mock subprocess for unit tests, optional @pytest.mark.skipif(not paddleocr_installed) integration test
  • tests/fixtures/ocr_sample.png — known-text PNG (e.g., "Error: COMP missing parent") for golden-test

Files to modify

  • src/td_mcp/vision/streamer.py (or monitor.py) — call OCR after capture; merge ocr_text into vision payload; gracefully degrade when extras not installed
  • td_component/tdpilot_api_runtime.py — Phase B vision payload now includes ocr_text field; LLM gets both {type: "image"} and {type: "text", text: "Visible text (OCR):\n..."}
  • pyproject.toml — add [project.optional-dependencies] section:
    [project.optional-dependencies]
    ocr = ["paddleocr>=2.7", "paddlepaddle>=2.6"]
  • README.md — document OCR extras + tradeoff (size, latency)
  • CHANGELOG.md — v2.5.2 entry

Architecture

TD capture (Phase B) → MCP server vision pipeline →
  1. Save PNG to /tmp/tdpilot_screen_<hash>.png
  2. Subprocess (if [ocr] installed): python ocr_worker.py < path → JSON
  3. Merge ocr_text + bounding boxes into vision payload
  4. Send to LLM with both image + text blocks

Subprocess lifecycle

  • Spawn on first OCR call (warmup ~5s; surface as {"type": "status", "status": "ocr_warming"} to chat UI)
  • Keep alive after warmup
  • Kill after 5min idle
  • Restart-on-crash with exponential backoff (1s, 2s, 4s; give up after 3 attempts, log + continue without OCR for the turn)

JSON-lines protocol

→ stdin: {"image_path": "/tmp/tdpilot_screen_abc.png"}
← stdout: {"text": "Error: COMP missing parent\n/project1/foo", "boxes": [...], "confidence": [0.98, 0.95]}

Tool surface

No new MCP tool — OCR runs INSIDE the vision pipeline transparently. Optionally expose td_ocr_image(path) if explicit invocation is useful (defer decision; ship without first).

Tests (10 total)

  • Subprocess spawn lifecycle (mock subprocess; 3 tests)
  • JSON-lines protocol roundtrip (mock subprocess; 2 tests)
  • Crash-and-restart with backoff (mock subprocess; 2 tests)
  • Vision-payload merge format (no subprocess; 2 tests)
  • End-to-end with real paddleocr on tests/fixtures/ocr_sample.png (@pytest.mark.skipif; 1 test)

Validation gates

pip install -e .[ocr]                                          # extras install succeeds
uv run pytest tests/test_v25_ocr.py -v                          # all pass (subprocess tests + skip integration if extras absent)
uv run pytest tests/ --ignore=tests/agent_evals -x -q          # no regressions; ~2023 pass
# Manual: run TD, send screenshot through Phase B path, verify ocr_text in chat-pipe log

Risks + mitigations

  • Risk: PaddleOCR install fails on Windows/Apple Silicon. Mitigation: extras-only install; clear error message if install fails; agent works fine without OCR.
  • Risk: First-run model download (~400MB) blocks first OCR call. Mitigation: lazy + status event so user sees "downloading OCR model…".
  • Risk: OCR latency adds 200-500ms to every Phase B capture. Mitigation: make Phase-B-OCR opt-in via Ocrmode COMP param (off / auto). Default auto when extras installed, off otherwise.

Resume from here

After this PR merges, mark v2.5.2-ocr as completed. Next phase: v2.5.3-tool-approval.


4. Phase v2.5.3 — Tool approval gates

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.3-tool-approval Tox rebuild: API .tox only (gate lives in chat-pipe runtime)

Why

v2.4 added the User-Intent Gate (B-008 Bug 8 fix) at system-prompt level. That's first-line defense. Tool Approval adds a runtime gate: even with valid auth + intent, destructive tools require explicit click-through. Layered defense. Closes the residual concern from project_tdpilot_api_authmode_open_default.md memory about drive-by-RCE risk in Authmode=open.

Pre-flight checks

grep "class TdpilotApiExt" td_component/tdpilot_api_extension.py    # extension class exists
grep -c "Authmode" td_component/build_tdpilot_api_tox.py            # Authmode param wired (≥1)

Files to create

  • tests/test_v25_tool_approval.py — ~15 tests across modes × gated tools × approve/deny/timeout

Files to modify

  • td_component/tdpilot_api_runtime.py_check_approval(tool_name, args) called before dispatch; gated-tool list constant
  • td_component/tdpilot_api_web_callbacks.py — new POST /approve endpoint
  • td_component/tdpilot_api_chat.html — approval banner UI with approve/deny + 30s countdown timer
  • td_component/build_tdpilot_api_tox.py — register Approvalmode Menu COMP param: off / destructive_only / all. Default destructive_only.
  • CHANGELOG.md — v2.5.3 entry

Gated tools (when Approvalmode=destructive_only)

APPROVAL_REQUIRED = frozenset({
    "td_exec_python",            # arbitrary code
    "td_delete_node",            # destructive
    "td_restore_snapshot",       # destructive
    "td_disconnect",             # network surgery
    "td_rename_node",            # only when target path is OUTSIDE the agent's own COMP
    "snapshot_restore_scoped",   # v2.3.0 addition; destructive
    "td_set_content",            # writes to DAT bodies; can be destructive
})

WS protocol

Server → Client: {
  "type": "approval_request",
  "id": "<uuid>",
  "tool": "td_delete_node",
  "args": {"path": "/project1/foo"},
  "timeout_ms": 30000
}

Client → Server (via POST /approve):
{
  "id": "<uuid>",
  "decision": "approve"   // or "deny"
}

Default behavior on timeout: deny

Inject tool_result: "User did not approve within 30s — tool denied." so the agent sees the denial as a normal tool error and can adapt (apologize, switch strategy, or ask the user explicitly).

Composition with existing gates

  1. Layer 1: User-Intent Gate (Bug 8 fix, system-prompt level) — blocks destructive tools when user message doesn't authorize.
  2. Layer 2: Tool Approval (this phase, runtime level) — even if Layer 1 lets through, asks click-through.
  3. Layer 3: Auth (Authmode token/open) — gates entire /send endpoint.
  4. Layer 4: Origin allowlist — rejects cross-origin browser CSRF.

All four layers compose. Each is opt-out individually if user needs unattended/headless operation.

Tests (15 total)

  • 1 per gated tool × Approvalmode=off (passes through unchanged) — 7 tests
  • 1 per mode (off, destructive_only, all) × td_exec_python × approve/deny/timeout — 9 tests, but overlap with above → net 8 new
  • 1 composition test: User-Intent-Gate-blocked tool also Tool-Approval-blocked (defense in depth) → 1 test
  • 1 chat-UI render test (mocked WS message) → 1 test

Validation gates

uv run pytest tests/test_v25_tool_approval.py -v
uv run pytest tests/ --ignore=tests/agent_evals -x -q
# Manual: rebuild API tox, drag .tox into TD, set Approvalmode=destructive_only,
#   send "delete node /project1/foo", verify banner appears

Risks + mitigations

  • Risk: Breaks unattended / CI / headless workflows. Mitigation: Approvalmode=off escape hatch; documented prominently in README.
  • Risk: Banner UX gets in the way during live performance. Mitigation: 30s timeout auto-denies (safe default); user can flip to off per performance.
  • Risk: Race between approval response and turn timeout. Mitigation: turn budget (Phase C.9 thinking-budget) extends while awaiting approval.

Resume from here

After this PR merges, mark v2.5.3-tool-approval as completed. Next phases (4–7) are quick polish — can dispatch in parallel.


5. Phase v2.5.4 — Auth fallback file

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.4-auth-fallback Tox rebuild: None

Why

The dpsk4 fork already cites ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env in AGENTS.md naming pins, but the MCP-server auth code may still rely on env vars only (verify). Closes the cold-start auth-401 scenario from feedback_td_mcp_auth.md memory.

Pre-flight checks

grep -rE "TD_MCP_SHARED_SECRET" src/td_mcp/auth_bootstrap.py    # confirm current source
test -f ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env                      # may or may not exist on dev machine

Files to modify

  • src/td_mcp/auth_bootstrap.py — read fallback file when TD_MCP_SHARED_SECRET env var unset; one-shot migration on first launch (if env IS set, copy to file so subsequent restarts work)
  • README.md + docs/GETTING_STARTED.md — document the fallback path
  • tests/test_v25_auth_fallback.py (new) — 5 tests

Resolution precedence

  1. TD_MCP_SHARED_SECRET env var (if set)
  2. ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env file (single-line TD_MCP_SHARED_SECRET=<value>)
  3. Legacy ~/.tdpilot-api/api_key (back-compat)
  4. None → MCP server starts in "no-secret" mode (open; print warning)

Migration

On startup, if env IS set and file is missing → write env value to file (0600 perms, atomic via os.rename from tmp). One-shot side effect. Log: "[auth_bootstrap] migrated TD_MCP_SHARED_SECRET env → ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env".

Tests (5)

  • env-present, file-absent → use env
  • env-absent, file-present → use file
  • both present → env wins
  • both absent, legacy present → use legacy + log deprecation
  • file corrupt (not key=value) → ignore, log warning

Validation gates

uv run pytest tests/test_v25_auth_fallback.py -v
test -f ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env && cat ~/.tdpilot-dpsk4/.tdpilot-dpsk4.env | grep -q TD_MCP_SHARED_SECRET
# Manual: unset env, restart MCP server, verify it still authenticates

Resume from here

After merge, mark completed. Independent of other phases.


6. Phase v2.5.5 — TD 2025.32820 release card

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.5-td-release-card Tox rebuild: None

Why

TD 2025.32820 (May 2026) added Trace/Triangulate POP, DMX POP pipeline, Layer Mix TOP, RTX Video TOP, ST2110 I/O, color overhaul. Content already lives in project_td_2025_32820_features.md memory. Materialize as a queryable knowledge card.

Pre-flight checks

ls src/td_mcp/knowledge/                                       # confirm knowledge corpus dir
grep -rl "td_2025" src/td_mcp/knowledge/ 2>/dev/null           # may or may not have one already

Files to create

  • src/td_mcp/knowledge/td_releases/td_2025_32820.md — full release notes
    • Header: build number, date, OS support, breaking changes
    • Sections: POPs additions (Trace, Triangulate), DMX pipeline, TOPs (Layer Mix, RTX Video), I/O (ST2110), color management changes
    • Compatibility table

Files to modify

  • src/td_mcp/registry/knowledge_corpus_tools.py (or equivalent — check actual filename) — verify the new markdown file is picked up by td_search_official_docs + td_get_release_delta

Tests

  • Add to existing knowledge corpus tests: assert the new card is indexed and td_get_release_delta("2025.32820") returns content

Validation gates

uv run python -c "from td_mcp.knowledge import load_corpus; print('td_2025_32820' in load_corpus())"
uv run pytest tests/test_knowledge_corpus.py -v

Resume from here

1-hour task. Mark completed immediately after.


7. Phase v2.5.6 — npm wrapper stdout/stderr separation

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.6-npm-stdio Tox rebuild: None

Why

When MCP server's Python logging goes to stdout, it mixes with JSON-RPC traffic and breaks Claude Desktop / Claude Code MCP framing. dreamrec/TDPilot v1.6.12 fixed this upstream. We forked before that fix → likely broken under Claude Desktop. Verify by reading our current npm/bin.js.

Pre-flight checks

cat npm/bin.js | head -40                                      # inspect current spawn config
grep -E "stdio|stderr" npm/bin.js

Files to modify

  • npm/bin.js — explicit stdio mode + stderr passthrough:
    const proc = spawn(pythonBin, args, {
        stdio: ['inherit', 'inherit', 'pipe']  // stderr piped
    });
    proc.stderr.on('data', d => process.stderr.write(d));
    proc.on('exit', code => process.exit(code));
  • tests/test_npm_wrapper.js (new, if doesn't exist) — smoke test asserting stdout contains only {-prefixed JSON lines and stderr contains the log marker

Validation gates

# Cold test in Claude Desktop / Claude Code MCP config
# Or: npx tdpilot-dpsk4 < /dev/null 2>/tmp/err > /tmp/out
#     grep -c "^{" /tmp/out                    # stdout: only JSON
#     grep -q "td_mcp" /tmp/err                # stderr: has logs

Resume from here

Independent. Mark completed after.


8. Phase v2.5.7 — td_check_for_updates

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.7-check-updates Tox rebuild: MCP only (new tool registered)

Why

Read-only update awareness. Surfaces BOTH server-version drift AND .tox source-hash freshness so the agent can advise users on what to do next. Full auto-apply (td_self_update) defers to v2.7 paired with Tox Updater (see v2.7_IMPLEMENTATION_PLAN.md).

Files to create

  • src/td_mcp/registry/lifecycle_tools.py — register td_check_for_updates
  • src/td_mcp/lifecycle/__init__.py
  • src/td_mcp/lifecycle/update_check.py — GitHub Releases API client + .tox-source-hash.json comparator
  • tests/test_v25_check_updates.py — 8 tests (mock GitHub API)

Tool surface

@mcp.tool()
async def td_check_for_updates() -> dict:
    """Check GitHub Releases for newer tdpilot-dpsk4 versions.
    Returns server-version comparison AND .tox source-hash freshness."""
    return {
        "server": {
            "current": "2.5.0",
            "latest": "2.5.1",
            "has_update": True,
            "url": "https://github.com/dreamrec/TDPilot_deepseekv4/releases/tag/v2.5.1"
        },
        "tox": {
            "tdpilot-dpsk4.tox": {
                "hash_matches": True,
                "rebuild_needed": False
            },
            "tdpilot_API.tox": {
                "hash_matches": False,
                "rebuild_needed": True,
                "reason": "source files modified since last build"
            }
        },
        "advice": "Server has an update. Run `npx tdpilot-dpsk4@latest`. Also rebuild tdpilot_API.tox in TouchDesigner."
    }

GitHub API request

  • GET https://api.github.com/repos/dreamrec/TDPilot_deepseekv4/releases/latest
  • Cache result for 1 hour (avoid hammering API)
  • Graceful failure: return {"server": {"check_failed": True, "reason": "..."}} instead of raising

Tests (8)

  • Mock GitHub API, version comparison correct (3 tests for newer/same/older)
  • .tox-source-hash.json drift detection (2 tests)
  • Cache: second call within 1h hits cache (1 test)
  • Graceful failure on 5xx / network error (1 test)
  • Tool registered & discoverable (1 test)

Validation gates

uv run pytest tests/test_v25_check_updates.py -v
grep "EXPECTED_MIN_TOOL_COUNT: int = 110" src/td_mcp/release_gates.py
# Manual: invoke td_check_for_updates from chat-pipe, verify advice string is actionable

Resume from here

Mark completed. Full update-apply path lives in v2.7.


9. Phase v2.5.8 — Log receiver (stretch)

Status: SHIPPED (v2.5.0, 2026-05-19) Branch suggestion: claude/v2.5.8-log-receiver Tox rebuild: API only Drop first if v2.5 schedule slips.

Why

The plan: ship a Log Receiver operator. Tail MCP-server stderr (and chat-pipe internal logs) into a TD Table DAT for in-app debugging. Multiplies the value of activity_log + journal_hints.

Files to create

  • td_component/tdpilot_api_log_tail.py — log buffer + Table-DAT renderer
  • tests/test_v25_log_receiver.py — 10 tests

Files to modify

  • td_component/tdpilot_api_chat.html — add "Logs" tab (lazy-loaded)
  • td_component/build_tdpilot_api_tox.py::_API_TOX_SOURCE_FILES — add tdpilot_api_log_tail.py

Skip if blocked

If chat HTML restructure feels heavy, ship without UI — table DAT alone is debuggable from Textport.

Resume from here

Mark completed or deferred_to_v2.6.


10. Release engineering — v2.5.0 ship checklist

Version bumps (seven manifests, all must match)

pyproject.toml                                    # version = "2.5.0"
src/td_mcp/__init__.py                            # __version__ = "2.5.0"
.claude-plugin/plugin.json                        # version
.claude-plugin/marketplace.json                   # plugins[0].version (drives Update button)
npm/package.json                                  # version
mcp/manifest.json                                 # version
td_component/mcp_webserver_callbacks.py           # API_VERSION  → forces tox rebuild

Plus EXPECTED_MIN_TOOL_COUNT: int = 110 in release_gates.py (assuming all 5 new tools land; if log_receiver slips, → 109).

User-facing tool-count copies to update

README.md, npm/README.md, plugin_README.md, docs/API_REFERENCE.md, docs/USER_GUIDE.md, docs/GETTING_STARTED.md, skills/tdpilot-dpsk4-core/SKILL.md, skills/tdpilot-dpsk4-production/SKILL.md (if exists), CLAUDE.md (user's global instructions — note: NOT in repo, mention in CHANGELOG instead), CHANGELOG.md top entry, .claude-plugin/marketplace.json description field, npm/package.json description, GitHub repo description (via gh repo edit dreamrec/TDPilot_deepseekv4 --description "...").

.tox rebuild discipline

Both .tox files need rebuild. Canonical Textport recipe in feedback_td_tox_rebuild_recipe.md memory. Auto-mirror (v2.2.1+) syncs to main repo.

Local pre-push sweep

uv run pytest tests/ --ignore=tests/agent_evals -x -q
uv run --extra dev ruff format --check src tests scripts td_component
uv run --extra dev ruff check src tests scripts td_component
uv run python scripts/check_versions.py
uv run python scripts/check_tox_freshness.py
uv run python scripts/check_tox_api_freshness.py

Plugin ZIP

uv run python scripts/build_plugin_zip.py

Tag + release

git tag v2.5.0 -m "TDPilot DPSK4 v2.5.0 — Agent self-awareness + safety + distribution polish"
git push origin v2.5.0
gh release create v2.5.0 \
  --title "v2.5.0 — Agent self-awareness + safety + distribution polish" \
  --notes-from-tag \
  td_component/tdpilot-dpsk4.tox \
  td_component/tdpilot_API.tox \
  tdpilot-dpsk4.plugin.zip \
  tdpilot-dpsk4.mcpb

npm publish

Trusted Publisher OIDC handles this automatically on tag push via .github/workflows/release-assets.yml. Do NOT add NPM_TOKEN — per feedback_npm_trusted_publisher_oidc.md memory.

Update marketplace.json description

After tag, edit description to mention v2.5 highlights. This is what users see on the plugin panel.


11. Risk register — v2.5 cross-cutting

Risk Phase Probability Impact Mitigation
OCR [ocr] extras fail on Windows / Apple Silicon 2.5.2 Med Med Optional install, clear error, agent works without OCR
Tool approval breaks unattended CI/headless 2.5.3 Med High Approvalmode=off escape hatch + README documentation
Activity log ring grows memory unbounded 2.5.1 Low Low deque(maxlen=200) + slots=True
Journal hints redundant with B-007 / cycle-detect 2.5.1 Low Low Tests pin distinct trigger thresholds (count=2 vs 3 vs 4)
Auth fallback collides with legacy storage 2.5.4 Low Med Precedence rules tested explicitly
td_check_for_updates hammers GitHub API 2.5.7 Med Low 1h cache; graceful 5xx fallback
Tool count drift fails CI All Med Low check_versions.py runs pre-push; bump EXPECTED_MIN_TOOL_COUNT per phase

12. Resume instructions per phase

If you're picking up v2.5 work in a fresh session:

  1. Read this file fully (you're doing it now).
  2. Check the phase-overview table at §1 — find the first row whose Status is not_started or in_progress.
  3. Follow that phase's section (§2–§9) to the letter:
    • Pre-flight checks → Files to create → Files to modify → Tests → Validation gates → Update status
  4. Before committing: run validation gates AT MINIMUM. Pre-push sweep ideally.
  5. PR title format: feat(v2.5.N): <one-line description> (matches v2.4 convention).
  6. After PR merges to main: edit this file's §1 overview table to set Status: completed. Push the doc update.

If all phases say completed

Run §10 release engineering. After gh release create v2.5.0 succeeds, mark this entire plan archived and create v2.5.1_IMPLEMENTATION_PLAN.md (patch release) if any follow-up fixes are needed, OR proceed to v2.6_IMPLEMENTATION_PLAN.md.

If you hit a blocker

Mark the phase Status: blocked and write a one-paragraph note under the phase explaining what blocks. Move to the next independent phase (e.g., if v2.5.2 OCR is blocked on PaddleOCR install woes, jump to v2.5.4 auth fallback).


End of v2.5 plan. Next: v2.6_IMPLEMENTATION_PLAN.md (retrieval + knowledge).