# @pleach/core changelog (/docs/changelog/core)



This page collects the **site-content changelog entries** that
materially touched the [`@pleach/core`](/docs/core) docs surface or
named a `@pleach/core` symbol, subpath, or audit gate. It is not
the runtime changelog — the canonical record of `@pleach/core`
runtime behavior lives in the upstream package
[`CHANGELOG.md`](https://github.com/pleachhq/getpleach) and on
[npmjs.org/package/@pleach/core](https://www.npmjs.com/package/@pleach/core).

See the combined site changelog at
[`/docs/changelog/combined`](/docs/changelog/combined) for the
chronological unified view across every SKU.

## Unreleased [#unreleased]

### Added [#added]

* **[Comparison](/docs/comparison) gains a category for agent-memory &
  knowledge-graph substrates (a read-side category).** Pins the
  read-vs-write delineation for `@pleach/core`: memory layers and
  knowledge-graph engines (Letta, Mem0, Zep) are a *read substrate* —
  the graph exists so the agent can query it to decide; `@pleach/core`
  is a *write substrate* — the ledger exists so every provider call is
  recorded, then read to account. Shared skeleton (append-only,
  content-addressed, causal chain, fork/replay); divergent object of
  record (the agent's knowledge/decisions vs. one provider call with
  model/tokens/cost). Compose-not-compete — a memory graph reads the
  same session the [`AuditableCall` row](/docs/auditable-call-row)
  writes.

### Fixed [#fixed]

* **Unknown safety-policy ids now throw at construction — even with
  no plugins.** [Safety](/docs/safety) promises "Construction throws
  `UnknownSafetyPolicyError` if the config references an id no plugin
  registered — operator typos surface at boot, not on the first turn."
  Previously the enable/validate loop was gated behind the plugin
  block, so a plugin-less `createPleachRuntime({ enabledSafetyPolicies: ["typo"] })` silently no-op'd the enable and the typo surfaced (or
  silently didn't) only at first turn. The enable loop now runs
  unconditionally (`core` `SessionRuntime.ts`); the `register()` loop
  stays plugin-gated, so a legitimately plugin-contributed id is
  registered before the enable finds it and valid constructions are
  unaffected. The impl now matches the documented guarantee.

* **The chronological error trail
  ([`errorCodesChronological`](/docs/audit-ledger)) now captures every
  error class.** A **completed** tool call carrying a structured
  `_error_code` (its status looks successful, so it tripped neither the
  failed-status nor the schema-validation branch) and a **caught
  async-planner throw** (`AsyncPlannerHandler`, previously console-only)
  now each persist an `error.code` row. A live turn with these errors
  previously persisted zero rows; the durable trail is now complete.
  (Direct-API tool errors were already covered by the host emit path.)

* **Core-concept doc snippets reconciled with shipped signatures
  (devharness FINDING 88, 91–93).** [Determinism](/docs/determinism)
  — the ledger read used a non-existent `ledger.list({ sessionId })`;
  the real `AuditableCallQuery` read is async positional
  `await ledger.getSession(sessionId)` (`core`
  `audit/ProviderDecisionLedger.ts`), and the "distinguishes **three**
  operating contracts" line corrected to **five** (the `RuntimeMode`
  union — and the page's own table — lists five).
  [OTel observability](/docs/otel-observability) — `executeMessage`
  is positional `executeMessage(sessionId, "hello")`, not the object
  form `executeMessage({ sessionId, content })` (`core`
  `SessionRuntime.ts`). [Stream events](/docs/stream-events) — the
  LangGraph `"values"` mapping used `runtime.sessions.get()`, which
  `RuntimeSessionsFacet` (`core` `facets/sessions.ts`) does not
  expose; corrected to `runtime.sessions.resume(sessionId)`.

### Added [#added-1]

* **[Gate failures → fixes](/docs/gate-failures) — the failure-string
  lookup.** Maps each gate's verbatim, greppable failure string to its
  meaning and the fix doc — `[graph-stages] Node "<name>" missing
  stageId in NODE_STAGE_MAP`, `[audit:c8-event-type-allowlist-coverage]
  FAIL`, `[auditable-call-version] FAIL rule 3`,
  `[audit:plugin-contract-completeness] FAIL`, and others, all verified
  against `scripts/audit/`. The agent-facing close of the
  [self-extension](/docs/extending#agent-driven-self-extension) loop.

* **`AuditRecordVersion` corrected `14` → `18`** on
  [CI enforcement](/docs/ci-enforcement) to match the source
  (`export type AuditRecordVersion = 18;`) and
  [the AuditableCall row](/docs/auditable-call-row).

* **[Extending Pleach](/docs/extending) — the gate-paired authoring
  map.** Pairs each extension point with the interface, the
  registration call, and the [audit gate](/docs/audit-gates) that
  fails if the addition is wrong — [nodes](/docs/nodes)
  (`audit:graph-stages`), event
  types via `contributeEventTypes` (`audit:c8-union-member-has-producer`
  * `audit:c8-event-type-allowlist-coverage`),
    [scrubbers](/docs/scrubbers), [plugin hooks](/docs/plugin-contract)
    (`audit:plugin-contract-completeness`), prompt blocks, and a custom
    audit field via `pluginPayloads` (`audit:auditable-call` — the
    structural column set is locked). Draws the host-extension vs
    upstream-contribution line, and adds an "Agent-driven
    self-extension" section: the gates as an autonomous write →
    `audit:*` → read-failure → fix loop, plus injecting the extension
    contract into the operating agent's prompt via
    `contributeRuntimeAwarePrompts`. [Nodes](/docs/nodes),
    [channels](/docs/channels), and [scrubbers](/docs/scrubbers) gained
    a "What CI checks when you add one" footer linking into it.

* **`createPleachRoute({ demo })` keyless first-run + demo-first
  `pleach init` scaffold documented.**
  [Getting started](/docs/getting-started),
  [Quickstart](/docs/quickstart), and [Install](/docs/install) now
  lead the wizard path with a keyless demo: with no provider key,
  `createPleachRoute({ demo: process.env.NODE_ENV !== "production" })`
  drives a real graph turn over canned model text and writes real
  `AuditableCall` rows, flagged `x-pleach-mode: demo`. A resolved
  `<PROVIDER>_API_KEY` always wins; demo never engages in a
  production build. The Next.js App Router scaffold adds an
  `/audit` view (`app/audit/page.tsx` + `app/api/audit/route.ts`)
  rendering the per-model-family rollup (family · rows · tokens ·
  est. cost). The bare `createPleachRoute()` no-key contract is
  unchanged (HTTP 503).

* **`engine/` superstep scheduler named as an architecture piece +
  `facets.mdx` synced to the shipped facet inventory (pkgclean 06 D7/D9).**
  [Architecture](/docs/architecture#the-execution-graph-cluster) and
  [Concept clusters](/docs/concept-clusters#execution-graph-cluster) now
  name the `engine/` scheduler (`SuperstepRunner` + its primitives) as the
  deterministic superstep executor that *runs* the compiled graph.
  [Facets](/docs/facets) gained the two shipped `SessionRuntime`
  accessors it omitted — `runtime.observe` and `runtime.probes` — plus the
  four per-stage graph introspection facets `graph.anchor` /
  `graph.toolLoop` / `graph.synthesize` / `graph.postTurn`.

* **`@pleach/core/skills` + `@pleach/core/agents/types` are real emitted
  subpaths.** [Skills](/docs/skills) updated to import `SkillLoader`,
  `parseSkillFrontmatter`, `Skill`, and `SkillFrontmatter` from the
  `@pleach/core/skills` barrel (the `/loader`, `/parser`, `/types`
  sub-subpaths do not exist); [Agents](/docs/agents) prose corrected
  from "wildcard path" to a real emitted `@pleach/core/agents/types`
  subpath.

### Changed [#changed]

* **`@pleach/core/internal/…` subpaths retired from the public docs
  surface** (pkgclean 06 D8). [streamSingleTurn](/docs/stream-single-turn)
  and [Subpath exports](/docs/subpath-exports) no longer document
  `@pleach/core/internal/strategies/streamSingleTurn/helpers` (+ its four
  direct helper entries) or
  `@pleach/core/internal/tools/execution/registryResolvedTools` as
  `stable` public imports. The `internal/` segment marks implementation
  details, not a supported API; the copy-paste helper-barrel import is
  removed and both paths moved to a "Not a public import surface" section
  with their public alternatives — `ToolDefinition` from
  `@pleach/core/types/generic`, and `@pleach/core/tools/execution` (which
  re-exports the resolved-tools registry functions). No
  `package.json:exports` change.

* **`core.mdx` stale numbers corrected.** `auditRecordVersion` updated
  `13 → 18` (real value at `AuditableCall.ts`), and the `HarnessPlugin`
  plugin-hook count updated `~41 → 67` `contributeX` hooks (derived by
  grepping `packages/core/src/plugins/types.ts`).

### Fixed [#fixed-1]

* **More SKU doc-page snippets reconciled with the shipped signatures (devharness
  FINDING 80–82).** [Fabrication detection](/docs/fabrication-detection) — the
  custom-detector example used a non-existent `ctx.toolArgs` and returned
  `{suspect, tier}`; corrected to the real `FabricationDetectorContext` fields + the
  `FabricationFinding` shape (`{detectorId, severity, reason, evidence?}`), and the
  `tier`-routing bullet corrected (routing keys on the tool's `safetyTier`, not a
  finding's `severity`). [Multi-tenant](/docs/multi-tenant) — `getAggregateUsage` is
  3-positional (`(client, store, filters)`), has no `groupBy: "tenantId"` value
  (`session`/`agent`/`model`/`tool`/`day`/`week`), nests dates under `dateRange`, and
  requires `userId` (it's single-user scoped; tenant rollups use the raw SQL).
  [Determinism](/docs/determinism) — the fingerprint round-trip read a non-existent
  `.fingerprint` audit field (real: `fingerprintComposite`, a string) and compared a
  string to a `Fingerprint` object; corrected to a like-for-like comparison over a real
  `FingerprintInput`. These are the ctor-shape / call-arity / field-name classes the
  new `audit:doc-import-resolution` gate doesn't cover (import-resolution only).

* **Event-log read side documented as store-agnostic — `eventReader` /
  `HarnessEventReader`.** [Event-log projections](/docs/event-log-projections)
  said `runtime.events.iterate` / `.fold` read `harness_event_log` rows ordered
  by `(chat_id, sequence_number)` and became live "on the Supabase backend in
  PA-1 Phase A.2" — store-specific claims that no longer match the shipped
  surface. Corrected: both accessors delegate to a `HarnessEventReader` injected
  as the runtime's `eventReader` (the read-side counterpart to `eventLogWriter`),
  so the host casts its own store into the reader **at construction** and
  `@pleach/core` itself stays free of backing-store knowledge — Postgres,
  Supabase, SQLite/pglite, or an HTTP API back the same surface. `iterate`'s
  options gain `tenantId`; the reader is **required** to read the durable log
  (no built-in reader, no implicit store fallback — a bare runtime yields
  nothing, matching the `MemoryAdapter` path) and throws a typed error at the
  call site when absent. New **Backing the reader** section shows the one-method
  contract yielding `EventLogRow` (the shape `reconstructSessionState` and the
  shipped projections fold). [Session runtime](/docs/session-runtime) config
  table gains `eventLogWriter` + `eventReader` rows; [Storage](/docs/storage)
  grows the `ci:devharness-sql` value-prop battery from four durable properties
  to nine — adding the `@pleach/observe` `postgres()` destination, time-travel
  fork, SQL-queryable audit ledger (`harness_auditable_calls`), tamper-evident
  hash chain, and `@pleach/replay` deterministic replay over the injected reader.

* **SKU doc pages reconciled with the shipped import/construction surface (devharness
  FINDING 76–79).** Four doc-site pages drifted from the (correct) package READMEs:
  [Building a chat UI](/docs/building-chat-ui) advertised a fabricated `@pleach/react`
  chat-hook API (`useChat`/`HarnessProvider` from the root, `useInterruptDecision`,
  `useChatVercelCompat`, `sendMessage`) — corrected to the real surface
  (`useSessionRuntime`/`useSessionMessageStream`/`useInterruptUI` from `@pleach/react`;
  `useChat`→`submit` from `@pleach/core/quickstart`; `HarnessProvider` from
  `@pleach/react/legacy`/`@pleach/core/react`; `useVercelAISDKCompat` from
  `@pleach/react/adapters/vercel-ai-sdk`). [LangChain](/docs/langchain) — `LangChainProvider`
  takes a POSITIONAL model arg and wires via the top-level `provider:` slot (not
  `{ model }` / `host.strategies.provider`). [Base tools](/docs/base-tools) — the
  scratchpad is per-SESSION (chatId-scoped, not per-turn), its param is `operation`
  (not `op`), and its value is a string. [Coding agent](/docs/coding-agent) — the four
  sandbox tools register via a `contributeTools` `HarnessPlugin` in `plugins:`
  (`SessionRuntimeConfig` has no `tools`/`context` field). A new
  `packages/core/test/docImportSurface.smoke.test.mjs` regression-locks the import-path
  half.

* **Observability + gateway-cost docs corrected to the shipped exporter/cost
  surface (devharness FINDING 73–75).** `@pleach/core` ships no `@opentelemetry/*`
  dependency and never reads the global tracer provider — the documented NodeSDK +
  OTLP recipe captured zero pleach spans. [OTel observability](/docs/otel-observability)
  and [Observability](/docs/observability) now show the real path: a host-supplied
  `OtelExporter` bridge injected via `new SessionRuntime({ otelExporter })` (default
  is `NoopOtelExporter`, which drops spans). `runtime.spans.snapshot()` returns
  `{ inFlightCount, isShutdown }` (exporter state) — to inspect emitted spans you wire
  a `CapturingOtelExporter` and read `.captured` (real fields `startTimeNs`/`endTimeNs`/
  `parentId`). And [Gateway cost events](/docs/gateway/cost-events) + the gateway OTel
  wiring: `computeCost` was reading `tenantId`/`family`/`callClass`/`byokActive`/
  `routingDecision` off `RouteChatCompletionOutput`, which carries none of them
  (`asTenantId(undefined)` threw → zero CostEvents) — the example now reads only real
  output fields + host-supplied observation context, and flags that `routeChatCompletion`
  is currently a slice-2 stub (`costUsd: 0`).

* **Subagents + async-tasks docs reconciled with the host-completion-required
  reality (devharness FINDING 66–72).** Bare `@pleach/core`'s subagent surface is
  host-completion-required (the host supplies `subagentExecutor` / drives
  `SubagentManager`); the docs presented it as substrate-delivered. Corrected in
  [Subagents](/docs/subagents): removed the fabricated `spawnsSubagent: true`
  auto-escalation flag; `LightweightRuntime` → the real `LightweightSessionRuntime`
  imported from `@pleach/core/subagents` (not root) with its real constructor;
  `fetchWorkspaceContext`/`formatWorkspaceContextPrompt` moved to the
  `@pleach/core/subagents` subpath; the concurrency model corrected (the runtime
  spawn path caps on the hard `SUBAGENT_LIMITS` constant and FAILS over-cap rather
  than queueing — the `SubagentManager` queue/DAG is host-driven); `context.variables`
  noted as currently inert; plus a host-completion caveat. And
  [Async tasks](/docs/async-tasks): the async-task tool-loop bridge is not wired in
  bare core (`ASYNC_TASK_BRIDGE_NOT_WIRED`), and the `"subagent"`/`"sandbox_exec"`
  task executors are host/plugin-supplied (the in-tree `SubagentTaskExecutor` is
  server-only), not "in-tree defaults".

* **Abort + resume docs synced to the shipped runtime behavior (devharness
  FINDING 56 + 62).** Aborting an in-flight turn now records
  `outcome.status: "user-aborted"` (not `"aborted"` / not lumped into
  `provider-error`) at BOTH the tool-loop decision call and the synthesis
  terminal, so cost rollups + compliance separate caller cancellation from
  provider failure — corrected across
  [session lifecycle](/docs/session-lifecycle#aborting-an-in-flight-turn),
  [SessionRuntime](/docs/session-runtime), [turn lifecycle](/docs/turn-lifecycle),
  and [API routes](/docs/api-routes). And
  [resuming a session](/docs/session-lifecycle#resuming-a-session) now describes
  the real three-layer rebuild: storage row → version-guarded checkpoint overlay
  (the crash-recovery path) → server-only event-log hydration of the *ephemeral
  card-lifecycle* state (interrupts/subagents/exports/user cards) onto a transient
  `Session.hydratedHarnessState` field — correcting the prior claim that
  `hydrateFromEvents` rebuilt messages/channel state.

* **Model-resolution-matrix path corrected — it is host-supplied, not a
  `@pleach/core` dir (pkgclean 06 D7).** The docs cited the family-lock
  matrix at the fictional `src/graph/modelfamily/`
  ([family-lock](/docs/family-lock)) and
  `@pleach/core/ai/modelFamily/matrix.ts` ([quickstart](/docs/quickstart),
  [provider detection](/docs/quickstart/provider-detection)) — neither
  path exists in the package. The `(family × callClass)` matrix table is
  host-supplied and reached through the `AgentAdapter.resolveModel<C>()`
  seam; the `ProviderFamily` set is now pointed at the real
  `@pleach/core/modelfamily` export. The routing-decision seam's
  Ivy-internal host path was removed from
  [routing decisions](/docs/plugins/routing-decisions).

* **Docs reconciled with the shipped `@pleach/core` runtime (value-prop-vs-impl
  audit, devharness FINDING 56–64).** Corrected doc claims that diverged from the
  implementation:
  * [Time travel](/docs/time-travel) — `fork` sets `parent_id: source.id` (not `null`).
  * [Checkpointing](/docs/checkpointing) — checkpoints write at message/event
    boundaries (discriminated by `CheckpointMetadata.source`), not stamped with a
    per-stage `stageId`; `SupabaseSaver` durability is `"replicated"`; rollback
    writes `source: "manual"` + `writes: ["rollback"]`; the `fork` snippet takes a
    string `newSessionId`.
  * [Session lifecycle](/docs/session-lifecycle) — the checkpoint snapshot is
    authoritative on the live restore path; the event-log canonical reader is a
    flag-gated shadow today.
  * [Sync](/docs/sync) — interactive conflict resolution (`runtime.resolveConflict`,
    the `sync.conflict` event, the 3xxx code range) is enterprise-tier/planned;
    open-core uses `SyncCoordinator` last-writer-wins-on-push with a free-text
    `lastError`.
  * [Query](/docs/query) — dropped the non-existent `queryAuditableCalls`; corrected
    `getChatUsage` / `lookupModelCost` / `getSessionReview` / `getInterruptChain`
    signatures + return shapes to the real `@pleach/core/query` surface.
  * [Memory](/docs/memory) — the production extraction path is the graph node
    (`createMemoryExtractionNode`), not the `memoryExtractionHook` → queue pipeline;
    `LearnedFact.source` is a provenance string (+`createdAt`/`lastConfirmedAt`) on
    extractor facts, with the object form documented alongside.
  * [Lineage](/docs/lineage) — opt-in via `config.lineageTracker`; the runtime
    records the session node + a `branched_from` fork edge, the other relations are
    host-recorded; `completeSession` is host-called.

### Added [#added-2]

* **Opt-in tool-event preview enrichment for the manifest projection.** New
  `runtimeQuirks.enrichToolEventPreviewsForReplay` flag (default **OFF**) lets the
  host write compact `args_preview` (`tool.started`) + `output_preview`
  (`tool.completed`) onto tool events; `chatManifestProjection` folds them into
  richer manifest `params`/`output`, closing the projection's intentional
  lossiness vs. the live provider. The previews are scrub-allowlisted (the C8
  redaction chain processes them). OFF by default preserves the
  `arguments_hash`-only privacy posture. This makes the projection
  canonical-*capable*; the projection-canonical flip itself stays gated. See
  [Replay](/docs/replay).
* **TimeTravel surfaces the chat manifest (`StateSnapshot.manifest`).**
  `getState` / `getStateHistory` / `fork` / `bulkUpdate` now carry the
  chat-manifest ledger AS-OF a checkpoint, for **inspection** (it does not feed
  the next generation — that runs through `rollbackToCheckpoint` / `resumeSession`
  into the live provider). The snapshot rides the checkpoint `metadata`
  (round-tripped via `SupabaseSaver.metadata_extra`; Memory/IndexedDB ride free),
  mirroring the `pendingTasks` pattern. See [Time travel](/docs/time-travel).
* **`ProviderFamilyExhaustedError` promoted to the top-level `@pleach/core`
  surface.** The provider-family cascade terminal (raised when every in-family
  rung is exhausted) + its `isProviderFamilyExhaustedError` guard are now public
  on `@pleach/core` — previously reachable only via the deep
  `@pleach/core/graph/seams/providerExhaustion` path. Catch + narrow it from the
  top-level import.
* **`@pleach/core/cache` — content-hash deriver.** `deriveContentHashKey`,
  `canonicalizeKeyInput`, and `ContentHashKeyInput` now have their canonical
  home in `@pleach/core/cache` (relocated from `@pleach/replay/cache`, which
  re-exports them for back-compat). The move lets `@pleach/core` derive +
  stamp a `cacheKey` into `tool.completed` / `turn.completed` events without
  the forbidden `core → replay` import. See [Subpath exports](/docs/subpath-exports).
* **`@pleach/core/manifest` — reload-durability + the manifest as a
  projection.** The chat-manifest ledger (the `[Session Manifest]`
  prompt block) now survives a process reload: `SessionRuntime.saveSession`
  stamps the live snapshot into the persisted session state and
  `resumeSession` rehydrates it (complementing the existing checkpoint
  rollback restore). New `chatManifestProjection` / `foldChatManifest`
  fold the manifest deterministically from the event log
  (`tool.*` / `job.*` / `model.upgraded`), replayable via
  `runtime.events.fold` and the `@pleach/replay` stepper — intentionally
  lossy vs. the live provider (the log carries hashes/sizes, not full
  params/output), measured by a flag-gated
  `[UXParity:manifest-projection-divergence]` shadow probe. See
  [Subpath exports](/docs/subpath-exports).
* **`tool.completed` / `turn.completed` — optional `cacheKey` payload
  slot.** Lets a deterministic replay consult the provider cache on the
  row (consumed by `@pleach/replay`'s `ReplayHandle`). Host- /
  `createCacheMiddleware`-populated; core does not derive it (the
  `core → replay` boundary forbids importing `deriveContentHashKey`).
  See [Replay](/docs/replay).

### Added [#added-3]

* **[CI enforcement of the audit contract](/docs/ci-enforcement)** —
  new Reference page indexing the [audit-gate
  catalog](/docs/audit-gates) by the auditability and replayability
  clause each gate enforces. Covers the `@pleach/core` event-log
  gates (`audit:auditable-call`, `audit:auditable-call-soak`,
  `audit:event-log-manifest-hash-completeness`, the three
  `audit:config-manifest-*` gates, `audit:c8-event-type-allowlist-coverage`,
  `audit:c9-hash-chain-integrity`) and the CI jobs that carry them
  (`graphnoderef`, `local-clone`, per-SKU `consumer-rehearsal`).
  Cross-linked from [Audit gates](/docs/audit-gates).
* **[Contribution namespaces](/docs/plugins/namespaces)** — new page
  documenting the nine namespaces every `HarnessPlugin` contribution
  hook resolves to (`prompts`, `stream`, `safety`, `audit`, `intent`,
  `tools`, `middleware`, `policy`, `lifecycle`) — the canonical,
  `audit:plugin-hook-category-assigned`-gated contract layout, with
  representative hooks per namespace and the rolling-out typed
  namespaced authoring form. [Plugin contract](/docs/plugin-contract)
  gains a cross-link; [Authoring a HarnessPlugin](/docs/plugins/authoring)
  reframes its informal 12-bucket grouping as the scan view of these
  nine canonical namespaces.
* **[Config manifest](/docs/config-manifest)** — new page documenting
  the content-addressable substrate snapshot the runtime writes to
  `harness_config_manifest`: one row per distinct
  plugin/prompt/node/channel/filter set, keyed by a SHA-256 Merkle
  roll-up (`pleach.manifest.v1` prefix, `0x1F` separator, mirroring
  the [hash chain](/docs/hash-chain)'s `pleach.c9.v1`
  canonicalization). Per-surface child hashes, the nullable
  `manifest_hash` foreign reference on `harness_event_log`,
  reference-counted retention (default forever), full-snapshot
  storage, and the canonical replay/audit query shapes. Marked
  `in-flight` — table and event-log column land as additive schema
  files during rollout.
* **[What Pleach writes to your database](/docs/emitted-data)** —
  new page mapping every `@pleach/core` table written to a buyer's
  database, what's default versus opt-in, and the destination-is-yours
  posture (no phone-home).
* **`manifest_hash` cross-references** added to
  [Event log](/docs/event-log) (stamping section + `EventLogRow`
  field) and [Schema](/docs/schema) (rolling-out
  `harness_config_manifest` section).
* **[OrchestratorClient](/docs/orchestrator-client)** — new
  top-level page documenting the per-turn handle the runtime
  threads through the stream body. Lifted from app-side into the
  substrate; canonical import path is
  `@pleach/core/runtime/orchestratorClient`. Covers the six
  user-facing facets (`config`, `history`, `context`, `tools`,
  `model`, `prompts`) and the `_internal.graph` snapshot consumer
  code must not touch.
* **[streamSingleTurn](/docs/stream-single-turn)** — new
  top-level page documenting the canonical per-turn body. Lifted
  into `@pleach/core/strategies/streamSingleTurn/` with a stable
  helpers barrel re-exporting `TurnAccumulator`, `ToolDefinition`,
  `OrchestratorMessage`, and `ProviderFallbackConfig`.
* **[Subpath exports](/docs/subpath-exports)** — adds two rows
  to the Runtime + composition table:
  `@pleach/core/runtime/orchestratorClient` and
  `@pleach/core/strategies/streamSingleTurn`. Both are now named
  subpath exports rather than wildcard-only reachable.
* **[Subpath exports → `@pleach/core/quickstart`](/docs/subpath-exports)** —
  added under the Wire + tooling cluster. Lists the five LANDED
  deliverables — `createPleachRoute` (fetch-handler factory),
  `useChat` (Pleach-native React hook), `<ChatBox />` (unstyled,
  accessible), `defaultPlugin` (empty baseline),
  `providerDetection` helpers + constants — plus the
  benchmark-adjacent `createBenchmarkPlugin`.
* **[Attestation](/docs/attestation)** — new top-level page on
  the Ed25519-signed envelope substrate that sits on top of the
  [hash chain](/docs/hash-chain). Documents the
  `@pleach/core/attestation` subpath (signer, verifier,
  key-store interface), the two stub production adapters (AWS
  KMS, Vault Transit), and the file-backed
  `attestation/testing` adapter.
* **[Architecture § Boundary rules](/docs/architecture#10-boundary-rules)** —
  adds a sixth row to the gate table:
  `audit:domain-string-purity` forbids domain-specific literals
  in `packages/core/src/**` across five pattern families. Pairs
  with the existing `lint:harness-boundary` row, which forbids
  domain *imports* — the new gate covers domain *strings*.
* **[Core](/docs/core) "What you get"** — new bullet
  "Composes under an existing Enterprise contract" after the
  AuditableCall ledger bullet.

### Changed [#changed-1]

* **Family-lock page documents the host-internal planner carve-out.**
  [`family-lock.mdx`](/docs/family-lock) gains a "Host-internal planner
  calls" section. The lock governs seam calls — `synthesize` /
  `converse` / `reasoning` / `utility` on the provider seam — but a
  host's [`anchor-plan`](/docs/plans) planner cold-start can run on a
  pinned host-configured model that never consults the
  `(family x callClass)` matrix, so it sits outside the lock by
  construction, not as a leak. The row is identifiable by
  `stage: "anchor-plan"`, `callClass: "utility"`, `payload.kind:
  "planGeneration"` and carries no `fallbackStep` / `family-exhausted`
  / `providerCascade`. Guidance: alert on any *other* non-locked-family
  row; allow this planner row.
* **The tool-loop decision call is `utility`-class on its own seam,
  not the synthesize seam (singleton-per-call-class, D-72).** Five
  pages carried the prior claim that the synthesizer node and the
  tool-loop `llmDecision` node share one `ProviderSeam<"synthesize">`
  identity (the old "D-35" statement). The tool-loop continuation
  decision — "call another tool or finish?" — now runs on a dedicated
  `utility` seam, so the synthesize cap is structural: the synthesizer
  (and the recovery path) are the only consumers of the synthesize
  seam, and the decision node can't reach the `TurnSynthesizeCounter`.
  Corrected in [Seams](/docs/seams#the-singleton-synthesize-seam) (the
  singleton section + the LangGraph callout's `ProviderSeam<"utility">`
  type), [Graph](/docs/graph#singleton-synthesize-seam),
  [Graph node catalog](/docs/graph-node-catalog) (the `llmDecision`
  row's `acceptsSeam` `converse`→`utility`, the Stage 2 intro, and the
  singleton-invariant wire-check), [Call classes](/docs/call-classes)
  (the `utility` row + section now name the tool-loop decision as the
  canonical utility call), and
  [Multi-synthesize](/docs/coding-agent/multi-synthesize). A reader on
  the old "shared seam" model would have mis-predicted which node bumps
  the synthesize counter.
* **Graph lattice docs synced to the current `@pleach/core`
  topology across eight pages.** The `NODE_STAGE_MAP` registry is
  **43** names, not 48. `ALLOWED_EDGE_PATTERNS` carries **nine**
  rows and `FORBIDDEN_EDGE_PATTERNS` **seven**, not eight/eight —
  the `tool-loop → post-turn` recovery-dispatch edge moved from the
  forbidden table into the allowed table. Recovery shaping is no
  longer four `synthesize`-stage graph nodes (`recovery`,
  `refusalHint`, `retryNarration`, `garbleRecovery`) plus a
  `recoveryDispatchPredicate` derivation node and a
  `shouldContinueWithGarbleRoute` wrapper; it is four post-turn
  stream filters (`refusalHintFilter` 100, `retryNarrationFilter`
  200, `garbleRecoveryFilter` 300, `standardRecoveryFilter` 400)
  dispatched via `StreamObserverRegistry` and locked by
  `audit:recovery-dispatch-single-surface`. `synthesize` is now a
  true singleton — only `synthesizer`. Touched [Graph](/docs/graph),
  [Node catalog](/docs/graph-node-catalog),
  [Edge catalog](/docs/graph-edge-catalog), [Nodes](/docs/nodes),
  [Architecture](/docs/architecture),
  [Ownership boundaries](/docs/ownership),
  [Plugin contract](/docs/plugin-contract), and
  [Audit gates](/docs/audit-gates). [Node catalog](/docs/graph-node-catalog)
  also gains a "How the substrate source is organized" section
  documenting the `src/graph/` layout (`nodes/` one-file-per-node,
  `wiring/` registrars split by stage, the `topology.ts` lattice
  layer, concern-split `seams/` / `predicates/` / `strategies/`),
  with upstream-contributor steps pointing at the
  `wiring/register<Stage>Nodes.ts` registrar.
* **Config manifest foreign key corrected in
  [Config manifest](/docs/config-manifest).** The
  `harness_event_log.manifest_hash` reference is a real foreign key
  with `ON DELETE SET NULL` and a load-bearing
  `DEFERRABLE INITIALLY DEFERRED` clause, not gate-only as the page
  previously claimed. Added the `reference_count` column to the
  table DDL and cited the `audit:config-manifest-fk-constraint-applied`
  and `audit:config-manifest-referential-integrity` gates.
* **Schema description for [`@pleach/core`](/docs/core) amended
  to name the language-agnostic contract.** The `description`
  field in the site's Organization + SoftwareApplication JSON-LD
  graph (inlined in `<head>` on every page) previously described
  `@pleach/core` only in TypeScript terms, contradicting both the
  marketing surface and the editorial position at
  [`/ai.txt`](https://getpleach.com/ai.txt). The description now
  names the wire shapes — HTTP+SSE, `StreamEvent`,
  [`AuditableCall`](/docs/auditable-call-row),
  [`Checkpoint`](/docs/checkpointing), version vector — as
  language-agnostic and clarifies that the npm package is the
  TypeScript reference distribution.
  `programmingLanguage: ["TypeScript"]` stays accurate — the npm
  package IS TypeScript; the language-agnosticism belongs to the
  contract, not the publishable artifact. See
  [Language-agnostic contract](/docs/language-agnostic-contract)
  for the full wire-shape inventory.
* **Cross-docs status-flip pass** — rolling correction across
  \~55 docs pages tracking the alpha-ship of
  [`@pleach/compliance`](/docs/compliance),
  [`@pleach/gateway`](/docs/gateway), and
  [`@pleach/replay`](/docs/replay). Touched pages include
  [Audit ledger](/docs/audit-ledger), [Event log](/docs/event-log),
  [Subpath exports](/docs/subpath-exports),
  [Seams](/docs/seams), [Prompts](/docs/prompts),
  [Prompt builder](/docs/prompt-builder), and
  [Session runtime](/docs/session-runtime).

### Fixed [#fixed-2]

* **[Family-lock](/docs/family-lock) lifetime claim corrected** — the
  page stated "switching family means a new session." The runtime
  ships a sanctioned in-session re-lock path (`sessions.updateProviderModel`,
  emitting a `session-lock-resynced` event), so switching family is an
  explicit operation, not a new-session requirement. The lock's real
  invariant is no *silent* mid-turn widen, not lifetime immutability.
* **[AuditableCall row](/docs/auditable-call-row) `AuditRecordVersion`
  corrected 13 → 14** — both the literal description and the
  version-log range (`v8 through v14`) now match the upstream
  `AUDIT_RECORD_VERSION_HISTORY`, whose latest entry is `to: 14`
  (Cluster C: additive `rewriteStatus?` + `tokenCost.callClass`).

## 2026-06-08 — chat-pipeline lift into `@pleach/core` [#2026-06-08--chat-pipeline-lift-into-pleachcore]

Foundational lift bringing `OrchestratorClient` and
`streamSingleTurn` into the substrate. See the Unreleased section
above for the Added/Changed entries. The Pre-Merge Gate
`audit:domain-string-purity` landed alongside.

## 2026-06-08 [#2026-06-08]

### Added [#added-4]

* New page [Runtime inspector](/docs/runtime-inspector) documenting
  `inspectRuntime()` from `@pleach/core/inspector` — the typed
  read-only introspection surface, the `InspectionReport` /
  `CapabilityReport` / `PluginReport` / `CapabilityDescriptor`
  shapes, and the 38 known capabilities (29 modern + 9 deprecated).
* `definePleachPlugin()` factory section in
  [Plugin contract](/docs/plugin-contract#definepleachplugin--the-typed-factory)
  covering the 8-key `PluginCapabilities` cluster and the `_raw`
  escape hatch.
* `contributeFabricationGuard` section documenting the typed
  `FabricationGuardImpl` hook (7 methods) that retires the
  `orchestratorHotpath.fabricationGuard` untyped-bag entry.
* `CreatePleachRuntimeConfig.host.{strategies, modules, raw}`
  section in [Host adapter](/docs/host-adapter) with the 15-key
  `HostExtensionBundle` list and the `host.modules` list
  (`eventLogWriter`, `store`, `interrupt`).
* C9 hash-chain probe section in
  [Tamper-evident hash chain](/docs/hash-chain) documenting
  `[UXParity:c9-hash-chain-row-stamp]` (PE-1) and
  `[UXParity:c9-hash-chain-verify]` (PE-2).
* `@pleach/core/inspector` row in
  [Subpath exports](/docs/subpath-exports).

### Changed [#changed-2]

* [Packages](/docs/packages) table corrected to real published
  version — `@pleach/core 1.1.0` (was largely stale "Reserved ·
  placeholder" framing).

### Fixed [#fixed-3]

* [Runtime inspector](/docs/runtime-inspector) — replaced fictional
  `RuntimeInspection` / `CapabilityState` types with the real
  `InspectionReport` / `CapabilityReport` / `PluginReport` /
  `CapabilityDescriptor`. Corrected the `status` discriminator to
  `"wired" | "deprecated-wired" | "unwired"`.
* [Plugin contract](/docs/plugin-contract) — `definePleachPlugin`
  capability fields take values, not thunks. Corrected to the real
  8 capability keys (`prompts`, `runtimeAwarePrompts`,
  `safetyPolicies`, `fabricationDetectors`, `tools`,
  `streamObservers`, `intentToolMap`, `toolCouplingHints`).
* [Host adapter](/docs/host-adapter) — `host.modules` corrected to
  `{ eventLogWriter, store, interrupt }`. `host.strategies`
  populated with the real 15-key `HostExtensionBundle` list.
* [Tamper-evident hash chain](/docs/hash-chain) — subpath corrected
  from `@pleach/core/event-log` to `@pleach/core/eventLog`
  (camelCase is the real export).
* [Event-log projections](/docs/event-log-projections) — subpath
  corrected from `@pleach/core/event-log/projections` to
  `@pleach/core/eventLog` (projections export from the
  `./eventLog` barrel; there is no `/projections` subpath).
