Repository navigation
fix(server): child threads keep reporting to their parent after a restart (#48) - #49
Conversation
…tart (#48) The threads toolkit bridge kept each child's reportBack flag and the last reported message only in memory, so after a server restart no child turn was reported back to its parent. Write both into the parent's task.* rows and restore them from there the first time a child's session changes after a restart. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Independent review of #49 at
|
… window (#48) Review of #49: the parent's thread detail holds only its newest 500 activities, so a child's task.started row and last reportedMessageId row can fall out of it. Every idle task.progress row now carries reportBack and the latest reportedMessageId, and restore reads the newest row. A child with no surviving row treats its current last reply as already reported. One parent read restores all of that parent's children. The restart test now covers a reportBack:false child. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review findings addressed in
Checks at this head:
|
Re-review of #49: delta
|
Closes #48.
Problem
Router child threads survive a server restart. The parent link and role stay in the
sub.<parent>.<role>-<rand>id, and every provider resumed its native session. But the threads toolkit bridge kept each child'sreportBackflag and its last reported message only in memory. After a restart, no finished child turn reached its parent. In the live run, 0 of 6 post-restart child turns were reported, against every turn before the restart.Fix
reportBackinto the child'stask.startedrow andreportedMessageIdinto thetask.progressrow that triggers a report, both on the parent.childReportState.ts.task.progressrow carries both values, and restore reads the newest row per child from one read of the parent. A child with no row left in the parent's 500-activity window restores asreportBack: true, with its current last reply counted as already reported. The remaining gap is documented inchildReportState.ts.Files:
apps/server/src/mcp/toolkits/threads/childReportState.tsand its testapps/server/src/mcp/toolkits/threads/handlers.ts: fork-owned and already allowlisted; persist and restoredocs/fork-features.md:child-threadsgains the new files and Verify Router child threads continue after a server restart #48No upstream-owned file changed.
Proof
childReportState.test.tsruns the real engine and projections on one SQLite file and restarts the toolkit twice against it, waiting on domain events with no sleeps:handlers.ts(the report never arrives) and passes with the fix.vp test run src/mcp/toolkits/threads/30/30; apps/servertsc --noEmitexit 0;vp lintandvp fmtclean on touched files;scripts/fork-check.shOK;fork-maintenance.test.ts12/12.reviewerrole child, kept their parent link, role, native session ids and earlier context across a restart.Independent review is still open, as #48 requires.
Model and harness: Claude Opus 5.5 in Claude Code (running inside T3 Code).
🤖 Generated with Claude Code