release: v0.35.4#680
Conversation
… bg-handle lifecycle (#374) [0.35.4rc1] (#575) * fix(runners): v0.35.4 Batch 1 — Pi diagnostics, Pi AutoRetry, Claude bg-handle lifecycle #565 (fix, pi): surface Pi stderr and distinguish a startup/early-exit crash (rc=0, zero translated events) from a truncated stream on the no-agent_end fallback path. Resolves the *diagnostics* gap the issue was re-scoped to — the original "resume is broken" mechanism did not reproduce (transient MCP-cold startup), so fix options A/B/C were deliberately NOT implemented. Threads stderr_lines through the polymorphic stream_end_events (base + 6 runners); logs had_events/resumed on pi.stream.no_agent_end. #460 (fix, pi): translate auto_retry_start/auto_retry_end (schema-defined but never handled) into note actions with a stable retry_N id across the pair. Watchdog "retry is CPU-active" suppression deferred (delays << stall threshold; translated events already refresh the idle timer). #374 (fix, claude; Monitor-only subset): clear a background handle only on a *terminal* tool_result. Fixes Monitor interim-result dropping the stall_monitor_active suppression. New _is_terminal_tool_result + is_terminal gate. A Monitor's results are arbitrary command stdout, so terminal detection is is_error OR the timeout_ms deadline — NOT text-marker matching (which would false-clear on a build printing "Done"). The deadline is the bounded, reliable backstop (no leak). Bash-bg/Agent-bg/ScheduleWakeup/RemoteTrigger keep clear-on-result; their terminal-detection (KillShell, exit, deadline sweeps) is deferred to v0.35.5 to avoid the inverse hang. Tests: test_pi_runner +8, test_claude_runner +5 Monitor-lifecycle (interim kept incl. "Done"-in-stdout, error/expired/unknown-deadline/foreground clear). Docs: pi untether-events §4.6. Full suite green, 82.70% coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: staging 0.35.4rc1 First rc of the v0.35.4 milestone — Batch 1 (Pi diagnostics #565, Pi AutoRetry #460, Claude bg-handle lifecycle #374). Bumps version + lockfile so the dev push publishes a fresh artifact to TestPyPI (skip-existing would otherwise skip 0.35.3). Changelog v0.35.4 section already carries all three issues. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ase CI, drain (#576) Security & ops hardening batch (5 issues, file-disjoint): - #381 security: SSRF-validate voice_transcription_base_url at the transcribe_voice chokepoint (async/DNS) + sync fast-fail at config load; new optional voice_transcription_url_allowlist for private endpoints. - #382 security: refuse to bind the webhook server when any webhook has auth="none" on a non-loopback host (initial bind + hot-reload); opt in via [triggers] allow_unauthenticated_webhooks. - #459 lockfile: replace os.kill(pid,0) liveness with fcntl.flock(2) advisory lock — kernel auto-releases on death, ending the PID-reuse crash-loop. Lock fd kept open for process lifetime, non-inheritable. - #376 release: push the auto-created tag with RELEASE_TAG_PAT instead of GITHUB_TOKEN so the tag push triggers release.yml (single-gate flow). Requires Nathan to create the secret + drop the pypi env reviewer. - #559 shutdown: shorten drain to 10s when the sole active run triggered the restart (#547 self-restart deadlock) + bounded outbox flush before close so an already-queued final message isn't dropped. Tests: +~440 lines across 9 test files. Full suite green (2783 passed, 82.65% coverage). CHANGELOG v0.35.4 (unreleased) section + FAQ Q9 touch-up. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump 0.35.4rc1 → 0.35.4rc2 so the v0.35.4 Batch 2 security/ops work (#381 #382 #459 #376 #559, merged in #576) publishes as a fresh TestPyPI wheel — rc1 already exists on TestPyPI (skip-existing skipped the unchanged version). No code/changelog change (rc versions skip changelog per validate_release). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.0 to 4.36.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@7211b7c...87557b9) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.36.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@de0fac2...df4cb1c) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.1.0 to 8.2.0. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](astral-sh/setup-uv@0880764...fac544c) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 8.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.1 to 4.36.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@87557b9...8aad20d) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.36.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](softprops/action-gh-release@b430933...718ea10) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@df4cb1c...9c091bb) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...54f647b) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.2.0 to 8.3.0. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](astral-sh/setup-uv@fac544c...d31148d) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 8.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...54f647b) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…p-audit advisories (#604) - aiohttp 3.13.5 → 3.14.1 (PYSEC-2026-237, PYSEC-2026-2104…2113) - click 8.3.1 → 8.4.2 (PYSEC-2026-2132) - msgpack 1.1.2 → 1.2.1 (GHSA-6v7p-g79w-8964) - pip 26.0.1 → 26.1.2 (PYSEC-2026-196) - pydantic-settings 2.12.0 → 2.14.2 (GHSA-4xgf-cpjx-pc3j) pip-audit has been failing every PR since these advisories landed (dev's last green CI predates them, 2026-06-02). Compatibility fix: aiohttp 3.14 calls resume_reading(resume_parser=…) on the StreamReader protocol, breaking the _NullProtocol shim used to parse multipart bodies from cached bytes (4 test failures, webhook multipart uploads would 500 in production). Both shim methods now accept arbitrary arguments. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… exit (#602) - #591: deliver the final Telegram message the moment a successful CompletedEvent arrives instead of waiting for the run generator to return (which blocks on proc.wait() behind lingering MCP children — up to the full 600s post-result watchdog, one answer lost to /cancel). Bridge-side _deliver_final closure is invoked early from run_runner_with_cancel and idempotently from the post-return flow; note_final()/_finalizing suppress progress repaints and the #470 closing notice so nothing overwrites the delivered answer. Cancel and error paths after delivery are logged, not rendered. - #591: new [watchdog] post_result_limbo_grace (default 60s, 0=off) — the post-result subcountdown SIGTERMs after the grace instead of the full timeout when the session is fully quiescent (no pending control/ask requests, no live background work per has_live_background_work), releasing MCP-held RSS/TCP promptly. - #599: manage_subprocess now awaits proc.aclose() on every exit path (shielded, suppressed) so the asyncio subprocess transport is released when the run ends instead of at interpreter shutdown ("RuntimeError: Event loop is closed" in BaseSubprocessTransport.__del__). Tests: early delivery while generator open, cancel-after-delivery keeps answer, ok=False stays on post-return path, note_final no-repaint, limbo grace fires/deferred-by-bg-work/0-disables, aclose on clean + kill paths + error suppression. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ron counts (#603) - #600: skipped outbox directories are moved into .untether-outbox/.skipped/ once (name collisions get _1/_2 suffixes), logged as outbox.skipped_dir_archived, and the user-facing skip notice now says where the directory went. Previously they were re-scanned, re-skipped, and re-logged on every run forever (#524 follow-up gap) — including outboxes containing ONLY a directory, which never even reached cleanup. Archival is gated on the same outbox_cleanup flag as sent-file deletion; the graveyard itself is excluded from scans. - #601: triggers.enabled now logs crons=<manager active count> plus crons_configured=<raw TOML entries> so it agrees with triggers.manager.updated / triggers.cron.started at startup. The previous raw count read as "N crons failed to load" during triage when the delta was just run_once entries already spent. Tests: graveyard move + reason rewrite, second-run silence, only-dir outbox archives, cleanup=false untouched, collision suffix, graveyard excluded from scan; manager active-vs-configured divergence semantics. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… post-exit orphan sweep (#605) - New reap_orphaned_group(): after the subprocess leader exits (including clean rc=0 — the fleet-wide 1-leak-per-run shape), sweep surviving process-group members (SIGTERM → 2s grace → SIGKILL) plus any descendant PIDs captured while the leader was alive. Wired into manage_subprocess teardown for all engines; logs subprocess.orphans_reaped. Config [watchdog] reap_orphans (default true per #573's session-end cleanup policy, OWASP ASI08/ASI10). - New signal_pid_group(): pid-based twin of _signal_process (#275 descendant snapshot + killpg + per-PID delivery). Replaces bare os.killpg in the Claude post-result subcountdown SIGTERM/SIGKILL and the base-runner liveness-kill / died-without-completion paths — those missed MCP chains that re-parented into separate sessions/pgroups (nsd: 25 children survived even systemd's kill sweep). - ClaudeStreamState.orphan_pid_snapshot: descendants captured at reader-done and refreshed at limbo detection, so late-spawned leakers (invisible to the first limbo snapshot per #590 evidence) are reaped. - contrib/untether.service: optional MemoryHigh/MemoryMax guidance for memory-constrained hosts (#589) — the leak fix removes the pressure source; the ceiling contains any future regression. Tests: group+descendant delivery, empty-group no-op, SIGTERM→SIGKILL survivor escalation, snapshot-escapee kill, manage_subprocess wiring + reap_orphans=False, limbo snapshot refresh; existing killpg tests guarded against real /proc descendant reads. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…597 image/document blocks (#606) - #595: catalog_staleness severity split — status=pending at system.init is a startup race, now INFO (catalog_staleness.pending, per-run dedup); needs-auth/failed keep the WARNING but dedup across runs via a process-lifetime (server, status) registry. The per-state dedup set reset on every subprocess spawn, multiplying a few broken connectors into ~2,930 WARNINGs/48h fleet-wide (96% of all nsd warnings). - #596: a 0-turn / 0-API-ms / empty-answer completion with ok=True (the upstream no-op resume shape) now logs runner.empty_result WARNING and delivers a user-visible note ("engine returned an empty result … resend or /new") instead of a bare error header with no body. Gated on usage being explicitly reported so engines without usage never trip it. - #597: schema accepts image + document content blocks (Read on binary media echoes them inside user-role messages; jsonl.msgspec.invalid x23 on nsd, #489 family). Permissive source dict — payload never rendered; both translate loops already fall through safely. Cheatsheet updated. Tests: severity split, cross-run dedup + suppressed log, pending stays per-run; anomaly note delivered, turns>0 not flagged, no-usage not flagged; image/document decode in user + assistant bodies. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…l enforcement (#607) - #592: new [watchdog] pre_result_silence_timeout (default 3600s, 0=off). A run whose stream goes silent forever BEFORE its first result was previously unbounded — the post-result watchdog only arms after a result and liveness never escalates an alive-but-idle process (8-day zombie Claude subprocess on mac, ~75% of that host's log volume in idle ticks). The watchdog's pre-result branch now kills the subprocess (descendant-aware, SIGTERM→grace→SIGKILL) once the cap elapses with no stream output, SUPPRESSED while permission/ask requests are pending (plan-approval waits stay safe per #526/#527) or background work is live. The reason is appended to stderr_capture so the run's error message explains itself (#575 stderr-excerpt machinery). - #593a: ClaudeRunner.run_impl now sets self.last_pid (base runner did; the override didn't) — the bridge's thread_pid() early-poll works for Claude, so stall diagnostics get a PID even when no StartedEvent ever arrives (the pid=None process_alive=None blind spot). - #593b: stall auto-cancel now ENFORCES teardown: after cancel_event is set, _enforce_cancel_teardown polls up to 30s for natural death, then kills the subprocess directly (descendant-aware; shielded with an early-exit poll). Observed: 14m52s between stall_auto_cancel and handle.cancelled on nsd during the OOM crisis. Tests: silence cap kills + reason in stderr, suppressed by pending request / live wakeup, 0 disables; enforcement escalates / no-ops on natural death / logs without pid; autouse neutraliser so existing auto-cancel tests with fake PIDs never probe real processes (cancel_enforcement marker registered). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…not-modified as no-op (#608) - HttpBotClient records the failure reason per (method, chat_id, message_id) on every failure path (API error description, HTTP status + body excerpt, network error class) in a bounded fetch-and-clear store; TelegramClient.pop_edit_error() exposes it for edits. - transport.edit.failed now logs error=<reason> — previously the Telegram description was only visible in a separate, uncorrelated telegram.api_error line, making the sl aq-callback failures (#598) undiagnosable from logs. - "message is not modified" is normalised to an info-level transport.edit.noop returning the original ref — the edit's intent is already satisfied (#364 family), so it must not read as a failure. The sl 3/3-deterministic aq keyboard-clear failure does NOT reproduce on the dev bot (full ask-mode flow verified: buttons, answer, keyboard clear, final answer all clean). The enriched log pins the actual reason on the next fleet occurrence; if it turns out to be not-modified, the noop path already absorbs it. Tests: reason recorded+popped, store bounded, failed-edit log carries error, not-modified noop returns ref, bots without pop still log. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…rrors (#585) * feat(voice): #584 harden transcription against transient connection errors Voice transcription surfaced transport-level failures from the Groq/ OpenAI-compatible API to the user as the opaque "Connection error." string. These are almost always brief, transient network / provider-edge blips (confirmed on channelo: 2 APIConnectionError events ever vs ~190 successful transcriptions; key/config/network/certs/SDK all healthy and the transport works in isolation). - widen AsyncOpenAI max_retries 2 -> 4 so a sub-~15s blip self-heals before reaching the user - special-case APIConnectionError (incl. subclass APITimeoutError) with an actionable hint instead of "Connection error." - fix unreachable except TimeoutError branch (subclass of OSError) by ordering it before the OSError handler Adds 4 tests covering the connection-error hint, timeout-subclass path, non-connection OpenAIError sanitisation, and the now-reachable stdlib TimeoutError branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(voice): #594 surface endpoint + cause in transcribe errors; validate api_key header-safety - openai.transcribe.error now logs the resolved endpoint (base_url or "openai-default") and the exception __cause__ — APIConnectionError's bare "Connection error." hid the real failure (the channelo outage was an illegal Authorization header from two concatenated keys, undiagnosable from logs). The timeout/error/ unexpected log sites gain the endpoint field too. - voice_transcription_api_key is validated at config load: control characters (embedded newline/tab from concatenated or line-wrapped key material) and non-latin-1 characters are rejected with a diagnosable ConfigError instead of surfacing hours later as a connection error on every voice note (fail-fast precedent: the #381 base-url SSRF validator on the same surface). Tests: endpoint+cause captured in log, default-endpoint marker, newline key rejected, non-latin-1 key rejected, normal key unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Carries the 2026-07-13 fleet-audit fixes (#589-#601 clusters): early answer delivery + limbo grace (#591), transport close (#599), MCP child reaping (#590/#589), pre-result silence cap (#592), cancel enforcement + PID threading (#593), catalog warning flood (#595), empty-result surfacing (#596), image/document schema blocks (#597), edit-failure observability (#598), voice hardening + observability (#584/#594), outbox graveyard (#600), cron count alignment (#601), and the pip-audit dependency bumps. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](softprops/action-gh-release@718ea10...3d0d988) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.3.0 to 8.3.2. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](astral-sh/setup-uv@d31148d...11f9893) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 8.3.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@54f647b...99df26d) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@54f647b...99df26d) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…final delivery (#615) Two-part fix for the cancel-during-post-result-window race: 1. run_runner now holds the runner.run() generator and acloses it in a shielded scope in its finally. Abandoning the async-for mid-body (e.g. /cancel while on_completed awaits) previously left the generator suspended at a yield inside run_impl's anyio task group; the event loop's asyncgen finalizer then threw GeneratorExit from a foreign task and the task group raised 'Attempted to exit cancel scope in a different task than it was entered in' as an unretrieved exception. 2. The #591 early final delivery is now shielded (bounded 15s) so a /cancel landing mid-send can no longer cancel delivery after the message hit the wire but before final_delivery['sent'] was recorded — which produced a spurious 'cancelled' render on top of the delivered answer via the #598 edit-failed fallback. Regression tests reproduce the exact production traceback pre-fix. Fixes #614 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
sl (@hetz_sl_bot, semantic-librarian) was outside the fleet scripts and required manual upgrades. Adds sl to ALL_HOSTS with systemd restart/ postcheck (install manager auto-detected — uv on sl), and updates the host enumerations in CLAUDE.md, release-discipline, dev-workflow, and dev-instance docs from 4 to 5 hosts. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The 15s bound from #615 expired mid-flag-set on a 4-chunk final under group-chat outbox pacing during the rc4 integration gate, re-creating the spurious 'cancelled' render after a fully delivered answer. 60s covers multi-chunk deliveries plus a 429 retry while still bounding a wedged transport. Fixes #618 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
GitHub push protection blocked a push on 2026-07-18 because a `git add -A docs/` swept in an untracked local file containing a Groq API key. Triage (all verified): - never committed — `git log --all -S<key>` and `git grep` both clean - only 1 of 89 untracked files affected — scanned for gsk_/sk-ant-/ghp_/ github_pat_/xoxb-/AWS/PEM patterns - NOT the live key: fingerprint differs from the current value in BWS and in both untether.toml files, consistent with the 2026-07-13 rotation So this was a working-tree-only exposure of an already-rotated credential. The raw value has been redacted from the file in place, and the file is now ignored so it cannot be swept in again. Deliberately NOT ignoring docs/tests/ or docs/audits/ wholesale — 5 files in those trees are tracked, so a blanket rule would misrepresent what is versioned. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude Code runs subagents in the background by default — the tool
contract reads "Subagents run in the background by default; ... Pass
`run_in_background: false` for a synchronous run" — so the flag is
normally ABSENT from the Agent/Task tool input.
`_register_background_handle` gated on `bool(raw_input.get(
"run_in_background"))`, which read omission as *foreground* and
therefore never registered a real subagent. Measured on nsd
2026-07-18: 11/11 Agent calls across the 5 quarantined sessions omit
the key; observed shape is `{"description":…, "subagent_type":…}` and
the tool_result ~25ms later reads "Async agent launched successfully."
Failure chain: no handle -> has_live_background_work() False -> the
post-result idle watchdog applies the 60s limbo grace instead of the
600s timeout -> SIGTERM of a subprocess whose subagents are still
working -> #632 quarantines the healthy session on
forced_teardown_after_result -> the user's next message diverts to a
fresh, contextless session. Runs without subagents exit cleanly and
were never affected, matching the reported negative control.
Fix: `_agent_runs_in_background()` — background unless the caller
explicitly opted out with literal False. Over-registering is the safe
direction (bounded by the 600s post-result ceiling and the 900s
BG_AGENT_MAX_KEEP_S age-out); under-registering force-kills live work
and poisons the session. Bash is deliberately untouched — its
run_in_background flag is genuinely opt-in upstream.
Quarantine semantics are unchanged: once a process has actually been
force-killed after a result its upstream turn may dangle regardless,
so the false positive is fixed at classification, not by weakening
the safety net.
Two tests that locked in the inverted contract now express the
foreground case with an explicit `run_in_background: False`.
Root cause independently validated with gpt-5.6-sol via pal debug.
Verified on @untether_dev_bot with 3 live background subagents: the
subprocess passed through limbo untouched and exited naturally rc=0
(previously SIGTERM rc=143 at the 60s grace); no quarantine, and the
follow-up message resumed the same session, recalling both the
codeword and all three subagents' findings.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ships the #646 default-background subagent registration fix (PR #648) to TestPyPI. The rc8 merge could not publish it — the version was unchanged, so the upload hit "400 File already exists" and was swallowed by skip-existing, leaving the 18 July rc8 wheel in place. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#646 liveness-aware ceiling + announced handoff, #593 diag enrichment (#651) * fix: post-result lifecycle batch — #650 silent reap after delivery, #647/#646 liveness-aware ceiling + announced handoff, #593 diag enrichment #650 — watchdog auto-cancel alarmed the user after a successful delivery: - ProgressEdits: the process_dead auto-cancel arm now checks stream.did_emit_completed first. A dead subprocess whose run already emitted its CompletedEvent is a normally-completed run being reaped late (the detector was racing the subcountdown's returncode poll) — reap through the same cancel machinery but silently: INFO progress_edits.reaped_after_delivery, no WARN, no user-facing "session appears stuck (process_dead)" notice. #614 made the user-initiated cancelled-after-delivery path quiet; this makes the watchdog-initiated variant actually quiet too. - claude.py subcountdown: periodic subcountdown_tick INFO (~30s, tunable) closes the observability blackout between the one-shot limbo_detected warning and loop exit. #647/#646-outcome — 600s post-result ceiling killed live subagent work: - The subcountdown deadline is now liveness-aware: while has_live_background_work() is true and /proc evidence does not show the tree demonstrably idle, the SIGTERM is deferred (re-checked each poll). Bounded twice: bg handles age out at BG_AGENT_MAX_KEEP_S and the hold never exceeds watchdog.post_result_bg_max_hold (default 1800s, 0 disables). Upstream runs subagents in the background by default since Claude Code v2.1.198 and never signals their completion on stream-json, so /proc is the only available evidence. - sigterm_after_timeout now logs live_background_work / bg_hold_extended / cpu_active / tree_active — the "tracked and killed anyway" cohort is identifiable regardless of which resume-divert label lands (#647 monitoring correction). #647 — follow-up during live background work no longer silently amnesic: - wait_for_session_handoff logs entry/exit (the wait could absorb minutes with no journal trace). - handle_message: when the base 30s handoff wait times out and the owner has live background work (new _SESSION_BG_STATE registry + session_live_bg_count()), the user is told why the reply is delayed and the wait extends (condition-based, bounded by new auto_continue.session_handoff_bg_timeout_s, default 600s). - Every fresh-session divert is now announced: handoff_timeout, entry-time quarantine, and the new quarantined_during_handoff re-check (the rc9 incident showed the ceiling can quarantine the owner while the handoff wait is in flight; resuming it would poison the next turn). #593 — diagnostics no longer blind at the cancel decision: - last_seen_alive_s tracked and attached to stall_detected and stall_auto_cancel logs (all liveness fields collapse to None once the process is gone; this records how recently it existed). The pid threading + teardown-enforcement halves of #593 were verified already fixed by #607 (last_pid at spawn, _enforce_cancel_teardown). Tests: 11 new (2 ceiling, 1 tick, 1 registry helper, 2 process_dead gate, 5 handoff policy); full suite 2977 passed, 1 known order-dependent failure (#641) unchanged, passes in isolation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: staging 0.35.4rc10 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…e broke sl/mac rolls (#652) uv's HTTP cache serves a pre-upload copy of the TestPyPI simple index, so 'uv tool install --force untether==X' fails with 'no version of untether==X' minutes after a publish even when the remote index already has the wheel. Bit the rc6 roll (worked around manually) and the rc10 roll (sl + mac FAILED, pipx hosts unaffected). --refresh forces revalidation of cached index data per invocation; install cost is unchanged materially. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…653 limbo_detected level, #654 queued-wait notice (#656) * fix(claude): post-result UX batch — #655 busy-aware limbo grace, #653 limbo_detected level, #654 queued-wait notice - #655: the #591 limbo-grace cap now consults cpu_active/tree_active — a demonstrably busy post-result process with no registered background handles falls through to the full timeout_s instead of being SIGTERM'd at 60s, quarantined, and diverted to a fresh contextless session - #653: runner.limbo_detected logs INFO when the same evaluation shows live background work or a busy process tree; WARNING reserved for the genuinely-quiescent stuck case; event enriched with live_background_work/cpu_active/tree_active fields - #654: queued follow-ups behind a lingering post-result Claude session now say why they're waiting (background-task count, context carry-over, /cancel hint) instead of sitting on a bare "queued" for up to 30 min Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(claude): #655 end-to-end check with a real busy subprocess and real /proc CPU accounting Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: staging 0.35.4rc11 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#659/#475/#641) (#660) * fix: 0.35.4rc12 batch — #657 bare rate-limit latch, #638 voice language hint, #572 Type-A auto-retry, #570 cooldown retirement, #659 plan-input outline gate, #475 resolved model hint, #641 test hang-guard - #657: bare rate_limit_event (all timing fields null) now latches DEFAULT_BARE_RATE_LIMIT_WAIT_S=60s with retry_after_source="default" so awaiting_rate_limit_retry() is directionally correct; estimate shown as "waiting to retry (~60s)" - #638: new [transports.telegram] voice_transcription_language ISO-639-1 hint passed to the Whisper API language param; hot-reloadable; validated at config parse - #572: [watchdog] stream_idle_auto_retry (default off) + stream_idle_max_retries — bounded auto-resume for Type-A mid-generation stream-idle timeouts; Type-B never retries; signal-death + cost-budget guards; claude.stream_idle.auto_retry structured log - #570: progressive-cooldown workaround retired — upstream ExitPlanMode immediate-retry loop verified fixed on CLI 2.1.215; text-based outline gate (mark_outline_pending) survives - #659: ExitPlanMode plan input now satisfies the outline gate — on plan-file CLIs no chat text is written and the gate deny-looped until Claude gave up; plan body renders as the standalone outline message - #475: /config Model line resolves the engine's real default (opencode/pi settings files) via get_engine_default_model(), placeholder hints kept as fallback - #641: 2s anyio.fail_after hang-guards flaked on cold coverage runs — raised to 30s (root cause was timing, not test-order state) - #639 (out-of-repo): watcher event-set + msgspec dedup signature documented in contrib/README-issue-watcher.md; script deployed to all 5 hosts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(tests): #641 — raise remaining fail_after hang-guards to 30s The CI 3.12 runner reproduced the same flake class in test_run_main_loop_routes_reply_to_running_resume (2s guard expired under coverage on a loaded runner). All run_main_loop hang-guards in this file are now 30s — they exist to catch hangs, not to race the loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ync counts and config keys (#661) - CLAUDE.md: test count 2914→3010 + drifted per-file counts; Pause & Outline bullet reflects #659 plan-input gate + #570 cooldown retirement; watchdog bullet gains #657 bare rate-limit latch + #572 stream_idle_auto_retry keys; hot-reload bullet names voice_transcription_language - docs/how-to/plan-mode.md: "Progressive cooldown" section rewritten as "The outline gate" (plan-input hold-open + no-outline auto-deny; historical note on the retired 30-120s escalation) - docs/how-to/voice-notes.md + docs/reference/config.md: voice_transcription_language documented (#638) - docs/faq/faq.md: feature-list phrasing updated (cooldown → Pause & Outline plan gate) - untether-architecture skill: claude_control.py row cooldown→outline-gate wiring Point-in-time records (docs/plans, docs/audits, docs/research, promo archives) deliberately untouched. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The v0.35.4 section documented only rc1, rc2 and the rc6 audit batch (12 entries). rc3, rc4, rc5 and rc7-rc12 were entirely undocumented — roughly 38 shipped issues, including the headline work of the cycle: the empty-resume quarantine-and-fresh recovery, the post-result lifecycle batch, the concurrency-aware OOM guard, and the rc12 interactive batch. 12 -> 50 entries across fixes / changes / tests / docs. Date corrected 2026-05-30 -> 2026-07-20. Verified before commit: - 52 cited issue numbers, all confirmed real issues (0 PR numbers), so validate_release.py will not trip on a PR reference - 0 entries missing an issue link - 0 shipped issues left undocumented; the only uncited refs in the v0.35.3..dev range are prior-milestone context refs, the v0.35.5 deferral #527, and three upstream claude-code numbers Two deliberate choices: - #374 amended in place rather than duplicated, and now records that Bash-bg/ScheduleWakeup/RemoteTrigger terminal-detection stays deferred to #573 - a header comment records that rc8's TestPyPI publish was a silent no-op (version unchanged -> skip-existing swallowed the 400), so the rc8 commits first executed on fleet hosts as rc9; "verified in rc8" is always wrong Follow-up to #661. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@9c091bb...3d3c42e) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@99df26d...7188fc3) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@99df26d...7188fc3) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.14.0 to 1.14.1. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](pypa/gh-action-pypi-publish@cef2210...ba38be9) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(commands): Phase 0 foundation for agentic loop commands
Commit the previously-untracked /debug 8-step bundle and lay the shared
foundation for the agentic loops & slash-command suite (plan:
docs/plans/agentic-loops-and-commands/README.md, Phase 0).
- track .claude/commands/{debug.md,debug/} (was ?? untracked)
- harden debug/step-evidence.md: mandatory redaction pass (tokens/keys/env/
chat-content/fleet identifiers) + output-size bound on evidence dumps +
a /handover pointer (no change to the investigation flow)
- add docs/LOOPS.md — the loop registry (L1-L9 agentic + A1-A6 automated),
honest build-status per loop
- add .claude/rules/workflow-commands.md — routing table + 7 cross-cutting
rules cited by every workflow command
- .gitignore: incoming/{*-runs,handovers,docs}/ (command run artefacts)
- CLAUDE.md: register the workflow-commands rule
No behaviour change to /debug. Foundation only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(commands): Phase 1 — /fix defect sweep + kaizen loop
Production action-half + continuous improvement (plan:
docs/plans/agentic-loops-and-commands/README.md, Phase 1).
- /fix — action-authority sweep over the /debug 8-step engine: rank the open
actionable-defect list, fix to a verified hand-off (branch off dev, minimal
fix, pytest+ruff, changelog, PR to dev, needs-verification). Runaway cap
(>8 → top 5 + queued-next-run; >15 → STOP diagnosis-only) + batch-cohesion
rule (never co-batch independent high-risk state machines) + PR #660 table body.
- /kaizen — session-end capture of 0-3 evidence-linked process learnings on a
lazily-created [kaizen] collector; read-only except ONE comment; 0 is valid.
- /kaizen-review — weekly propose-only promotion (gate + hierarchy); never
auto-edits rules/hooks/CLAUDE.md/code.
- .claude/rules/kaizen.md (thin slice) + docs/kaizen/README.md (full policy).
- .claude/hooks/kaizen-stop-nudge.sh (Stop-hook nudge; Untether/stop-loop-safe;
transcript-driven substantive/friction gate) + test harness (10/10 pass).
hooks.json wiring is a propose-to-Nathan step; /kaizen self-invokes until then.
- docs/LOOPS.md: L6/L7/L8 → available. CLAUDE.md: register kaizen rule.
Live slash-command dry-runs (/fix --dry-run, /kaizen, /kaizen-review) are
operator follow-ups — they make confirm-gated GitHub writes and the collector
scheme is pending Nathan's confirmation (plan §9).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(commands): Phase 2 — /plan + /implement delivery prepare-half
Delivery prepare-half (plan: docs/plans/agentic-loops-and-commands/README.md,
Phase 2).
- /plan — idea (incoming/<file>.md | #issue | inline) → grounded phased plan
pack under docs/plans/<slug>/. Read-only on code: redirect-check →
superpowers:brainstorming → Explore current-state map → research gate
(/research + docs/findings/) → feature-dev:code-architect blueprint + reuse
map → pack (README/00-current-state/NN-phase/decisions/progress-tracker).
Untether exit-gate = "signature emitted by a real code path, not a fixture";
coverage-inventory sweep dropped.
- /implement — execute ONE approved phase to a verified hand-off: gate-check →
pre-flight ground-truth (#1200 analogue) → TDD in-scope → whole-class sweep
(#1201 analogue) → verify (phase tests + pytest + ruff + rule-mandated suite
on runner/schema/telegram surfaces) → commit to feature/<slug>-phase-N →
STOP before the PR. Refuses multi-phase; never widens scope; defect work → /fix.
- docs/LOOPS.md: L1/L2 → available.
Live slash-command runs (/plan a small idea, /implement --dry-run) are operator
acceptance steps — project slash commands are operator-invoked, and /plan drives
brainstorming + Explore + code-architect subagents.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(commands): Phase 3 — /qa validate + /pr-dev + /pr-main release split
Delivery VALIDATE + RELEASE boundaries from the agentic-loops plan (P3):
- /qa — adaptive QA-0..QA-5; guide-first live-bot guardrails (default
plan/dry-run, dev-bot allowlist + fail-closed, bounded + run-ID-tagged);
SHA-bound attestation write on green via run-integration-tests.sh.
- /pr-dev — everyday finalise → ONE PR to dev with docs folded in
(CHANGELOG/FAQ/## Tests), batch-cohesion, table-shaped body; may merge
to dev only (--merge + confirm).
- /pr-main — stable bump + uv lock + changelog collapse + validate + FAQ
→ open dev→master PR, then STOP at Nathan's merge gate.
allowed-tools grant no forbidden verbs (no gh pr merge/tag/release/
fleet-rollout on /qa or /pr-main). Registry status flips ship in Phase 4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(commands): Phase 4 — /handover, advisory reviewers, /docs, research findings
Optional/continuity half of the agentic-loops plan (P4) + registry completion:
- /handover — H0–H3 interruption stop-state, derived from persisted state
(git/tests/quarantine, never chat memory), git-fingerprinted; defaults DOWN.
- .claude/agents/{debug,delivery,qa}-reviewer — advisory, non-authoring
read-only reviewers (verdict + gaps; never edit/file/merge).
- /docs — thin escape-hatch to reconcile docs OUTSIDE a PR (default path
stays folded into /pr-dev).
- docs/findings/README.md — the /research findings convention (reuses the
global /research; provider/current-truth gate for /plan//debug//qa).
Registry: docs/LOOPS.md flips L3/L4a/L4b/L9 → available, adds the support-
commands + advisory-reviewers sections; workflow-commands.md adds /docs to
the authority ladder + the reviewer note.
Deliberately NOT built (plan gates behind "only if a real need appears"):
/context-health, /issue-triage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#675) The fleet-rollout gate marker was effectively a reusable boolean — it bound a version string but not the commit tested, so a marker written against commit A satisfied a rollout of a different build at the same version. - run-integration-tests.sh: record head_sha (auto-derived from the script's repo via git rev-parse, overridable via --head-sha / UT_INTEGRATION_HEAD_SHA) and machine-readable dev_bot_id (default 8678330610, UT_DEV_BOT_ID override) in the marker JSON. Existing fields unchanged (backward compatible). - fleet-rollout.sh: surface the bound head_sha + dev-bot identity at the gate (Guardrail 3, informational) and warn on pre-#674 markers with no head_sha. No hard-gate change — old markers keep working. - /qa (qa.md) Q-6: pass --head-sha as a first-class field instead of stuffing the SHA into --notes. - tests/test_attestation_marker.py: 5 tests — auto-derive, both overrides, dev_bot_id default/override, tiers+notes preserved (temp-HOME subprocess). Full suite: 3013 passed, 2 skipped, 83.03% coverage. Operator tooling only (not shipped in the wheel) — no version bump, no changelog entry. Closes #674 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…handoff-failure log (#668) (#676) rc13 follow-ups to the v0.35.4 background/handoff batch. #667 (#640 follow-up): capture proc_returncode in run_impl's finally, not only the happy-path assignment after `await proc.wait()`. /cancel, /new, drain, a task-group/JSONL-reader exception, and the early pipes RuntimeError all skipped it and left proc_returncode None — so _is_signal_death(None) was False and the auto-continue death-spiral guard was inert on exactly those paths. manage_subprocess.__aexit__ already runs a shielded bounded terminate+reap before the finally, so the capture is a guarded non-blocking read (guarded because proc/stream can be unbound on early-exit paths). #668 (#633 follow-up): bind engine/session_id/chat_id onto session.handoff_check_failed so a persistent fail-closed probe failure is attributable (the issue-watcher dedups by signature, collapsing every chat into one report). Correct the stale serialize_session_owner comment in settings.py to match the divert-fresh-WITHOUT-quarantine behaviour, noted as a deliberate deviation from the #633 W4 design. Tests: new cancel-mid-flight harness test driving a real ClaudeRunner subprocess + hang_before_result fake-CLI scenario; attribution assertions on test_633_probe_failure_fails_closed. Full suite green (3014 passed), ruff clean. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…skUserQuestion (#598) (#677) A keyboard-clear edit that the outbox coalesces out before dispatch — a newer same-key progress edit supersedes it, a delete/replace drops it, or a RetryAfter requeue collides — was resolved to None, indistinguishable at the edit() layer from a real editMessageText failure. That produced the `transport.edit.failed error=None` warning after every answered AskUserQuestion, and is why PR #608's HTTP-layer error capture never populated it: a superseded op never makes an HTTP call, so no api_error is recorded. Edit ops now opt into a per-op SUPERSEDED outbox disposition threaded through all three intentional-drop sites (same-key supersede, drop_pending, RetryAfter requeue collision). TelegramTransport.edit treats SUPERSEDED as a benign transport.edit.superseded no-op and returns the ref. send/delete keep the None failure contract, so a superseded replace-send can never be misread as a sent message. Root-caused with gpt-5.6-sol (pal). 5 new tests across test_telegram_queue.py and test_telegram_bridge.py. Bumps staging to 0.35.4rc14. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cs (#678) Bump 0.35.4rc14 → 0.35.4 (pyproject.toml + uv.lock) and finalise the v0.35.4 release documentation ahead of the dev→master release PR. - CHANGELOG: finalise the v0.35.4 section (rc1–rc14), add the #674 attestation- marker entry, refresh the header comment + date; regenerate the docs mirror (docs/reference/changelog.md gains the missing v0.35.3 + v0.35.4 sections) - config reference: document 8 new-in-0.35.4 TOML keys — pre_result_silence_timeout, post_result_limbo_grace, post_result_bg_max_hold (#591/#592/#647), session_handoff_bg_timeout_s (#647), voice_transcription_url_allowlist + SSRF note (#381), outbox_deliver_directories/outbox_notify_skipped (#524/#628), allow_unauthenticated_webhooks (#382) - how-to/update: new "Upgrading to v0.35.4" operator section - voice-notes + telegram ref: SSRF caveat + allowlist for local Whisper (#381) - triggers + webhooks-and-cron: auth="none" non-loopback refusal (#382) - file-transfer + faq: outbox directory-zip delivery now shipped (#628) - troubleshooting + faq: empty-resume quarantine-and-fresh (#631/#632), queued-follow-up notice (#654) - README: retire "progressive cooldown" → plan outline gate (#570); outbox zip - CLAUDE.md test count 3015 → 3021; specification + tutorial version strings Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 139 files, which is 39 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (139)
You can disable this status message by setting the ✨ 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 |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
…e-A auto-retry, voice SSRF) (#681) Aligns user-facing docs with features already shipped and changelogged in v0.35.4. No behaviour change; no CHANGELOG entry (these describe #570/#572/#381, already covered in the v0.35.4 release notes). - README.md: "Plan mode ... progressive cooldown" -> "the outline gate" (the progressive cooldown was retired in v0.35.4, #570) - docs/how-to/troubleshooting.md: the Type-A stall note said auto-retry was "deferred to a future release" — it shipped in v0.35.4 (#572). Now documents the opt-in `[watchdog] stream_idle_auto_retry` / `stream_idle_max_retries`. - docs/how-to/troubleshooting.md: voice section now notes the SSRF refusal of private/loopback `voice_transcription_base_url` and the `voice_transcription_url_allowlist` opt-in (#381). - docs/tutorials/interactive-control.md: two spots described the retired progressive cooldown as current behaviour — rewritten to the outline gate, matching docs/how-to/plan-mode.md. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release PR for v0.35.4 (56 commits on
devsincev0.35.3; full rc1–rc14 cycle). Merging this tomasteris the single release gate — it triggersauto-tag-on-master.yml(v0.35.4tag) →release.yml(PyPI publish via OIDC + GitHub Release). SeeCHANGELOG.mdfor the full entry.Highlights
Reliability (the empty-resume / post-result-lifecycle milestone)
tool_progressheartbeat schema (0.35.4rc7: claude schema rejects new 'tool_progress' event type (jsonl.msgspec.invalid ×2) — #489/#597 family #637)Security
voice_transcription_base_url(security: enforce SSRF validation on voice_transcription_base_url #381); refuseauth="none"webhooks on non-loopback hosts (security: startup-block when any webhook has auth=none #382);flock(2)lockfile replacing the reused-PIDos.killcheck (lockfile: PID-reuse race causes permanent crash-restart loop #459)Features
voice_transcription_languagehint (0.35.4rc7: voice transcription renders wrong language on short utterances ('Continue' → '계속') — no language hint passed #638);fleet-status.sh+ attestation guardrails (Fleet ops: scripts/fleet-status.sh + rollout attestation-gate guardrails #627); attestation-marker SHA-binding (enh: SHA-bind the integration-test attestation marker (head_sha + dev-bot identity) #674)Engines / plan mode
Release CI
RELEASE_TAG_PATso the auto-tag actually triggersrelease.yml(release pipeline: auto-tag-on-master.yml tag push doesn't trigger release.yml (manual dispatch required) #376) — restores the unattended single-gate flowVersion / docs
0.35.4rc14→0.35.4(pyproject.toml+uv.lock); user-facing docs synced to the release (config keys,update.mdupgrade section, voice/webhook/outbox behaviour) — see chore(release): finalise v0.35.4 (stable) — version bump + release docs #678.Integration-test status
@untether_dev_botattestation marker from 2026-07-22.0.35.4attestation marker exists yet. That gatesscripts/fleet-rollout.sh(production-host rollout), not this PyPI publish. Recommend writingscripts/run-integration-tests.sh 0.35.4 --manual(rc14 coverage) before the fleet roll — happy to do that on request.Opened by Claude Code as the release artifact; the merge is reserved for Nathan Schram (@nathanschram) (release-guard + CODEOWNERS). This PR was not merged and will not be merged by Claude Code.
🤖 Generated with Claude Code