github →

New request

#2097: Follow-up: turn-journal hardening — double-terminal detection + multi-process append safety

We'll provision a sandbox, run an agent against the issue, and open a draft PR. You can pull the branch and iterate from there.

Issue
enhancement

Background

Two cleanup items from the v0.51.46 / Release V Opus advisor review. Filing as a single batch so they can be picked up by a contributor (or rolled into the next contributor PR that touches these areas).

1. Turn-journal completed × interrupted lifecycle double-write

PR #2062 places completed inside an outer try block and interrupted inside the outer except. Mutually exclusive in the normal case. Edge case: if anything between the completed event write and the start of the outer except raises an unhandled exception (e.g. a logger crash, an OOM in the middle of state cleanup), both events fire for the same turn.

Currently derive_turn_journal_states resolves the collision by latest created_at — data is non-crashing but lossy.

Fix shape

Add a terminal: bool field on completed and interrupted events. Have the audit detect double-terminal turns explicitly (e.g. report turn_journal_double_terminal for the audit consumer) rather than silently collapsing.

~10-15 LOC + 1 regression test.

2. Multi-process append safety for long messages

api/turn_journal.py uses O_CREAT|O_APPEND|O_WRONLY + single write() per event. POSIX guarantees atomic appends only up to PIPE_BUF (4096 bytes on Linux). The submitted event line contains the full user message, so a >4KB paste crosses the atomicity boundary.

For the current single-process deployment this is fine. If a future deployment runs multiple webui workers on the same state dir (e.g. behind a load balancer with sticky sessions, or a multi-worker uvicorn), large submitted events from different workers could interleave at the byte level — corrupting the JSONL.

Fix shape

Two options:

(a) Per-process journal files. Append pid-{os.getpid()} to the journal filename, and iter_turn_journal_session_ids globs across the variants. Simple, zero coordination needed.

(b) flock-based writer. fcntl.flock(LOCK_EX) around the write block. Slightly slower (lock contention), but keeps the on-disk filename stable.

(a) is simpler. (b) is more standard.

Either is ~20-30 LOC + a multi-process regression test (spawn N writer subprocesses, hammer the same session, verify no interleaved lines).

Why both items together

Both are forward-looking — neither is a current-deployment bug. Filing as a single hardening ticket for whichever contributor wants to harden the turn-journal subsystem for multi-process or robustness scenarios.

References

  • PR #2059 / #2062 (turn-journal stack)
  • Release V notes (v0.51.46)
  • Opus advisor stage-339 review (verbatim from items 3 and 4)
Assessmentadvisory
feature easy85% confidence

Localized turn-journal hardening adding explicit double-terminal detection and append-safety guards.

Likely files
  • api/turn_journal.py
  • tests/test_turn_journal.py
Create the request

This opens a fresh agent run and a draft PR for issue #2097.

Cancel