# Decisions (/docs/decisions)



Pleach ships a handful of close-cousin primitives that often
collapse into one in a reader's head. Different branches of the
same lattice — looking similar from a distance, doing different
work at the joint. This page is the catalog of "when to reach
for which" — short answers with links to the deeper page on
each side.

If your question is "do I need this SKU at all?" — see
[Which SKU do I need?](/docs/which-sku). If it's "Pleach vs.
another framework" — see [Comparison](/docs/comparison). This
page is for choices *inside* Pleach.

## Session vs Turn vs Subagent [#session-vs-turn-vs-subagent]

The three substrate-grain levels. They nest:
**Session → Turn → Subagent**.

| Concept      | Lifetime                   | What it represents                                                                                                | Reach for                                                                                          |
| ------------ | -------------------------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Session**  | Long-lived (days / months) | One user × one product surface. Holds channels, storage, family-lock, the event log.                              | Persisting conversation state across pages, processes, and clients. Per-user resource attribution. |
| **Turn**     | One round-trip             | One user-message-in / one-answer-out cycle. Walks the four stages.                                                | Auditing what one user message cost. The grain of the `AuditableCall` row.                         |
| **Subagent** | Inside a turn              | A specialist sub-call spawned by the root turn — research / verify / refactor / etc. Rolls up to parent `turnId`. | Decomposing one user message into parallel specialist tasks; bounded fan-out with cost ceiling.    |

A real example: a user types "summarize this PDF and email me
the key risks." That's **1 session, 1 turn, N subagents** (one
to read the PDF, one to extract risks, one to draft the email).
All N subagent rows roll up to the same `turnId`.

**Read:** [SessionRuntime](/docs/session-runtime) ·
[Turn lifecycle](/docs/turn-lifecycle) ·
[Subagents](/docs/subagents).

## Ledger vs Observability vs Eval [#ledger-vs-observability-vs-eval]

Three observability planes that look identical from a distance.

| Plane                      | Grain                                                     | Latency        | Reach for                                                                                                        |
| -------------------------- | --------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------- |
| **`AuditableCall` ledger** | One row per LLM call, keyed by `turnId`                   | Write-time     | Per-tenant cost rollup, compliance review, regulator-shaped queries. Joinable to your billing in one `GROUP BY`. |
| **OTel spans**             | One span per stage / tool / call, with parent threading   | Sampled, async | Distributed tracing, latency profiling, error correlation across services. Pipes to Honeycomb, Tempo, Datadog.   |
| **Eval / replay**          | Fixture-driven regression run against the recorded ledger | Offline        | "Does this change regress against my golden corpus?" CI-runnable scoring with Welch t-test.                      |

You usually want **all three**. The ledger is structural (every
turn writes); OTel is sampled (only some make it to your
backend); evals are explicit (you run them in CI). They don't
replace each other.

**Read:** [Audit ledger](/docs/audit-ledger) ·
[OTel observability](/docs/otel-observability) ·
[Eval](/docs/eval) · [Replay](/docs/replay).

## Scrubber vs ComplianceRuntime [#scrubber-vs-complianceruntime]

Both fire on the path from runtime to event log. They cover
different shapes.

| Primitive               | Where it lives                                                                | What it transforms                                                                                                              | Reach for                                                                                                                                                        |
| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`Scrubber`**          | `@pleach/core/scrubbers` contract; concrete instances in `@pleach/compliance` | Field-level: regex / Luhn / dictionary scrub on a string value (SSN, CC, driver license, custom keyed regex)                    | Surgical PII / PHI removal at the field level. Composable into bundles per regulatory profile.                                                                   |
| **`ComplianceRuntime`** | `@pleach/compliance-contract` (interface) + `@pleach/compliance` (impl)       | Whole-profile: assembles the right scrubber bundle for `HIPAA` / `GDPR` / `PCI-DSS` / `SOC2` and wires it into `EventLogWriter` | Profile-driven compliance posture — pass `{ profile: "HIPAA" }` and get the canonical scrubber set + attestation hooks without enumerating individual scrubbers. |

You compose `Scrubber`s when you want bespoke control. You
hand the `ComplianceRuntime` a profile when you want the
canonical answer. Both are real — neither is sugar over the
other.

**Read:** [Scrubbers](/docs/scrubbers) ·
[Compliance](/docs/compliance).

## Recipe vs direct `SessionRuntime` [#recipe-vs-direct-sessionruntime]

Same substrate, different abstraction layer.

| Path                                                                                                                                   | What you write                                                                          | What you give up                                                                        | Reach for                                                                      |
| -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| **`@pleach/recipes` factory** (`simpleChatbot`, `ragChatbot`, `compliantChatbot`, `verticalAgent`, `subagentSwarm`, `enterpriseAgent`) | One function call with a config object                                                  | Fine-grained channel registration, custom interrupt managers, novel plugin compositions | Use-case-shaped agents that match a known pattern. Under 100 lines of glue.    |
| **`new SessionRuntime(...)`**                                                                                                          | Full constructor + channel registry + plugin set + interrupt manager + extension loader | Boilerplate; choice paralysis on what to actually wire                                  | Novel agent shapes, multi-tenant SaaS, regulated deployment, custom transport. |

The recipes are the same `SessionRuntime` underneath — they're
not a separate runtime. You can always reach into
`recipe.runtime` for direct access. Start with the recipe;
graduate to the constructor when you outgrow it.

**Read:** [Recipes](/docs/recipes) ·
[Runtime construction](/docs/runtime-construction).

## `AiSdkProvider` vs `AnthropicSdkProvider` vs `AgentProvider` [#aisdkprovider-vs-anthropicsdkprovider-vs-agentprovider]

Three transport-layer choices.

| Provider                       | Wraps                        | Reach for                                                                                             |
| ------------------------------ | ---------------------------- | ----------------------------------------------------------------------------------------------------- |
| **`AiSdkProvider`**            | `streamText` from `ai@6.x`   | Unified provider switching via OpenRouter or any `@ai-sdk/<vendor>` package. The recommended default. |
| **`AnthropicSdkProvider`**     | `@anthropic-ai/sdk` directly | Native Anthropic features without a wrapper — prompt caching, extended thinking, tool-use beta flags. |
| **`AgentProvider` (your own)** | Anything that streams        | OpenAI directly, a custom gateway, a local Ollama, a proprietary vendor — implement one interface.    |

Most projects pick `AiSdkProvider` with OpenRouter — one key,
six families, identical request shape. The other two exist
because some teams need them. See
[Providers](/docs/providers) for the swap pattern and config
matrix.

## Memory vs Storage vs Checkpointer [#memory-vs-storage-vs-checkpointer]

Three persistence layers that all touch the database.

| Layer                             | What it persists                                      | When it writes                                 | Reach for                                                                         |
| --------------------------------- | ----------------------------------------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------- |
| **Memory**                        | Long-term cross-session facts the agent should recall | Explicit `memory.store()` calls in your plugin | RAG-shaped recall across sessions; user preferences; learned habits.              |
| **Storage** (`StorageAdapter`)    | Per-session state — messages, channels, the event log | Every turn, automatically                      | The session as a unit. Resume across processes / clients via version-vector sync. |
| **Checkpointer** (`Checkpointer`) | Snapshots of channel state at each superstep          | Per-step within a turn                         | Time-travel debugging, replay-deterministic regression, interrupt-resume.         |

Storage is mandatory (`MemoryAdapter` for tests; `SupabaseAdapter`
for prod). Checkpointer is optional but required for time travel.
Memory is a separate concern entirely — agent-facing, not
infrastructure.

**Read:** [Memory](/docs/memory) · [Storage](/docs/storage) ·
[Checkpointing](/docs/checkpointing) ·
[Time travel](/docs/time-travel).

## Channels vs Event log vs Stream events [#channels-vs-event-log-vs-stream-events]

Three event surfaces that look related but serve different
audiences.

| Surface           | Audience                                   | Shape                                                              | Reach for                                                                                   |
| ----------------- | ------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| **Channels**      | The graph engine itself                    | Reactive write-many / read-once values inside a turn               | Wiring graph nodes; coordinating between stages within one turn. &#x2A;*Internal.**         |
| **Event log**     | Your audit / billing / compliance pipeline | Append-only typed `StreamEvent` rows in your DB                    | Per-tenant cost rollup, regulator-shaped queries, replay determinism. &#x2A;*Durable.**     |
| **Stream events** | The client (browser / mobile / CLI)        | The same `StreamEvent` discriminated union, served as NDJSON / SSE | Rendering streaming UI, driving `useChat`, building custom transports. &#x2A;*Wire-level.** |

The event log and the stream-events wire are the SAME typed
union; the difference is durability vs transport. Channels are
internal — you don't read them from outside the graph.

**Read:** [Channels](/docs/channels) ·
[Event log](/docs/event-log) ·
[Stream events](/docs/stream-events).

## Plugin contract vs Plugin bundle [#plugin-contract-vs-plugin-bundle]

A composition question.

| Shape                          | What you author                                                                   | Reach for                                                                                                                                        |
| ------------------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Single-facet plugin** (flat) | One `definePleachPlugin({ ... })` const with all hooks inline                     | Quick scaffold; one-facet plugins (just prompts, just tools, just safety).                                                                       |
| **Multi-facet bundle**         | Multiple `definePleachPlugin({ facet: "prompts" })` consts composed into an array | Bigger plugins where each facet (prompts + safety + tools) can be reviewed / tested / shipped independently. The default `pleach init` template. |

Bundles aren't required — the runtime accepts a single plugin
and N composed plugins identically. But review surface scales
better when prompts.ts and safety.ts are separate files.

**Read:** [Plugin contract](/docs/plugin-contract) ·
[Plugin bundles](/docs/plugin-bundles) ·
[Plugin authoring standards](/docs/plugin-authoring-standards).

## Coding-agent SKU vs DIY tool wiring [#coding-agent-sku-vs-diy-tool-wiring]

Pleach ships `@pleach/coding-agent` as a SKU. You also have
`@pleach/tools` + `@pleach/sandbox` to roll your own.

| Path                            | What you get                                                                                                                       | Reach for                                                                                                          |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **`@pleach/coding-agent`**      | SWE-bench-shaped runtime — `start()` / `stop()` / `executeStep()` + LRU `CodingContextManager` for file-context budget management. | A coding agent that matches the SWE-bench shape; benchmark adapters; out-of-the-box file-context budgeting.        |
| **Tools + sandbox, hand-wired** | Compose `@pleach/tools` definitions with `@pleach/sandbox` execution; full control over context management                         | Coding agents with bespoke context-management strategies, multi-language sandboxes, or non-SWE-bench-shaped flows. |

The SKU is a specific opinionated shape. The hand-wired path
gives up that opinionation for flexibility.

**Read:** [`@pleach/coding-agent`](/docs/coding-agent) ·
[Tools](/docs/tools) · [`@pleach/sandbox`](/docs/sandbox).

## Where to go next [#where-to-go-next]

<Cards>
  <Card title="Which SKU do I need?" href="/docs/which-sku" description="Decide which @pleach/* packages to install for what you're building." />

  <Card title="Comparison" href="/docs/comparison" description="How @pleach/* compares to AI SDK, LangChain, Mastra, and other harnesses." />

  <Card title="Concept clusters" href="/docs/concept-clusters" description="The full concept map of the substrate — seven cluster triplets at four depths." />

  <Card title="Glossary" href="/docs/glossary" description="Canonical terms — anchor-plan, CallClass, family-lock, seam, and the rest of the vocabulary." />
</Cards>
