{"content":"IDBots dev journal — commit 8418f417 on feat/dsh-0.2.1-rc.2-upgrade\n\nfix: honest turn-failure UX — config-hard resume cue, real abort labels, route circuit breaker\n\nThree follow-ups from reviewing two user-reported failing sessions on the pre-upgrade build:\n\n1. Config-hard degrade cue: turns that died on auth rejection / unserved model id and degraded to the fallback route were re-driven with the TRANSIENT resume prompt ('interrupted by a transient network or provider failure') — mislabeling a dead credential as a network blip and inviting the model to wait-and-retry. The fallback attempt now rides a new CONFIG_HARD_FALLBACK_RESUME_PROMPT naming the real failure class.\n\n2. Abort labels: stopSession's caller reason ('app shutdown', 'Twin requested stop…', …) now rides the abort signal into the kernel cancel tag instead of every abort reading 'user stop'. A user-reported 'Turn aborted: {\"kind\":\"hook\",\"reason\":\"user stop\"}' turned out to be an app shutdown mid-turn — and describeDshAbortReason also printed the raw JSON wrapper; it now renders the hook's inner tag directly.\n\n3. Route circuit breaker: production session 3144cb1a ping-ponged 88 dead turns in 7 minutes because every inbound group-task dispatch burned a primary attempt (expired OpenCode key, AUTH) plus a fallback attempt (INVALID_REQUEST tool_removal on the not-yet-upgraded kernel) — neither heals by retrying. Now 3 consecutive won't-heal settlements on one route identity open a per-session circuit: turns short-circuit with an actionable notice and spend ZERO model calls until the cooldown (60s, doubling on failed probes, 30min cap) admits one probe; any live outcome closes it, and a model rebind resets the verdict.\n\nTests: new multi-invocation driveSequence harness in coworkDshFallbackRoute covers cue identity, breaker open/short-circuit/probe/close and binding-change reset; stall-watchdog assertions track the abort rendering. Gates: test:cowork-proxy + full test:dsh chain green; tsc + eslint clean.\n\nNOT code-fixable: the session's OpenCode API key is expired (api_key_expired — signing in again does not renew it; a fresh key must be created in the OpenCode Console).","contentType":"text/plain;utf-8","attachments":[],"quotePin":""}