Symptom
Staging @hetz_lba1_bot running 0.35.3rc12. Plan-mode research/audit completions now produce final Telegram messages of 25k-42k characters — split into 8-12 Telegram messages. User report (Nathan, 2026-05-12): "I had a summary from Claude Code yesterday which was 11 Telegram messages long!! What I really want back is to have Claude Code provide summaries like we have here in command line — summaries of plans (not the entire plan), summaries of recommendations and/or findings and/or next steps (where relevant)."
The rc11 fix for #508 has the right intent but over-corrects: instead of the original 584-char "Plan approved" repro, we now ship the entire plan body + a paraphrased restatement + the structured ## Summary block, all concatenated.
Evidence from staging logs (journalctl --user -u untether.service --since '48 hours ago')
| chat |
date |
answer_len |
approximate Telegram messages |
| aushistory (-5189982707) |
2026-05-11 02:37 |
14,267 |
~4 |
| aushistory |
2026-05-11 03:06 |
28,195 |
~8 |
| aushistory |
2026-05-11 03:34 |
35,463 |
~10 |
| aushistory |
2026-05-11 03:59 |
42,634 |
~12 ← user's repro |
| scout (-5243261989) |
2026-05-12 03:06 |
26,176 |
~8 |
| scout |
2026-05-12 03:32 |
27,558 |
~8 |
Pre-fix comparison (rc10, 2026-05-10 04:16, scout): 584 chars — "Plan approved — research is complete..."
Telegram MCP search_messages for the literal 📋 Plan (approved): returned hits on every recent plan-mode completion in both chats — confirming Layer E IS the load-bearing over-firer (not preamble alone).
Root cause — three stacked over-shoots
-
Preamble A1 (src/untether/runner_bridge.py:293-304) tells Claude: "For research/audit tasks where no further work is expected after approval, expand the bullets into a substantive summary." — plan body now 2,000-5,000 chars instead of 3-5 bullets.
-
Preamble A2 (same paragraph) instructs: "your next assistant message ... MUST repeat the substantive findings or decisions" — Claude paraphrases the plan, producing another 500-2,000 chars.
-
Layer E (src/untether/runners/claude.py:_prepend_exitplanmode_plan) prepends the plan body with the substring-skip rule body in final_answer. Because A2 told Claude to paraphrase rather than literal-copy, the substring check fails on every well-behaved run and Layer E concatenates the full plan body in front of the paraphrased post-approval text — doubling the content.
Fix (rc13)
Replace verbose-driving preamble clauses with CLI-style brevity; replace the broken substring skip with a length gate; cap the plan-body re-emit when it does fire.
A1 — plan body shape
When calling ExitPlanMode, your plan parameter MUST be a concise 3-5 bullet summary of findings, decisions, or proposed changes — never just a file path. Keep it short: the plan is shown to the user for approval, not as the final deliverable.
A2 — post-approval text shape
After ExitPlanMode is approved, your next assistant message — which becomes the user's final Telegram message — MUST contain a brief CLI-style summary: 3-7 bullets or 1-2 short paragraphs covering key findings, recommendations, decisions, and next steps. Aim for ~500-1500 characters. Do not re-paste the full plan content — the user has already seen it during approval. Brevity is the goal.
A3 — ## Summary ### Plan/Document Created block
[Path AND a 3-5 bullet headline summary — the user has already seen the plan during approval, so this is a pointer + headline, not a re-paste of the full content]
Layer E (_prepend_exitplanmode_plan)
- Replace
body in final_answer substring check with len(final_answer or "") < 600 length gate. The substring check remains as a secondary skip (cheap; covers exact-copy edge cases).
- When prepending, cap plan body to first 1500 chars +
…\n\n(plan truncated — shown in full during approval) to prevent runaway when the gate does fire.
Tests
tests/test_preamble.py: update A1/A2/A3 assertions to match new wording; add coverage for substring-skip, length-gate, and body-cap branches of _prepend_exitplanmode_plan.
tests/test_claude_runner.py: existing test_translate_result_prepends_exitplanmode_plan_into_answer covered the prepend path — extend to confirm a longer (>600 char) post-approval answer does NOT trigger the prepend, and that body-cap fires for >1500-char plans.
Verification
- Integration test via
@untether_dev_bot on a Claude plan-mode chat with a research-task prompt ("audit X and tell me Y"). Expect:
Target release
v0.35.3rc13. Ships on dev from fix/v0.35.3rc13-plan-summary-overfire.
References
Symptom
Staging
@hetz_lba1_botrunning 0.35.3rc12. Plan-mode research/audit completions now produce final Telegram messages of 25k-42k characters — split into 8-12 Telegram messages. User report (Nathan, 2026-05-12): "I had a summary from Claude Code yesterday which was 11 Telegram messages long!! What I really want back is to have Claude Code provide summaries like we have here in command line — summaries of plans (not the entire plan), summaries of recommendations and/or findings and/or next steps (where relevant)."The rc11 fix for #508 has the right intent but over-corrects: instead of the original 584-char "Plan approved" repro, we now ship the entire plan body + a paraphrased restatement + the structured
## Summaryblock, all concatenated.Evidence from staging logs (
journalctl --user -u untether.service --since '48 hours ago')Pre-fix comparison (rc10, 2026-05-10 04:16, scout): 584 chars — "Plan approved — research is complete..."
Telegram MCP
search_messagesfor the literal📋 Plan (approved):returned hits on every recent plan-mode completion in both chats — confirming Layer E IS the load-bearing over-firer (not preamble alone).Root cause — three stacked over-shoots
Preamble A1 (
src/untether/runner_bridge.py:293-304) tells Claude: "For research/audit tasks where no further work is expected after approval, expand the bullets into a substantive summary." — plan body now 2,000-5,000 chars instead of 3-5 bullets.Preamble A2 (same paragraph) instructs: "your next assistant message ... MUST repeat the substantive findings or decisions" — Claude paraphrases the plan, producing another 500-2,000 chars.
Layer E (
src/untether/runners/claude.py:_prepend_exitplanmode_plan) prepends the plan body with the substring-skip rulebody in final_answer. Because A2 told Claude to paraphrase rather than literal-copy, the substring check fails on every well-behaved run and Layer E concatenates the full plan body in front of the paraphrased post-approval text — doubling the content.Fix (rc13)
Replace verbose-driving preamble clauses with CLI-style brevity; replace the broken substring skip with a length gate; cap the plan-body re-emit when it does fire.
A1 — plan body shape
A2 — post-approval text shape
A3 —
## Summary### Plan/Document CreatedblockLayer E (
_prepend_exitplanmode_plan)body in final_answersubstring check withlen(final_answer or "") < 600length gate. The substring check remains as a secondary skip (cheap; covers exact-copy edge cases).…\n\n(plan truncated — shown in full during approval)to prevent runaway when the gate does fire.Tests
tests/test_preamble.py: update A1/A2/A3 assertions to match new wording; add coverage for substring-skip, length-gate, and body-cap branches of_prepend_exitplanmode_plan.tests/test_claude_runner.py: existingtest_translate_result_prepends_exitplanmode_plan_into_answercovered the prepend path — extend to confirm a longer (>600 char) post-approval answer does NOT trigger the prepend, and that body-cap fires for >1500-char plans.Verification
@untether_dev_boton a Claude plan-mode chat with a research-task prompt ("audit X and tell me Y"). Expect:📋 Plan (approved):does NOT appear when A2 produces a real summaryTarget release
v0.35.3rc13. Ships on
devfromfix/v0.35.3rc13-plan-summary-overfire.References
24b6684+51c52c8)