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
cleared and the queued message drives the next turn normally.
**Mtime-snapshot resumption**: `wait_for_login`
snapshots the `~/.claude/` dir (newest file mtime + file count)
at entry and only resumes when that snapshot advances — not
just when credentials exist on disk. This prevents a silent
infinite-401 loop: stale credentials already on disk at the
time of the 401 no longer cause an immediate false-resume. The
`DirSnapshot` struct tracks both axes; either a mtime advance
OR a file-count change triggers resume (the count axis handles
filesystems where `modified()` errors on every file).
snapshots the `~/.claude/` dir (newest mtime + count over the
credential files in `login::CRED_FILE_NAMES`) at entry and only
resumes when that snapshot advances — not just when credentials
exist on disk. This prevents a silent infinite-401 loop: stale
credentials already on disk at the time of the 401 no longer cause
an immediate false-resume. The `DirSnapshot` struct tracks both
axes; either a mtime advance OR a file-count change triggers resume
(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
tight loops on transient failures.
@ -108,14 +112,16 @@ and the manager fall through to operator).
### 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 |
| --- | --- |
| `Ok` / `Compacted` | `ack_turn` |
| `RateLimited` | sleep `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), requeue inflight, status back to `online` |
| `AuthFailed` | emit `needs_login_idle` sentinel, requeue inflight, park in `wait_for_login` |
| `Failed(err)` | route `[system] \`<qualified-label>\` claude turn failed:\n<err>` to `<parent>` via `send_to_parent` |
| `Ok(_)` (`false` normal / `true` compacted) | `ack_turn` |
| `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 |
| `Err(RateLimited)` | sleep `HIVE_RATE_LIMIT_SLEEP_SECS` (default 300), requeue inflight, status back to `online` |
| `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
`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
*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
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
size crossed the policy watermark. While the session is still healthy it
runs one synthetic *notes-checkpoint* turn (`CHECKPOINT_PROMPT`