[P0][SAFETY] Agentic crisis screen reads a key nothing writes since ff421d3 — multi-turn escalation detection is inert in the beta brain #129

Closed
opened 2026-08-07 03:18:40 +00:00 by tim.lingo · 0 comments
Member

A safety regression we introduced on 2026-08-05 is in the current beta brain. Filing immediately per our own rule that the safety layer is sacred.

What is broken

On the agentic path, the crisis screen's history-amplification half receives an empty string on every real conversation, so it always scores 0. Single-message crisis detection still works; escalation across a multi-message conversation does not.

Root cause — a key rename that missed one reader

ff421d3 ("history keeps its provenance and its session", 2026-08-05) correctly moved conversation history to a per-session key:

fn conv_hist_key(session_id) { if session_id == "" { return "conv_history" } return "session_hist_" + session_id }

chat.el:2559 was not moved with it and still reads the old anonymous bucket:

let history: String = state_get("conv_history")
let screen_result: String = safety_screen(message, history)

The desktop app always mints a session id (DaemonClient.kt:706), so session_id is never empty in practice, history is always written under session_hist_<id>, and state_get("conv_history") always returns "".

The bitter part: the comment directly above that line documents this exact bug being fixed once already under issue #9"Old key caused history-amplification in safety_screen to always receive '' on agentic path." The fix was correct then; the rename re-broke it, and the comment now describes a repair that no longer holds.

Scope

  • Agentic turns only. The plain path is correct (soul.el:397 uses conv_hist_key).
  • Confirmed present in the round-9.1 lineage: ff421d3 is an ancestor of the branch the shipped brain (8ec39278…) was built from — verified with git merge-base --is-ancestor.
  • Hard-bell on a single message is unaffected. What is lost is the case the escalation logic exists for: distress that builds across several turns.

What done looks like

chat.el:2559 reads through conv_hist_key(session_id) like every other consumer, plus a test that fails if any safety input is read from a key no producer writes. Suggested general rule — the same defect class produced three other findings in today's audit: any read of a state key that nothing writes should be a build-time error, not a silent empty string.

Not exploited, not user-reported — found by a static sweep, verified by reading both sides.

**A safety regression we introduced on 2026-08-05 is in the current beta brain. Filing immediately per our own rule that the safety layer is sacred.** ## What is broken On the **agentic path**, the crisis screen's history-amplification half receives an empty string on every real conversation, so it always scores 0. Single-message crisis detection still works; **escalation across a multi-message conversation does not.** ## Root cause — a key rename that missed one reader `ff421d3` ("history keeps its provenance and its session", 2026-08-05) correctly moved conversation history to a per-session key: ``` fn conv_hist_key(session_id) { if session_id == "" { return "conv_history" } return "session_hist_" + session_id } ``` `chat.el:2559` was not moved with it and still reads the old anonymous bucket: ``` let history: String = state_get("conv_history") let screen_result: String = safety_screen(message, history) ``` The desktop app **always** mints a session id (`DaemonClient.kt:706`), so `session_id` is never empty in practice, history is always written under `session_hist_<id>`, and `state_get("conv_history")` always returns `""`. **The bitter part:** the comment directly above that line documents this exact bug being fixed once already under issue #9 — *"Old key caused history-amplification in safety_screen to always receive '' on agentic path."* The fix was correct then; the rename re-broke it, and the comment now describes a repair that no longer holds. ## Scope - Agentic turns only. The plain path is correct (`soul.el:397` uses `conv_hist_key`). - Confirmed present in the round-9.1 lineage: `ff421d3` is an ancestor of the branch the shipped brain (`8ec39278…`) was built from — verified with `git merge-base --is-ancestor`. - Hard-bell on a single message is unaffected. What is lost is the case the escalation logic exists for: distress that builds across several turns. ## What done looks like `chat.el:2559` reads through `conv_hist_key(session_id)` like every other consumer, plus a test that fails if any safety input is read from a key no producer writes. **Suggested general rule** — the same defect class produced three other findings in today's audit: any read of a state key that nothing writes should be a build-time error, not a silent empty string. Not exploited, not user-reported — found by a static sweep, verified by reading both sides.
tim.lingo added the BETA-CRITICALblocks-public-betaP0 labels 2026-08-07 13:51:30 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: neuron-technologies/neuron#129