chore: sync master into dev to unblock v0.35.1 release#310
Merged
Nathan Schram (nathanschram) merged 2 commits intodevfrom Apr 15, 2026
Merged
chore: sync master into dev to unblock v0.35.1 release#310Nathan Schram (nathanschram) merged 2 commits intodevfrom
Nathan Schram (nathanschram) merged 2 commits intodevfrom
Conversation
* feat: three-mode support, startup mode indicator, dev branch workflow (#158, #159) * ci: add CODEOWNERS, update action SHA pins, add permission comments - Create .github/CODEOWNERS requiring @littlebearapps/core review - Pin setup-uv to v7.4.0 (6ee6290f), download-artifact to v8.0.1 (3e5f45b2) - Add precise version comments on all action SHAs (codeql v3.32.6, pypi-publish v1.13.0, action-gh-release v2.5.0, fetch-metadata v2.5.0) - Document write permissions with why-comments (OIDC, releases, auto-merge) Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add release guard hooks and document protection in CLAUDE.md Defence-in-depth hooks prevent Claude Code from pushing to master, merging PRs, creating tags, or triggering releases. Feature branch pushes and PR creation remain allowed. - release-guard.sh: Bash hook blocking master push, tags, releases, PR merge - release-guard-protect.sh: Edit/Write hook protecting guard files and hooks.json - release-guard-mcp.sh: GitHub MCP hook blocking merge and master writes - hooks.json: register all three hooks - CLAUDE.md: document release guard, update workflow roles, CI pipeline notes Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: clarify /config default labels and remove redundant "Works with" lines Default labels now explain what "default" means for each setting: - Diff preview: "default (off)" — matches actual behaviour (was "default (on)") - Model/Reasoning: "default (engine decides)" - API cost: "default (on)", Subscription usage: "default (off)" - Plan mode home hint: "agent decides" - Diff preview home hint: "buttons only" Added info lines to plan mode and reasoning sub-pages explaining the default behaviour in more detail. Removed all 9 "Works with: ..." lines from sub-pages — they're redundant because engine visibility guards already hide settings from unsupported engines. Fixes #119 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: suppress redundant cost footer on error runs When a run fails (e.g. subscription limit hit), the diagnostic context line from _extract_error() already shows cost, turns, and API time. The 💰 cost footer was duplicating this same data in a different format. Now the cost footer only appears on successful runs where it's the sole source of cost information. Error runs still show cost in the diagnostic line, and budget alerts still fire regardless. Also adds usage field to mock Return dataclass (matching ErrorReturn) so tests can verify cost footer behaviour on success runs. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: suppress stall notifications when CPU-active + heartbeat re-render When cpu_active=True (extended thinking, background agents), suppress Telegram stall warning notifications and instead trigger a heartbeat re-render so the elapsed time counter keeps ticking. Notifications still fire when cpu_active=False or None (no baseline). Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: staging 0.34.5rc2 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: CI release-validation tomllib bytes/str mismatch tomllib.loads() expects str but was receiving bytes from sys.stdin.buffer.read() and open(...,'rb').read(). First triggered when PR #122 changed the version (rc1 → rc2). Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs: integrate screenshots into docs with correct JPG references - Add 44 screenshots to docs/assets/screenshots/ - Fix all image refs from .png to .jpg across 25 doc files - README uses absolute raw.githubusercontent.com URLs for PyPI rendering - Fix 5 filename mismatches (session-auto-resume→chat-auto-resume, etc.) - Comment out 11 missing screenshots with TODO markers - Add CAPTURES.md checklist tracking capture status Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: convert markdown images to HTML img tags for GitHub compatibility Switch from MkDocs `{ loading=lazy }` syntax to HTML `<img>` tags with width="360" and loading="lazy". Fixes two GitHub rendering issues: `{ loading=lazy }` appearing as visible text, and oversized images with no width constraint. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: fix 3 screenshot mismatches and replace 3 screenshots - first-run.md: rewrite resume line text to match footer screenshot - interactive-control.md: update planmode show admonition to match screenshot (auto not on) - switch-engines.md: swap engine-footer.jpg for multi-engine-switch.jpg - Replace startup-message.jpg with clean v0.34.4 capture (was rc/6-projects) - Replace cooldown-auto-deny.jpg with post-outline approve/deny buttons - Replace file-put.jpg with photo save confirmation Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add iOS caption limitation note to file transfer guide Telegram iOS doesn't show a caption field when sending documents via the File picker, so /file put <path> captions aren't easily accessible. Added a note with workarounds (use Desktop, send as photo, or let auto-save handle it). Updated screenshot alt text to match actual screenshot content. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: temp swap README image URLs to feature branch for preview Will revert to master before merging. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: lay out all 3 README screenshots in a single row Reduce from 360px to 270px each and combine into one <p> block so all three hero screenshots sit side by side. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap 3rd hero screenshot for config-menu for visual variety Replace plan-outline-approve (too similar to approval-diff-preview) with config-menu showing the /config settings grid. The three hero images now tell: voice input → approve changes → configure everything. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add captions under README hero screenshots Small <sub> captions: "Send tasks by voice (Whisper transcription)", "Approve changes remotely", "Configure from Telegram". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: use table layout for README hero screenshots with captions Fixes stacking issue — <br> in a <p> broke inline flow. A table keeps images side by side with captions underneath each one. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: replace table layout with single hero collage image Composite image scales proportionally on mobile instead of requiring horizontal scroll. Captions baked into the image via ImageMagick. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap middle hero screenshot for full 3-button approval view Replace approval-diff-preview with approval-buttons-howto showing Approve / Deny / Pause & Outline Plan — more visually impressive. Caption now reads "Approve changes remotely (Claude Code)". Added footnote linking to engine compatibility table. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap config-menu for parallel-projects in hero collage Third hero screenshot now shows 10+ projects running simultaneously across different repos — much more compelling than a settings menu. New caption: "Run agents across projects in parallel". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: revert README image URL to master for merge Swap hero-collage URL back from feature/github-hardening to master. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.34.5rc3 - fix: preserve all EngineOverrides fields when setting model/planmode/reasoning (was silently wiping ask_questions, diff_preview, show_api_cost, etc.) - fix: /config home page resolves "default" to effective values - feat: file upload auto-deduplication (append _1, _2 instead of requiring --force) - feat: media groups without captions now auto-save instead of showing usage text - feat: resume line visual separation (blank line + ↩️ prefix) - fix: claude auto-approve echoes updatedInput in control response Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: expand permission policies for Codex CLI and Gemini CLI in /config Codex gets a new "Approval policy" page (full auto / safe) that passes --ask-for-approval untrusted when safe mode is selected. Gemini's approval mode expands from 2 to 3 tiers (read-only / edit files / full access) with --approval-mode auto_edit for the middle tier. Both engines now show an "Agent controls" section on the /config home page. Engine-specific model default hints replace the generic "from CLI settings" text. Also adds staging.sh helper, context-guard-stop hook, and docs updates. Closes #131 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.34.5rc4 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: /config UX overhaul, resume line toggle, cost budget settings, model metadata /config UX cleanup: - Convert all binary toggles from 3-column (on/off/clear) to 2-column (toggle + clear) for better mobile tap targets - Merge Engine + Model into combined "Engine & model" page - Reorganise home page to max 2 buttons per row across all engines - Split plan mode 3-option rows (off/on/auto) into 2+1 layout - Add _toggle_row() helper for consistent toggle button rendering New features: - #128: Resume line /config toggle — per-chat show_resume_line override via EngineOverrides with On/Off/Clear buttons, wired into executor - #129: Cost budget /config settings — per-chat budget_enabled and budget_auto_cancel overrides on the Cost & Usage page, wired into _check_cost_budget() in runner_bridge.py Model metadata improvements: - Show Claude Code [1m] context window suffix: "opus 4.6 (1M)" - Strip Gemini CLI "auto-" prefix: "auto-gemini-3" → "gemini-3" - Future-proof: unknown suffixes default to .upper() (e.g. [500k] → 500K) Bug fixes: - #124: Standalone override commands (/planmode, /model, /reasoning) now preserve all EngineOverrides fields including new ones - Error handling: control_response.write_failed catch-all in claude.py, ask_question.extraction_failed warning, model.override.failed logging Hardening: - Plan outline sent as separate ephemeral message (avoids 4096 char truncation) - Added show_resume_line, budget_enabled, budget_auto_cancel to EngineOverrides, EngineRunOptions, normalize/merge, and all constructors Tests: 1610 passed, 80.56% coverage, ruff clean. Integration tested on @untether_dev_bot across all 6 engine chats. Closes #128, closes #129, fixes #124 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: trigger CI for PR #132 * fix: address 11 CodeRabbit review comments on PR #132 Bug fixes: - claude.py: fix UnboundLocalError when factory.resume is falsy in ask_question.extraction_failed logging path - ask_question.py: reject malformed option callbacks instead of silently falling back to option 0 - files.py: raise FileExistsError when deduplicate_target exhausts 999 suffixes instead of returning the original (overwrite risk) - config.py: disambiguate Codex "Full auto" (fa) vs Gemini "Full access" (ya) callback IDs and toast labels Hardening: - codex.py: add --ask-for-approval to _EXEC_ONLY_FLAGS guard - model.py: add try/except to clear path (matching set path) - reasoning.py: add try/except to clear path (matching set path) - loop.py: notify user when media group upload fails instead of silently dropping - export.py: log session count instead of identifiers at info level - config.py: resolve resume-line default from config instead of hardcoding True - staging.sh: pin PyPI index in rollback/reset with --pip-args Skipped (not applicable): - CHANGELOG.md: RC versions don't get changelog entries per release discipline - docs/tutorials TODO screenshot: pre-existing, not introduced by PR - .claude/hooks/context-guard-stop.sh: ContextDocs plugin hook, not Untether source Tests: 1611 passed, 80.48% coverage, ruff clean. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: replace bare pass with debug log to satisfy bandit B110 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: setup wizard security + UX improvements - Auto-set allowed_user_ids from captured Telegram user ID during onboarding (security: restricts bot to the setup user's account) - Add "next steps" panel after wizard completion with pointers to /config, voice notes, projects, and account lock confirmation - Update install.md: Python 3.12+ (not just 3.14), dynamic version string, /config mention for post-setup changes - Update first-run.md: /config → Engine & model for default engine Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: plan outline UX — markdown rendering, buttons, cleanup (#139, #140, #141) - Render outline messages as formatted text via render_markdown() + split_markdown_body() instead of raw markdown (#139) - Add approve/deny buttons to last outline message so users don't have to scroll up past long outlines (#140) - Delete outline messages on approve/deny via module-level _OUTLINE_REGISTRY callable from callback handler; suppress stale keyboard on progress message (#141) - 8 new tests for outline rendering, keyboard placement, and cleanup - Bump version to 0.35.0rc5 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: /continue command — cross-environment resume for all engines (#135) New `/continue` command resumes the most recent CLI session in the project directory from Telegram. Enables starting a session in your terminal and picking it up from your phone. Engine support: Claude (--continue), Codex (resume --last), OpenCode (--continue), Pi (--continue), Gemini (--resume latest). AMP not supported (requires explicit thread ID). Includes ResumeToken.is_continue flag, build_args for all 6 runners, reserved command registration, resume emoji prefix stripping for reply-to-continue, docs (how-to guide, README, commands ref, routing explanation, conversation modes tutorial), and 99 new test assertions. Integration tested against @untether_dev_bot — all 5 supported engines passed secret-recall verification via Telegram MCP. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: agent outbox file delivery + fix cross-chat ask stealing (#143, #144) Outbox delivery (#143): agents write files to .untether-outbox/ during a run; Untether sends them as Telegram documents on completion with 📎 captions. Config: outbox_enabled, outbox_dir, outbox_max_files, outbox_cleanup. Deny-glob security, size limits, auto-cleanup. Preamble updated for all 6 engines. Integration tested across Claude, Codex, OpenCode, Pi, and Gemini. AskUserQuestion fix (#144): _PENDING_ASK_REQUESTS and _ASK_QUESTION_FLOWS were global dicts with no chat_id scoping — a pending ask in one chat would steal the next message from any other chat. Added channel_id contextvar and scoped all ask lookups by it. Session cleanup now also clears stale pending asks. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: v0.35.0 changelog completion + fix #123 updatedInput - Complete v0.35.0 changelog: add missing entries for /continue (#135), /config UX overhaul (#132), resume line toggle (#128), cost budget (#129), model metadata, resume line formatting (#127), override preservation (#124), and updatedInput fix (#123) - Fix #123: register input for system-level auto-approved control requests so updatedInput is included in the response - Add parameterised test for all 5 auto-approve types input registration - Remove unused OutboxResult import (ruff fix) Issues closed: #115, #118, #123, #124, #126, #127, #134 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.35.0rc6 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: rc6 integration test fixes (#145, #146, #147, #148, #149) - Reduce Telegram API timeout from 120s to 30s (#145) - OpenCode error runs show error text instead of empty body (#146) - Pi /continue captures session ID via allow_id_promotion (#147) - Post-outline approval uses skip_reply to avoid "not found" (#148) - Orphan progress message cleanup on restart (#149) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: post-outline notification reply + OpenCode empty body (#148, #150) - #148: skip_reply callback results now bypass the executor's default reply_to fallback, sending directly via the transport with no reply_to_message_id. Previously, the executor treated reply_to=None as "use default" which pointed to the (deleted) outline message. - #150: OpenCode normal completion with no Text events now falls back to last_tool_error. Added state.last_tool_error field populated on ToolUse error status. Covers both translate() and stream_end_events(). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: suppress post-outline notification to avoid "message not found" (#148) After outline approval/denial, the progress loop's _send_notify was firing for the next tool approval, but the notification's reply_to anchor could reference deleted state. Added _outline_just_resolved flag to skip one notification cycle after outline cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: note OpenCode lacks auto-compaction — long sessions degrade (#150) Added known limitation to OpenCode runner docs and integration testing playbook. OpenCode sessions accumulate unbounded context (no compaction events unlike Pi). Workaround: use /new before isolated tests. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: skip_reply on regular approve path when outline was deleted (#148) The "Approve Plan" button on outline messages uses the real ExitPlanMode request_id, routing through the regular approve path (not the da: synthetic path). When outline messages exist, set skip_reply=True on the CommandResult to avoid replying to the just-deleted outline message. Also added reply_to_message_id and text_preview to transport.send.failed warning for easier debugging. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: update changelog for rc6 integration test fixes (#145-#150) Updated fix descriptions for #146/#150 (OpenCode last_tool_error fallback) and #148 (regular approve path skip_reply). Added docs section for OpenCode compaction limitation. Updated test counts. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: fix formatting after merge resolution Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address CodeRabbit review comments on PR #151 - bridge.py: replace text_preview with text_len in send failure warning to avoid logging raw message content (security) - runner_bridge.py: move unregister_progress() after send_result_message() to avoid orphan window between ephemeral cleanup and final message send - cross-environment-resume.md: add language spec to code block Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: resolve /config "default" labels to effective on/off values (#152) Sub-pages showed "Current: default" or "default (on/off)" while buttons already showed the resolved value. Now all boolean-toggle settings show the effective on/off value in both text and buttons. Affected: verbose, ask mode, diff preview, API cost, subscription usage, budget enabled/auto-cancel, resume line. Home page cost & resume labels also resolved. Plan mode, model, and reasoning keep "default" since they depend on CLI settings and aren't simple on/off booleans. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: update changelog for rc7 config default labels fix (#152) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: update documentation for v0.35.0 - fix missing nav entries in zensical.toml (cross-env resume, Gemini/Amp runners) - rewrite inline-settings.md for /config UX overhaul (2-column toggles, budget/resume toggles) - update plan-mode.md with outline rendering, buttons-on-last-chunk, ephemeral cleanup - update interactive-control tutorial with outline UX improvements - add orphan progress cleanup section to operations.md - add engine-specific approval policies to interactive-approval.md - add per-chat budget overrides to cost-budgets.md - update module-map.md with Gemini/Amp and new modules (outbox, progress persistence, proc_diag) - update architecture.md mermaid diagrams with all 6 engines - bump specification.md to v0.35.0, add progress persistence and outbox sections - add v0.35.0 screenshot entries to CAPTURES.md Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: broaden frozen ring buffer stall escalation beyond MCP tools (#155) Frozen ring buffer escalation was gated on `mcp_server is not None`, so general stalls with cpu_active=True and no MCP tool running were silently suppressed indefinitely. Broadened to fire for all stalls after 3+ checks with no new JSONL events regardless of tool type. New notification: "CPU active, no new events" for non-MCP frozen stalls. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: tool approval buttons no longer suppressed after outline approval (#156) After "Approve Plan" on an outline, the stale discuss_approve action remained in ProgressTracker with completed=False. The renderer picked up its stale "Approve Plan"/"Deny" buttons first, then the suppression logic at line 994 stripped ALL buttons — including new Write/Edit/Bash approval buttons. Claude blocked indefinitely waiting for approval. Fix: after suppressing stale buttons, complete the discuss_approve action(s) in the tracker, reset _outline_sent, and trigger a re-render so subsequent tool requests get their own Approve/Deny buttons. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add workflow mode indicator to startup message, fix startup crash on topics validation failure (#158, #159) Features: - Startup message now shows mode: assistant/workspace/handoff - Derived from session_mode + topics.enabled config values - _resolve_mode_label() helper in backend.py Bug fixes: - Fix UnboundLocalError crash when topics validation fails on startup (#158) - Moved import signal and shutdown imports before try block in loop.py - Downgrade can_manage_topics check from fatal error to warning (#159) - Bot can now start without manage_topics admin right - Existing topics work fine; only topic creation/editing affected Tests: - 17 new unit tests for stateless/handoff mode (test_stateless_mode.py) - _should_show_resume_line, _chat_session_key, ResumeResolver, ResumeLineProxy - Integration-level: stateless shows resume lines, no auto-resume, chat hides lines - 3 new tests for mode indicator in startup message (test_telegram_backend.py) Docs: - New docs/reference/modes.md — comprehensive reference for all 3 workflow modes - Updated docs/reference/index.md and zensical.toml nav with modes page * docs: comprehensive three-mode coverage across all documentation New: - docs/how-to/choose-a-mode.md — decision tree, mode comparison, mermaid sequence diagrams, configuration examples, switching guide, workspace prerequisites Updated: - README.md — improved three-mode description in features list - docs/tutorials/install.md — added mode selection step (section 10) - docs/tutorials/first-run.md — added 'What mode am I in?' tip - docs/reference/config.md — cross-linked session_mode/show_resume_line to modes.md - docs/reference/transports/telegram.md — added mode requirement callouts for forum topics and chat sessions sections - docs/how-to/chat-sessions.md — added session persistence explanation (state files, auto-resume mechanics, handoff note) - docs/how-to/topics.md — expanded prerequisites checklist with group privacy, can_manage_topics, and re-add steps - docs/how-to/cross-environment-resume.md — added handoff mode terminal workflow with mermaid sequence diagram - docs/how-to/index.md — added 'Getting started' section with choose-a-mode - zensical.toml — added choose-a-mode to nav * docs: add three-mode summary table to README Quick Start section * feat: migrate to dev branch workflow — dev→TestPyPI, master→PyPI Branch model: - feature/* → PR → dev (TestPyPI auto-publish) → PR → master (PyPI) - master always matches latest PyPI release - dev is the integration/staging branch CI changes: - ci.yml: TestPyPI publish triggers on dev push (was master) - ci.yml, codeql.yml: CI runs on both master and dev pushes - dependabot.yml: PRs target dev branch Hook changes: - release-guard.sh: updated messages to mention dev branch - release-guard-mcp.sh: updated messages to mention dev branch - Both hooks already allow dev pushes (only block master/main) Documentation: - CLAUDE.md: updated 3-phase workflow, CI table, release guard docs - dev-workflow.md: added branch model section - release-discipline.md: added dev branch staging notes * ci: retrigger CI for PR #160 * feat: allow Claude Code to merge PRs targeting dev branch only Release guard hooks now check the PR's base branch: - dev → allowed (TestPyPI/staging) - master/main → blocked (PyPI releases remain Nathan-only) Both Bash hook (gh pr merge) and MCP hook (merge_pull_request) updated with base branch checking via gh pr view. --------- Co-authored-by: Claude Opus 4.6 <[email protected]> * chore: staging 0.35.0rc7 * docs: add workflow mode indicator to CLAUDE.md (#162) * ci: add CODEOWNERS, update action SHA pins, add permission comments - Create .github/CODEOWNERS requiring @littlebearapps/core review - Pin setup-uv to v7.4.0 (6ee6290f), download-artifact to v8.0.1 (3e5f45b2) - Add precise version comments on all action SHAs (codeql v3.32.6, pypi-publish v1.13.0, action-gh-release v2.5.0, fetch-metadata v2.5.0) - Document write permissions with why-comments (OIDC, releases, auto-merge) Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add release guard hooks and document protection in CLAUDE.md Defence-in-depth hooks prevent Claude Code from pushing to master, merging PRs, creating tags, or triggering releases. Feature branch pushes and PR creation remain allowed. - release-guard.sh: Bash hook blocking master push, tags, releases, PR merge - release-guard-protect.sh: Edit/Write hook protecting guard files and hooks.json - release-guard-mcp.sh: GitHub MCP hook blocking merge and master writes - hooks.json: register all three hooks - CLAUDE.md: document release guard, update workflow roles, CI pipeline notes Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: clarify /config default labels and remove redundant "Works with" lines Default labels now explain what "default" means for each setting: - Diff preview: "default (off)" — matches actual behaviour (was "default (on)") - Model/Reasoning: "default (engine decides)" - API cost: "default (on)", Subscription usage: "default (off)" - Plan mode home hint: "agent decides" - Diff preview home hint: "buttons only" Added info lines to plan mode and reasoning sub-pages explaining the default behaviour in more detail. Removed all 9 "Works with: ..." lines from sub-pages — they're redundant because engine visibility guards already hide settings from unsupported engines. Fixes #119 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: suppress redundant cost footer on error runs When a run fails (e.g. subscription limit hit), the diagnostic context line from _extract_error() already shows cost, turns, and API time. The 💰 cost footer was duplicating this same data in a different format. Now the cost footer only appears on successful runs where it's the sole source of cost information. Error runs still show cost in the diagnostic line, and budget alerts still fire regardless. Also adds usage field to mock Return dataclass (matching ErrorReturn) so tests can verify cost footer behaviour on success runs. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: suppress stall notifications when CPU-active + heartbeat re-render When cpu_active=True (extended thinking, background agents), suppress Telegram stall warning notifications and instead trigger a heartbeat re-render so the elapsed time counter keeps ticking. Notifications still fire when cpu_active=False or None (no baseline). Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: staging 0.34.5rc2 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: CI release-validation tomllib bytes/str mismatch tomllib.loads() expects str but was receiving bytes from sys.stdin.buffer.read() and open(...,'rb').read(). First triggered when PR #122 changed the version (rc1 → rc2). Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs: integrate screenshots into docs with correct JPG references - Add 44 screenshots to docs/assets/screenshots/ - Fix all image refs from .png to .jpg across 25 doc files - README uses absolute raw.githubusercontent.com URLs for PyPI rendering - Fix 5 filename mismatches (session-auto-resume→chat-auto-resume, etc.) - Comment out 11 missing screenshots with TODO markers - Add CAPTURES.md checklist tracking capture status Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: convert markdown images to HTML img tags for GitHub compatibility Switch from MkDocs `{ loading=lazy }` syntax to HTML `<img>` tags with width="360" and loading="lazy". Fixes two GitHub rendering issues: `{ loading=lazy }` appearing as visible text, and oversized images with no width constraint. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: fix 3 screenshot mismatches and replace 3 screenshots - first-run.md: rewrite resume line text to match footer screenshot - interactive-control.md: update planmode show admonition to match screenshot (auto not on) - switch-engines.md: swap engine-footer.jpg for multi-engine-switch.jpg - Replace startup-message.jpg with clean v0.34.4 capture (was rc/6-projects) - Replace cooldown-auto-deny.jpg with post-outline approve/deny buttons - Replace file-put.jpg with photo save confirmation Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add iOS caption limitation note to file transfer guide Telegram iOS doesn't show a caption field when sending documents via the File picker, so /file put <path> captions aren't easily accessible. Added a note with workarounds (use Desktop, send as photo, or let auto-save handle it). Updated screenshot alt text to match actual screenshot content. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: temp swap README image URLs to feature branch for preview Will revert to master before merging. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: lay out all 3 README screenshots in a single row Reduce from 360px to 270px each and combine into one <p> block so all three hero screenshots sit side by side. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap 3rd hero screenshot for config-menu for visual variety Replace plan-outline-approve (too similar to approval-diff-preview) with config-menu showing the /config settings grid. The three hero images now tell: voice input → approve changes → configure everything. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add captions under README hero screenshots Small <sub> captions: "Send tasks by voice (Whisper transcription)", "Approve changes remotely", "Configure from Telegram". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: use table layout for README hero screenshots with captions Fixes stacking issue — <br> in a <p> broke inline flow. A table keeps images side by side with captions underneath each one. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: replace table layout with single hero collage image Composite image scales proportionally on mobile instead of requiring horizontal scroll. Captions baked into the image via ImageMagick. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap middle hero screenshot for full 3-button approval view Replace approval-diff-preview with approval-buttons-howto showing Approve / Deny / Pause & Outline Plan — more visually impressive. Caption now reads "Approve changes remotely (Claude Code)". Added footnote linking to engine compatibility table. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap config-menu for parallel-projects in hero collage Third hero screenshot now shows 10+ projects running simultaneously across different repos — much more compelling than a settings menu. New caption: "Run agents across projects in parallel". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: revert README image URL to master for merge Swap hero-collage URL back from feature/github-hardening to master. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.34.5rc3 - fix: preserve all EngineOverrides fields when setting model/planmode/reasoning (was silently wiping ask_questions, diff_preview, show_api_cost, etc.) - fix: /config home page resolves "default" to effective values - feat: file upload auto-deduplication (append _1, _2 instead of requiring --force) - feat: media groups without captions now auto-save instead of showing usage text - feat: resume line visual separation (blank line + ↩️ prefix) - fix: claude auto-approve echoes updatedInput in control response Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: expand permission policies for Codex CLI and Gemini CLI in /config Codex gets a new "Approval policy" page (full auto / safe) that passes --ask-for-approval untrusted when safe mode is selected. Gemini's approval mode expands from 2 to 3 tiers (read-only / edit files / full access) with --approval-mode auto_edit for the middle tier. Both engines now show an "Agent controls" section on the /config home page. Engine-specific model default hints replace the generic "from CLI settings" text. Also adds staging.sh helper, context-guard-stop hook, and docs updates. Closes #131 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.34.5rc4 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: /config UX overhaul, resume line toggle, cost budget settings, model metadata /config UX cleanup: - Convert all binary toggles from 3-column (on/off/clear) to 2-column (toggle + clear) for better mobile tap targets - Merge Engine + Model into combined "Engine & model" page - Reorganise home page to max 2 buttons per row across all engines - Split plan mode 3-option rows (off/on/auto) into 2+1 layout - Add _toggle_row() helper for consistent toggle button rendering New features: - #128: Resume line /config toggle — per-chat show_resume_line override via EngineOverrides with On/Off/Clear buttons, wired into executor - #129: Cost budget /config settings — per-chat budget_enabled and budget_auto_cancel overrides on the Cost & Usage page, wired into _check_cost_budget() in runner_bridge.py Model metadata improvements: - Show Claude Code [1m] context window suffix: "opus 4.6 (1M)" - Strip Gemini CLI "auto-" prefix: "auto-gemini-3" → "gemini-3" - Future-proof: unknown suffixes default to .upper() (e.g. [500k] → 500K) Bug fixes: - #124: Standalone override commands (/planmode, /model, /reasoning) now preserve all EngineOverrides fields including new ones - Error handling: control_response.write_failed catch-all in claude.py, ask_question.extraction_failed warning, model.override.failed logging Hardening: - Plan outline sent as separate ephemeral message (avoids 4096 char truncation) - Added show_resume_line, budget_enabled, budget_auto_cancel to EngineOverrides, EngineRunOptions, normalize/merge, and all constructors Tests: 1610 passed, 80.56% coverage, ruff clean. Integration tested on @untether_dev_bot across all 6 engine chats. Closes #128, closes #129, fixes #124 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: trigger CI for PR #132 * fix: address 11 CodeRabbit review comments on PR #132 Bug fixes: - claude.py: fix UnboundLocalError when factory.resume is falsy in ask_question.extraction_failed logging path - ask_question.py: reject malformed option callbacks instead of silently falling back to option 0 - files.py: raise FileExistsError when deduplicate_target exhausts 999 suffixes instead of returning the original (overwrite risk) - config.py: disambiguate Codex "Full auto" (fa) vs Gemini "Full access" (ya) callback IDs and toast labels Hardening: - codex.py: add --ask-for-approval to _EXEC_ONLY_FLAGS guard - model.py: add try/except to clear path (matching set path) - reasoning.py: add try/except to clear path (matching set path) - loop.py: notify user when media group upload fails instead of silently dropping - export.py: log session count instead of identifiers at info level - config.py: resolve resume-line default from config instead of hardcoding True - staging.sh: pin PyPI index in rollback/reset with --pip-args Skipped (not applicable): - CHANGELOG.md: RC versions don't get changelog entries per release discipline - docs/tutorials TODO screenshot: pre-existing, not introduced by PR - .claude/hooks/context-guard-stop.sh: ContextDocs plugin hook, not Untether source Tests: 1611 passed, 80.48% coverage, ruff clean. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: replace bare pass with debug log to satisfy bandit B110 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: setup wizard security + UX improvements - Auto-set allowed_user_ids from captured Telegram user ID during onboarding (security: restricts bot to the setup user's account) - Add "next steps" panel after wizard completion with pointers to /config, voice notes, projects, and account lock confirmation - Update install.md: Python 3.12+ (not just 3.14), dynamic version string, /config mention for post-setup changes - Update first-run.md: /config → Engine & model for default engine Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: plan outline UX — markdown rendering, buttons, cleanup (#139, #140, #141) - Render outline messages as formatted text via render_markdown() + split_markdown_body() instead of raw markdown (#139) - Add approve/deny buttons to last outline message so users don't have to scroll up past long outlines (#140) - Delete outline messages on approve/deny via module-level _OUTLINE_REGISTRY callable from callback handler; suppress stale keyboard on progress message (#141) - 8 new tests for outline rendering, keyboard placement, and cleanup - Bump version to 0.35.0rc5 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: /continue command — cross-environment resume for all engines (#135) New `/continue` command resumes the most recent CLI session in the project directory from Telegram. Enables starting a session in your terminal and picking it up from your phone. Engine support: Claude (--continue), Codex (resume --last), OpenCode (--continue), Pi (--continue), Gemini (--resume latest). AMP not supported (requires explicit thread ID). Includes ResumeToken.is_continue flag, build_args for all 6 runners, reserved command registration, resume emoji prefix stripping for reply-to-continue, docs (how-to guide, README, commands ref, routing explanation, conversation modes tutorial), and 99 new test assertions. Integration tested against @untether_dev_bot — all 5 supported engines passed secret-recall verification via Telegram MCP. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: agent outbox file delivery + fix cross-chat ask stealing (#143, #144) Outbox delivery (#143): agents write files to .untether-outbox/ during a run; Untether sends them as Telegram documents on completion with 📎 captions. Config: outbox_enabled, outbox_dir, outbox_max_files, outbox_cleanup. Deny-glob security, size limits, auto-cleanup. Preamble updated for all 6 engines. Integration tested across Claude, Codex, OpenCode, Pi, and Gemini. AskUserQuestion fix (#144): _PENDING_ASK_REQUESTS and _ASK_QUESTION_FLOWS were global dicts with no chat_id scoping — a pending ask in one chat would steal the next message from any other chat. Added channel_id contextvar and scoped all ask lookups by it. Session cleanup now also clears stale pending asks. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: v0.35.0 changelog completion + fix #123 updatedInput - Complete v0.35.0 changelog: add missing entries for /continue (#135), /config UX overhaul (#132), resume line toggle (#128), cost budget (#129), model metadata, resume line formatting (#127), override preservation (#124), and updatedInput fix (#123) - Fix #123: register input for system-level auto-approved control requests so updatedInput is included in the response - Add parameterised test for all 5 auto-approve types input registration - Remove unused OutboxResult import (ruff fix) Issues closed: #115, #118, #123, #124, #126, #127, #134 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.35.0rc6 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: rc6 integration test fixes (#145, #146, #147, #148, #149) - Reduce Telegram API timeout from 120s to 30s (#145) - OpenCode error runs show error text instead of empty body (#146) - Pi /continue captures session ID via allow_id_promotion (#147) - Post-outline approval uses skip_reply to avoid "not found" (#148) - Orphan progress message cleanup on restart (#149) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: post-outline notification reply + OpenCode empty body (#148, #150) - #148: skip_reply callback results now bypass the executor's default reply_to fallback, sending directly via the transport with no reply_to_message_id. Previously, the executor treated reply_to=None as "use default" which pointed to the (deleted) outline message. - #150: OpenCode normal completion with no Text events now falls back to last_tool_error. Added state.last_tool_error field populated on ToolUse error status. Covers both translate() and stream_end_events(). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: suppress post-outline notification to avoid "message not found" (#148) After outline approval/denial, the progress loop's _send_notify was firing for the next tool approval, but the notification's reply_to anchor could reference deleted state. Added _outline_just_resolved flag to skip one notification cycle after outline cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: note OpenCode lacks auto-compaction — long sessions degrade (#150) Added known limitation to OpenCode runner docs and integration testing playbook. OpenCode sessions accumulate unbounded context (no compaction events unlike Pi). Workaround: use /new before isolated tests. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: skip_reply on regular approve path when outline was deleted (#148) The "Approve Plan" button on outline messages uses the real ExitPlanMode request_id, routing through the regular approve path (not the da: synthetic path). When outline messages exist, set skip_reply=True on the CommandResult to avoid replying to the just-deleted outline message. Also added reply_to_message_id and text_preview to transport.send.failed warning for easier debugging. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: update changelog for rc6 integration test fixes (#145-#150) Updated fix descriptions for #146/#150 (OpenCode last_tool_error fallback) and #148 (regular approve path skip_reply). Added docs section for OpenCode compaction limitation. Updated test counts. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: fix formatting after merge resolution Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address CodeRabbit review comments on PR #151 - bridge.py: replace text_preview with text_len in send failure warning to avoid logging raw message content (security) - runner_bridge.py: move unregister_progress() after send_result_message() to avoid orphan window between ephemeral cleanup and final message send - cross-environment-resume.md: add language spec to code block Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: resolve /config "default" labels to effective on/off values (#152) Sub-pages showed "Current: default" or "default (on/off)" while buttons already showed the resolved value. Now all boolean-toggle settings show the effective on/off value in both text and buttons. Affected: verbose, ask mode, diff preview, API cost, subscription usage, budget enabled/auto-cancel, resume line. Home page cost & resume labels also resolved. Plan mode, model, and reasoning keep "default" since they depend on CLI settings and aren't simple on/off booleans. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: update changelog for rc7 config default labels fix (#152) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: update documentation for v0.35.0 - fix missing nav entries in zensical.toml (cross-env resume, Gemini/Amp runners) - rewrite inline-settings.md for /config UX overhaul (2-column toggles, budget/resume toggles) - update plan-mode.md with outline rendering, buttons-on-last-chunk, ephemeral cleanup - update interactive-control tutorial with outline UX improvements - add orphan progress cleanup section to operations.md - add engine-specific approval policies to interactive-approval.md - add per-chat budget overrides to cost-budgets.md - update module-map.md with Gemini/Amp and new modules (outbox, progress persistence, proc_diag) - update architecture.md mermaid diagrams with all 6 engines - bump specification.md to v0.35.0, add progress persistence and outbox sections - add v0.35.0 screenshot entries to CAPTURES.md Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: broaden frozen ring buffer stall escalation beyond MCP tools (#155) Frozen ring buffer escalation was gated on `mcp_server is not None`, so general stalls with cpu_active=True and no MCP tool running were silently suppressed indefinitely. Broadened to fire for all stalls after 3+ checks with no new JSONL events regardless of tool type. New notification: "CPU active, no new events" for non-MCP frozen stalls. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: tool approval buttons no longer suppressed after outline approval (#156) After "Approve Plan" on an outline, the stale discuss_approve action remained in ProgressTracker with completed=False. The renderer picked up its stale "Approve Plan"/"Deny" buttons first, then the suppression logic at line 994 stripped ALL buttons — including new Write/Edit/Bash approval buttons. Claude blocked indefinitely waiting for approval. Fix: after suppressing stale buttons, complete the discuss_approve action(s) in the tracker, reset _outline_sent, and trigger a re-render so subsequent tool requests get their own Approve/Deny buttons. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add workflow mode indicator to startup message, fix startup crash on topics validation failure (#158, #159) Features: - Startup message now shows mode: assistant/workspace/handoff - Derived from session_mode + topics.enabled config values - _resolve_mode_label() helper in backend.py Bug fixes: - Fix UnboundLocalError crash when topics validation fails on startup (#158) - Moved import signal and shutdown imports before try block in loop.py - Downgrade can_manage_topics check from fatal error to warning (#159) - Bot can now start without manage_topics admin right - Existing topics work fine; only topic creation/editing affected Tests: - 17 new unit tests for stateless/handoff mode (test_stateless_mode.py) - _should_show_resume_line, _chat_session_key, ResumeResolver, ResumeLineProxy - Integration-level: stateless shows resume lines, no auto-resume, chat hides lines - 3 new tests for mode indicator in startup message (test_telegram_backend.py) Docs: - New docs/reference/modes.md — comprehensive reference for all 3 workflow modes - Updated docs/reference/index.md and zensical.toml nav with modes page * docs: comprehensive three-mode coverage across all documentation New: - docs/how-to/choose-a-mode.md — decision tree, mode comparison, mermaid sequence diagrams, configuration examples, switching guide, workspace prerequisites Updated: - README.md — improved three-mode description in features list - docs/tutorials/install.md — added mode selection step (section 10) - docs/tutorials/first-run.md — added 'What mode am I in?' tip - docs/reference/config.md — cross-linked session_mode/show_resume_line to modes.md - docs/reference/transports/telegram.md — added mode requirement callouts for forum topics and chat sessions sections - docs/how-to/chat-sessions.md — added session persistence explanation (state files, auto-resume mechanics, handoff note) - docs/how-to/topics.md — expanded prerequisites checklist with group privacy, can_manage_topics, and re-add steps - docs/how-to/cross-environment-resume.md — added handoff mode terminal workflow with mermaid sequence diagram - docs/how-to/index.md — added 'Getting started' section with choose-a-mode - zensical.toml — added choose-a-mode to nav * docs: add three-mode summary table to README Quick Start section * feat: migrate to dev branch workflow — dev→TestPyPI, master→PyPI Branch model: - feature/* → PR → dev (TestPyPI auto-publish) → PR → master (PyPI) - master always matches latest PyPI release - dev is the integration/staging branch CI changes: - ci.yml: TestPyPI publish triggers on dev push (was master) - ci.yml, codeql.yml: CI runs on both master and dev pushes - dependabot.yml: PRs target dev branch Hook changes: - release-guard.sh: updated messages to mention dev branch - release-guard-mcp.sh: updated messages to mention dev branch - Both hooks already allow dev pushes (only block master/main) Documentation: - CLAUDE.md: updated 3-phase workflow, CI table, release guard docs - dev-workflow.md: added branch model section - release-discipline.md: added dev branch staging notes * ci: retrigger CI for PR #160 * feat: allow Claude Code to merge PRs targeting dev branch only Release guard hooks now check the PR's base branch: - dev → allowed (TestPyPI/staging) - master/main → blocked (PyPI releases remain Nathan-only) Both Bash hook (gh pr merge) and MCP hook (merge_pull_request) updated with base branch checking via gh pr view. * docs: add workflow mode indicator and modes.md to CLAUDE.md --------- Co-authored-by: Claude Opus 4.6 <[email protected]> * fix: restore frozen ring buffer stall escalation (#155), staging 0.35.0rc8 * ci: add CODEOWNERS, update action SHA pins, add permission comments - Create .github/CODEOWNERS requiring @littlebearapps/core review - Pin setup-uv to v7.4.0 (6ee6290f), download-artifact to v8.0.1 (3e5f45b2) - Add precise version comments on all action SHAs (codeql v3.32.6, pypi-publish v1.13.0, action-gh-release v2.5.0, fetch-metadata v2.5.0) - Document write permissions with why-comments (OIDC, releases, auto-merge) Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add release guard hooks and document protection in CLAUDE.md Defence-in-depth hooks prevent Claude Code from pushing to master, merging PRs, creating tags, or triggering releases. Feature branch pushes and PR creation remain allowed. - release-guard.sh: Bash hook blocking master push, tags, releases, PR merge - release-guard-protect.sh: Edit/Write hook protecting guard files and hooks.json - release-guard-mcp.sh: GitHub MCP hook blocking merge and master writes - hooks.json: register all three hooks - CLAUDE.md: document release guard, update workflow roles, CI pipeline notes Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: clarify /config default labels and remove redundant "Works with" lines Default labels now explain what "default" means for each setting: - Diff preview: "default (off)" — matches actual behaviour (was "default (on)") - Model/Reasoning: "default (engine decides)" - API cost: "default (on)", Subscription usage: "default (off)" - Plan mode home hint: "agent decides" - Diff preview home hint: "buttons only" Added info lines to plan mode and reasoning sub-pages explaining the default behaviour in more detail. Removed all 9 "Works with: ..." lines from sub-pages — they're redundant because engine visibility guards already hide settings from unsupported engines. Fixes #119 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: suppress redundant cost footer on error runs When a run fails (e.g. subscription limit hit), the diagnostic context line from _extract_error() already shows cost, turns, and API time. The 💰 cost footer was duplicating this same data in a different format. Now the cost footer only appears on successful runs where it's the sole source of cost information. Error runs still show cost in the diagnostic line, and budget alerts still fire regardless. Also adds usage field to mock Return dataclass (matching ErrorReturn) so tests can verify cost footer behaviour on success runs. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: suppress stall notifications when CPU-active + heartbeat re-render When cpu_active=True (extended thinking, background agents), suppress Telegram stall warning notifications and instead trigger a heartbeat re-render so the elapsed time counter keeps ticking. Notifications still fire when cpu_active=False or None (no baseline). Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: staging 0.34.5rc2 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: CI release-validation tomllib bytes/str mismatch tomllib.loads() expects str but was receiving bytes from sys.stdin.buffer.read() and open(...,'rb').read(). First triggered when PR #122 changed the version (rc1 → rc2). Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs: integrate screenshots into docs with correct JPG references - Add 44 screenshots to docs/assets/screenshots/ - Fix all image refs from .png to .jpg across 25 doc files - README uses absolute raw.githubusercontent.com URLs for PyPI rendering - Fix 5 filename mismatches (session-auto-resume→chat-auto-resume, etc.) - Comment out 11 missing screenshots with TODO markers - Add CAPTURES.md checklist tracking capture status Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: convert markdown images to HTML img tags for GitHub compatibility Switch from MkDocs `{ loading=lazy }` syntax to HTML `<img>` tags with width="360" and loading="lazy". Fixes two GitHub rendering issues: `{ loading=lazy }` appearing as visible text, and oversized images with no width constraint. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: fix 3 screenshot mismatches and replace 3 screenshots - first-run.md: rewrite resume line text to match footer screenshot - interactive-control.md: update planmode show admonition to match screenshot (auto not on) - switch-engines.md: swap engine-footer.jpg for multi-engine-switch.jpg - Replace startup-message.jpg with clean v0.34.4 capture (was rc/6-projects) - Replace cooldown-auto-deny.jpg with post-outline approve/deny buttons - Replace file-put.jpg with photo save confirmation Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add iOS caption limitation note to file transfer guide Telegram iOS doesn't show a caption field when sending documents via the File picker, so /file put <path> captions aren't easily accessible. Added a note with workarounds (use Desktop, send as photo, or let auto-save handle it). Updated screenshot alt text to match actual screenshot content. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: temp swap README image URLs to feature branch for preview Will revert to master before merging. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: lay out all 3 README screenshots in a single row Reduce from 360px to 270px each and combine into one <p> block so all three hero screenshots sit side by side. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap 3rd hero screenshot for config-menu for visual variety Replace plan-outline-approve (too similar to approval-diff-preview) with config-menu showing the /config settings grid. The three hero images now tell: voice input → approve changes → configure everything. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add captions under README hero screenshots Small <sub> captions: "Send tasks by voice (Whisper transcription)", "Approve changes remotely", "Configure from Telegram". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: use table layout for README hero screenshots with captions Fixes stacking issue — <br> in a <p> broke inline flow. A table keeps images side by side with captions underneath each one. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: replace table layout with single hero collage image Composite image scales proportionally on mobile instead of requiring horizontal scroll. Captions baked into the image via ImageMagick. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap middle hero screenshot for full 3-button approval view Replace approval-diff-preview with approval-buttons-howto showing Approve / Deny / Pause & Outline Plan — more visually impressive. Caption now reads "Approve changes remotely (Claude Code)". Added footnote linking to engine compatibility table. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: swap config-menu for parallel-projects in hero collage Third hero screenshot now shows 10+ projects running simultaneously across different repos — much more compelling than a settings menu. New caption: "Run agents across projects in parallel". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: revert README image URL to master for merge Swap hero-collage URL back from feature/github-hardening to master. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.34.5rc3 - fix: preserve all EngineOverrides fields when setting model/planmode/reasoning (was silently wiping ask_questions, diff_preview, show_api_cost, etc.) - fix: /config home page resolves "default" to effective values - feat: file upload auto-deduplication (append _1, _2 instead of requiring --force) - feat: media groups without captions now auto-save instead of showing usage text - feat: resume line visual separation (blank line + ↩️ prefix) - fix: claude auto-approve echoes updatedInput in control response Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: expand permission policies for Codex CLI and Gemini CLI in /config Codex gets a new "Approval policy" page (full auto / safe) that passes --ask-for-approval untrusted when safe mode is selected. Gemini's approval mode expands from 2 to 3 tiers (read-only / edit files / full access) with --approval-mode auto_edit for the middle tier. Both engines now show an "Agent controls" section on the /config home page. Engine-specific model default hints replace the generic "from CLI settings" text. Also adds staging.sh helper, context-guard-stop hook, and docs updates. Closes #131 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: staging 0.34.5rc4 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: /config UX overhaul, resume line toggle, cost budget settings, model metadata /config UX cleanup: - Convert all binary toggles from 3-column (on/off/clear) to 2-column (toggle + clear) for better mobile tap targets - Merge Engine + Model into combined "Engine & model" page - Reorganise home page to max 2 buttons per row across all engines - Split plan mode 3-option rows (off/on/auto) into 2+1 layout - Add _toggle_row() helper for consistent toggle button rendering New features: - #128: Resume line /config toggle — per-chat show_resume_line override via EngineOverrides with On/Off/Clear buttons, wired into executor - #129: Cost budget /config settings — per-chat budget_enabled and budget_auto_cancel overrides on the Cost & Usage page, wired into _check_cost_budget() in runner_bridge.py Model metadata improvements: - Show Claude Code [1m] context window suffix: "opus 4.6 (1M)" - Strip Gemini CLI "auto-" prefix: "auto-gemini-3" → "gemini-3" - Future-proof: unknown suffixes default to .upper() (e.g. [500k] → 500K) Bug fixes: - #124: Standalone override commands (/planmode, /model, /reasoning) now preserve all EngineOverrides fields including new ones - Error handling: control_response.write_failed catch-all in claude.py, ask_question.extraction_failed warning, model.override.failed logging Hardening: - Plan outline sent as separate ephemeral message (avoids 4096 char truncation) - Added show_resume_line, budget_enabled, budget_auto_cancel to EngineOverrides, EngineRunOptions, normalize/merge, and all constructors Tests: 1610 passed, 80.56% coverage, ruff clean. Integration tested on @untether_dev_bot across all 6 engine chats. Closes #128, closes #129, fixes #124 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: trigger CI for PR #132 * fix: address 11 CodeRabbit review comments on PR #132 Bug fixes: - claude.py: fix UnboundLocalError when factory.resume is falsy in ask_question.extraction_failed logging path - ask_question.py: reject malformed option callbacks instead of silently falling back to option 0 - files.py: raise FileExistsError when deduplicate_target exhausts 999 suffixes instead of returning the original (overwrite risk) - config.py: disambiguate Codex "Full auto" (fa) vs Gemini "Full access" (ya) callback IDs and toast labels Hardening: - codex.py: add --ask-for-approval to _EXEC_ONLY_FLAGS guard - model.py: add try/except to clear path (matching set path) - reasoning.py: add try/except to clear path (matching set path) - loop.py: notify user when media group upload fails instead of silently dropping - export.py: log session count instead of identifiers at info level - config.py: resolve resume-line default from config instead of hardcoding True - staging.sh: pin PyPI index in rollback/reset with --pip-args Skipped (not applicable): - CHANGELOG.md: RC versions don't get changelog entries per release discipline - docs/tutorials TODO screenshot: pre-existing, not introduced by PR - .claude/hooks/context-guard-stop.sh: ContextDocs plugin hook, not Untether source Tests: 1611 passed, 80.48% coverage, ruff clean. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: replace bare pass with debug log to satisfy bandit B110 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: setup wizard security + UX improvements - Auto-set allowed_user_ids from captured Telegram user ID during onboarding (security: restricts bot to the setup user's account) - Add "next steps" panel after wizard completion with pointers to /config, voice notes, projects, and account lock confirmation - Update install.md: Python 3.12+ (not just 3.14), dynamic version string, /config mention for post-setup changes - Update first-run.md: /config → Engine & model for default engine Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: plan outline UX — markdown rendering, buttons, cleanup (#139, #140, #141) - Render outline messages as formatted text via render_markdown() + split_markdown_body() instead of raw markdown (#139) - Add approve/deny buttons to last outline message so users don't have to scroll up past long outlines (#140) - Delete outline messages on approve/deny via module-level _OUTLINE_REGISTRY callable from callback handler; suppress stale keyboard on progress message (#141) - 8 new tests for outline rendering, …
Master's v0.35.0 squash-merge (commit e5bff3c) introduced content that was never back-merged into dev — primarily the `applies_to:` frontmatter on .claude/rules/*.md files added by the v0.35.0 release commit. This caused genuine merge conflicts when attempting the v0.35.1 dev → master PR. Merge strategy: - `-X ours` (dev's version wins on overlapping lines) since dev has v0.35.1 content the release is about - Master's additive `applies_to:` frontmatter preserved on rules files (non-conflicting addition) - Fixed one duplicate block in `src/untether/telegram/commands/config.py` that `-X ours` combined incorrectly Verification: 2164 tests pass, ruff format/lint clean, 81.65% coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Merged
11 tasks
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.
Problem
Master's v0.35.0 squash-merge (commit `e5bff3c`) introduced content that was never back-merged into dev — primarily the `applies_to:` frontmatter on `.claude/rules/*.md` files. This caused genuine merge conflicts when attempting the v0.35.1 dev → master release PR (#309).
Fix
Merged `origin/master` into `dev` via `git merge -X ours`:
Files from master that merged cleanly
Known acceptable drift
Test plan
Why this is safe
No behaviour changes. Only additive documentation frontmatter from master, and the duplicate-block fix restores `config.py` to dev's original state.
🤖 Generated with Claude Code