docs(turn-loop): document PromptTooLong archive+requeue, TurnOutcome=Result, credential-file session detection

This commit is contained in:
müde 2026-07-05 22:27:09 +02:00
commit dd7f8c5ebb

View file

@ -43,14 +43,18 @@ Each agent harness (`hive serve` — one binary for all agents) runs:
the per-agent web UI login flow; on success the sentinel is the per-agent web UI login flow; on success the sentinel is
cleared and the queued message drives the next turn normally. cleared and the queued message drives the next turn normally.
**Mtime-snapshot resumption**: `wait_for_login` **Mtime-snapshot resumption**: `wait_for_login`
snapshots the `~/.claude/` dir (newest file mtime + file count) snapshots the `~/.claude/` dir (newest mtime + count over the
at entry and only resumes when that snapshot advances — not credential files in `login::CRED_FILE_NAMES`) at entry and only
just when credentials exist on disk. This prevents a silent resumes when that snapshot advances — not just when credentials
infinite-401 loop: stale credentials already on disk at the exist on disk. This prevents a silent infinite-401 loop: stale
time of the 401 no longer cause an immediate false-resume. The credentials already on disk at the time of the 401 no longer cause
`DirSnapshot` struct tracks both axes; either a mtime advance an immediate false-resume. The `DirSnapshot` struct tracks both
OR a file-count change triggers resume (the count axis handles axes; either a mtime advance OR a file-count change triggers resume
filesystems where `modified()` errors on every file). (the count axis handles filesystems where `modified()` errors on
every file). Boot detection (`login::has_session`, deciding Online
vs NeedsLogin) keys off the same `CRED_FILE_NAMES` set — the files
`/logout` deletes — so leftover session-history files don't read as
a live session after a logout + container recreate.
7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid 7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid
tight loops on transient failures. tight loops on transient failures.
@ -108,14 +112,16 @@ and the manager fall through to operator).
### Turn outcomes ### Turn outcomes
`turn::TurnOutcome` drives the post-claude branch: `turn::TurnOutcome` (`Result<bool, TurnError>``Ok(compacted)` on success,
else a `TurnError`) drives the post-claude branch:
| Outcome | Action | | Outcome | Action |
| --- | --- | | --- | --- |
| `Ok` / `Compacted` | `ack_turn` | | `Ok(_)` (`false` normal / `true` compacted) | `ack_turn` |
| `RateLimited` | sleep `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), requeue inflight, status back to `online` | | `Err(PromptTooLong)` | `drive_turn` archived the session (the lib already compacted + retried and it still overflowed); requeue inflight so the message redelivers into a fresh session that fits — no status park |
| `AuthFailed` | emit `needs_login_idle` sentinel, requeue inflight, park in `wait_for_login` | | `Err(RateLimited)` | sleep `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), requeue inflight, status back to `online` |
| `Failed(err)` | route `[system] \`<qualified-label>\` claude turn failed:\n<err>` to `<parent>` via `send_to_parent` | | `Err(AuthFailed)` | emit `needs_login_idle` sentinel, requeue inflight, park in `wait_for_login` |
| `Err(Failed(err))` | route `[system] \`<qualified-label>\` claude turn failed:\n<err>` to `<parent>` via `send_to_parent` |
After the outcome handler, the stats sink records a row and the After the outcome handler, the stats sink records a row and the
`hyperhive-continue` sentinel (dropped by the `request_next_turn` `hyperhive-continue` sentinel (dropped by the `request_next_turn`
@ -233,7 +239,12 @@ window with two triggers baked into its `run`:
- **Reactive** — claude-code prints `Prompt is too long`. The session is - **Reactive** — claude-code prints `Prompt is too long`. The session is
*already* past the window, so no turn can run on it — the session *already* past the window, so no turn can run on it — the session
`/compact`s straight away and retries the same wake-up prompt once. No `/compact`s straight away and retries the same wake-up prompt once. No
notes-checkpoint turn is possible here: the detail is gone. notes-checkpoint turn is possible here: the detail is gone. If the retry
*still* overflows, `run` surfaces `Error::PromptTooLong`; `drive_turn`
then archives the session (session lifecycle stays hive-side) and the
serve loop requeues the message so it redelivers into a fresh session
(see [Turn outcomes](#turn-outcomes) — the wake prompt itself is tiny, so
the overflow was the accumulated context the archive clears).
- **Proactive** — a turn finishes cleanly but the last inference's context - **Proactive** — a turn finishes cleanly but the last inference's context
size crossed the policy watermark. While the session is still healthy it size crossed the policy watermark. While the session is still healthy it
runs one synthetic *notes-checkpoint* turn (`CHECKPOINT_PROMPT` runs one synthetic *notes-checkpoint* turn (`CHECKPOINT_PROMPT`