Skip to content

rc11/rc12 #508 fix over-fires: 11-message Telegram finals on research/audit runs (plan body + post-approval text both bloated + Layer E concatenates) #515

@nathanschram

Description

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

  1. 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.

  2. 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.

  3. 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

Target release

v0.35.3rc13. Ships on dev from fix/v0.35.3rc13-plan-summary-overfire.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions