# Project layout (/docs/project-layout)



Pleach is a library, not a framework. There's no `pleach.config.ts`,
no `defineConfig`, no required directory. The runtime is a class
you construct; adapters are constructor arguments; storage paths
live in whichever adapter you wire.

This page exists because that freedom is unhelpful on day one. So:
what's actually load-bearing, what a layout looks like when you
stop thinking about it, and what each [use case](#descend-into-a-use-case)
adds on top.

## What the runtime actually requires [#what-the-runtime-actually-requires]

Three things. Everything else is convention.

| Requirement                                   | Where it lives                                      | Page                                                                |
| --------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------- |
| Exactly one `SessionRuntime` per host process | A module you write — typically exports the instance | [SessionRuntime](/docs/session-runtime)                             |
| A `ProviderDecisionLedger` you've picked      | Passed into the `SessionRuntime` constructor        | [Audit ledger](/docs/audit-ledger)                                  |
| A host adapter, if you serve HTTP             | A route handler in your web framework               | [Host adapter](/docs/host-adapter) · [API routes](/docs/api-routes) |

The ledger choice is the one that catches people. The default is
`MemoryProviderDecisionLedger` — fine for tests, lost on restart.
`NoopProviderDecisionLedger` writes nowhere; pick it deliberately,
not by accident. Anything durable is an adapter you wire.

## A layout that works [#a-layout-that-works]

A Next.js App Router app with one agent, one tool, and an HTTP
route. Move any of these — the runtime resolves whatever your
imports resolve.

```
my-app/
  src/
    pleach/
      runtime.ts             # constructs and exports `runtime`
      tools/
        search.ts            # → /docs/tools
      prompts/
        triage.ts            # → /docs/prompt-builder
    app/
      api/
        agents/[id]/route.ts # → /docs/api-routes
      page.tsx               # → /docs/react
  package.json
```

The split is convention, not contract:

* **`src/pleach/runtime.ts`** — the single place `new SessionRuntime(...)`
  is called. Exports the instance so the host route and any
  background worker import the same one.
* **`src/pleach/tools/`** — each file exports a `defineTool(...)`
  call. The runtime doesn't scan this directory; you import the
  tools where you register them.
* **`src/pleach/prompts/`** — plain modules that build prompt
  objects. The runtime doesn't scan this directory either.
* **`src/app/api/agents/[id]/route.ts`** — exports
  `createPleachRoute()` from `@pleach/core/quickstart`. This is the
  shipped host adapter the runtime expects when it's behind HTTP.

Read the inverse to internalize the model: nothing the runtime
does depends on directory names. Rename `pleach/` to `agents/`,
flatten everything into `src/`, split per-feature — the runtime
boots the same. The only thing it sees is the `SessionRuntime`
you handed it.

A common second instinct: "where do checkpoints and the audit
database live on disk?" Wherever the adapter you wired writes
them. `MemoryProviderDecisionLedger` writes nowhere; the
[SQLite recipe](/docs/recipes#3-custom-storage-adapter-sqlite)
writes to a path you pass in; a managed-DB adapter writes to a
connection string. Pleach doesn't pick the path because Pleach
doesn't know whether you're on one machine, a Lambda, or a
multi-region cluster.

## Descend into a use case [#descend-into-a-use-case]

Each shape adds a small, predictable delta to the baseline above.
Open the page for the shape closest to yours; the layout grows in
one of a few directions, not many.

<Cards>
  <Card title="Customer support" href="/docs/customer-support-agent" description="Baseline layout fits. Add a tool module per integration; the ledger row is what your QA team reads." />

  <Card title="Research" href="/docs/research-agent" description="Adds a subagent registry and a deterministic replay path so 'why did it cite that?' is one command, not a log dive." />

  <Card title="Coding agent" href="/docs/coding-agent" description="Adds a sandboxed tool surface and a checkpoint-per-edit pattern. Survives restarts means a durable checkpoint adapter, not the in-memory default." />

  <Card title="Internal knowledge" href="/docs/internal-knowledge-agent" description="Adds an index/embeddings module the tools call into, plus scrubbers on the audit-ledger plug-points." />

  <Card title="Multi-tenant SaaS" href="/docs/multi-tenant-saas-agent" description="Build the runtime per request from the tenant resolver. The tenant facet keys the cache, ledger, and OTel attributes." />

  <Card title="Regulated domain" href="/docs/regulated-domain-agent" description="Adds @pleach/compliance — tamper-evident hash chain on the ledger, PII scrubbers on the row, GDPR soft-delete on the request." />
</Cards>

## Filesystem-touching recipes [#filesystem-touching-recipes]

Recipes are the small, copy-pasteable pieces. Three touch the
project's on-disk shape directly:

| Recipe                                                                                           | What it adds                                                                                                                |
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| [Next.js App Router chat with streaming](/docs/recipes#1-nextjs-app-router-chat-with-streaming)  | The `app/api/agents/[id]/route.ts` + client component pair the baseline assumes.                                            |
| [Custom storage adapter: SQLite](/docs/recipes#3-custom-storage-adapter-sqlite)                  | A durable adapter that picks a real on-disk path for the audit ledger. The first thing past `MemoryProviderDecisionLedger`. |
| [Building a custom event-log projection](/docs/recipes#8-building-a-custom-event-log-projection) | Where projection state lives — typically alongside the ledger, in whatever store the ledger writes to.                      |

## What Pleach won't tell you to do [#what-pleach-wont-tell-you-to-do]

A short list because each of these is a deliberate non-choice.

* **No config file.** The runtime is wired in code so the
  TypeScript compiler sees every adapter you pass and your IDE
  jumps to the definition. A `pleach.config.ts` would buy
  convention at the cost of static analysis.
* **No reserved directory.** Nothing called `.pleach/` is
  created by the runtime. Storage paths live in whichever
  adapter you wire — the deployment shape (one machine vs.
  Lambda vs. managed DB) decides the path, not the library.
* **No `agents/` folder convention.** The `agents/<specName>/`
  prefix you'll see in [Agents](/docs/agents) is an internal
  *channel-path* prefix the runtime uses to namespace agent
  state. It's a string in the runtime's address space, not a
  directory on your disk.
* **No required test layout.** [Eval and replay](/docs/eval-and-replay)
  reads recorded sessions, not test files. Put tests wherever
  your test runner finds them.

If a piece of structure isn't on this page, the runtime doesn't
have an opinion on it — and that's the contract.
