conductor(track): nagent_review_v3 §2 Conversation safety net cluster
This commit is contained in:
@@ -64,7 +64,62 @@ update {slug} {
|
||||
|
||||
## §2 Conversation safety net
|
||||
|
||||
(filled in by Phase 3 — covers `38d3d4f`, `6426a67`)
|
||||
**Source:** nagent `38d3d4f`, `6426a67` (`bin/nagent:1455-1687` + `:1840-1881` + `:2463-2677` + `:2819`, `bin/helpers/nagent_distill_lib.py:587-654` + `:851-862`, `config.example.json:3-7`, `prompts/checkpoint-conversation.md`, `README.md:653-668` + `:323-332`, `issues/0004-conversation-safety-net.md`, `tests/test_nagent_safety.py`, `tests/test_nagent_distill.py`)
|
||||
**One-liner:** A conversation that outgrows its window gets caught, not killed. Checkpoints are a separate one-call writer, not the working model; rebuild is a deterministic string assembly that runs a synchronous checkpoint first; saves are instant because the summary is extracted from the checkpoint's already-paid-for Intent line, not a new LLM call.
|
||||
**Pattern(s) vs v2.3:** EXTENDS v2.3 Pattern 5 ("the loop") with failure-recovery semantics. v2.3 had the loop; v3 makes the loop survive long-running conversations. EXTENDS v2.3 Pattern 11 ("large files as explicit artifacts") — checkpoints are an explicit working-state artifact (separate from the conversation) that the user can edit between triggers. The instant-saves change extends v2.3 Pattern 7 ("repo history as data") with deferred-cost summaries — the LLM cost moves to a place where it's visible (dry-run reports) and bounded (per-pass), not paid up-front.
|
||||
**Manual Slop implications:** The "sync checkpoint first" invariant maps to Manual Slop's existing `Result[T]` discipline (per `conductor/code_styleguides/error_handling.md`) — failure never blocks; the failure widens the fallback instead. Manual Slop's current Discussion entry write paths could adopt the `summary_source: extracted | llm` pattern; right now every save may do an implicit LLM call. The 3-number config (`checkpoint_interval_minutes`, `checkpoint_max_new_kb`, `rebuild_at_kb`) is a model Manual Slop should follow: operations should be configurable in units `ls -l` can verify, not in token-percentage estimates that drift per provider.
|
||||
**Decision candidate:** NEW Candidate 18 (HIGH). "Discussion-window safety net for Manual Slop": adopt the checkpoint + rebuild pattern for the discussion history; backfill summary entries from the existing intent line; surface extracted-vs-llm provenance in the discussion index. See `decisions.md` Candidate 18.
|
||||
**Cross-refs:** `conductor/tracks/fable_review_20260617` (the Fable review's analysis of "watch-dogging" is the opposite pattern — nagent's safety net is structural, not persona-driven). §1 Campaigns cross-references the safety net as the failure-recovery layer for what decomposition cannot bound.
|
||||
**Source-read citations:**
|
||||
- `bin/nagent:1455-1687` — `run_safety_net` + `checkpoint_due` + `rebuild_due` + `write_checkpoint` + `rebuild_conversation` (38d3d4f)
|
||||
- `bin/nagent:1840-1881` — `extract_conversation_summary` (6426a67)
|
||||
- `bin/nagent:2463-2677` — `--summarize-conversation` CLI surface (6426a67)
|
||||
- `bin/nagent:2819` — `safety_settings=load_safety_settings(...)` wired into `run_agent_loop` (38d3d4f)
|
||||
- `config.example.json:3-7` — 3 safety-net config numbers, all units `ls -l` can verify (38d3d4f)
|
||||
- `prompts/checkpoint-conversation.md` — checkpoint LLM prompt (38d3d4f)
|
||||
- `bin/helpers/nagent_distill_lib.py:587-654` — `_summary_backfill_candidates` + `_backfill_saved_summaries` (6426a67)
|
||||
- `bin/helpers/nagent_distill_lib.py:851-862` — backfill wired into the distill apply path (6426a67)
|
||||
- `README.md:653-668` — safety-net teaching in Part VI (38d3d4f)
|
||||
- `README.md:323-332` — instant-saves teaching in Part II (6426a67)
|
||||
- `issues/0004-conversation-safety-net.md` — the spec; reworked at 6443d70 to wall-clock cadence (199a36b)
|
||||
- `tests/test_nagent_safety.py` — safety-net test file (38d3d4f)
|
||||
**Honest gaps in this cluster:**
|
||||
- The `delta_start = min(meta[1], len(content))` clamp at `bin/nagent:1566` could produce a misleading delta if a user edit deletes characters between checkpoints (the recorded size becomes larger than current content). The clamp hides the failure; the delta would be the entire current content, not the actual new activity. Minor edge case; the spec does not address it.
|
||||
- The `REBUILD_TAIL_CHARS = 64 * 1024` default at `bin/nagent:1463` is explicitly unmeasured ("mirrors MiMo's ~65K tokens until measured otherwise" per `issues/0004-conversation-safety-net.md:42-44`). A future track should measure actual rebuild-tail needs.
|
||||
- `best-of-N` is mentioned in the initial context at `bin/nagent:775` as a directive to the model, not implemented as machinery — it is the same "direction before machinery" pattern v2.3 used for compaction. A follow-up track could lift it to a driver.
|
||||
|
||||
**Pattern deep-dive.** The safety-net is a four-piece composition: **trigger**, **writer**, **rebuild**, **provenance**. The trigger is wall-clock + burst guard, both computed from data on disk (`bin/nagent:1519-1539` — `checkpoint_due`); the writer is a separate one-call LLM call (`bin/nagent:1547-1587` — `write_checkpoint`); the rebuild is a deterministic string assembly that runs the writer synchronously first (`bin/nagent:1590-1662` — `rebuild_conversation`); the provenance is the deterministic header (`updated:`, `conversation_chars:`) that lets the writer find the delta on the next pass. The cadence reasoning is explicit: "time and context consumption are uncorrelated in exactly the wrong direction" (`issues/0004-conversation-safety-net.md:30`). Token-percentage triggers were "an approximation of an approximation" — three numbers in units `ls -l` can verify are the data-grounded alternative.
|
||||
|
||||
The "sync checkpoint first" invariant is the load-bearing one. A naive rebuild that trusted the most-recent checkpoint's freshness would fail on the exact conversation the safety net is meant to save (a conversation that grew past `rebuild_at_kb` between scheduled checkpoints). The rebuild runs the writer synchronously, and on writer failure widens the tail 4× (`bin/nagent:1610-1612`) — the rebuild is "blockable by a provider outage" would be the wrong failure mode. Failure as data, not failure as control flow.
|
||||
|
||||
The instant-saves change (`6426a67`) is a smaller, sharper version of the same idea: the cost of an LLM summary is moved from the hot path (every save) to the maintenance path (`nagent-distill --apply` backfill + `--summarize-conversation` on demand). The summary is the artifact's own data — the checkpoint's `## Intent` line, already paid for — or the first user prompt truncated. The `summary_source: extracted | llm` provenance in the index is what makes this safe: the user can see which entries have been upgraded and which are still extracted, and the backfill pass reports its cost in the dry-run summary.
|
||||
|
||||
A code-shape sketch using survey grammar (per the format commitment §5.1):
|
||||
|
||||
```
|
||||
safety_settings := { checkpoint_interval_minutes: int,
|
||||
checkpoint_max_new_kb: int,
|
||||
rebuild_at_kb: int }
|
||||
checkpoint := { updated: timestamp, conversation_chars: int,
|
||||
body: ## Intent | ## Next action | ## Constraints | ... }
|
||||
|
||||
due { meta, conversation_chars, now, settings } {
|
||||
if elapsed > interval and chars grew -> fire {ssdl} [I]
|
||||
if chars grew > max_new -> fire
|
||||
if meta is nil and chars > max_new -> fire first time only
|
||||
else -> idle
|
||||
}
|
||||
|
||||
rebuild { conversation, llm, now } {
|
||||
try write_checkpoint(conversation, llm)
|
||||
recover widen tail * 4
|
||||
archive(conversation)
|
||||
write initial_context + {checkpoint} + tail {ssdl} [S]
|
||||
reset checkpoint.conversation_chars = fresh_window_size
|
||||
}
|
||||
```
|
||||
|
||||
The `{ssdl}` markers note the two transformations: checkpoint write is an `[I]` (inspectable, the writer's output is user-editable), and rebuild is an `[S]` (string concatenation — no LLM call beyond the synchronous checkpoint; the deterministic assembly is what makes the rebuild safe to reason about).
|
||||
|
||||
## §3 Hooks
|
||||
|
||||
|
||||
Reference in New Issue
Block a user