Skip to content

Commit 257aeb8

Browse files
committed
Retire the stale dead-session opacity backlog item with regression proof
ROADMAP #38 no longer reflects current main. The runtime already runs a post-compaction session-health probe, but the backlog lacked explicit regression proof. This change adds focused tests for the two important behaviors: a broken tool surface aborts a compacted session with a targeted error, while a freshly compacted empty session does not false-positive as dead. With that proof in place, the roadmap item can be marked done. Constraint: User required fresh cargo fmt/clippy/test evidence before closing any backlog item Rejected: Leave #38 open because the implementation already existed | backlog stays stale and invites duplicate work Confidence: high Scope-risk: narrow Reversibility: clean Directive: Reopen #38 only with a fresh same-turn repro that bypasses the current health-probe gate Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: No live long-running dogfood session replay beyond existing automated coverage
1 parent 7ea4535 commit 257aeb8

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ Model name prefix now wins unconditionally over env-var presence. Regression tes
440440

441441
37. **Claude subscription login path should be removed, not deprecated** -- dogfooded 2026-04-09. Official auth should be API key only (`ANTHROPIC_API_KEY`) or OAuth bearer token via `ANTHROPIC_AUTH_TOKEN`; the local `claw login` / `claw logout` subscription-style flow created legal/billing ambiguity and a misleading saved-OAuth fallback. **Done (verified 2026-04-11):** removed the direct `claw login` / `claw logout` CLI surface, removed `/login` and `/logout` from shared slash-command discovery, changed both CLI and provider startup auth resolution to ignore saved OAuth credentials, and updated auth diagnostics to point only at `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`. Verification: targeted `commands`, `api`, and `rusty-claude-cli` tests for removed login/logout guidance and ignored saved OAuth all pass, and `cargo check -p api -p commands -p rusty-claude-cli` passes. Source: gaebal-gajae policy decision 2026-04-09.
442442

443-
38. **Dead-session opacity: bot cannot self-detect compaction vs broken tool surface** -- dogfooded 2026-04-09. Jobdori session spent ~15h declaring itself "dead" in-channel while tools were actually returning correct results within each turn. Root cause: context compaction causes tool outputs to be summarised away between turns, making the bot interpret absence-of-remembered-output as tool failure. This is a distinct failure mode from ROADMAP #31 (executor quirks): the session is alive and tools are functional, but the agent cannot tell the difference between "my last tool call produced no output" (compaction) and "the tool is broken". Downstream: repetitive false-dead signals in the channel, work not getting done despite the execution surface being live. Fix shape: (a) probe with a short known-output command at turn start if context has been compacted; (b) gate "I am dead" declarations behind at least one within-turn tool call with a verified non-empty result; (c) consider adding a session-health canary cron that fires a wake with a minimal probe and checks the result. Source: Jobdori self-dogfood 2026-04-09; observed in #clawcode-building-in-public across multiple Clawhip nudge cycles.
443+
38. **Dead-session opacity: bot cannot self-detect compaction vs broken tool surface** -- dogfooded 2026-04-09. Jobdori session spent ~15h declaring itself "dead" in-channel while tools were actually returning correct results within each turn. Root cause: context compaction causes tool outputs to be summarised away between turns, making the bot interpret absence-of-remembered-output as tool failure. This is a distinct failure mode from ROADMAP #31 (executor quirks): the session is alive and tools are functional, but the agent cannot tell the difference between "my last tool call produced no output" (compaction) and "the tool is broken". **Done (verified 2026-04-11):** `ConversationRuntime::run_turn()` now runs a post-compaction session-health probe through `glob_search`, fails fast with a targeted recovery error if the tool surface is broken, and skips the probe for a freshly compacted empty session. Fresh regression coverage proves both the failure gate and the empty-session bypass. Source: Jobdori self-dogfood 2026-04-09; observed in #clawcode-building-in-public across multiple Clawhip nudge cycles.
444444

445445
39. **Several slash commands are registered but not implemented: /branch, /rewind, /ide, /tag, /output-style, /add-dir** -- dogfooded 2026-04-09. These commands appear in the REPL completions surface but silently print 'Command registered but not yet implemented.' and return false. Users (mezz2301 in #claw-code) hit this as 'many features are not supported in this version now'. Fix shape: either (a) implement the missing commands, or (b) remove them from completions/help output until they are ready, so the discovery surface matches what actually works. Source: mezz2301 in #claw-code 2026-04-09; pinpointed in main.rs:3728.
446446

rust/crates/runtime/src/conversation.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,88 @@ mod tests {
16111611
);
16121612
}
16131613

1614+
#[test]
1615+
fn compaction_health_probe_blocks_turn_when_tool_executor_is_broken() {
1616+
struct SimpleApi;
1617+
impl ApiClient for SimpleApi {
1618+
fn stream(
1619+
&mut self,
1620+
_request: ApiRequest,
1621+
) -> Result<Vec<AssistantEvent>, RuntimeError> {
1622+
panic!("API should not run when health probe fails");
1623+
}
1624+
}
1625+
1626+
let mut session = Session::new();
1627+
session.record_compaction("summarized earlier work", 4);
1628+
session
1629+
.push_user_text("previous message")
1630+
.expect("message should append");
1631+
1632+
let tool_executor = StaticToolExecutor::new().register("glob_search", |_input| {
1633+
Err(ToolError::new("transport unavailable"))
1634+
});
1635+
let mut runtime = ConversationRuntime::new(
1636+
session,
1637+
SimpleApi,
1638+
tool_executor,
1639+
PermissionPolicy::new(PermissionMode::DangerFullAccess),
1640+
vec!["system".to_string()],
1641+
);
1642+
1643+
let error = runtime
1644+
.run_turn("trigger", None)
1645+
.expect_err("health probe failure should abort the turn");
1646+
assert!(
1647+
error
1648+
.to_string()
1649+
.contains("Session health probe failed after compaction"),
1650+
"unexpected error: {error}"
1651+
);
1652+
assert!(
1653+
error.to_string().contains("transport unavailable"),
1654+
"expected underlying probe error: {error}"
1655+
);
1656+
}
1657+
1658+
#[test]
1659+
fn compaction_health_probe_skips_empty_compacted_session() {
1660+
struct SimpleApi;
1661+
impl ApiClient for SimpleApi {
1662+
fn stream(
1663+
&mut self,
1664+
_request: ApiRequest,
1665+
) -> Result<Vec<AssistantEvent>, RuntimeError> {
1666+
Ok(vec![
1667+
AssistantEvent::TextDelta("done".to_string()),
1668+
AssistantEvent::MessageStop,
1669+
])
1670+
}
1671+
}
1672+
1673+
let mut session = Session::new();
1674+
session.record_compaction("fresh summary", 2);
1675+
1676+
let tool_executor = StaticToolExecutor::new().register("glob_search", |_input| {
1677+
Err(ToolError::new(
1678+
"glob_search should not run for an empty compacted session",
1679+
))
1680+
});
1681+
let mut runtime = ConversationRuntime::new(
1682+
session,
1683+
SimpleApi,
1684+
tool_executor,
1685+
PermissionPolicy::new(PermissionMode::DangerFullAccess),
1686+
vec!["system".to_string()],
1687+
);
1688+
1689+
let summary = runtime
1690+
.run_turn("trigger", None)
1691+
.expect("empty compacted session should not fail health probe");
1692+
assert_eq!(summary.auto_compaction, None);
1693+
assert_eq!(runtime.session().messages.len(), 2);
1694+
}
1695+
16141696
#[test]
16151697
fn build_assistant_message_requires_message_stop_event() {
16161698
// given

0 commit comments

Comments
 (0)