ADR 0001: a session is a stream — total model, archive, structured input, turn-less Session #53
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/claude-code-query!53
Loading…
Reference in a new issue
No description provided.
Delete branch "wave-2-session-stream"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The full implementation of ADR 0001 (docs/adr/0001-session-is-a-stream-not-turns.md) and the session-contract proposal, as one consolidated PR: 29 commits, four waves, each implemented under TDD with per-wave multi-agent review and adversarial verification. Supersedes the stacked PRs #51 and #52.
Waves (in landing order)
Docstring hazard fix — the ADR's "goes first, today": all three
Session.streamscaladocs now state the real contract (shared long-lived queue, out-of-turn emission, mid-turn merge, single-consumer constraint) instead of the turn-shaped promise that cost a consumer a week.Wave 1 — total message model (
core, all modules)parseMessageis total: unknown wire types survive asUnknownMessage(type, json)—rate_limit_eventand the system subtypes stop vanishing.MessageId(vendoruuid),origin: Option[ResultOrigin]with key-absent ≠ null (the in-turn/out-of-turn discriminator), full result envelope (stopReason,terminalReason,permissionDenialsas raw JSON,apiErrorStatus, timings), realTokenUsage(Longcounts, one shared decoder, moved tocore.model),parentToolUseId+modelon assistant messages, control-protocol types.Wave 4 —
ConversationArchive(custody, not attribution)Tagless port in
core/log+ ZIO adapter. PureMirrorPlanner(copy/extend/recopy plan as data) andArchivePaths(locate by encoding the known cwd — never decoding, the encoding is lossy). Mirrors the whole session tree (main jsonl + subagents + meta). Append-extension only after byte-prefix verification; per-file failures recorded, never a half-aborted mirror. Per the ADR's rejected alternatives: no result journal, no attributed store.Wave 3 — structured
UserInputUserInput(text, context, channel)encoded as content blocks; verbatim user text is identified by structural position, never by parsing its content, so the round-trip lawdecode(encode(i)) == Some(i)holds against adversarial inputs (property-tested, including</interactive>and convention-mimicking text). Proven end-to-end against the real CLI: the transcript decodes back to the exact original.Wave 2 — the turn-less ZIO
Sessionstream,send(String)and the"pending"sentinel are deleted (breaking, 0.5.0). The new surface: fire-and-forgetsend(UserInput); completion as readable state — pureSessionState.foldbehind aRef, monotoneresultsSeen/notificationsSeen(origin-absent vs origin-present),awaitResultAfterwith the no-window guarantee and death-race;events/stateChangesas bounded-sliding Hub fan-out (capacity is a stated contract parameter); a real control-protocolinterruptwith vendorrequest_idcorrelation returningInterruptOutcome(stillQueued);inforeplacing the sentinel (blocks until the CLI names the session, fails on death, interruptible — preserving the 0.4.1 init-hang fix); optional best-effort archive mirroring off the reader's critical path.direct/effectfulsessions untouched per the ZIO-first decision; the one-shotquerypath keeps its honest turn framing (ADR decision 2).Verification
Every wave:
./mill __.compilewarning-free, full unit suites, full integration suites including live-CLI E2Es (structured-input round trip; archive locate→read→mirror on a real transcript tree; probe #5 as a permanent regression test — a real interrupt mid-turn yields an error result and the session survives a follow-up turn). Final state:__.testand__.itestfully green.Each wave also went through a four-dimension review (spec fidelity, correctness/concurrency, architecture/FCIS, test quality) with adversarial verification of every finding; 17 confirmed findings were fixed across the waves, including a real send-path deadlock against the live CLI and a
stateChangeshub that never completed on session end.🤖 Generated with Claude Code
Code review gate (iterative-works review-code): PASSED after fixes
Nine specialized reviewers (architecture, ZIO, Scala 3, composition, style, testing, security, error handling, port/adapter) over the full diff: 1 critical, 15 warnings, 18 suggestions → all critical+warnings fixed, plus 5 suggestions and two rulings from Michal (documented FS-in-unit-tests exception; opaque
SessionId/RequestIdnow). Full report + resolution:project-management/reviews/review-pr53-2026-07-15.md.Highlights:
sessionIdinto archive paths — allow-list validation centralized inArchivePaths, fuzzed with bypass attempts (encodings, unicode lookalikes, backslashes) by an independent re-review, which also caught that the first symlink fix missed two vectors (symlinked main transcript; symlinked tree root —os.walkdereferences a symlinked root regardless offollowLinks). Both closed with regression tests.ReaderContextrefactor (10→3 constructor params).SessionId/RequestIdacross the model (40 files).Final gates:
__.compilewarning-free,__.test526/526,__.itest730/730 (incl. real-CLI E2Es: structured-input round-trip, archive custody flow, real mid-turn interrupt with session survival).🤖 Generated with Claude Code