From 7cfaa82c9b68e9261094f33d5c958254e6640f96 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:26:54 +0300
Subject: [PATCH 01/12] docs(book): add front matter and system maps
---
src/content/book/00-cover.mdx | 38 ++++++
src/content/book/01-how-to-read.mdx | 70 +++++++++++
src/content/book/02-contents.mdx | 97 ++++++++++++++
src/content/book/03-architecture-map.mdx | 122 ++++++++++++++++++
src/content/book/04-request-trace.mdx | 154 +++++++++++++++++++++++
5 files changed, 481 insertions(+)
create mode 100644 src/content/book/00-cover.mdx
create mode 100644 src/content/book/01-how-to-read.mdx
create mode 100644 src/content/book/02-contents.mdx
create mode 100644 src/content/book/03-architecture-map.mdx
create mode 100644 src/content/book/04-request-trace.mdx
diff --git a/src/content/book/00-cover.mdx b/src/content/book/00-cover.mdx
new file mode 100644
index 0000000..f432c60
--- /dev/null
+++ b/src/content/book/00-cover.mdx
@@ -0,0 +1,38 @@
+---
+slug: cover
+order: 0
+kind: front
+part: Start here
+partOrder: 0
+title: T3 Code Decoded
+shortTitle: Cover
+summary: A source-grounded, end-to-end field guide to T3 Code's control plane, provider adapters, clients, remote access, and distribution system.
+status: source-checked
+gates: [sources]
+objectives: []
+keywords: [T3 Code, architecture, agent harness, control plane]
+sourceAreas: [apps/server, apps/web, apps/desktop, apps/mobile, packages/contracts]
+visuals: [book cover]
+updatedAt: 2026-08-24
+---
+
+import BookCover from "../../components/BookCover.astro";
+import Callout from "../../components/Callout.astro";
+
+
+
+
+ This edition is locked to [`pingdotgg/t3code@fa219001d`](https://github.com/pingdotgg/t3code/tree/fa219001dc2f14cfd9c7774c2c03c153359144be). “Shipped,” “supported,” and “implemented” always mean that revision unless a dated release or roadmap note says otherwise.
+
+
+## What this book is trying to make obvious
+
+T3 Code is easy to mistake for an agent. It is more useful—and more technically
+interesting—to see it as a **control surface and execution boundary around several
+independent agent runtimes**. Providers own their model loops and native context.
+T3 owns durable orchestration history and query projections, coordinates workspace
+operations, and gives clients shared semantics with deliberate platform-specific
+capabilities. Some delivery and side-effect machinery remains intentionally
+ephemeral; the book marks those seams instead of calling all server state durable.
+
+That division of ownership is the spine of the book. Every later module connects back to it.
diff --git a/src/content/book/01-how-to-read.mdx b/src/content/book/01-how-to-read.mdx
new file mode 100644
index 0000000..be1994b
--- /dev/null
+++ b/src/content/book/01-how-to-read.mdx
@@ -0,0 +1,70 @@
+---
+slug: how-to-read
+order: 1
+kind: front
+part: Start here
+partOrder: 0
+title: How to read a changing system
+shortTitle: Reading guide
+summary: The evidence rules, source labels, revision contract, and reading paths used throughout the book.
+status: source-checked
+gates: [sources]
+objectives:
+ - Distinguish verbatim source, abridged source, diagrams, and interpretation.
+ - Choose a linear, feature-first, or implementation-first reading path.
+ - Understand what the pinned revision does and does not promise.
+keywords: [evidence, source references, revision, reading path]
+sourceAreas: [sources/t3code.lock.json, sources/excerpts.manifest.json]
+visuals: [evidence legend]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceSnapshot from "../../components/SourceSnapshot.astro";
+
+This is a field guide to a moving codebase. It therefore treats provenance as part of the interface, not a footnote.
+
+
+
+## Four evidence labels
+
+1. **Verified behavior** is backed by executable code, a test, schema, migration, or workflow at the pinned revision.
+2. **Documented intent** reports what a maintainer document says without silently upgrading prose into runtime behavior.
+3. **Inference** names a design consequence derived from cited evidence. The sources prove the inputs; the book owns the interpretation.
+4. **Future / proposed** is explicitly unshipped. A builder capability, community request, or future-work paragraph is never called a supported feature.
+
+Exact source cards are a separate presentation device. Their code is read directly
+from the locked Git object—not the possibly dirty working tree—and shows real line
+numbers, a visible checksum prefix, and an immutable GitHub permalink.
+
+
+
+
+ A stable source lock makes an explanation reproducible. It does not make it permanently current; later editions must deliberately refresh and review the evidence.
+
+
+## Three reading paths
+
+### Linear: follow one task
+
+Start with **The request trace**, then read the numbered chapters in order. This follows a user action from client state through authenticated RPC, commands, events, provider I/O, checkpoints, and back to every UI.
+
+### Architecture-first: map then zoom
+
+Open **The complete system map**, choose a layer, and follow its cross-links. This is useful when you already know event sourcing, provider protocols, or Electron/React Native and want the unfamiliar seams.
+
+### Transfer-first: design another orchestrator
+
+Read the ownership model, adapter boundary, domain kernel, connection runtime, and final transfer guide. Those chapters separate reusable patterns from choices that only make sense for T3 Code's bring-your-own-subscription model.
+
+
+ The live repository moves quickly. A source link with `fa219001d` is intentionally historical and stable. A link to `main` may be newer, but it cannot prove what this edition read. The book prefers reproducibility over silently chasing HEAD.
+
+
+## Status words
+
+- **Draft**: structured and cited, but still awaiting a second source pass.
+- **Source checked**: claims and diagrams have been reconciled to the pinned checkout.
+- **Verified**: source checked, link checked, and reviewed in the end-to-end narrative.
diff --git a/src/content/book/02-contents.mdx b/src/content/book/02-contents.mdx
new file mode 100644
index 0000000..e56a7a8
--- /dev/null
+++ b/src/content/book/02-contents.mdx
@@ -0,0 +1,97 @@
+---
+slug: contents
+order: 2
+kind: front
+part: Start here
+partOrder: 0
+title: Contents and learning route
+shortTitle: Contents
+summary: "The complete ordered reading route: establish ownership, then trace the durable control plane, adapters, work lifecycle, clients, remote paths, shipping system, and architectural synthesis."
+status: source-checked
+gates: [sources, links, editorial]
+objectives:
+ - See why the order follows runtime causality instead of repository folders.
+ - Understand the scope of every completed part and its visual explanations.
+keywords: [contents, chapters, roadmap, learning path]
+sourceAreas: [BOOK_PLAN.md]
+visuals: [chapter roadmap]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+
+The old book introduced several keystone concepts long after depending on them. This edition orders the material by **causal distance from one user request**. The repository atlas remains available as reference, but it does not dictate the story.
+
+## Start here
+
+- **How to read a changing system** — evidence, revision, and reading paths.
+- **The complete system map** — the same architecture in spatial form.
+- **One request, every boundary** — a step-through trace before the zoomed chapters.
+
+## Part I · Boundaries and vocabulary
+
+1. Control surface, not agent brain
+2. Environment · project · thread · turn · session
+3. Repository and dependency atlas
+4. Runtime topologies and technology choices
+
+## Part II · Boot and connect
+
+5. The `npx t3` bootstrap path
+6. Server composition and execution boundary
+7. Effect RPC, subscriptions, and wire contracts
+8. Pairing, scopes, credentials, and WebSocket upgrade
+
+## Part III · The durable domain kernel
+
+9. Commands and invariants
+10. Events, receipts, and idempotency
+11. Projection tables and read models
+12. Post-commit reactors: serialized handling without durable delivery
+13. SQLite, files, settings, secrets, migrations, and recovery
+
+## Part IV · Five harnesses, one product model
+
+14. The `ProviderAdapter` contract
+15. Drivers, instances, registries, and multi-instance routing
+16. Codex through app-server JSON-RPC
+17. Claude through the Agent SDK
+18. Cursor and Grok through ACP
+19. OpenCode and the normalization matrix
+20. Two usage systems: live context telemetry and transcript accounting
+
+## Part V · The work lifecycle
+
+21. Project discovery and `t3.json`
+22. Current checkout vs isolated worktrees
+23. Start, stream, steer, interrupt, settle
+24. Permission modes, approvals, and structured input
+25. Threads, provider tasks, plans, skills, and subagents
+26. Who owns context, compaction, and memory
+27. Hidden-ref checkpoints, diffs, and revert
+28. Terminals, files, previews, MCP, VCS, and pull requests
+
+## Part VI · Client architectures: shared semantics, platform edges
+
+29. The shared client runtime
+30. Web routes, state, and rendering performance
+31. Composer, work log, review, and sidebar lifecycle
+32. Desktop: Electron, IPC, server ownership, browser, SSH
+33. Mobile: React Native, persistence, outbox, sharing, notifications
+
+## Part VII · Reach and ship
+
+34. Primary, paired bearer, Tailscale endpoints, and SSH access
+35. T3 Connect: OAuth, DPoP, relay, and tunnel
+36. Reconnect, multi-environment state, notifications, version skew
+37. Distribution artifacts: CLI, hosted app, desktop, mobile, marketing, and AUR
+38. Release graph, three update systems, and observability/privacy
+
+## Part VIII · Synthesis
+
+39. Six complete traces: ownership, convergence, and failure boundaries
+40. Decisions, trade-offs, limitations, and an honest roadmap
+
+
+ `BOOK_PLAN.md` is the authoring contract for this repository. Product roadmap claims inside the book use a stricter split: shipped code, explicit maintainer future work, and community ideas are three different categories.
+
diff --git a/src/content/book/03-architecture-map.mdx b/src/content/book/03-architecture-map.mdx
new file mode 100644
index 0000000..0d7aee9
--- /dev/null
+++ b/src/content/book/03-architecture-map.mdx
@@ -0,0 +1,122 @@
+---
+slug: architecture-map
+order: 3
+kind: front
+part: Start here
+partOrder: 0
+title: The complete system map
+shortTitle: System map
+summary: A spatial map that separates client surfaces, transport, transactional state, hot delivery, side effects, provider routing, and external execution.
+status: source-checked
+gates: [sources]
+objectives:
+ - Locate every major package at its runtime boundary.
+ - See which edges carry commands, events, provider-native protocol, or filesystem effects.
+keywords: [architecture map, components, packages, boundaries]
+sourceAreas: [apps/server, packages/contracts, packages/client-runtime, apps/web, apps/desktop, apps/mobile, apps/marketing, infra/relay]
+visuals: [interactive layer map, ownership table]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import LayerMap from "../../components/LayerMap.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceList from "../../components/SourceList.astro";
+
+For a selected environment, authoritative repository and provider execution occurs
+behind that environment's server boundary. A client can supervise several such
+environments, while Electron and mobile also own device-local storage, navigation,
+notifications, preview, update, and operating-system integrations.
+
+
+
+
+
+## Two return paths, one write path
+
+
+ |typed command| W[WebSocket RPC + scope]
+ W --> Q[Serialized in-memory queue]
+ Q --> D[Decision: invariants + clock/UUID Effects]
+ D -->|then enter SQL transaction| S[(Events + SQL projections + accepted receipt)]
+ S -.->|post-commit publish| B[Hot event PubSub]
+ B -.->|best effort; not replayed| R[Provider command reactor]
+ R --> V[ProviderService + instance registry]
+ V --> A[Provider adapter]
+ A --> P[Codex · Claude · Cursor · Grok · OpenCode]
+ P -->|native notifications| A
+ A -->|canonical runtime events| I[Runtime ingestion]
+ I -->|internal commands| Q
+ B -.->|committed live events| L[WebSocket subscription]
+ L --> C
+ S --> H[Authenticated HTTP snapshot query]
+ H --> C`} />
+
+
+The downward path is imperative: “start this turn,” “accept this approval,” “revert
+this checkpoint.” The upward path is descriptive: assistant deltas, activities,
+plans, usage, session state, and changed-file summaries. The adapter is where each
+harness's vocabulary becomes the canonical provider-runtime vocabulary.
+
+
+ Event append, SQL projection updates, and an accepted command receipt are atomic. Publication to reactors happens afterwards through memory. A committed intent can therefore survive while its provider or filesystem side effect is lost across a crash.
+
+
+## Three UI families, four execution clients, five delivery shapes
+
+`apps/web` serves three roles: the browser UI copied into the npm CLI, the hosted
+product UI, and the Electron renderer. Electron and React Native add very different
+native systems around shared connection/state semantics. The marketing Astro site
+is the fifth delivery shape, but it is a publication surface—not an execution
+client.
+
+
+
+
Cross-surface capability comparison for local web, hosted web, Electron, iOS, and Android
+
Capability
Local web
Hosted web
Electron
iOS
Android
+
+
Environment server
CLI-owned
remote
managed local/WSL
remote
remote
+
Terminal renderer
Ghostty WASM
Ghostty WASM
Ghostty WASM
native Ghostty
native Ghostty VT
+
Offline new-task intent
—
—
—
durable outbox
durable outbox
+
Environment provisioning
primary/pair
pair/relay
primary, WSL, SSH
pair/relay
pair/relay
+
Distinctive native edge
local browser
cloud identity
preview, updates, OS IPC
APNs, Live Activities, share
share, widgets
+
+
+
+
+
+ A phone does not run Codex and the hosted web app does not proxy repository traffic. Each selected environment keeps provider processes, workspace files, Git, PTYs, and authoritative orchestration state behind its own server. A client may merge several environments, and its native shell still owns device-local concerns.
+
+
+## Source trail
+
+
diff --git a/src/content/book/04-request-trace.mdx b/src/content/book/04-request-trace.mdx
new file mode 100644
index 0000000..5d71bd2
--- /dev/null
+++ b/src/content/book/04-request-trace.mdx
@@ -0,0 +1,154 @@
+---
+slug: request-trace
+order: 4
+kind: front
+part: Start here
+partOrder: 0
+title: One request, every boundary
+shortTitle: Request trace
+summary: Step through a user turn from a client command to an ordered SQL commit, provider-native execution, canonical event ingestion, checkpoint diff, and streamed UI state.
+status: source-checked
+gates: [sources]
+objectives:
+ - Build a causal mental model before studying individual modules.
+ - Distinguish the synchronous command acknowledgement from asynchronous provider work.
+ - Locate durability, side effects, streaming, and checkpoint settlement.
+keywords: [turn lifecycle, command, event, provider, checkpoint, stream]
+sourceAreas: [packages/contracts/src/orchestration.ts, apps/server/src/orchestration, apps/server/src/provider]
+visuals: [interactive request stepper, sequence diagram]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import TraceLab from "../../components/TraceLab.astro";
+
+The easiest way to understand T3 Code is to separate **accepting intent** from
+**performing work**. This first trace deliberately follows a turn on an existing
+thread with no new attachments or worktree bootstrap. That keeps one transactional
+write path visible; the multi-step bootstrap branch appears immediately afterwards.
+
+
+
+## The command is deliberately rich
+
+
+
+## The acknowledgement is not the answer
+
+
+
+
+
+
+ A successful dispatch returns the committed event sequence, and its accepted command receipt is internal SQLite state in the same transaction. A rejected command returns an error. Only invariant rejection is recorded, and that write is best effort with persistence failure discarded.
+
+
+
+ `DispatchResult` is the client success value. The orchestration command receipt is internal idempotency state. `RuntimeReceiptBus` exposes short-lived checkpoint milestones to tests and is a no-op broadcaster in production. They solve three different problems.
+
+
+The turn reaches another milestone when its projected provider session is no longer
+`running`. Checkpoint state can arrive independently, and capture may be missing,
+skipped, or erroneous. A single “complete” boolean cannot faithfully represent all
+three phases.
+
+## The bootstrap fork is not one transaction
+
+The richer client command can create a thread, create or select a worktree, run
+setup, and start the first turn. Those are several operations with compensating
+tombstone cleanup on failure. Only the two-to-four-event batch for an already
+existing thread—optional lifecycle resets, then message and turn-start—is decided
+and committed atomically.
+
+
+ Attachment normalization persists decoded bytes before the engine transaction. A command can therefore fail after external filesystem work has begun; Chapter 9 models that branch explicitly.
+
+
+## The full causal loop
+
+
+ >C: send prompt
+ C->>RPC: thread.turn.start
+ RPC->>N: canonicalize external input
+ N->>E: dispatch existing-thread command
+ E->>E: decide command → events
+ E->>DB: events + SQL projections + accepted receipt
+ DB-->>E: commit sequence
+ E-->>RPC: DispatchResult { sequence }
+ RPC-->>C: successful dispatch result
+ E-->>R: committed turn-start-requested
+ R->>V: ensure session + send
+ V->>A: start/resume + sendTurn
+ A->>P: provider-native request
+ P-->>A: native notifications
+ A-->>V: canonical runtime events
+ V-->>I: canonical runtime events
+ I->>E: internal commands
+ E->>DB: commit projected state
+ E-->>RPC: thread event stream
+ RPC-->>C: render messages/activity/session
+ V-->>I: turn.completed
+ V-->>K: turn.completed
+ K->>G: attempt hidden-ref capture + diff
+ K->>E: thread.turn.diff.complete
+ E->>DB: commit checkpoint event + projection
+ E-->>RPC: committed checkpoint event`} />
+
+
+The diagram intentionally keeps the checkpoint worker in the reactor lane, but its
+last write does **not** bypass the domain kernel. Git capture is a side effect; the
+result becomes durable only after a new internal command passes through the engine.
+
+
+ Provider intent is consumed from a hot stream. If the server exits after the SQL commit but before the reactor handles that event, restart does not replay the pending provider send. Retrying the same command finds the accepted receipt and does not regenerate the event.
+
+
+
From d5487ab1ab9b1bc7753185477c04f33c9e4631b1 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:26:58 +0300
Subject: [PATCH 02/12] docs(book): publish part one boundaries
---
src/content/book/10-control-surface.mdx | 106 ++++++++++++++++++
src/content/book/20-domain-vocabulary.mdx | 117 ++++++++++++++++++++
src/content/book/30-repository-atlas.mdx | 118 +++++++++++++++++++++
src/content/book/40-runtime-topologies.mdx | 110 +++++++++++++++++++
4 files changed, 451 insertions(+)
create mode 100644 src/content/book/10-control-surface.mdx
create mode 100644 src/content/book/20-domain-vocabulary.mdx
create mode 100644 src/content/book/30-repository-atlas.mdx
create mode 100644 src/content/book/40-runtime-topologies.mdx
diff --git a/src/content/book/10-control-surface.mdx b/src/content/book/10-control-surface.mdx
new file mode 100644
index 0000000..030f93e
--- /dev/null
+++ b/src/content/book/10-control-surface.mdx
@@ -0,0 +1,106 @@
+---
+slug: control-surface
+order: 10
+number: "1"
+kind: chapter
+part: Part I · Boundaries and vocabulary
+partOrder: 1
+title: Control surface, not agent brain
+shortTitle: The ownership boundary
+summary: For each environment, T3 Code authoritatively coordinates repository and provider execution without replacing a provider's model loop, native context, or authentication.
+status: source-checked
+gates: [sources]
+objectives:
+ - Draw the ownership boundary between client, T3 server, provider adapter, and provider CLI.
+ - Explain why multi-harness support is an adapter problem rather than a shared agent implementation.
+ - Identify the server services that make T3 Code more than a terminal wrapper.
+keywords: [control surface, harness, ownership, server, provider adapter]
+sourceAreas: [docs/internals/overview.md, apps/server/src/server.ts, apps/server/src/provider/Services/ProviderAdapter.ts]
+visuals: [ownership matrix, server capability assembly]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import DecisionGrid from "../../components/DecisionGrid.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+The product's cleanest architectural sentence is: **each T3 environment is a
+server-authoritative control plane around replaceable coding-agent runtimes.**
+
+Clients are thin with respect to trusted workspace execution—not small programs.
+They express intent and render projected state, while Electron and mobile still own
+substantial native behavior. For the selected environment, the server owns
+repository-facing orchestration. A provider runtime owns its model/tool loop. The
+adapter and `ProviderService` divide live protocol behavior from persisted session
+binding and recovery state.
+
+
+
+## Six ownership layers
+
+
+
+
Ownership of durable, runtime, provider-native, client-local, and workspace state
T3's multi-environment UI, checkpoint model, or product lifecycle
+
Client shell
navigation, presentation, caches, device settings; mobile drafts and durable outbox; Electron OS integrations
authoritative environment thread history or remote workspace execution
+
Selected environment
workspace files, provider processes, Git, PTYs, server SQLite/files/settings/secrets
other environments supervised by the same client
+
+
+
+
+
+ “The server is authoritative” applies per environment. The shared client registry can keep several environment descriptors and merge their projected work into one product surface.
+
+
+## A normalized, deliberately bounded seam
+
+
+
+
+
+The contract is not a promise of feature equality. Its lifecycle, interaction,
+history, rollback, feedback, and event-stream surface is deliberately bounded, but
+not tiny. At this revision, the separate common capability object only describes
+session model switching; other semantic differences
+are expressed by adapter behavior, errors, and provider presentation. Uniform
+orchestration is achieved by **normalizing at the boundary**, not by pretending the
+providers are identical.
+
+
+ Capability discovery currently advertises only in-session model switching. The actual adapter seam also covers session lifecycle, turns, approvals, structured input, history, rollback, feedback, shutdown, and canonical events.
+
+
+## Why the server is the real product kernel
+
+
+
+
+
+
+ In this book, **agent** is a provider-owned coding runtime or subagent, **provider adapter** is its T3 integration, **client** is a T3 product surface, and **environment** is one running T3 server plus the machine/state it exposes. Calling all four “the agent” hides the architecture.
+
+
+## Source trail
+
+
diff --git a/src/content/book/20-domain-vocabulary.mdx b/src/content/book/20-domain-vocabulary.mdx
new file mode 100644
index 0000000..2864676
--- /dev/null
+++ b/src/content/book/20-domain-vocabulary.mdx
@@ -0,0 +1,117 @@
+---
+slug: domain-vocabulary
+order: 20
+number: "2"
+kind: chapter
+part: Part I · Boundaries and vocabulary
+partOrder: 1
+title: Environment, project, thread, turn, and session
+shortTitle: Domain vocabulary
+summary: The entities share a UI, but they have different identity, cardinality, ownership, and restart behavior.
+status: source-checked
+gates: [sources]
+objectives:
+ - Distinguish product work items from provider and transport sessions.
+ - Understand which entity owns workspace, provider selection, history, and lifecycle.
+ - Separate durable thread state from ephemeral liveness and live plan progress.
+keywords: [environment, project, thread, turn, session, entity model]
+sourceAreas: [packages/contracts/src/environment.ts, packages/contracts/src/orchestration.ts, packages/contracts/src/providerRuntime.ts]
+visuals: [entity relationship table, lifecycle scenario lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import ScenarioLab from "../../components/ScenarioLab.astro";
+import SourceList from "../../components/SourceList.astro";
+
+T3 Code's screens place projects, threads, turns, and provider sessions close
+together, but none are synonyms. The clean model starts with an **environment**:
+one running T3 server plus the machine and capabilities it exposes. A client can
+hold several environment descriptors and merge their work into one view.
+
+## The entity ladder
+
+
+
+
Identity, parent, durable contents, and lifecycle for the main T3 Code entities
+
Entity
Parent / cardinality
What it carries
Important boundary
+
+
Environment
client catalog → many
server identity, label, platform, version, capabilities, authenticated endpoint
one authority for the execution context it exposes; one physical host may expose several
transport lifetime; reconnect replaces it without replacing the thread
+
+
+
+
+The thread is where product concepts converge. It identifies a project and
+workspace, remembers the chosen provider instance and modes, and projects messages,
+activities, proposed plans, checkpoints, and a summary of provider runtime state.
+The provider's live session object remains outside that aggregate.
+
+
+ Background-agent liveness and live plan progress are convenience folds for the current process. They appear beside durable lifecycle and message state but intentionally disappear on restart.
+
+
+## Similar names, different restart behavior
+
+
+
+Startup and lazy recovery are two separate transitions. On boot, projected
+`starting` or `running` sessions that have no live adapter entry are settled as an
+error. A later routed operation may use the persisted binding and continuation
+cursor to create or resume a new adapter session; the old in-memory object itself is
+never resurrected.
+## A vocabulary rule for the rest of the book
+
+When the text says **session**, it qualifies the owner: provider session, RPC
+session, browser auth session, or desktop backend process. When it says **task**, it
+means either the durable product work item—a thread—or explicitly a provider-emitted
+task/subagent activity. This avoids importing one harness's internal vocabulary into
+the cross-provider domain model.
+
+
+ One client can supervise many environments. One environment exposes many projects. One project has many threads. One thread has many turns but only a current provider binding. Reconnect, resume, and restart act on different rungs of that ladder.
+
+
+
diff --git a/src/content/book/30-repository-atlas.mdx b/src/content/book/30-repository-atlas.mdx
new file mode 100644
index 0000000..1ab04e1
--- /dev/null
+++ b/src/content/book/30-repository-atlas.mdx
@@ -0,0 +1,118 @@
+---
+slug: repository-atlas
+order: 30
+number: "3"
+kind: chapter
+part: Part I · Boundaries and vocabulary
+partOrder: 1
+title: Repository and dependency atlas
+shortTitle: Repository atlas
+summary: The monorepo is a product graph—five apps, shared contracts and runtime packages, protocol bridges, remote infrastructure, native modules, and release machinery.
+status: source-checked
+gates: [sources]
+objectives:
+ - Map repository folders to runtime and distribution responsibilities.
+ - Distinguish dependency edges from process and protocol boundaries.
+ - Understand how the source inventory is reproduced from the pinned Git object.
+keywords: [monorepo, packages, apps, dependencies, build graph, inventory]
+sourceAreas: [apps, packages, infra, native, scripts, pnpm-workspace.yaml]
+visuals: [monorepo dependency flow, package atlas, reproducible inventory]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import FlowLab from "../../components/FlowLab.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceList from "../../components/SourceList.astro";
+import SourceSnapshot from "../../components/SourceSnapshot.astro";
+
+The repository is large enough that folder-by-folder reading obscures the design.
+Treat it as a directed product graph: contracts define the language, runtime
+packages implement reusable algorithms, apps choose a process topology, and
+infrastructure/native/build layers complete delivery.
+
+
+
+These counts are not hand-maintained marketing numbers. `source:check` enumerates
+tracked files at the locked Git commit, applies versioned extension and test-path
+rules, and counts lines from Git objects. Assets and the `.repos` reference clones
+are excluded; generated protocol source remains part of the implementation surface.
+
+## The atlas
+
+
+
+## Dependency is not the same as deployment
+
+
+ S[apps/server]
+ C --> R[packages/client-runtime]
+ R --> W[apps/web]
+ R --> M[apps/mobile]
+ C --> D[apps/desktop]
+ W --> S
+ W --> D
+ S --> N[npm t3: server + copied web]
+ S --> D
+ X[effect-acp / codex bridge] --> S
+ H[ssh / tailscale / shared] --> S
+ H --> D
+ I[infra/relay] -.control plane.-> S`} />
+
+
+The arrow from web to server is a build edge: `apps/server` copies `apps/web/dist`
+into its published client directory. At runtime, a browser still communicates with
+the server over authenticated HTTP/WebSocket boundaries. Likewise, Electron imports
+shared code and bundles a server, but its renderer remains isolated behind preload
+IPC.
+
+
+
+
+ Shared schemas and state algorithms reduce semantic drift across products. The cost is coordinated versioning and capability-skew handling; sharing a package does not erase the security or process boundary.
+
+
+
+ The `.repos` directory contains synchronized reference repositories and is not T3 Code production ownership. Generated protocol source inside first-party packages is included because it ships and constrains runtime behavior. The atlas always states which category a number uses.
+
+
+
diff --git a/src/content/book/40-runtime-topologies.mdx b/src/content/book/40-runtime-topologies.mdx
new file mode 100644
index 0000000..b6dba6f
--- /dev/null
+++ b/src/content/book/40-runtime-topologies.mdx
@@ -0,0 +1,110 @@
+---
+slug: runtime-topologies
+order: 40
+number: "4"
+kind: chapter
+part: Part I · Boundaries and vocabulary
+partOrder: 1
+title: Runtime topologies and technology placement
+shortTitle: Runtime topologies
+summary: The same contracts appear in several process graphs; local CLI, hosted web, Electron, and mobile move ownership without moving repository execution off the environment server.
+status: source-checked
+gates: [sources]
+objectives:
+ - Draw the process and trust boundary for every execution client.
+ - Separate product UI families from delivery and publication shapes.
+ - Explain technology placement as verified mechanics or explicitly labeled inference.
+keywords: [topology, Effect, SQLite, React, Electron, Expo, Astro]
+sourceAreas: [apps/server, apps/web, apps/desktop, apps/mobile, apps/marketing]
+visuals: [topology switcher, technology placement matrix]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import ScenarioLab from "../../components/ScenarioLab.astro";
+import SourceList from "../../components/SourceList.astro";
+
+There is no single “T3 Code process.” The invariant is an environment server that
+owns authoritative repository/provider execution. What changes by product shape is
+who launches that server, where the renderer runs, how it reaches the endpoint, and
+which device-local systems surround the shared semantics.
+
+
+## Technology placement, not mythology
+
+
+
+
Where major technologies are used and the observable consequence of their placement
one transactional event/projection/accepted-receipt store plus indexed query models
+
React
shared web renderer
one UI serves local browser, hosted app, and Electron with runtime-specific composition
+
Electron
desktop main/preload
OS/backend/update/preview authority stays outside the sandboxed renderer
+
React Native + Expo
iOS/Android
native persistence, navigation, modules, stores, and OTA use a separate release domain
+
Ghostty
WASM on web; native modules on mobile
terminal semantics are shared conceptually while rendering/input paths remain platform-specific
+
Astro
marketing site
download/publication pages build independently of the React product client
+
+
+
+
+
+ The code proves where each technology is used and the boundaries it creates. Unless an upstream decision record says otherwise, explanations of *why*—for example, minimizing remote authority or preserving native scrolling—are architectural inference, not quoted maintainer intent.
+
+
+## Marketing is the fifth delivery shape, not a fifth client
+
+The independent Astro marketing site discovers stable downloads and links to app
+stores. It does not load `client-runtime`, connect to environments, or render thread
+state. Counting it clarifies release/distribution ownership; placing it in the
+execution topology would confuse publication with control.
+
+
+ Server, contracts, web, and desktop release in a lockstep version domain. Mobile's store version/build number and native fingerprint form another. The same architecture can therefore be compatible at the contract level while its packages show different version strings.
+
+
+
From 74d4bb5b1dd7c568cdb6ea3e4cdac1488cb1edf4 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:02 +0300
Subject: [PATCH 03/12] docs(book): publish part two boot and connect
---
src/content/book/50-cli-bootstrap.mdx | 185 +++++++++++++
src/content/book/60-server-composition.mdx | 185 +++++++++++++
src/content/book/70-rpc-snapshots-resume.mdx | 200 ++++++++++++++
src/content/book/80-auth-pairing.mdx | 267 +++++++++++++++++++
4 files changed, 837 insertions(+)
create mode 100644 src/content/book/50-cli-bootstrap.mdx
create mode 100644 src/content/book/60-server-composition.mdx
create mode 100644 src/content/book/70-rpc-snapshots-resume.mdx
create mode 100644 src/content/book/80-auth-pairing.mdx
diff --git a/src/content/book/50-cli-bootstrap.mdx b/src/content/book/50-cli-bootstrap.mdx
new file mode 100644
index 0000000..2adfad4
--- /dev/null
+++ b/src/content/book/50-cli-bootstrap.mdx
@@ -0,0 +1,185 @@
+---
+slug: cli-bootstrap
+order: 50
+number: "5"
+kind: chapter
+part: Part II · Boot and connect
+partOrder: 2
+title: The `npx t3` bootstrap path
+shortTitle: CLI bootstrap
+summary: The published package resolves commands and configuration, locates its copied web client, and hands one readonly ServerConfig value to the layered runtime.
+status: source-checked
+gates: [sources]
+objectives:
+ - Trace npm's executable entry through command selection and server launch.
+ - Apply the real CLI, environment, desktop-bootstrap, and default precedence rules.
+ - Distinguish bundled JavaScript, copied web assets, and native external dependencies.
+keywords: [npx t3, CLI, ServerConfig, bootstrap, static assets, npm package]
+sourceAreas: [apps/server/src/bin.ts, apps/server/src/cli, apps/server/scripts/cli.ts, apps/server/vite.config.ts]
+visuals: [package exploder, interactive bootstrap stepper, configuration scenario resolver]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import FlowLab from "../../components/FlowLab.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ScenarioLab from "../../components/ScenarioLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+`npx t3` looks like one command, but it crosses three distinct systems: npm
+selects a published executable, the Effect CLI selects a command and resolves
+configuration, and the server runtime acquires the services described in Chapter 6.
+Keeping those stages separate makes several otherwise surprising behaviors obvious.
+
+## What npm actually installs
+
+The package is named `t3`, exposes `dist/bin.mjs` as the `t3` executable, and
+publishes only `dist`. That directory is deliberately more than one JavaScript file.
+
+
+
+
Artifacts inside or beside the published T3 CLI bundle
+
Artifact
How it gets there
Why it remains distinct
+
+
dist/bin.mjs
bundled from src/bin.ts
the npm executable and command graph
+
dist/service-launcher.mjs
second bundle entry
standalone managed-update trial and handoff process
+
dist/client/*
copied from the completed web build
static web application served by the CLI
+
selected node_modules
kept external by policy
native binaries/loaders must exist on the real filesystem
+
+
+
+
+
+ |bundle| CLI[dist/bin.mjs]
+ L[src/service-launcher.ts] -->|bundle| SL[dist/service-launcher.mjs]
+ W[apps/web/dist] -->|copy| UI[dist/client]
+ N[native dependency closure] -->|external| NM[node_modules]
+ CLI --> P[npm package: t3]
+ SL --> P
+ UI --> P
+ NM -.installed beside.-> P`} />
+
+
+
+ A local build warns and continues when `apps/web/dist` is absent. The publish path is stricter: it refuses to publish without the copied client entry and service launcher. At runtime the browser still crosses HTTP and WebSocket boundaries.
+
+
+## One entry, several command meanings
+
+
+
+Bare `t3` and `t3 start` both call `runServerCommand` with normal browser
+presentation. `t3 serve` is not merely an alias for `--no-browser`: it requests
+headless presentation and forces cwd auto-bootstrap off so a service can start
+without inventing project state.
+
+
+
+
+
+## Configuration precedence is field-specific
+
+The common helper selects the first present `Option`, but callers supply different
+subsets of channels and defaults. Mode and port consult CLI, environment, desktop
+bootstrap, and a default; dev URL has CLI/environment inputs; cwd uses CLI/process
+cwd; presentation comes from the selected command. “CLI always wins” is therefore
+directionally useful, not a complete specification.
+
+
+
+
+
+
+ The Electron parent can scan sequentially and supplies its chosen port with other
+ process metadata in the bootstrap envelope. The CLI resolver's own standalone
+ desktop fallback is exactly the default port. Keep those two owners distinct.
+
+
+## State paths and client selection
+
+The resolver derives paths only after the base directory and dev URL are known,
+creates the cwd and server directories, loads persisted observability endpoints, and
+then returns the effective readonly `ServerConfig` value. A dev URL enables a
+loopback redirect and disables eager static lookup; a non-redirected eligible request
+can still resolve built assets lazily. Without a dev URL, lookup eagerly checks the
+compiled-adjacent `client` directory before the monorepo fallback. If neither has an
+index, the catch-all route returns HTTP 503 instead of an application shell.
+
+
+ Startup output constructs `/pair#token=…`. URL fragments are not sent in the HTTP request; the browser client extracts and removes the fragment before exchanging the credential in Chapter 8.
+
+
+
diff --git a/src/content/book/60-server-composition.mdx b/src/content/book/60-server-composition.mdx
new file mode 100644
index 0000000..17c8373
--- /dev/null
+++ b/src/content/book/60-server-composition.mdx
@@ -0,0 +1,185 @@
+---
+slug: server-composition
+order: 60
+number: "6"
+kind: chapter
+part: Part II · Boot and connect
+partOrder: 2
+title: Server composition, activation, and readiness
+shortTitle: Server activation
+summary: Effect layers acquire the server graph, while an explicit activation barrier separates listening, parked roots, command admission, and lifecycle readiness.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Read the server as a scoped layer graph rather than one startup function.
+ - Distinguish an open listener from an activated, command-ready runtime.
+ - Trace startup failure and shutdown ownership without inventing transactional guarantees.
+keywords: [Effect layers, activation, readiness, server startup, SQLite, shutdown]
+sourceAreas: [apps/server/src/server.ts, apps/server/src/serverRuntimeStartup.ts, apps/server/src/serverActivation.ts, apps/server/src/persistence]
+visuals: [layer stack, activation-barrier flow, startup failure explorer]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import FlowLab from "../../components/FlowLab.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ScenarioLab from "../../components/ScenarioLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Chapter 5 ended with one resolved `ServerConfig`. It does not get passed to a
+constructor that imperatively starts everything. `runServer` launches an Effect
+layer whose acquisition builds a graph of platform services, application services,
+routes, long-running roots, and release finalizers. A second mechanism—the
+activation barrier—controls when already-acquired roots may begin external work.
+
+## The server is a scoped graph
+
+
+ P[Bun or Node platform]
+ P --> DB[SQLite + migrations]
+ P --> OS[filesystem · process · PTY · HTTP]
+ DB --> D[persistence · auth · settings]
+ D --> K[orchestration · providers · VCS · checkpoints]
+ OS --> K
+ K --> E[terminal · preview · workspace · cloud · telemetry]
+ E --> R[HTTP API · assets · WebSocket RPC · MCP]
+ R --> H[scoped HTTP server]
+ A{{activation deferred}} -.opens after preparation.-> K
+ A -.opens after preparation.-> E`} />
+
+
+The graph is broad because the environment server is the execution authority from
+Chapter 1. Composition still preserves boundaries: each service exposes a typed
+interface, layers declare requirements, and the launched scope owns acquisition and
+release. `makeServerLayer` is the composition root; `Layer.launch` keeps it alive.
+
+### SQLite is acquired before command traffic
+
+The SQLite layer configures a busy timeout, enables foreign keys, selects WAL, and
+runs a statically ordered migration manifest during acquisition. At this revision
+the manifest contains 41 migrations. That number is a fact about the pinned source,
+not a compatibility promise for later revisions.
+
+
+ The migration map imports and numbers every migration explicitly. Startup does not
+ scan a folder and hope filesystem ordering matches schema history.
+
+
+## Four signals that sound similar but are not
+
+
+
+
Distinct synchronization signals in server startup
+
Signal
What it proves
What it does not prove
+
+
HTTP listening
the platform server has bound an address
commands are accepted or auxiliary roots are active
+
parked / routes ready
each participating root reached its pre-activation wait point
the activation gate has opened
+
command ready
the global route barrier and startup command queue may release waiting effects
every later lifecycle subscriber has observed ready
+
ready event
activation and command admission have occurred and startup publishes its final lifecycle payload
all future optional integrations will remain healthy
+
+
+
+
+The listener can therefore exist while every route effect waits behind global
+middleware; a separate FIFO startup queue also holds WebSocket-dispatched commands.
+That is intentional: routes need a real bound address before presentation and
+remote-access setup can be prepared, but external work must not race ahead of a
+candidate runtime that has not crossed its activation boundary.
+
+
+
+
+
+After `activate`, the parked fibers are released concurrently. Heartbeat,
+browser/headless presentation, optional auto-bootstrap welcome, runtime-state
+persistence, and remote-access work may interleave with command admission and the
+`ready` publication. The flow orders the gate operations; it does not serialize all
+post-activation side effects.
+
+## Why park before activating?
+
+`forkParked` is small but architectural. Its child fiber first fulfills a “parked”
+deferred and then waits on `ServerActivation`. The parent can prove all participating
+roots exist and are waiting before it releases a single shared gate. That prevents
+one early root from acting against a partially prepared graph.
+
+This is synchronization, not a global transaction. Once activation opens, roots can
+perform filesystem, provider, Tailscale, cloud, browser, or other side effects with
+their own failure handling. Effect scopes provide release ownership; they do not
+magically roll back every effect that crossed an external boundary.
+
+
+
+
+ `docs/internals/overview.md` places command readiness before listener readiness and
+ describes a different final order. At the pinned revision, executable code places
+ listener/auxiliary preparation first, then launcher trial, welcome, activation,
+ command readiness, and `ready`. This book follows code and keeps the contradictory
+ documentation as evidence of drift.
+
+
+
+ No single Boolean explains startup. Several deferreds and a lifecycle event encode
+ which consumers may proceed at each boundary. Treating “socket accepted” as “server
+ ready” erases the command gate and makes the stale overview look plausible.
+
+
+
diff --git a/src/content/book/70-rpc-snapshots-resume.mdx b/src/content/book/70-rpc-snapshots-resume.mdx
new file mode 100644
index 0000000..bd1814a
--- /dev/null
+++ b/src/content/book/70-rpc-snapshots-resume.mdx
@@ -0,0 +1,200 @@
+---
+slug: rpc-snapshots-resume
+order: 70
+number: "7"
+kind: chapter
+part: Part II · Boot and connect
+partOrder: 2
+title: HTTP, WebSocket RPC, snapshots, and resume
+shortTitle: RPC and resume
+summary: Typed contracts span HTTP and Effect RPC, while snapshot, bounded catch-up, buffered live delivery, and client watermarks repair connection races.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Separate domain schemas, RPC definitions, transport serialization, and handlers.
+ - Simulate the attach-before-snapshot resume algorithm and its gap cutoff.
+ - Explain which layer owns coalescing, deduplication, and reconnection.
+keywords: [Effect RPC, WebSocket, HTTP, snapshot, cursor, replay, synchronization]
+sourceAreas: [packages/contracts/src/orchestration.ts, packages/contracts/src/rpc.ts, packages/contracts/src/environmentHttp.ts, apps/server/src/ws.ts, packages/client-runtime/src]
+visuals: [contract stack, animated resume interleaver, cursor decision lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import FlowLab from "../../components/FlowLab.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ScenarioLab from "../../components/ScenarioLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+The server is active, but a client still needs two different kinds of truth:
+authoritative state it can load now and a live stream that will not miss what changes
+while that load is happening. T3 Code splits that job across typed HTTP endpoints,
+typed Effect RPC methods, and a deliberately ordered snapshot/catch-up/live protocol.
+
+## Four layers, one contract boundary
+
+
+ H[environment HTTP API]
+ S --> R[Effect RPC definitions]
+ H --> HI[HTTP handlers]
+ R --> WI[WebSocket handlers]
+ WI --> J[Effect RPC JSON protocol]
+ J <--> C[client RPC session]
+ C --> D[session-aware subscriptions]
+ HI <--> L[HTTP snapshot loader]
+ L --> D`} />
+
+
+The schema map does not specify a hand-written WebSocket frame such as
+`{type, requestId, payload}`. Effect RPC owns that protocol envelope and the route
+provides JSON serialization. This book therefore documents typed operations and
+observable stream items, not a fabricated wire format.
+
+
+ HTTP exposes authoritative shell and thread snapshot reads alongside other
+ environment APIs. WebSocket RPC exposes unary commands and long-lived shell/thread
+ subscriptions. The client may seed from HTTP and pass that snapshot sequence into
+ a WebSocket subscription instead of downloading the same state again.
+
+
+The two transports share decoded command schemas, but not every policy step. HTTP
+normalizes and dispatches directly. The WebSocket path additionally owns startup
+queueing, bootstrap compensation, origin attribution, and selected archive/settle
+follow-ups. “Same contract type” therefore does not mean interchangeable handler
+semantics.
+
+## Capabilities make synchronization evolvable
+
+The first RPC session caches `server.getConfig`. Besides environment, providers,
+settings, and observability data, the response advertises whether the connected
+server supports shell and thread completion markers and thread snapshot pagination.
+Clients gate newer behavior on those flags rather than assuming version equality.
+
+One WebSocket session attempt explicitly disables internal transport retries. A
+higher connection supervisor owns replacement sessions, backoff, lifecycle wakeups,
+and target changes; `subscribeDynamic` switches each durable subscription to the
+current session. Keeping retry ownership outside the raw protocol attempt prevents
+two independent retry loops from fighting each other.
+
+## Attach live first
+
+The central race is simple: if the server reads a snapshot and only then subscribes
+to live events, an event committed between those operations appears in neither
+result. T3 Code reverses the first two actions: subscribe into a scope-bound buffer,
+then inspect authoritative state.
+
+
+
+
+
+## The cursor decision is intentionally bounded
+
+ 1,000",
+ title: "Reset from a fresh snapshot",
+ summary: "The cap avoids decoding an unbounded global range only to discard nearly all events for one thread.",
+ facts: ["A truncated replay would silently omit relevant thread events, so truncation is not used.", "turnLimit affects the fallback snapshot window, not the replay count.", "The fresh snapshot replaces loaded thread history on the client."],
+ sourceIds: ["ws-resume-gap-cap", "ws-thread-subscription", "orchestration-snapshot-contracts", "client-thread-convergence"],
+ tone: "warning"
+ },
+ {
+ label: "Cursor ahead",
+ title: "Treat the cursor as invalid and reset",
+ summary: "A negative gap means the client watermark is ahead of this server's authoritative event head, such as after switching or restoring an environment.",
+ facts: ["The server sends a snapshot rather than accepting impossible history.", "Client snapshot application replaces the sequence watermark and loaded thread history.", "The new snapshot becomes the current subscription baseline."],
+ sourceIds: ["ws-shell-resume", "ws-thread-subscription", "client-thread-convergence"],
+ tone: "failure"
+ }
+]} />
+
+## Shell and thread streams optimize different projections
+
+
+
+
Comparison of shell and thread-detail stream behavior
+
Concern
Shell stream
Thread-detail stream
+
+
Projection
project/thread summaries for navigation
one thread's messages, activities, turns, plans, checkpoints, and session detail
+
Live optimization
50 ms / 512-input window; last event per aggregate triggers a current-shell refetch
filtered detail events stream without shell coalescing
+
Replay cutoff
global gap 0…1,000
global gap 0…1,000, then filter to the requested thread
+
Fallback
fresh shell snapshot
fresh thread snapshot, optionally windowed by negotiated turnLimit
+
+
+
+
+
+ The event store makes catch-up possible, but each live subscription buffer is an
+ unbounded in-memory queue bound to that stream's scope. Process loss clears it.
+ Reconnection converges by persisted replay or a fresh snapshot—not by restoring
+ the old queue. Chapter 12 compares this with the mobile durable outbox.
+
+
+The `threadSequence` attached to an older-page snapshot is a thread-scoped merge
+watermark for pagination. It is not the `afterSequence` subscription cursor. The
+client can park a page read ahead of its live state until that watermark arrives,
+while its subscription continues to advance on the global sequence.
+
+
+ A successful dispatch response reports the committed orchestration sequence. The
+ provider reactor and its external work occur after that transaction, so the
+ acknowledgement proves durable command acceptance—not a completed harness turn.
+
+
+
+ It tells a negotiating client that base-state and buffered catch-up work are now
+ applied, so the projection may move from synchronizing to live. The stream remains
+ open for future events and will be replaced when the supervisor installs another
+ RPC session.
+
+
+
diff --git a/src/content/book/80-auth-pairing.mdx b/src/content/book/80-auth-pairing.mdx
new file mode 100644
index 0000000..b156510
--- /dev/null
+++ b/src/content/book/80-auth-pairing.mdx
@@ -0,0 +1,267 @@
+---
+slug: auth-pairing
+order: 80
+number: "8"
+kind: chapter
+part: Part II · Boot and connect
+partOrder: 2
+title: Pairing, credentials, TTLs, and scopes
+shortTitle: Pairing and auth
+summary: Short-lived bootstrap grants establish trust, durable signed sessions carry scopes, DPoP binds selected tokens to a key, and WebSocket tickets adapt a current session to an upgrade URL.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Distinguish bootstrap grants, steady-state sessions, DPoP proofs, and WebSocket tickets.
+ - Apply credential precedence, lifetime, one-use, and scope rules exactly.
+ - Identify replay and revocation boundaries without overstating the implementation.
+keywords: [pairing, authentication, scopes, session, DPoP, WebSocket ticket]
+sourceAreas: [packages/contracts/src/auth.ts, apps/server/src/auth, apps/web/src/environments/primary/auth.ts, apps/web/src/hostedPairing.ts, packages/client-runtime/src/connection/onboarding.ts, packages/client-runtime/src/authorization]
+visuals: [credential ladder, lifetime ledger, authentication decision lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import FlowLab from "../../components/FlowLab.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ScenarioLab from "../../components/ScenarioLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Pairing is not the authentication mechanism used for every request. It is a trust
+bootstrap: a short-lived or process-injected credential is exchanged for a
+SQLite-backed session. Ordinary HTTP traffic presents that session as a cookie,
+Bearer token, or DPoP-bound token. WebSocket upgrades can present a short-lived
+ticket that points back to the same current session row.
+
+## The advertised policy describes posture
+
+The server derives an authentication descriptor from runtime mode and whether its
+bind host is remotely reachable.
+
+
+
+
Authentication policy and bootstrap methods by runtime posture
+
Runtime posture
Advertised policy
Bootstrap methods
+
+
desktop, local-only host
desktop-managed-local
desktop bootstrap
+
desktop, remotely reachable host
remote-reachable
desktop bootstrap + one-time token
+
web/server, local-only host
loopback-browser
one-time token
+
web/server, remotely reachable host
remote-reachable
one-time token
+
+
+
+
+All four environment-server posture rows advertise browser-cookie, Bearer, and DPoP session methods. The contract
+also defines `unsafe-no-auth`, but the policy implementation does not produce it at
+the pinned revision. A descriptor is capability and posture metadata; it is not an
+authorization bypass.
+
+
+ A hosted pairing URL carries the backend host in its query and the one-time grant in
+ its fragment. The hosted client strips that fragment and registers the remote
+ environment with a Bearer session. Clerk authentication to T3 Connect is a separate
+ issuer; a managed-relay authorization later obtains its own DPoP-bound environment
+ session. Chapter 35 follows that cloud and relay path without conflating it with the
+ four environment-server rows above.
+
+
+
+ D[auth descriptor]
+ B[desktop bootstrap seed] --> X{exchange shape}
+ O[one-time pairing grant] --> X
+ X --> C[browser cookie session]
+ X --> T[Bearer session]
+ X --> I[verify token-endpoint DPoP proof]
+ I --> K[DPoP-bound signed session]
+ C --> H[HTTP session authentication]
+ T --> H
+ K --> Q[verify per-request DPoP proof] --> H
+ H --> S[endpoint scope check where required]
+ H --> W[issue WS ticket]
+ W --> U[verify ticket + reload session]
+ H -.direct upgrade without ticket.-> V["/ws authenticated session"]
+ U --> V
+ V --> R[per-method RPC scope map]
+ D -.advertises methods.-> X`} />
+
+
+## Credential lifetime and replay ledger
+
+The classes differ in storage, duration, and consumption semantics. “Token” alone is
+too vague to reason about their security properties.
+
+
+
+
Credential storage, default lifetime, and replay behavior
+
Credential
Storage / binding
Default lifetime / acceptance window
Use behavior
+
+
desktop bootstrap seed
in-memory map; trusted process handoff
24 hours
unbounded reuse while process state survives
+
ordinary pairing link
plaintext credential in SQLite row
5 minutes
exactly one successful atomic consume
+
development startup link
plaintext credential in SQLite row
24 hours
exactly one successful atomic consume
+
normal startup link
plaintext credential in SQLite row
5 minutes
exactly one successful atomic consume
+
browser or Bearer session
server-signed token + SQLite session row
30 days
reusable until expiry or revocation
+
DPoP access session
signed token with a jkt claim + SQLite session row; bound to a P-256 key
1 hour
requires a valid token-bound proof per request
+
DPoP proof
signed JWT + exclusive replay-marker file
5-minute acceptance window; 5-second future tolerance; no exp claim
the pinned issue/verify path records no one-use state
+
+
+
+
+
+ Persisted grants use a conditional SQLite update so two exchanges cannot both win.
+ The process-local desktop seed is a different class: it deliberately retains
+ unbounded uses so a trusted desktop renderer can establish a fresh session after a
+ reload.
+
+
+
+ Ordinary pairing tokens are persisted verbatim rather than as a one-way digest.
+ Short lifetime and atomic one-use consumption or revocation are implemented; OS
+ access control over `state.sqlite` remains security-sensitive. Avoid describing the
+ credential as hashed or implying that SQLite setup explicitly hardens its file mode.
+
+
+## Scopes are capabilities, not roles
+
+The contract defines eight independent scopes. A standard client receives five:
+`orchestration:read`, `orchestration:operate`, `terminal:operate`, `review:write`,
+and `relay:read`. The administrative set adds `access:read`, `access:write`, and
+`relay:write`.
+
+
+
+
How pairing and exchange paths select environment scopes
+
Path
Scope result
Expansion rule
+
+
desktop bootstrap seed
administrative set
seeded by the trusted desktop process
+
ordinary one-time link
standard set unless the issuer supplied another set
issuance policy owns the grant
+
startup link
administrative set
created by server startup
+
browser-session exchange
the complete grant set
the browser path does not request a narrower subset
+
Bearer or DPoP token exchange
requested subset, or the grant set by default
cannot add a scope absent from the grant
+
session delegates another pairing link
nonempty, unique requested subset
issuer needs access:write and must hold every delegated scope
+
+
+
+
+
+ Every resulting session still reaches endpoint and RPC authorization checks. The
+ profiles determine which capabilities a session can satisfy; they do not disable
+ authorization.
+
+
+## One exchange, then steady-state authentication
+
+
+
+
+ The conditional SQLite update makes one-time grant consumption race-safe. Session
+ issuance happens afterward as a separate operation. If issuance fails after the
+ consume succeeds, no compensation restores that grant; the client needs a fresh
+ pairing credential.
+
+
+## Credential selection has no fallback after selection
+
+
+
+
+
+
+
+
+ The internal guide says a WebSocket ticket carries scopes. Executable code wins at
+ this revision: the ticket carries `sid` and timestamps, and verification reloads
+ scopes from the SQLite session row. The resulting socket is scoped; the ticket
+ itself is not a scope-bearing snapshot.
+
+
+
+ The pinned implementation creates exclusive `dpop-proof-*` marker files. A
+ repository-wide source search found no cleanup path for those markers. That is an
+ absence-of-code inference, not an upstream guarantee; a later revision may add
+ retention or garbage collection.
+
+
+The browser cookie is set `HttpOnly`, `SameSite=Lax`, and path `/`; the code does not
+set an explicit `Secure` attribute. That is one environment-server session shape,
+not the credential used by every remote client. Direct hosted pairing stores a
+Bearer environment session, while the managed-relay path later establishes a
+distinct DPoP-bound environment session. Chapter 35 follows those remote boundaries.
+
+
From e068e9fe9816ebb02c4a4eabafc59361758d0197 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:04 +0300
Subject: [PATCH 04/12] docs(book): publish part three domain kernel
---
src/content/book/100-events-receipts.mdx | 263 ++++++++++++
.../book/110-projections-read-models.mdx | 225 ++++++++++
src/content/book/120-post-commit-reactors.mdx | 278 ++++++++++++
src/content/book/130-persistence-recovery.mdx | 401 ++++++++++++++++++
src/content/book/90-commands-invariants.mdx | 294 +++++++++++++
5 files changed, 1461 insertions(+)
create mode 100644 src/content/book/100-events-receipts.mdx
create mode 100644 src/content/book/110-projections-read-models.mdx
create mode 100644 src/content/book/120-post-commit-reactors.mdx
create mode 100644 src/content/book/130-persistence-recovery.mdx
create mode 100644 src/content/book/90-commands-invariants.mdx
diff --git a/src/content/book/100-events-receipts.mdx b/src/content/book/100-events-receipts.mdx
new file mode 100644
index 0000000..86fa0a1
--- /dev/null
+++ b/src/content/book/100-events-receipts.mdx
@@ -0,0 +1,263 @@
+---
+slug: events-receipts
+order: 100
+number: "10"
+kind: chapter
+part: Part III · Transactional domain core and post-commit delivery
+partOrder: 3
+title: Events, receipts, idempotency, and the post-commit gap
+shortTitle: Events and receipts
+summary: Events record durable domain facts, command receipts bind an id to one aggregate but not its payload, and hot publication after commit leaves a crash window that an acknowledgement cannot close.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Read the durable event envelope without confusing internal storage columns with public fields.
+ - Derive accepted, rejected, conflict, and no-receipt retry behavior.
+ - Identify exactly what shares the SQL transaction and what happens after commit.
+ - Explain why a returned sequence proves durable intent but not provider execution.
+keywords: [events, command receipts, idempotency, retry, sequence, publication, PubSub, crash window]
+sourceAreas: [packages/contracts/src/orchestration.ts, apps/server/src/persistence/Layers/OrchestrationEventStore.ts, apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts, apps/server/src/orchestration/Layers/OrchestrationEngine.ts, apps/server/src/orchestration/Layers/ProviderCommandReactor.ts]
+visuals: [event envelope ledger, transaction boundary, duplicate-command matrix, crash-window explorer]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ReceiptWindowLab from "../../components/ReceiptWindowLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+T3 Code persists two related ledgers for every accepted command:
+
+- the **event ledger** says what happened, in global order;
+- the **receipt ledger** says what result belongs to this command id.
+
+They commit together, but they answer different questions. An event supports replay
+and projection. A receipt prevents one command id from creating a second domain
+transaction for the same aggregate. Neither ledger records whether a provider
+harness ultimately completed the work triggered by that intent.
+
+## The public event envelope
+
+Every event variant combines the same base envelope with a type-specific payload.
+
+
+
+
Meaning of fields in the public orchestration event envelope
+
Field
Meaning
Reading rule
+
+
sequence
global durable order assigned by SQLite
resume and projection cursors compare this value across all aggregates
+
eventId
unique event identity allocated by the decider
identity is not the ordering mechanism
+
aggregateKind + aggregateId
project or thread stream owning the fact
these also define the accepted receipt's aggregate binding
+
type + payload
the domain fact and its variant-specific data
projectors switch on the type; the payload is not stored in the command receipt
+
occurredAt
domain timestamp selected during decision
do not use it as a replacement for global sequence order
+
commandId
command that caused the event, or null
several events can share one command id
+
causationEventId
direct event-level cause where one is known
for a turn start, the request event points at the user-message event
+
correlationId
command-level correlation, normally the command id
groups a batch without replacing event identity
+
metadata
provider ids, adapter key, request id, ingestion time, and optional client origin
metadata carries attribution and integration context, not aggregate state itself
+
+
+
+
+
+ SQLite assigns a zero-based per-aggregate stream_version and infers an
+ actor_kind column while inserting. The decoded public event returned by
+ this store contains the global sequence and envelope fields above, but not either
+ storage column. Actor classification is inferred from command-id prefixes and
+ provider metadata; it is not a claim supplied as a public event field.
+
+
+The event store therefore has two orders with different visibility:
+
+- a global `sequence`, exposed on every event and used for resumable reads;
+- a per-stream `stream_version`, computed and stored internally.
+
+The per-stream version is not an expected-version token in the command protocol.
+As Chapter 9 showed, one serialized command worker prevents concurrent decisions
+inside a server process.
+
+## A receipt is smaller than the command it deduplicates
+
+
+
+For an accepted command, `resultSequence` is the sequence of the last event in its
+batch. For an invariant rejection, the same schema is used differently: no event is
+appended, `status` is `rejected`, `error` carries the rejection, and
+`resultSequence` is the command model's current snapshot sequence. The field named
+`acceptedAt` is populated for both statuses.
+
+
+ A receipt hit first compares aggregate kind and aggregate id. If they match, status
+ determines whether the engine returns the accepted sequence or the stored
+ rejection. The engine does not compare command type or body. Reusing an id for a
+ changed command on the same aggregate silently refers to the first result.
+
+
+This is **deduplication by caller discipline**: the caller must treat a command id as
+the permanent name of one intent. The server catches reuse across aggregates, but it
+cannot catch changed intent within the same aggregate.
+
+
+ HTTP and WebSocket clients are authorized and normalized before the command enters
+ the engine. An accepted receipt prevents another event transaction; it does not
+ skip schema decoding, timestamp canonicalization, workspace normalization, or
+ attachment staging. Retrying an upload can therefore write new attachment files
+ before the engine discovers the receipt.
+
+
+## Accepted events, projections, and receipt share one transaction
+
+
+
+
+ T
+ subgraph T[one SQL transaction]
+ E[append all events] --> P[apply SQLite projections]
+ P --> R[upsert accepted receipt]
+ end
+ T -->|commit| M[replace in-memory model]
+ M --> H[publish event 1..N]
+ H --> A[return last sequence]
+ H --> X[hot reactor streams]
+ X --> V[provider + side effects]
+ T -.process dies after commit.-> G[durable intent; hot event missing]`} />
+
+
+Three observations follow directly from this ordering:
+
+1. A successful commit never leaves an accepted receipt without its event batch or
+ transactional projections.
+2. Hot publication is not part of that atomic unit.
+3. The RPC result waits for the publication loop, but publication only offers events
+ to in-memory subscribers; it does not wait for provider work to finish.
+
+## Duplicate behavior is a five-row matrix
+
+Receipt lookup happens before the decider. Aggregate comparison happens before
+receipt-status replay.
+
+
+
+
Engine outcome for each durable receipt and retry target combination
+
Stored receipt
Retry aggregate
Engine result
New events / publication
+
+
accepted
same kind + id
return stored resultSequence
none / none
+
accepted
different kind or id
command-id conflict
none / none
+
rejected
same kind + id
previously-rejected error with stored detail
none / none
+
rejected
different kind or id
command-id conflict
none / none
+
no row
any
decide against current state
depends on current decision
+
+
+
+
+An accepted same-aggregate retry does **not** replay the original events into the hot
+stream. It returns the stored sequence immediately. This is desirable after a lost
+RPC response when the original publication completed. It cannot repair a
+commit-to-publication crash gap.
+
+
+ After an invariant error, the engine attempts to save a rejected receipt and
+ ignores failure of that receipt write. If the row exists, a same-id retry stays
+ rejected without asking the decider again—even if domain state has since changed.
+ If the best-effort write failed, there is no row and a retry can be decided again.
+
+
+Persistence failures inside the accepted transaction are different. The transaction
+rolls back, no accepted receipt remains, and the failure is not recorded as a domain
+invariant rejection. A retry can run the decision and transaction again.
+
+## The hard-crash gap is real
+
+After SQL commit, the engine first replaces its in-memory model, then publishes each
+committed event sequentially. The pinned production-code audit found no outbox row
+whose delivery acknowledgement commits with the events.
+
+
+ If the process dies after commit and before the relevant event is published, the
+ event batch and accepted receipt survive. The provider command reactor explicitly
+ subscribes to a hot stream that cannot resume work pending before it starts. A
+ same-id retry sees the accepted receipt and returns its sequence without
+ republishing. Together, those verified code paths imply a durable-intent / missing-
+ side-effect window.
+
+
+The failure handler does provide a narrower repair path while the process is still
+alive. For most dispatch failures, it reads the durable tail after the command's
+starting sequence, folds that tail back into the command model, and republishes it.
+That can reconcile a recoverable post-commit failure in the same process. It cannot
+run after a hard process crash, and it is not a durable outbox protocol. If a prefix
+was already published before a recoverable failure, replaying the durable tail can
+offer that prefix again; reconciliation is not exactly-once delivery.
+
+
+ The engine's catch path can rediscover committed events during a live dispatch
+ failure. The provider reactor's own startup comment says pre-subscription pending
+ work cannot be resumed. Do not generalize the catch path into guaranteed reactor
+ replay after restart.
+
+
+## Explore receipts and crash windows
+
+The lab starts with an accepted receipt. Change its status, target aggregate, body,
+and original delivery window. In particular, compare “response lost” with “hard
+crash after commit”: both can look like silence to the first caller, but only one
+preserves the original hot publication.
+
+
+
+## What a sequence acknowledgement proves
+
+
+
+
Guarantees established and not established at each completion boundary
every event was offered to current in-memory subscribers
durable delivery or completed reactor handling
+
RPC returns {`{ sequence }`}
server exposes the last durable event sequence
that the response reached the client
+
client receives sequence
client knows its durable intent was accepted
provider process start, token output, or turn completion
+
provider lifecycle events arrive
the corresponding later provider milestone
milestones not yet represented by a later event
+
+
+
+
+
+ The engine returns after its PubSub publication loop. The provider command reactor
+ consumes thread.turn-start-requested later and sends work through its
+ own worker/forked path. The returned sequence therefore proves durable domain
+ intent, not provider acceptance, execution, or completion.
+
+
+
+ The similarly named RuntimeReceiptBus is a test synchronization hook
+ for runtime ingestion; its production layer is a no-op. It does not deduplicate
+ commands, close the PubSub crash gap, or strengthen the RPC acknowledgement.
+
+
+The right mental model is deliberately narrow: **receipt idempotency protects one
+serialized domain commit**. It is not payload equality, not normalization
+idempotency, not saga idempotency, not durable reactor delivery, and not provider
+exactly-once execution.
+
+
diff --git a/src/content/book/110-projections-read-models.mdx b/src/content/book/110-projections-read-models.mdx
new file mode 100644
index 0000000..2aaea7c
--- /dev/null
+++ b/src/content/book/110-projections-read-models.mdx
@@ -0,0 +1,225 @@
+---
+slug: projections-read-models
+order: 110
+number: "11"
+kind: chapter
+part: Part III · Transactional domain core and post-commit delivery
+partOrder: 3
+title: Projection tables and read models
+shortTitle: Projections
+summary: New events fold through ordered SQLite projectors before the enclosing command commits; bootstrap replays committed events from per-projector cursors, and queries expose deliberately different read-model shapes.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace one durable event through every projection cursor and its SQL boundary.
+ - Distinguish the safe snapshot watermark from the durable event-store head.
+ - Separate full snapshots, command state, shell summaries, detail pages, and live replay.
+keywords: [projections, SQLite, read model, cursor, snapshot, replay, checkpoint]
+sourceAreas: [apps/server/src/orchestration/Layers/ProjectionPipeline.ts, apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts, apps/server/src/persistence/Layers/ProjectionState.ts, apps/server/src/ws.ts]
+visuals: [ordered projection fold, cursor watermark lab, read-model comparison]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ProjectionCursorLab from "../../components/ProjectionCursorLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+An orchestration event is not directly a UI row. The durable event store records a
+global sequence; a projection pipeline folds that event into several SQLite read
+tables; query services compose those tables into views for a particular reader.
+This is a synchronous part of normal command acceptance, not a background
+eventually-consistent worker that happens after a command has returned.
+
+## Nine folds share one event order
+
+Each named projector has its own `projection_state` cursor. For every event, the
+pipeline considers every projector in this exact sequence:
+
+1. Projects
+2. Messages
+3. Proposed plans
+4. Activities
+5. Sessions
+6. Turns
+7. Checkpoints
+8. Pending approvals
+9. Threads
+
+The final placement is meaningful. The thread projection refreshes summary fields
+after the message, plan, activity, session, turn, and approval tables have been
+updated. It can therefore compute navigation-oriented facts such as pending
+approval/input counts and whether a plan is actionable from current dependent rows.
+
+
+ The registry names nine cursored projectors. Its concrete array puts the aggregate
+ thread/shell fold last, after the tables it reads to derive shell summary fields.
+
+
+
+ P[projects]
+ P --> M[messages]
+ M --> PL[plans] --> A[activities] --> S[sessions] --> T[turns]
+ M -. optional prune after\nmessage SQL + cursor .-> FM[attachment filesystem]
+ FM -. then continue .-> PL
+ T --> C[checkpoints\nno-op] --> PA[pending approvals] --> TH[threads\nderived shell]
+ TH --> R[(accepted receipt)]
+ TH -. optional delete after\nthread SQL + cursor .-> FT[attachment filesystem]
+ FT -. then continue .-> R
+ R --> O[(outer SQL commit)] --> L[post-commit live stream]`} />
+
+
+
+ The engine appends each planned event, updates the in-memory command model,
+ invokes the projection pipeline, and writes the accepted receipt within its
+ outer SQL transaction. The pipeline itself runs its ordered projector work
+ sequentially. Publication happens after that transaction succeeds.
+
+
+
+
+## Cursor means “safe through here,” not “the newest event”
+
+The event store has a monotonically increasing, global `sequence`; each aggregate
+also has its own stream version. Projection restart uses the former. On bootstrap,
+each projector reads only events after *its own* `last_applied_sequence` and advances
+that cursor after its SQL fold. A projector that has nothing to do for an event still
+records that it safely considered the event.
+
+The query layer does not advertise the highest event in the log as a snapshot
+sequence. It checks the required projector state and returns their minimum. If a
+required state row is missing, the safe sequence is zero. That conservative
+watermark prevents a snapshot composed from several tables from claiming it includes
+an event that one required read side has not reached.
+
+
+ Bootstrap is a projector-by-projector replay from each durable cursor, not a
+ one-time global replay cursor shared by every table.
+
+
+
+ The event-store reader defaults to a 1,000-event total limit, and its pagination
+ loop decrements that one allowance rather than requesting every remaining page.
+ Projection bootstrap calls the reader without overriding the limit. A projector
+ more than 1,000 events behind therefore stops early at this revision; the small
+ bootstrap tests do not cover 1,001 events.
+
+
+
+
+
+
+
+ The registered checkpoint projector is currently a literal no-op. It is still
+ traversed and cursor-tracked in the pipeline. This is useful evidence against an
+ appealing but false rule: “cursor N means this projector wrote a row for event N.”
+
+
+## Checkpoint is a naming trap
+
+Checkpoint data is currently carried by turn projection rows, rather than an active
+independent checkpoint table projector. The projector named `projection.checkpoints`
+returns no work. The detail snapshot query obtains checkpoint-oriented rows from the
+turn read side. Treat the name as a tracked pipeline slot, not evidence of a separate
+materialized checkpoint store.
+
+
+ The implementation defines the checkpoint projector as `Effect.void` while
+ preserving its registration and cursor slot. Turn projection owns the current
+ checkpoint metadata.
+
+
+
+ A windowed thread detail limits its raw turn scan to 150 rows. This is a query-read
+ limit, not an event replay limit and not a promise that one user turn's whole
+ subagent fan-out will fit in one page.
+
+
+## One database, several deliberately incomplete views
+
+“The snapshot” is not one universal object. The full hydrated snapshot composes
+projects, threads, messages, plans, activities, sessions, checkpoints, turns, and
+projector state in a single query transaction. The engine instead starts from a
+lighter command model that intentionally omits message bodies, activities, and
+checkpoints. The navigation shell is leaner still: project and active-thread summary
+rows. Thread detail is a dedicated, possibly paged read.
+
+
+
+
Comparison of projection query shapes
+
Reader
Purpose
What it intentionally leaves out
+
+
Full hydrated snapshot
authoritative composed read for complete state
nothing from its selected projection families; it is the expensive shape
+
Command read model
engine bootstrap and command decisions
messages, activities, and checkpoints
+
Shell snapshot
project/thread navigation and live shell refetches
older turn windows beyond its cursor until requested
+
+
+
+
+
+ Full, command, shell, archived-shell, and thread-detail reads are separate query
+ shapes. The detail path intentionally reads its rows and watermark inside one
+ transaction.
+
+
+The HTTP `/orchestration/snapshot` endpoint returns the command-model shape, not the
+full hydrated snapshot. That is an API-level reminder not to infer a UI payload just
+from a type named “read model.” Shell and thread readers have their own endpoints and
+WebSocket stream behavior.
+
+## Live repair uses global cursors; page merging has another watermark
+
+WebSocket resume is bounded. A client’s `afterSequence` is a global event sequence.
+The handler attaches a live buffer before replay or snapshot work, then either
+replays a non-negative global gap through a captured head or falls back to a fresh
+snapshot when the gap is absent, impossible, or exceeds 1,000 events. Buffered live
+input can overlap the replay; client reducers deduplicate by global sequence.
+
+Thread detail adds `threadSequence` to a *windowed page*. It is the greatest
+deliverable detail-event sequence for that thread at or below the snapshot sequence.
+Its purpose is to delay merging a historical page until the live reducer has caught
+up. It is **not** the `afterSequence` accepted by the subscription API, and it cannot
+replace the global cursor.
+
+
+
+
+ A windowed page carries a thread-scoped merge watermark so historical rows do not
+ race current deltas. The subscription cursor remains global because unrelated
+ events also occupy the durable event sequence used for replay and overlap
+ deduplication.
+
+
+
+ Attachment pruning and thread attachment cleanup run after the relevant projector's
+ SQL and cursor step, but before the remaining command work and outer SQL commit.
+ Cleanup errors are logged and swallowed so the fold can continue. A file mutation
+ cannot be undone if a later SQL failure rolls the outer transaction back; on a
+ successful command, the rows and cursors commit normally. This code does not turn
+ the filesystem into a transactional resource.
+
+
+## Source trail
+
+
diff --git a/src/content/book/120-post-commit-reactors.mdx b/src/content/book/120-post-commit-reactors.mdx
new file mode 100644
index 0000000..bc3fbd5
--- /dev/null
+++ b/src/content/book/120-post-commit-reactors.mdx
@@ -0,0 +1,278 @@
+---
+slug: post-commit-reactors
+order: 120
+number: "12"
+kind: chapter
+part: Part III · Transactional domain core and post-commit delivery
+partOrder: 3
+title: Post-commit reactors and the delivery gap
+shortTitle: Post-commit reactors
+summary: Durable intent crosses a non-durable hot-stream seam into independent reactor workers, whose serialized handlers can still fork overlapping provider work and whose progress is not reconstructed after a crash.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Locate the exact commit-before-publish crash window and predict what a command retry does.
+ - Distinguish serialized reactor handlers from forked provider fibers and independent consumers.
+ - Trace provider results back through volatile runtime ingestion into durable internal commands.
+ - Evaluate activation and shutdown boundaries without inventing an outbox, cursor, or production-wide drain.
+keywords: [reactors, PubSub, side effects, delivery gap, DrainableWorker, provider ingestion, crash recovery]
+sourceAreas: [apps/server/src/orchestration/Layers, apps/server/src/provider/Layers/ProviderService.ts, apps/server/src/serverRuntimeStartup.ts, packages/shared/src/DrainableWorker.ts]
+visuals: [commit publication sequence, crash cursor, reactor topology, worker ownership matrix]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import DeliveryGapLab from "../../components/DeliveryGapLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+The transaction gives T3 Code durable **intent**. It does not give every post-commit
+consumer durable **delivery**. The engine commits events, SQL projections, and an
+accepted receipt, returns from that transaction, replaces its in-memory read model,
+and only then publishes each event to an in-process PubSub. Reactors turn those hot
+events into provider, checkpoint, terminal, deletion, and awareness effects.
+
+That separation is useful: an external harness call never holds the SQLite
+transaction open. It also creates the defining failure window of this chapter: the
+database can say “accepted” while every side-effect subscriber sees nothing.
+
+## The transaction ends before delivery begins
+
+
+
+
+ D[decide]
+ D --> TX[(event + projections + receipt)]
+ TX -->|COMMIT| M[in-memory model]
+ M -. hot publish .-> B{{domain PubSub}}
+ B -.-> R1[provider reactor worker]
+ B -.-> R2[checkpoint worker]
+ B -.-> R3[deletion + awareness workers]
+ R1 -. fork .-> P[ProviderService + adapter]
+ P -. hot runtime event .-> RB{{runtime PubSub}}
+ RB -.-> I[runtime ingestion worker]
+ I -->|internal command| C
+ TX -. "crash: intent durable, delivery absent" .- B`} />
+
+
+The engine's returned sequence is therefore an acknowledgement of command
+acceptance, not provider completion. A client retry using the same command id finds
+the accepted receipt and returns its stored sequence. That lookup does **not**
+republish the event. Receipt idempotency protects the transaction from duplicate
+command execution; it does not repair the post-commit side-effect bridge.
+
+
+ The durable event store can serve client catch-up reads, but the inspected reactor
+ subscriptions are fresh streams created from process-local PubSubs. The provider
+ reactor explicitly says pending hot-stream work cannot resume. No reactor progress
+ row, outbox append beside the command transaction, or startup event-store replay
+ appears in these paths at the pinned revision.
+
+
+
+
+The cursor exposes two different uncertainties. Before SQL commit, rollback is
+clean: no accepted receipt survives. After commit but before reactor observation,
+intent survives while work disappears. Once an external adapter call begins, the
+problem changes again: the server may no longer know whether repeating the call is
+safer than leaving it alone.
+
+## One queue serializes one handler—not the whole system
+
+`OrchestrationReactor.start` starts several consumers in one scope: provider runtime
+ingestion, provider command handling, checkpoint capture/revert, thread deletion,
+and agent awareness. They do not share one global queue. Each worker serializes its
+own handler invocations while the consumers themselves run independently.
+
+
+
+
+
+
What each reactor serializes, forks, and can recover
+
Consumer
Serialized body
Work outside that body
Restart behavior
+
+
provider command
domain-event handler dequeue
sendTurn, first-turn branch naming, and title work are scoped forks
fresh hot subscription; volatile dedupe disappears
+
runtime ingestion
runtime events plus selected domain inputs; the current domain handler is a no-op
buffer timers and internal commands cross back into the engine
source audit found no durable runtime-event cursor; selected activity lookup is a narrow fallback
+
checkpoint
capture/revert trigger handling
Git refs, workspace mutation, provider rollback, then internal commands
source audit found no replay of a missed hot trigger or durable saga phase
+
thread deletion
one deletion side-effect handler at a time
provider stop and terminal cleanup touch separate systems
source audit found no startup scan for a missed deletion event
+
awareness
awareness event handling
delayed confirmation fibers and relay publication
re-emits current active state, not the missing event history
+
+
+
+
+Calling this “serialized provider execution” would be wrong. The provider reactor's
+queue orders the start of its handler bodies. At `ProviderCommandReactor.ts:1171–1173`,
+`sendTurn` is forked, so two provider sends can overlap. First-turn title and branch
+work are also forked. Other reactors may concurrently process the same domain or
+provider event through their own subscriptions.
+
+
+ `ProviderCommandReactor.drain` waits for its main worker and title-regeneration
+ worker. The forked `sendTurn` fiber is not counted by either worker's outstanding
+ counter. The production composite exposes `start`, not a system-wide drain.
+
+
+## Provider intent uses volatile duplicate suppression
+
+For a turn-start request, the provider reactor derives a turn key and checks an
+in-memory bounded cache with a 30-minute TTL. The key is recorded before the
+external send. Within that process, a duplicate can therefore be suppressed even
+if later work fails. Restart clears the cache—but it also clears the hot event, so
+the durable original does not automatically receive a second attempt.
+
+Failure handling records selected durable error/session activities by dispatching
+internal commands. Those compensating commands are themselves best-effort from the
+reactor's perspective: the handler catches, logs, and swallows non-interruption
+causes so that one bad event does not terminate its worker.
+
+Within one process, this is an **effectively at-most-once observation path per
+derived turn key, with explicit compensations**. It is not a general exactly-once
+executor: the cache expires and disappears at restart, while the source event is
+not replayed. The underlying provider may have accepted a call before
+`ProviderService` persists its binding or running state, leaving an ambiguous
+external/local boundary after a crash.
+
+## Runtime results return through another hot bridge
+
+Provider adapters emit canonical runtime events. `ProviderService` writes its
+best-effort diagnostic log and publishes the event to a second PubSub. Runtime
+ingestion subscribes to that provider stream alongside selected domain events; its
+current domain handler is a no-op, while provider observations translate into
+internal commands and dispatch through the same engine. Only the internal command's
+successful transaction makes the result part of durable domain truth.
+
+Runtime ingestion deliberately buffers high-rate content. Its flush path removes a
+buffer entry before dispatching the aggregate internal command. If dispatch is
+interrupted or fails, the removed batch is not restored. Handler errors are logged
+and swallowed. This limits memory and prevents a poisoned event from killing the
+loop, but it is not a durable queue.
+
+
+ Internal command ids incorporate the provider event id and tag plus a random UUID.
+ Re-delivering the same runtime event can therefore create a different command id.
+ Stable message identity helps correlation, but streaming projections append
+ deltas; it does not turn arbitrary event replay into a no-op.
+
+
+The worker also subscribes to `thread.turn-start-requested`, but its selected-domain
+handler currently returns `Effect.void`. Non-interruption handler failures are logged
+and swallowed so the worker can continue; this isolates a bad input without making
+that input durable or retryable.
+
+Tests cover deduplication between selected *semantic completion signals*, such as an
+item-completed event followed by turn-completed. They do not inject the exact same
+provider event twice across a process crash. The distinction matters: semantic
+coalescing is narrower than an idempotent ingestion log.
+
+## Parked roots narrow startup races, not delivery semantics
+
+Startup creates a dedicated reactor scope and forks the reactor composite behind a
+shared activation deferred. Each activation-aware root first reports that it is
+parked. Provider-session reconciliation runs before activation; HTTP and auxiliary
+roots prepare; startup sends welcome, opens activation, and only then admits queued
+commands.
+
+
+ The barrier prevents an early root from deliberately acting against a partially
+ prepared graph. The inspected code does not persist events published while a
+ subscription remains behind the gate, nor does activation wait for a second latch
+ proving that every post-activation stream has attached before command admission.
+
+
+Startup provider reconciliation compares projected active sessions with live
+adapter inventory. An orphaned projection is marked stopped/error and the internal
+dispatch is retried once. That reconciler does not replay the original turn or
+eagerly resume every persisted provider binding; the pinned production-code audit
+found no separate arbitrary-reactor replay pass. Later routed operations can adopt
+or resume a binding lazily when enough cursor data exists.
+
+## Scope close interrupts; production does not globally drain
+
+`DrainableWorker` owns an unbounded transactional queue, one scoped worker, and an
+outstanding counter. `enqueue` increments the counter, handler completion or failure
+decrements it, and `drain` waits for zero. Its source describes that drain as a
+deterministic synchronization utility for tests.
+
+On scope close, the worker queue is shut down and the worker fiber is interrupted.
+The inspected orchestration composite exposes no step that first stops ingress and
+drains every worker and child fiber. ProviderService has its own substantial best-effort finalizer—persist
+active bindings, stop adapters, revoke credentials, mark bindings stopped, flush
+analytics—but that is not an atomic completion barrier for every reactor effect.
+
+
+ Worker tests prove that an explicit drain waits for work counted by that worker.
+ They do not prove that production shutdown invokes that drain, that forked children
+ are counted, or that an external provider call and its durable follow-up finish
+ atomically.
+
+
+## The other reactors have different partial recovery stories
+
+Checkpoint capture creates a hidden Git ref, refreshes state, computes a diff,
+dispatches checkpoint metadata, inspects test receipts, and then records activity.
+Revert runs in the opposite world: it mutates the workspace, refreshes, rolls the
+provider back, deletes newer refs, and only then records durable completion. Chapter
+13 examines the cross-store crash windows and the staging-state overclaim.
+
+Thread deletion stops the provider session, closes the terminal, and deletes its
+history. Failures are logged. The pinned production-code audit found no durable
+cleanup job or startup thread scan that reconstructs a missed hot deletion event.
+
+Agent awareness is intentionally more reconstructive: it keeps volatile dedupe and
+confirmation timers, then publishes a delayed snapshot of currently active threads
+after activation. That can restore current liveness presentation. Because both the
+published-state map and confirmation deadlines are process-local, this path does not
+replay a missed historical tombstone or preserve an in-flight timer across restart.
+
+## What the tests establish—and what they do not
+
+
+
+
Tested guarantees and untested failure windows for post-commit reactors
+
Claim
Evidence
Classification
+
+
a later projector failure rolls back a multi-event transaction
OrchestrationEngine.test.ts:906–1024
tested
+
the command worker continues after an append failure
OrchestrationEngine.test.ts:781–904
tested
+
retrying one genuine command id returns its stored accepted sequence
OrchestrationEngine.test.ts:1234–1296
tested
+
one worker drain waits for subsequently enqueued counted work
DrainableWorker.test.ts:8–56
tested
+
hard crash between commit and first PubSub publish is repaired
no process-crash test and no outbox/cursor implementation
not guaranteed
+
same id + same aggregate + changed payload conflicts
receipt has no type/payload fingerprint; no matching test
not guaranteed
+
shutdown drains forked provider sends
fork is outside the worker counter; no production drain test
not guaranteed
+
replaying an identical provider event is a no-op
generated internal id includes random UUID; no crash/replay test
not guaranteed
+
+
+
+
+
+ A meta-harness that promises eventual execution needs more than a durable intent
+ event: use a transactional outbox or per-reactor cursor, bind idempotency keys to
+ canonical payloads, journal external saga phases, and stop ingress before draining
+ queues **and their child fibers**. That is a design lesson derived from this gap,
+ not a claim about T3 Code's announced roadmap.
+
+
+
diff --git a/src/content/book/130-persistence-recovery.mdx b/src/content/book/130-persistence-recovery.mdx
new file mode 100644
index 0000000..ae66ca5
--- /dev/null
+++ b/src/content/book/130-persistence-recovery.mdx
@@ -0,0 +1,401 @@
+---
+slug: persistence-recovery
+order: 130
+number: "13"
+kind: chapter
+part: Part III · Transactional domain core and post-commit delivery
+partOrder: 3
+title: Persistence, reconstruction, and crash recovery
+shortTitle: Persistence and recovery
+summary: SQLite is the durable domain core, but settings, secrets, attachments, terminal history, logs, and hidden Git refs cross separate filesystems and recovery protocols with sharply different guarantees.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace SQLite acquisition, ordered migrations, projector cursors, and command-model reconstruction.
+ - Reproduce the hidden 1,000-event projector bootstrap ceiling.
+ - Compare the journaled service-update database restore with unjournaled cross-store sagas.
+ - Separate tombstones, derived-row pruning, log rotation, and in-memory caps from one another.
+keywords: [SQLite, WAL, migrations, recovery, settings, secrets, attachments, terminal history, Git refs, tombstones]
+sourceAreas: [apps/server/src/persistence, apps/server/src/orchestration/Layers/ProjectionPipeline.ts, apps/server/src/serviceLauncher.ts, apps/server/src/serverSettings.ts, apps/server/src/terminal, apps/server/src/vcs]
+visuals: [startup reconstruction DAG, projector replay counter, recovery matrix, retention taxonomy]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import RecoveryMatrixLab from "../../components/RecoveryMatrixLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+T3 Code does not have one persistence boundary. SQLite owns the orchestration event
+log, command receipts, projection tables, authentication, and several runtime
+records. JSON and secret files own configuration. Image files own attachments.
+Terminal and provider logs have independent buffering and retention. Hidden Git
+refs own checkpoint trees. A provider process owns work that may already be running
+outside all of them.
+
+Recovery quality follows the coordination protocol between those stores—not the
+strongest store named in the operation.
+
+## SQLite is the domain core
+
+The server chooses a Bun or Node SQLite implementation at runtime, creates the
+database directory, opens `state.sqlite`, and configures the connection before
+application traffic begins.
+
+
+
+The Node implementation wraps one `DatabaseSync` connection with a semaphore, so
+operations through that service are serialized. WAL permits a separate CLI and
+server process to coordinate with less reader/writer blocking, while the explicit
+busy timeout waits for locks instead of immediately returning `SQLITE_BUSY`.
+
+
+ The setup executes `busy_timeout = 5000`, `foreign_keys = ON`, and
+ `journal_mode = WAL`. Repository search at the pinned commit found no explicit
+ `PRAGMA synchronous` choice or application-triggered WAL checkpoint. The book can
+ state the configured pragmas, but should not invent stronger fsync or power-loss
+ guarantees from the word “WAL.”
+
+
+## Migrations are a numbered program
+
+`Migrations.ts` imports every migration and constructs a statically ordered map. At
+this pinned revision the manifest contains 41 migrations. Startup invokes the
+migration runner after the SQLite pragmas; it does not discover filenames and sort
+them at runtime.
+
+The first migrations establish the architectural core:
+
+
+
+
Foundational migration tables and constraints
+
Migration
Durable role
Important boundary
+
+
001 orchestration events
append-only event envelopes with stream versions
unique event id and aggregate-version pair
+
002 command receipts
accepted/rejected result lookup by command id
no command type or payload fingerprint
+
004 provider bindings
provider instance, native session id, and resume cursor
external provider state is not in the same transaction
+
005 projection tables
projects, threads, messages, activities, sessions, turns, and per-projector cursor
deleted projects/threads use tombstones
+
007 attachment references
JSON metadata in projection rows
image bytes remain separate files
+
013 plans
durable plan projection
later migrations refine linkage and shape
+
+
+
+
+
+ Migration `003` creates `checkpoint_diff_blobs`, but a repository-wide production
+ search at this commit found no read or write outside migration registration. Active
+ checkpoint diffs are derived from hidden Git refs. Treat the table as dormant or
+ legacy evidence, not proof that checkpoint blobs currently drive recovery.
+
+
+Selected migration tests exercise upgrades from older shapes. There is no injected
+process-crash test halfway through the full 41-step startup runner.
+
+## Projectors reconstruct from cursors—with a 1,000-event ceiling
+
+The projection pipeline registers nine projectors. For each committed event it
+runs every projector sequentially; each projector's SQL changes and cursor update
+share a transaction. Startup reads every projector's stored cursor and asks the
+event store for the tail beginning at `lastSequence + 1`.
+
+The surprising part is the read default. `readFromSequence` defaults `limit` to
+1,000 and treats that number as the **total remaining result count**, even though it
+pages internally. Bootstrap calls it without supplying a larger or unlimited
+value. A projector that is 1,001 events behind applies only the first 1,000 during
+that startup and the server proceeds with the projector still behind.
+
+
+ |yes| R[restore state.sqlite + WAL + SHM]
+ U -->|no| O[open SQLite]
+ R --> O
+ O --> P[busy timeout + FK + WAL]
+ P --> M[run 41 migrations]
+ M --> C[read each projector cursor]
+ C -->|default total limit 1000| F[fold persisted events]
+ F --> Q[load optimized command model]
+ Q --> K[park reactor roots]
+ K --> S[reconcile projected active sessions]
+ S --> A[prepare listeners + activate]
+ A -. lazy .-> L[provider adoption · terminal history open]
+ A -. "source audit: no general repair" .-> N[attachments · hidden refs · secret pairs · missed reactor work]`} />
+
+
+
+ The event-store loop may issue several SQL pages, but it decrements one caller
+ limit and stops when that reaches zero. Because projector bootstrap omits a limit,
+ its total result is capped at 1,000—not 1,000 per page until the tail is empty.
+
+
+Existing bootstrap tests fold three events and verify that rerunning at the stored
+cursor is idempotent. No test creates 1,001 events behind one projector. A valuable
+regression test is mechanical: append 1,001 events, reset one projector cursor to
+zero, bootstrap once, and assert whether its cursor reaches the global head.
+
+After projection bootstrap, the engine loads an optimized command read model from
+SQL: projects, threads, plans, sessions, latest turns, and projector states. It does
+not reload every message, activity, or checkpoint. Snapshot safety uses the minimum
+required projector cursor as its sequence watermark, so clients are not told that a
+newer partially projected head is complete. That safe watermark does not itself
+finish a projector left behind by the cap.
+
+## The service updater has an explicit recovery journal
+
+Managed server updates are the clearest crash-aware persistence protocol in the
+repository. The active child first persists a pending update record. After the
+handoff delay, the launcher stops that child, stages and syncs copies of
+`state.sqlite`, `state.sqlite-wal`, and `state.sqlite-shm`, and then starts the trial.
+It does **not** write the restore marker before the trial.
+
+The marker belongs to the rollback path: immediately before copying backup files
+over the live triplet, the launcher creates and syncs a marker inside the backup.
+On a later boot, pending service state without that marker resumes the trial; a
+marker makes startup retry the interrupted rollback restoration first.
+
+Commit writes terminal service state before discarding the backup. Rollback restores
+the triplet, records the terminal state, discards the backup, and then restarts the
+old child. Tests verify commit only after `prepared`, reject a rollback with the
+wrong update id, and restore a trial-mutated main/WAL/SHM set.
+
+
+ The updater documentation correctly excludes attachments and other files. Restoring
+ SQLite cannot rewind settings, secrets, logs, terminal histories, hidden Git refs,
+ provider processes, or arbitrary workspace changes made during a candidate trial.
+
+
+No test kills the launcher at every instruction between pending-state persistence,
+backup staging, marker creation, partial triplet replacement, directory sync,
+terminal-state write, and backup deletion. The protocol is designed for retry, but
+those injected-crash cases remain valuable coverage.
+
+## Cross-store operations are small sagas
+
+
+
+The matrix makes an important distinction: “atomic write” usually protects one
+file replacement. It does not make a sequence spanning several files and SQLite
+atomic. Among the inspected cross-store paths, only the service-update flow has an
+explicit durable phase marker and a startup reconciler across its named resources.
+
+## Settings JSON and secret files
+
+Settings load into a cache guarded by a semaphore and publish a hot change stream.
+A missing file becomes defaults. Malformed JSON is logged and also falls back to
+defaults; the file is not quarantined or repaired automatically. Redacted settings
+resolve their referenced secret files, and a missing secret materializes as an empty
+value.
+
+An update mutates secret files first, normalizes the redacted public shape, atomically
+renames the settings JSON, then replaces the cache and publishes. This ordering
+prevents plaintext secrets from entering settings JSON, but it creates cross-file
+failure windows:
+
+- adding a secret and then failing the JSON write leaves an orphan secret;
+- removing a secret and then failing the JSON write leaves the old redacted pointer
+ with no value;
+- the pinned startup-path audit found no reconciler that proves every redacted
+ pointer and secret file form one committed version.
+
+The JSON helper writes a same-directory temporary file and renames it, without an
+explicit file or directory sync. Secret `set` similarly writes a temporary file,
+applies `0600`, renames, and reapplies permissions; exclusive `create` syncs its new
+file, but the multi-file settings update still has no shared journal.
+
+Tests verify redaction, roundtrip materialization, race handling, and permissions.
+They do not inject a crash between secret mutation and settings JSON replacement.
+
+## Attachments cross the transaction in both directions
+
+Attachment normalization validates an image, creates a random attachment id, and
+writes its file **before** the HTTP or WebSocket path dispatches the command. A
+rejected command therefore leaves a possible orphan. Retrying an already accepted
+command id repeats normalization first, creates another random file, and then learns
+from the receipt that the older command already succeeded.
+
+Projection cleanup has the inverse ordering problem. A projector records reference
+changes and advances its SQL cursor transaction, then performs scheduled filesystem
+deletions with errors logged and swallowed. During engine dispatch, that cleanup can
+occur while the engine's larger transaction still has later events to process; a
+later projection failure can roll SQL back after the file has disappeared. During
+startup bootstrap, a crash after cursor commit but before cleanup means the cursor
+prevents that cleanup from being selected again.
+
+
+ The store validates ids and constrains file paths. That blocks path traversal. It
+ does not provide a transaction that joins pre-dispatch file creation, command
+ acceptance, projection references, and later deletion.
+
+
+The real revert-cleanup test creates files and verifies removal. A differently named
+rollback test never creates its supposed source file; it proves projection-row
+rollback and nonexistence, not rollback of a filesystem deletion. No general orphan
+garbage collector was found at this revision.
+
+## Terminal history restores text, not a process
+
+Terminal history is capped at 5,000 lines by default and persisted through a keyed
+coalescing worker after a 40 ms debounce. Output updates memory, schedules the
+history write, and then reaches live subscribers. The worker directly overwrites the
+history file; it does not use the settings atomic-rename helper.
+
+Explicit terminal close persists immediately and drains the keyed worker. General
+manager scope finalization kills active sessions but does not explicitly flush and
+drain every history key. A hard crash can therefore lose the debounce window.
+Because the worker directly overwrites the target instead of using an atomic rename,
+an abruptly interrupted write also carries a partial-file risk; the exact filesystem
+outcome is an inference, not a behavior asserted by the normal write path.
+
+On first open after restart, the manager reads and sanitizes saved text and then
+starts a new PTY. Saved text does not encode the old shell process, working job,
+input buffer, or terminal emulator state. Tests cover caps, sanitization, deletion,
+inactive-history eviction, and legacy filename migration—not crash injection or
+scope-finalizer flush.
+
+## Provider logs are diagnostic, not recovery truth
+
+Provider runtime NDJSON logging declares itself best-effort. It rotates at 10 MiB
+with ten files by default, applies age and total-size retention, buffers writes, and
+deliberately excludes high-rate canonical deltas and progress events. Drain clears
+the pending batch even when writes fail, while setup failure degrades to a no-op
+logger. A hard crash can lose the buffered window.
+
+The ordinary server logger emits pretty stdout plus the observability trace logger.
+The pinned logging-path audit found no separate authoritative `server.log` event
+ledger. Local trace rotation and optional OTLP export support diagnosis. Durable “work log” activities are a
+different system: runtime ingestion dispatches activity commands into SQLite
+projection rows, and revert can prune those derived rows.
+
+## Checkpoints live in hidden Git refs
+
+Checkpoint refs use `refs/t3/checkpoints//turn/`. Capture
+creates an isolated temporary Git index, seeds it from HEAD, runs `git add -A`, writes
+a tree and parentless commit, then updates the hidden ref. SQL checkpoint metadata is
+dispatched afterward, so a crash can leave an orphan ref.
+
+Restore reads the hidden tree into worktree and index, cleans untracked files, then
+resets the index toward HEAD. Consequently the implementation preserves file content
+on a successful restore but does not preserve the original staged-versus-unstaged
+boundary. The `CheckpointStore` comment promising “workspace and staging state” is
+too strong.
+
+Revert is an unjournaled saga: restore workspace, refresh, roll the provider back,
+delete later refs, then dispatch durable completion. A crash can leave any prefix of
+those mutations. The inspected thread-deletion handler does not delete checkpoint
+refs, and the pinned startup-path audit found no ref/SQL reconciliation pass.
+
+
+ `git add -A` in an isolated index collapses both staged and unstaged file content
+ into one checkpoint tree. Restore can reproduce that tree's files, but its final
+ index reset cannot recreate where the user had drawn the staging boundary.
+
+
+## Tombstones are not a retention policy
+
+Several mechanisms remove different kinds of data and must not be merged under the
+word “cleanup”:
+
+
+
+
Different persistence cleanup mechanisms; remaining-state cells include explicitly scoped source-audit findings
the append-only orchestration event history and revert events
+
provider/terminal log retention
rotates, caps, or evicts independent log/history files
SQLite domain history unless a separate domain operation changes it
+
in-memory fold caps
limits current message/checkpoint collections in memory
SQL rows; the cap is not database garbage collection
+
+
+
+
+The event-store API exposes append and reads, not event-history deletion. The receipt
+repository exposes upsert/get, not a retention pass. Provider-binding deletion exists
+in the repository interface, but a pinned-source search found no production caller;
+that is an absence-of-code inference, not a permanent product promise.
+
+## What startup repairs
+
+
+
+
Eager, lazy, and absent startup reconstruction
+
Timing
State
Limit
+
+
before server boot
restore-marker-directed recovery of an interrupted SQLite-triplet rollback
only named database files
+
eager
migrations, projector cursors, optimized command model
projector read defaults to 1,000 total events
+
eager best-effort
projected active provider sessions missing from live adapter inventory become stopped/error
retry once; does not replay the original turn
+
delayed/current-state
agent awareness republishes active threads after activation
not historical event replay
+
lazy
provider binding adoption/resume and terminal text restoration
requires a later routed operation or first terminal open
power loss, WAL checkpoint policy, crash halfway through the full runner
+
projection bootstrap
three-event fold and repeat-at-cursor idempotency
1,001-event tail and serving while a projector remains behind
+
service updater
prepared-before-commit, id validation, main/WAL/SHM rollback
kill and restart at every marker/restore/state-write edge
+
settings/secrets
redaction, roundtrip, races, file permissions
crash between secret mutation and JSON replacement; malformed-file repair
+
attachments
real revert cleanup and SQL rollback
orphan collection, external delete followed by outer rollback, crash after cursor advance
+
terminal
normal persistence, sanitizer, cap, eviction, migration
torn overwrite, hard crash in debounce window, general-scope drain
+
checkpoints
diffs and basic hidden-ref behavior
staging-boundary roundtrip, orphan-ref startup repair, crash during revert saga
+
+
+
+
+
+ The service updater demonstrates the useful pattern: stop concurrent writers,
+ stage recoverable state, persist a phase marker before mutation, make restoration
+ retryable, and clear backup state only after a terminal record. Settings, secrets,
+ attachments, checkpoint refs, provider calls, and terminal writes need their own
+ scoped variants if a meta-harness wants stronger recovery guarantees.
+
+
+
diff --git a/src/content/book/90-commands-invariants.mdx b/src/content/book/90-commands-invariants.mdx
new file mode 100644
index 0000000..07244a9
--- /dev/null
+++ b/src/content/book/90-commands-invariants.mdx
@@ -0,0 +1,294 @@
+---
+slug: commands-invariants
+order: 90
+number: "9"
+kind: chapter
+part: Part III · Transactional domain core and post-commit delivery
+partOrder: 3
+title: Commands, invariants, and the boundary of atomicity
+shortTitle: Commands and invariants
+summary: Client commands are authenticated and normalized before a serialized, Effectful decider plans events; an existing-thread turn commits atomically, while first-turn bootstrap is a WebSocket saga with narrower compensation.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Distinguish client wire commands, dispatchable commands, and trusted internal commands.
+ - Locate authorization, normalization, decision, and SQL transaction boundaries.
+ - Derive the exact two-to-four-event batch for a turn on an existing thread.
+ - Contrast that atomic batch with the WebSocket-only bootstrap saga and its compensation limits.
+keywords: [commands, invariants, normalization, decider, aggregate, transaction, bootstrap, compensation]
+sourceAreas: [packages/contracts/src/orchestration.ts, apps/server/src/orchestration/Normalizer.ts, apps/server/src/orchestration/decider.ts, apps/server/src/orchestration/Layers/OrchestrationEngine.ts, apps/server/src/ws.ts, apps/server/src/orchestration/http.ts]
+visuals: [command boundary map, existing-turn event batch, bootstrap saga, failure-position explorer]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import CommandBoundaryLab from "../../components/CommandBoundaryLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+A command is a request to change orchestration state. It is not yet a fact, and it
+is not a direct provider call. The server first turns an accepted client shape into
+a dispatchable command, decides that command against its current domain model, and
+only then opens the transaction that makes the resulting facts durable.
+
+That order is the key to this part of the book:
+
+> authorize → normalize → queue → deduplicate → decide → transact → publish → react
+
+For an accepted receipt miss, crossing the first six boundaries through a successful
+transaction establishes durable intent. A receipt hit refers to intent committed
+earlier, while a rejected command establishes no accepted intent. Provider execution
+begins later.
+
+## “Command” names three different shapes
+
+The contracts deliberately separate what a client may send from what the engine may
+receive.
+
+
+
+
+
+
Client, dispatchable, and internal orchestration command boundaries
+
Shape
Who can construct it
What changes before the next boundary
+
+
ClientOrchestrationCommand
authenticated HTTP or WebSocket client
timestamps, workspace paths, and attachment data still need server normalization; turn start may include bootstrap instructions
+
DispatchableClientOrchestrationCommand
server after client normalization
raw attachment data has become persisted attachment metadata; the shape is ready for the domain engine
+
InternalOrchestrationCommand
trusted server/provider reactors
assistant deltas, sessions, plan updates, activities, diff completion, and similar runtime results re-enter the same domain engine
+
+
+
+
+“Client” and “internal” are admission boundaries, not a complete actor history.
+Events later infer an actor class from command-id prefixes and provider metadata.
+Do not interpret the internal union as “commands that are not serialized” or
+“commands that bypass invariants”: both dispatchable client commands and trusted
+internal commands enter the engine's command union.
+
+
+ HTTP dispatch requires orchestration:operate, then normalizes its
+ payload. WebSocket RPC authorization likewise checks the method's required scope
+ before the dispatch handler normalizes the command. Internal reactor dispatch is a
+ separate trusted path; it is not made client-reachable by belonging to the engine
+ union.
+
+
+## The full command boundary
+
+
+ A[schema + scope]
+ A --> N[normalize + stage]
+ I[trusted internal command] -.trusted path.-> Q[serialized queue]
+ N --> Q
+ Q --> R{receipt?}
+ R -->|hit| X[stored outcome]
+ R -->|miss| D[decide against read model]
+ D --> T[SQL transaction]
+ T --> E[append event batch]
+ E --> P[synchronous projections]
+ P --> K[accepted receipt]
+ K --> M[in-memory fold]
+ M --> H[hot PubSub]
+ H --> Z[async reactors]
+ H --> S[return last sequence]`} />
+
+
+The queue is an important concurrency boundary. The implementation creates one
+unbounded command queue and drains it with one worker. Every command therefore sees
+the command read model produced by the commands before it. This is how an invariant
+check closes a race: two clients may submit concurrently, but they do not decide
+concurrently against the same stale model.
+
+
+ The event store assigns a per-stream version when inserting, but dispatch does not
+ ask the caller for an expected version. Correctness at this boundary comes from the
+ single engine worker and its authoritative in-memory model, not from rejecting an
+ expected-version mismatch supplied by a client.
+
+
+## Normalization can perform irreversible work early
+
+Normalization does more than validate syntax. It replaces client timestamps with the
+server receipt time, canonicalizes project workspace roots, validates image data,
+creates attachment identifiers, creates directories, and writes attachment bytes.
+
+
+
+That placement has two consequences:
+
+1. An invariant rejection or later SQL failure does not automatically roll back the
+ staged attachment bytes.
+2. Every client retry normalizes before receipt lookup. With no accepted receipt it
+ can allocate new attachment identifiers before trying the transaction again; even
+ an accepted same-id retry can stage new files before the engine returns the old
+ receipt result.
+
+
+ “Atomic” in the existing-turn path refers to the SQL event, projection, and
+ receipt writes. It does not include normalization-time directories and attachment
+ bytes. Chapter 13 maps file retention and cleanup in detail.
+
+
+## The decider is isolated, but not a deterministic pure function
+
+The decider receives a normalized command and the current command read model. It
+does not query SQLite, write files, or call a provider. It does, however, run in
+Effect: some cases read the clock, and every planned event receives a random UUID.
+
+
+ The decider's dependency surface is domain state plus clock and crypto services.
+ The engine invokes it before sql.withTransaction. If a persistence
+ failure leaves no receipt, a retry decides again and may allocate different event
+ identifiers or times even though the command id is unchanged.
+
+
+Within a turn start, the decider enforces four concrete rules or transitions:
+
+
+
+
Invariant checks and lifecycle transitions for thread turn start
+
Question
Decision
Why it belongs here
+
+
Does the target thread exist?
missing thread rejects the command
the serialized model is authoritative at decision time
+
Was a proposed plan named?
the referenced plan must exist on its source thread
a structurally valid id is not proof that the plan is present
+
Does that plan cross projects?
source and target thread must belong to the same project
the decider can inspect related aggregates in one model
+
Was the target settled or snoozed?
emit lifecycle-reset events before message and turn intent
valid activity evolves state; it is not always a rejection
+
+
+
+
+
+ A WebSocket cleanup comment says archived threads reject turn starts, but the
+ pinned thread.turn.start case calls the general thread-presence check,
+ not the explicit “not archived” helper. The executable case shown here therefore
+ does not support that blanket invariant. Treat it as a source discrepancy to test,
+ not as a guaranteed rule.
+
+
+## An existing-thread turn is one two-to-four-event decision
+
+
+
+For a normal turn on an existing thread, the planned batch is:
+
+1. optional `thread.unsettled`;
+2. optional `thread.unsnoozed`;
+3. mandatory `thread.message-sent`;
+4. mandatory `thread.turn-start-requested`, causally linked to the message event.
+
+The engine opens one SQL transaction only after that complete batch exists. It
+appends each event, folds it into a temporary command model, applies all synchronous
+SQLite projectors, and finally upserts one accepted receipt whose result is the last
+event's global sequence. A failure in any one of those SQL operations rolls back the
+whole batch.
+
+
+ No observer can durably see only the user message from this command while the
+ turn-start request is absent. The optional lifecycle resets, message, turn intent,
+ their transactional projections, and the accepted receipt either commit together
+ or roll back together.
+
+
+
+ The returned sequence is the last event in this batch. It does not mean a provider
+ harness has accepted the prompt, started a process, streamed a token, or completed
+ the turn. Those are post-commit reactor and runtime-ingestion concerns.
+
+
+## First-turn bootstrap is a WebSocket saga
+
+The first turn can ask the server to create a thread, prepare a Git worktree, run a
+setup script, and then start the turn. Those operations cannot share the
+existing-turn transaction: Git and process launch are external effects, and several
+domain subcommands each commit independently.
+
+
+ N[normalize]
+ N --> A[Tx A: thread.create]
+ A --> G[Git fetch + create worktree]
+ G --> B[Tx B: thread.meta.update]
+ B --> R[refresh Git status]
+ R --> U[launch setup process]
+ U --> C[Tx C: final turn.start]
+ U -.launch failure is logged; continue.-> C
+ A -.later non-interruption failure.-> D[Tx D: thread.delete]
+ G -.worktree remains.-> D
+ C --> Q[last sequence to WS client]`} />
+
+
+The saga's real boundaries are easy to miss:
+
+- `thread.create`, `thread.meta.update`, and compensation use fresh
+ server-generated command ids; the final turn retains the client's outer id.
+- Setup launch failure is converted into best-effort activity plus a warning, then
+ the final turn is still attempted.
+- Cleanup runs only when this request successfully created the thread and the later
+ failure is not interruption-only.
+- Successful cleanup reports a `deleted` disposition, but it adds a thread tombstone;
+ it does not erase the committed history.
+
+
+ The compensation function dispatches thread.delete. The shown failure
+ path contains no worktree or branch removal. That is an absence-of-code inference
+ at the pinned revision, not a promise that future cleanup will remain narrow.
+
+
+
+ The HTTP endpoint accepts the same client command schema, requires operate scope,
+ normalizes, and dispatches directly to the engine. Only the WebSocket helper
+ branches on thread.turn.start with bootstrap to run the
+ create/worktree/setup saga. Sending that payload over HTTP does not execute the
+ WebSocket bootstrap choreography.
+
+
+
+ Individual engine subcommands have receipt semantics, but there is no receipt for
+ the saga as a whole. A byte-for-byte retry can attempt thread creation and Git work
+ again rather than replaying one completed outer result. Treat “same command id” as
+ an engine-transaction guarantee, not as a distributed-saga guarantee.
+
+
+## Explore the failure boundary
+
+The same word—“failed”—can describe a request rejected before decision, SQL rolled
+back after decision, a hard crash after commit, or a bootstrap failure after Git
+state exists. Move the boundary and compare what survives.
+
+
+
+The durable unit is deliberately small: an accepted engine command and its event
+batch. Everything before it can leave staging residue; everything after it can miss
+hot delivery; and the bootstrap path composes several such units with external
+effects. Chapter 10 now examines the receipt and publication windows inside that
+unit.
+
+
From 93c8ccde1d3f80168bd56cedddba506ada36afe9 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:08 +0300
Subject: [PATCH 05/12] docs(book): publish part four provider harnesses
---
.../book/140-provider-adapter-contract.mdx | 259 ++++++++++++++
.../book/150-provider-instances-routing.mdx | 331 ++++++++++++++++++
.../book/160-codex-app-server-json-rpc.mdx | 134 +++++++
src/content/book/170-claude-agent-sdk.mdx | 133 +++++++
src/content/book/180-acp-cursor-grok.mdx | 203 +++++++++++
.../book/190-opencode-normalization.mdx | 228 ++++++++++++
src/content/book/200-usage-accounting.mdx | 285 +++++++++++++++
7 files changed, 1573 insertions(+)
create mode 100644 src/content/book/140-provider-adapter-contract.mdx
create mode 100644 src/content/book/150-provider-instances-routing.mdx
create mode 100644 src/content/book/160-codex-app-server-json-rpc.mdx
create mode 100644 src/content/book/170-claude-agent-sdk.mdx
create mode 100644 src/content/book/180-acp-cursor-grok.mdx
create mode 100644 src/content/book/190-opencode-normalization.mdx
create mode 100644 src/content/book/200-usage-accounting.mdx
diff --git a/src/content/book/140-provider-adapter-contract.mdx b/src/content/book/140-provider-adapter-contract.mdx
new file mode 100644
index 0000000..39e0dcb
--- /dev/null
+++ b/src/content/book/140-provider-adapter-contract.mdx
@@ -0,0 +1,259 @@
+---
+slug: provider-adapter-contract
+order: 140
+number: "14"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: The ProviderAdapter contract
+shortTitle: ProviderAdapter contract
+summary: ProviderAdapter is the narrow provider-native command and event boundary; ProviderService supplies instance routing and recovery, while orchestration remains the durable product model.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Separate the adapter SPI from ProviderService, orchestration, and each native transport.
+ - Read every operation, return value, error family, and declared capability without inventing a universal feature matrix.
+ - Trace a durable turn intent into one adapter call and canonical runtime events back into durable commands.
+ - Identify acknowledgement, hot-stream, logging, and recovery boundaries precisely.
+keywords: [ProviderAdapter, ProviderService, provider runtime, canonical events, sessions, turns, capabilities, errors]
+sourceAreas: [apps/server/src/provider/Services/ProviderAdapter.ts, apps/server/src/provider/Layers/ProviderService.ts, apps/server/src/provider/Errors.ts, packages/contracts/src/provider.ts, packages/contracts/src/providerRuntime.ts, apps/server/src/orchestration/Layers/ProviderCommandReactor.ts, apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts]
+visuals: [adapter boundary map, operation ledger, canonical event funnel, round-trip swimlane, interactive contract tracer]
+updatedAt: 2026-08-24
+---
+
+import AdapterContractLab from "../../components/AdapterContractLab.astro";
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+`ProviderAdapter` is not T3 Code's domain model and it is not a transport
+protocol. It is the server-side boundary that gives five very different native
+runtimes one callable shape and one event vocabulary.
+
+That placement explains both its power and its limits:
+
+- orchestration decides and durably records product intent;
+- `ProviderService` resolves a configured instance and owns durable session
+ bindings, recovery policy, MCP preparation, logging, and event fan-in;
+- one `ProviderAdapter` owns native process/session behavior;
+- `ProviderRuntimeIngestion` translates the adapter's hot canonical events back
+ into internal orchestration commands.
+
+The adapter therefore normalizes **how to talk to a harness**. It does not make
+the harness itself durable, and it does not prove every provider supports every
+event in the canonical union.
+
+## The contract is deliberately smaller than the product
+
+
+
+
+
+The complete shape has fourteen required members and one optional operation.
+Reading them by responsibility is more useful than reading them in file order.
+
+
+
+
ProviderAdapter operations, immediate results, and boundaries
manage adapter-owned live sessions keyed by T3 thread id
that native state is stored in the orchestration transaction
+
Turn control
sendTurn, interruptTurn
request a native turn start or interruption
assistant output or completed interruption
+
Human interaction
respondToRequest, respondToUserInput
resolve a pending native approval or structured-input request
that every provider can originate both request kinds
+
Conversation state
readThread, rollbackThread
read or mutate the provider's own thread representation
that rollback has identical semantics or support everywhere
+
Optional extension
uploadFeedback?
provider-specific feedback upload when present
a negotiated generic extension mechanism
+
Output
streamEvents
one canonical hot runtime-event stream
durable replay, delivery acknowledgement, or provider conformance to every event variant
+
+
+
+
+`sendTurn` returns `ProviderTurnStartResult`: a T3 `threadId`, a `turnId`, and an
+optional opaque resume cursor. It does not return generated text. Text, tool
+activity, plan changes, requests, usage, completion, and failures arrive later on
+`streamEvents`.
+
+
+ There is no steer member in the SPI. A provider can interpret
+ sendTurn while a turn is already active as steering—OpenCode has a
+ concrete tested implementation—but the generic contract alone promises only a
+ send request. Chapters 16–19 document each adapter's behavior separately.
+
+
+## A session uses T3 identity and opaque native continuation
+
+The public `ProviderSession` is centered on `threadId`. It also carries the driver
+and, during the ongoing migration, an optional configured instance id, plus status,
+runtime mode, working directory, model, active turn, timestamps, last error, and an
+opaque `resumeCursor`. Runtime routing promotes legacy persisted bindings before it
+requires that instance identity.
+
+The opaque cursor is an important design choice. Codex can store app-server resume
+material, Claude can store SDK/transcript position, ACP adapters can store an ACP
+session id, and OpenCode can store a versioned `ses_…` token without forcing their
+native identities into one false universal schema. `ProviderService` can persist
+the envelope while the owning adapter remains responsible for decoding it.
+
+
+ A persisted binding maps a T3 thread to a configured provider instance and can
+ retain an opaque resume cursor. The adapter instance, child process, SDK query,
+ queues, and stream subscriptions live in an Effect scope. A binding can survive
+ while no adapter session is active; a later allowed operation may adopt or resume
+ it.
+
+
+## Capability negotiation is only one field today
+
+The declared capability object contains one property:
+`sessionModelSwitch: "in-session" | "unsupported"`. It tells the provider reactor
+whether a model change may stay within an existing native session or requires a
+restart.
+
+That is the entire SPI capability surface at this revision. Approvals, structured
+input, plans, tasks, skills, usage telemetry, rollback, steering, and native modes
+must not be inferred from this one object. They are concrete adapter behaviors,
+sometimes expressed through typed failure, sometimes absent, and sometimes
+implemented through provider-specific extensions.
+
+
+ Codex, Claude, Cursor, Grok, and OpenCode all advertise
+ sessionModelSwitch: "in-session" at the pinned commit. The
+ unsupported branch is real reactor logic and part of the contract,
+ but it is not a current five-provider difference.
+
+
+## Canonical events are a grammar, not a checklist
+
+
+
+Every runtime event shares an id, driver kind, T3 thread, timestamp, optional
+instance/turn/item/request ids, provider references, and optional raw provenance.
+The union then supplies typed payloads for session, thread, turn, content, item,
+approval, structured input, task, hook, tool, authentication, account, MCP, model,
+configuration, file, warning, and error events.
+
+That breadth provides a stable **target vocabulary** for adapters and consumers.
+It is not proof of feature parity. For example, the schema can represent token
+usage for any provider, while Chapter 20 verifies that only Codex and Claude emit
+that event at this revision.
+
+
+ |hot domain event| R[ProviderCommandReactor]
+ R --> S[ProviderService]
+ S -->|thread → instance| A[ProviderAdapter]
+ A --> N[native runtime]
+ N -.native messages.-> A
+ A -.ProviderRuntimeEvent.-> S
+ S -.correlate + log + PubSub.-> G[Runtime ingestion]
+ G -->|internal command| E[orchestration engine]
+ E --> D[(durable events + projections)]
+ A -->|start result only| S
+ S -->|start result only| R`} />
+
+
+
+ ProviderService may write canonical runtime events to a best-effort NDJSON
+ logger before publishing them. Runtime ingestion does not replay that file as a
+ delivery queue. Durable product state appears only when ingestion successfully
+ dispatches internal commands through the orchestration engine.
+
+
+## ProviderService owns the cross-provider policy
+
+The adapter's contract intentionally omits several product concerns that
+`ProviderService` supplies:
+
+- decode and validate public inputs;
+- resolve `threadId → persisted binding → providerInstanceId → live adapter`;
+- prepare and revoke the T3 MCP credential for the session;
+- adopt an already-live adapter session or resume from the stored cursor;
+- persist the latest provider binding, runtime mode, model selection, cwd, status,
+ and resume data;
+- stop stale sessions for the same thread on other live instances;
+- correlate every runtime event with the adapter instance that emitted it;
+- fan all instance streams into one provider-runtime PubSub;
+- collect metrics and analytics around the operation.
+
+This is why an adapter should not perform cross-provider orchestration. It knows
+how its native runtime behaves. The service knows how that runtime participates in
+the product.
+
+## The error taxonomy crosses three boundaries
+
+
+
+
Provider adapter, driver, registry, and service error responsibilities
+
Boundary
Typed families
What the distinction preserves
+
+
Adapter
validation, session-not-found, session-closed, request, process
bad T3 input is different from a missing native session, protocol failure, or dead process
+
Driver construction
ProviderDriverError
a configured instance can become an unavailable UI shadow without crashing server boot
routing/configuration failure remains distinct from native adapter failure
+
+
+
+
+One naming wrinkle matters: the live adapter facade currently reports a missing or
+unavailable instance as `ProviderUnsupportedError`, even though a separate
+`ProviderInstanceNotFoundError` type exists. The book follows executed paths, not
+just exported class names.
+
+## Walk the contract boundary
+
+Choose an operation and advance its round trip. The moving token stops at the
+immediate return boundary before continuing along the independent runtime-event
+lane. Try sending while a turn is already running: the lab deliberately refuses to
+label that generic call “steer,” because only each concrete adapter can settle that
+meaning.
+
+
+
+## What the tests establish—and do not
+
+The canonical runtime schema has decoder tests. `ProviderService` tests route
+sessions, recover persisted bindings, validate instance identity, and merge runtime
+events in order. Every concrete adapter has behavior tests for its own transport and
+normalization.
+
+There is no repository-wide executable conformance suite that feeds the same full
+feature script to all five adapters and proves semantic equivalence. That absence
+is appropriate to surface: one interface gives the product a stable integration
+point, while Chapters 16–19 retain provider differences instead of hiding them.
+
+
+ A meta-harness benefits from a small command SPI and a rich canonical event
+ grammar, but only if it retains driver, instance, native references, and raw
+ provenance. Capability claims should be explicit and sparse. Unsupported,
+ emulated, and merely not-evidenced behavior are different product states.
+
+
+
diff --git a/src/content/book/150-provider-instances-routing.mdx b/src/content/book/150-provider-instances-routing.mdx
new file mode 100644
index 0000000..e4d3303
--- /dev/null
+++ b/src/content/book/150-provider-instances-routing.mdx
@@ -0,0 +1,331 @@
+---
+slug: provider-instances-routing
+order: 150
+number: "15"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: Drivers, instances, registries, and multi-instance routing
+shortTitle: Provider instances and routing
+summary: T3 Code separates an open driver kind from configured instance identity, discovery snapshots, durable thread bindings, native sessions, and continuation compatibility so several accounts of one harness can coexist.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Name every provider identity and use only providerInstanceId as the normal routing key.
+ - Derive explicit-versus-legacy settings precedence, enabled state, secret materialization, and unavailable shadows.
+ - Follow instance construction, child-scope replacement, snapshot aggregation, binding lookup, adoption, and resume.
+ - Explain what configuration reload and recovery do not guarantee.
+keywords: [ProviderDriver, ProviderInstance, registry, multi-instance, routing, settings, secrets, resume, recovery]
+sourceAreas: [packages/contracts/src/providerInstance.ts, packages/contracts/src/settings.ts, apps/server/src/provider/ProviderDriver.ts, apps/server/src/provider/Layers/ProviderInstanceRegistryHydration.ts, apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts, apps/server/src/provider/Layers/ProviderAdapterRegistry.ts, apps/server/src/provider/Layers/ProviderRegistry.ts, apps/server/src/provider/Layers/ProviderService.ts, apps/server/src/provider/Layers/ProviderSessionDirectory.ts]
+visuals: [provider identity ladder, settings reconciliation timeline, routing and recovery decision tree, interactive fleet router]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ProviderRoutingLab from "../../components/ProviderRoutingLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+“Which provider owns this thread?” sounds like one lookup. In the implementation it
+is an identity chain:
+
+```text
+T3 thread → persisted provider-instance id → current ProviderInstance
+ → captured adapter → native session / resume cursor
+```
+
+The driver kind travels beside that chain as an implementation and consistency
+label. A native account may be visible in a discovery snapshot. Neither replaces
+the instance id as the normal route.
+
+This separation is what lets `codex_personal` and `codex_work` use the same Codex
+driver with different homes, environment, credentials, processes, snapshots, and
+adapter state.
+
+## The identity ladder
+
+
+
+
+
+
Provider-related identities and their architectural roles
+
Identity
Example
Owner and role
Routing key?
+
+
Driver kind
codex
open branded slug selecting implementation, config decoder, probe, adapter, and text generation
No; it verifies implementation and names the default legacy instance
+
Instance id
codex_work
user-defined slug naming one materialized configuration and its scoped closures
Yes
+
Discovery snapshot
models, auth state, health
UI-facing observation for one instance; cached and refreshed independently
No; its instanceId field points back to the route
+
Native account
provider email or login
provider-reported observation; may affect access and models
No
+
T3 thread id
thread_…
durable product conversation/work item
Entry to the persisted binding lookup
+
Native session id
app-server, SDK, ACP, or ses_…
adapter/provider-owned continuation identity
No; wrapped in an opaque resume cursor
+
Continuation key
codex:instance:codex_work
driver-defined compatibility group used when deciding whether a session may continue across instance changes
No; a compatibility predicate
+
+
+
+
+`ProviderDriverKind` is intentionally not a closed five-literal union. The shipped
+build registers Codex, Claude, Cursor, Grok, and OpenCode, but settings and persisted
+state can outlive a build, move between forks, or refer to a driver that is absent
+after rollback. Schema decoding accepts any valid slug; the runtime makes absence
+visible.
+
+
+ `ServerProvider` declares instanceId as its stable routing identity.
+ The live adapter facade resolves only by instance id. Its legacy
+ listProviders view merely lists driver kinds whose default instance
+ currently exists; it does not restore kind-based routing for custom instances.
+
+
+## Drivers are factories; instances are scoped records
+
+
+
+
+
+A `ProviderDriver` contributes:
+
+- an open `driverKind` and static presentation metadata;
+- a schema for its opaque config envelope and a typed default;
+- a scoped `create` function.
+
+The registry decodes config once, then passes the typed value, instance id,
+presentation overrides, effective enabled flag, and materialized environment into
+`create`. The result is a `ProviderInstance` containing three captured closures:
+discovery snapshot, provider adapter, and text generation.
+
+This record-based SPI avoids the singleton trap. Two Context tags named “Codex”
+would still describe one service per Effect environment; two ordinary instance
+records can each own different Refs, PubSubs, child processes, file watchers, and
+finalizers.
+
+
+ The metadata documentation says a driver may set
+ supportsMultipleInstances: false and the registry will reject a
+ duplicate configuration. At the pinned commit, all built-ins permit multiple
+ instances and the inspected registry reconciliation path never reads that field.
+ Treat it as documented intent, not an enforced runtime guarantee.
+
+
+## Settings hydrate an explicit map over legacy defaults
+
+T3 Code is midway through a provider-instance migration. Settings contain both:
+
+- legacy typed blobs under `settings.providers.`;
+- a new `providerInstances` record whose keys are instance ids and whose values
+ carry driver, opaque config, generic environment, enabled state, and optional
+ presentation.
+
+Hydration begins with the explicit map. For each built-in driver it computes the
+default instance id—literally the driver slug. Only if that key is absent does it
+synthesize an instance from the legacy blob. An explicit `providerInstances.codex`
+entry therefore wins over `providers.codex`; a custom `codex_work` exists alongside
+either default.
+
+Enabled state has its own precedence. Explicit `false` in the instance envelope or
+decoded driver config always disables. Otherwise the envelope value wins, then the
+driver config value, then the default is enabled.
+
+
+ H[explicit map + missing legacy defaults]
+ H --> Q{unchanged live identity?}
+ Q -->|yes| K[retain existing scoped instance]
+ Q -->|no| C[close prior child scope if present]
+ C --> X{replacement still configured?}
+ X -->|no · removed| M[set live Ref, then unavailable Ref]
+ X -->|yes| D{driver + config valid?}
+ D -->|yes| L[create scoped live instance]
+ D -->|no| U[build unavailable shadow]
+ K --> M
+ L --> M
+ U --> M
+ M --> G{entries, order, or shadows changed?}
+ G -->|no| N[no change tick]
+ G -->|yes| T[publish one registry change tick]
+ T --> P[discovery registry re-pulls]
+ T --> A[ProviderService re-subscribes]
+ B[(durable thread binding)] -.next eligible operation.-> R[adopt or resume]
+ A --> R`} />
+
+
+
+ The old changed scope closes before the replacement is built. Its native process
+ and event stream may stop during that interval. The durable binding remains, but
+ recovery is operation-triggered and needs a current live instance plus either an
+ already-adoptable session or persisted resume state. Configuration reload is not
+ a zero-downtime handoff protocol.
+
+
+## Unavailable shadows preserve configuration truth
+
+Three construction failures become UI-visible shadows instead of server-boot
+failures:
+
+1. the driver slug is valid but not registered in this build;
+2. the registered driver's config decoder rejects the opaque value;
+3. driver creation returns `ProviderDriverError`.
+
+The shadow is disabled, not installed, in error status, has unknown auth, carries
+the exact unavailable reason, and appears in discovery aggregation. It is excluded
+from live adapter lookup and `listInstances`, so a caller cannot accidentally route
+a turn to a presentation-only object.
+
+This is rollback/fork tolerance: “I understand the saved shape but cannot run it”
+is represented as product state, not a corrupt settings file.
+
+## Environment values are materialized, not sandboxed
+
+Every instance can define generic environment entries. Sensitive entries are
+persisted in the server secret store under a name derived from instance id and
+variable name. Client-facing settings replace their value with an empty string and
+set `valueRedacted: true`; server-internal settings materialize the secret before
+driver construction. A redacted patch preserves the existing secret.
+
+At process construction, the materialized instance environment overlays inherited
+`process.env`. An intentional empty string overrides the inherited value. This is
+ordinary child-process configuration—not isolation from the parent environment.
+
+
+ Non-sensitive values persist in the settings document. Sensitive values are
+ split into the secret store and redacted on client reads. The mechanism does not
+ make every provider config field secret automatically.
+
+
+
+ The OpenCode config schema labels serverPassword as stored in plain
+ text on disk; its password-shaped UI input is not encryption. When an external
+ OpenCode URL is configured, the adapter passes that value as Basic
+ authentication. A locally launched managed OpenCode server follows a different
+ process-ownership path.
+
+
+## Discovery and routing are two registries
+
+`ProviderRegistry` aggregates **snapshots** for presentation: availability, models,
+authentication, health, maintenance state, and per-instance refresh streams. It
+can hydrate a correlated cache at boot and retain selected prior model metadata
+while a fresh probe is partial.
+
+`ProviderAdapterRegistry` resolves **live behavior**. It is a stateless facade over
+the instance registry: `getByInstance`, `getInstanceInfo`, `listInstances`, and a
+change stream. Snapshot presence is not sufficient to route; a shadow is visible
+to the first registry and absent from the second.
+
+These surfaces all key by instance id, but their correlation checks live at different
+boundaries. `ProviderRegistry` validates that a discovery snapshot's driver and
+instance match its source. `ProviderService` performs the corresponding check for hot
+runtime events. `ProviderAdapterRegistry` is the exact live lookup/list/change facade;
+it does not perform either payload-correlation check.
+
+## The durable route is recovered lazily
+
+For an operation such as send, interrupt, approval, structured input, or rollback,
+`ProviderService` performs this decision:
+
+1. load the persisted binding for the T3 thread;
+2. require its instance id and resolve that exact current adapter;
+3. if the adapter already has the thread session, route immediately;
+4. otherwise, when recovery is allowed, invoke the recovery helper; it rechecks the
+ adapter and can list, adopt, and upsert an existing session;
+5. if that recheck finds no listed session, require an opaque resume cursor;
+6. restore persisted cwd/model/runtime mode, prepare MCP, and call
+ `startSession` with that cursor;
+7. validate the returned driver and update the durable binding.
+
+`stopSession` deliberately does not recover a stopped process merely to stop it.
+Starting a new session also stops stale live sessions for the same T3 thread on
+other current instances.
+
+
+ B{persisted binding?}
+ B -->|no| X[validation failure]
+ B -->|yes| I[resolve exact instance adapter]
+ I -->|missing or shadow| U[route failure]
+ I -->|live| H{adapter has session?}
+ H -->|yes| A[route to active live session]
+ H -->|no| Q{recovery allowed?}
+ Q -->|no · stop| Z[mark stopped without restart]
+ Q -->|yes| J{recovery recheck finds listed session?}
+ J -->|yes| A2[adopt listed live session]
+ A2 --> W[upsert durable binding]
+ J -->|no| C{resume cursor?}
+ C -->|no| X
+ C -->|yes| R[startSession with saved cursor + mode + cwd + model]
+ R -->|identity valid| W
+ R -->|native error| F[propagate typed failure]`} />
+
+
+Default continuation identity includes both driver and instance. Codex and Claude
+can override it with home/layout-derived groups, so a same-driver instance switch is
+compatible only when both driver kind **and** continuation key match. A shared
+native login by itself is not enough evidence.
+
+## Route a conceptual provider fleet
+
+Use the lab to compare a legacy default, an explicit same-driver work instance, an
+unknown-driver shadow, a removed instance with an old binding, and a rebuilt
+instance with or without resume state. The animation separates settings
+materialization from live lookup and lazy recovery.
+
+
+
+## Evidence and remaining seams
+
+Registry tests construct two Codex instances and verify distinct adapter,
+text-generation, and snapshot closures. They cover nested enabled-state precedence
+and boot-safe unknown-driver shadows. Settings tests verify that marked sensitive
+environment values do not enter `settings.json`, while redacted updates preserve
+the stored secret. ProviderService tests exercise persisted routing, resume,
+stale-session cleanup, event correlation, and input validation.
+
+The inspected tests do not inject a hard crash into every instruction of child-scope
+replacement and subsequent lazy recovery. They also do not enforce
+`supportsMultipleInstances: false`, because the production registry does not yet
+implement that documented branch.
+
+
+ A multi-harness orchestrator should use separate types for driver kind, configured
+ instance, native account observation, durable task/thread, native session, and
+ continuation group. Persist the configured instance route, preserve unavailable
+ entries, and require an explicit migration policy before moving live work between
+ accounts.
+
+
+
diff --git a/src/content/book/160-codex-app-server-json-rpc.mdx b/src/content/book/160-codex-app-server-json-rpc.mdx
new file mode 100644
index 0000000..8d00e36
--- /dev/null
+++ b/src/content/book/160-codex-app-server-json-rpc.mdx
@@ -0,0 +1,134 @@
+---
+slug: codex-app-server-json-rpc
+order: 160
+number: "16"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: Codex through app-server JSON-RPC
+shortTitle: Codex app-server
+summary: Codex runs as a local app-server child speaking typed JSON-RPC over stdio; T3 translates its volatile native requests and notifications into canonical runtime events without making provider delivery durable.
+status: source-checked
+gates: [sources, interaction]
+objectives: [Trace the Codex process and JSON-RPC lifecycle, Distinguish native app-server shapes from canonical T3 events, Locate resume models plans approvals and live usage, Separate durable intent from volatile provider state]
+keywords: [Codex, app-server, JSON-RPC, stdio, resume, approvals, plans, token usage]
+sourceAreas: [packages/effect-codex-app-server/src, apps/server/src/provider/Layers/CodexAdapter.ts]
+visuals: [Codex JSON-RPC normalization flow, native-to-canonical boundary ledger]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import CodexRpcBoundaryLab from "../../components/CodexRpcBoundaryLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Codex is not called by a browser or mobile client. T3 starts a local `codex
+app-server` child, speaks JSON-RPC over stdio, and owns the translation from its
+native protocol to the product’s `ProviderRuntimeEvent` stream. Process handles,
+pending RPC requests, and notification queues remain volatile. Durable truth still
+enters through orchestration commands and runtime ingestion.
+
+## A typed stdio client separates the child from the product
+
+
+
+The transport tracks pending requests by JSON-RPC id, queues notifications, routes
+incoming server requests to handlers, and fails pending work when the stream ends.
+Spawn, process-exit, protocol, transport, and native request failures stay typed
+until the adapter maps them into T3’s shared provider-error taxonomy.
+
+
+ R[provider command reactor]
+ R --> A[Codex adapter]
+ A -->|spawn + stdio| C[Codex app-server]
+ C -->|notifications / requests| A
+ A -->|canonical ProviderRuntimeEvent| H{{hot runtime stream}}
+ H --> I[runtime ingestion]
+ I -->|internal command| E[(event + projections + receipt)]
+ C -. native thread / pending request .-> V[volatile provider state]`} />
+
+
+The stable contract begins at `ProviderRuntimeEvent`, not at an upstream method
+name. A product event is not evidence that T3 persists every native field; an
+unknown upstream notification can be ignored or mapped later without changing the
+client-facing contract.
+
+
+ The protocol package distinguishes process, protocol, transport, and JSON-RPC
+ request errors. The adapter classifies session-runtime process/transport loss,
+ missing native thread identity, and other operation failures as shared adapter
+ validation, session, request, or process errors.
+
+
+## Session start is also resume and configuration
+
+
+
+For one thread, `startSession` validates provider identity, closes any active Codex
+context, and creates a runtime with cwd and runtime mode. It forwards a resume
+cursor only when it matches Codex’s expected shape. Model and service-tier options,
+and later reasoning effort, are forwarded only when selection belongs to the
+adapter’s bound provider instance.
+
+The event consumer starts before the runtime start completes. This prevents an
+immediate native notification from being missed by the adapter; it does not journal
+notifications or make a process crash replay them. The reported
+`sessionModelSwitch: "in-session"` capability only says the adapter can change a
+live model; it does not promise every discovered model/option is accepted upstream.
+
+## Turns and interactions move in opposite directions
+
+`sendTurn` forwards text, attachments, model, reasoning effort, and service tier.
+Interrupt, read, and rollback address a live native context. A missing adapter
+session is not evidence that the durable T3 thread was deleted.
+
+When Codex already has an active turn, the runtime still calls native
+`turn/start`. App-server can accept that follow-up and return a queued native turn
+id, while the session deliberately keeps the current active id as the interrupt
+target. This is queued native-turn behavior—not the generic adapter SPI acquiring a
+`steer` method or reusing one product turn id.
+
+In the reverse direction, app-server requests approval or structured input. The
+adapter emits canonical pending-request observations; client responses return through
+`respondToRequest` or `respondToUserInput`. The native handler and waiting deferred
+are volatile. A product request becomes durable only after runtime ingestion commits
+an internal command.
+
+
+
+
+ A durable turn-start command does not prove app-server started, a native request
+ reached Codex, an approval was answered, or a final result crossed the hot stream
+ and committed back into orchestration.
+
+
+## Notification normalization is selective and product-shaped
+
+
+
+Codex item activity becomes assistant content, tool/item lifecycle, and activity
+observations. Native plan material can become a proposed plan or plan delta;
+task/subagent-shaped signals are classified into product activity/task vocabulary.
+Model reroute becomes `model.rerouted`; retryable native error can become
+`runtime.warning`, fatal error `runtime.error`.
+
+Codex token-usage updates feed live per-thread context telemetry. They are not the
+Usage page’s transcript scanner, should not be summed with historical accounting,
+and do not represent subscription billing.
+
+
+ The product contract has a finite event union while Codex emits versioned native
+ methods and payloads. The adapter retains selected diagnostics but does not make a
+ durable, complete archive of every notification or unknown upstream field.
+
+
+Stopping closes the runtime and interrupts its event fiber; listing sessions reads
+the adapter’s in-memory map. Tests cover instance-bound options, plans, errors,
+approval/input, usage, and consumer lifetime. They do not prove process-crash repair
+between adapter publication and runtime ingestion.
+
+
diff --git a/src/content/book/170-claude-agent-sdk.mdx b/src/content/book/170-claude-agent-sdk.mdx
new file mode 100644
index 0000000..65ac233
--- /dev/null
+++ b/src/content/book/170-claude-agent-sdk.mdx
@@ -0,0 +1,133 @@
+---
+slug: claude-agent-sdk
+order: 170
+number: "17"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: Claude through the Agent SDK
+shortTitle: Claude Agent SDK
+summary: Claude uses an SDK query stream rather than app-server RPC; T3 holds live query context and deferred interactions while normalizing assistant, tool, task, plan, usage, and result observations into one product contract.
+status: source-checked
+gates: [sources, interaction]
+objectives: [Follow Claude query configuration resume steering interruption and cleanup, Map permissions input tools TodoWrite tasks and results, Separate provider-owned stream state from durable T3 history, Explain skills commands and live usage without cross-provider overclaim]
+keywords: [Claude, Agent SDK, query stream, permissions, skills, commands, TodoWrite, tasks, resume, usage]
+sourceAreas: [apps/server/src/provider/Layers/ClaudeAdapter.ts, apps/server/src/provider/Drivers/ClaudeDriver.ts, apps/server/src/provider/Drivers/ClaudeSkills.ts]
+visuals: [Claude SDK message normalization, skill precedence, query lifecycle]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import ClaudeSdkStreamLab from "../../components/ClaudeSdkStreamLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Claude reaches T3 through the Agent SDK query/message stream, not Codex app-server
+and not ACP. The adapter owns a live context per T3 thread: pending approval/input
+deferreds, turn bookkeeping, task state, model/mode, resume metadata, and last-known
+context telemetry. That creates a coherent product surface; it is not a durable
+cross-provider scheduler or replay log of SDK traffic.
+
+## Configure a query, then preserve enough to resume
+
+
+
+
+
+The adapter derives native SDK permission behavior from T3 runtime policy:
+`full-access` maps to `bypassPermissions`, `auto-accept-edits` to `acceptEdits`, and
+`auto` to `auto`. `approval-required` has no mapping, so `permissionMode` is omitted
+and the SDK default applies. Instance-bound model selection controls effort, fast,
+and thinking options only where the selected model supports them.
+
+Resume is metadata, not a replayed T3 transcript. The adapter validates a persisted
+cursor containing the T3 thread id, a provider-native `resume` identifier (while
+accepting legacy `sessionId`), optional `resumeSessionAt`, and a turn count, then
+supplies those hints to a fresh query. T3 durable history remains an independent
+orchestration record.
+
+
+ Q[Claude SDK query]
+ Q -->|assistant · tool · TodoWrite · result| A[Claude adapter]
+ Q -->|approval / input request| D[pending Deferred]
+ D -->|client response| Q
+ A -->|canonical ProviderRuntimeEvent| H{{hot runtime stream}}
+ H --> I[runtime ingestion]
+ I -->|internal command| T[(durable event + projections)]
+ A -. live resume / usage / tasks .-> M[volatile session context]`} />
+
+
+
+ Runtime policy selects SDK permission behavior; it does not create a universal
+ sandbox. Resume gives a later query provider-specific identifiers and context
+ hints, while durable T3 history is separately owned by orchestration.
+
+
+## A second send while work is live is a steer
+
+When a turn is active, a later `sendTurn` continues that live provider turn instead
+of creating a second canonical one. This is an adapter semantic grounded in Claude
+query behavior—not a claim that all providers accept concurrent prompts identically.
+Interruption uses native terminal-result classification, distinguishing explicit
+aborts and cancellation from real provider failures.
+
+
+ A running query and active-turn bookkeeping live in the adapter process. After a
+ crash, durable turn-start intent and any cursor remain, but in-flight SDK messages
+ and uncommitted observations do not become a transaction automatically.
+
+
+## Tools, requests, plans, and tasks are separate translations
+
+
+
+Tool activity maps to canonical item lifecycle. For approval or structured input,
+the adapter stores a deferred, emits a canonical request observation, and resumes the
+native interaction after the client response. Unknown ids are typed request errors.
+The pending maps are not durable inboxes; restart empties them until the provider
+flow creates another request.
+
+TodoWrite and agent/task-shaped messages are interpreted into product plan steps,
+task activity, completion, and selected per-task usage. This feeds T3 work views,
+but does not make a T3 plan control Claude’s scheduler or establish the same task
+vocabulary for every adapter.
+
+
+
+## Skills and commands are discovered native configuration
+
+The Claude driver probes account/capability information and scans native slash-command
+and skill paths with normalized path/preference rules. That enriches a configured
+provider instance. It is distinct from adapter message normalization: discovery does
+not prove a skill loaded for a particular query, and query activity is not proof that
+every installed skill was discoverable.
+
+
+ The driver discovers native metadata for configuration/UI. The adapter interprets
+ SDK activity after a query starts. The inspected paths do not show a durable,
+ universal T3 skill-execution ledger joining both stages.
+
+
+## Live context telemetry is not historical accounting
+
+Claude result and selected message usage can update a per-thread token/context
+snapshot, including input/cache/output/reasoning/tool/duration fields where present.
+This supports the live context meter. Chapter 20’s Usage page independently scans
+provider-owned transcripts, de-duplicates them, and prices where possible; it does
+not use this runtime event as its source of truth.
+
+At the pinned revision only Codex and Claude emit this live telemetry. A generic
+runtime schema can represent future data, but that is not evidence Cursor, Grok, or
+OpenCode emit it now.
+
+`stopSession` closes a Claude context and emits an exit observation; session listing
+and finalization operate on in-memory contexts. Tests cover permission modes,
+model/effort options, assistant/tool mapping, steering, TodoWrite plans, resume,
+approval/input, usage, and cleanup. They do not prove crash recovery of a pending
+SDK deferred or replay of an event published but not yet ingested.
+
+
diff --git a/src/content/book/180-acp-cursor-grok.mdx b/src/content/book/180-acp-cursor-grok.mdx
new file mode 100644
index 0000000..d009164
--- /dev/null
+++ b/src/content/book/180-acp-cursor-grok.mdx
@@ -0,0 +1,203 @@
+---
+slug: acp-cursor-grok
+order: 180
+number: "18"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: ACP transport, Cursor, and Grok
+shortTitle: ACP, Cursor, and Grok
+summary: A shared JSON-RPC runtime gives Cursor and Grok one transport skeleton, while their startup, mode/model, extension, interruption, and rollback semantics deliberately diverge.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace an ACP child from stdio JSON-RPC through a normalized hot runtime event.
+ - Separate negotiated ACP schema affordances from the product adapter’s declared SPI capability.
+ - Compare Cursor’s extensions and local transcript rollback with Grok’s XAI extensions and explicitly unsupported native rollback.
+ - Explain why steering and interruption need provider-specific race handling.
+keywords: [ACP, JSON-RPC, Cursor, Grok, session resume, extension, approval, steering, interrupt, rollback]
+sourceAreas: [packages/effect-acp/src, apps/server/src/provider/acp, apps/server/src/provider/Layers/CursorAdapter.ts, apps/server/src/provider/Layers/GrokAdapter.ts]
+visuals: [ACP session startup sequence, semantic fork matrix, steering race explorer]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import AcpForkLab from "../../components/AcpForkLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Cursor and Grok are not two copies of a generic chat API. Both launch an Agent Client
+Protocol (ACP) child over stdio and reuse T3’s ACP session runtime; after that shared
+transport, their adapter code makes different promises. Those differences matter more
+than the shared method names when a meta-harness decides whether a turn can be resumed,
+steered, cancelled, or rolled back.
+
+This chapter uses **ACP capability** in its protocol sense only: the upstream
+initialization response can describe optional operations such as session load, fork,
+resume, or model switching. It does not mean T3 exposes a dynamic capability-negotiation
+surface to its own callers. The inspected T3 `ProviderAdapter` SPI declares only
+`sessionModelSwitch`; it is reported as `in-session` by these adapters. Do not turn the
+comparison below into a claim that every row is a negotiated runtime feature.
+
+## The reusable ACP machine
+
+`packages/effect-acp` is a typed ACP JSON-RPC client/server bridge. It owns a child
+process stdio transport, routes agent requests back to registered client handlers, and
+exposes typed calls for initialization, authentication, session lifecycle, prompt,
+cancel, configuration, and extension messages. `AcpSessionRuntime` puts the operational
+policy on top: it spawns the child inside a scope, serializes prompts, holds the active
+prompt fiber, parses `session/update` notifications, and gives adapters a queue of
+normalized ACP observations.
+
+
+
+
+ |stdio JSON-RPC| A[effect-acp client]
+ A --> R[AcpSessionRuntime]
+ R --> S[initialize → authenticate → load/new]
+ S --> Q[parsed local event queue]
+ Q --> D[provider-specific adapter]
+ D --> H[hot ProviderRuntimeEvent]
+ H -. internal commands .-> I[runtime ingestion]
+ I --> X[(durable events + projections)]
+ R -. scoped volatile state .-> V[child · prompt fiber · Deferred waits]`} />
+
+
+The runtime gives a resumed session special treatment. It asks ACP for `session/load`
+with the stored native session id and suppresses replay-shaped updates during an
+initial bounded gate. It can complete on an RPC response or an idle replay gap, with a
+default 90-second timeout. That prevents a historical replay from being emitted as a
+new live turn, but it does not make the child’s upstream history a durable T3
+projection.
+
+
+ `AcpSessionRuntime` protects prompt calls with a semaphore. Its cancel path interrupts
+ the active local prompt fiber and asynchronously sends ACP `session/cancel`. This is
+ a best-effort coordination step across a process boundary, not a transaction that
+ proves the agent stopped before a later native update arrives.
+
+
+## Cursor: config-driven mode, typed extensions
+
+Cursor spawns `cursor-agent … acp`, optionally passing a configured endpoint. Its ACP
+runtime advertises Cursor’s parameterized model-picker capabilities; start requires a
+non-empty working directory and may pass a persisted native `sessionId` as a resume
+input. It can add T3’s MCP server to that ACP session when one exists for the thread.
+
+After ACP startup, the adapter first applies the requested model and then configuration
+options. It derives a suitable Cursor mode from T3’s runtime/interaction intent:
+`plan`/`architect` aliases for plan work; approval/implement aliases for ordinary work.
+Those are selection heuristics over Cursor’s returned modes, not universal names
+guaranteed by ACP. A missing matching mode falls back to another available non-plan
+mode or the current mode.
+
+
+
+The native extension surface adds product-specific meaning:
+
+- `cursor/ask_question` parks a Deferred answer, emits canonical
+ `user-input.requested`, and sends a resolved event when T3 responds.
+- `cursor/create_plan` becomes `turn.proposed.completed` with plan markdown.
+- `cursor/update_todos` is normalized into plan updates.
+- standard ACP `session/request_permission` either auto-selects an allowed option in
+ full-access mode or parks a Deferred approval and emits canonical request events.
+
+The Deferred is deliberately hot. A product request becomes durable only after the
+later ingestion command succeeds; a restart cannot re-enter an in-process ACP
+callback by reading the projection alone.
+
+## Grok: a different ACP dialect on the same rails
+
+Grok starts `grok agent stdio`, selects API-key versus cached-token authentication
+from the environment, and adds the `t3code` OAuth referrer. It uses the same base
+runtime lifecycle—initialize, authenticate, load-or-new session, queued updates—but
+wraps it with XAI prompt-completion handling and its own model resolver. A requested
+Grok model is applied with unstable ACP `session/set_model` only when it differs from
+the session setup’s current model.
+
+
+
+Grok’s user-input extension is `x.ai/ask_user_question` (and an underscore-prefixed
+variant). It has a distinct cancellation response shape. Its standard ACP permission
+path is similar to Cursor’s but chooses a compatible permission option rather than
+assuming identical labels. Both adapters map tool, item, content, and plan updates
+into product runtime events, but neither claims that every native field becomes
+durable product data.
+
+## Where shared names fork
+
+
+
+
+
+
Concrete Cursor and Grok adapter behavior, not generic ACP capability claims
+
Concern
Cursor adapter
Grok adapter
+
+
launch
cursor-agent … acp; optional endpoint
grok agent stdio; XAI auth/referrer setup
+
resume
passes a versioned Cursor session id to the shared ACP runtime
passes a versioned Grok session id to the same load-or-new path
+
mode/model
model plus config-option mode heuristic from discovered mode state
compares setup model then calls ACP session/set_model when needed
XAI ask-user extension receives bespoke mapping; ordinary ACP updates can still carry plans
+
running second prompt
reuses active turn id; only the last prompt completion settles it
same intent, with turn-target-aware settlement and interrupted-turn suppression
+
interrupt
cancels ACP and resolves pending approval/input waits
marks a target as interrupted before the lock, filters late notifications, then cancels and settles slots
+
rollback
emulates local rollback by truncating its in-memory turn snapshot
explicitly unsupported: returns a provider request error; no provider-side rollback is invoked
+
+
+
+
+The rollback row is intentionally asymmetric. Cursor’s implementation removes entries
+from its local `ctx.turns` array; source inspection does not find a Cursor ACP native
+revert call in that method. That changes the adapter’s read snapshot, not necessarily
+the provider’s remote history. Grok refuses the operation explicitly. Orchestration
+revert still has its own durable events and projection cleanup (Chapter 13); neither
+adapter row should be read as a transaction across that durable model and native
+provider state.
+
+
+ `interruptTurn` records the targeted id before it takes the per-thread lock, checks
+ that the session and active turn still match, resolves waits, sends cancel, and
+ settles outstanding prompt slots. Notification handling suppresses observations
+ for ids already marked interrupted. The corresponding tests exercise cancellation
+ before late updates and interruption followed by a new turn.
+
+
+Cursor also serializes per-thread start/stop work and tracks prompts in flight, so a
+second prompt becomes a steer instead of a second product turn. Its completion logic
+lets only the final outstanding prompt settle the turn. The shared semantic—one
+product turn while upstream work is running—does not make the races identical.
+
+
+ A meta-harness can share a transport/session kernel, but must keep provider policies
+ explicit: resume cursor schema, callback cancellation response, mode-selection
+ heuristic, late-event filtering, and rollback ownership. A generic “ACP supports
+ it” checkbox is too weak to make a recovery or UX promise.
+
+
+## Evidence boundaries and useful tests
+
+The pinned tests cover model/mode setup, mid-turn steering, cancellation, callback
+responses, and a regression where notification consumers accidentally died when the
+`startSession` caller fiber ended. They do not prove that an arbitrary real Cursor or
+Grok server will expose the same optional ACP capabilities, extension payloads, or
+remote-history semantics. Those are server/version-dependent integrations.
+
+
diff --git a/src/content/book/190-opencode-normalization.mdx b/src/content/book/190-opencode-normalization.mdx
new file mode 100644
index 0000000..3b881db
--- /dev/null
+++ b/src/content/book/190-opencode-normalization.mdx
@@ -0,0 +1,228 @@
+---
+slug: opencode-normalization
+order: 190
+number: "19"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: OpenCode ownership, recovery, and five-provider normalization
+shortTitle: OpenCode and normalization
+summary: OpenCode is an HTTP SDK integration with scoped local-server ownership or external-server attachment; its session adoption and cwd-fork policy show why a common product model must preserve provider-specific recovery semantics.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace OpenCode’s local/external server ownership and session-scoped event pump.
+ - Follow resume adoption, not-found handling, cwd equivalence, and history-preserving fork.
+ - Separate native OpenCode methods from canonical runtime events and durable orchestration state.
+ - Read the five-provider matrix as an evidence ledger, not a feature-negotiation protocol.
+keywords: [OpenCode, SDK, external server, session adoption, fork, cwd, permissions, promptAsync, rollback, normalization, tasks, skills, usage]
+sourceAreas: [apps/server/src/provider/Layers, apps/server/src/provider/opencodeRuntime.ts, apps/server/src/provider/Services/ProviderAdapter.ts, apps/server/src/usage]
+visuals: [OpenCode session ownership lifecycle, resume decision tree, provider normalization matrix]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import OpenCodeRecoveryLab from "../../components/OpenCodeRecoveryLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+OpenCode enters T3 through the OpenCode SDK rather than ACP or Codex app-server
+JSON-RPC. That difference is architectural, not cosmetic: a session may own a locally
+spawned OpenCode server process, or it may attach to an externally managed server whose
+lifetime T3 must not end. In either case, the canonical provider adapter produces the
+same product-shaped hot runtime events—but it does not erase the ownership boundary.
+
+## Transport and server ownership
+
+`OpenCodeRuntime.connectToOpenCodeServer` chooses between an explicit configured
+server URL and a locally started server. The external branch returns a connection with
+no scope-owned exit lifecycle. The local branch starts the OpenCode binary, waits for
+its stdout startup announcement, parses the announced URL, and binds that child to the
+Effect scope supplied by the caller. This startup path does not make a separate HTTP
+readiness probe.
+`OpenCodeAdapter.startSession` creates one session scope and uses it for the server,
+the SDK event subscription, and exit-watch fibers.
+
+
+
+
+ |yes| E[external server handle\nnot scope-owned]
+ U -->|no| L[spawn local OpenCode server\nsession scope owns child]
+ E --> C[SDK client + directory]
+ L --> C
+ C --> S[adopt · fork · or create session]
+ S --> P[event.subscribe pump]
+ P --> H[hot canonical runtime events]
+ H -. internal commands .-> D[(durable orchestration projections)]
+ X[close session scope] --> P
+ X --> L
+ X -. does not stop .-> E`} />
+
+
+The event pump uses an `AbortController` registered as a session-scope finalizer and
+forks its subscriber and local-server exit watcher into that same scope. An unexpected
+local exit is turned into an adapter-side runtime error. This is lifetime management,
+not a persistent process supervisor: after T3 restarts, it must establish a new
+connection and re-adopt a session if one is still available upstream.
+
+## Resume means re-adopt a native session id
+
+T3 persists a versioned cursor containing an OpenCode `sessionId`. On a later
+`startSession`, the adapter first decodes that envelope. An absent, malformed, or
+wrong-version cursor means “no resume” and creates a fresh session without probing an
+upstream id. For a recognized cursor, the adapter probes `session.get`; a confirmed
+404 or exact `NotFoundError` can start fresh. Transport, authentication, and other
+probe failures deliberately propagate: silently starting an empty conversation during
+a transient failure would discard context.
+
+The adapter compares requested and stored working directories carefully. Lexically
+different names that resolve to the same canonical location reuse the native session.
+If the adopted session belongs to a genuinely different cwd, T3 calls native
+`session.fork` into the requested directory, reapplies the current permission rules,
+and persists the new fork’s id. It chooses history continuity over a new empty
+session. This is concrete OpenCode adapter behavior—not evidence that the other
+harnesses offer the same native fork semantics.
+
+
+
+
+ The not-found classifier looks for a numeric 404 or exact `NotFoundError` through a
+ bounded structured-cause walk. A non-404 status seals that branch. Malformed and
+ wrong-version cursors never enter it: the decoder treats them as no resume. Tests
+ cover a stale recognized cursor, a malformed cursor, a transient probe failure, a
+ cwd-changing fork, and lexically different spellings of the same directory.
+
+
+## Turning native OpenCode events into product events
+
+The adapter subscribes to OpenCode’s event stream and routes matching session events
+into a local context. Examples include assistant content deltas, tool-part lifecycles,
+permission questions and replies, user questions and answers, session busy/idle/retry,
+and session errors. They become selected T3 runtime events such as `content.delta`,
+`item.updated`, `request.opened`, `user-input.requested`, `turn.completed`, or
+`runtime.error`.
+
+It is tempting to say “OpenCode session status is the durable thread status.” It is
+not. The subscription and its maps of pending questions, permission requests, emitted
+text, part identities, and active turn are hot adapter state. A canonical runtime event
+crosses another boundary before internal orchestration commands make a durable event
+and update the product’s projections. A crash can therefore leave a native session
+doing work, a stale product projection, and a future recovery attempt that must
+re-adopt rather than replay the old hot stream.
+
+## Turn operations: send, steer, interrupt, ask, read, revert
+
+`sendTurn` requires a `provider/model` slug, normalizes text plus attachments into
+OpenCode prompt parts, and calls `session.promptAsync`. Optional model selection
+options map `agent` and `variant`; a T3 plan interaction chooses the `plan` agent
+unless an explicit agent option wins. A second send while `activeTurnId` exists is a
+**steer**: it calls `promptAsync` again but reuses that existing product turn id.
+Failure of a fresh prompt clears state and emits `turn.aborted`; failure of a steer
+leaves the original active turn intact.
+
+`interruptTurn` calls native `session.abort` and emits a canonical abort observation.
+Permission and question responses are delegated to `permission.reply` and
+`question.reply` only after the adapter confirms the request is still pending. That
+check catches a stale UI action, but it is not a distributed exactly-once protocol.
+
+`readThread` queries native `session.messages` and selects assistant-role entries for
+its adapter snapshot. `rollbackThread` reads those messages, counts assistant entries,
+then invokes native `session.revert` at the remaining assistant message—or without a
+message id to revert the whole native thread when all assistant turns are removed.
+That is real provider-side OpenCode behavior. It remains a separate saga from T3’s
+durable thread-revert path and any workspace/Git checkpoint restoration.
+
+
+
+
+ OpenCode’s `session.revert` changes native conversation state. T3’s orchestration
+ revert has independently durable event/projection and filesystem/Git-ref work.
+ Neither call is wrapped in one transaction, so a meta-harness should expose the
+ reconciliation boundary rather than claiming a single atomic undo.
+
+
+## Five providers, one comparison language
+
+The book’s five harnesses are Codex, Claude, Cursor, Grok, and OpenCode. The matrix is
+an evidence ledger with four cell types: **native mapping**, **adapter behavior or
+emulation**, **explicitly unsupported**, and **not evidenced in the inspected source**.
+It is not the `ProviderAdapter` SPI and not a runtime capability negotiation table.
+The SPI’s declared capability object at this pinned revision contains only
+`sessionModelSwitch`; these five adapters report it as in-session.
+
+
+
+
Source-grounded normalization categories across five providers
+
Operation
Codex
Claude
Cursor
Grok
OpenCode
+
+
transport
native app-server JSON-RPC mapping
native Agent SDK stream mapping
native ACP stdio mapping
native ACP stdio + XAI extension mapping
native SDK/HTTP event mapping
+
resume
adapter passes a Codex resume cursor to app-server runtime
adapter uses SDK resume metadata
adapter passes native session id to ACP load/new flow
adapter passes native session id to ACP load/new flow
a recognized cursor re-adopts sessionId; confirmed miss starts fresh; absent/malformed/wrong-version cursor means no resume; cwd change forks
+
mid-turn send
calls native turn/start; app-server may queue a new native turn id while interrupt still targets the current one
queues into the live SDK loop and reuses the active product turn id
reuses active product turn while prompts are in flight
reuses active turn with target-aware settlement
calls promptAsync and reuses active product turn
+
approval/input
native JSON-RPC requests mapped to canonical request/input events
SDK-side deferred interactions mapped to canonical request/input events
native plans plus multi-agent signals become plan and task.* events
TodoWrite, coordinator, and member observations become plan and task.* events
Cursor plan/todo extensions become plan events; no task.* emission branch found
ACP plan observations become plan events; no task.* emission branch found
plan mode selects a native agent and task-like tools become item activity; no plan or task.* emission branch found
+
commands and skills discovery
snapshot requests native skills and adds a feedback slash command
snapshot combines initialization commands with discovered filesystem skills
snapshot exposes models/probe state; no skills or slash commands found
snapshot exposes models/probe state; no skills or slash commands found
provider inventory exposes skills; no slash commands found
+
live context telemetry
emits canonical token-usage snapshots
emits canonical token-usage snapshots when normalization succeeds
no canonical token-usage emission branch found
no canonical token-usage emission branch found
no canonical token-usage emission branch found
+
historical Usage source
Codex JSONL session transcripts are scanned
Claude JSONL project transcripts are scanned
not scanned
not scanned
not scanned
+
rollbackThread behavior
native app-server rollback mapping
adapter-local turn snapshot truncation plus resume-cursor refresh
adapter-only local snapshot truncation
explicitly unsupported
native session.revert mapping
+
in-session model switch
declared SPI capability: in-session
declared SPI capability: in-session
declared SPI capability: in-session
declared SPI capability: in-session
declared SPI capability: in-session
+
failure projection
typed process/protocol/request failures plus runtime warning/error events
typed SDK/request failures plus terminal result classification
typed ACP failures plus provider-specific callback and cancel handling
typed ACP failures plus late-event suppression around interruption
typed SDK/HTTP failures; when probing a recognized cursor, only confirmed not-found permits fresh-session replacement
+
+
+
+
+An absence cell means no implementation branch was found at this pinned revision;
+it does not claim the upstream product can never expose that feature. Chapters 16,
+17, and 20 provide the longer Codex, Claude, and usage trails. The matrix keeps
+provider discovery (skills/commands), live adapter normalization (plans/tasks and
+context telemetry), and the independent transcript scanner in separate rows so one
+surface cannot masquerade as another.
+
+
+ The adapters can all emit product-shaped session, turn, item, request, and input
+ observations, yet their owned resources and recovery paths differ materially. The
+ source supports common normalization at the adapter edge; it does not support an
+ inference that a portable operation has identical upstream semantics everywhere.
+
+
+
diff --git a/src/content/book/200-usage-accounting.mdx b/src/content/book/200-usage-accounting.mdx
new file mode 100644
index 0000000..4ec50bd
--- /dev/null
+++ b/src/content/book/200-usage-accounting.mdx
@@ -0,0 +1,285 @@
+---
+slug: usage-accounting
+order: 200
+number: "20"
+kind: chapter
+part: Part IV · Five harnesses, one product model
+partOrder: 4
+title: Usage accounting without a false ledger
+shortTitle: Usage accounting
+summary: "The live context meter and historical transcript usage view are deliberately different measurements: one projects the latest usable context snapshot for a thread, while the other scans provider-owned files into a deduplicated, priced historical estimate."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace the live context-window snapshot from provider adapter to the web composer.
+ - Trace historical Claude and Codex transcript records through parsing, deduplication, bucketing, pricing, and environment merge.
+ - Interpret coverage, source fingerprints, cache state, and cost provenance without claiming a billing settlement.
+ - Keep task and subagent accounting as a distinct later concern.
+keywords: [usage, context window, transcripts, pricing, deduplication, cache, RPC, provenance, environments]
+sourceAreas: [packages/contracts/src/providerRuntime.ts, packages/contracts/src/usage.ts, apps/server/src/provider/Layers, apps/server/src/orchestration/Layers, apps/server/src/usage, packages/shared/src/usageMerge.ts, apps/web/src/components/usage, apps/mobile/src/features/usage]
+visuals: [two-lane usage pipeline, usage accounting lab, source-fingerprint merge]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import UsageAccountingLab from "../../components/UsageAccountingLab.astro";
+
+“Usage” names two different questions in T3 Code.
+
+The composer asks: **how full is this thread's context window at the latest valid
+provider update?** The Usage screens ask: **what token-shaped records can this
+environment read from selected provider transcript directories over a historical
+time range?** They share words such as model, tokens, and session, but they do not
+share a source of truth, an update cadence, or a settlement guarantee.
+
+
+ A current context snapshot is not added to the historical Usage total. Conversely,
+ a historical transcript scan cannot certify that the currently displayed context
+ meter is complete, current, or billed. The first is a live operational aid; the
+ second is an evidence-limited accounting estimate.
+
+
+## The live lane projects a current context snapshot
+
+The runtime contract calls the canonical event
+`thread.token-usage.updated`. Its snapshot requires `usedTokens`; it may also carry
+the provider's total processed tokens, maximum context size, input/cache/output
+breakdown, reasoning subset, duration, tool count, and a compaction flag. The event
+also carries provider, thread, time, and optional turn and provider-instance
+identity.
+
+
+
+At this pinned revision, source inspection finds canonical token-usage emission in
+two adapters:
+
+
+
+
Canonical live token usage emission paths found in the pinned provider adapters
+
Adapter
Provider-side input
Canonical emission boundary
Coverage qualification
+
+
Codex
native thread/tokenUsage/updated notification
CodexAdapter normalizes it, rejecting a non-positive usedTokens
emitted when that native notification arrives; total processed is read from native total, current use from native last
+
Claude
normalized Claude SDK message / task-progress usage
emitThreadTokenUsage
emitted only when normalization produced usage; the adapter remembers its last known values
+
Cursor, Grok, OpenCode, ACP adapters
no canonical emission branch found in this revision
—
absence in this audit is not evidence that a provider can never expose usage; it means this build does not project it through this event
+
+
+
+
+
+
+
+
+
+
+The provider service validates the provider/instance association, optionally writes
+the canonical event to its best-effort NDJSON log, and publishes it on the hot
+runtime stream. Runtime ingestion accepts a token-usage event
+only when `usedTokens > 0`, then writes one `context-window.updated` activity with
+the event id as activity id. The web selector walks activities from newest to oldest
+and takes the first valid context-window payload. It derives percentage and
+remaining capacity from `usedTokens / maxTokens` only when a usable maximum exists.
+It does not sum snapshots, recover skipped notifications, or consult transcript
+files.
+
+
+
+
+
+
+ The meter can be absent because no valid activity has reached it, because an
+ adapter has no current emission path, or because the activity has no finite used
+ token value. A snapshot without a usable maximum still renders a raw used-token
+ control; only its percentage, remaining-capacity, and progress visualization are
+ unavailable. Its `totalProcessedTokens` is provider-reported progress
+ metadata—not a cross-thread, cross-day, or cost total.
+
+
+## The historical lane scans files, not orchestration state
+
+The version-4 usage contract intentionally reads selected CLI homes: Claude JSONL
+under a resolved Claude home (preferring `.claude/projects`, with a `projects`
+fallback) and Codex JSONL under the resolved Codex shared-home `sessions` tree. The
+server exposes preaggregated buckets, never raw transcript records. Consequently it
+can include provider turns made outside T3 Code, but it cannot account for a
+provider whose history is not in those two scanned layouts.
+
+
+
+
+
+The time rule has two layers. Daily buckets use the caller-selected IANA time zone
+so a day reflects the user's experience. Hourly queries have exact inclusive/
+exclusive instants and are capped at 24 hours. Candidate files are screened using a
+UTC window with a 36-hour mtime slack, then individual records are parsed and tested
+against the actual requested range. A file's mtime is therefore an optimization
+filter, not the semantic timestamp of a usage record.
+
+Parser rules are provider-specific. Claude accepts assistant records with a usage
+object and deduplicates repeated content-block accounting by message/request key.
+Codex follows turn-context model state and `last_token_usage` deltas; it subtracts
+cached tokens from the reported input count and applies a one-second fork-copy
+suppression heuristic. Codex records carry no global duplicate key: consecutive
+delta suppression and fork-copy suppression are parser-local. That one-second
+threshold is implementation behavior, not a portable provider promise.
+
+
+
+## Deduplicate before bucketing, then price with provenance
+
+The reader streams JSONL files and uses a cache only when path, provider, size, and
+mtime still match. A cache miss or unreadable file does not change the semantic
+deduplication order: after provider-local parser suppression, aggregation removes
+non-null duplicate keys within a file, then removes matching Claude copy/fork keys
+across files, then applies time bounds and buckets by day (and optional hour),
+provider, and model. Totals treat reasoning as a subset of output, not an additional
+token category.
+
+
+
+When a transcript contains a finite provider-reported USD value, that value wins.
+Otherwise the server can multiply uncached input, cache reads, cache creation, and
+output by a LiteLLM rate table. The cache is memory-then-disk, has a 24-hour TTL,
+and falls back to a still-cached table after a rate-fetch failure; without a usable
+table, a model is unpriced. The UI therefore labels the computed value as an API
+estimate. It is not evidence of subscription charges, discounts, enterprise
+contracts, or a provider invoice.
+
+
+
+
+ `providerReported`, `modelPriced`, and `unpriced` describe cost provenance, not
+ payment finality. Cached input and cache creation are distinct from uncached
+ input; reasoning is already inside output and must not be charged again.
+
+
+## Coverage and merge are part of the result
+
+Each environment returns source diagnostics and a fingerprint containing host id,
+provider, resolved home path, and filesystem volume id. The web and mobile clients
+request every connected environment, reject an incompatible contract version, and
+merge only one claimant for an identical non-missing source fingerprint. They sum
+distinct source sessions rather than adding bucket session counts, which could count
+one session repeatedly across day/model buckets.
+
+
+ Each client enumerates its connected environment presentations, requests one usage
+ summary per environment/window, retains pending and failure status per source, and
+ passes answered summaries to the shared versioned merge.
+
+
+
+
+
+
+
+ B{usedTokens > 0?}
+ B -->|yes| C[context-window.updated activity]
+ C --> D[newest valid activity for thread]
+ D --> E[composer context meter]
+ end
+ subgraph H[Historical transcript accounting · selected files]
+ F[Claude / Codex JSONL homes] --> G[parse + normalize]
+ G --> I[within-file and cross-file dedupe]
+ I --> J[time bounds + day/hour buckets]
+ J --> K[reported cost or rate-table estimate]
+ K --> M[environment fingerprint merge]
+ M --> N[historical usage total]
+ end
+ %% Invisible layout constraint only; no data flows between lanes.
+ A ~~~ F`} />
+
+
+There is an important diagnostic limitation at this pinned revision. The contract
+permits `ok`, `missing`, `partial`, and `failed` source states, but the service path
+observed here emits `missing` when the directory is absent—or when the existence
+check itself fails—and otherwise reports `ok`. Directory listing errors are swallowed
+by the reader; unreadable files
+increment skipped-file work but return no records; malformed-record count is
+initialized to zero. An apparently `ok` zero is therefore not a proof that every
+eligible record was readable. Treat coverage indicators as useful evidence, not as
+completeness certificates.
+
+
+
+
+
+
+ The contract has partial and failed states, but this service implementation's
+ observed success path does not surface per-file read/list failures as either. That
+ conclusion compares the declared result vocabulary with the reader/service control
+ flow; it is not a claim that all future implementations behave this way.
+
+
+## Work the accounting lab by hand
+
+The lab keeps the two lanes visible. Its historical miniature has one duplicate
+inside a Claude file, one resumed/forked duplicate across files, and a distinct
+Codex record. Advance the stages to see why the final total is 218 tokens, while a
+live `88,000 / 200,000` context snapshot remains deliberately outside that total.
+
+
+
+## Time, sessions, and scope are constraints—not decoration
+
+- A daily range is inclusive by local day in the supplied IANA zone. An hourly
+ range uses exact `[since, until)` instants and can span at most 24 hours. Invalid
+ IANA input falls back to UTC in the aggregation helper, so callers should supply
+ a real zone rather than infer one after the fact.
+- Transcript session totals describe distinct contributing source sessions. They do
+ not establish a one-to-one mapping to T3 threads, provider native sessions, live
+ context snapshots, invoices, or a user's total work.
+- Scan-home resolution uses legacy Claude/Codex provider settings. It is not a
+ registry-wide scan of every possible provider instance configuration.
+- The scan cache trades repeated parsing for path/size/mtime reuse. It is local
+ implementation state, not an immutable evidence store; cross-file dedupe still
+ runs after cached records are read.
+
+
+ Runtime task/subagent work has its own lifecycle and optional usage-shaped data.
+ This chapter intentionally does not roll it into thread context telemetry or
+ provider-transcript accounting. The planned Chapter 25 will define its identity,
+ fan-out, retries, and attribution rules before a task-level total is presented.
+
+
+
+ A future version could distinguish a clean zero-record scan from listing failure,
+ unreadable file, malformed record, stale pricing, and deduplicated shadow source.
+ Such a change should be versioned; it cannot be inferred retroactively from the
+ current summary.
+
+
+
From 3b5942cd4e6a95462099a93ea52dcaa75f4d3264 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:11 +0300
Subject: [PATCH 06/12] docs(book): publish part five work lifecycle
---
src/content/book/210-project-discovery.mdx | 272 +++++++++++++++
src/content/book/220-worktree-topology.mdx | 229 ++++++++++++
src/content/book/230-turn-lifecycle.mdx | 320 +++++++++++++++++
src/content/book/240-permissions-input.mdx | 207 +++++++++++
src/content/book/250-work-items.mdx | 265 ++++++++++++++
src/content/book/260-context-memory.mdx | 290 ++++++++++++++++
src/content/book/270-checkpoints-revert.mdx | 235 +++++++++++++
src/content/book/280-workbench-services.mdx | 363 ++++++++++++++++++++
8 files changed, 2181 insertions(+)
create mode 100644 src/content/book/210-project-discovery.mdx
create mode 100644 src/content/book/220-worktree-topology.mdx
create mode 100644 src/content/book/230-turn-lifecycle.mdx
create mode 100644 src/content/book/240-permissions-input.mdx
create mode 100644 src/content/book/250-work-items.mdx
create mode 100644 src/content/book/260-context-memory.mdx
create mode 100644 src/content/book/270-checkpoints-revert.mdx
create mode 100644 src/content/book/280-workbench-services.mdx
diff --git a/src/content/book/210-project-discovery.mdx b/src/content/book/210-project-discovery.mdx
new file mode 100644
index 0000000..ad6073f
--- /dev/null
+++ b/src/content/book/210-project-discovery.mdx
@@ -0,0 +1,272 @@
+---
+slug: project-discovery
+order: 210
+number: "21"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Project discovery and t3.json
+shortTitle: Project discovery
+summary: "A T3 project is an environment-local durable record for one normalized workspace root; checked-in t3.json and Git-remote identity enrich its behavior and presentation without replacing that record."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Separate a filesystem workspace root, a durable project record, a Git identity, and a cross-environment presentation group.
+ - Trace t3.json decoding, its constrained schema, and the precedence of explicit, project, checked-in, and global workspace choices.
+ - Follow project creation through normalization, command invariants, durable events, and projected read models.
+ - Inspect setup-script environment labels without mistaking them for a shell sandbox or project identity.
+keywords: [projects, workspace root, discovery, t3.json, repository identity, environments, configuration, setup scripts, projection]
+sourceAreas: [apps/server/src/workspace/WorkspacePaths.ts, apps/server/src/project, packages/contracts/src/t3ProjectFile.ts, packages/shared/src/threadEnvMode.ts, packages/client-runtime/src/state/projectGrouping.ts]
+visuals: [filesystem discovery decision explainer, project identity projection]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ProjectDiscoveryLab from "../../components/ProjectDiscoveryLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+“Project” is deliberately not a synonym for “Git repository.” At this pinned
+revision, it starts as an environment-local durable record with a normalized
+directory as its `workspaceRoot`. Git can later provide an identity for that
+directory; clients can then choose to group equivalent identities across
+environments. Neither upgrade makes paths, directories, and project records the
+same thing.
+
+## Four layers answer four different questions
+
+| Layer | Question it answers | What it is not |
+|---|---|---|
+| Workspace root | Is this supplied path a directory T3 may use? | A proof that it is Git-backed or unique across machines |
+| Project record | Which durable project id, title, defaults, scripts, and lifecycle state belong here? | A recursive filesystem scan or a repository identity |
+| Repository identity | Which normalized fetch remote describes this Git repository? | A durable project event field that every directory must have |
+| Logical project group | Which physical project entries should a client present together? | A replacement for the selected environment/project target |
+
+The root service trims and resolves a supplied path (including a leading home
+shortcut), verifies it is a directory, and can create a missing directory only
+when the caller explicitly requests that. Relative files are separately resolved
+under a validated root; absolute paths and traversal outside it are rejected.
+
+
+
+The `project.create` normalization path uses that root service and passes
+`createWorkspaceRootIfMissing` through as the opt-in flag. The decider then
+rejects a second active project whose comparison-normalized root is already
+claimed. Thus “discover” in this chapter means validating and registering a root,
+not crawling the disk and guessing every possible project.
+
+
+
+
+
+
+ A valid directory can become a project even when Git identity resolution later
+ returns null. Conversely, a Git repository does not enter the durable project
+ read model until a project-create command is accepted and projected.
+
+
+### Registration has explicit entry points, plus one configured startup shortcut
+
+T3 does not recursively discover directories. The client add/browse path validates
+one chosen path and builds `project.create`; `t3 project add` normalizes one CLI
+argument and dispatches the same durable intent. Clone flows likewise resolve a
+specific destination before registration. One deliberate exception to a person
+clicking or typing a path is the optional server startup flag: when
+`autoBootstrapProjectFromCwd` is enabled, startup registers the configured cwd when
+needed and creates or selects an initial local thread. That is a single configured
+root—not a disk crawl.
+
+| Entry point | Candidate source | Durable effect | Discovery boundary |
+|---|---|---|---|
+| Client add / browse | one selected or typed path | builds `project.create` | validates only that target |
+| CLI `t3 project add` | one path argument | dispatches `project.create` | rejects an already-registered normalized root |
+| Clone flow | one resolved destination | registers the clone destination | no unrelated directory scan |
+| Optional cwd bootstrap | configured server cwd | may create project plus first local thread | gated startup shortcut for one cwd |
+
+## t3.json is checked-in input, not the project database
+
+The checked-in file is named `t3.json` at the workspace root. Its schema permits a
+schema URL, a workspace-relative icon path, a default thread environment mode, and
+up to 50 named scripts. A script has a display name and shell command; optional
+fields select an icon, mark a script to run after worktree creation, and describe
+desktop preview behavior. The schema says `defaultThreadEnvMode` is either `local`
+or `worktree`; it does not make a non-Git folder capable of creating a Git worktree.
+
+
+
+The loader joins that file name to the already-selected workspace root. A missing
+file is normal; unreadable, malformed, and schema-invalid input is logged and
+treated as absent. JSONC is accepted by the shared decoder, as the loader test
+demonstrates. That makes `t3.json` advisory configuration with a safe fallback—not
+an all-or-nothing prerequisite for opening a project.
+
+
+
+
+
+### Precedence is specific to a new thread's workspace choice
+
+The shared resolver gives a per-project stored setting first priority, then the
+checked-in `t3.json` value, then the global default. An explicit composer choice
+outranks that resolver before it is consulted. The web settings surface states the
+same project > `t3.json` > global order and lets `null` clear the project override.
+This precedence does not mean that every `t3.json` field overrides every stored
+project field: scripts are imported/stored separately, and the project record owns
+its own selected script list.
+
+
+
+
+
+
+
+
+ An invalid `t3.json` is not silently accepted as partial configuration. Its
+ declared actions and icon are ignored in that checkout, while the underlying
+ project record and its filesystem root remain independently valid.
+
+
+### Checked-in actions become durable scripts only after import
+
+Raw `t3.json` actions are suggestions. The web scripts control excludes a suggestion
+when an existing durable project script already has the same command or a
+case-insensitive matching name. A person chooses an import; the UI converts the file
+shape into a project script and updates the project's stored `scripts` list. Later
+setup selects from that durable list. The setup runner does not execute raw file
+entries merely because a checkout contains `t3.json`.
+
+
+
+
+ L[best-effort file loader]
+ L --> I[deduplicated import suggestions]
+ I -->|person imports| U[project update command]
+ U --> D[(durable project.scripts)]
+ D --> S[first runOnWorktreeCreate script]
+ S --> T[setup terminal command]`} />
+
+
+## Identity is resolved at read time from Git remotes
+
+Repository identity resolution first asks Git for the top-level root, then reads
+fetch remotes. It prefers `upstream`, then `origin`, then the lexicographically
+first fetch remote. The selected URL is normalized into a canonical key and can
+yield a display name, provider, owner, and repository name. A non-Git directory or
+a Git repository with no remote returns `null`; positive and negative results are
+cached for one minute by default.
+
+
+
+The snapshot query resolves identities for distinct active workspace roots with a
+bounded concurrency of four, then attaches the result to each returned project
+shell. The durable project projector itself starts with the project id, title,
+root, defaults, scripts, timestamps, and deletion state. In other words, a client
+receives a projection enriched by a fresh repository lookup; the event stream does
+not promise that a remote URL is permanently frozen into project history.
+
+
+
+
+
+
+
+
+ R{validated directory?}
+ R -->|yes| C[project.create]
+ R -->|no| X[structured root error]
+ C --> E[project.created event]
+ E --> D[(durable project projection)]
+ P --> G[Git top-level + fetch remote]
+ G --> I[optional canonical repository identity]
+ D --> S[project shell snapshot]
+ I --> S
+ S --> L[client logical grouping + environment labels]`} />
+
+
+## Grouping is presentation, and labels identify a member's environment
+
+Client grouping has three modes. `separate` uses a physical key of environment id
+plus normalized workspace path. `repository` uses the canonical remote key when
+available. The repository-and-path mode appends a workspace path relative to the
+Git root when it can derive one. If identity is absent or relative-path derivation
+fails, grouping falls back to the physical key or the canonical repository key as
+the source says. The group retains member project references, so a grouped sidebar
+title is never authorization to send a command to a vague “shared project.”
+
+
+
+The web sidebar decorates each retained member using an environment-label resolver,
+calculates local/remote/mixed presence against the primary environment, and keeps
+the member project refs. An environment label therefore tells the reader where a
+physical checkout lives; it is not part of the Git remote key and it does not alter
+the project root.
+
+
+
+## Setup commands carry both roots into the new worktree terminal
+
+The first script marked `runOnWorktreeCreate` is the setup script. When it runs,
+its terminal cwd is the new worktree, while its environment sets
+`T3CODE_PROJECT_ROOT` to the project's original root and
+`T3CODE_WORKTREE_PATH` to the new linked checkout. This is useful context for a
+setup command; it is not an assertion that arbitrary child processes are confined
+to that directory or that the two paths have the same Git state.
+
+
+
+
+
+## Work the filesystem decision by hand
+
+The lab does not touch your files. It makes the boundary visible: a valid root, a
+durable project record, usable checked-in configuration, and a Git identity can
+coexist—but none automatically proves the next one.
+
+
+
+
+ The source preserves physical member references after grouping and resolves
+ identity during snapshot reads. From those choices, it is reasonable to infer
+ that grouping is a client presentation convenience rather than a globally durable
+ cross-machine project object. That is an interpretation of the boundary, not a
+ separately declared protocol guarantee.
+
+
+
+ A future importer might enumerate candidate directories and present their root,
+ Git, and t3.json evidence before dispatching project.create commands. The current
+ source inspected here validates a root supplied to project creation; it does not
+ establish such a recursive importer.
+
+
+
diff --git a/src/content/book/220-worktree-topology.mdx b/src/content/book/220-worktree-topology.mdx
new file mode 100644
index 0000000..8cfd65d
--- /dev/null
+++ b/src/content/book/220-worktree-topology.mdx
@@ -0,0 +1,229 @@
+---
+slug: worktree-topology
+order: 220
+number: "22"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Current checkout versus isolated worktrees
+shortTitle: Worktree topology
+summary: "A new thread may operate in the project's current checkout or, for a Git repository, receive a newly created branch in a linked Git worktree; both routes retain Git as the authority for branch occupancy, filesystem state, and cleanup."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Distinguish current-checkout execution from an isolated linked Git worktree.
+ - Trace branch/base selection, optional origin refresh, worktree creation, setup, and thread metadata update.
+ - "Identify Git-owned invariants: a branch's worktree occupancy, dirty-state removal failure, and non-Git fallback."
+ - Read bootstrap failure recovery as a bounded orchestration cleanup, not a transaction that reverses Git.
+keywords: [Git, worktree, branch, base branch, origin, setup scripts, cleanup, bootstrap, topology, recovery]
+sourceAreas: [packages/contracts/src/git.ts, packages/contracts/src/vcs.ts, apps/server/src/vcs/GitVcsDriverCore.ts, apps/server/src/ws.ts, apps/server/src/git/GitWorkflowService.ts, apps/mobile/src/lib/projectThreadStartTurn.ts, apps/server/src/checkpointing/Utils.ts, apps/server/src/orchestration/Layers/ThreadDeletionReactor.ts, apps/web/src/hooks/useThreadActions.ts]
+visuals: [worktree topology lab, current checkout versus linked worktree]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import WorktreeTopologyLab from "../../components/WorktreeTopologyLab.astro";
+
+A workspace choice is a choice about **where this thread may change files and
+checkout state**. `worktree` means a new linked Git checkout when the project is a
+Git repository. `local` means **do not create a new worktree**. It usually uses the
+project checkout, but it is not a promise that the thread has no stored workspace
+path: the mobile bootstrap can retain a selected existing `worktreePath` for a
+local request. Later runtime workspace resolution prefers that stored thread path
+and only then falls back to the project's root. It is not a generic container mode,
+and the current web send path deliberately downgrades the request to `local` for a
+non-Git project.
+
+
+
+## One repository can expose several checkout paths
+
+Git's ref list is also the occupancy map T3 presents to the user. The driver asks
+Git for `worktree list --porcelain -z`, parses branch/path pairs, normalizes them,
+filters paths that no longer exist, and attaches the surviving linked path to local
+branch refs. A branch marked with a `worktreePath` is therefore not an abstract
+availability hint: another checkout has that branch checked out.
+
+
+
+
+ T3 does not make one branch safely checkout-able in arbitrary simultaneous
+ directories. It asks Git to create or remove worktrees and reports the result.
+ Branch occupancy, uncommitted changes, hook failures, and path state remain
+ Git-level concerns that can reject an operation.
+
+
+The RPC contract exposes the relevant choices directly: creation accepts a base
+`refName`, optional new branch name and base reference metadata, plus an optional
+explicit path; removal accepts the path and an optional `force` flag. The result
+returns a worktree path and ref name. A pull-request preparation result separately
+admits `isOnPullRequestHead: false`, documenting that an existing linked checkout
+can be intentionally retained even when it could not safely be moved to the
+requested head.
+
+
+
+
+
+## Current checkout and new worktree diverge at bootstrap
+
+For an ordinary local request, no `git worktree add` call appears in the bootstrap
+branch. Its execution cwd is the thread's already-stored `worktreePath`, if one is
+present, otherwise the project root. A selected new-worktree branch instead follows
+this ordered path:
+
+1. Start with the selected base branch.
+2. If “start from origin” is requested *and* an `origin` remote exists, fetch
+ origin and resolve the selected base to a remote-tracking commit.
+3. Ask the Git workflow to create a worktree from that base, with the requested
+ new branch name and the selected base as merge-base metadata.
+4. Save the returned branch/path onto the thread, refresh Git status at that
+ linked path, run any opted-in setup script there, then start the turn.
+
+
+
+The distinction between a request and a cwd matters on mobile. Its builder sets a
+new-worktree request's initial path to `null`, but leaves the supplied path intact
+for a local request. The runtime resolver then gives any non-null thread path
+priority over the project root. Thus “local” is best read as “no **new** worktree
+creation,” not as “this thread can never point at an existing linked checkout.”
+
+The important qualification is step 2: the source checks for `origin` first. A
+stored preference to start from origin does not make an origin-less repository fail;
+the path stays on the selected local base. That is a practical fallback, not an
+assertion that the local base equals a server's latest commit.
+
+
+
+At the Git boundary, a new branch uses `git worktree add -b `;
+an existing ref omits `-b`. Absent an explicit path, the driver creates a path below
+its configured worktrees directory using repository name and a slash-sanitized
+branch name. When both a new branch and base-ref metadata are supplied, it sets
+`branch..gh-merge-base` using the branch portion of a remote ref when it can
+parse one.
+
+
+
+## Setup sees a worktree cwd and two path labels
+
+After creation, the setup runner finds the project, chooses its first
+`runOnWorktreeCreate` script, opens a terminal at the linked worktree path, and
+writes the command. It supplies `T3CODE_PROJECT_ROOT` for the original project root
+and `T3CODE_WORKTREE_PATH` for the linked checkout. A missing selected script returns
+`no-script`; terminal failures become structured runner errors. In the bootstrap
+path, a setup launch failure records a `setup-script.failed` activity and warning;
+a successful launch records requested/started activities. These outcomes are
+observability around the selected workspace, rather than a hidden switch back to a
+different checkout.
+
+
+
+
+
+
+ L[local request: no new worktree]
+ L --> R[stored thread path, else project root]
+ R --> T[turn starts]
+ P --> W{worktree requested\nand Git repository?}
+ W -->|yes| O[optional origin fetch + base resolution]
+ O --> A[git worktree add\nnew branch + linked path]
+ A --> M[thread metadata: branch + worktreePath]
+ M --> S[setup terminal at linked path]
+ S --> T
+ W -->|no| L
+ A -. bootstrap failure .-> C[delete newly created thread when applicable]`} />
+
+
+## Thread deletion and worktree cleanup are separate operations
+
+Durably deleting a thread is not itself a Git worktree removal command. The server's
+post-delete reactor attempts to stop that thread's provider session and close its
+terminal with terminal-history deletion. It does not call the Git worktree driver in
+this reactor. This is important even for a thread that has a `worktreePath`: deleting
+the conversation's durable record and cleaning an on-disk checkout have different
+owners and failure modes.
+
+The web client can add a second, explicitly confirmed cleanup step. When the deleted
+thread is the only thread linked to that path and a local API is available, it offers
+to delete the orphaned worktree too. Only after the durable thread deletion succeeds
+does it call `removeWorktree` with `force: true`. If that Git removal or the
+subsequent refresh fails, the client reports “Thread deleted, but worktree removal
+failed”; the durable thread stays deleted while the worktree can remain for manual
+inspection or later cleanup.
+
+
+ The server deletion reactor contains provider-session and terminal-history
+ cleanup, while the web client separately asks for confirmation and invokes the
+ forced Git removal only after thread deletion. This establishes distinct
+ operations and an observable partial-failure seam; it does not claim every client
+ surface offers the same optional cleanup prompt.
+
+
+## Explicit worktree removal is a Git command, while bootstrap recovery is narrower
+
+The explicit removal route invokes `git worktree remove ` and adds `--force`
+only if requested. A driver test confirms a created linked path disappears after
+normal removal. It does not delete the branch in the shown implementation, and it
+does not turn a dirty-tree refusal into success. Treat `force` as an explicit Git
+operation, not an automatic recovery action.
+
+
+
+
+
+Bootstrap failure has a different purpose. If a turn-start bootstrap fails after it
+created its thread, the server attempts an uninterruptible `thread.delete` cleanup
+and reports whether that cleanup happened. The observed code does **not** issue a
+matching `git worktree remove` in this catch path. Therefore a failed bootstrap may
+leave Git evidence that needs an operator's inspection; claiming atomic rollback of
+both durable and filesystem changes would be false.
+
+
+
+
+ The operation crosses Git and durable orchestration boundaries in separate calls,
+ while the failure path explicitly deletes a created thread but does not show a
+ compensating Git removal. That supports a saga-style interpretation and the need
+ for inspection/retry; it does not prove the exact filesystem outcome of every Git
+ failure timing.
+
+
+## Work the topology by hand
+
+Choose a route below. The only animation is the short, user-triggered transition
+between explanations; reduced-motion users receive the same state update without
+motion, and the complete ledger remains available for printing or without script.
+
+
+
+
+ A future workflow could persist an explicit cleanup attempt/result and reconcile
+ it against Git's worktree listing after a failed bootstrap. That capability is a
+ design proposal; it is not established by the current thread cleanup path.
+
+
+
diff --git a/src/content/book/230-turn-lifecycle.mdx b/src/content/book/230-turn-lifecycle.mdx
new file mode 100644
index 0000000..dba889e
--- /dev/null
+++ b/src/content/book/230-turn-lifecycle.mdx
@@ -0,0 +1,320 @@
+---
+slug: turn-lifecycle
+order: 230
+number: "23"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Start, stream, steer, interrupt, settle
+shortTitle: Turn lifecycle
+summary: A T3 turn begins as a durably accepted command and later crosses hot reactor and provider-runtime boundaries; session state, output, checkpoints, and client liveness each have narrower guarantees.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Follow a thread turn from a client command through decision, commitment, reactors, and provider events.
+ - Separate durable thread and turn projections from provider-process liveness and hot delivery.
+ - Explain buffering, user-interaction pauses, interruption, failures, completion, checkpoints, and settlement without treating any one signal as universal.
+ - Use correlation and turn guards to reason about delayed, missing, or superseded provider events.
+keywords: [thread, turn, lifecycle, receipt, reactor, provider runtime, buffering, interrupt, checkpoint, settlement, liveness]
+sourceAreas: [packages/contracts/src/orchestration.ts, packages/contracts/src/providerRuntime.ts, apps/server/src/orchestration/decider.ts, apps/server/src/orchestration/Layers/ProviderCommandReactor.ts, apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts, apps/server/src/orchestration/Layers/CheckpointReactor.ts, apps/server/src/provider/Layers/ProviderService.ts, packages/client-runtime/src/state/threads.ts]
+visuals: [turn lifecycle swimlane, illustrative-control state-machine lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import TurnLifecycleLab from "../../components/TurnLifecycleLab.astro";
+
+A T3 **thread** is the durable conversation and workspace record. A **turn** is
+one requested user-to-agent cycle within it. They overlap, but they do not share
+one clock or one source of truth.
+
+The command path can durably record that a turn was requested before a provider
+process has accepted anything. Later, canonical provider events can make session
+and message projections durable. Between those crossings, a live process, a
+hot stream, and a client subscription can all disappear independently.
+
+
+ An accepted command receipt proves that T3 committed the command's domain
+ event batch. It does not prove that a hot reactor observed the event, that a
+ provider started, or that the provider finished. Conversely, provider liveness
+ is operational evidence, not a durable replay log.
+
+
+## 1. Request: a command records intent, not generated work
+
+The `thread.turn.start` command schema carries a user message, optional model/title
+choices, runtime and interaction mode fields, and optional bootstrap metadata. On
+the current web path for an already-created server thread, selected model and modes
+are first persisted with separate thread-setting commands. The turn decider then
+validates the thread (and optional source plan) and writes that target thread's
+already-stored runtime and interaction modes into a user `thread.message-sent` plus
+causally linked `thread.turn-start-requested`. Those command fields therefore should
+not be read as a separate per-turn override on this path. Activity may also unsettle
+or unsnooze the thread.
+
+
+
+The engine appends that planned batch, applies synchronous projections, and
+writes the accepted command receipt in one SQL transaction. Only after commit
+does it publish each event to its in-memory domain stream. A same-id retry can
+return the receipt rather than create a second command batch; it does **not**
+replay the post-commit publication or provider request.
+
+
+ T3 can acknowledge thread.turn.start after its event batch and
+ receipt commit, before ProviderService.sendTurn runs. This is
+ intentionally narrower than provider acceptance or turn completion.
+
+
+### Bootstrap is a surrounding saga, not a turn-state transition
+
+Bootstrap metadata can ask the command entry path to create a thread, prepare a
+worktree, and optionally launch setup before the final turn command. Those steps
+span separate commands and filesystem effects. The final `thread.turn.start` is
+still the durable intent boundary described above; a worktree or setup outcome
+does not make a provider turn running by itself.
+
+This chapter uses “start” for the final turn command. It does not imply that
+every first-message bootstrap step is atomic with that command.
+
+## 2. React: committed intent reaches a hot provider boundary
+
+`ProviderCommandReactor` subscribes to committed domain events and serializes
+its work. For a start request it finds the recorded user message, optionally
+launches first-turn naming work, ensures or resumes a provider session, marks the
+projected session `starting` while a start is pending, and calls
+`ProviderService.sendTurn`. The call is forked: its eventual failure is handled
+asynchronously.
+
+
+
+If session setup or `sendTurn` fails, the reactor writes a session with `error`
+status and appends a `provider.turn.start.failed` activity. That is a later,
+separate internal command sequence; it does not rewrite the original accepted
+receipt. Retrying is therefore a **new intent with a new command id**, not an
+automatic re-execution promised by the original receipt.
+
+
+ The reactor keeps a time-limited in-memory cache of handled starts and consumes
+ a hot stream. Neither mechanism is a persisted provider-outbox cursor. A
+ process loss in the post-commit bridge can leave durable intent without this
+ reactor observing it.
+
+
+## 3. Run: correlate provider facts before changing the thread
+
+Each adapter emits canonical `ProviderRuntimeEvent` values. The provider service
+checks that the emitting adapter's configured instance and driver agree with the
+event, fills that instance identity into the event when needed, may log it, and
+then publishes it to its own hot stream. `threadId` is the product anchor;
+`turnId`, item ids, request ids, native references, and provider-instance id are
+the correlation material around it.
+
+
+
+Runtime ingestion guards lifecycle-changing facts. In particular, a completion
+for another active turn—or an untargeted completion while no active turn is
+known—is rejected from the lifecycle update. A `turn.started` that names a new
+expected turn may supersede the former turn; this supports provider-specific
+steering behavior without allowing arbitrary stale events to overwrite the
+thread.
+
+
+
+The resulting durable session projection has practical states such as
+`starting`, `running`, `ready`, `error`, and `stopped`. A turn row separately
+uses `running`, `completed`, `interrupted`, or `error`. Do not read this as a
+universal provider state machine: the runtime union has `turn.started`,
+`turn.completed`, and `turn.aborted`, while the projection chooses how to fold
+selected events into T3's user-facing state.
+
+The shared client reducer makes the same distinction: a completed assistant
+message does not settle its turn while that turn remains the session's active
+running turn; leaving `running` is the turn-end signal for the client model.
+
+
+ E[(message + start intent + receipt)]
+ E -. hot domain event .-> R[command reactor]
+ R --> P[ProviderService / adapter]
+ P -. canonical runtime event .-> S[correlate + hot PubSub]
+ S -.-> I[runtime ingestion]
+ I --> D[(session, messages, requests, turn rows)]
+ S -.-> K[checkpoint reactor]
+ K --> G[(git checkpoint when available)]`} />
+
+
+## 4. Stream: buffering changes cadence, not ownership
+
+For `content.delta` with `assistant_text`, ingestion gets or creates an assistant
+message associated with the incoming turn. In the default buffered delivery mode,
+it collects text and dispatches only spill chunks; the legacy setting dispatches
+each delta directly. On a provider request for the user, ingestion flushes the
+buffer and finalizes the active assistant segment. On `turn.completed`, it
+finalizes remembered assistant messages and buffered proposed-plan material.
+
+
+
+Buffering is not a second transcript and it is not a guarantee that every native
+token becomes a durable event. It is a server-side delivery policy before the
+next internal commands commit. The durable history remains the projected messages
+that successfully pass through the orchestration engine.
+
+### Waiting is an in-flight turn that needs a person
+
+`request.opened` and `user-input.requested` are distinct runtime facts. They
+create pending interaction state and close the current assistant segment so a
+person can respond. The generic command boundary sends an approval decision or
+structured answers back to the active provider session. The turn can remain
+running while it waits; the UI deliberately keeps a Stop action available in
+this state.
+
+This is not the same as a settled thread. Settlement is a separate thread
+lifecycle classification and refuses to hide a starting/running session or an
+open approval/user-input request.
+
+## 5. Control: interrupt and steer have intentionally different meanings
+
+`thread.turn.interrupt` durably records `thread.turn-interrupt-requested` first.
+The reactor then interrupts **by session**, because an orchestration turn id is
+not a provider turn id. An interrupt request can immediately mark a matching
+turn projection interrupted, but native completion/abortion and session state
+still arrive through the provider-runtime path.
+
+
+
+There is no generic `steer` method in `ProviderAdapter`. Sending another start
+request while a provider turn is live may mean “steer” for a concrete adapter;
+the ingestion guard explicitly has a controlled superseding-start case. Treat it
+as provider-specific behavior, not a cross-provider promise.
+
+
+ The interactive state machine shows steering as a controlled replacement of an
+ active provider turn. That is a teaching model inferred from the guarded
+ supersession path; a product control must still check its chosen adapter's
+ concrete behavior.
+
+
+## 6. Finish: completion, error, compaction, and checkpoint are separate signals
+
+`turn.completed` carries a runtime state and may contain stop reason, usage,
+cost, or error text. Ingestion maps a failed completion to session `error` and
+other completion to `ready`, clears its active turn id, and finalizes buffered
+content. A `runtime.error` can also record session error. `turn.aborted` clears
+plan-progress liveness, but its exact durable session fold is not equivalent to
+the `turn.completed` path in the inspected code.
+
+`context_compaction` is a canonical item type, and ingestion maps a canonical
+`thread.state.changed` value of `compacted` into a durable
+`context-compaction` activity. It is still not a documented transition in this
+chapter's session-status switch. The activity records provider-originated
+provenance; it must not be presented as a new T3 turn, completion, checkpoint,
+or a T3-owned summary artifact.
+
+
+ At this pinned revision, the runtime vocabulary can represent context
+ compaction and ingestion can preserve its observation as activity. The lifecycle
+ folding inspected here does not establish a generic “compaction completed the
+ turn” rule. Any future product behavior that does so should be explicitly sourced
+ and tested.
+
+
+Checkpointing is a parallel best-effort lifecycle. Its earliest baseline path
+observes the durable `thread.turn-start-requested` or qualifying user-message event
+and tries to capture the pre-turn Git state; an observed provider `turn.started`
+provides an additional baseline path. On completion, the reactor can capture a
+post-turn ref from a correlated runtime event. Because the shared runtime stream may
+miss that completion, ingestion can first create a `missing` checkpoint from
+`turn.diff.updated`, and the reactor listens to the resulting durable domain event
+to replace the placeholder with a real capture. A non-Git workspace, missing id,
+checkpoint failure, or stream gap can prevent a checkpoint without invalidating the
+turn receipt or provider completion.
+
+
+
+
+ The accepted command receipt is committed with the initial domain batch. Test
+ receipt publication for checkpoint milestones is separate, and the production
+ runtime receipt bus intentionally retains and broadcasts nothing.
+
+
+## Exercise illustrative paths
+
+The lab deliberately presents a small set of useful teaching paths; each click moves
+once and stops. These controls are **not** a complete server-enforced legality
+matrix. The turn-start decider validates the target thread and optional source plan,
+but it has no “must be resting/ready” guard. UI affordances and concrete provider
+behavior constrain concurrent sends elsewhere. The lab distinguishes the durable
+record from hot provider liveness and includes a static table for no-script and
+print use.
+
+
+
+## What survives which boundary?
+
+| Observation | What it establishes | What it does not establish |
+| --- | --- | --- |
+| Accepted start receipt | initial message and start intent committed with projections | provider saw, started, or completed the work |
+| `starting` / `running` session projection | accepted runtime lifecycle folding has been persisted | the native process is currently reachable |
+| Buffered or completed assistant message | a selected content result committed | every native token or tool event was durably preserved |
+| `turn.completed` folded to `ready` or `error` | a correlated completion event was accepted by ingestion | checkpoint capture, client delivery, or billing settlement |
+| Checkpoint row/ref | checkpoint reactor recorded a filesystem-derived milestone | a provider turn receipt or universal rollback semantics |
+| Client “live” / “synchronizing” state | subscription/cache synchronization condition | provider execution state or a durable liveness lease |
+
+
+ Provider streams and client subscriptions are both live, scope-bound mechanisms.
+ The durable projections are the recoverable product record; neither hot stream
+ proves the other is alive after a disconnect or process restart.
+
+
+## Ambiguities worth keeping visible
+
+- The canonical union admits more event variants than every adapter emits. This
+ chapter does not claim that every provider reports compaction, usage, pause,
+ or a matching turn id.
+- A pending request makes a thread await a person, but an exact provider-native
+ pause/resume protocol remains adapter-owned.
+- The start reactor's hot subscription and short-lived dedupe cache do not by
+ themselves specify crash recovery for a missed initial start. That would need
+ a durable outbox/reconciler design; none is established by these paths.
+- A checkpoint is conditional on workspace and Git conditions. The sources do
+ not establish it as a prerequisite for calling a turn complete.
+
+
diff --git a/src/content/book/240-permissions-input.mdx b/src/content/book/240-permissions-input.mdx
new file mode 100644
index 0000000..59b5dd2
--- /dev/null
+++ b/src/content/book/240-permissions-input.mdx
@@ -0,0 +1,207 @@
+---
+slug: permissions-and-input
+order: 240
+number: "24"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Permission modes, approvals, and structured input
+shortTitle: Permissions and input
+summary: T3 Code persists a four-value runtime-mode choice on a thread, but each provider maps that choice into its own controls; live approval and structured-input requests travel through distinct canonical flows.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Name the four canonical runtime modes without treating them as an equivalent provider security policy.
+ - Distinguish approval requests from structured user-input requests, including their response payloads and pending state.
+ - Trace an interactive response through durable intent, the provider reactor, and native completion.
+keywords: [runtime mode, permission, approval, structured input, reactor, pending state, provider adapter, security]
+sourceAreas: [packages/contracts/src/orchestration.ts, packages/contracts/src/provider.ts, apps/server/src/provider, apps/server/src/orchestration/Layers, apps/web/src/session-logic.ts, apps/mobile/src/state/use-selected-thread-requests.ts]
+visuals: [permission mapping matrix, interactive request router]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import PermissionFlowLab from "../../components/PermissionFlowLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+T3 Code has four persisted runtime-mode values: `approval-required`,
+`auto-accept-edits`, `auto`, and `full-access`. They belong to the thread and
+travel with provider session/turn start. They are compact product controls, not
+proof that five native runtimes have the same sandbox, reviewer, allow-list, or
+escalation semantics.
+
+## Four durable labels, provider-specific effects
+
+The canonical union contains exactly these four values; its default is
+`full-access`. A runtime-mode change becomes a `thread.runtime-mode-set` event.
+When a turn starts, the decider writes the target thread's already-stored runtime
+and interaction modes into the start event. Read the labels as durable thread
+policy, not an independent per-turn override hidden in the start command.
+
+
+
+| T3 mode | Codex app-server mapping | Claude SDK mapping | Cursor ACP path | Grok ACP path | OpenCode SDK mapping |
+|---|---|---|---|---|---|
+| `approval-required` | `untrusted` approval, `read-only` sandbox, user reviewer | no explicit entry in the adapter’s mode map; base mode is left unset | prefers a native approval-mode alias when offered; permission callbacks still wait | no ACP session-mode mapping; a raised permission callback waits for an explicit response | broad operations ask; `question` is allowed |
+| `auto-accept-edits` | `on-request`, `workspace-write`, user reviewer | `acceptEdits` | prefers an implement-mode alias when offered; permission callbacks still wait | no ACP session-mode mapping; a raised permission callback waits for an explicit response | broad operations ask; `question` is allowed |
+| `auto` | `on-request`, `workspace-write`, `auto_review` reviewer | `auto` | prefers an implement-mode alias when offered; permission callbacks still wait | no ACP session-mode mapping; a raised permission callback waits for an explicit response | broad operations ask; `question` is allowed |
+| `full-access` | `never`, `danger-full-access`, user reviewer | `bypassPermissions` plus `allowDangerouslySkipPermissions: true` | prefers an implement-mode alias and auto-selects an offered allow option; otherwise waits on the callback | auto-selects an offered allow-always or allow-once option; otherwise waits on the callback | allows all permission rules |
+
+The table records executed mappings at this pinned revision. It does **not** turn
+the blank Claude map entry, Cursor mode aliases, Grok callback policy, or OpenCode's
+shared non-full-access ruleset into claims of equivalent safety. Codex, for example,
+also selects a native sandbox and reviewer for every mode. Claude intercepts
+`AskUserQuestion` before the full-access shortcut, so it still uses structured input;
+`ExitPlanMode` is intercepted separately.
+
+
+
+
+ The same persisted T3 value reaches materially different native mechanisms.
+ Security-sensitive UI and documentation must therefore name the selected provider
+ and its concrete mapping rather than promise a cross-provider sandbox strength.
+
+
+
+ `ProviderAdapter.capabilities` currently declares only whether an existing
+ session can switch models. It does not advertise approvals, structured input,
+ sandbox strength, task behavior, or a feature-complete permission matrix.
+
+
+## Two interactive request shapes
+
+An approval asks whether a provider may perform an action. Its canonical response
+contains a `requestId` and one decision: `accept`, `acceptForSession`, `decline`,
+or `cancel`. Runtime ingestion classifies known native request types into command,
+file-read, and file-change when it can; unknown native request types remain an
+approval without a fabricated subtype.
+
+Structured input is different. It carries questions with ids, headers, options, and
+optional multi-select behavior. Its response is an answer record keyed by question
+id. An answer is not an approval decision, and an approval choice is not an answer
+map.
+
+
+
+- Codex maps `item/tool/requestUserInput` into `user-input.requested` and native
+ approval methods into `request.opened`.
+- Claude handles `AskUserQuestion` through the input flow; other tool decisions
+ use the approval flow unless its runtime mode is full access.
+- Cursor and Grok wait on ACP deferred values for permission callbacks and their
+ question extensions.
+- OpenCode maps `permission.asked` and `question.asked` separately, then calls
+ `permission.reply` or `question.reply`.
+
+All five adapters implement both response operations in the current source. That
+does not promise every provider originates both request kinds in every configuration.
+The adapter capability surfaces are the positive evidence here; they are not a
+guarantee that a native runtime will open a given request type during every session.
+
+## Pending is partly durable and partly live
+
+Activities record `approval.requested`/`approval.resolved` and
+`user-input.requested`/`user-input.resolved`. Web and mobile derive open requests
+from ordered activity history, allowing another client surface to discover what
+needs attention.
+
+Approvals additionally receive a projected pending-approval row, keyed by request
+id, with pending/resolved status, decision, and timestamps. Structured input does
+not reuse that table; its pending state derives from activities. The native
+adapter’s Deferred, map entry, or SDK callback remains live memory. A durable
+request record cannot recreate that wait after a process or server restart.
+
+
+ Durable state says what was requested and what response intent T3 recorded. The
+ provider remains authoritative about whether its live request is known, current,
+ and acceptable. A stale native request becomes a response-failed activity.
+
+
+
+ The audited adapters keep pending request state in process-local maps/Refs and
+ Deferreds. Durable request history and projected approval rows can outlive that
+ state—and unresolved request activity is pinned outside the ordinary recent
+ activity window—but T3 does not reconstruct the original native wait after restart.
+ A stale response becomes a failure activity instead of a claim that the provider
+ consumed the answer.
+
+
+## One response crosses four responsibilities
+
+A web or mobile client dispatches either `thread.approval.respond` or
+`thread.user-input.respond`. The decider records response-requested intent. The
+provider command reactor resolves the thread, checks for a non-stopped session,
+calls `ProviderService`, and that service routes to the bound adapter. The adapter
+replies to its native runtime; later canonical resolution events return through
+runtime ingestion and become durable activities.
+
+Acceptance of the client command is not a synchronous certificate that a provider
+completed the action, and the hot provider bridge is not a replayable outbox.
+
+
+ I[runtime ingestion]
+ I --> A[(request activity)]
+ I --> Q[(approval projection when applicable)]
+ C[client response command] --> R[(durable response intent)]
+ R --> X[provider command reactor]
+ X -. invoke live callback .-> N
+ N -. canonical resolution .-> I`} />
+
+
+
+
+## Surface resolution and security boundaries
+
+Web session logic and mobile’s selected-thread hook both derive pending approvals
+and pending user input from thread activities, then dispatch distinct commands.
+Mobile scopes drafts by environment-plus-request id. The shared model is a request
+addressed to a thread; each client chooses its controls.
+
+| Boundary | Owns | Does not prove |
+|---|---|---|
+| Thread/orchestration | selected mode, response intent, activities, projected approval state | a live native request still exists or an action succeeded |
+| Client | request presentation and answer/decision collection | sandbox enforcement or provider identity |
+| Provider reactor/service | exact bound-session route and response failures | provider parity or crash-proof delivery |
+| Adapter/native runtime | reviewer, sandbox, callback, and reply semantics | that a T3 label means the same thing elsewhere |
+| Person operating the client | the authorization decision | that a broad mode replaces reviewing a specific request |
+
+Permission capability metadata could make these differences easier to negotiate in
+a UI, but none exists at this pinned revision. That is a **future design direction**,
+not current behavior.
+
+
diff --git a/src/content/book/250-work-items.mdx b/src/content/book/250-work-items.mdx
new file mode 100644
index 0000000..718751b
--- /dev/null
+++ b/src/content/book/250-work-items.mdx
@@ -0,0 +1,265 @@
+---
+slug: threads-and-work-items
+order: 250
+number: "25"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Threads, provider tasks, plans, skills, and subagents
+shortTitle: Threads and work items
+summary: A T3 thread is the durable work record. Provider task and subagent activity is normalized into activities and roster projections; high-frequency progress and usage streams are bounded, while liveness and plan progress stay explicitly ephemeral.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Separate the durable thread work record from provider task and subagent observations.
+ - Trace task lifecycle normalization into activities, bounded latest-state streams, the Agents projection, and the quiet work log.
+ - Identify which liveness and plan-progress indicators disappear on restart or settlement.
+ - Explain lifecycle, pin, and snooze as thread overlays rather than scheduler state.
+keywords: [thread, work item, task, subagent, workflow, liveness, plan progress, pin, snooze, work log]
+sourceAreas: [packages/contracts/src/orchestration.ts, apps/server/src/orchestration, packages/client-runtime/src/state/subagentRuntime.ts, apps/web/src/components, apps/mobile/src/features/threads]
+visuals: [interactive work-log projector, durable and ephemeral state map]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import WorkLogLab from "../../components/WorkLogLab.astro";
+
+“Task” is overloaded in agent products. Here, a **thread** is the durable work
+item: it has a project, title, model selection, runtime and interaction modes,
+conversation, activities, checkpoints, session summary, and lifecycle fields. A
+provider task or subagent is observed runtime activity inside that thread, not a
+second durable cross-provider work-item aggregate.
+
+This lets a provider expose a fleet, workflow, child agent, background shell, or
+nothing at all without forcing every provider into a false scheduler schema. T3 can
+retain useful normalized evidence while preserving provider task identity and
+provenance in an activity payload.
+
+## The durable record is the thread
+
+The thread read model holds message/activity history, latest turn, checkpoints,
+proposed plans, session summary, and lifecycle overlays. It is the address used for
+routing, recovery, and cross-surface navigation. A task id is not promoted to that
+role: it travels inside normalized `task.started`, `task.progress`, `task.updated`,
+and `task.completed` activities.
+
+Thread lifecycle is durable. Archive, settled/active override, and delete have
+their own state. Snooze is explicitly an overlay on an active thread: it suppresses
+the inbox until `snoozedUntil` passes or activity wakes it. Pinning is another
+overlay: active pinned threads render in a pinned block, while settled and snoozed
+threads remain in their own shelves. A fractional pin order key lets a client move
+one pin without rewriting its neighbors.
+
+These overlays are separate fields, but their commands deliberately coordinate
+visibility state. Settling also unpins and unsnoozes; pinning unsets settlement and
+unsnoozes. Snooze rejects pending approval/input and queued-start windows, yet it
+can hide a thread whose provider session is already running. That coupling organizes
+the inbox; it still does not schedule or cancel native provider work.
+
+
+ Archive, settlement, snooze, pinning, and fractional pin ordering are commands,
+ events, and projected thread fields. They can change where a work item appears and
+ whether it is considered settled, but these paths do not assign or resume a native
+ provider task.
+
+
+
+ The source does not define a global queue, dependency graph, retry policy,
+ assignment service, or durable task-completion contract across providers. A
+ provider lifecycle is useful observability within a thread, not evidence that T3
+ can resume, schedule, or reconcile that task universally after restart.
+
+
+## Native activity becomes product evidence; high-frequency streams are bounded
+
+Adapters translate native task/subagent updates into canonical task events. Runtime
+ingestion turns those into thread activities. High-frequency progress uses a stable
+per-thread/per-task id, so a meaningful tick replaces latest state instead of
+creating an unbounded timeline. Typed usage has a second stable id, and agent-owned
+tool heartbeats have another stable latest-state shape. By contrast, `task.started`,
+`task.updated`, and `task.completed` keep the provider event id. They are retained
+activities, but this code does not collapse all lifecycle evidence into one bounded
+row. A usage-only update cannot erase the latest status line, and a status update
+cannot erase the last known task usage.
+
+
+
+The payload can retain task type, agent linkage, title, role, model, effort,
+tool-use id, workflow/phase metadata, output file, status, error, and typed usage.
+The server stamps `agentKind`, so clients do not reverse-engineer an agent from a
+provider-specific task-type vocabulary. Legacy rows without that server stamp fall
+back to background classification and therefore do not enter the current Agents
+roster merely because their task type looks agent-like.
+
+
+
+Codex and Claude have concrete task normalization paths in the pinned source.
+Codex turns collaboration-agent notifications into task lifecycle/progress events.
+Claude maps SDK task, workflow, and member progress messages into task events,
+including typed usage when present. This audit does not establish equivalent
+task-rollup coverage for Cursor, Grok, or OpenCode; absence here is an evidence
+boundary, not a claim they can never support it.
+
+
+ A[provider adapter]
+ A -. canonical task event .-> I[runtime ingestion]
+ I --> D[(thread activities)]
+ I --> B[bounded progress / usage / heartbeat latest state]
+ B --> D
+ D --> F[client subagent fold]
+ F --> W[web Agents roster]
+ D --> Q[quiet web / mobile work logs]
+ I -. live update .-> L[in-memory liveness + plan progress]`} />
+
+
+## The Agents view is a projection, not a second log
+
+The client folds retained `task.*` and agent-owned `tool.progress` activities into
+a source-neutral subagent model. It accepts pending, running, waiting, idle,
+completed, failed, cancelled, and interrupted; idle is explicitly resumable rather
+than automatically “working.” The web Agents panel displays stable spawn order,
+current activity, model/effort, token count, tool-use count, and elapsed time. Its
+elapsed clock is presentation state: settled rows freeze at completion.
+
+The fold is tolerant of missing starts, late completions, reactivation, and session
+death. Task usage is parsed field by field and max-merged so duplicate or late
+cumulative frames cannot shrink a known count or erase a known breakdown.
+
+
+
+For Codex and Claude task data, the normalizer can roll up per-task `totalTokens`,
+input/cache/output/reasoning fields, `toolUses`, and `durationMs` when typed usage
+supplies them. These are task-local, provider-observed rollups. They are not invoice
+records. **Inference from the separate ingestion paths:** they do not feed either
+Chapter 20 lane; neither the live thread-context meter nor the transcript-scan
+historical usage total consumes this task rollup.
+
+
+ Chapter 20’s live lane selects one context-window snapshot for a thread; its
+ historical lane scans selected Codex and Claude transcript homes. Task usage is
+ activity payload for an Agents/work-log projection. No common accumulator joins
+ those three measurements in the pinned implementation.
+
+
+
+ The inspected task path terminates in activity payloads and the Agents fold. The
+ live context path admits thread.token-usage.updated, while historical
+ accounting reads provider transcript sources. No inspected accumulator joins the
+ task rollup into either Chapter 20 total; that negative conclusion is bounded to
+ these concrete paths.
+
+
+## Live liveness and plan progress are intentionally not durable
+
+`ThreadBackgroundLivenessService` is an in-memory registry for sidebar status
+labels. Agent work becomes `working`; monitor work becomes `monitoring` only when
+no agent work is live. Inert plan-mode bookkeeping is excluded. The registry is
+empty after restart until fresh task events arrive, and a dead session clears it.
+
+Plan progress follows the same pattern. It stores a current in-progress or next
+pending step with completed/total counts in memory, clears when every step is
+complete, and clears when the turn settles or the session dies. It is a live
+annotation for working indicators, not a durable plan-execution ledger.
+
+A durable thread can therefore remain visible after its liveness pill and
+plan-progress label disappear. That is not loss of the thread record; it avoids
+showing stale runtime work as live.
+
+## Quiet views select from the same evidence
+
+Web chat keeps the parent narrative quiet: agent lifecycle rows collapse into a
+spawn-batch call to action that opens Agents. Direct agents group by spawn turn;
+workflow members group under their coordinator. The detailed per-agent state lives
+in the derived Agents roster rather than one parent-log row per task. Mobile has no
+matching Agents roster on this path, so it retains compact terminal/task signals per
+task identity in the work log. Both are presentations of thread activity, not
+separate stores.
+
+### Skills are provider inventory, not orchestrated tasks
+
+Each provider-instance snapshot carries default-empty `slashCommands` and `skills`
+arrays. Their contents remain driver-specific: Codex asks app-server for its native
+skill inventory and adds T3's feedback command; Claude combines discovered skills
+with commands reported during SDK initialization; OpenCode flattens its inventory;
+the inspected Cursor and Grok snapshots expose models and probe state without
+skills or slash commands.
+
+The composer then normalizes presentation, not execution, with surface-specific
+menu policy. On web, enabled skills join `/` only when `showSkillsInSlashMenu` is
+enabled, and a visible skill name suppresses the same-named provider slash command.
+Mobile independently includes enabled skills alongside matching provider commands
+and does not apply that web collision helper. Both `$` paths search enabled skills;
+choosing one inserts `$skill ` provider-facing prompt syntax. That insertion does not
+create a durable task, plan step, dependency, or scheduler assignment by itself.
+
+
+ The product receives skills and commands inside each provider snapshot, then
+ filters and searches that inventory in the composer. No generic orchestration
+ command in this path converts a skill selection into a durable work item.
+
+
+
+
+## What the model does and does not promise
+
+| State or view | Durable? | Source of truth | Appropriate claim |
+|---|---:|---|---|
+| Thread, messages, activities, checkpoints, lifecycle overlays | yes | orchestration read model/projections | durable project work record |
+| Provider task/subagent identity and status | activity-backed, provider-derived | normalized runtime events and client fold | observed state, subject to provider delivery and retention |
+| Web Agents roster and rollups | derived | client fold or future v2 projection | current presentation of retained evidence |
+| Background liveness / plan progress | no | in-memory server service | current server observation only |
+| Snooze / pin / settled placement | yes | thread lifecycle fields | inbox/display organization, not execution scheduling |
+| Composer skills / commands | no task state implied | provider inventory and composer UI | selectable invocation affordance |
+
+A durable scheduler could add assignments, dependencies, retry rules, and
+reconciliation receipts. That is a **future design**, not an inference that the
+current activity fold already provides those guarantees.
+
+
diff --git a/src/content/book/260-context-memory.mdx b/src/content/book/260-context-memory.mdx
new file mode 100644
index 0000000..82fc46a
--- /dev/null
+++ b/src/content/book/260-context-memory.mdx
@@ -0,0 +1,290 @@
+---
+slug: context-compaction-memory
+order: 260
+number: "26"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Context is provider-owned; history is T3-owned
+shortTitle: Context and memory
+summary: "T3 Code retains a product-visible thread history and an opaque provider continuation cursor, while each provider owns the prompt context it may resume or compact; the pinned system has no universal T3 long-term-memory subsystem."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Separate a provider's prompt context from T3's durable thread history, session binding, and live context telemetry.
+ - Explain what a persisted resume cursor can and cannot promise after a server or provider-session restart.
+ - Read compaction as a provider event normalized into T3 activity, rather than as a universal T3 memory operation.
+ - Keep client drafts, offline delivery, and historical usage accounting in their proper later or separate lanes.
+keywords: [context, compaction, memory, resume cursor, session recovery, thread history, telemetry, restart]
+sourceAreas: [packages/contracts/src/provider.ts, packages/contracts/src/providerRuntime.ts, apps/server/src/persistence/ProviderSessionRuntime.ts, apps/server/src/provider/Layers, apps/server/src/orchestration/Layers, packages/client-runtime/src/state]
+visuals: [context ownership map, restart ledger]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import ContextOwnershipLab from "../../components/ContextOwnershipLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+“Memory” is an attractive word for a system with threads, histories, resumable
+sessions, cached screens, drafts, and transcript accounting. It is too imprecise
+to describe the pinned T3 Code implementation.
+
+T3 owns a durable **product record** for a thread: messages, activities, plans,
+session projection, and related work state. It also constructs the visible current
+turn payload—message text, attachment paths, model and mode choices—before calling
+an adapter. The provider-native session owns the accumulated conversation context,
+hidden history, and any private compaction state used when that turn reaches a
+model. T3 stores a provider-specific, opaque **resume cursor** with the
+provider-session binding so it can ask that provider to continue later. That cursor
+is a continuation handle, not a portable copy of a prompt, summary, embedding
+index, or cross-provider memory format.
+
+
+ A visible T3 conversation is not evidence that T3 can reconstruct the native
+ context sent to a model. A provider continuation cursor is not evidence that a
+ provider can recover after its own history/session is missing. Keep the durable
+ product history, hot native session, and provider-owned prompt context separate.
+
+
+## 1. Five similarly named things have different owners
+
+| Thing | Primary owner | What T3 keeps | What it does not establish |
+|---|---|---|---|
+| Current turn input | T3 constructs the visible request; adapter maps it to native input | user text, attachments, selected model/mode, and resulting observations | the provider's accumulated hidden conversation state |
+| Accumulated native context | Provider harness and its native session | provider identity, runtime mode, optional cursor, and normalized observations | hidden history, compaction result, model state, or portability to another provider |
+| Thread history | T3 orchestration projection | durable messages, activities, plans, session and work overlays | that every provider token, hidden message, or native item was captured |
+| Resume cursor | Provider defines the shape; T3 persists it opaquely | JSON-valued cursor bound to one thread/provider instance | a universal checkpoint, a durable provider callback, or a guarantee that native history still exists |
+| Context-window telemetry | Provider emits it; T3 normalizes/latest-selects it | a `context-window.updated` activity when usable | a full history, exact prompt contents, cost settlement, or a cross-provider meter |
+| Client cache, draft, or outbox | A client surface | locally scoped presentation/delivery state | server-side memory or a provider continuation |
+
+
+ T3 builds the explicit request delivered across the adapter boundary, including
+ attachment paths. The generic provider contract and persistence shape still do
+ not expose the provider's full hidden conversation or a portable reconstruction
+ of its accumulated model context.
+
+
+The provider-session contract explicitly makes `resumeCursor` an optional
+`Unknown` value. The persistent provider-runtime row stores it as JSON alongside
+provider/instance identity, session status, runtime mode, last-seen timestamp, and
+adapter payload. This is intentionally a narrow handoff boundary: a provider
+adapter receives its own cursor back at session start, rather than T3 decoding one
+generic “conversation memory” representation.
+
+
+
+
+ T3 persists the continuation value by thread and provider binding, then passes it
+ to the selected adapter while recovering or starting a session. The contracts do
+ not define a shared prompt, summary, embedding, retrieval, or long-term-memory
+ payload.
+
+
+## 2. A restart uses a handle, not a replayed prompt
+
+The recovery path first looks for an active compatible session. If none is
+available, it requires a persisted provider binding and a non-null continuation
+cursor. It restores the stored working directory, selected model information when
+available, runtime mode, and the cursor, then asks the same provider adapter to
+start. The adapter is the place that interprets that cursor.
+
+This distinction is concrete in the adapter implementations:
+
+- Codex turns a valid cursor into the native `threadId` supplied when opening its
+ app-server thread, then returns the provider thread id as its next cursor.
+- Claude validates a provider-shaped resume state before choosing the native
+ resume session id.
+- OpenCode treats its persisted `ses_…` id as a session lookup. It reuses the
+ native session only when its directory matches; a directory move can fork the
+ native session to preserve that provider's conversation history, while a known
+ missing session begins fresh.
+
+These are adapter-specific policies—not an inter-provider migration protocol. A
+Codex thread id is not meaningful to Claude; an OpenCode session id is not an
+executable representation of a Codex prompt.
+
+
+ C
+ end
+ subgraph T[T3 Code durable product state]
+ H[(Thread messages, activities,\nplans and session projection)]
+ B[(Provider binding:\ninstance + opaque resume cursor)]
+ end
+ C -. normalized runtime observations .-> H
+ A -. returns / updates cursor .-> B
+ B -. same-provider cursor only .-> A
+ H -. visible product history .-> U[Person / client]`} />
+
+
+
+ Recovery fails validation when no binding or cursor exists. A provider may also
+ reject or be unable to find its native session. The inspected OpenCode path
+ deliberately starts a new native session on a confirmed not-found; it does not
+ reconstruct a replacement prompt from T3's message projection. Other adapters
+ have their own resume semantics.
+
+
+## 3. Compaction is performed upstream and recorded downstream
+
+The generic runtime vocabulary can report `thread.state.changed` and a canonical
+`context_compaction` item type. The provider adapters turn their own native signals
+into those observations. Codex maps its native `thread/compacted` notification to
+the canonical `compacted` state. Claude maps a `compact_boundary` system message
+to both an updated usage snapshot (when it can normalize one) and a `compacted`
+thread-state event.
+
+Runtime ingestion accepts a canonical `thread.state.changed` whose state is
+`compacted` and appends a durable `context-compaction` activity. It does **not**
+take T3's message rows, create a provider summary, replace the provider's native
+context, or reset a turn merely because a compaction activity was observed.
+
+
+ The inspected mapping is provider event → canonical runtime event → durable
+ context-compaction activity. It records that a provider reported compaction; it
+ does not establish a generic T3-owned compaction algorithm or a replayable
+ summary artifact.
+
+
+This matters for comparisons between provider capabilities. The canonical event
+union can represent a compaction signal, but an adapter that does not emit one will
+not fabricate it. A context compaction activity is useful provenance in the work
+log; it is not itself a complete history of what the provider retained, discarded,
+or summarized.
+
+## 4. A context meter is current telemetry, not memory
+
+`thread.token-usage.updated` carries a provider-normalized snapshot with required
+`usedTokens` and optional maximum/context categories. Runtime ingestion admits a
+usable snapshot as one `context-window.updated` activity. The client reducer
+supersedes earlier resolvable snapshots with the same `turnId` value. The web
+selector then walks backward across the activities returned for the thread and
+uses the newest valid context-window update it can find. That returned thread
+detail is itself bounded to the 500 most recent activities.
+
+Thus the meter answers a narrow operational question: **what is the latest usable
+provider-reported context observation available for this thread?** It does not sum
+all past context sizes. It does not expose the prompt text. It does not turn a
+provider's total-processed counter into historical usage, cost, or memory capacity.
+
+At the pinned revision, the live emission paths inspected in Chapter 20 are Codex
+and Claude. Cursor, Grok, and OpenCode should not be shown as providing a T3
+context meter merely because their model metadata or native runtimes may have a
+context-window concept.
+
+
+
+
+ A valid update can remain product activity and later render after reconnect while
+ it is included in the bounded thread snapshot. The meter is therefore latest-state
+ telemetry—not a complete event archive, a provider prompt snapshot, or a durable
+ sequence of every native token event.
+
+
+For the separate historical question—tokens/cost-shaped records from provider
+transcript files—read [Chapter 20, “Usage accounting without a false ledger”](../usage-accounting/).
+That scan is neither the source of the live context meter nor a T3 memory layer.
+
+## 5. “Long-term memory” is absent as a universal product subsystem
+
+**Inference from the pinned generic contracts and recovery paths:** this revision
+does not establish a universal T3 long-term-memory subsystem. Those inspected
+paths model projects, threads, messages, activities, plans, checkpoints, provider
+sessions, current-turn payloads, and opaque continuation state. They do not define
+a cross-provider memory entity or a generic contract for injecting retrieved facts
+into a future provider turn.
+
+That is not a claim that a provider lacks its own native memory/history feature.
+It is a boundary claim about what **T3 Code itself standardizes** at the source
+lock. A provider-specific session resume, its own transcript files, a generated
+plan, a Git checkpoint, and a client draft are not interchangeable evidence of a
+universal product-memory feature.
+
+
+ Within these generic models, provider-binding records, turn construction, and
+ recovery paths, no cross-provider memory entity or prompt-injection contract is
+ established. This bounded source inference says nothing about a provider's
+ private implementation or an upstream roadmap. A future T3 memory feature would
+ need explicit ownership, privacy, retrieval, retention, injection, and failure
+ semantics.
+
+
+## 6. Client-local state belongs to a different recovery story
+
+The shared client runtime can cache an environment's thread snapshot and seed its
+event-subscription `afterSequence` from the cached shell sequence. That helps a
+screen catch up; it does not act as provider context or server authority. Mobile
+additionally owns environment-scoped local drafts and a persisted command outbox.
+Those are client resilience/delivery mechanisms and receive their detailed
+treatment in Chapter 33 rather than being rebranded as memory here.
+
+The distinction is particularly important after a disconnected mobile action:
+delivery retry can re-attempt a client command, while provider session recovery
+still depends on the server's binding and the provider's ability to use its cursor.
+Neither mechanism means a client has captured a complete native model context.
+
+
+ Chapter 33 will cover client caches, optimistic state, drafts, offline queues,
+ and reconnect reconciliation by surface. This chapter only places them outside
+ the provider-context boundary so a reader does not mistake local UI state for
+ T3-owned long-term memory.
+
+
+## Work the ownership and restart ledger
+
+Choose a teaching event, then inspect which record changes, who is authoritative,
+and what a later server restart can actually attempt. The lab has no autonomous
+motion; keyboard tab navigation and a complete static/print ledger are included.
+
+
+
+## Boundary checks to carry into another meta-harness
+
+1. Persist a provider-continuation handle only with its provider/instance identity;
+ never treat it as a portable prompt serialization.
+2. Keep a durable product conversation useful on its own terms, while stating that
+ it is not proof of hidden native context or tool state.
+3. Represent compaction as provider-originated provenance unless the product owns
+ the summarization algorithm and its exact replay/retention semantics.
+4. Name context telemetry, task usage, and transcript accounting as distinct data
+ products. Chapter 20's historical ledger must not silently become a memory
+ system.
+5. If a universal memory feature is proposed, design its user control, source
+ provenance, deletion/retention, isolation, provider injection, and restart
+ failure behavior explicitly rather than piggybacking on a resume cursor.
+
+
diff --git a/src/content/book/270-checkpoints-revert.mdx b/src/content/book/270-checkpoints-revert.mdx
new file mode 100644
index 0000000..fcde684
--- /dev/null
+++ b/src/content/book/270-checkpoints-revert.mdx
@@ -0,0 +1,235 @@
+---
+slug: checkpoints-revert
+order: 270
+number: "27"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Hidden-ref checkpoints, diffs, and revert
+shortTitle: Checkpoints and revert
+summary: Each completed turn can leave a Git tree snapshot under a hidden ref. Review reads those snapshots; revert restores workspace content first, then attempts provider-history rollback before it can complete the durable history rewrite.
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Follow a pre-turn baseline and post-turn checkpoint from a Git workspace into the durable thread projection.
+ - Separate checkpoint turn diffs from live working-tree and branch comparisons.
+ - Recognize that restore is destructive and that a failed provider rollback can leave a real partial state.
+ - Compare provider rollback implementations without inventing a universal undo guarantee.
+keywords: [checkpoint, git, hidden ref, diff, restore, revert, rollback, worktree, projection]
+sourceAreas: [apps/server/src/checkpointing, apps/server/src/orchestration/Layers/CheckpointReactor.ts, apps/server/src/vcs/GitVcsDriver.ts, apps/server/src/provider/Layers, apps/web/src/components/DiffPanel.tsx]
+visuals: [checkpoint timeline, checkpoint graph lab, revert saga]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import CheckpointGraphLab from "../../components/CheckpointGraphLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceList from "../../components/SourceList.astro";
+
+A T3 checkpoint is neither a Git branch nor a provider transcript backup. It is a
+Git tree snapshot stored under a hidden ref for one thread and numbered turn
+boundary. Its job is narrow: make a turn's filesystem delta reviewable and make a
+local content restore possible. The product then coordinates that filesystem action
+with a separate provider-conversation rollback and a durable thread-history update.
+
+
+ The Git path first restores selected snapshot content into the worktree and index,
+ runs git clean -fd, then resets the index path when HEAD
+ exists. The final index is therefore not a preserved staging snapshot of the
+ checkpoint. Untracked non-ignored files under the workspace can disappear. Review
+ a diff before requesting a revert; do not treat it as an undo stack for remote
+ commits, pushes, or external provider state.
+
+
+## A checkpoint is a hidden tree snapshot
+
+The ref naming helper makes one location per thread and turn count:
+`refs/t3/checkpoints/<base64url thread id>/turn/<n>`. A baseline at turn `0`
+is attempted before the first real turn when absent. When the checkpoint reactor
+observes a matching completion, it attempts the next snapshot. If that hot
+subscription misses completion while runtime ingestion receives it, the resulting
+durable `turn.diff.updated` path can first create a `missing` placeholder and later
+trigger a replacement attempt. A successful capture diffs against the preceding
+checkpoint, parses the patch into changed-file rows, and only then dispatches the
+durable `thread.turn.diff.complete` command.
+
+Git capture deliberately uses a temporary index. It reads `HEAD` into that index
+when one exists, stages the workspace into the isolated index, writes a tree,
+creates a commit from that tree, and updates the hidden ref. The ordinary checked-out
+branch is not moved by this sequence. That makes the checkpoint an implementation
+snapshot of content, not a normal branch-history commit a user is expected to
+checkout or push.
+
+
+ The capture reactor can log a missing predecessor baseline, but it still captures
+ the target ref. If deriving the patch summary fails after capture, it appends a
+ failure activity and records an empty file list rather than claiming the hidden
+ ref was never created.
+
+
+The durable checkpoint summary keeps the associated turn id, numbered boundary,
+hidden ref, a `ready` / `missing` / `error` status, changed-file additions and
+deletions, assistant-message linkage, and completion time. For the runtime-derived
+path, `failed` becomes `error`; `cancelled` or `interrupted` becomes `missing`; and
+`completed` or an unknown state becomes `ready`. Those are projection/capture-flow
+diagnostics—not proof that a later diff, restore, or provider rollback will work.
+`missing` can be a placeholder that the checkpoint reactor later attempts to replace
+with a real Git-backed snapshot. `error` is an error-status result, not proof that
+the workspace stayed untouched.
+
+## One review screen contains three different comparisons
+
+The web review chooser labels these as **Working tree**, **Branch changes**,
+**Latest turn**, and a selected prior turn. Only the turn modes call the checkpoint
+diff query. A selected turn compares its prior checkpoint to its own numbered
+checkpoint; a full-thread diff uses turn `0` as the baseline and a selected later
+checkpoint as the target. Both read hidden refs and default to ignoring whitespace.
+
+Working-tree and branch comparisons are not checkpoint snapshots. They query Git's
+current state at the selected workspace, can refresh on focus or a newer completed
+turn, and may therefore include edits made after the selected turn. They are useful
+side-by-side review modes, but they answer different questions.
+
+| Mode | Revisions compared | Time model | Can change while open? |
+|---|---|---|---|
+| Turn *n* | hidden checkpoint *n - 1* → hidden checkpoint *n* | completed-turn boundary | no; it is a stored pair until refs are pruned |
+| Full thread through *n* | hidden baseline 0 → hidden checkpoint *n* | accumulated completed turns | no; it is a stored pair until refs are pruned |
+| Working tree | `HEAD` → current tracked worktree, plus patches for untracked files | live filesystem | yes |
+| Branch changes | merge-base(selected base, `HEAD`) → `HEAD` via `git diff ...HEAD` | live repository refs | yes |
+
+
+ Z[hidden baseline ref · turn 0]
+ Z --> P[provider changes workspace]
+ P --> E[matching completion observation]
+ E -. hot runtime trigger .-> C[attempt hidden checkpoint · turn n]
+ E --> H[(missing placeholder from durable diff update)]
+ H -. replacement trigger .-> C
+ C --> D[successful capture: diff prior checkpoint → n]
+ D --> S[changed-file summary]
+ S --> O[(durable turn checkpoint)]
+ C --> T[turn / full-thread review]
+ W[live worktree] --> L[working-tree review]
+ R[live branch refs] --> G[branch review]`} />
+
+
+## A revert is an ordered, cross-boundary saga
+
+The client command `thread.checkpoint.revert` only records a
+`thread.checkpoint-revert-requested` event after the decider confirms the thread
+exists. The checkpoint reactor does the effectful work. It requires a bound provider
+session with a Git workspace, checks that the requested turn count is not ahead of
+the latest checkpoint, and resolves a target ref. A nonzero target must be available
+in the checkpoint read model. Turn `0` uses the generated baseline ref and the Git
+restore may fall back to current `HEAD` when that hidden baseline is unavailable.
+Several of these preconditions fail before restore; failures inside the sequential
+effect path can occur after filesystem mutation has begun.
+
+If the target is available, the order is important:
+
+1. Git restores target content into worktree and index, removes untracked
+ non-ignored paths, then resets the index path when `HEAD` exists.
+2. The workspace file index refreshes.
+3. The reactor asks `ProviderService` to roll back exactly the number of later
+ checkpoint turns.
+4. It attempts best-effort deletion of hidden refs newer than the target.
+5. It dispatches `thread.revert.complete`; projections then retain only derived
+ messages, turns, plans, activities, and checkpoint data at or before that count.
+
+There is no compensating Git restore if step 3 fails. The code catches the reactor
+failure and appends `checkpoint.revert.failed`; because the completion command is
+not dispatched, the durable projection and ref pruning do not report a completed
+revert. That ordering creates a genuine **partial failure**: the files may already
+match the target while provider history, newer refs, and durable thread history have
+not converged. This is a code-path inference from the ordered calls, not a claim that
+T3 repairs every such state automatically.
+
+
+ Once restore has begun, later Git operations, workspace-entry refresh, provider
+ rollback, ref cleanup, or completion dispatch can fail. Only a later successful
+ completion causes derived projection pruning. The observed path records a failure
+ activity; it does not compensate every partially applied state by restoring newer
+ content.
+
+
+## “Rollback” means different work in each adapter
+
+The service asks every bound adapter for `rollbackThread(threadId, numTurns)`, but
+the implementation is not uniform. Codex delegates to its live app-server runtime.
+Claude and Cursor truncate their adapter-maintained turn arrays (Claude also updates
+its resume cursor). OpenCode reads native session messages, finds the earlier
+assistant-message boundary, then calls `session.revert`. Grok deliberately returns
+an unsupported provider-side rollback error at this pinned revision.
+
+That table is a capability map for this implementation, not a promise that the
+upstream provider has erased files, billed tokens, cached context, tool side effects,
+or an external action. In particular, an adapter-local array truncation is not a
+general remote-history deletion guarantee.
+
+| Adapter path | Rollback behavior observed here | Important limit |
+|---|---|---|
+| Codex | delegates counted rollback to the session runtime | requires a recoverable live/bound session and native support |
+| Claude | truncates local tracked turns and refreshes resume cursor | this adapter-local state is not a provider transcript eraser |
+| Cursor | truncates local tracked turns | adapter state only |
+| Grok | returns “not supported … yet” | a revert that removes one or more checkpoint turns becomes unsupported after Git restore |
+| OpenCode | calls native `session.revert` at an earlier assistant-message boundary | semantics depend on the OpenCode session/message history it can read |
+
+
+ This chapter stops at server-side completion and derived projection pruning. How
+ web and mobile invalidate, race, refetch, and reconcile a reverted snapshot is a
+ client history-epoch concern deferred to Chapter 29.
+ Do not infer that a fresh server event alone settles every local cache race.
+
+
+
+
+## Read the status and failures as diagnostics
+
+- A `missing` checkpoint can mean there is no usable hidden ref for that boundary
+ yet. The reactor has a specific placeholder-replacement route, so `missing` is
+ not a claim that Git capture is permanently impossible.
+- An `error` checkpoint status and a `checkpoint.capture.failed` activity describe
+ different stages: a ref may exist while its changed-file summary cannot be
+ derived, and a turn diff can fail because a required ref/workspace is unavailable.
+- A revert can fail before restore when the thread, bound session workspace, Git
+ repository, requested count, or nonzero read-model ref is unavailable. For turn
+ `0`, restore may fall back to current `HEAD` when the hidden baseline ref is
+ unavailable. Sequential effect failures can still occur after filesystem mutation.
+- A completed `thread.reverted` event prunes selected derived projection rows; it
+ does not rewrite Git's ordinary branch history, certify every stale-ref deletion,
+ or roll back actions outside the workspace/provider contract.
+
+
+ Capture-summary failures append an activity while allowing checkpoint metadata to
+ land. Revert precondition and effect failures append a revert-failed activity;
+ only the successful path dispatches the durable revert completion command.
+
+
+
diff --git a/src/content/book/280-workbench-services.mdx b/src/content/book/280-workbench-services.mdx
new file mode 100644
index 0000000..f7d0c64
--- /dev/null
+++ b/src/content/book/280-workbench-services.mdx
@@ -0,0 +1,363 @@
+---
+slug: workbench-services
+order: 280
+number: "28"
+kind: chapter
+part: Part V · The work lifecycle
+partOrder: 5
+title: Terminals, files, previews, MCP, VCS, and pull requests
+shortTitle: Workbench services
+summary: "T3 Code's chat surrounds itself with server-owned workbench services. Their transports differ deliberately: terminal and preview state stream, assets use expiring signed HTTP paths, pull-request diffs use authenticated HTTP slices, and desktop owns the only in-app browser host."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace the ownership and transport boundary for terminals, workspace files, signed assets, preview, MCP, VCS, and pull requests.
+ - Distinguish server metadata from the desktop-only processes and webviews that enact a workbench action.
+ - Read surface parity as capability-specific rather than assuming every web, desktop, and mobile client has the same tool.
+ - Identify where authorization is re-checked instead of trusting a visible client control.
+keywords: [terminal, PTY, assets, signed URL, preview, browser automation, MCP, VCS, worktree, pull request, authorization, streaming]
+sourceAreas: [apps/server/src/terminal, apps/server/src/assets, apps/server/src/preview, apps/server/src/mcp, apps/server/src/vcs, apps/server/src/pullRequest, packages/contracts/src]
+visuals: [interactive workbench capability matrix, service data paths, surface parity ledger]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import WorkbenchCapabilityLab from "../../components/WorkbenchCapabilityLab.astro";
+
+Chat is the control surface, not the whole application. A running T3 environment
+also owns processes, workspace paths, Git state, provider credentials, and source-
+control integrations. The book has already traced turn orchestration, worktree
+creation, context, and checkpoints. This chapter follows the adjacent **workbench
+services** without retelling those internal sagas.
+
+The rule that prevents most incorrect diagrams is simple: a visible panel does not
+move authority into the client. In the pinned implementation, server services keep
+the filesystem, PTY, VCS, asset-signing, and remote-host integrations. Web, desktop,
+and mobile clients ask one selected environment for a capability and render the
+result through surface-specific code.
+
+
+ Do not collapse all of these into “the WebSocket.” Terminal attach and preview
+ metadata are streamed. Asset bytes are fetched through a signed HTTP URL after
+ an RPC issues it. Pull-request controls are RPC, while potentially large diff
+ slices use authenticated HTTP. MCP is a provider-scoped bearer HTTP endpoint that
+ brokers to a compatible desktop host.
+
+
+## 1. The service map: owner before UI
+
+| Service | Primary authority | Main data movement | A tempting but false shortcut |
+|---|---|---|---|
+| Terminal | server PTY manager | RPC commands; attach snapshot then live events | “The browser owns the shell.” |
+| Files | server workspace services | authenticated project RPC | “The file tree is a local checkout.” |
+| Assets | server asset access | issue URL by RPC, fetch bytes by signed HTTP | “A file URL exposes the workspace.” |
+| Preview | server metadata plus desktop Electron host | RPC/events plus desktop IPC/webview | “The server is the browser renderer.” |
+| T3 MCP preview tools | provider-session registry and MCP server | scoped bearer HTTP, then broker stream | “T3 controls every MCP server.” |
+| VCS / worktrees | server driver and filesystem | RPC snapshots/commands | “A client branch selector creates a checkout locally.” |
+| Pull requests | server host-provider service | RPC controls; HTTP diff slices | “A rendered action proves the user may write.” |
+
+
+ |authenticated RPC| S[Environment server services]
+ S -->|snapshot + live events| T[Server PTY]
+ S -->|signed URL| A[Asset HTTP route]
+ S -->|authenticated diff request| D[PR diff HTTP route]
+ S -->|preview metadata| C
+ C -. curated desktop IPC .-> E[Electron main PreviewManager]
+ E --> W[Guest Chromium WebContents]
+ P[Provider session] -->|scoped bearer HTTP| M[T3 MCP preview toolkit]
+ M --> B[Preview automation broker]
+ B -. compatible host lease .-> E`} />
+
+
+
+
+The lab's surface rows intentionally say *which edge is evidenced*, not which
+feature is “supported” in the abstract. Desktop often shares the web renderer, but
+its Electron main/preload boundary adds authority that a hosted browser does not
+receive. Conversely, mobile has its own native terminal and file paths even though
+the server protocol is shared.
+
+## 2. A terminal is a server process with a streamed screen
+
+The terminal contract names a thread and a client-chosen terminal id; it carries a
+cwd, optional worktree path, dimensions, and bounded environment overrides. The
+server manager opens/reuses a PTY session, exposes lifecycle commands (open, write,
+resize, clear, restart, close), and offers two different subscriptions:
+
+- `attachStream` begins with the selected terminal's snapshot and follows it with
+ output/lifecycle events. This is the high-fidelity path used to reconstruct a
+ visible screen.
+- metadata subscription is a cheaper stream of terminal summaries for a broader
+ shell/session view.
+
+
+
+Terminal history is maintained in memory, persisted with a debounce/cap, and then
+replayed on a later first open before a new process is started. That makes a
+reopened screen useful after a client disconnect; it does **not** turn terminal
+history into a durable replay of every process state or side effect. The manager's
+finalizer kills its sessions, and thread deletion performs best-effort terminal and
+provider cleanup. Treat a terminal as a live server resource with an inspectable
+text history, not as a durable orchestration event stream.
+
+
+ The manager caps and persists screen history, reloads it before starting a new
+ PTY, and kills live sessions during finalization. Thread deletion asks the
+ cleanup reactor to close the thread's terminals and delete their history.
+
+
+Web renders that stream through its Ghostty-based surface. Mobile drives the same
+open/attach/write/resize/close protocol but renders through its native terminal
+module; it also explicitly reopens a stale, ended subscription only when the panel
+is shown. Desktop uses the web renderer path for the terminal panel. Those are
+rendering differences around one server-owned PTY boundary.
+
+## 3. Files and assets are deliberately different paths
+
+Workspace files use authenticated project operations: list/search entries, read a
+file, and write a file. The web file browser, preview, and editor are therefore
+views of an environment's workspace service; they are not a sync engine that copies
+the project into the client. Mobile has a tree/file-preview path and obtains a
+workspace-file asset URL using the same thread/environment address.
+
+An asset request is narrower. Its resource union names a workspace file in a
+thread, an attachment, or a project favicon. The server resolves the workspace
+context, normalizes the root, rejects paths outside that root, restricts previewable
+workspace resource types, resolves real paths, and emits an expiring signed URL.
+On retrieval it verifies the signature and expiry again; workspace claims also
+enforce exact-name or constrained relative-path rules, extension allow-lists, and
+canonical containment.
+
+
+ The default asset token lifetime is one hour at this revision. A URL proves only
+ that the server issued claims for that resource/path and time window. It is not a
+ generic bearer credential for project search, terminal control, or a parent
+ directory listing. Clients refresh asset URLs through shared asset state instead
+ of treating them as permanent file identifiers.
+
+
+
+
+This split also explains why “file preview” has more than one meaning. Source text
+can arrive by a project read operation; an image, browser document, or nested asset
+can arrive through a signed HTTP path; and the interactive application preview in
+the next section is neither of those things.
+
+## 4. Preview state streams; Chromium authority stays on desktop
+
+The server `PreviewManager` holds small per-thread/tab snapshots: URL/navigation
+state, history affordances, viewport setting, timestamps, a server epoch, and a
+monotonic revision. It serializes state mutation and publishes ordered lightweight
+events, so reconnecting clients can list metadata and converge on a tab's recorded
+state. The manager's `refresh` operation only confirms that a session exists; the
+actual reload is performed by the desktop bridge and later reported back.
+
+The actual in-app browser is intentionally desktop-only. The Electron preload
+exposes a curated preview IPC API for tab creation, navigation, zoom, screenshot,
+recording, element picking, and automation actions. The web route rejects the
+preview keybinding outside a supported desktop runtime with a “Preview is
+desktop-only” message. Thus the server can coordinate preview metadata across
+subscribers, but it cannot make a plain web client or phone into a Chromium webview
+host.
+
+This is a useful two-layer model:
+
+1. **Server coordination:** tab identity, state/revision, and typed preview RPC.
+2. **Desktop enactment:** the renderer requests actions through curated preload
+ IPC; Electron main's preview manager validates and owns the guest Chromium
+ `WebContents`, then reports navigation, loading, and failures over the bridge.
+
+That division prevents a remote client from assuming it can inspect a page merely
+because a server has a preview record. A record may exist while no compatible
+desktop host is connected.
+
+
+ The server's revisioned snapshot can exist without a desktop browser host.
+ Renderer code receives only the curated IPC surface; Electron main validates
+ the guest webview before the desktop preview manager adopts it.
+
+
+## 5. T3's MCP service gives a provider a scoped preview toolkit
+
+T3 can attach its own `t3-code` MCP server to a provider session when the agent
+browser-access setting permits it. This is not the same as discovering or managing
+every external MCP server configured by Codex, Claude, Cursor, Grok, or OpenCode.
+It is a narrowly scoped T3 endpoint for preview operations.
+
+At provider-session preparation, the server either revokes/clears prior MCP state
+when access is disabled or issues a fresh random bearer credential. Its scope is
+bound to the environment, thread, provider instance, and a registry-generated MCP
+session id—the latter is not an adapter-native Codex, Claude, Cursor, Grok, or
+OpenCode session identity. The registry stores a hash, not the raw token, and only
+advertises the `preview` capability. The `/mcp` route sits outside ordinary
+environment auth, so it requires that provider-scoped bearer credential. Registry
+liveness is refreshed by both MCP traffic and active provider turns; stop/revoke
+paths remove credentials eagerly, while abandoned records age out after the
+liveness window.
+
+
+
+All five adapter paths can consume the T3-owned MCP configuration, but their native
+injection mechanisms differ: Codex uses app-server arguments plus a bearer-token
+environment variable; Claude supplies an HTTP MCP server; Cursor and Grok pass ACP
+MCP server definitions; OpenCode registers the remote server only when T3 owns the
+local OpenCode server (`!server.external`). This is adapter integration for one T3
+toolkit, not shared management of each harness's external MCP inventory.
+
+The MCP server authenticates the request, provides the resulting invocation scope
+to preview toolkit handlers, and sends automation through `PreviewAutomationBroker`.
+The broker selects a compatible desktop host in the same environment, keeps a
+provider-session-to-host lease while the connection remains live, and returns an
+unavailable error when no suitable host can perform an operation. It does not
+silently transfer an active browser automation session to another desktop because a
+credential timer expired.
+
+
+ Authentication yields an invocation scope. The broker still needs a compatible
+ desktop host in the same environment and keeps a live provider-session assignment
+ sticky so a multi-step browser interaction does not silently jump between hosts.
+
+
+
+ Provider runtime observations include MCP status/OAuth event types, and adapters
+ can receive their own native MCP configuration. This chapter establishes only the
+ T3-owned, provider-session-scoped preview toolkit. It does not infer a cross-
+ provider control plane for arbitrary third-party MCP servers.
+
+
+## 6. VCS remains an environment service; worktrees remain a lifecycle saga
+
+The VCS contract exposes a driver kind and capability flags such as worktree,
+bookmark, atomic-snapshot, and default-push support. Client views query or command
+the server driver against the selected workspace; they do not obtain raw local Git
+access merely by rendering a branch, diff, or worktree picker.
+
+
+ The public contract advertises driver capabilities and sends cwd-scoped worktree
+ and ref operations to the selected environment. It does not hand a browser raw
+ access to a local Git process.
+
+
+Two earlier boundaries matter here:
+
+- [Chapter 22](../worktree-topology/) traces the start-turn worktree
+ saga, including its server filesystem effects and failure seam.
+- [Chapter 27](../checkpoints-revert/) traces hidden checkpoint refs,
+ comparison modes, and the destructive restore/revert path.
+
+The workbench is where those results become visible: a terminal's cwd can be the
+thread worktree, a file panel reads it, and review/VCS panels query its Git state.
+That co-location is not a transaction. A terminal command, a Git operation, and a
+provider file edit still have their own ordering and error channels.
+
+## 7. Pull requests add host authorization and a second transport lane
+
+For each eligible project with a repository identity and supported host provider,
+the pull-request service builds a repository target. Projects without the required
+identity/provider/repository are skipped. It deduplicates worktrees of one repository
+for listing purposes, but retains alternate roots for host-viewer lookup so one
+unreadable checkout does not make a whole host look signed out. Host support and
+account identity are therefore server-side facts, not client guesses.
+
+For a write, the service checks two distinct gates in order:
+
+1. **Host/provider capability:** does the selected provider support this action,
+ merge strategy, update strategy, comment/review feature, or other requested
+ operation?
+2. **Fresh viewer permission:** does the currently signed-in account have the
+ necessary host-reported right now?
+
+Only then does it call the host provider. The server deliberately does not try to
+invent permissions it cannot query, such as whether a person may edit their own
+already-posted text; that decision stays with the host. Successful mutations bump
+server-side invalidation epochs so subsequent list/detail reads refresh rather than
+pretend cached rows are authoritative.
+
+
+
+Most PR controls use typed RPC. Diff content is exceptional: large, compressible
+diff slices travel through an authenticated HTTP endpoint, with a host-opaque cursor
+and truncation/next-cursor fields. The web route checks each connected environment's
+advertised pull-request capability and selects a capable source. Desktop inherits
+that web route. This pinned mobile source audit found terminal, file, and VCS paths
+(including a control that can open an existing PR externally), but no corresponding
+in-app pull-request list/detail route; that observation is not a promise about a
+future mobile release.
+
+
+ Server-side capability and fresh viewer checks guard writes. Successful mutations
+ invalidate shared epochs. Large diff slices use authenticated HTTP, while the web
+ route selects only environments that advertised pull-request capability.
+
+
+## What a surface can honestly claim
+
+| Surface | Strongly evidenced workbench roles | Important non-equivalence |
+|---|---|---|
+| Web | terminal renderer, files/editor, signed assets, VCS views, capability-gated PR route | cannot host the in-app Chromium preview |
+| Desktop | web roles plus Electron preview/webview and preview automation host | the web renderer alone does not acquire Electron IPC authority |
+| Mobile | native terminal rendering, file tree/preview, signed workspace assets, server-backed VCS state/actions | no matching desktop-style preview host or in-app PR list/detail route was found in this audit |
+| Provider session | provider harness work plus optional T3-scoped MCP preview toolkit | no generic ownership of client filesystem, terminal, or arbitrary MCP inventory |
+
+The durable lesson is to locate authority before adding an arrow: **environment
+server** for resources and host integrations, **desktop** for browser enactment,
+**provider adapter/session** for harness-native behavior, and **client** for the
+interactive view of a selected environment.
+
+
From 03177dd470807996955fa25cc2628bdea40b12d6 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:14 +0300
Subject: [PATCH 07/12] docs(book): publish part six client architectures
---
.../book/290-shared-client-runtime.mdx | 291 ++++++++++++++
src/content/book/300-web-runtime.mdx | 211 +++++++++++
src/content/book/310-web-product-surfaces.mdx | 213 +++++++++++
src/content/book/320-desktop-electron.mdx | 261 +++++++++++++
src/content/book/330-mobile-client.mdx | 358 ++++++++++++++++++
5 files changed, 1334 insertions(+)
create mode 100644 src/content/book/290-shared-client-runtime.mdx
create mode 100644 src/content/book/300-web-runtime.mdx
create mode 100644 src/content/book/310-web-product-surfaces.mdx
create mode 100644 src/content/book/320-desktop-electron.mdx
create mode 100644 src/content/book/330-mobile-client.mdx
diff --git a/src/content/book/290-shared-client-runtime.mdx b/src/content/book/290-shared-client-runtime.mdx
new file mode 100644
index 0000000..717e907
--- /dev/null
+++ b/src/content/book/290-shared-client-runtime.mdx
@@ -0,0 +1,291 @@
+---
+slug: shared-client-runtime
+order: 290
+number: "29"
+kind: chapter
+part: "Part VI · Client architectures: shared semantics, platform edges"
+partOrder: 6
+title: "Shared runtime: connections, state, and convergence"
+shortTitle: Shared client runtime
+summary: "packages/client-runtime makes web and mobile agree about environment selection, one-attempt RPC sessions, reconnect supervision, and snapshot-plus-cursor convergence. Its most consequential work is rejecting stale history rather than merely rendering new data."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace an environment from persisted target through resolution, supervision, and a scoped RPC session.
+ - Separate one WebSocket attempt from retry policy and from subscription replacement.
+ - Explain how keyed Effect Atom state machines own caches, shell state, and thread-detail state.
+ - Follow the snapshot, cursor, pagination, reconnect, and revert race guards that keep a client from resurrecting history.
+keywords: [client-runtime, environment, websocket, RPC, Effect Atom, snapshot, cursor, reconnect, retry, pagination, revert, cache]
+sourceAreas: [packages/client-runtime/src/connection, packages/client-runtime/src/rpc, packages/client-runtime/src/state, packages/contracts/src/orchestration.ts, apps/server/src/ws.ts]
+visuals: [shared-runtime convergence map, client convergence lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import ClientConvergenceLab from "../../components/ClientConvergenceLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Web and mobile do not share a DOM, storage adapter, or platform lifecycle. They do
+share the code that answers a harder question: *what is the currently justified
+local view of one server-owned environment?* `packages/client-runtime` is that
+shared seam. It turns a selected environment into a supervised RPC lease, then
+builds keyed state machines whose snapshots, cursors, and reducers converge after
+disconnects and reordered deliveries.
+
+This chapter calls the state “converged” only when the implementation has an
+authoritative snapshot or has caught a known snapshot up to its cursor. It does not
+mean every client has already rendered the same frame, nor that a local cache has
+become a second source of truth.
+
+
+ Do not credit the WebSocket client with all reconnection behavior. An RPC session
+ opens one socket attempt with its transport retries disabled. The environment
+ supervisor classifies failures and decides retry/backoff. A durable shell or
+ thread subscription switches to each resulting session and makes its own
+ snapshot/resume decision.
+
+
+## 1. An environment target names a route, not a live connection
+
+The target union has four variants: **Primary**, **Bearer**, **Relay**, and **SSH**.
+Primary holds direct HTTP and WebSocket bases and may operate without a bearer when
+the platform provides none. Bearer and SSH targets hold a `connectionId` whose
+profile/credential data is resolved locally. Relay names the target environment and
+obtains its relay bootstrap through the cloud/device path. SSH asks a
+platform-provided gateway to prepare a bootstrap, persists the returned SSH target
+profile, then authorizes the resulting endpoint with the bootstrap bearer.
+
+Those are target-specific preparation paths, not four client data models. Each
+successful path returns the same `PreparedConnection`: expected environment id,
+label, HTTP base, socket URL, optional HTTP authorization, and the original target.
+The resolver checks that a profile or authorization result names the expected
+environment; a stored connection id is therefore not treated as proof that it
+still points at the intended server.
+
+| Target | Resolving edge | Credential fact worth preserving |
+|---|---|---|
+| Primary | direct bases, plus optional platform bearer authorization | it may be unauthenticated at this layer |
+| Bearer | stored profile plus stored bearer, then remote authorization | profile and token must match the target environment |
+| Relay | cloud session/device identity → managed-relay bootstrap → DPoP authorization | relay bootstrap is distinct from a reusable direct bearer |
+| SSH | platform SSH gateway → fresh bootstrap → bearer authorization | the gateway, not shared TypeScript, owns the SSH transport |
+
+
+ The resolver switches on the four tagged targets and produces one prepared
+ connection shape. The driver then reports preparation/opening/synchronizing,
+ constructs a session through the session factory, and waits for that session's
+ readiness before returning a lease.
+
+
+The `EnvironmentRegistry` owns the next level of multiplicity. It loads persisted
+targets into a catalog, creates a scoped supervisor on demand per environment, and
+uses a per-environment lease lock so concurrent consumers acquire the same current
+supervisor instead of independently opening competing connections. Closing that
+service scope closes the supervisor and its resources. This is why an atom or RPC
+helper is keyed by `environmentId`: the key chooses an authority boundary, not
+merely a UI filter.
+
+## 2. One attempt has a deliberately narrow job
+
+`RpcSessionFactory.connect` creates connection/disconnection deferreds, opens a
+WebSocket with a 15-second open timeout, and creates Effect's socket RPC protocol
+with `retryTransientErrors: false` and `Schedule.recurs(0)`. It caches the initial
+`serverGetConfig` request; readiness requires both the socket-connect signal and
+that initial configuration. Its probe either calls `serverProbe` when the config
+advertises it or reuses the config request. The session exposes its closure as a
+transient transport failure.
+
+
+
+The seemingly strict “no retry” setting is a division of responsibility. The
+supervisor owns intent (`connect`/`disconnect`), network status, session generation,
+last failure, and the visible phases `available`, `offline`, `connecting`,
+`backoff`, `connected`, and `blocked`. It reports preparation, opening, and
+synchronization separately. Transient failures retry on the pinned 3/4/8/16-second
+ladder; blocked errors wait for an external signal rather than spinning. A stable
+connection for at least 30 seconds resets retry state. A manual retry resets it;
+a foreground probe can replace a stale mobile lease and make the immediate first
+reconnect without the first backoff rung. These are policy choices in the
+supervisor—not a promise that every failed operation is idempotent.
+
+
+ The session owns one attempt. The supervisor publishes the replacement session
+ and generation after a successful connection. `subscribeDynamic` switch-maps
+ its durable subscription to that current session; a transport failure waits for
+ the next session instead of performing a hidden socket retry inside the stream.
+
+
+## 3. Atom families supply one machine per key
+
+The runtime's helpers create Effect Atom families around an environment key and,
+where needed, an input or thread id. They route effects through the registry's
+environment lease. Query families watch connected generations, use stale-while-
+revalidate behavior, and have an idle TTL; subscription families follow the live
+environment stream. Command helpers execute through an `AtomRegistry`, and their
+scheduler can be parallel, FIFO serial, single-flight, or latest-wins per key.
+
+This is not a global “store of all server objects.” The registry is the mounted
+atom runtime; individual families own the lifetime of a particular observable
+state machine. `createEnvironmentShellAtoms` and
+`createEnvironmentThreadStateAtoms`, for example, return environment/thread-keyed
+atoms. The app surface decides how to mount and render them; the shared package
+decides how their data reaches a justified state.
+
+
+ Platform capabilities inject storage, primary auth, relay identity, or an SSH
+ gateway. Shared runtime code does not pretend that a browser can tunnel SSH or
+ that mobile persistence is web localStorage. It does centralize the target,
+ authorization, supervisor, RPC, cache, cursor, reducer, and state-machine
+ semantics that must not drift between clients.
+
+
+## 4. Caches accelerate first paint; snapshots re-establish authority
+
+Shell and eligible settled thread state can be read from `EnvironmentCacheStore`.
+That starts the UI in `cached`, rather than erasing a useful previous view. Active
+or starting thread sessions are deliberately not persisted on every streamed
+change: their large, rapid payloads remain server-authoritative until settling.
+Persist writes travel through a one-item sliding queue and a 500 ms debounce, so
+cache encoding is not put on the event-stream hot path.
+
+But a cursor alone is insufficient across a new RPC session. For the shell,
+`lastAuthoritativeSession` distinguishes a foreground resubscription on the same
+session (resume from the in-memory snapshot cursor) from a replacement session
+(fetch HTTP snapshot first). If that refresh fails, it omits the cached cursor so
+the server must send a complete socket snapshot. Threads similarly seed a warm
+cache's `lastSequence`, fetch an HTTP snapshot if they have no local detail, then
+subscribe with `afterSequence` only when they have a current detail.
+
+The server's paired protocol makes that defensible: it attaches live delivery
+before loading snapshot/replay state, captures a head for the bounded replay, then
+emits the buffered tail. A completion marker, when the server advertises support,
+means the buffered work before that marker has been delivered; it is not a claim
+that future domain events have stopped.
+
+
+ R[Resolver]
+ R --> P[Prepared connection]
+ P --> G[Environment registry + supervisor]
+ G --> S[One-attempt RPC session\nnew generation on reconnect]
+ S --> H[Shell state machine]
+ S --> D[Thread state machine]
+ C[(platform cache)] --> H
+ C --> D
+ H -->|HTTP snapshot if new session| X[authoritative snapshot]
+ D -->|HTTP snapshot if needed| X
+ X -->|afterSequence| L[bounded replay + live tail]
+ L -->|newer only| H
+ L -->|newer only| D
+ D --> E[history epoch + page watermark]
+ E --> D`} />
+
+
+## 5. The sequence rule handles snapshot overlap and duplicate delivery
+
+The shell reducer replaces state on a snapshot and applies a shell event only when
+its sequence is greater than `snapshotSequence`. Thread state does the analogous
+check against `lastSequence`; an event at or below it is ignored. This is expected
+to discard replay/live overlap, an event already represented in an HTTP snapshot,
+and duplicate delivery after resubscription. It is not an attempt to make delivery
+exactly once.
+
+When the cursor is missing, ahead of the server, or beyond the configured bounded
+gap, the server chooses a fresh snapshot rather than silently truncating replay.
+On a fresh thread snapshot, the client sets the new sequence and **replaces all
+loaded history**, then advances the history epoch. That replacement matters: a
+client disconnected during a revert has no remaining per-event removal to apply to
+an older cached page.
+
+
+ Shell and thread folds ignore events at or below their watermarks. The server
+ falls back to snapshots for invalid/oversized cursor gaps. Thread snapshot
+ application increments the history epoch and replaces loaded windowed history,
+ while a `thread.reverted` event also increments that epoch before its reducer
+ filters loaded turns.
+
+
+## 6. Pagination is a second stream race, not “just append older rows”
+
+Thread detail may begin with a recent turn window and a keyset `beforeCursor`.
+“Load earlier” is serialized through a sliding one-item request queue, no-ops while
+one page is loading, and is enabled only after the connected server advertises
+pagination. The client merges only windowed collections from the older snapshot;
+newer thread metadata stays authoritative. Identity de-duplication protects
+overlapping rows even if a server/cursor edge returns them.
+
+The awkward cases are intentional design constraints:
+
+- **Stale page:** a response whose snapshot sequence is below loaded state, or
+ whose epoch differs from the request's epoch, is discarded under the same lock
+ used for live stream application.
+- **Page ahead of live cursor:** it is parked instead of merged. Its
+ thread-scoped watermark may include out-of-window updates the subscription has
+ not delivered; merging immediately could show them and later replay them again.
+ The parked page merges only once live `lastSequence` reaches that watermark.
+- **Snapshot or revert during page fetch:** both rewrite the meaningful history
+ and bump the epoch. The in-flight page cannot merge afterwards, so removed turns
+ cannot be resurrected.
+- **Reconnect to an older server:** pagination support is reset on disconnect and
+ re-read from the new session config. A cached window is dropped before resume if
+ the replacement server lacks pagination, because it could not supply the missing
+ older pages.
+
+
+ The reducer can safely ignore an old *event* by sequence. An older-page HTTP
+ response contains a history window, so it needs the additional epoch and
+ page-watermark checks. A keyset cursor remaining syntactically usable after a
+ revert does not authorize an in-flight response captured before that revert to
+ merge.
+
+
+
+
+The lab is intentionally a small model rather than a network mock. Each control
+advances one visible transition: it can replace a snapshot, reject a duplicate,
+choose snapshot recovery for a gap, replace the session, invalidate history on a
+revert, or park/merge/reject an older page. Its rules mirror the boundaries above;
+it does not claim to emulate Effect scheduling or a real server.
+
+## 7. Read a reconnect as a proof obligation
+
+The practical question after a disconnect is not “did the socket reopen?” It is:
+
+1. Did a target resolve and a supervisor publish a ready session?
+2. Did the relevant state machine obtain an authoritative snapshot for that session
+ or a bounded replay after a known snapshot cursor?
+3. Did its reducer reject overlap, and—if it is a thread—did its page merge survive
+ both the sequence/watermark and history-epoch tests?
+
+Only then is it reasonable for the UI to label the state live. That discipline
+lets web and mobile differ at the platform edge while sharing the same answer to
+the expensive correctness questions: what may be cached, what may be resumed, and
+what must be thrown away.
+
+
diff --git a/src/content/book/300-web-runtime.mdx b/src/content/book/300-web-runtime.mdx
new file mode 100644
index 0000000..9a5d8b6
--- /dev/null
+++ b/src/content/book/300-web-runtime.mdx
@@ -0,0 +1,211 @@
+---
+slug: web-runtime
+order: 300
+number: "30"
+kind: chapter
+part: "Part VI · Client architectures: shared semantics, platform edges"
+partOrder: 6
+title: One React renderer, three runtime edges
+shortTitle: Web runtime
+summary: "The web client keeps one route tree and environment-oriented state model across hosted, locally served, and Electron renderer deployments. Platform differences enter at history, authentication, native-host, and transport boundaries—not in a forked product model."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Separate the hosted, locally served, and Electron-renderer deployment choices from the shared React application.
+ - Trace entry, AppRoot, atom registry, router history, generated route tree, and environment-scoped session state.
+ - Explain why virtualized timelines and row-local structural sharing are implementation choices, not performance benchmarks.
+ - Locate tracing export and the browser terminal at their respective client/server authority boundaries.
+keywords: [web, Electron, history, router, environment, atoms, virtualization, tracing, terminal, performance]
+sourceAreas: [apps/web/src/main.tsx, apps/web/src/AppRoot.tsx, apps/web/src/router.ts, apps/web/src/connection, apps/web/src/components/chat, apps/web/src/components/ThreadTerminalDrawer.tsx]
+visuals: [web runtime architecture and hot path]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+The web client is not three applications. It is one React renderer deployed in
+three circumstances with materially different edges:
+
+- **Hosted web** serves the app as a regular browser application and can connect
+ to environments discovered or paired through the product's connection model.
+- **Locally served web** is the same browser application delivered by a local T3
+ server; the server distribution copies the already-built web bundle.
+- **Electron renderer** loads that renderer from a file-backed shell, gains a
+ preload bridge and Electron-only hosts, and uses hash history so route paths do
+ not become file paths.
+
+This is a deployment distinction, not evidence of three independent thread
+stores. The selected environment remains the authority for sessions, snapshots,
+commands, and streamed updates in every case.
+
+
+
+
+ Electron changes browser integration and can assemble a managed backend elsewhere
+ in the desktop application. The renderer's isElectron branch alone
+ does not prove that the selected environment is local, connected, online, or
+ authoritative for a particular thread.
+
+
+## Entry establishes the small platform seam
+
+At module load, `isElectron` means that the preload bridge placed
+`window.desktopBridge` on the page. The entry then chooses TanStack Router's hash
+history for Electron and browser history otherwise, creates the router once, and
+performs Electron-specific window-control class synchronization. Cloud
+authentication is optional: it is enabled only when both a publishable key and
+public cloud configuration are present; Electron receives its Electron Clerk
+provider and passkey support, while a normal browser receives the browser provider.
+In both branches, the child is the same `AppRoot`.
+
+The local CLI packaging path provides another useful boundary: it packages a
+built web client as server static output. That says how a browser gets the assets;
+it does not merge the React process and the server process into one memory space.
+
+
+ The inspected entry constructs one router from one route tree. Its explicit
+ Electron divergence is hash history, document integration, and an Electron
+ authentication provider; otherwise the same AppRoot is rendered. The CLI build
+ copies built web assets into the server distribution, which is delivery reuse—not
+ proof that browser state is server state.
+
+
+## AppRoot owns renderer-wide state, then routes
+
+`AppRoot` supplies one Effect atom registry around both routed content and its
+longer-lived hosts. The router renders the generated file-route tree. Preview
+automation hosts, the Electron browser host, and the quit-hold overlay sit beside
+the router rather than inside a route. The source comment makes the reason
+concrete: Electron webviews must survive a route transition, while still sharing
+the registry with the routed UI.
+
+
+
+
+
+The file routes divide broadly into connection/pairing, settings, usage, project
+redirects, and the `_chat` shell. The chat subtree provides an index, a draft view,
+a scoped `$environmentId/$threadId` view, and pull-request work. A URL therefore
+names a presentation location and a scoped thread identity; it is not a substitute
+for the environment session or a serialized thread snapshot.
+
+
+ The browser host and automation hosts are renderer-wide siblings of
+ `RouterProvider`. Preview use still has a runtime gate: a regular web route does
+ not acquire an in-app Chromium host merely by navigating to a chat route.
+
+
+## Environment atoms keep connection multiplicity out of route components
+
+The web layer creates one connection atom runtime, uses it to make an environment
+catalog and per-environment session atoms, and exposes hooks that read an
+environment id rather than a process-global server. A prepared connection carries
+the HTTP base URL for the named environment; session state is independently read
+from that environment's authentication endpoint. The registry is renderer-wide and
+is reset only by the test helper, not on each route render.
+
+
+
+
+
+This pattern matters because a route can be active while its environment is
+reconnecting, and a client can hold several environment presentations at once.
+The route tells the UI *which* scoped thread to request; the registry and
+environment atoms answer *through which prepared connection* it can be reached.
+
+
+ BH[browser history]
+ E[Electron file shell] --> HH[hash history]
+ BH --> R[one router + generated route tree]
+ HH --> R
+ R --> A[AppRoot + atom registry]
+ A --> C[chat route: environmentId + threadId]
+ A --> X[Electron BrowserHost\noutside router]
+ C --> EC[environment catalog + session atoms]
+ EC --> S[snapshot + resumable stream]
+ S --> T[LegendList timeline\nstable row identities]
+ S --> SB[sidebar\nmemoized rows]
+ C --> TS[terminal session attach]
+ TS --> PTY[server-owned PTY]
+ A -. OTLP batches .-> O[primary environment\nobservability endpoint]`} />
+
+
+## The chat hot path constrains work per update
+
+The message timeline uses `LegendList`, stable row ids, estimated item size, end
+anchoring, and explicit visible-content maintenance. Before it reaches the list,
+the row builder structurally reuses a prior row object whenever its relevant fields
+are unchanged. Shared callbacks and non-row-scoped state travel through React
+context so they do not need to become freshly allocated props for every row.
+
+
+
+
+
+The sidebar follows the same intent at a different scale. It derives a visible
+order—pinned, active, a route-preserved snoozed row, and the visible settled tail—
+then stores ordering and lookup data in refs for callbacks. The comment is explicit
+that passing fresh collection identities through row props would defeat
+memoization during streaming. That is a narrowly scoped update-locality decision;
+it is not a claim that every update repaints only one DOM node.
+
+
+
+
+ The timeline virtualizes items and preserves unchanged row identities; the
+ sidebar avoids collection-dependent row callbacks during streaming. These are
+ implementation choices intended to bound avoidable render work. The inspected
+ source supplies no frame-time, CPU, heap, or maximum-list-size measurement, so
+ none should be inferred from them.
+
+
+## Trace export is observability, not an application event log
+
+Client tracing builds an OTLP delegate whose URL is resolved from the primary
+environment and labels the resource as `t3-web` with `browser` or `electron` mode.
+It replaces and closes an earlier tracing runtime when configuration changes, and
+logs a sanitized configuration failure. This is a best-effort observability
+pipeline: successful configuration makes a delegate available locally; it does not
+prove remote collector durability or replay a product command.
+
+
+
+## A browser terminal renders a remote screen
+
+The terminal drawer creates a Ghostty surface in the browser, but its session is
+addressed by environment, thread, terminal id, cwd, optional worktree, and runtime
+environment. User input becomes a typed write command; resize becomes a typed
+resize command; the renderer hydrates the terminal surface from the attached
+session's buffered text and status. The client owns the screen component and local
+focus/selection behavior. The environment server owns the PTY, as Chapter 28
+establishes.
+
+
+ Virtualization, memoization, bounded detail windows, and worker-pool sizing are
+ defensible design evidence. A statement such as “the chat stays at 60 fps” needs
+ a workload, device/browser, instrumentation, and observed result; it is not
+ established by an import of `LegendList` or a `memo` call.
+
+
+
diff --git a/src/content/book/310-web-product-surfaces.mdx b/src/content/book/310-web-product-surfaces.mdx
new file mode 100644
index 0000000..a9bda49
--- /dev/null
+++ b/src/content/book/310-web-product-surfaces.mdx
@@ -0,0 +1,213 @@
+---
+slug: web-product-surfaces
+order: 310
+number: "31"
+kind: chapter
+part: "Part VI · Client architectures: shared semantics, platform edges"
+partOrder: 6
+title: One thread, many deliberate projections
+shortTitle: Web product surfaces
+summary: "The composer, timeline, Agents panel, review panel, and sidebar do not own competing thread records. They select and project one canonical environment-scoped thread through different interaction contracts, including optimistic local affordances that must yield to canonical state."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace composer content, attachments, provider commands and skills to a durable turn lifecycle without confusing UI insertion with execution.
+ - Distinguish an optimistic client affordance from accepted durable state and streamed projection.
+ - Explain quiet work-log and Agents views as different folds of canonical thread activity.
+ - Compare review modes and sidebar sorting/pinning as surface-specific selections of one scoped thread model.
+keywords: [composer, attachment, command, skill, optimistic UI, Agents, work log, review, sidebar, pinning, projection]
+sourceAreas: [apps/web/src/components/chat, apps/web/src/components/AgentsPanel.tsx, apps/web/src/components/DiffPanel.tsx, apps/web/src/components/Sidebar.tsx, packages/client-runtime/src/state]
+visuals: [interactive thread projection lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import ThreadProjectionLab from "../../components/ThreadProjectionLab.astro";
+
+The web application has many ways to look at a thread. That does not make each
+panel a source of truth. The canonical thread projection carries its messages,
+activities, turn/session state, checkpoints, lifecycle overlays, and provider
+observations. Web surfaces take selections from that material:
+
+- the composer prepares the next intent;
+- the timeline favors conversation and quiet grouped work;
+- Agents unfolds retained task evidence into a roster;
+- review selects one comparison basis;
+- the sidebar sorts, shelves, and pins thread shells.
+
+All of those are scoped by environment and thread identity. A title, pin order,
+or status pill in a sidebar is not a competing copy of the conversation.
+
+
+ The timeline intentionally hides or groups some task rows. The Agents panel
+ makes those task details visible. Neither behavior deletes the underlying
+ activity or proves that the other surface is stale; they answer different
+ navigation questions from the same retained evidence.
+
+
+## The composer composes intent; the server accepts or rejects it
+
+The composer combines prompt text with selected provider/model/mode controls,
+attachments, terminal and preview context, pending approval/input/review states,
+and menu affordances. Before a regular turn is sent, web code persists selected
+model and modes on the thread. The server's normalizer then owns timestamp,
+workspace, and attachment normalization before dispatch. A successful dispatch
+result names the accepted command/sequence boundary; it is stronger than a local
+button state, but later provider work still crosses the post-commit reactor seam
+described in Chapters 10–12 and 23.
+
+
+ The web persists chosen mode/model before sending a turn; normalization and
+ dispatch occur at the server boundary. An accepted result records a command
+ outcome, while provider execution remains a subsequent asynchronous concern.
+ The composer cannot turn a locally selected file, model, or button label into a
+ completed provider turn by itself.
+
+
+### Attachments are references with a lifecycle
+
+Attachments are not just inline text pasted into a browser control. The server
+normalizes attachment paths under its attachment root and later collects projected
+attachment references for cleanup. A rendered attachment token therefore must not
+be treated as proof that arbitrary files on the browser's machine are readable, or
+that an attachment will survive a thread deletion indefinitely.
+
+
+ The inspected paths normalize and constrain attachment paths, and projection
+ processing collects retained references before best-effort cleanup. This is an
+ authority and lifecycle boundary; it does not establish a client-side filesystem
+ sync protocol.
+
+
+### Commands and skills are prompt affordances, not generic jobs
+
+In the `/` menu, web combines built-in commands with provider slash commands and
+eligible provider skills. Enabled skills join that menu only when the relevant
+setting is enabled; a visible skill can suppress a same-named provider slash
+command. A `$` skill choice replaces prompt text with `$skill ` syntax. Provider
+slash command selection similarly inserts `/command ` text. These operations are
+useful because the selected provider sees its native prompt convention; they do not
+by themselves create a durable task, plan step, or scheduler assignment.
+
+
+ Commands and skills arrive in provider-instance inventory and are filtered for
+ this web menu. Selecting a command or skill edits the prompt according to the
+ UI policy; durable work begins only if a later turn is actually accepted.
+
+
+## Optimistic state is a temporary projection with a surrender rule
+
+Some interactions must feel immediate before the durable stream reflects them.
+The sidebar's pin drag keeps an optimistic ordering snapshot containing the desired
+order, its observed pin-order keys, and the keys it wrote. It retains that local
+order until canonical state confirms it—but releases it if membership changes, a
+foreign concurrent key lands, all local assignments land, or canonical order
+already agrees. That last set of conditions is the important contract: local
+optimism may bridge latency, but it must yield to the canonical thread projection.
+
+
+
+
+ The web release logic recognizes a pin key that it did not write as a foreign
+ update and drops the optimistic override. The server's lifecycle commands remain
+ responsible for projected pin state; the browser merely presents a short-lived
+ preferred order while that state converges.
+
+
+## Quiet log and Agents roster intentionally disagree in granularity
+
+The timeline's quiet-work policy groups direct agents by spawn turn and workflow
+members under their coordinator. It replaces a flood of task lifecycle rows with a
+call to action that opens Agents. The client-runtime fold reads retained `task.*`
+and agent-owned tool activity into a source-neutral roster, tolerating missing
+starts, late completion, reactivation, and session death. The web Agents panel then
+shows rows and aggregate information from that folded model.
+
+
+ The quiet timeline deliberately condenses activity; the Agents panel expands
+ derived task state. Both consume retained thread evidence through client logic.
+ In particular, elapsed display is a presentation concern and a task rollup is
+ provider-observed activity data—not an invoice or a global scheduler record.
+
+
+## Review modes select a comparison, not a different thread
+
+The review panel can select working-tree changes, branch changes, the latest turn,
+or an earlier turn. A turn selection derives an adjacent checkpoint range; a
+non-turn selection asks the environment for a working-tree or branch diff. The
+panel refreshes an applicable branch diff on browser focus and after the newest
+completed turn changes. Those are different evidence sources and cache scopes, but
+they remain anchored to the active environment/thread/workspace context.
+
+
+ “Latest turn” is a checkpoint-based turn comparison in this path. “Working tree”
+ and “Branch changes” are live VCS-oriented queries. A rendered diff does not
+ certify that it is complete, untruncated, merged, committed, or reviewed by a
+ human; the panel retains explicit loading, error, and truncation states.
+
+
+## Sidebar shelves and sorts the same thread shells
+
+The sidebar creates one visible ordering from pinned, active, route-preserved
+snoozed, and visible settled threads. Pinning is a lifecycle overlay, not a new
+thread type: the durable model provides `pinOrderKey`, snooze, settlement, and
+archive fields; the UI applies shelving and sorting policy. The active route is
+special-cased so a snoozed thread reached through a deep link does not disappear
+behind a collapsed shelf.
+
+
+ The durable thread model carries lifecycle and pin fields. The sidebar chooses
+ visible shelves and order, retaining an active routed snoozed row for navigation
+ continuity. Drag state may be optimistic, but it has explicit surrender rules.
+
+
+## Exercise the projection boundary
+
+The lab keeps one canonical thread scenario on the left and lets each surface
+choose its own projection. Change the scenario and selected surface, then compare
+the displayed pin, task, review, and local-view fields to see why a quiet timeline
+and a detailed Agents panel can honestly look different without holding different
+canonical threads.
+
+
+
+
+
+
+ The inspected surfaces all take environment-scoped thread data or activities and
+ apply different filters, folds, and comparison selectors. It is therefore a
+ reasonable architectural inference that their visible disagreement is usually
+ intentional projection policy rather than evidence of competing durable thread
+ stores. This inference does not rule out an individual implementation bug or a
+ temporarily stale client connection.
+
+
+
diff --git a/src/content/book/320-desktop-electron.mdx b/src/content/book/320-desktop-electron.mdx
new file mode 100644
index 0000000..d40bc15
--- /dev/null
+++ b/src/content/book/320-desktop-electron.mdx
@@ -0,0 +1,261 @@
+---
+slug: desktop-electron
+order: 320
+number: "32"
+kind: chapter
+part: "Part VI · Client architectures: shared semantics, platform edges"
+partOrder: 6
+title: "Electron desktop: one renderer, explicit native authority"
+shortTitle: Electron desktop
+summary: "The desktop reuses the web renderer but not browser authority: Electron main assembles effects, supervises local backend children, exposes a narrow preload bridge, and separately brokers WSL, SSH, previews, menus, updates, telemetry, and shutdown."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Locate authority across Electron main, preload, renderer, local backend children, WSL, and SSH.
+ - Trace desktop boot through endpoint selection, protocol and IPC setup, backend readiness, and window creation.
+ - Distinguish the host-local primary and Windows-only WSL pool instances from an SSH remote-environment gateway.
+ - Read fd3, fd4, and fd5 as separate bootstrap, telemetry, and diagnostics-demand channels.
+keywords: [electron, desktop, preload, backend pool, WSL, SSH, telemetry, preview, shutdown]
+sourceAreas: [apps/desktop/src/main.ts, apps/desktop/src/app, apps/desktop/src/backend, apps/desktop/src/wsl, apps/desktop/src/ssh, apps/desktop/src/preload.ts, apps/desktop/src/preview]
+visuals: [desktop process topology, interactive boot readiness shutdown lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import DesktopProcessLab from "../../components/DesktopProcessLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Electron desktop is not “the web app in a window.” The renderer shares much of the
+web client, but its extra powers remain on the other side of two boundaries:
+**Electron main** owns native APIs and child processes; **preload** exposes a
+curated set of IPC calls; **renderer** renders product state and asks through that
+bridge. The environment server still owns the workbench and orchestration services
+described in earlier chapters.
+
+
+ The ordinary primary is a pooled child on the host—macOS, Linux, or Windows. On
+ Windows, WSL-only mode can make the primary itself a Linux child; dual mode keeps
+ the Windows primary and registers a second WSL pool instance. An SSH target is
+ neither: it is a remote environment reached through the desktop SSH gateway and
+ its tunnel lifecycle. Do not draw SSH as another local pool member.
+
+
+## 1. Authority is layered before it is interactive
+
+The main entry builds an Electron service layer, desktop foundation services,
+server-exposure and preview services, window services, the backend pool, the WSL
+orchestrator, local-environment authentication, then application services including
+lifecycle, menu, shell, SSH, and updates. Strict pre-ready configuration wraps the
+runtime so Electron options that must precede readiness are acquired before ordinary
+startup work.
+
+At startup, main installs the shell environment, resolves early Linux password-store
+options, selects Electron user data, loads settings, configures app identity and
+lifecycle/Clerk hooks, then awaits Electron readiness. Only after that does it
+configure identity again, application menu, updates, Linux URL handling, and the
+desktop bootstrap. This is a dependency order, not an instruction for a renderer to
+call native modules.
+
+The preload uses `contextBridge.exposeInMainWorld` to publish `desktopBridge` with
+specific operations: local bootstrap credentials, connection catalog, SSH discovery
+and connection, server exposure and WSL settings, file/dialog helpers, menu/window
+events, update actions/state, and preview actions. It does not publish raw
+`ipcRenderer`, Node process spawning, or Electron `BrowserWindow` control to page
+code. Main registers the IPC handlers before the backend is started.
+
+
+
+
+ The web runtime selects an Electron-aware route when available, while the desktop
+ implementation keeps native service assembly and the curated preload surface out
+ of the renderer. A renderer request is therefore an IPC request, not authority to
+ invoke arbitrary Electron APIs.
+
+
+## 2. Bootstrap chooses an endpoint before it opens a window
+
+Desktop bootstrap obtains the pool's primary instance, selects a configured port or
+scans upward from the desktop default across `127.0.0.1`, `0.0.0.0`, and `::`, then
+configures server exposure from persisted settings. Local-only binds loopback;
+network-accessible mode may bind broadly and advertises a usable LAN endpoint. If no
+LAN address is available, it falls back to local-only rather than pretending the
+network endpoint exists.
+
+That resolved backend URL is also an input to desktop protocol registration: the
+renderer target is the development server in development and the backend HTTP origin
+in production, while the protocol knows the backend origin for its own routing
+rules. IPC handlers are installed next. In WSL-only mode the window service can show
+a connecting splash; otherwise the primary starts, and WSL reconciliation is forked
+so a slow `wsl.exe` cold start does not block the primary readiness path.
+
+
+
+
+ |curated preload IPC| M[Electron main]
+ M -->|protocol + window + menu + updates| R
+ M --> C{Primary configuration}
+ C -->|ordinary host-local| P[Host-local primary\nmacOS / Linux / Windows]
+ C -->|Windows WSL-only| WP[WSL primary]
+ M -. dual-mode registration .-> W[Optional WSL secondary]
+ M -->|fd3 bootstrap + fd4 telemetry| P
+ P -->|fd5 diagnostics demand| M
+ M -->|stdin bootstrap| WP
+ M -->|stdin bootstrap| W
+ P -->|HTTP readiness| M
+ WP -->|HTTP readiness| M
+ W -->|HTTP readiness| M
+ R -->|SSH IPC request| M
+ M -->|SSH gateway / tunnel| S[Remote SSH environment]
+ R -->|preview IPC| M
+ M -->|validates and owns| G[Guest Chromium WebContents]
+ M -->|SIGTERM + grace| P
+ M -->|SIGTERM + grace| WP
+ M -->|SIGTERM + grace| W
+ M -->|disconnect lifecycle| S`} />
+
+
+## 3. The pool supervises local instances, not all environments
+
+`DesktopBackendPool` makes one primary instance for its lifetime. It can register
+additional instances with a child scope; unregistering a non-primary closes that
+scope and invokes the instance's stop finalizer. The pool refuses to unregister the
+primary. Each instance has independent configuration resolution, state, mutex,
+restart loop, readiness flag, and active child process. A ready primary calls the
+window service with its HTTP base URL; a ready WSL secondary does not replace that
+window callback.
+
+The WSL orchestrator reconciles persisted enablement/distro settings after primary
+start and after its IPC settings changes. In dual mode, it allocates a distinct port,
+registers `wsl:default` or `wsl:`, and starts that child. It deliberately
+does nothing parallel in WSL-only mode because the primary itself is then resolved
+as WSL. The Linux child omits the Windows T3 home so it does not share the primary's
+database, and the renderer discovers it through the desktop-local bootstrap/saved-
+environment path.
+
+
+
+
+
+SSH takes a different route. The preload asks main to discover hosts, ensure a
+target, bootstrap a bearer session, issue a WebSocket token, inspect session state,
+or disconnect. `DesktopSshEnvironment` delegates those operations to the SSH
+environment manager with desktop password prompts. A ready remote descriptor is a
+normal environment connection; it does not create a local `DesktopBackendInstance`.
+
+
+ The host-local primary and WSL instances have desktop bootstrap/process
+ configuration. SSH remains one
+ branch of the client connection-target model and is entered through the desktop
+ SSH IPC surface. The distinction preserves separate filesystem, process, and
+ shutdown ownership.
+
+
+## 4. Three file descriptors carry three different concerns
+
+For the ordinary host-local primary, the child is run in Node mode and receives its JSON
+bootstrap envelope on **fd3** (`--bootstrap-fd 3`). The envelope specifies desktop
+mode, `noBrowser`, port, home, bind host, bootstrap token, exposure settings, and
+the telemetry descriptors. It sets **fd4** as parent-to-child host telemetry and
+**fd5** as child-to-parent telemetry control.
+
+fd4 starts with a hello and carries sampled Electron-main host/power/process data.
+fd5 carries control messages such as `setDiagnosticsDemand`: main tracks the demand
+per backend source, samples expensive Electron app metrics only while at least one
+source demands diagnostics, and removes demand when a control stream stops or a
+backend exits. It is a demand signal, not a general command channel and not a claim
+that the server can directly inspect every renderer or remote machine.
+
+
+
+WSL is the explicit exception. `wsl.exe` drops extra file descriptors across the
+Windows-to-Linux handoff, so the WSL child uses stdin (`--bootstrap-fd 0`) for the
+bootstrap envelope. The packaged Windows sidecar is also not passed to Linux;
+resource telemetry is unavailable for that WSL backend configuration. The fd3/fd4/
+fd5 diagram therefore describes the primary local spawn, not a universal property
+of every environment.
+
+
+ The primary configuration assigns telemetry fd 4 and control fd 5 beside its fd-3
+ bootstrap. The desktop process runner treats control as an output stream from the
+ child, disables demand when that stream ends, and uses stdin only for the WSL
+ forwarding exception.
+
+
+## 5. Readiness opens the main window; preview guests stay in main
+
+Starting a child is not readiness. The manager repeatedly probes its configured
+HTTP URL while the child remains alive. On success it resets that instance's restart
+attempt and invokes the instance callback. For the primary, the pool passes that URL
+to the window service, which handles the main-window-ready transition. A slow WSL
+cold boot gets another probe interval rather than making the primary window wait.
+
+
+
+The desktop preview is another useful boundary test. The renderer can request tab,
+navigation, zoom, screenshot, picking, recording, and automation operations through
+the curated preload API. Electron main's preview manager validates and owns the
+guest Chromium `WebContents`. Server preview state can coordinate a tab record, but
+neither a remote client nor the page renderer thereby owns a browser process.
+
+
+ HTTP readiness is the local-child gate used by the primary window callback. Preview
+ actions cross renderer-to-main IPC, where the desktop preview manager validates
+ and retains the guest WebContents instead of exposing it to renderer code.
+
+
+## 6. Menus, updates, and termination are stateful platform edges
+
+Main configures the application menu and update service after Electron becomes ready;
+the renderer receives menu actions and update state through subscription-shaped
+preload calls and requests update checks/download/install through named IPC methods.
+Those UI messages are not the updater itself. The lifecycle code makes an important
+exception for updater-controlled quit: preventing the following `before-quit` can
+break Electron's native install/relaunch sequence, especially on macOS.
+
+For ordinary quit, the lifecycle marks the app as quitting, requests desktop
+shutdown, waits for the program's finalization route, and then allows Electron to
+quit. The finalizer lists every pool instance and stops them concurrently, so WSL is
+not left to an OS hard kill. Individual backend stops cancel restart work, request
+SIGTERM with a grace period, and clear readiness. A child that exits unexpectedly
+while still desired schedules its own restart; that is intentionally different from
+the explicit shutdown path. On macOS, closing every window does not itself quit;
+activation can re-open the window if shutdown has not begun.
+
+
+
+
+
+
+ The code establishes a graceful stop path for locally supervised backends, but it
+ cannot promise that every child, updater, operating system, or remote SSH process
+ reaches a clean application-level checkpoint. “Restart” is also context-specific:
+ an unexpected local child exit can retry, a deliberate pool stop suppresses that
+ retry, and updater-controlled quit follows Electron's updater sequence.
+
+
+
+ The desktop program finalizer stops all current pool instances. Preload separately
+ exposes update state/actions, while the lifecycle permits the updater's own quit
+ sequence instead of treating it as an ordinary cancellable window close.
+
+
+## Work the process map
+
+Use the lab to choose the host-local primary, Windows + WSL delivery, or SSH gateway, then
+advance from boot to readiness to shutdown. The labels intentionally keep transport
+and authority separate: a remote environment can be fully connected without being a
+locally pooled process.
+
+
+
+The durable rule is compact: **renderer asks; preload narrows; main owns native
+effects; local pool owns local children; WSL is optional local Linux delivery; SSH
+owns a remote connection path.** Keeping those nouns separate prevents both unsafe
+desktop APIs and misleading topology diagrams.
+
+
diff --git a/src/content/book/330-mobile-client.mdx b/src/content/book/330-mobile-client.mdx
new file mode 100644
index 0000000..f219337
--- /dev/null
+++ b/src/content/book/330-mobile-client.mdx
@@ -0,0 +1,358 @@
+---
+slug: mobile-client-continuity
+order: 330
+number: "33"
+kind: chapter
+part: "Part VI · Client architectures: shared semantics, platform edges"
+partOrder: 6
+title: "Mobile: persistence, outbox, sharing, and native systems"
+shortTitle: Mobile continuity
+summary: "The Expo client reuses T3 Code's connection and projection semantics but owns a deliberately mobile durability layer: cached snapshots in SQLite, credentials in secure storage, drafts and commands in atomic files, a transactional share inbox, native rendering bridges, and an OTA restart gate that yields to unsaved work."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Locate the React Native shell, shared connection runtime, platform services, and native UI boundaries.
+ - Separate cached server projections, credentials, composer drafts, queued commands, incoming shares, and authoritative server state.
+ - Trace an offline command from optimistic enqueue through durable confirmation, shell convergence, retry, and cleanup.
+ - Explain why OTA application waits for persistence and a genuinely safe background transition.
+keywords: [mobile, Expo, React Native, connection runtime, SQLite, SecureStore, drafts, outbox, sharing, native terminal, native diff, Live Activity, OTA]
+sourceAreas: [apps/mobile/app.config.ts, apps/mobile/src/App.tsx, apps/mobile/src/Stack.tsx, apps/mobile/src/connection, apps/mobile/src/persistence, apps/mobile/src/state, apps/mobile/src/features/sharing, apps/mobile/src/features/updates, apps/mobile/modules]
+visuals: [mobile ownership graph, offline and OTA continuity simulator, native capability matrix]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import MobileContinuityLab from "../../components/MobileContinuityLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+The phone is a **remote-native client**, not a pocket-sized T3 server. It owns
+navigation, presentation, connection credentials, caches, unsent intent, and
+device integrations. The selected environment still owns projects, threads,
+workspaces, provider processes, terminals, Git, and authoritative orchestration
+state.
+
+That distinction makes the mobile implementation easier to read. Most of its
+special machinery answers one of two questions:
+
+1. How can a device remain useful while its process, network, and foreground state
+ are unstable?
+2. Which work belongs in a native surface without moving server authority onto the
+ phone?
+
+
+ A cached thread can render while disconnected, a draft can survive a restart,
+ and a queued command can wait for delivery. None of those records proves that an
+ environment accepted a turn. The server's synchronized shell/detail projections
+ and command result remain the authority after reconnection.
+
+
+## 1. The application shell wraps one shared connection runtime
+
+`App.tsx` mounts one Effect Atom registry, cloud-auth and appearance providers,
+gesture/keyboard/safe-area hosts, the incoming-share provider, and a static native
+navigation tree. Deep links wake navigation, except for Expo's development-client
+and share-extension lifecycle URLs: the latter is deliberately ignored because the
+**durable share inbox**, not a transient URL, owns presentation.
+
+
+
+The connection runtime is not a mobile rewrite of Chapter 29. It merges the shared
+`Connection.layer` and shared shell/thread snapshot loaders with mobile platform
+services:
+
+- Expo network state supplies current connectivity and change events.
+- foreground transitions emit a lightweight probe or a stronger reconnect wakeup,
+ depending on how long the app was backgrounded;
+- the mobile catalog supplies targets, profiles, credentials, and DPoP tokens;
+- SQLite implements the shared environment-cache interface;
+- a mobile background-activity observer/reporter narrows connection work while the
+ app is not active;
+- environment removal also clears that environment's drafts and outbox records.
+
+
+
+The platform supplies no SSH gateway. Its `SshEnvironmentGateway` operations fail
+with an explicit “desktop only” blocked error. Mobile can connect through supported
+direct/bearer or relay targets, but it does not provision a remote CLI over SSH and
+does not spawn a local provider. Chapter 34 returns to the access transports;
+Chapter 35 follows relay authentication and tunneling.
+
+
+ CR
+ UI --> NS
+ CR <--> DB
+ CR <--> SS
+ UI <--> FS
+ end
+ CR <-->|authenticated HTTP + WebSocket| ENV[Environment server]
+ ENV --> AUTH[Durable thread projections]
+ ENV --> OWN[Workspace / provider / PTY / Git]
+ FS -. queued intent after reconnect .-> CR`} />
+
+
+## 2. “Mobile persistence” is four stores, not one database
+
+The persistence layer merges an Expo SQLite database, Expo SecureStore, preferences,
+connection/cache adapters, and a few file-backed state machines. Their records have
+different confidentiality, failure, and authority properties.
+
+| Store | Representative records | Write semantics | What it is not |
+|---|---|---|---|
+| SQLite `t3code-client.db` | shell/thread/server-config/VCS caches; client preferences | WAL database; keyed upserts; corrupt cache entries can be discarded | the server event store |
+| SecureStore | connection targets/profiles/credentials, remote DPoP tokens, device identity/registration | serialized catalog update behind a semaphore | a workspace or transcript store |
+| document files | composer drafts, one JSON file per queued command, one JSON file per incoming share | serialized and/or atomic file writes, feature-specific recovery | one general transaction spanning every feature |
+| kept Effect Atoms / React state | hydrated views, optimistic queue row, route and disclosure state | process-local, sometimes seeded from a durable store | proof of server acceptance |
+
+SQLite creates `client_cache` with the compound key
+`(environment_id, kind, cache_key)` and a singleton `client_preferences` row. Cache
+payloads remain schema-versioned envelopes. An unavailable or corrupt cache can
+degrade to an empty client view; it must not become fabricated server truth.
+
+
+
+The secure connection catalog has its own schema and lock. It migrates a legacy
+connection list, keeps a memory copy after decoding, and persists each transformed
+catalog before swapping that copy. The shared runtime sees narrow interfaces for
+target registration, profiles, credentials, and remote access tokens instead of an
+Expo-specific storage API.
+
+
+ Removing an environment clears the shared environment cache through its cache
+ store and separately asks the mobile platform cleanup service to clear composer
+ drafts and queued messages. This is not one cross-store transaction; failures in
+ the intent cleanup path are logged so connection removal is not silently treated
+ as a successful atomic purge of every device record.
+
+
+## 3. A draft and an outbox message represent different commitments
+
+A composer draft is editable device state: text, attachments, imported-share ids,
+provider/model/mode choices, and an optional workspace selection. Draft changes
+update a kept Atom and schedule a 200 ms serialized atomic-file write. Ordinary
+background persistence is best-effort so an I/O failure does not make the composer
+unusable. The explicit flush path is stricter: it lands every pending debounce and
+propagates a write failure to an OTA coordinator deciding whether teardown is safe.
+
+
+
+An outbox message is a stronger delivery intent. Its versioned record carries stable
+environment, thread, message, and command ids plus the payload and selected runtime
+settings. A new-thread item additionally snapshots project presentation/workspace
+inputs so it stays editable and deliverable even if the live project shell is not
+currently loaded.
+
+Enqueue has a carefully split acknowledgment:
+
+1. publish the row synchronously into the kept Atom for immediate UI feedback;
+2. serialize an atomic file write for crash recovery;
+3. if that write fails, remove **that object reference**, not every value with the
+ same message id, because a later retry may already have replaced it;
+4. before delivery, serialize a `confirmQueued` read behind pending mutations so an
+ optimistically visible record whose file write failed can never reach the server.
+
+
+
+This is a client-side durability barrier, not a distributed transaction with the
+environment. Once RPC begins, the stable command/message/thread identities and the
+server's command receipts/invariants take over.
+
+## 4. The drain waits for synchronized evidence, then classifies failure
+
+The root stack mounts the outbox drain in a null-rendering leaf. That placement is a
+performance boundary: queue, shell, project, and connection updates do not need to
+rerender the whole navigation layout. The drain considers only the first queued
+message per thread and dispatches one message globally at a time.
+
+For a queued **creation**, transport connection is insufficient. It waits until the
+shell is `live`, because a previously accepted creation whose local cleanup failed
+could otherwise look absent and be issued again. A live shell that already contains
+the stable thread id selects `remove`; a live shell without it can select `send`.
+For an existing-thread message, a missing thread is removed only after a live shell
+establishes the absence; before then it waits.
+
+
+
+One subtle implementation fact is worth making explicit: the resolver accepts a
+`threadBusy` input, and the hook supplies the current session status, but the pinned
+decision function does not consult it. Do not describe this client outbox as an
+idle-thread scheduler. It may cross RPC while a thread appears busy; current server
+normalization and domain invariants still determine whether that command is legal.
+
+Before an existing-thread `startTurn`, the drain can send deterministic settings
+commands to reconcile model selection, runtime mode, and interaction mode with the
+queued snapshot. Any settings-sync failure retries. For the final turn command,
+interruptions, `ConnectionTransientError`, and recognized transport errors retry;
+other observed failures select discard. Retry delay grows from one second and caps
+at sixteen seconds. While retrying, the durable file remains.
+
+
+ Stable ids, durable confirmation, serialized one-at-a-time dispatch, live-shell
+ existence checks, and server receipts make retries safer. There is still no
+ atomic commit spanning the phone's file and the remote environment's database.
+ The explicit creation-replay check exists because acceptance and local deletion
+ can be separated by a process or network failure.
+
+
+
+
+## 5. Incoming sharing uses a durable handoff before navigation
+
+The system share extension supplies transient native payloads. The app converts
+them into a draft with a SHA-256 content-derived handoff id, but it does **not** clear
+the native payload first. `IncomingShareInbox` serializes all mutations, loads and
+deduplicates persisted drafts, writes the newly built draft, cleans temporary
+files, and only then acknowledges the native handoff.
+
+If the app terminates after the durable write but before native acknowledgement,
+the next refresh sees the same id, cleans any replayed image payload, and clears the
+handoff without adding a second inbox row. Once a share is consumed and its file is
+removed, a later intentional share of identical content can be ingested again.
+
+
+
+Presentation adds another durable state: `reserve(shareId, destination)` binds a
+share to one environment/project draft. Re-reserving for the same destination is
+idempotent; a different destination is rejected. Conditional release refuses to
+erase a reservation that changed underneath the caller. The root navigation layout
+observes the oldest pending presentation candidate and opens the new-task sheet,
+but that route is only a view over the inbox record.
+
+## 6. Native systems are presentation and OS-integration edges
+
+Mobile is not “the web UI in a WebView.” It uses a native-stack route tree and an
+adaptive workspace layout. The thread feed is a keyboard-aware virtualized
+LegendList with fixed-size hints for compact rows, per-type estimation for variable
+messages, visible-content preservation, and end-follow controls. Those choices are
+implementation strategies, not a benchmark claim.
+
+| Edge | JavaScript responsibility | Native responsibility | Authority retained elsewhere |
+|---|---|---|---|
+| Terminal | attach to server terminal stream; replay buffer; translate input/resize | Ghostty-backed view renders and encodes device input | server owns PTY/process/history boundary |
+| Review diff | parse/categorize change data; flatten rows/comments; cache mapping | Swift/Kotlin canvas renders large code surfaces | server/Git services own diff inputs and mutation |
+| Selectable Markdown/composer | derive structured spans/chips and callbacks | native text/editor views handle selection and keyboard behavior | thread message remains server state; draft remains client state |
+| Hardware keyboard | most-recently-mounted scoped handler dispatch | iOS native module exposes command events; platform shortcuts wake routes | command target still resolves through current screen/runtime |
+| Agent Activity | derive a compact activity projection and registration intent | iOS widget/Live Activity renders lock-screen/Dynamic Island families | relay/environment activity remains the remote source |
+
+The native terminal wrapper checks whether its Expo view config exists, caches a
+successful resolution, permanently remembers a failed resolution for the process,
+and exposes a compiled hardware-key revision. That revision is a version-skew
+diagnostic: JavaScript can detect an older binary instead of assuming every OTA
+bundle has the expected native code.
+
+
+
+The review adapter similarly converts parsed files, hunk/line/comment rows, themes,
+and word-diff ranges into a native-friendly flattened model; a WeakMap reuses that
+expensive mapping until comment identity changes. Native rendering avoids moving
+Git authority to the device.
+
+Agent-awareness settings are explicitly presented as **iOS only** at this revision.
+Release iOS builds configure an Agent Activity widget/Live Activity and push
+capability; personal-team builds remove capabilities they cannot sign. Android has
+an Expo notification resource configuration, so the precise claim is about the
+inspected product capability gate and Live Activity path—not that no Android
+notification API could exist in the binary. Chapter 36 traces notification and
+multi-environment reconciliation in full.
+
+## 7. OTA updates coordinate binary compatibility and volatile work
+
+Expo Updates uses a fingerprint runtime-version policy rather than the marketing
+app version. Native dependencies, config plugins, and patches therefore participate
+in compatibility: a JavaScript bundle should not land on a binary missing the
+native surface it expects.
+
+After download, normal application is deferred. The next background transition
+tries to flush composer drafts and outbox writes, then rechecks that the app is
+still backgrounded and that no app-initiated foreground handoff is active. Android
+can report background while an image picker, auth tab, or share surface covers the
+activity; a small reference-counted handoff marker prevents the update coordinator
+from tearing down that live flow.
+
+
+
+If a flush fails or the lifecycle check is unsafe, the install stays pending and is
+rearmed for a **future** background rather than polling inside the same background
+session. If the app remains foregrounded for thirty minutes with a ready update, it
+asks whether to install now. An explicit user-requested install may proceed after a
+failed flush once the failure is reported; automatic application does not. A remote
+rollback directive also takes the immediate path, though an automatic rollback
+whose flush fails is retained for deferred retry.
+
+
+ The compatibility fingerprint protects the native/JavaScript boundary. The
+ flush and lifecycle guards protect drafts, queued commands, and active OS
+ handoffs. Neither mechanism upgrades a client cache into server authority; after
+ reload, Chapter 29's snapshot/cursor convergence still reconciles with each
+ environment.
+
+
+## 8. The design lesson is selective durability
+
+T3 Code does not place every mobile value in one “offline database.” It gives each
+kind of state the smallest recovery contract it needs:
+
+- server projections are schema-versioned, replaceable cache;
+- credentials and connection profiles use secure device storage;
+- drafts favor responsive editing plus best-effort persistence, with a strict flush
+ only at a teardown boundary;
+- queued commands add stable ids, atomic files, serialized confirmation, replay
+ guards, and retry classification;
+- incoming shares make the file write the native-to-app transaction boundary and
+ add destination reservation;
+- OTA restart waits for those state-bearing stores instead of assuming
+ `background` means “safe now.”
+
+For a meta-harness, the transferable pattern is not “add an outbox everywhere.” It
+is: identify which intent must survive client death, bind it to stable remote
+identities, make replay ambiguity observable through synchronized server state,
+and keep caches visibly subordinate to authority.
+
+
From 3f828f9721f4517bbf9a457b595706d76915068f Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:18 +0300
Subject: [PATCH 08/12] docs(book): publish part seven reach and ship
---
src/content/book/340-access-transports.mdx | 239 ++++++++++++++
src/content/book/350-t3-connect.mdx | 209 ++++++++++++
.../book/360-reconnect-environments.mdx | 310 ++++++++++++++++++
.../book/370-distribution-artifacts.mdx | 240 ++++++++++++++
.../380-release-updates-observability.mdx | 264 +++++++++++++++
5 files changed, 1262 insertions(+)
create mode 100644 src/content/book/340-access-transports.mdx
create mode 100644 src/content/book/350-t3-connect.mdx
create mode 100644 src/content/book/360-reconnect-environments.mdx
create mode 100644 src/content/book/370-distribution-artifacts.mdx
create mode 100644 src/content/book/380-release-updates-observability.mdx
diff --git a/src/content/book/340-access-transports.mdx b/src/content/book/340-access-transports.mdx
new file mode 100644
index 0000000..0cca9e2
--- /dev/null
+++ b/src/content/book/340-access-transports.mdx
@@ -0,0 +1,239 @@
+---
+slug: access-transports
+order: 340
+number: "34"
+kind: chapter
+part: "Part VII · Reach and ship"
+partOrder: 7
+title: "Reachability is a route; authority is a separate proof"
+shortTitle: Access transports
+summary: "A T3 Code environment can be host-local, paired through an endpoint, provisioned through a Tailnet, or reached through the desktop SSH gateway. These paths differ in how they launch and expose a server, but every usable route still has to establish an endpoint, enroll a client, and preserve the server's authority boundary."
+status: source-checked
+gates: [sources, interaction, links]
+objectives:
+ - Separate launch transport from the HTTP/WebSocket access route a client ultimately uses.
+ - Distinguish a primary target from a paired bearer registration, including the bind and enrollment boundaries.
+ - Explain why Tailscale provisions advertised endpoints rather than creating a new connection-target kind.
+ - Follow the desktop SSH gateway from remote server readiness to a local forwarded endpoint.
+keywords: [remote access, primary, bearer, pairing, Tailscale, Tailnet, SSH, endpoint, exposure, trust boundary]
+sourceAreas: [apps/server/src/auth, packages/client-runtime/src/connection, apps/desktop/src/backend, apps/desktop/src/ssh, packages/tailscale/src, packages/ssh/src]
+visuals: [access transport route map, interactive access transport lab]
+updatedAt: 2026-08-24
+---
+
+import AccessTransportLab from "../../components/AccessTransportLab.astro";
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceList from "../../components/SourceList.astro";
+
+Remote access is easiest to misunderstand when “transport” is used for every
+arrow in the picture. This chapter uses two narrower terms:
+
+- **Launch transport** is how an environment server comes into existence or is
+ found: a desktop child, an already-running process, or desktop-owned SSH work
+ that starts or reuses a remote process.
+- **Access transport** is the usable HTTP/WebSocket route after that: loopback,
+ LAN, a selected HTTPS endpoint, or a local SSH forward.
+
+Neither word is authorization. A route merely lets a client reach an environment
+that can evaluate its credential and scopes. The durable server, filesystem,
+provider processes, and work state stay on the environment side of that boundary.
+For credential exchange, ticketing, and per-method scopes, see [Chapter 8](../auth-pairing/); for the shared resolver, supervisor, and RPC
+session after a route is prepared, see [Chapter 29](../shared-client-runtime/).
+
+
+ Tailscale can make one or more endpoints available—a Tailnet IP or a MagicDNS
+ HTTPS endpoint—but the client target union remains Primary, Bearer, Relay, and
+ SSH. A Tailnet endpoint selected during pairing is normally reached as a
+ **Bearer** connection. Endpoint provisioning and connection identity are
+ deliberately different layers.
+
+
+## 1. First establish a route, then prove the right to use it
+
+The client runtime's target model is compact. A **Primary** target already holds
+HTTP and WebSocket bases. **Bearer** and **SSH** targets instead retain a connection
+id and obtain the associated profile through the catalog; Relay has its own
+managed-bootstrap path. This is a model of *how a client prepares a connection*,
+not a taxonomy of every network that may carry the bytes.
+
+That distinction resolves a common false equivalence. A desktop app launching its
+own host-local backend has a launch path, but a paired phone or browser never needs
+to replay that launch. It needs an advertised endpoint and an enrollment credential.
+Conversely, a desktop SSH flow has substantial launch machinery, yet the renderer
+eventually reaches a local HTTP/WebSocket forward rather than implementing SSH
+itself.
+
+
+ Primary carries direct bases. Bearer and SSH targets carry a connection id, while
+ the resolver switches on those four target tags before producing its common
+ prepared-connection shape. Tailscale is absent from that union because its code
+ advertises endpoints rather than representing a client target.
+
+
+
+ |launch| E[Environment server\nfiles · providers · durable state]
+ P -->|loopback HTTP / WS| E
+ L[Paired client] -->|LAN or HTTPS endpoint| E
+ L -. one-time enrollment .-> E
+ T[Tailscale endpoint provider] -. Tailnet IP or MagicDNS HTTPS .-> L
+ D[Electron main] -->|SSH launch or reuse| R[Remote server]
+ D -->|local SSH forward| S[Renderer]
+ S -->|loopback HTTP / WS| R`} />
+
+
+## 2. Bind policy determines exposure, not entitlement
+
+Desktop exposure begins with a conservative default. `local-only` resolves to a
+`127.0.0.1` bind and has no advertised network endpoint. `network-accessible`
+resolves to `0.0.0.0` and advertises a usable non-loopback LAN IPv4 address when
+one can be found; if one cannot, desktop treats that requested network exposure as
+unavailable rather than fabricating a destination. The server's auth descriptor
+independently categorizes the configured host as loopback, desktop-managed-local,
+or remote-reachable and chooses the bootstrap methods accordingly.
+
+The important restraint is what this does **not** imply. A broad bind is a larger
+set of machines able to attempt a connection; it is not a grant of orchestration,
+terminal, or review authority. Likewise, a loopback bind is a route constraint,
+not a substitute for the desktop main/preload boundary described in
+[Chapter 32](../desktop-electron/). Bind deliberately, then make the client prove
+its entitlement at the environment.
+
+
+ Desktop's local-only and network-accessible modes choose different bind and
+ advertisement values. Environment auth separately derives a policy from runtime
+ mode and whether the configured host is remotely reachable, including which
+ bootstrap methods it permits.
+
+
+## 3. Pairing enrolls a client once; registration preserves a usable route
+
+A one-time pairing credential is deliberately an enrollment secret, not the
+long-term object the remote client repeatedly puts on the wire. The pairing store
+creates a credential with a default five-minute TTL, standard client scopes unless
+the issuer narrows them, and optional proof-key binding. It persists the record.
+Consumption checks expiration and the optional proof key, atomically consumes an
+available stored credential, and removes the active pairing link from the change
+stream. A manager issuing another pairing credential must already hold
+`access:write`; requested delegated scopes must be nonempty, distinct, and a subset
+of that manager's scopes.
+
+After that exchange, pairing onboarding builds a Bearer registration: a target with
+a stable `connectionId`, a profile containing normalized HTTP and derived WebSocket
+bases, and a bearer credential. Those values are intentionally associated, not a
+random URL plus a token pasted into unrelated settings. Later edits refuse to
+continue if the saved target, profile, or credential no longer forms that bearer
+shape.
+
+This chapter stops at the trust-boundary consequence. It does not restate the token
+exchange, browser-session, DPoP, WebSocket-ticket, or RPC-scope machinery from
+Chapter 8. The operational point here is simpler: **an endpoint makes enrollment
+possible; enrollment makes a saved access route usable; neither transfers
+environment ownership to the client.**
+
+
+ The server persists and consumes a one-time credential, whereas client onboarding
+ represents the resulting remote environment as a target, a profile, and a bearer
+ credential sharing one connection id. The issuer cannot delegate scopes it does
+ not possess.
+
+
+## 4. Tailscale supplies endpoints; pairing and bearer access remain ordinary
+
+The desktop Tailscale add-on scans interfaces for Tailnet IPv4 addresses and
+advertises them as private-network endpoints. Separately, if it can determine a
+MagicDNS name, it can advertise a Tailscale HTTPS endpoint. That HTTPS endpoint is
+only marked available after Tailscale Serve is enabled and its probe succeeds;
+otherwise the same name is presented as requiring configuration. The distinction
+is useful: discovering a hostname is not proof that it currently forwards to the
+environment.
+
+Tailscale Serve is explicit endpoint provisioning. The wrapper configures
+`tailscale serve --bg --https=` to proxy to the local backend and disables
+the corresponding mapping with `serve ... off`. Desktop keeps the enablement and
+port in its exposure state and only asks the endpoint provider to inspect Tailscale
+when network exposure or Serve is opted in. That avoids making private-network
+discovery an invisible background prerequisite for ordinary local use.
+
+Once a user selects a reachable Tailnet endpoint and pairs, the client has not
+become a “Tailscale connection.” It has a Bearer target whose profile points at a
+Tailnet address. That is why the resolver, retry policy, cache ownership, and
+session readiness remain the shared-runtime concerns of Chapter 29, regardless of
+whether the bytes cross Wi-Fi, a Tailnet, or another HTTPS route.
+
+
+ Tailscale's advertised-endpoint provider is tagged as a private-network add-on
+ and may return IP and MagicDNS HTTPS endpoints. The core client target union has
+ no Tailscale variant; its paired route is represented through the existing bearer
+ registration.
+
+
+## 5. SSH is a desktop gateway with a local access endpoint
+
+SSH is the exception that proves why launch transport needs its own name. The
+renderer does not spawn `ssh`, read `~/.ssh/config`, or receive an SSH password.
+Through the curated desktop boundary, Electron main delegates host discovery,
+ensure, and disconnect operations to the SSH environment manager, with desktop
+password prompts supplied at that native edge.
+
+On provisioning, the manager resolves the SSH target, starts or reuses the remote
+server, reserves a local loopback port, and launches `ssh -L` from that local port
+to the remote server's loopback port. It waits for HTTP readiness through the
+forward before returning HTTP and WebSocket bases. The onboarding layer then saves
+an SSH target/profile; when that target is prepared later, the platform gateway
+refreshes the SSH bootstrap path before remote authorization continues.
+
+The consequence is deliberately asymmetric: the desktop owns the SSH process and
+tunnel lifetime; the renderer sees a ready environment route; the remote machine
+still owns the server process, projects, files, git, terminals, and provider
+sessions. That complements Chapter 32's process map rather than turning SSH into
+another desktop-local backend pool member.
+
+
+ The desktop service delegates to an SSH environment manager. The manager reserves
+ a local port, forwards it to remote loopback, waits for HTTP readiness, and
+ returns the local bases. Client onboarding stores a distinct SSH profile rather
+ than treating that tunnel as a primary or bearer target.
+
+
+## Work the route, not a vague idea of “remote”
+
+Use the lab to select a route and read four independent facts: how the server is
+launched or found, what endpoint carries normal client access, where authority
+narrows, and what durable registration is kept. It intentionally has no connect
+button, timer, or simulated retry loop; those are runtime concerns, not proof that
+the trust boundary is understood.
+
+
+
+The resulting checklist is short:
+
+1. Is there a real endpoint that the intended client can reach under browser and
+ network constraints?
+2. Does the bind/exposure policy match the trust zone of that endpoint?
+3. Is enrollment bounded to the intended scopes, lifetime, and device proof?
+4. Does the saved target describe the route honestly—Primary, Bearer, Relay, or
+ SSH—without mistaking Tailscale or an SSH forward for a new authority model?
+
+If all four answers are sound, Chapter 29 can supervise a normal prepared
+connection. If any answer is missing, a live socket would only conceal a
+reachability or trust-boundary defect until the next device, network, or restart.
+
+
diff --git a/src/content/book/350-t3-connect.mdx b/src/content/book/350-t3-connect.mdx
new file mode 100644
index 0000000..27be0df
--- /dev/null
+++ b/src/content/book/350-t3-connect.mdx
@@ -0,0 +1,209 @@
+---
+slug: t3-connect
+order: 350
+number: "35"
+kind: chapter
+part: "Part VII · Reach and ship"
+partOrder: 7
+title: "T3 Connect: OAuth, DPoP, relay, and tunnel"
+shortTitle: T3 Connect
+summary: "T3 Connect uses a Clerk account credential and a device-held DPoP key to authorize relay control-plane operations, links a local environment through a signed proof, provisions a Cloudflare tunnel, then gives the client a direct DPoP-bound connection to that environment. The relay authorizes, provisions, and brokers setup; normal T3 traffic does not transit it."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Trace Clerk OAuth from the hosted handoff page through PKCE callback and stored CLI credential.
+ - Separate a device DPoP key, relay DPoP access token, environment bootstrap credential, environment access token, and WebSocket ticket.
+ - Explain environment link registration and managed Cloudflare tunnel provisioning without turning the relay into a fictional data proxy.
+ - Identify the control-plane and launch calls that involve the relay versus the direct environment data plane.
+keywords: [T3 Connect, Clerk, OAuth, PKCE, DPoP, relay, Cloudflare Tunnel, cloudflared, environment registration, WebSocket]
+sourceAreas: [docs/internals/t3-connect.md, packages/shared/src/connectAuth.ts, apps/server/src/cloud/CliTokenManager.ts, apps/web/src/cloud/dpop.ts, packages/client-runtime/src/relay/managedRelay.ts, packages/client-runtime/src/authorization/service.ts, infra/relay/src/environments]
+visuals: [credential ladder, relay control-plane and direct data-plane map]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import ConnectPlaneLab from "../../components/ConnectPlaneLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+T3 Connect is a **connection-launch system**, not a hosted copy of an environment.
+It uses a cloud account to discover and authorize an environment, arranges a managed
+endpoint when requested, and gives a client credentials to enter that environment.
+Once connected, the selected environment—not the relay—continues to own T3 HTTP,
+WebSocket, orchestration, terminal, and project traffic.
+
+
+Clerk authenticates the person to the hosted control plane. A device-held DPoP key
+proves possession for proof-bound tokens. The environment issues its own access
+session. None of those roles makes the relay a proxy for ordinary T3 work.
+
+
+## 1. Clerk establishes account authority; the CLI uses hosted OAuth handoff
+
+Web, desktop, and mobile use the configured Clerk application for cloud account
+state. For the headless CLI, the OAuth public client uses PKCE: the process creates
+a verifier, challenge, and state, starts either a loopback callback listener or an
+out-of-band prompt, and exchanges the resulting authorization code at Clerk’s token
+endpoint. The CLI stores the returned OAuth credential in its environment secret
+store; `t3 connect login` does **not** enable remote exposure.
+
+The browser starts at the hosted `/connect` page, not Clerk’s authorize endpoint
+directly. The request carries state and PKCE challenge in the fragment. After the
+hosted page has a Clerk session, it forwards the authorization request with those
+parameters intact. This avoids a signed-out Clerk redirect losing the authorize
+request. A loopback request supplies a port and returns to
+`http://127.0.0.1:/callback`; an SSH or `--headless` flow instead uses hosted
+`/connect/callback`, displays `code.state`, and the terminal checks the state before
+exchanging the code.
+
+
+
+
+
+## 2. The device key binds proof, rather than becoming an account password
+
+For managed relay access, a browser creates a P-256 signing key and retains it in
+its DPoP key store. Its public JWK yields a thumbprint. Each proof is a signed JWT
+with method (`htm`), normalized target URL (`htu`), fresh id (`jti`), issue time, and,
+when an access token is being presented, its hash (`ath`). The private key does not
+travel to the relay or environment.
+
+The verifier checks the signature, requested method and URL, expected thumbprint,
+optional access-token hash, and a bounded issue-time window. The environment also
+persists a thumbprint-plus-`jti` replay marker. A captured access token therefore is
+not sufficient for a DPoP route without a matching fresh proof; a reused proof is
+rejected.
+
+
+
+
+
+
+ The source makes a device key central to DPoP-bound relay and environment calls.
+ It does not claim that a DPoP key replaces the Clerk session, or that one proof can
+ be replayed for an unrelated URL, method, or access token.
+
+
+## 3. Linking registers an environment and its managed endpoint intent
+
+Linking has two authorities. First the signed-in client asks the relay for an
+environment-link challenge. It then asks the local primary environment to sign a
+link proof containing its descriptor, environment public key, requested endpoint,
+origin, scopes, and challenge. The relay verifies the proof and challenge, consumes
+replay nonces, checks that the descriptor and environment id agree, and upserts the
+account-to-environment link.
+
+For a managed link, the relay accepts only a loopback origin, provisions a managed
+endpoint, then returns both a per-environment relay credential and runtime connector
+configuration. The local environment persists that configuration and starts the
+managed connector. `--publish-only` deliberately takes a different path: it links
+activity publishing but asks for no managed tunnel, so a client must reach the
+environment out of band.
+
+
+
+
+
+## 4. A tunnel exposes the environment; it does not route through the relay
+
+Provisioning derives a stable hostname and tunnel name, reserves an allocation,
+creates or reuses the Cloudflare tunnel, points its ingress at the local loopback
+HTTP origin, creates a proxied CNAME to `*.cfargotunnel.com`, obtains a connector
+token, and marks the allocation ready. The environment runtime launches the managed
+client as `tunnel run` with that token and supervises it.
+
+That is an endpoint-exposure path: client traffic can travel through the managed
+Cloudflare tunnel to the environment. It is not a relay data hop. The relay’s own
+README says that normal API and WebSocket traffic goes directly between client and
+selected environment after connection; the client runtime independently constructs
+the environment’s token and WebSocket-ticket requests from the endpoint URL.
+
+
+ |sign in / account credential| C
+ U -->|discover, link, relay DPoP| R
+ R -->|link challenge / endpoint allocation| E
+ R -->|managed tunnel provisioning| CF
+ E -->|cloudflared connector| CF
+ R -->|short-lived mint request| E
+ U -->|bootstrap exchange + DPoP| E
+ U <-->|normal API + WebSocket\nthrough managed endpoint| CF
+ CF <-->|tunnel traffic| E
+ R -. not on normal data plane .-> CF`} />
+
+
+
+
+
+
+## 5. The exact credential ladder crosses the relay once, then leaves it
+
+The verbs matter more than calling every string a “token.” A fresh connection takes
+this ladder:
+
+1. A Clerk session credential authorizes the account with the relay.
+2. The client’s DPoP key signs a proof; the relay exchanges the Clerk credential for
+ a scoped, short-lived relay DPoP access token and requires proof of the same key.
+3. The relay’s `connect` operation creates a two-minute signed mint request bound to
+ the client key thumbprint and sends it to the managed environment. The verified
+ response supplies an **environment bootstrap credential**.
+4. The client calls that environment’s `/oauth/token` directly with the bootstrap
+ credential and a DPoP proof. The environment issues a DPoP-bound **environment
+ access token**.
+5. The client calls that environment’s WebSocket-ticket endpoint directly with the
+ environment token and another proof, then attaches the resulting ticket to the
+ environment WebSocket URL.
+6. The client and environment carry ordinary T3 API/WebSocket work directly. The
+ tunnel is the reachable endpoint; the relay is not the normal hot path.
+
+The bootstrap credential is intentionally not the long-lived data-plane session,
+and the relay DPoP token is intentionally not an environment token. That separation
+is why a relay-side account operation, a device proof, an environment access session,
+and a ticket can have distinct expiry, audience, and replay rules.
+
+
+
+
+
+
+
+## 6. Evidence boundary: what this pinned source does and does not establish
+
+
+ The relay README specifies the intended direct post-connection API/WebSocket path,
+ and the client authorization implementation calls environment endpoint URLs for
+ token exchange and WebSocket tickets. This chapter treats “relay not in the normal
+ hot path” as documented architecture supported by those local call sites.
+
+
+
+ The pinned code can show Cloudflare tunnel provisioning intent and the connector
+ command, but not Cloudflare’s internal edge routing or a production Clerk
+ dashboard’s current configuration. Those are external-service facts, not claims
+ this source tree can independently prove. Likewise, future endpoint providers or
+ mobile redirect variants should not be inferred from today’s Cloudflare-only
+ managed runtime.
+
+
+
+ The maintainer guide records Clerk application, JWT-template, and redirect-URI
+ setup. This pinned source can verify the callback paths and OAuth request shape;
+ it cannot verify the current values configured in a Clerk dashboard. Treat those
+ deployment instructions as documentation-only until the deployed service is
+ inspected separately.
+
+
+The compact model is: **Clerk identifies the account; DPoP proves the device;
+the relay authorizes and launches; the tunnel exposes; the environment admits; the
+client and environment do the work.** Drawing the relay across every subsequent
+arrow makes the architecture less secure to reason about and less true to the code.
+
+
diff --git a/src/content/book/360-reconnect-environments.mdx b/src/content/book/360-reconnect-environments.mdx
new file mode 100644
index 0000000..6129ea9
--- /dev/null
+++ b/src/content/book/360-reconnect-environments.mdx
@@ -0,0 +1,310 @@
+---
+slug: reconnect-environments
+order: 360
+number: "36"
+kind: chapter
+part: "Part VII · Reach and ship"
+partOrder: 7
+title: "Reconnect, environments, notifications, and version skew"
+shortTitle: Reachability recovery
+summary: "T3 Code recovers reachability one environment at a time: a registry leases a single supervisor and RPC session per environment, projections reconcile independently after a new generation, background work follows declared demand, notifications wake attention rather than synchronize state, and capability plus exact-version recovery makes skew explicit."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Distinguish a connection attempt, a session lease, a retry supervisor, and environment-scoped projections.
+ - Explain why a reconnect is not evidence that a shell or thread has synchronized.
+ - Trace the awareness relay and APNs path without treating a notification as authoritative state delivery.
+ - Choose capability checks, exact-version guidance, and server-update paths that remain safe under version skew.
+keywords: [reconnect, environment, generation, lease, supervisor, cache, background, APNs, notifications, awareness relay, version skew, self-update]
+sourceAreas: [packages/client-runtime/src/connection, packages/client-runtime/src/state, apps/mobile/src/connection, apps/mobile/src/features/agent-awareness, infra/relay/src/agentActivity, packages/contracts/src/environment.ts, apps/web/src/versionSkew.ts, apps/server/src/cloud]
+visuals: [environment recovery boundary, reachability and notification recovery lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ReachabilityRecoveryLab from "../../components/ReachabilityRecoveryLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+A remote client does not have one global connection to “T3 Code.” It has a
+catalog of environments, and each environment is a separate authority with its
+own endpoint, credentials, session, caches, shell, threads, and failure history.
+That is the unit of recovery.
+
+The distinction prevents two tempting but incorrect shortcuts:
+
+1. a new socket is not proof that a shell or thread is current; and
+2. a notification is not a state update merely because it mentions a thread.
+
+Both are **signals to begin or focus reconciliation**. The environment server
+remains the authority, and the client becomes current through a new session plus
+the normal snapshot/subscription paths.
+
+
+ “Reachable” says an endpoint may be attempted. “Connected” says an RPC lease
+ completed its initial configuration. “Shell live” and “thread live” say their
+ separate projection streams completed synchronization. Preserve the distinction
+ in state, UI, and retry policy; otherwise a green transport indicator can
+ accidentally certify stale cached work as current.
+
+
+## 1. The catalog is a map of authorities, not a pool of interchangeable servers
+
+The shared runtime loads persisted targets into a map keyed by `EnvironmentId`.
+The registry holds environment-scoped service scopes and a per-environment lease
+lock. Starting the runtime acquires supervisors for persisted targets, but a caller
+asking to run an operation still resolves the specific environment at that moment.
+This is the boundary that lets a phone or browser display several environments
+without cross-wiring their sessions.
+
+
+
+An environment id must travel with an object reference. The contracts make this
+concrete for projects and threads: a `ScopedThreadRef` is `(environmentId,
+threadId)`, not a thread id that can be looked up globally. A superficially
+similar repository or a reused thread-shaped identifier in another environment
+does not authorize a merge.
+
+
+
+### Multi-environment presentation is a merge of views, not truth
+
+The client can combine shell summaries from every catalog entry into one sidebar,
+connection list, or “needs attention” count. That is a presentation merge. Each
+input remains owned by its environment's cache and its own snapshot/stream state.
+Do not de-duplicate two threads because their labels or Git remotes look alike,
+and do not use an environment A session to repair environment B cache.
+
+This constraint is useful even when a user has one desktop server exposed through
+direct LAN, relay, and SSH-assisted routes: access and launch can differ while the
+saved environment identity decides which runtime scope owns recovery. [Chapter 34](../access-transports/)
+separates access from authority; this chapter follows the recovery consequence.
+
+
+ A[Environment A scope]
+ C --> B[Environment B scope]
+ A --> AS[one supervisor]
+ B --> BS[one supervisor]
+ AS --> AL[session generation / lease]
+ BS --> BL[session generation / lease]
+ AL --> AC[(A shell + thread caches)]
+ BL --> BC[(B shell + thread caches)]
+ AC --> UI[combined presentation]
+ BC --> UI
+ ENV[Environment A projection] -. signed awareness publish .-> R[relay]
+ R -. APNs attention signal .-> N[mobile route: A + thread]
+ N --> AS
+ AS --> AC`} />
+
+
+## 2. One supervisor owns retry; a session performs one attempt
+
+The terms describe deliberately different responsibilities.
+
+| Layer | Owns | Does not own |
+|---|---|---|
+| Connection attempt / RPC session | one prepared endpoint, transport, initial configuration, probe, and close signal | retry ladder or another session after close |
+| Environment supervisor | desired state, network/lifecycle inputs, the current lease, generation, backoff, and replacement | shell or thread reducer semantics |
+| Environment shell/thread state | cached snapshot, synchronization status, sequence/cursor rules, and domain errors | raw transport creation or independent retry loops |
+| UI component | selection and presentation | a socket, timer, or RPC client |
+
+The supervisor clears the prepared connection and session together when a lease
+ends. A successful attempt publishes one `connected` state with its generation,
+then monitors either the session close or a wakeup probe. The main loop advances
+the generation only for an established attempt; offline releases a lease and waits
+for a signal rather than consuming retry attempts.
+
+
+
+
+
+There can therefore be **one reconnect owner per environment**, not one per
+panel, route, subscription, or command. A second retry loop is harmful: it can
+race a close against a new session, reset backoff inconsistently, or attach a
+subscription to a lease that another loop is about to discard. Domain streams wait
+for the supervisor's replacement session; expected domain failures can be handled
+without declaring the transport dead.
+
+
+ A connection generation tells callers that a new established session replaced
+ an old lease. It does not order shell and thread content across environments and
+ does not make cached projection data live. Each domain must still establish its
+ own snapshot/stream boundary on that session.
+
+
+## 3. Reconciliation follows the replacement lease, domain by domain
+
+The shell starts from a cache when available, but caches are presentation
+continuity—not an authority handoff. On a new subscription session, the shell
+loader obtains an authoritative HTTP snapshot before attempting cursor resume.
+If that refresh fails, it omits the cached cursor so the socket fallback sends a
+complete snapshot. A later `synchronized` marker is what upgrades a retained
+snapshot to live when that protocol feature is available.
+
+
+
+Thread detail has the same separation, with an additional history race. A fresh
+thread snapshot replaces all loaded history and advances a local history epoch.
+An older-page request captured before that epoch cannot merge afterward. The rule
+is intentionally local to a scoped thread: it protects one detail window without
+pretending to establish a global event order across all threads or environments.
+
+
+
+The safe recovery order is therefore:
+
+1. retain the selected `(environmentId, threadId)` address and any cache marked
+ cached;
+2. let that environment's supervisor establish or retain a healthy lease;
+3. let shell and thread factories attach to the replacement session;
+4. replace or resume using their snapshot/sequence rules; and
+5. only then present the relevant projection as live.
+
+No step says “replay every command because the connection returned.” Commands
+resolve their current environment runtime at execution time. Durable command and
+outbox semantics belong to [Chapter 24](../permissions-and-input/) and
+[Chapter 33](../mobile-client-continuity/); reconnect supplies reachability, not
+permission to duplicate intent.
+
+## 4. Background work has demand and policy, not a hidden always-on connection
+
+The phone reports client activity for each registered environment. Its baseline
+report includes visibility, focus, recent interaction, app state, and declared
+background scopes; retained subscriptions add only known scope types such as
+diagnostics or VCS status, reference-counted per environment. This lets the server
+choose what remains useful while the app is backgrounded without equating
+“installed” with “keep every stream alive.”
+
+
+
+
+
+This is a policy boundary. A background report can help the server decide whether
+to retain an eligible activity, but it does not authorize an arbitrary client
+timer, guarantee OS execution time, or make a notification channel a durable
+subscription. The current mobile reporter does have its own reporting cadence;
+that operational mechanism is not a reason for UI or labs to create autonomous
+reconnect timers.
+
+## 5. Awareness relay → APNs is an attention path, not state synchronization
+
+When agent-awareness publishing is enabled and relay credentials exist, the
+environment reads its projected thread and project shell, derives a deliberately
+small awareness state, signs a short-lived environment proof, and publishes it to
+the relay. The server skips an unchanged projected state. These are useful
+suppression and privacy properties: the payload is not a transcript, and the
+relay does not need a client to pretend that every provider stream event is a push
+notification.
+
+
+
+
+
+The relay's APNs work is delivery work. It may queue, succeed, or report a
+failure; iOS may also throttle delivery. A device registration is not treated as
+locally enabled merely because notification permission exists: the mobile module
+records whether the relay actually accepted the registration. It briefly collapses
+bursty re-registration, yet preserves a later registration/replay opportunity so
+foreground reconciliation can repair drifted Live Activity presentation.
+
+
+
+
+
+
+ A late or missing notification should not induce a second shadow state machine.
+ When the user opens the app—or the system otherwise wakes it—the existing
+ environment supervisor, snapshots, and subscriptions reconcile current state.
+ Push improves attention and deep-linking; it is not the only path to truth.
+
+
+The notification response consumer protects the route boundary too. It validates
+the deep link, deduplicates a delivered response identifier, and only navigates.
+It does not mutate a thread from untrusted notification data. Once routed, the
+selected environment's normal state runtime establishes what remains pending,
+completed, or deleted.
+
+
+
+
+
+## 6. Capability and exact-version recovery solve different kinds of skew
+
+The environment descriptor contains a server version and capability record.
+Optional capability keys allow a newer client to hide or avoid a command that an
+older server never implemented; for example, an absent settlement or pinning
+capability means the client does not optimistically send that command. This is
+feature-level compatibility, not a claim that every pair of versions behaves the
+same.
+
+
+
+For broader incompatibility, the web compares normalized exact client and server
+version strings and stores dismissals per environment plus that version pair. It
+then chooses guidance from the advertised update capability: boot service/respawn,
+desktop-managed update, or manual relaunch. A dismissal is scoped to that observed
+pair, so a changed environment version can surface a new warning.
+
+
+
+When a launcher-managed server can self-update, it requires an exact target
+version, stages that pinned runtime, runs a preflight, and only then asks the
+launcher to activate it. The update naturally closes the old connection; the same
+environment supervisor handles the involuntary close and re-establishes the lease.
+Do not make the transport layer a process manager merely to disguise that restart.
+
+
+
+
+ Capability absence suppresses a feature-specific request. Exact-version mismatch
+ communicates broader divergence and chooses a supported update/relaunch path.
+ Neither check converts cached client state into server truth, and neither is a
+ substitute for re-synchronizing after a server restart.
+
+
+## 7. The compact rule set
+
+Keep these rules together when extending any client surface:
+
+1. Address every cache, operation, notification route, and selection with its
+ environment.
+2. Give each environment one supervisor and let that supervisor own session
+ replacement and retry policy.
+3. Treat connection generation as a session-lease boundary, then let shell and
+ thread state independently become live.
+4. Merge multi-environment summaries in presentation only; do not blend their
+ authorities or caches.
+5. Declare background demand narrowly and accept that OS delivery is policy,
+ not a durable stream.
+6. Treat relay/APNs activity as an attention signal; navigate and synchronize
+ afterward.
+7. Gate new features by capability, surface exact-version skew honestly, and
+ recover a server update through the ordinary reconnect owner.
+
+That model is modest by design. It does not promise exactly-once notification
+delivery, a global merged event log, or a perpetual mobile socket. It does make
+the important recovery path legible: one environment, one lease owner, current
+state re-established from the authority.
+
+
diff --git a/src/content/book/370-distribution-artifacts.mdx b/src/content/book/370-distribution-artifacts.mdx
new file mode 100644
index 0000000..4d9f9fd
--- /dev/null
+++ b/src/content/book/370-distribution-artifacts.mdx
@@ -0,0 +1,240 @@
+---
+slug: distribution-artifacts
+order: 370
+number: "37"
+kind: chapter
+part: "Part VII · Reach and ship"
+partOrder: 7
+title: "Distribution: artifacts, channels, and what actually ships"
+shortTitle: Distribution artifacts
+summary: "T3 Code distributes several deliberately different products: an npm CLI that contains a bundled web client, platform-specific Electron installers built from a staged closure, a release-controlled hosted web channel, store binaries plus fingerprint-gated mobile OTAs, and checksum-pinned AUR packages derived from the published Linux AppImage. The release matrix—not every target the builder knows—defines what is actually shipped."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Identify the concrete artifact and publication boundary for CLI, desktop, hosted web, mobile, and AUR delivery.
+ - Trace how the npm CLI embeds the web renderer and why its exact version publishes before the GitHub Release.
+ - Separate the desktop builder's supported platform targets from the four artifacts release CI currently produces.
+ - Explain release-controlled hosted channels and fingerprint-gated mobile updates without calling either an arbitrary-commit deployment.
+ - Follow a marketing download link and an AUR package back to a named GitHub Release asset.
+keywords: [distribution, npm, CLI, Electron, DMG, AppImage, NSIS, GitHub Releases, Vercel, Expo, EAS, fingerprint, AUR, stable, nightly]
+sourceAreas: [apps/server/package.json, apps/server/scripts/cli.ts, scripts/build-desktop-artifact.ts, .github/workflows/release.yml, .github/workflows/mobile-eas-production.yml, apps/web/vercel.ts, apps/marketing/src, packaging/aur]
+visuals: [distribution artifact map, interactive artifact factory]
+updatedAt: 2026-08-24
+---
+
+import ArtifactFactoryLab from "../../components/ArtifactFactoryLab.astro";
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+“T3 Code release” does not name one file. It is a coordinated set of artifacts with
+different consumers, packaging closures, update mechanisms, and trust boundaries:
+an npm executable, desktop installers, a hosted web deployment, native mobile
+builds and JavaScript updates, and an Arch package that repackages a release asset.
+The important question is therefore not “what command builds T3 Code?” but **which
+artifact is this consumer actually receiving, and what gate certified it?**
+
+
+ `build-desktop-artifact.ts` has platform/target configuration for macOS, Linux,
+ and Windows, and its naming accepts an architecture. That is a construction
+ capability. The checked release workflow currently schedules four desktop lanes:
+ macOS arm64, macOS x64, Linux x64, and Windows x64. The Windows arm64 entry is
+ commented out. Do not turn a latent builder option into a claim that users can
+ download that artifact.
+
+
+## 1. One source revision fans out into several delivery contracts
+
+The monorepo can produce a server/CLI bundle, a web build, desktop process output,
+and mobile app output, but their distribution boundaries diverge quickly. The CLI is
+an npm package named `t3`; desktop is an Electron-builder product with installer and
+updater side files; the hosted web app is a channel alias; mobile has native-store
+binaries and compatible OTA bundles; the AUR packages consume a published AppImage
+rather than independently publishing a desktop build.
+
+The release workflow first resolves stable versus nightly metadata, pins all later
+jobs to its chosen ref, and gives the channel a version, tag, npm dist-tag, and
+“latest” policy. Quality, relay configuration, desktop builds, and resource-monitor
+artifacts feed into CLI publication. Only a successful npm publication unlocks the
+GitHub Release. A successful GitHub Release then unlocks the AUR handoff and hosted
+web deploy. This is dependency ordering, not an assertion that all downstream
+consumer updates happen simultaneously.
+
+
+ WEB[Web build]
+ REF --> SERVER[Server / CLI bundle]
+ WEB --> CLI[dist/client inside npm t3]
+ SERVER --> CLI
+ CLI -->|publish exact version| NPM[npm dist-tag]
+ REF --> DESKTOP[Stage desktop + server + web + deps]
+ WEB --> DESKTOP
+ SERVER --> DESKTOP
+ DESKTOP --> MACA[macOS arm64 DMG]
+ DESKTOP --> MACX[macOS x64 DMG]
+ DESKTOP --> LINUX[Linux x64 AppImage]
+ DESKTOP --> WIN[Windows x64 NSIS]
+ NPM -. release gate .-> GH[GitHub Release]
+ MACA --> GH
+ MACX --> GH
+ LINUX --> GH
+ WIN --> GH
+ GH --> AUR[AUR stable / nightly metadata]
+ GH -. release gate .-> HOSTED[Hosted web channel alias]
+ REF --> MOBILE[Store reconciliation]
+ MOBILE -->|matching fingerprint only| OTA[Production OTA]`} />
+
+
+
+ The release workflow makes `publish_cli` depend on successful preflight, quality,
+ and desktop builds, and makes GitHub Release publication depend on that CLI job.
+ The release operations guide calls out the corresponding exact-version invariant.
+ It is an ordering guard for products that need to agree on a release version—not a
+ two-phase commit across npm, GitHub, Vercel, Expo, and AUR.
+
+
+## 2. The npm artifact is a server executable plus an embedded renderer
+
+`apps/server/package.json` declares a public executable: `t3` resolves to
+`./dist/bin.mjs`, and npm publishes only `dist`. The CLI build first runs the server
+bundle, then looks for `apps/web/dist`. When that web output exists it is copied into
+`apps/server/dist/client`; development icon replacements are applied there as a
+post-copy product adjustment. That means the npm artifact can carry both the server
+entrypoints and a renderer payload—rather than downloading the web product at
+install time.
+
+
+
+
+
+The build step can warn and continue if `apps/web/dist` is missing. Publication is
+stricter: before it rewrites temporary publish metadata it verifies
+`dist/bin.mjs`, `dist/service-launcher.mjs`, and `dist/client/index.html`. The
+publish path then resolves workspace catalog/override dependencies into an npm-ready
+manifest, applies publish icon overrides, invokes package publication with the
+selected dist-tag/version, and restores the original local metadata and icons in
+its release finalizer. That restore is a workspace-cleanliness guarantee; it does
+not undo an already completed npm publication.
+
+
+ The CLI build can skip a missing web directory, while the publish command rejects
+ a missing `dist/client/index.html`. Release CI runs a web build before the CLI
+ build and publishes the CLI only after release quality/build dependencies succeed.
+ This turns a permissive local build convenience into a stricter release contract.
+
+
+## 3. Desktop packaging constructs a closure, then CI selects four lanes
+
+The desktop artifact builder does more than wrap `apps/desktop`. It requires emitted
+desktop, resource, and server directories; verifies that the server bundle is
+self-contained enough for its runtime needs; requires the embedded
+`server/dist/client/index.html`; applies channel-appropriate web branding; and
+stages Electron output, resources, server output, production dependencies, patches,
+and platform-specific native materials into a temporary application tree.
+
+The platform split matters. macOS and Linux package a merged application tree. The
+Windows packaging path deliberately keeps desktop main-process dependencies in
+`app.asar` and builds a separate server `asar` sidecar; it also carries the
+Windows-to-WSL server support needed by that product. This is an implementation
+choice about runtime resolution and payload shape, not a claim that every desktop
+consumer launches a local WSL server.
+
+
+
+
+
+`electron-builder` receives an explicit platform flag, architecture, and
+`--publish never`; packaging jobs upload their artifacts, then the release job merges
+the relevant updater manifests and attaches DMGs, ZIPs, AppImages, EXEs, blockmaps,
+and YAML manifests to GitHub Releases. The builder’s macOS `dmg` target includes a
+ZIP updater artifact, which explains why a GitHub Release can have more files than
+the user-facing installer matrix names.
+
+### Shipped matrix vs. possible matrix
+
+| Release CI entry | Product installer target | Status in inspected workflow |
+|---|---|---|
+| macOS arm64 | DMG (plus updater ZIP) | active |
+| macOS x64 | DMG (plus updater ZIP) | active |
+| Linux x64 | AppImage | active |
+| Windows x64 | NSIS installer | active |
+| Windows arm64 | NSIS configuration is structurally possible | **commented out; not shipped** |
+
+
+
+
+ Artifact names include version and architecture. Marketing and AUR do not search
+ for an abstract “Linux build”; they resolve the expected suffix or exact
+ `T3-Code--x86_64.AppImage` name. Renaming an artifact can therefore
+ break downstream distribution even when the Electron package itself is valid.
+
+
+## 4. Hosted web and mobile use channels, but their compatibility gates differ
+
+The hosted web app deliberately disables Vercel’s normal Git deployment trigger.
+Release CI performs the deployment, chooses `latest` for stable or `nightly` for a
+nightly release, and aliases the deployment to that channel domain. The public router
+uses a long-lived secure cookie to route `app.t3.codes` requests to the selected
+channel origin. Thus “latest” is a release result, not necessarily the newest source
+commit in the repository.
+
+Mobile has a separate production workflow. It runs from Linux because its Expo
+fingerprint needs to match the build environment, reconciles store builds when the
+declared mobile version needs one, and publishes an OTA only for each platform with
+at least one completed production build matching the current native fingerprint.
+The mobile config explicitly uses the fingerprint runtime-version policy: native
+dependencies, config plugins, and patches participate in the compatibility identity.
+
+
+
+
+ Hosted web is an explicitly release-controlled Vercel alias with Git deployment
+ disabled. Mobile store binaries follow EAS profiles and store submission paths;
+ OTAs have a further per-platform native-fingerprint gate. Neither code path says
+ a normal source push can overwrite every user’s running client.
+
+
+## 5. Download resolution and AUR both trust named release assets
+
+The marketing download page fetches the latest GitHub Release and maps cards by
+asset suffix: arm64/x64 DMGs, an x64 Windows `.exe`, and an x86_64 AppImage. If the
+asset lookup or fetch fails, cards fall back to the releases page rather than
+inventing a direct URL. Mobile cards are intentionally different: they point to the
+App Store and Google Play, not a GitHub asset.
+
+
+
+The AUR workflow begins only after GitHub Release publication. Its release script
+accepts either a stable semver tag or the prescribed nightly tag pattern; selects
+`t3code-bin` or `t3code-nightly-bin` accordingly; reads the exact AppImage digest
+from that GitHub Release; reads the LICENSE at the same tag; edits the matching
+PKGBUILD; validates it with `namcap` and `makepkg`; then pushes only if its AUR SSH
+credential exists. A missing key is intentionally a “validated, not published” end
+state.
+
+
+
+
+ The AUR script derives its source and SHA-256 from a named GitHub Release asset,
+ then runs package validation. The marketing page independently resolves user
+ download links from the latest release asset list. Both consumers therefore make
+ artifact names and release attachment completeness observable boundaries.
+
+
+## Work the artifact factory
+
+Choose a delivery lane below and advance it one gate at a time. The desktop lane is
+useful for testing the crucial distinction: the builder can describe Windows arm64,
+but the final gate reports that the inspected release matrix does not activate it.
+
+
+
+The durable reading rule is: **source builds components; packaging assembles a
+consumer-specific closure; release policy selects and publishes a subset; channel and
+platform compatibility gates decide what a particular client may receive.** Treating
+those as one step hides the real failure boundaries.
+
+
diff --git a/src/content/book/380-release-updates-observability.mdx b/src/content/book/380-release-updates-observability.mdx
new file mode 100644
index 0000000..76defad
--- /dev/null
+++ b/src/content/book/380-release-updates-observability.mdx
@@ -0,0 +1,264 @@
+---
+slug: release-updates-observability
+order: 380
+number: "38"
+kind: chapter
+part: "Part VII · Reach and ship"
+partOrder: 7
+title: "Release, update, and observability: three safety boundaries"
+shortTitle: Release operations
+summary: "T3 Code's release graph publishes an exact CLI runtime before clients can require it. Desktop, service, and mobile each cross an update boundary differently, while analytics and diagnostics distinguish opt-out product telemetry from local traces, optional OTLP export, authenticated browser ingestion, and demand-driven native resource history."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Follow stable and nightly release-channel resolution through the exact-version publication invariant.
+ - Compare the desktop, boot-service, and Expo update boundaries without treating them as one protocol.
+ - Locate the point at which a server update becomes reversible, committed, and ready for client correlation.
+ - Separate anonymous product analytics, local traces, optional remote exporters, authenticated browser traces, and ephemeral resource diagnostics.
+keywords: [release, nightly, npm, GitHub Releases, exact version, electron-updater, service launcher, SQLite snapshot, Expo Updates, fingerprint, PostHog, OTLP, resource telemetry, privacy]
+sourceAreas: [.github/workflows/release.yml, docs/operations/release.md, docs/internals/server-updates.md, apps/desktop/src/updates, apps/server/src/cloud, apps/server/src/telemetry, apps/server/src/observability, apps/server/src/resourceTelemetry, apps/mobile/src/features/updates]
+visuals: [release dependency graph, updater boundary comparison, release operations lab]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import ReleaseOperationsLab from "../../components/ReleaseOperationsLab.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+A release is not one artifact copied to three clients. It is a dependency graph
+with a particular invariant: a client can offer to update a connected server to
+its own version only after the exact `t3@` runtime exists. The mechanisms
+that consume that release then have different owners and different safe points:
+Electron exits and installs an application, a boot-service launcher trials an
+immutable server runtime around a SQLite snapshot, and Expo swaps a compatible
+JavaScript bundle only after mobile persistence and lifecycle checks.
+
+The same precision matters for diagnostics. Product analytics, server traces,
+browser traces, metrics, and native resource samples are related operational
+signals, but they do not share a destination, retention model, or authorization
+boundary.
+
+
+Stable versus nightly decides which release metadata and artifacts a client is
+eligible to discover. It does not decide whether a particular runtime may replace
+another one. Each updater has its own preconditions, commit point, and recovery
+story.
+
+
+## Act I — release graph: publish the runtime before exposing the client
+
+The release workflow resolves two channels. A scheduled or explicitly nightly run
+uses the next patch version plus a UTC date and run number, publishes a GitHub
+prerelease, and uses npm's `nightly` dist-tag. A stable run requires a semver-like
+version and produces a `v` tag. The nightly lane is deliberately not the
+latest stable release and has its own desktop updater channel.
+
+
+
+The important edge is downstream of naming. Connected servers are asked to install
+the **client's exact version**, not “whatever npm currently calls latest.” The
+operations guide therefore requires `publish_cli` before `release`, and `release`
+before the hosted web deployment. This prevents a newly visible client from issuing
+an update request for a package version that cannot yet be fetched.
+
+
+ N --> G --> W
+ G --> C
+ W --> C
+ C -->|request exact V| S
+ N -->|"t3@V must already exist"| S
+ R -. nightly selects prerelease\nnightly updater metadata .-> G`} />
+
+
+
+
+This is a release-time guarantee, not a claim that every machine is immediately
+updated. A disconnected server can remain on an earlier immutable runtime; a
+desktop client can decline to download; a mobile binary may be incompatible with an
+OTA bundle. The graph makes versions available in a safe order. The updater on each
+machine chooses whether and when to cross its own boundary.
+
+
+ The inspected release guide explicitly calls for `t3@<version>`, and the service
+ update implementation rejects a target that is not an exact version. Describing
+ connected-server update as a normal `npm update` would erase the source's most
+ important compatibility control.
+
+
+## Act II — three update architectures cross three different boundaries
+
+### 1. Desktop: check automatically, but download and install by user action
+
+The packaged desktop app configures an Electron updater for the selected channel.
+It may check on startup and on an interval, but it does not silently download or
+install. A check moves update state toward `available`; a user action starts the
+download; a later user action starts quit-and-install. A single active action
+reservation prevents overlapping check, download, channel-change, and install work.
+
+
+
+Install is a process-lifecycle boundary, not merely a file replacement. Before
+`quitAndInstall`, the app stops every backend in the pool—including parallel WSL
+and Windows instances—with a grace budget, destroys windows, then yields control to
+the updater. That avoids relying on an application shutdown cascade after the OS
+has begun quitting it.
+
+
+
+### 2. Boot-service server: stage, preflight, trial, then have the launcher commit
+
+The remote server path is intentionally stricter because it can migrate the state
+that serves a remote client. It only exists for a server running under the supported
+background-service launcher; a desktop-managed server tells the user to update its
+desktop app, and an unsupported process shape gives manual-install guidance.
+
+The active child first downloads an immutable exact-version runtime into staging and
+runs its `__service-preflight`. The preflight result must name the requested version
+and report readiness before the runtime is placed into its version directory. Only
+then does the child ask the stable launcher to activate the prepared version.
+
+
+
+The stable launcher, rather than the child it is about to stop, owns durable active
+selection. It writes a pending trial, stops the old server after response-flush
+grace, snapshots SQLite database/WAL/shared-memory files, starts the target under an
+activation gate, and waits for `prepared`. Only the launcher commits the new active
+version and removes the snapshot. A failed or timed-out trial restores the snapshot
+and restarts the old version.
+
+```text
+old child: stage V → preflight V → request-update(V)
+launcher: record pending → stop old → snapshot SQLite → start V as trial
+trial V: migrate + bind + prepare roots → report prepared
+launcher: commit V → discard snapshot → let V open activation gate
+```
+
+
+
+The snapshot is a bounded rollback tool, not a universal undo. It covers the
+database, WAL, and shared-memory files around the trial; attachments and other
+state-directory files are outside that boundary. A launcher protocol that is too
+old blocks a target that needs this safety path. After a commit, ordinary service
+manager restart policy applies; there is no promise of a second automatic rollback
+for every later runtime failure.
+
+### 3. Mobile: OTA eligibility is native compatibility plus a safe teardown moment
+
+Mobile's Expo Updates configuration uses a fingerprint runtime version. Inference:
+because the fingerprint is derived from native-project inputs, it is a compatibility
+gate between an OTA JavaScript bundle and the installed native binary—not a release
+label intended to synchronize mobile with npm.
+
+
+
+After an eligible bundle downloads, automatic application is deferred to a safe
+background transition. The update coordinator flushes draft and outbox persistence,
+then checks that the app is still backgrounded and is not behind an app-initiated
+foreground handoff such as a picker. A failed flush or unsafe lifecycle state keeps
+the current runtime alive and rearms a later transition. Explicit user-requested
+update application is a separate immediate path.
+
+
+ The mobile update environment requires pending writes to flush and requires a
+ safe background state before a deferred reload. This does not prove every Expo
+ deployment has an update available; it describes the repository's client-side
+ decision boundary when one is downloaded.
+
+
+
+ DA[Update available]
+ DA -->|user clicks| DD[Download]
+ DD -->|user clicks| DS[Stop backend pool + quit/install]
+ end
+ subgraph S[Boot-service server]
+ SS[Stage exact t3@V] --> SP[Preflight V]
+ SP --> SL[Launcher: pending + SQLite snapshot]
+ SL --> ST[Trial V reports prepared]
+ ST -->|success| SC[Commit V]
+ ST -->|failure / timeout| SR[Restore snapshot + run old V]
+ end
+ subgraph M[Expo mobile]
+ MF[Fingerprint-compatible OTA] --> MD[Download bundle]
+ MD --> MP[Flush drafts + outbox]
+ MP --> MS{Still safe background?}
+ MS -->|yes| MR[Reload JS runtime]
+ MS -->|no / flush failed| MX[Defer next transition]
+ end`} />
+
+
+## Act III — observability and privacy are separate lanes, not one data pipe
+
+The server's anonymous PostHog analytics layer is configured on by default but can
+be disabled with `T3CODE_TELEMETRY_ENABLED`. Events remain buffered in memory and
+are sent in batches only when delivery is enabled; the payload explicitly disables
+PostHog person profiles. The service supplies platform, architecture, client type,
+and T3 Code version alongside the event properties.
+
+
+
+
+
+Identity selection is an ordered, hashed ladder: a Codex account id if readable,
+else a Claude user id, else an installation-scoped anonymous UUID in the T3 home
+directory. The code hashes the chosen value with its source namespace before
+returning it. That reduces direct identifier exposure to the analytics service; it
+does **not** mean all event properties are automatically anonymous, so the payload
+schema remains an operational privacy boundary.
+
+
+
+Tracing has a different default. The server creates a bounded local file trace sink
+and applies HTTP-header redaction. When an OTLP trace URL is configured, the local
+tracer also delegates to the remote exporter; metrics, by contrast, exist only when
+an OTLP metrics URL is configured. This is a local-plus-optional-remote trace lane,
+not a claim that metrics are retained locally.
+
+
+
+Browser OTLP records follow another route: the server authenticates the raw request
+at the `operate` scope, then **attempts** to decode and record it locally. A decode or
+local-collection failure is logged and recovered; it does not prevent the original
+JSON body from being forwarded when a remote trace endpoint is configured. With no
+remote URL, the route returns no content after that best-effort local attempt. The
+authentication boundary is strict, while local recording and remote forwarding are
+deliberately independent delivery lanes.
+
+
+
+Finally, native resource telemetry is diagnostics-oriented rather than an event
+archive. Its native sidecar keeps a one-hour **in-memory**, bounded ring. Periodic
+streaming is off until a diagnostics subscription is retained; explicit refresh
+still works. The model samples counters and process trees, so processes that begin
+and end between samples may not be seen. It is explicitly not syscall, eBPF, ETW,
+or endpoint-security tracing.
+
+
+
+
+ This is an inference from the independently configured paths: product analytics
+ can be opted out of and is batch-delivered, traces always have a local sink with
+ optional OTLP delegation, browser trace ingestion requires an environment scope,
+ and resource history is bounded in a native sidecar. Treating all four as a single
+ “telemetry upload” would overstate both retention and network behavior.
+
+
+
+
+The operational reading is therefore concise: **release ordering makes an exact
+runtime available; each platform crosses its own replacement boundary; diagnostics
+retain, export, and authorize different things on purpose.**
+
+
From 19fab9b99f3e299b5a63234545a1b12132f31331 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:21 +0300
Subject: [PATCH 09/12] docs(book): publish part eight synthesis
---
src/content/book/390-six-complete-traces.mdx | 355 +++++++++++++++
.../400-decisions-limitations-roadmap.mdx | 413 ++++++++++++++++++
2 files changed, 768 insertions(+)
create mode 100644 src/content/book/390-six-complete-traces.mdx
create mode 100644 src/content/book/400-decisions-limitations-roadmap.mdx
diff --git a/src/content/book/390-six-complete-traces.mdx b/src/content/book/390-six-complete-traces.mdx
new file mode 100644
index 0000000..3f3fc37
--- /dev/null
+++ b/src/content/book/390-six-complete-traces.mdx
@@ -0,0 +1,355 @@
+---
+slug: six-complete-traces
+order: 390
+number: "39"
+kind: chapter
+part: "Part VIII · Synthesis"
+partOrder: 8
+title: "Six complete traces: ownership, convergence, and failure boundaries"
+shortTitle: Six complete traces
+summary: "Six source-grounded paths connect the book's modules end to end: a local turn, a T3 Connect mobile turn, an approval, an offline outbox drain, a checkpoint diff and revert, and an exact-version service update. Each trace names the owner of every handoff and the point where the observed implementation stops promising convergence."
+status: source-checked
+gates: [sources, interaction, links]
+objectives:
+ - Trace six representative operations across client, relay, domain, side-effect, and projection boundaries.
+ - Distinguish a durable fact from a best-effort handoff, a cached intention, and a completed external effect.
+ - Reuse the owning chapters without flattening their separate authorization, recovery, and update protocols into one story.
+ - Identify the exact failure boundary for each trace before treating a local design choice as a general guarantee.
+keywords: [synthesis, trace, orchestration, T3 Connect, mobile outbox, approval, checkpoint, revert, exact-version update, convergence]
+sourceAreas: [apps/server/src/orchestration, apps/server/src/provider, packages/client-runtime/src, apps/mobile/src/state, infra/relay/src/environments, apps/server/src/checkpointing, apps/server/src/vcs, apps/server/src/cloud, apps/server/src/serviceLauncher.ts, .github/workflows/release.yml, docs/operations/release.md]
+visuals: [synchronized trace swimlanes, six-trace stepper]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+import SynchronizedTraceLab from "../../components/SynchronizedTraceLab.astro";
+
+This chapter is a synthesis, not a new seventh protocol. It puts already-audited
+pieces on one time axis so that the ownership changes are visible: a device can
+record intent, a relay can authorize connection setup, an environment can commit a
+fact, and a provider, Git driver, or stable launcher can perform a later effect.
+Those verbs are deliberately not interchangeable.
+
+The six traces are chosen because they exercise different seams. They do **not**
+combine into a global exactly-once guarantee. In particular, a server command may
+commit before a hot reactor observes it; a phone file and a server database have no
+shared transaction; a checkpoint revert can mutate files before provider rollback;
+and a prepared service runtime is not committed until its independent launcher says
+so.
+
+
+ Read a trace straight through once, then use its linked prerequisite chapter for
+ the local state machine. The swimlanes deliberately collapse detail; each arrow
+ means an observed handoff, not an assertion that all arrows are durable, ordered,
+ or retryable in the same way.
+
+
+## The common reading key: trigger, owner, durable fact, convergence boundary
+
+Every trace below has four questions:
+
+1. **What triggered work?** A user action, provider callback, queued file, or
+ release workflow begins the path.
+2. **Which owner may act next?** An owner can pass an intent or fact onward, but
+ does not automatically own the recipient's state machine.
+3. **What became durable, where?** A committed orchestration event, mobile outbox
+ file, hidden Git ref, or launcher state has a scope. It is not universal proof.
+4. **What converges—or stops?** Snapshots and subscriptions converge client views;
+ some other paths terminate at an honest failure activity, retry policy, or manual
+ recovery boundary.
+
+
+ >E: authorized turn command
+ E->>D: commit event + projection + receipt
+ E-->>W: hot provider intent
+ W-->>E: runtime facts
+ E->>D: commit projected result
+ E-->>C: snapshot / resumed events
+ end
+ rect rgb(247, 242, 255)
+ Note over C,W: 2 · relay-connected mobile turn
+ C->>R: DPoP-bound connect authorization
+ R->>E: short-lived bootstrap mint request
+ E-->>R: proof-bound bootstrap credential
+ R-->>C: endpoint + bootstrap credential
+ C->>E: direct bootstrap exchange + WebSocket ticket
+ C->>E: mobile turn through direct session
+ E-->>C: environment snapshot / stream
+ end
+ rect rgb(255, 248, 234)
+ Note over C,W: 3 · approval round-trip
+ W-->>E: native approval request
+ E->>D: project pending activity
+ E-->>C: synchronized pending request
+ C->>E: response command
+ E->>D: response-requested event
+ E-->>W: hot provider response
+ W-->>E: native resolution or stale-request failure
+ E->>D: later resolution / failure activity
+ end
+ rect rgb(239, 251, 241)
+ Note over C,W: 4 · offline mobile outbox drain
+ C->>D: atomic queued intent confirmation
+ C->>E: after connection + live-shell evidence
+ E->>D: accepted command receipt
+ E-->>C: accepted command result
+ C->>D: remove delivered local item
+ E-->>C: later snapshot / stream reconciliation
+ end
+ rect rgb(255, 241, 241)
+ Note over C,W: 5 · checkpoint diff / revert
+ E-->>W: completion triggers capture
+ W->>D: hidden Git ref + patch result
+ E->>D: checkpoint metadata
+ C->>E: revert request
+ E->>D: durable revert request acceptance
+ E-->>W: ordered restore + provider rollback
+ W-->>E: restore / rollback outcomes
+ E->>D: durable completion only after effects
+ end
+ rect rgb(241, 246, 250)
+ Note over A,W: 6 · stable exact-version update
+ A->>D: publish exact t3@V
+ A->>C: expose compatible client artifact
+ C->>E: request server target V
+ E->>W: preflight then launcher handoff
+ W->>D: snapshot, trial, commit or rollback
+ W-->>E: candidate ready / rollback outcome
+ E-->>C: reconnect / recovery signal
+ end`} />
+
+
+## Trace 1 — local first turn: commit intent before provider work
+
+An existing-thread turn begins in a local, web, desktop, or mobile renderer. The
+client sends the typed command through an authenticated RPC method; the server
+normalizes the external input, serializes a decision through its command queue, and
+commits the emitted event, read-model work, and accepted receipt in one SQLite
+transaction. That transaction is the durable acceptance boundary.
+
+Only after that commit does `ProviderCommandReactor` observe the committed intent
+and ask `ProviderService` to establish or continue the provider session and send the
+turn. The provider adapter maps native notifications to the canonical runtime union.
+Runtime ingestion turns those facts back into internal commands, whose resulting
+projections flow to a client through an HTTP snapshot and resumable subscription.
+The detailed command-to-checkpoint path belongs to [Chapter 4](../request-trace/),
+with the lifecycle guards expanded in [Chapter 23](../turn-lifecycle/).
+
+
+
+The decisive limitation is after the commit: the reactor consumes a hot stream, not
+a durable outbox. A crash in the narrow window after the transaction but before the
+provider reactor handles the event does not automatically replay a missing send on
+restart. Retrying the same command finds the accepted receipt and does not create a
+new event. This is intentionally safer than duplicating the durable fact, but it is
+not a proof that every accepted turn reached a harness.
+
+
+ The observed ordering gives one atomic environment-side acceptance point and a
+ later, hot side-effect consumer. Client convergence reads committed projections;
+ it cannot retroactively make an unobserved provider handoff durable.
+
+
+## Trace 2 — relay-connected mobile turn: launch through the relay, work directly with the environment
+
+A relay-connected phone first holds account authority and a device DPoP key. The
+relay authorizes discovery or connection setup and asks the selected environment to
+mint a short-lived bootstrap credential bound to that proof key, then returns the
+endpoint and bootstrap material to the phone. The phone exchanges that bootstrap
+**directly with the environment**, persists the environment-bound access material,
+gets a direct WebSocket ticket, and then runs the same shared connection, snapshot,
+and command path as another client.
+
+The tunnel can expose the local environment, but the hosted relay does not carry
+normal T3 API or WebSocket traffic after launch. Thus the mobile turn begins only
+after the environment session is established; neither a successful Clerk session
+nor a successful relay call is a thread update. [Chapter 35](../t3-connect/) owns
+the credential ladder and tunnel setup; [Chapter 29](../shared-client-runtime/) and
+[Chapter 33](../mobile-client-continuity/) own the client session and remote-native
+continuity edges.
+
+
+
+
+
+
+ The relay is explicitly outside normal post-launch traffic. The safe next step is
+ environment-scoped session establishment followed by normal snapshot and stream
+ reconciliation, not treating a control-plane response as authoritative thread
+ state.
+
+
+## Trace 3 — approval round-trip: persist a request and a response around a native callback
+
+A provider can request approval or structured user input while processing a turn.
+The adapter normalizes that native request; runtime ingestion flushes buffered
+assistant content before the interaction pause, records canonical pending activity,
+and projection storage exposes it to a selected web or mobile thread. The client
+derives a response UI from the synchronized pending request—not from an untrusted
+notification payload or a local approximation of provider state.
+
+When the person answers, the client sends a typed response intent. The decider
+creates a durable response-requested event; the provider command reactor later
+routes it to the exact bound session, and the adapter uses the provider-native reply
+mechanism. A subsequent provider event and projection update are what settle the
+visible pending state. The canonical and provider-specific mapping is in
+[Chapter 24](../permissions-and-input/).
+
+
+ This trace has two durable request sides—provider request projection and user
+ response intent—but the actual provider callback is a later reactor action. Do not
+ infer universal approval semantics from a T3 runtime-mode label; adapters map
+ their native controls differently.
+
+
+## Trace 4 — offline mobile task drain: durable phone intent waits for remote evidence
+
+While offline, the phone first exposes a queued row optimistically, then serializes
+an atomic file write. Before delivery, it confirms that the durable record still
+exists behind pending mutations. That prevents an item whose write failed from
+escaping merely because it was briefly visible in the UI.
+
+After reachability returns, the drain does not send every record at once. It handles
+one message globally and the first queued message per thread. For a creation, it
+waits for a **live shell**: a shell that already contains the stable thread id means
+the local cleanup is stale and the item is removed; only a live shell that lacks it
+may send. Existing-thread items likewise wait for enough shell evidence before a
+missing thread is discarded. Settings reconciliation can precede the final turn
+command; the environment then applies its normal receipt and invariant rules.
+
+Once the start-turn command returns its selected success result, the drain removes
+its local file; it does not wait for a later shell/detail projection to make that
+cleanup decision. Snapshot and stream state reconcile afterward and remain useful
+evidence on later drain passes. That joins no transaction across phone storage and
+server SQLite. [Chapter 33](../mobile-client-continuity/) is the full mobile
+state-machine account, while [Chapter 10](../events-receipts/) explains the
+environment receipt boundary.
+
+
+
+
+
+
+ Stable identities, serialized confirmation, live-shell evidence, and environment
+ receipts make retry safer. A crash or network loss between the atomic phone file
+ and the remote command still leaves a cross-store ambiguity that the drain handles
+ with evidence and policy rather than an exactly-once claim.
+
+
+## Trace 5 — checkpoint diff and revert: Git content first, then a conditional history rewrite
+
+For an eligible completed turn, the checkpoint reactor can capture the workspace
+through a temporary Git index into a hidden, thread-scoped ref. It derives the patch
+against the preceding checkpoint and dispatches a `thread.turn.diff.complete`
+command only after that capture/diff work. The engine then makes the checkpoint
+summary visible in the durable thread projection. A review screen can compare hidden
+turn boundaries; live working-tree and branch modes are separate Git queries.
+
+Revert begins with a durable request event, but it is an ordered saga rather than
+one transaction. It checks the thread, binding, Git workspace, turn count, and
+target; restores content and index, refreshes the workspace, asks the bound provider
+to roll back later turns, attempts ref pruning, returns those outcomes to the
+checkpoint reactor, and only then has the orchestration engine dispatch durable
+completion. If provider rollback or a later step fails after Git restore, files may
+already match the target while provider history and projected thread history do not.
+That partial state is an implementation-path inference, not a promise of automatic
+repair. See [Chapter 27](../checkpoints-revert/) for the full preconditions and
+provider matrix.
+
+
+ The ordered calls show filesystem replacement before provider rollback and before
+ durable completion. A failure later in that sequence records failure rather than
+ compensating every changed boundary; a completed projection is therefore not
+ warranted until the completion path runs.
+
+
+## Trace 6 — stable release and exact-version update: publish, prepare, trial, commit or roll back
+
+The release workflow first resolves a stable version and publishes the exact
+`t3@V` CLI/runtime package. Only then can the GitHub release make clients discoverable
+and a hosted deployment follow. A connected client that asks an eligible server to
+update targets that exact version, not whichever package currently owns an npm
+dist-tag.
+
+The active server rejects non-exact or concurrent targets, stages the immutable
+runtime, preflights it, and hands prepared target information to the stable service
+launcher. The launcher owns the migration/trial boundary: it records pending state,
+captures the SQLite triplet, stops the old child, starts the new candidate, and
+commits only after the candidate reports prepared. A failed or timed-out trial can
+restore the snapshot and select the old runtime; after durable commit, later
+failures are governed by ordinary restart policy instead. [Chapter
+38](../release-updates-observability/) separates this boot-service updater from the
+desktop and mobile state machines.
+
+
+
+
+
+
+ Publication is a release graph invariant, preflight is an active-server check,
+ and trial/rollback/commit belong to the independent launcher. Conflating these
+ three boundaries would make a package publish look like a successful machine
+ update, which the source does not support.
+
+
+
+
+## What these traces establish—and what they refuse to claim
+
+The repeatable pattern is not “make every operation a distributed transaction.” It
+is more disciplined: give each durable store a narrow contract, serialize the owner
+that must make a decision, pass effects through explicit handoff points, and show
+the reader or operator what evidence establishes convergence. Inference across
+modules is useful only when it keeps the seams visible.
+
+For example, a relay-connected mobile turn legitimately combines credential launch,
+environment supervision, and an ordinary turn. It does **not** imply that the relay
+stores the thread. Likewise, an outbox retry and a command receipt together reduce
+duplicate work, but they cannot promise that every provider-side effect occurred
+once. These distinctions are the basis for the decision review that follows.
+
+
diff --git a/src/content/book/400-decisions-limitations-roadmap.mdx b/src/content/book/400-decisions-limitations-roadmap.mdx
new file mode 100644
index 0000000..c3e0ba1
--- /dev/null
+++ b/src/content/book/400-decisions-limitations-roadmap.mdx
@@ -0,0 +1,413 @@
+---
+slug: decisions-limitations-roadmap
+order: 400
+number: "40"
+kind: chapter
+part: "Part VIII · Synthesis"
+partOrder: 8
+title: "Decisions, trade-offs, limitations, and an honest roadmap"
+shortTitle: Decision ledger
+summary: "T3 Code's architecture is a set of bounded choices: server authority, transactional events, hot reactors, cursored projections, adapters, scoped reconnect, durable mobile intent, exact-version updates, and demand-driven background work each make one failure mode tractable while deliberately leaving another visible."
+status: source-checked
+gates: [sources, interaction]
+objectives:
+ - Read the major architectural choices as pressure, choice, benefit, cost, alternative, and reversal trigger rather than as universal patterns.
+ - Separate shipped behavior, documented intent, source-bounded inference, latent capability, and explicit future work.
+ - Identify the persistence and delivery seams, including the conditions under which each choice should be reconsidered.
+ - Leave a precise inventory of platform asymmetries and repository discrepancies without inventing roadmap commitments.
+keywords: [architecture, trade-off, server authority, event sourcing, reactor, projection, provider adapter, reconnect, outbox, update, retention, roadmap]
+sourceAreas: [apps/server/src/orchestration, apps/server/src/provider, apps/server/src/auth, packages/client-runtime/src/connection, apps/mobile/src/state, apps/mobile/src/connection, docs/internals/remote.md, docs/operations/release.md]
+visuals: [decision ledger, trade-off path, limitation taxonomy]
+updatedAt: 2026-08-24
+---
+
+import Callout from "../../components/Callout.astro";
+import DecisionLedgerLab from "../../components/DecisionLedgerLab.astro";
+import EvidenceClaim from "../../components/EvidenceClaim.astro";
+import Figure from "../../components/Figure.astro";
+import Mermaid from "../../components/Mermaid.astro";
+import SourceExcerpt from "../../components/SourceExcerpt.astro";
+import SourceList from "../../components/SourceList.astro";
+
+This is a ledger, not a claim that T3 Code found the one right architecture. At
+the locked revision, its choices consistently put one environment server in charge
+of product authority, preserve accepted intent in SQLite, and make provider-native
+execution, filesystem work, and client presentation explicitly separate. That
+produces a comprehensible control surface across many harnesses. It also produces
+real seams: a committed event can miss a hot reactor, an external harness can cross
+an ambiguous crash boundary, and not every client or retained artifact has equal
+capabilities.
+
+Use the six columns throughout this chapter precisely:
+
+| Lens | Question it answers |
+| --- | --- |
+| Pressure | What failure or product constraint is being controlled? |
+| Choice | What is actually implemented at the pinned revision? |
+| Benefit | Which guarantee becomes easier to state or test? |
+| Cost | What complexity, boundary, or weaker guarantee remains? |
+| Alternative | What a different design could optimize instead—not a promise about T3. |
+| Reversal trigger | The product pressure that would justify revisiting this choice. |
+
+
+ **Shipped** means executable behavior at the lock. **Documented** means an upstream
+ statement of intent or operations rule. **Inference** is this book's explicitly
+ bounded conclusion from cited paths. **Future** is only an upstream item named as
+ unbuilt. A latent builder target or compatibility bridge is neither a shipped
+ surface nor a future commitment.
+
+
+## 1. Authority and durable intent: make the environment server the product boundary
+
+### Server authority
+
+The pressure is remote control without pretending that a browser, desktop shell, or
+phone owns a provider process, Git worktree, terminal, or filesystem. T3 puts those
+operations behind the environment server. Clients authenticate, use typed RPC, and
+hold projections and presentation state; a provider still owns its native reasoning
+and context engine. This makes one environment the place where authorization,
+orchestration, workspace effects, and product history meet.
+
+
+ Server composition acquires authentication, orchestration, provider, VCS, terminal,
+ filesystem, and remote-endpoint services in one owned runtime. The adapter boundary
+ keeps provider-native session behavior outside the product's durable domain core.
+
+
+The benefit is a clear remote model: adding another device adds another client of an
+environment rather than another competing owner of a workspace. The cost is that
+availability, upgrades, and recovery concentrate around that server; clients must
+reconnect and reconcile instead of making local state authoritative. A peer-to-peer
+or client-owned model could improve disconnected autonomy, but would need a conflict,
+credential, and workspace-execution story that this implementation deliberately does
+not carry. Revisit the choice if concurrent offline editing or multi-writer workspace
+authority becomes a primary product requirement.
+
+### Transactional event core
+
+The pressure is accepting a user command exactly enough to answer a retry without
+claiming provider completion. The choice is a serialized engine that decides a
+command, appends its event batch, applies projections, and records the command receipt
+inside one SQLite transaction. The benefit is a sharp acceptance boundary: retrying
+the same command id can recover the stored result, and a failed transaction leaves no
+accepted receipt. The cost is that normalization-time files, provider calls, and
+later reactor work are outside that transaction.
+
+
+
+An append-only log is not free: commands and deciders must retain invariants, read
+models must be maintained, and a receipt is not an end-to-end idempotence proof for a
+multi-step bootstrap saga. A direct CRUD model could lower local complexity when there
+is no need to replay or compose state. Revisit the event core when the product no
+longer benefits from immutable command history, independently shaped projections, or
+receipt-based retry semantics.
+
+
+ S[Environment server\nauthority]
+ S --> T[(Event + projection + receipt\nSQL transaction)]
+ T -. committed event .-> R[Hot reactor]
+ R --> A[Provider adapter]
+ A --> P[Native harness / process]
+ T --> Q[Projection cursors\nrebuild read models]
+ C --> O[Mobile durable\nintent outbox]
+ S --> U[Exact-version\nstage + preflight]
+ C -. retained demand .-> B[Scoped background work]
+ R -. no durable delivery record .-> X[Crash / replay seam]
+ A -. native state remains external .-> X
+ P -. result returns hot .-> R`} />
+
+
+## 2. Delivery and read state: choose explicit eventual boundaries
+
+### Hot, best-effort post-commit reactors
+
+The pressure is to avoid calling a harness while a domain transaction is still open.
+T3 publishes only after commit, then runs provider, runtime-ingestion, checkpoint,
+deletion, and awareness consumers as hot scoped workers. The benefit is simple:
+rollback cannot have caused a provider turn, and one reactor failure is handled without
+rolling back accepted domain history. The cost is the **reactor crash window**. A
+process can die after the receipt/event commit and before a reactor observes it; the
+source contains no durable outbox row, per-reactor delivery cursor, or startup replay
+of pending hot work.
+
+
+ The provider command reactor consumes current hot events and records duplicate
+ suppression in process memory before forking its send. Its source explicitly says
+ pending work is not replayed on a later subscription.
+
+
+That is a reasonable trade where an external operation can be ambiguous to repeat:
+a durable outbox still needs idempotency keys, attempt records, and a policy for a
+harness that accepted work just before a crash. It would be the alternative when the
+product requires guaranteed post-commit execution. The reversal trigger is explicit:
+if “accepted turn” must eventually imply “provider send attempted” across server
+crashes, add a durable delivery protocol rather than describing the current reactor as
+exactly once.
+
+### Snapshot plus cursor
+
+The pressure is a fast UI that can restart, replay, and subscribe without claiming
+that one monolithic snapshot is globally current. Each projector has a durable cursor;
+normal command acceptance advances its projector SQL and cursor together. A composed
+snapshot uses the minimum cursor among its required projectors, while live clients
+attach before their replay/snapshot read and use global event sequence for gap repair.
+
+
+ Projectors bootstrap independently from their own cursors. The snapshot helper
+ deliberately returns the minimum required projection sequence, not the event-log
+ head; subscription setup attaches live input before deciding replay or snapshot.
+
+
+The benefit is independently shaped and rebuildable read state. The cost is more
+than one watermark, a bounded bootstrap path, ordering-sensitive projectors, and
+careful client race guards. A single authoritative document per thread could simplify
+some reads but makes fan-out and independently evolved views harder. Revisit cursor
+architecture if projection lag, cross-projector joins, or operational replay needs
+outgrow the current SQLite/replay ceiling.
+
+## 3. Harness and client continuity: normalize the boundary, not the world
+
+### Provider adapters
+
+The pressure is five harnesses with different session, approval, stream, context,
+and process semantics. T3 chooses a narrow adapter lifecycle and canonical runtime
+event grammar; `ProviderService` owns routing, bindings, correlation, credentials,
+and cross-provider policy. The benefit is one product domain that can preserve native
+provenance without forcing all harnesses into a fictional universal feature set.
+
+The cost is an adapter matrix, capability gaps, and no repository-wide proof that all
+providers have semantic parity. A generic “agent protocol only” design could reduce
+some integrations but would either lose native features or push product policy into
+each provider driver. Revisit this boundary when the normalized contract can no
+longer represent an important native lifecycle without pervasive escape hatches.
+
+
+ The adapter exposes provider lifecycle and canonical event operations. The service
+ resolves persisted bindings, adopts/resumes sessions, correlates instance events,
+ and supplies product-level policy around the concrete adapter.
+
+
+### One reconnect owner per environment
+
+The pressure is multiple pages, caches, notifications, and devices observing one
+remote environment without creating retry storms or merging unrelated authorities.
+The choice is one environment registry entry with one supervisor generation and at
+most one active RPC lease; shell and thread synchronizers retain separate cache and
+cursor responsibilities. The benefit is one place to own transport lifecycle while
+each projection remains honest about its own authoritative refresh.
+
+The cost is a sophisticated supervisor, generations, leases, and surface-specific
+resynchronization. A global connection manager is attractive but would blur separate
+environment authority and corrupt cache ownership. Revisit this model if the product
+adds genuine cross-environment aggregation or a shared write model—not merely a UI
+that displays several environment summaries.
+
+
+ The runtime registry replaces an environment-scoped lease, and each new generation
+ leads the shell back through an authoritative refresh rather than elevating cache
+ continuity into authority.
+
+
+### Durable mobile intent outbox
+
+The pressure is a phone losing foreground time or connectivity after the user has
+pressed send. Mobile uses an optimistic enqueue with durable persistence, serializes
+its manager, and asks the user for a delivery decision when a queued turn collides
+with a changed thread state. The benefit is preserving user intent locally before it
+can be delivered to an environment. The cost is another state machine: existence
+guards, capped backoff, confirmations, and explicit replay semantics instead of an
+assumption that a compose action immediately became a server turn.
+
+An always-online client could omit this machinery, but it would trade away the
+recovery behavior that matters on a mobile lifecycle. Revisit the outbox when mobile
+intent becomes multi-device collaborative work requiring server-issued identities or
+when all clients need an equivalent durable intent queue.
+
+
+
+### Exact-version updates
+
+The pressure is a newly visible client asking a connected server to run an incompatible
+runtime. T3 publishes the exact CLI package before a release exposes the clients that
+can request it; a boot-service server rejects non-exact targets, stages and preflights
+that runtime, then hands activation to a stable launcher. The benefit is a precise
+compatibility target and a reversible SQLite-bound trial. The cost is release ordering,
+launcher protocol compatibility, and platform-specific update machinery.
+
+A floating channel update can reduce operations friction but makes an update request
+less reproducible. Revisit the invariant if a compatibility protocol—not matching
+versions—becomes sufficient to prove safe server/client combinations.
+
+
+ The operations guide names the npm-before-clients invariant; the self-update path
+ accepts only an exact target and validates its staged runtime before launcher handoff.
+
+
+### Scope-driven background work
+
+The pressure is staying useful while mobile is backgrounded without keeping every
+environment permanently active. Mobile background activity reports retained demand
+through reference-counted environment scopes. The benefit is a bounded reason for
+work to continue: a caller declares interest and releases it. The cost is that
+subscription ownership and lifecycle cleanup must be correct; background liveness is
+not a durable scheduler, and platform APIs constrain what actually runs.
+
+An always-on global worker would simplify call sites but waste resources and make
+ownership leaks more damaging. Revisit scopes when the product needs a durable
+background-job contract with OS-managed scheduling and completion receipts.
+
+
+ Mobile background work is reported per environment and reference-counted by retained
+ scopes; the platform layer owns connection cleanup rather than making a background
+ indicator evidence of durable execution.
+
+
+
+
+## 4. Limitations are part of the architecture contract
+
+The ledger above describes choices. This section records the places where their edges
+must stay visible in a design review.
+
+### Shipped asymmetries and discrepancies
+
+| Classification | What the pinned source establishes | Consequence for a reader or successor |
+| --- | --- | --- |
+| Shipped platform asymmetry | Desktop, boot-service server, and Expo mobile update through three different state machines; mobile's OTA is fingerprint-gated and its safe reload waits for persistence and lifecycle conditions. | “Update” cannot be one shared abstraction without losing its owner and rollback boundary. |
+| Latent artifact | Root build scripts support more target names than the inspected release matrix actually publishes; Windows ARM64 is present as a latent/commented target rather than a distributed artifact at this lock. | Builder support is not evidence that a user can download a release. |
+| Transition / compatibility path | The web hides OpenCode's `plan` agent when legacy plan mode is off and heals old stored selections after settings hydrate; mobile retains a device-local legacy plan-mode preference. | The plan UI is transitional compatibility code, not proof of a new universal planning model. |
+| Shipped model boundary | Provider resume state is an opaque binding; thread plans, checkpoints, drafts, and native harness history remain distinct records. | There is no universal memory layer or provider-independent continuation guarantee. |
+
+
+ The sources persist different identities and lifecycle shapes for bindings, domain
+ plans, checkpoints, and mobile intent, while plan progress is explicitly in memory.
+ Calling their collection “universal memory” would overstate what can be restored or
+ resumed across a provider boundary.
+
+
+
+ The web hydration pass repairs stale saved selections, the web capability filter
+ removes the OpenCode plan option while legacy mode is off, and mobile independently
+ keeps a device-local legacy preference. These are distinct compatibility paths.
+
+
+### Retention and cleanup are selected operations, not one erase button
+
+Tombstoning a project or thread changes its projected visibility; it does not erase
+the append-only event history or command receipts. Thread revert removes selected
+derived messages, plans, activities, turns, attachment files, and later checkpoint
+refs, but retains event history and records the revert itself. The thread-deletion
+reactor performs best-effort provider and terminal cleanup; the web separately offers
+an orphan-worktree removal path. Provider binding deletion exists as a repository
+operation, yet no caller appeared in the inspected production sources. Hidden checkpoint
+refs likewise do not gain a repository-wide lifecycle reconciler simply because a
+thread is tombstoned.
+
+
+ This conclusion is scoped to the inspected production paths: they distinguish
+ tombstones, selected revert pruning, hot deletion cleanup, and optional web worktree
+ cleanup. It is not a promise that future retention behavior will remain unchanged.
+
+
+Attachment cleanup has the opposite shape: normalizers write image bytes before
+command dispatch, while projectors perform later filesystem deletion after their SQL
+and cursor step. That reduces database/file coupling but makes cross-store atomicity
+unavailable. A rejected or retried command can leave an early file; a failed cleanup
+is logged, and a crash after cursor advancement can prevent that event from selecting
+the same cleanup again. This is **shipped behavior**, not a future garbage collection
+protocol.
+
+
+ File creation occurs before domain dispatch. The projector's SQL/cursor work occurs
+ before best-effort filesystem cleanup, and bootstrap resumes from durable cursors.
+
+
+DPoP replay defense records a replay marker under a hashed proof-derived key through
+exclusive secret creation.
+The source path establishes rejection of a duplicate marker. **Inference:** the
+inspected source does not show a corresponding marker-expiry or retention sweep, so
+this book cannot claim a bounded replay-marker store merely because proof timestamps
+are recorded. That is a retention inference, not a security weakness claim.
+
+
+ The code writes a durable marker keyed by proof material and maps an existing marker
+ to replay rejection. This audit did not locate a deletion or expiry path for those
+ markers at the pinned revision.
+
+
+### The only roadmap claims here are explicit upstream future work
+
+The internal remote document labels three items **unbuilt**: additional third-party
+tunnel endpoint providers, a **relay-hosted OAuth callback broker**, and richer
+multi-environment UI beyond the current connections list. They are future work, not
+dates, milestones, or a promise that their current design will ship. The pinned code
+and connect documentation do implement a hosted static `/connect/callback` handoff
+page. That is not evidence of a relay-hosted backend broker: only the broad claim
+that there is *no callback path* is stale, while the literal broker item remains
+future work.
+
+
+ The source explicitly calls the three remote items unbuilt. Separately, the hosted
+ callback handoff is executable; it does not establish the future relay broker. The
+ source provides no release date, priority, or implementation guarantee for the
+ broker or either other item.
+
+
+## 5. A decision review is more useful than a pattern checklist
+
+When evaluating T3 Code's choices, the useful question is not “should every system
+copy this?” It is: **which pressure does this choice address, which boundary is
+durable, and which cost does it impose?** T3's strongest recurring lesson is truthful separation:
+accepted intent is not provider completion; a projection is not a universal snapshot;
+a remote notification is not authority; a bounded local record is not a retention
+policy; and a compatibility branch is not a roadmap.
+
+
From a9cff004fb06aa647e6a431511ef88e5bfb1dab5 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:24 +0300
Subject: [PATCH 10/12] docs(project): add public edition guides
---
BOOK_PLAN.md | 718 ++++++++++++++++++++++++++++++++++++++++++++++++
CONTRIBUTING.md | 259 +++++++++++++++++
README.md | 269 +++++++++++++++++-
3 files changed, 1244 insertions(+), 2 deletions(-)
create mode 100644 BOOK_PLAN.md
create mode 100644 CONTRIBUTING.md
diff --git a/BOOK_PLAN.md b/BOOK_PLAN.md
new file mode 100644
index 0000000..d69e67c
--- /dev/null
+++ b/BOOK_PLAN.md
@@ -0,0 +1,718 @@
+# T3 Code Decoded — editorial and implementation plan
+
+This plan is pinned to T3 Code commit
+`fa219001dc2f14cfd9c7774c2c03c153359144be` (2026-08-23). It is both an
+editorial contract and a build checklist: every chapter must explain one coherent
+slice of the system, show the relevant runtime boundaries, and let the reader jump
+to the exact source revision that supports the explanation.
+
+## 1. Outcome
+
+The finished book should let a technical reader answer five questions without
+reading the monorepo in repository order:
+
+1. What does T3 Code own, and what remains owned by Codex, Claude, Cursor, Grok,
+ OpenCode, Git, the shell, or the operating system?
+2. How does one user intent travel from a client, through authorization and the
+ durable domain kernel, into a provider process, then return as ordered UI state?
+3. How do local web, hosted web, desktop, and mobile share semantics while keeping
+ different process, storage, rendering, and native-integration designs?
+4. How do pairing, remote access, updates, packaging, telemetry, and release
+ channels work beyond the happy path?
+5. Which behaviors are guaranteed, best effort, latent, transitional, or explicit
+ future work, and where do the important failure and retention seams remain?
+
+The book is explanatory, not a replacement for the upstream user guide. It covers
+implementation, invariants, failure handling, and design consequences. It does not
+invent a product roadmap or present repository clues as commitments.
+
+## 2. Source and claim contract
+
+### 2.1 Four claim classes
+
+Every non-trivial claim belongs to exactly one class:
+
+| Label | Meaning | Required evidence |
+| --- | --- | --- |
+| **Verified behavior** | Executable behavior at the pinned revision | Code, test, schema, migration, or workflow |
+| **Documented intent** | A maintainer's explanation or operating rule | Pinned internal/user/operations document |
+| **Inference** | A design consequence derived from several sources | All inputs cited; inference named explicitly |
+| **Future / proposed** | An explicit unshipped idea or limitation | Pinned future-work text or issue; never phrased as shipped |
+
+Release notes and the public docs may establish product wording, but code wins when
+describing current mechanics. A discrepancy is shown, not silently reconciled.
+
+### 2.2 Source lock
+
+- Canonical checkout: the path in `T3CODE_SOURCE_DIR`, or the sibling `../t3code`
+- Full source SHA: stored in `sources/t3code.lock.json`
+- Exact excerpts: declared in `sources/excerpts.manifest.json`
+- Generated excerpt text, line numbers, checksums, and permalinks:
+ `src/generated/excerpts.json`
+- Refresh: `npm run source:sync`
+- Drift check: `npm run source:check`
+
+No prose author copies a source block by hand. The synchronizer extracts it from the
+pinned checkout; the validator rejects unknown IDs, changed checksums, invalid
+ranges, and a checkout whose SHA no longer matches the lock.
+
+### 2.3 Status vocabulary
+
+- `draft`: narrative or visual is incomplete.
+- `source-checked`: material claims have a pinned source trail and exact excerpts
+ have passed validation.
+- `verified`: source checks, production build, interaction checks, links, and an
+ editorial review all pass.
+
+### 2.4 Roadmap vocabulary
+
+The book keeps these categories separate:
+
+- **Shipped at the source lock** — present in executable code.
+- **Latent capability** — builder or contract supports it, but current distribution
+ or UI does not ship it.
+- **Transition / compatibility path** — old and new behavior coexist.
+- **Explicit future work** — upstream docs say it is planned or desired.
+- **Community idea** — discussed externally but not committed by maintainers.
+
+There is no milestone-derived roadmap in the pinned repository. The known explicit
+future items in `docs/internals/remote.md` are handled as future work, while the
+implemented OAuth callback path is called out as a stale-document discrepancy.
+
+## 3. Reader routes
+
+The canonical route follows runtime causality:
+
+```text
+ownership → vocabulary → boot/auth → command/event kernel → provider boundary
+→ work lifecycle → client projection → remote path → distribution/operations
+```
+
+Alternative routes are surfaced in the reading guide:
+
+- **System designer:** 1, 4, 7, 9–15, 20, 26, 29, 34, 36, 40.
+- **Provider integrator:** 1, 7, 9–20, 23–27, 39.
+- **Client engineer:** 2, 7–8, 11, 23–24, 29–36, 39.
+- **Release/operator:** 4–8, 13, 34–40.
+- **Architecture auditor:** 1, 3, 9–15, 20, 23–26, 29, 36, 39–40.
+
+The front matter includes a complete spatial architecture map and a step-through
+request trace, so readers have both a map and a causal story before Chapter 1.
+
+## 4. Chapter specification
+
+Each chapter entry below specifies the question it must settle, its primary source
+anchors, and the visual or interactive artifact that makes the mechanism testable.
+All paths are relative to the pinned T3 Code checkout.
+
+### Start here
+
+#### Cover
+
+- **Purpose:** establish the edition, source revision, and independent, public,
+ source-locked nature.
+- **Artifact:** responsive use of the supplied cover, with an accessible text
+ alternative and no derivative asset generation.
+
+#### How to read a changing system
+
+- **Question:** how can a reader distinguish a fact, an interpretation, and a plan?
+- **Sources:** source-lock and generated-excerpt pipeline in this repository.
+- **Visual:** evidence ladder and freshness model.
+
+#### Contents and learning route
+
+- **Question:** why is the book ordered by causality instead of folders?
+- **Artifact:** part roadmap plus role-specific routes.
+
+#### The complete system map
+
+- **Question:** where do clients, trust, domain logic, work services, adapters, and
+ provider processes live?
+- **Sources:** `docs/internals/overview.md`, `apps/server/src/server.ts`,
+ `packages/contracts/src/rpc.ts`, `packages/client-runtime/src/connection`.
+- **Visual:** interactive ownership bands and intent/state flow.
+
+#### One request, every boundary
+
+- **Question:** what happens between pressing Send and seeing a settled answer?
+- **Sources:** orchestration contracts, normalizer, engine, reactors, provider
+ service, projectors, client thread reducer, checkpoint settlement.
+- **Artifacts:** an interactive phase lab and a full sequence diagram.
+
+### Part I — Boundaries and vocabulary
+
+#### 1. Control surface, not agent brain
+
+- **Settles:** T3 Code owns orchestration, durable product state, work surfaces, and
+ transport; each provider still owns its native reasoning/context engine.
+- **Sources:** `docs/internals/overview.md:5-28`, `ProviderAdapter.ts`, provider
+ drivers, orchestration contracts.
+- **Visual:** responsibility matrix for T3, provider, OS, Git, and client.
+
+#### 2. Environment, project, thread, turn, session
+
+- **Settles:** the vocabulary and cardinalities that later chapters assume.
+- **Sources:** `packages/contracts/src/environment.ts`, `project.ts`,
+ `orchestration.ts`, server projection schemas.
+- **Visual/lab:** clickable entity relationship map; lifecycle vocabulary quiz.
+
+#### 3. Repository and dependency atlas
+
+- **Settles:** what each app/package/infra/native directory builds and which edges
+ are runtime, build-time, protocol, generated, or deployment-only.
+- **Sources:** root/package manifests, workspace graph, Vite/Electron/Expo/Astro
+ configurations, native crates and packages.
+- **Visual:** filterable monorepo graph and a reproducible production/test inventory by area.
+
+#### 4. Runtime topologies and technology placement
+
+- **Settles:** the five delivery shapes: local CLI web, hosted web, Electron,
+ React Native, and marketing; why Effect, SQLite, React, Expo, Astro, Electron,
+ native Ghostty, and typed contracts appear where they do. Unrecorded rationale is
+ labeled as inference; code proves placement and consequences, not author intent.
+- **Sources:** each app entrypoint/config, server composition, runtime manifests.
+- **Visual:** topology switcher showing process and trust boundaries per surface.
+
+### Part II — Boot and connect
+
+#### 5. The `npx t3` bootstrap path
+
+- **Settles:** CLI argument/config resolution, server startup, address selection,
+ browser/local client behavior, and bundled web assets.
+- **Sources:** `apps/server/src/bin.ts`, `apps/server/src/cli/config.ts`,
+ `apps/server/src/cli/server.ts`, `apps/server/src/config.ts`,
+ `apps/server/vite.config.ts`, and `apps/server/package.json`.
+- **Visual:** CLI bootstrap swimlane; package-content exploder.
+
+#### 6. Server composition and execution boundary
+
+- **Settles:** how Effect layers assemble HTTP, RPC, auth, orchestration,
+ providers, VCS, terminal, files, assets, usage, relay, and telemetry services.
+- **Sources:** `apps/server/src/server.ts`, runtime-startup files, layer constructors.
+- **Visual:** layer-construction DAG with startup and shutdown ownership.
+
+#### 7. Effect RPC, subscriptions, and wire contracts
+
+- **Settles:** request/response versus subscription semantics, schemas,
+ serialization, errors, capabilities, snapshots, and cursors.
+- **Sources:** `packages/contracts/src/rpc.ts`, domain contract files,
+ server WebSocket router, `packages/client-runtime/src/rpc`.
+- **Lab:** inspect a command, acknowledgement, snapshot, and event frame.
+
+#### 8. Pairing, scopes, credentials, and WebSocket upgrade
+
+- **Settles:** environment auth policy, browser/bearer/DPoP credentials, pairing
+ grants, session/ticket lifetimes, per-method scopes, secret handling, and the
+ authenticated WebSocket upgrade. Target resolution and reconnect stay in Chapter 29.
+- **Sources:** `docs/internals/environment-auth.md`, server auth/session/pairing/
+ secret-store layers, HTTP credential exchange, and RPC scope enforcement.
+- **Lab:** credential, scope, TTL, one-use, persistence, and replay-resistance ladder.
+
+### Part III — Transactional domain core and post-commit delivery
+
+#### 9. Commands and invariants
+
+- **Settles:** external/internal commands, normalization, authorization, the
+ decider's persistence- and provider-I/O-free boundary despite clock/UUID Effect
+ dependencies, aggregate validation, the atomic event batch for an existing turn,
+ and multi-step bootstrap with compensating cleanup outside that transaction.
+- **Sources:** orchestration contracts, `Normalizer.ts`, deciders, aggregate tests.
+- **Lab:** choose valid/invalid command transitions and inspect emitted events.
+
+#### 10. Events, receipts, and idempotency
+
+- **Settles:** event envelope, command receipt, retry behavior, event publication,
+ and why acknowledgement does not mean provider work is complete.
+- **Sources:** `OrchestrationEngine.ts`, persistence schemas, command receipt tests.
+- **Visual:** transaction boundary and duplicate-command timeline.
+
+#### 11. Projection tables and read models
+
+- **Settles:** how events update shell/thread/activity/plan/checkpoint/runtime views,
+ then become HTTP snapshots and live subscriptions.
+- **Sources:** projector registry/implementations, read stores, snapshot handlers.
+- **Lab:** fold the same event stream into several projections.
+
+#### 12. Post-commit reactors: serialized handling without durable delivery
+
+- **Settles:** intent events trigger side effects only after commit, internal results
+ re-enter the command queue, workers serialize handling, scopes interrupt work on
+ shutdown, and the hot stream does not durably replay pending side effects.
+- **Sources:** reactor layers, drainable worker utilities, engine dispatch tests.
+- **Visual:** commit/publish/react/ingest timing diagram with crash points; contrast
+ the non-durable server bridge with mobile's durable client outbox.
+
+#### 13. SQLite, files, settings, secrets, migrations, and recovery
+
+- **Settles:** SQLite schema/WAL/migrations, settings JSON, secret files,
+ attachments/logs/terminal history, hidden Git refs, tombstones/retention, and
+ startup reconstruction. Provider adoption semantics wait for Chapter 15.
+- **Sources:** persistence package, migrations, recovery services and tests.
+- **Lab:** crash at selected phases and show the recoverable state.
+
+### Part IV — Five harnesses, one product model
+
+#### 14. The `ProviderAdapter` contract
+
+- **Settles:** discovery, sessions, turns, steer/interrupt, approval/input, modes,
+ canonical event streams, errors, and capabilities.
+- **Sources:** `ProviderAdapter.ts`, provider contracts, adapter conformance tests.
+- **Visual:** normalized boundary with required and optional capability lanes.
+
+#### 15. Drivers, instances, registries, and multi-instance routing
+
+- **Settles:** provider identity versus binary/driver/instance/native account/session,
+ discovery and health, registry lookup, managed processes, and recovery.
+- **Includes:** legacy provider settings merged into explicit opaque instance configs
+ (explicit wins), secret-backed/redacted environment values, live child-scope
+ replacement, unavailable shadow entries, and the plaintext OpenCode password exception.
+- **Sources:** provider registry/instance layers, driver discovery, ProviderService.
+- **Lab:** route several threads across installed providers and accounts.
+
+#### 16. Codex through app-server JSON-RPC
+
+- **Settles:** app-server process lifecycle, initialize/account/model/config,
+ thread resume, turn calls, approvals, streaming notifications, and normalization.
+- **Sources:** Codex driver and `packages/effect-codex-app-server`.
+- **Visual:** native JSON-RPC to canonical event mapping.
+
+#### 17. Claude through the Agent SDK
+
+- **Settles:** SDK session lifecycle, permissions, models/modes, skills/commands,
+ resume metadata, tool events, and result handling.
+- **Sources:** Claude driver, Claude skills scanner, adapter tests.
+- **Visual:** Claude SDK event-to-product mapping and skill precedence.
+
+#### 18. Cursor and Grok through ACP
+
+- **Settles:** shared ACP client/transport, provider-specific launch/configuration,
+ session calls, capabilities, request forwarding, and notification mapping.
+- **Sources:** Cursor/Grok drivers and `packages/effect-acp`.
+- **Visual:** shared ACP spine with provider-specific forks.
+
+#### 19. OpenCode and the normalization matrix
+
+- **Settles:** OpenCode transport/session path and a five-provider comparison of
+ resume, models, plans, approvals, input, commands, skills, subagents, usage, and
+ failure semantics.
+- **Sources:** OpenCode driver plus all adapter conformance fixtures.
+- **Lab:** capability matrix that explains UI enablement and fallback.
+
+#### 20. Two usage systems: live context telemetry and transcript accounting
+
+- **Settles two separate pipelines:** (1) live per-thread token/context-window
+ telemetry normalized from provider runtime events; and (2) the historical Usage
+ page, which scans provider-owned transcript files independently of T3's
+ orchestration projections. The chapter never treats one as the source of the other.
+- **Provider coverage:** the historical scanner supports Codex and Claude at this
+ revision; Cursor, Grok, and OpenCode contribute no Usage-page transcript source.
+ Live context telemetry is likewise emitted only by the Codex and Claude adapters
+ at this revision; the generic runtime event contract can represent it for a future
+ adapter, but Cursor, Grok, and OpenCode do not currently emit it. The five-adapter
+ matrix from Chapter 19 makes that implementation gap explicit.
+- **Scanner pipeline:** provider home resolution → transcript discovery with mtime
+ window slack → provider-specific parse → within-file and cross-file de-duplication
+ → canonical timestamped records → `(day, hour?, provider, model)` aggregate
+ buckets → session counts and source diagnostics → persisted
+ `(path, size, mtime, provider)` scan cache. The contract can represent partial and
+ failed scans, while the current implementation predominantly reports `ok` or
+ `missing` and leaves malformed-record accounting at zero.
+- **Accounting rules:** IANA-time-zone day buckets and an exact rolling 24-hour mode;
+ cached input and cache creation remain disjoint from uncached input; reasoning is a
+ subset of output and is never added twice; cost is provider-reported, LiteLLM
+ model-priced, or explicitly unpriced; cache savings are estimated alongside cost;
+ API-equivalent cost is not subscription billing.
+- **Cross-environment composition:** raw transcripts never cross the wire. Each
+ environment returns typed aggregate buckets and a physical-source fingerprint.
+ Web and mobile use the shared merge to claim duplicate transcript directories once,
+ exclude incompatible contract versions, count distinct sessions without summing
+ per-bucket duplicates, and expose partial/failed coverage honestly.
+- **Sources:** `packages/contracts/src/usage.ts`, `apps/server/src/usage/UsageService.ts`,
+ `usageTranscriptReader.ts`, `usageTranscripts.ts`, `usageAggregation.ts`,
+ `usagePricing.ts`, `usageScanCache.ts`, `packages/shared/src/usageMerge.ts`, the
+ usage RPC, web/mobile usage state and presentation, and their tests.
+- **Third usage path:** Codex and Claude can also report per-task/subagent usage for
+ the Agents surface. It is neither the context meter nor historical cost input and
+ is deferred to Chapter 25 with provider tasks.
+- **Visual:** two-lane topology separating live thread telemetry from transcript
+ accounting all the way through their distinct product presentations; there is no
+ joining or summation arrow.
+- **Lab:** usage-accounting workbench. Choose provider records, duplicates, session
+ boundaries, cache categories, model-rate availability, time zone, and two
+ environments that may share a source fingerprint; step through parse, normalize,
+ deduplicate, bucket, price, summarize, and merge while every total shows its formula.
+
+### Part V — The work lifecycle
+
+#### 21. Project discovery and `t3.json`
+
+- **Settles:** roots, discovery, project identity, configuration precedence,
+ explicit registration entry points, checked-in-script import boundaries, setup
+ commands, environment labels, and project projection.
+- **Sources:** project contracts/services, config loader, discovery tests and docs.
+- **Visual:** filesystem decision lab, checked-in-action import flow, and project
+ identity projection.
+
+#### 22. Current checkout versus isolated worktrees
+
+- **Settles:** workspace kinds, branch/base selection, creation/setup/cleanup,
+ Git invariants, normal thread-deletion versus optional worktree cleanup, and
+ failure recovery.
+- **Sources:** workspace/worktree services, VCS contracts, orchestration decider.
+- **Lab:** choose a task topology and inspect checkout/branch consequences.
+
+#### 23. Start, stream, steer, interrupt, settle
+
+- **Settles:** complete thread/turn state machine and provider runtime states,
+ including buffering, steering, interruption, retries, compaction, and completion.
+- **Sources:** orchestration contract/decider/reactors, ProviderService, reducers.
+- **Lab:** event timeline with illustrative teaching controls at each state; it is
+ explicitly not presented as the server's complete legality matrix.
+
+#### 24. Permission modes, approvals, and structured input
+
+- **Settles:** approval-required, auto-accept-edits, auto, full-access; how provider
+ prompts become canonical pending requests and how clients resolve them.
+- **Sources:** runtime mode contracts, provider adapters, approval/input reactors,
+ web/mobile components.
+- **Visual:** security-responsibility matrix and multi-provider mapping.
+
+#### 25. Threads, provider tasks, plans, skills, and subagents
+
+- **Settles:** threads as durable work items; provider-emitted tasks/subagents as
+ normalized durable activity; ephemeral liveness and live plan progress; lifecycle/
+ pin/snooze overlays; per-task token/tool/duration rollups currently normalized by
+ Codex and Claude; and why this is not a durable cross-provider scheduler. Task
+ usage is not fed into either live context telemetry or Chapter 20's historical
+ transcript accounting.
+- **Sources:** orchestration/settings contracts, provider skills, work-log logic.
+- **Lab:** quiet work-log projector for web, Agents surface, and mobile.
+
+#### 26. Who owns context, compaction, and memory
+
+- **Settles:** provider-owned prompt context and compaction versus T3-owned thread
+ history, resume cursor, and context-window telemetry, with client drafts/outbox
+ deferred to Chapter 33. It explicitly documents that no universal T3 long-term
+ memory subsystem exists at this revision and cross-links the separate historical
+ accounting pipeline in Chapter 20 instead of conflating usage with memory.
+- **Sources:** provider sessions, runtime context-window events, thread state/reducer,
+ provider compaction paths, and client cache ownership.
+- **Lab:** ownership and restart ledger covering provider context, T3 projections,
+ live telemetry, client cache, drafts, and provider-session recovery.
+
+#### 27. Hidden-ref checkpoints, diffs, and revert
+
+- **Settles:** before/after turn checkpoints, hidden Git refs, changed-file summary,
+ turn/branch/working-tree diffs, destructive restore semantics, `ready`/`missing`/
+ `error`, provider-specific rollback, and partial failure. Client history-epoch
+ implementation is deferred to Chapter 29.
+- **Sources:** checkpoint service/reactor/contracts, diff state, web/mobile review.
+- **Lab:** Git graph before a turn, after a turn, and after revert.
+
+#### 28. Terminals, files, previews, MCP, VCS, and pull requests
+
+- **Settles:** the workbench services surrounding chat, their authorization and
+ streaming models, signed assets, desktop-only preview, and surface parity.
+- **Sources:** terminal/files/assets/MCP/VCS/PR contracts and services; Ghostty
+ implementations; desktop preview APIs.
+- **Visual:** capability matrix plus terminal and signed-asset data paths.
+
+### Part VI — Client architectures: shared semantics, platform edges
+
+#### 29. The shared client runtime
+
+- **Settles:** Primary/Bearer/Relay/SSH target taxonomy, connection registry/resolver/
+ supervisor/one-attempt session, Effect Atom registry,
+ shell/thread factories, snapshot/cursor algorithms, cache ownership, and retry.
+- **Sources:** `packages/client-runtime/README.md` and `src/connection`, `src/rpc`,
+ `src/state`.
+- **Lab:** snapshot, duplicate, gap, reconnect, revert, and older-page races.
+
+#### 30. Web routes, state, and rendering performance
+
+- **Settles:** hosted/local/Electron runtime choice, router history, providers,
+ atoms, virtualization, row-local updates, trace export, and browser terminal.
+- **Sources:** `apps/web/src/main.tsx`, `AppRoot.tsx`, route tree, atom registry,
+ `MessagesTimeline.tsx`, `ChatView.tsx`.
+- **Visual:** React composition and hot-path rendering diagram.
+
+#### 31. Composer, work log, review, and sidebar lifecycle
+
+- **Settles:** message composition/attachments/commands/skills, optimistic states,
+ quiet timeline and Agents surface, review modes, thread ordering and pinning.
+- **Sources:** web composer, session logic, diff panel, thread sort/sidebar.
+- **Lab:** same canonical thread projected into focused product surfaces.
+
+#### 32. Desktop: Electron, IPC, server ownership, browser, and SSH
+
+- **Settles:** main/preload/renderer boundary, the host-local primary, Windows
+ WSL-only versus dual-instance modes, backend-pool ownership, fd 3/4/5 bootstrap
+ and telemetry, exposure, the separate SSH gateway, preview webviews, menus, and
+ updates.
+- **Sources:** desktop main/app/preload/backend/preview/SSH/update modules.
+- **Visual/lab:** mutually exclusive primary choices, optional WSL secondary,
+ separate SSH authority, and the boot/readiness/shutdown lifecycle.
+
+#### 33. Mobile: persistence, outbox, sharing, and native systems
+
+- **Settles:** Expo app composition, native navigation/feed choices, durable drafts
+ and intent outbox, share reservation, native terminal/diff/keyboards/widgets,
+ iOS notification capability, and OTA coordination.
+- **Sources:** app config, connection runtime, outbox, ThreadFeed, native modules,
+ awareness, updates.
+- **Lab:** offline command-outbox state machine and guarded foreground/background
+ OTA update handoff.
+
+### Part VII — Reach and ship
+
+#### 34. Primary, paired bearer, Tailscale endpoints, and SSH access
+
+- **Settles:** launch transport versus access transport, bind/exposure policy,
+ one-time pairing, bearer registration, Tailscale endpoint provisioning, and
+ desktop SSH gateway behavior.
+- **Sources:** remote/environment-auth docs, Tailscale/SSH packages, resolver,
+ desktop exposure and gateway code.
+- **Visual:** primary/direct, paired bearer over LAN or Tailscale, and desktop-managed
+ SSH sequences. Tailscale is an endpoint provider, not a connection target kind.
+
+#### 35. T3 Connect: OAuth, DPoP, relay, and tunnel
+
+- **Settles:** Clerk session, device key, DPoP, environment registration, relay
+ broker, tunnel provisioning, OAuth callback path, and what traffic does *not*
+ traverse the relay.
+- **Sources:** `docs/internals/t3-connect.md`, relay infra, hosted routes, remote auth.
+- **Visual/lab:** credential ladder and launch/data-plane toggle.
+
+#### 36. Reconnect, multi-environment state, notifications, and version skew
+
+- **Settles:** environment catalog, generation leases, shell/thread reconciliation,
+ background demand, multi-environment merge, awareness relay/APNs, and capability/
+ exact-version recovery.
+- **Sources:** client supervisor/state, background contracts/policy, relay awareness,
+ client version skew and self-update state.
+- **Labs:** connection supervisor and notification throttling/fallback.
+
+#### 37. Distribution artifacts: CLI, hosted app, desktop, mobile, marketing, and AUR
+
+- **Settles:** npm CLI plus copied web build, hosted static web, Electron artifact
+ matrix/signing/native staging, mobile stores, marketing, AUR, and the difference
+ between builder support and artifacts actually shipped.
+- **Sources:** build configs/scripts, package manifests, release workflow, marketing
+ download resolver, AUR scripts.
+- **Visual:** artifact factory from source tree to installable products.
+
+#### 38. Release graph, three update systems, and observability/privacy
+
+- **Settles in three explicit acts:** (1) release DAG, two version domains, and the
+ npm-before-clients invariant; (2) Electron updater, managed-server exact-version
+ preflight, and EAS fingerprint/OTA as three independent state machines; (3)
+ PostHog product analytics, local/OTLP tracing, browser trace ingestion, and local
+ desktop resource telemetry with precise identities, destinations, and controls.
+- **Sources:** release/mobile workflows, updater state machines, self-update,
+ analytics/observability/resource telemetry.
+- **Labs:** release-channel resolver, OTA eligibility grid, update handshake.
+
+### Part VIII — Synthesis
+
+#### 39. Six complete traces
+
+- **Traces:** local first turn; relay-connected mobile turn; approval round-trip;
+ offline mobile task drain; checkpoint diff/revert; stable release and exact-version
+ update.
+- **Artifact:** synchronized swimlanes whose steps link back to the owning chapters
+ and exact sources.
+
+#### 40. Decisions, trade-offs, limitations, and an honest roadmap
+
+- **Settles:** why server authority, the transactional event core, hot post-commit
+ reactors, snapshot + cursor, adapters, one reconnect owner, durable mobile intent,
+ exact-version updates, and scope-driven background work are valuable—and what
+ complexity each choice creates.
+- **Includes:** verified discrepancies, platform asymmetries, latent artifact targets,
+ transitional plan UI, no universal memory layer, and explicitly documented future
+ remote work. It also covers tombstone deletion/selected cleanup, retained event/
+ binding/worktree/checkpoint state, attachment cleanup outside the projection
+ transaction, the replay-marker retention inference, and the server reactor crash window.
+- **Visual:** decision ledger with pressure, choice, benefit, cost, alternative, and
+ reversal trigger.
+
+## 5. Visual system
+
+Visuals use a consistent grammar rather than decorative diagrams:
+
+- navy = client/product surface;
+- blue = typed transport or contract;
+- amber = durable intent/state;
+- green = post-commit side effect or external execution;
+- violet = provider-native protocol;
+- red = failure, trust, or destructive boundary;
+- dashed edge = asynchronous, retryable, or eventual;
+- solid edge = synchronous call or transactional relation.
+
+Every figure has a title, numbered caption, text equivalent, source trail, keyboard
+operation where interactive, and a static print state. Mermaid is reserved for
+sequences/flows whose source is clearer as text. Bespoke Astro components handle
+state machines, comparisons, timelines, and simulations.
+
+## 6. Interaction inventory
+
+The final book contains, at minimum:
+
+1. ownership layer map;
+2. end-to-end request trace;
+3. entity/cardinality explorer;
+4. monorepo graph filters;
+5. runtime topology switcher;
+6. connection supervisor state machine;
+7. RPC frame inspector;
+8. command/decider lab;
+9. projection fold lab;
+10. crash/recovery lab;
+11. provider capability matrix;
+12. work lifecycle controller;
+13. quiet work-log projector;
+14. usage accounting and cross-environment de-duplication workbench;
+15. context/compaction/memory ownership ledger;
+16. checkpoint Git graph;
+17. cursor/reconnect race lab;
+18. mobile outbox lab;
+19. notification delivery lab;
+20. artifact/release explorer;
+21. OTA/update eligibility lab;
+22. synchronized six-trace ownership and failure-boundary stepper;
+23. decision ledger;
+24. background demand/power-policy lab;
+25. three-updater failure comparison;
+26. telemetry identity/destination/privacy flow.
+
+An interaction is included only when changing an input reveals a state transition,
+invariant, or trade-off that static prose would obscure.
+
+## 7. Book engine
+
+The old `codex-decoded` engine supplied the useful visual precedent: source cards,
+architecture diagrams, and linear chapter navigation. This edition replaces its
+eager Vite/hash/HTML-string architecture with:
+
+- Astro static routes and typed MDX content;
+- one content collection as the navigation/search/metadata authority;
+- build-time excerpt extraction and source-lock validation;
+- Pagefind full-text search with development metadata fallback;
+- lazy Mermaid and no framework runtime for ordinary pages;
+- accessible sidebar, keyboard search, theme, heading navigation, previous/next,
+ reduced motion, and print styles;
+- responsive source cards with real line numbers, checksums, copy, and immutable
+ GitHub permalinks;
+- `BASE_PATH` support for repository-scoped GitHub Pages without changing links.
+
+The target is a content-first static site: JavaScript is paid only for search,
+diagrams, and genuine simulations.
+
+## 8. Authoring waves and review gates
+
+### Wave A — mental model and kernel
+
+- Front matter and Chapters 1–15.
+- Gate: one request can be traced from RPC to committed event, reactor, provider,
+ runtime ingestion, projection, and client cursor with no unexplained jump.
+
+### Wave B — providers, usage, and work lifecycle
+
+- Chapters 16–28.
+- Gate: every provider is compared against the actual adapter contract; context,
+ memory, task, plan, live token telemetry, historical usage accounting, and
+ checkpoint ownership are not conflated.
+
+### Wave C — clients and remote access
+
+- Chapters 29–36.
+- Gate: every surface difference is explicit; shared runtime algorithms and
+ presentation-specific algorithms are both explained.
+
+### Wave D — distribution and synthesis
+
+- Chapters 37–40 and six end-to-end traces.
+- Gate: current stable, nightly, builder-only, mobile, and managed-server paths are
+ distinct; roadmap statements are evidence-classified.
+
+### Review loop for every wave
+
+1. Source audit against the pinned checkout.
+2. Claim/excerpt/source-trail validation.
+3. Cross-chapter vocabulary and forward-reference review.
+4. Diagram and simulator invariant review.
+5. Production build, search index, internal links, and responsive static checks.
+6. Editorial pass for causal order, repetition, and unstated assumptions.
+7. Local commit. Merge authored waves through pull requests once the public remote
+ and branch protection are active.
+
+## 9. Automated quality gates
+
+Required local commands:
+
+```sh
+npm run source:check
+npm test
+npm run build
+```
+
+The validation suite will grow to enforce:
+
+- unique slugs and chapter order;
+- a complete 1–40 chapter table of contents;
+- valid excerpt IDs and source-lock SHA;
+- no source-checked chapter without a source trail;
+- no broken internal route/heading/source permalink;
+- alt text and accessible names for visual/interactive components;
+- all simulations usable by keyboard and meaningful in print;
+- Pagefind indexing every non-cover chapter;
+- bounded client bundles, with Mermaid and labs split by route;
+- zero external analytics or network dependency in the local book.
+
+## 10. Definition of done
+
+The project is complete when all 40 chapters and front matter are present, all are
+`source-checked` or `verified`, every planned flow has either a figure or lab, exact
+source references resolve at the pinned revision, the six synthesis traces agree
+with their detailed chapters, the static build and validation suite pass, and a
+fresh reader can progress from ownership to deployment without requiring knowledge
+that appears later in the book.
+
+## 11. Public repository and GitHub Pages
+
+The publication target is a public GitHub repository named `t3code-decoded` with
+GitHub Pages serving the validated static build. It is owned by the personal
+`BenAlaa` account, not an EasyGenerator organization.
+
+- Default branch: `main` (no parallel `master` branch).
+- Pages source: GitHub Actions artifact from `npm ci`, source validation, tests,
+ and `npm run build`.
+- Pull requests: required before any authored book commit reaches `main`; the only
+ bootstrap exception is GitHub's generated placeholder commit used to create the
+ base branch before protection is enabled.
+- Reviews: at least one approving review; stale approvals dismissed when new
+ commits are pushed; latest-push approval and conversation resolution required.
+ The owner account `BenAlaa` receives **pull-request-only** bypass so a solo-owned
+ PR can merge without self-approval while still leaving a PR and bypass audit
+ trail. It receives no routine direct-push exemption.
+- Checks: `Validate and build` and `Conventional changes` must pass; the latter
+ enforces the PR title/body and every fine-grained commit subject. Force pushes
+ and branch deletion are disabled, and history stays linear through rebase merges.
+- Community files: detailed `README.md`, `CONTRIBUTING.md`, code of conduct,
+ security policy, issue forms, pull-request template, and `CODEOWNERS`.
+- Licensing/attribution: distinguish original book prose/site code from short
+ MIT-licensed T3 Code excerpts, and state clearly that this is an independent,
+ unofficial study guide.
+
+The public repository and Pages pipeline are active. The complete source-validated
+edition is published from protected `main`. New work remains on local or topic
+branches until it is pushed and opened as a pull request, then merges only after
+the required checks and review policy are satisfied. No authored project work is
+pushed directly to `main`; every change keeps its pull-request audit trail.
+
+Commits inside a part remain fine-grained: shared engine capability, individual
+chapter or tightly coupled chapter pair, source manifest change, lab/figure, and
+review correction are separate when they can be understood and reverted alone.
+Pull-request bodies use the repository template and explain outcome, non-goals,
+evidence, interactions, validation, and the review's riskiest assumptions.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..bcbc5c4
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,259 @@
+# Contributing to T3 Code Decoded
+
+Thank you for helping make the architecture easier to understand. This repository
+values corrections and well-grounded explanations more than volume. A contribution
+is successful when a reader can verify it at the pinned T3 Code revision and it
+fits the causal learning path of the book.
+
+## Before you start
+
+Read:
+
+1. [README.md](./README.md), especially the source-grounding model;
+2. [BOOK_PLAN.md](./BOOK_PLAN.md), for chapter ownership and intended figures;
+3. [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
+
+For a typo, broken link, small accessibility fix, or unambiguous source correction,
+a pull request is enough. Open an issue first when proposing:
+
+- a new chapter or major reorder;
+- a new dependency or framework runtime;
+- a source-lock update;
+- a public claim about roadmap, security, privacy, or product behavior;
+- a simulation that changes the visual grammar;
+- a license or governance change.
+
+## Development setup
+
+```sh
+npm ci
+npm run dev
+```
+
+Run the complete local gate before requesting review:
+
+```sh
+npm test
+npm run build
+```
+
+To verify generated excerpts against a T3 Code checkout:
+
+```sh
+T3CODE_SOURCE_DIR=/absolute/path/to/t3code npm run source:check
+```
+
+The checkout must be at the commit in `sources/t3code.lock.json`.
+
+## Choose the right change boundary
+
+Keep one concern per branch and pull request:
+
+- `docs/...` for chapter prose or source corrections;
+- `feat/...` for a new reader-facing component or lab;
+- `fix/...` for engine, accessibility, or interaction defects;
+- `test/...` for validators and regression coverage;
+- `ci/...` for validation and deployment workflows;
+- `chore/...` for tooling, dependencies, and repository maintenance.
+
+A whole book part can be one review milestone when its chapters form a coherent
+causal unit. Within that branch, keep each chapter, visual, or shared engine change
+in a focused commit. Do not mix a provider chapter rewrite with unrelated CSS or
+release automation.
+
+## Conventional Commits
+
+Every commit and pull-request title follows this form:
+
+```text
+():
+```
+
+Allowed common types:
+
+- `feat` — reader-facing engine, component, figure, or lab;
+- `fix` — correction to behavior, layout, accessibility, or facts;
+- `docs` — book prose, README, plan, or governance documentation;
+- `test` — tests and validation assertions;
+- `refactor` — internal restructuring with no reader-facing behavior change;
+- `perf` — measurable build/runtime improvement;
+- `build` — build-system or packaging changes;
+- `ci` — continuous-integration and deployment changes;
+- `style` — formatting-only changes with no semantic effect;
+- `chore` — dependencies, workflows, and maintenance.
+
+Useful scopes include `book`, `engine`, `sources`, `provider-codex`,
+`provider-claude`, `clients`, `remote`, `release`, `pages`, and `repo`.
+
+Good examples:
+
+```text
+docs(kernel): explain command receipts and crash windows
+feat(checkpoints): add the hidden-ref restore simulator
+fix(remote): separate relay provisioning from the data plane
+test(sources): reject excerpts outside the locked checkout
+chore(pages): publish the base-path-aware static artifact
+```
+
+Avoid vague summaries such as `updates`, `fix docs`, or `changes`.
+
+Use the body to explain *why* when the diff cannot do so by itself. Add a footer for
+breaking authoring/engine changes:
+
+```text
+BREAKING CHANGE: chapter frontmatter now requires an evidenceClass field.
+```
+
+## Writing and evidence rules
+
+### Start with the question
+
+Every chapter should settle one named architectural question. Its opening should
+locate the mechanism in the end-to-end request, and its closing should state the
+invariant or trade-off the reader should retain.
+
+### Classify claims
+
+Use the book's four evidence classes:
+
+- verified behavior;
+- documented intent;
+- inference;
+- future/proposed.
+
+Do not turn a comment, test fixture, release artifact builder, issue, or external
+post into a shipped-product claim without checking the executable path.
+
+### Prefer exact source anchors
+
+Use `SourceList` for a trail and `SourceExcerpt` only when the source shape itself
+teaches something important. Keep excerpts short enough to annotate. Add a manifest
+entry instead of hand-copying code:
+
+```json
+{
+ "id": "descriptive-stable-id",
+ "path": "apps/server/src/example.ts",
+ "start": 10,
+ "end": 28,
+ "language": "typescript",
+ "label": "What this excerpt demonstrates"
+}
+```
+
+Then run `npm run source:sync`. Generated JSON is committed with the manifest and
+chapter that uses it.
+
+### Explain failure behavior
+
+For commands, side effects, persistence, transport, updates, and cleanup, answer:
+
+- What is atomic?
+- What is merely ordered?
+- What survives a crash or reconnect?
+- Who retries?
+- What is idempotent?
+- What can partially succeed?
+- Which provider or surface behaves differently?
+
+### Respect source licensing
+
+Quote only the minimum source required to explain the design. Do not copy upstream
+documentation or substantial source files into the book. Preserve attribution and
+immutable links. Never include credentials, local paths containing personal data,
+private downstream implementation details, or unpublished repository content.
+
+## Diagrams and interactive labs
+
+Use a visual only when it makes structure, sequence, mapping, state, or a trade-off
+materially clearer. Follow the grammar in `BOOK_PLAN.md` and provide:
+
+- a title and numbered caption;
+- a text equivalent in the surrounding prose;
+- source references;
+- keyboard operation and visible focus when interactive;
+- a useful static/print state;
+- reduced-motion behavior;
+- no essential meaning encoded by color alone.
+
+Simulations model real invariants. Their controls and transitions must be derived
+from source and covered by a small unit test where practical. Do not add decorative
+animation or a generic chart that repeats the prose.
+
+## MDX chapter checklist
+
+Each chapter needs valid collection frontmatter:
+
+```yaml
+slug: durable-command-core
+order: 14
+number: "9"
+kind: chapter
+part: Part III · The durable domain kernel
+partOrder: 3
+title: Commands and invariants
+shortTitle: Commands
+summary: A one-sentence promise to the reader.
+status: draft
+objectives:
+ - A concrete question the chapter resolves.
+keywords: [commands, decider, invariants]
+sourceAreas: [packages/contracts/src/orchestration.ts, apps/server/src/orchestration]
+visuals: [command microscope]
+updatedAt: 2026-08-24
+```
+
+Keep the `order` unique and preserve causal order. Prefer short sections and local
+forward links over repeating a concept before its chapter.
+
+## Pull requests
+
+Pull-request titles use the same Conventional Commit format as commits. Complete
+the template with:
+
+- reader outcome and explicit non-goals;
+- source lock and material source anchors;
+- evidence-class or discrepancy notes;
+- figures/labs and accessibility implications;
+- validation performed;
+- review risks, open questions, and screenshots only when visual review benefits.
+
+CI validates the pull-request title, required body sections, and every commit
+subject. The protected `main` branch normally requires an approving review,
+passing checks, and resolved conversations. New commits dismiss stale approval.
+For owner-authored work, `BenAlaa` may use the ruleset's **pull-request-only**
+bypass; this waives the impossible self-approval requirement but still requires a
+PR and records the bypass. Direct pushes are not the maintenance workflow. Do not
+force-push after review begins unless rewriting is necessary and reviewers are
+warned.
+
+## Review rubric
+
+Reviewers evaluate, in this order:
+
+1. factual correctness at the source lock;
+2. claim classification and source sufficiency;
+3. causal learning order and vocabulary consistency;
+4. failure, security, platform, and provider nuances;
+5. interaction/accessibility/print behavior;
+6. maintainability, tests, and bundle impact;
+7. prose clarity and visual polish.
+
+A chapter should not reach `verified` merely because it builds.
+
+## Updating the T3 Code source lock
+
+Source refreshes are dedicated milestone pull requests. They must:
+
+1. update the full commit and capture date;
+2. regenerate every excerpt;
+3. run the inventory and source-drift checks;
+4. identify changed claims, capabilities, migrations, and distributions;
+5. update discrepancies and roadmap classifications;
+6. leave unrelated editorial rewrites for later pull requests.
+
+## Getting help
+
+Open a focused discussion or issue with the relevant source paths and the question
+you are trying to settle. If evidence conflicts, show both paths rather than
+guessing which one is authoritative.
diff --git a/README.md b/README.md
index 89dbb9d..7fab792 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,267 @@
-# t3code-decoded
-A source-grounded, interactive architecture book for T3 Code.
+
+
+
+ # T3 Code Decoded
+
+ **A source-grounded, interactive guide to the architecture and implementation of T3 Code.**
+
+ [Read the book](#read-the-book) · [Explore the plan](./BOOK_PLAN.md) ·
+ [Contribute](./CONTRIBUTING.md) · [Source policy](#source-grounding)
+
+
+> [!IMPORTANT]
+> This is an independent, unofficial study guide. It is not maintained or endorsed
+> by Ping Labs or the T3 Code maintainers. Product behavior is described at one
+> pinned source revision and may change upstream.
+
+Created and maintained by [Ahmed Alaa (`@BenAlaa`)](https://github.com/BenAlaa).
+
+> [!NOTE]
+> The public Pages site is deployed from protected `main`. It reflects the latest
+> merged revision; topic branches remain local or unmerged until their reviewed
+> pull requests are ready.
+> No authored work is pushed directly to `main`.
+
+## Read the book
+
+[Read the published book](https://benalaa.github.io/t3code-decoded/). When reviewing
+an unmerged change, run that branch locally with the instructions below—the
+published site intentionally tracks protected `main`, not unpublished work.
+
+The current edition is pinned to
+[`pingdotgg/t3code@fa219001d`](https://github.com/pingdotgg/t3code/tree/fa219001dc2f14cfd9c7774c2c03c153359144be).
+Every exact excerpt in the book is generated from that revision, checksum-verified,
+and linked back to immutable GitHub source lines.
+
+## What this book explains
+
+T3 Code is not the reasoning engine inside Codex, Claude, Cursor, Grok, or
+OpenCode. It is the server-authoritative control plane around them: it normalizes
+intent, durably records product state, starts and supervises provider runtimes,
+projects ordered state to several clients, and surrounds the conversation with
+worktrees, Git checkpoints, terminals, files, previews, remote access, usage, and
+distribution machinery.
+
+```text
+Web · Desktop · Mobile
+ │ typed, authenticated Effect RPC
+ ▼
+T3 server: command → event → projection → reactor
+ │ │
+ │ └─ files · Git · terminal · tunnel
+ ▼
+ProviderAdapter
+ │ native protocol
+ ▼
+Codex · Claude · Cursor · Grok · OpenCode
+```
+
+The book follows that causal path instead of mirroring repository folders. Its
+eight parts cover:
+
+1. ownership boundaries, vocabulary, repository topology, and runtime shapes;
+2. CLI/server boot, Effect RPC, pairing, authorization, subscriptions, and resume;
+3. commands, events, receipts, projections, reactors, SQLite, and recovery;
+4. the provider adapter contract, all five harness integrations, and both usage
+ systems;
+5. projects, worktrees, turns, permissions, plans, tasks, context, memory,
+ checkpoints, terminals, files, VCS, MCP, preview, and pull requests;
+6. the shared client runtime plus web, Electron, and React Native clients;
+7. direct/relay/Tailscale/SSH access, reconnection, packaging, releases, updates, and telemetry;
+8. complete end-to-end traces, architectural trade-offs, limitations, and
+ evidence-bounded roadmap analysis.
+
+See [BOOK_PLAN.md](./BOOK_PLAN.md) for the complete 40-chapter specification,
+figure/lab inventory, review gates, and definition of done.
+
+## Why another set of docs?
+
+The upstream documentation is primarily a product and operations guide. This book
+answers a different class of questions:
+
+- Where is the transaction boundary?
+- What does a command receipt actually prove?
+- Which state survives a server restart?
+- How do five provider protocols become one product vocabulary?
+- Why does reconnect logic live above a one-attempt RPC session?
+- What is shared across clients, and what deliberately differs?
+- Which remote component allocates credentials, and where does application traffic
+ really flow?
+- Which capabilities ship today, exist only in builders/contracts, or are explicit
+ future work?
+
+The explanations include failure windows and discrepancies where prose, comments,
+contracts, code, and release automation do not agree.
+
+## Source grounding
+
+The repository treats evidence as build input rather than editorial decoration.
+
+| File | Role |
+| --- | --- |
+| [`sources/t3code.lock.json`](./sources/t3code.lock.json) | Immutable upstream revision and inventory |
+| [`sources/excerpts.manifest.json`](./sources/excerpts.manifest.json) | Exact source ranges selected by authors |
+| [`sources/references.manifest.json`](./sources/references.manifest.json) | Pinned file, directory, test, workflow, and documentation anchors |
+| [`src/generated/excerpts.json`](./src/generated/excerpts.json) | Generated code, line numbers, and SHA-256 checksums |
+| [`scripts/sync-source-excerpts.mjs`](./scripts/sync-source-excerpts.mjs) | Extraction and source-revision guard |
+| [`scripts/validate-sources.mjs`](./scripts/validate-sources.mjs) | Manifest/generated-integrity validation |
+
+Claims use four explicit classes:
+
+- **Verified behavior** — backed by executable code, tests, schema, migration, or
+ workflow at the source lock.
+- **Documented intent** — backed by a pinned upstream document.
+- **Inference** — a named interpretation with every input cited.
+- **Future / proposed** — explicitly unshipped; never presented as current behavior.
+
+Mechanisms are also classified by durability: transactional, durable but
+eventually reconciled, provider-owned and resumable, ephemeral, or best effort.
+This matters especially for post-commit reactors, live agent state, checkpoints,
+and provider rollback.
+
+## Interactive reading experience
+
+The book is a static Astro site with focused interactivity rather than a client-side
+application shell:
+
+- clean, linkable chapter and heading routes;
+- typed MDX content and one authoritative content manifest;
+- full-text Pagefind search with a development fallback;
+- light, dark, and system themes;
+- keyboard-accessible navigation and simulations;
+- source cards with exact line numbers, checksums, copy, and immutable permalinks;
+- lazy diagrams with mouse/trackpad zoom, two-axis pan, drag, and expanded focus mode;
+- route-local, keyboard-operable labs with static and print fallbacks;
+- responsive layouts, reduced-motion behavior, and print styles;
+- base-path-aware URLs for GitHub Pages.
+
+The supplied book cover is reused directly; the site does not generate or alter it.
+
+## Repository layout
+
+```text
+.
+├── src/content/book/ # ordered MDX chapters
+├── src/components/ # figures, source cards, and interactive labs
+├── src/layouts/ # book shell and metadata
+├── src/styles/ # tokens, reading layout, print/responsive rules
+├── src/generated/ # checked-in exact source excerpts
+├── sources/ # upstream lock, excerpt manifest, and reference manifest
+├── scripts/ # source, content, search, and link validation
+├── tests/ # source-pipeline and book invariants
+├── .github/ # quality, Pages, issue, PR, and governance workflows
+└── BOOK_PLAN.md # editorial architecture and delivery gates
+```
+
+## Local development
+
+### Requirements
+
+- Node.js 24.10 or newer
+- npm
+- Git
+- Optional: a sibling checkout of `pingdotgg/t3code` at the pinned commit when
+ refreshing or independently verifying excerpts
+
+### Start the book
+
+```sh
+git clone https://github.com/BenAlaa/t3code-decoded.git
+cd t3code-decoded
+npm ci
+npm run dev
+```
+
+The development server prints its local URL.
+
+### Run the quality gates
+
+```sh
+npm run source:check
+npm test
+npm run build
+```
+
+`npm run build` validates content and generated evidence, creates the static Astro
+site, and builds its Pagefind index.
+
+### Verify against the upstream checkout
+
+Clone T3 Code beside this repository, or point `T3CODE_SOURCE_DIR` at an existing
+checkout:
+
+```sh
+git clone https://github.com/pingdotgg/t3code.git ../t3code
+git -C ../t3code checkout fa219001dc2f14cfd9c7774c2c03c153359144be
+npm run source:check
+```
+
+To intentionally refresh generated excerpts after editing the manifest:
+
+```sh
+npm run source:sync
+```
+
+Never refresh against a different upstream commit without updating the source lock
+as a dedicated, reviewed change.
+
+## Contributing
+
+Contributions are welcome for source corrections, clearer explanations, diagrams,
+simulations, tests, accessibility, and new evidence discovered at the pinned
+revision. Start with [CONTRIBUTING.md](./CONTRIBUTING.md).
+
+The short version:
+
+1. open or find an issue for material architectural changes;
+2. branch from `main`—direct pushes are protected;
+3. make focused commits using [Conventional Commits](https://www.conventionalcommits.org/);
+4. keep excerpts generated and every material claim source-linked;
+5. run `npm test` and `npm run build`;
+6. open a pull request with a Conventional Commit title and the repository template;
+7. resolve review conversations and obtain the required approval before merge;
+ the owner may use the audited pull-request-only bypass for a solo-maintained PR.
+
+Chapter corrections should not be bundled with unrelated engine refactors. A book
+part may be one review milestone, but its chapters should remain fine-grained
+commits so reviewers can inspect the evidence and narrative separately.
+
+## Branch and release policy
+
+`main` is the only default line—there is no duplicate `master` branch. It is
+protected by pull-request review, passing book checks, resolved review
+conversations, and force-push/deletion restrictions.
+
+`BenAlaa` is the sole owner and initial reviewer. The ruleset grants that account
+**pull-request-only** bypass so solo maintenance remains possible without allowing
+routine direct pushes: an owner-authored change still needs a PR and leaves its
+review and bypass trail on GitHub. Contributions from everyone else require the
+configured approval.
+
+GitHub Actions deploys the exact validated `main` artifact to GitHub Pages. Build
+output is never committed to the source branch. Source updates, engine changes, and
+book-part milestones are reviewed as separate pull requests.
+
+## Attribution and trademarks
+
+T3 Code source excerpts are taken from
+[`pingdotgg/t3code`](https://github.com/pingdotgg/t3code), which is distributed
+under the MIT License. Each excerpt links to its immutable upstream revision.
+
+“T3 Code,” Ping Labs, Codex, Claude, Cursor, Grok, OpenCode, GitHub, and other
+names and marks belong to their respective owners. Their use here is descriptive.
+This project does not imply affiliation or endorsement.
+
+## License
+
+- Site engine, scripts, and original code: [MIT](./LICENSE-CODE)
+- Original book prose and diagrams: [Creative Commons Attribution 4.0](./LICENSE-CONTENT)
+- Upstream excerpts: their original MIT license and notices apply
+- Supplied cover artwork: included as a project asset but excluded from both grants
+
+See [NOTICE.md](./NOTICE.md) for the exact attribution boundary.
+
+## Security and conduct
+
+Please use the private reporting path in [SECURITY.md](./SECURITY.md) for a genuine
+security issue. Community participation follows [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
From 9d5450c43b13d523197f2694f132bd772c2c37c8 Mon Sep 17 00:00:00 2001
From: Ahmed Alaa <46384841+BenAlaa@users.noreply.github.com>
Date: Mon, 24 Aug 2026 13:27:28 +0300
Subject: [PATCH 11/12] test(book): add publication quality gates
---
tests/book-shell.test.mjs | 31 +++++
tests/built-site-validator.test.mjs | 21 +++
tests/change-metadata.test.mjs | 28 ++++
tests/diagram-explorer.test.mjs | 43 ++++++
tests/domain-core-labs.test.mjs | 121 ++++++++++++++++
tests/part-05-work-lifecycle.test.mjs | 115 ++++++++++++++++
tests/part-06-client-architectures.test.mjs | 105 ++++++++++++++
tests/part-07-reach-ship.test.mjs | 144 ++++++++++++++++++++
tests/part-08-synthesis.test.mjs | 135 ++++++++++++++++++
tests/provider-harness-labs.test.mjs | 124 +++++++++++++++++
tests/source-pipeline.test.mjs | 77 +++++++++++
11 files changed, 944 insertions(+)
create mode 100644 tests/book-shell.test.mjs
create mode 100644 tests/built-site-validator.test.mjs
create mode 100644 tests/change-metadata.test.mjs
create mode 100644 tests/diagram-explorer.test.mjs
create mode 100644 tests/domain-core-labs.test.mjs
create mode 100644 tests/part-05-work-lifecycle.test.mjs
create mode 100644 tests/part-06-client-architectures.test.mjs
create mode 100644 tests/part-07-reach-ship.test.mjs
create mode 100644 tests/part-08-synthesis.test.mjs
create mode 100644 tests/provider-harness-labs.test.mjs
create mode 100644 tests/source-pipeline.test.mjs
diff --git a/tests/book-shell.test.mjs b/tests/book-shell.test.mjs
new file mode 100644
index 0000000..02348b8
--- /dev/null
+++ b/tests/book-shell.test.mjs
@@ -0,0 +1,31 @@
+import assert from "node:assert/strict";
+import { readFile } from "node:fs/promises";
+import test from "node:test";
+
+const bookLayout = await readFile(new URL("../src/layouts/BookLayout.astro", import.meta.url), "utf8");
+const baseLayout = await readFile(new URL("../src/layouts/BaseLayout.astro", import.meta.url), "utf8");
+const styles = await readFile(new URL("../src/styles/global.css", import.meta.url), "utf8");
+
+test("book shell keeps chapter navigation in a reserved fixed dock", () => {
+ assert.match(bookLayout, /