Skip to content
pleach
ReferenceChangelog

@pleach/core changelog

Release history for @pleach/core — site-docs entries that materially touched the @pleach/core surface, newest-first. Canonical runtime history lives upstream.

This page collects the site-content changelog entries that materially touched the @pleach/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 and on npmjs.org/package/@pleach/core.

See the combined site changelog at /docs/changelog/combined for the chronological unified view across every SKU.

Unreleased

Fixed

  • Server samples import only from @pleach/core/quickstart. @pleach/core/server isn't a published subpath, and Server taught it in five samples. The createHarnessServer sample is now a createPleachRoutes({ capabilities }) override; the createInMemorySharedInterruptResolver sample imports from @pleach/core/quickstart and passes the resolver as capabilities.interruptResolver; the Next.js, Express, and Hono mounting examples use routes.nextRouteHandlers(), routes.nodeHttp(), and routes.fetch. The bare HarnessServer / ROUTES import block is gone, and the handler sections are labelled as internals reached over HTTP. Two corrections ride along: the durable-resolver sample passed a resolver as capabilities.interrupts instead of capabilities.interruptResolver, and the Next.js catch-all samples on Server and API routes omitted basePath, without which the dispatcher can't match /api/harness/... paths. Interrupts, Turn lifecycle, and SessionRuntime now point at the route factory instead of HarnessServer.

Added

  • Per-call-class model routing is honoured, not recorded-only. Model resolution matrix gains a "Per-call-class overrides" section for SessionRuntimeConfig.perCallClassModelRouting: routes are honoured by createProviderAdapter and the quickstart standard adapter; cross-family routes, routes whose family can't be derived, and routes that arrive before the lock is observable are refused and disclosed ([PerCallClassRouting] override-refused, auditMetadata.perCallClassRouting); a routed call is priced at the matched rung's rates, and a model in no rung is left unpriced; a custom quickstart providerStream must dispatch to ctx.resolvedModel when it is present. Multi-synthesize drops the "forward-compatible, honoured once the core widening ships" framing for createPerCallClassRouting: core now reads the field. Upstream source, not yet published to npm.

  • Sub-agent cost rolls up to the parent turn. Subagents gains a "Cost attribution" section for SessionRuntime#getTurnCostRollup(turnId?), which returns { turnId, self, descendants, total, partial, truncated } and splits the root turn's own calls from every sub-agent's. Each bucket reports callCount, pricedCalls, unpricedCalls, costUsd (omitted when nothing was priced) and four token totals. The section states the limits: background async.spawn tasks are excluded; callCount and costUsd are floors when calls are unpriced; total can differ from the durable cost.rollup row; retention is LRU over 32 turns, and a row arriving after release or eviction is marked truncated. Event log documents the quickstart message.assistant row's payload.cost_rollup snapshot (root_turn_key, snapshot, as_of, self, descendants, total, partial, truncated). The "Subagent cost attribution to the parent turn" row in content/docs/migrating-from-anthropic-enterprise.mdx now names runtime.getTurnCostRollup(turnId). Upstream source, not yet published to npm.

Changed

  • Turn stop is a client disconnect with a declared intent. SessionRuntime gains a "Stopping a streamed turn from the client" section: the client declares at stream-open what its disconnect means (header x-pleach-on-disconnect or query on_disconnect), the stream handler classifies the abort ("user" for a declared stop, "error" otherwise, "timeout" first when the server closes the stream — first abort wins), and there is no stop route or shared stop flag. Documents parseDisconnectIntent, bindDisconnectToTurnAbort, createTurnAborter, and DISCONNECT_INTENT; notes the intent values may be renamed before publish. Deployment replaces the abortSignal: req.signal route example with a bound disconnect. Server documents the optional third request argument to HarnessServer.handleExecuteMessage and threads it through the Next.js, Express, and Hono adapters. Turn lifecycle, Session lifecycle, and API routes point at the new contract; resume after a stop hydrates from the session store and checkpointer.
  • Edge runtimes are no longer supported. @pleach/core runs on Node.js (server side) or in a browser. Edge servers (Vercel Edge, Cloudflare Workers) and any server runtime without AsyncLocalStorage are refused: tool execution needs per-request isolation, and a shared context slot would let concurrent requests see each other's identity. Entering a tool-execution scope there throws ToolExecutionContextUnsupportedRuntimeError (code PLEACH_TOOL_EXECUTION_CONTEXT_UNSUPPORTED_RUNTIME, runtime of "edge" or "server-without-async-local-storage"; see Error codes), and the quickstart createPleachRoute() answers 501 before any stream starts. Move Edge routes to the Node.js runtime. The install, quickstart, deployment, and FAQ pages drop their Workers / Edge claims accordingly.

Fixed

  • Audit-record claims now match what the code enforces. The homepage pillar, components/session-manifest-diagram.tsx, content/docs/security.mdx, replay.mdx, ci-enforcement.mdx, audit-ledger.mdx, and compliance/gdpr-erasure.mdx said rows are "written once and never edited or deleted" or "immutable". The ledger API has no update or delete primitive, but that does not stop a writer with direct database access. The pages now say so, and say what the hash chain adds: edits, deletions, and reordering become detectable after the fact.
  • Drifting counts removed or labelled. The package count read 13, 14, 15, or 19 depending on the page, while the packages.mdx table lists 17 entries; overview cards, which-sku.mdx, license-compatibility.mdx, and organization.jsonld now point at the table instead of restating a number. Hook counts are labelled by what they count: 80 total (plugins/authoring.mdx), 71 contribute* hooks (plugin-contract.mdx); the stale 41 in runtime-inspector.mdx is gone. nodes.mdx and graph-node-catalog.mdx disagreed on every node total (44 vs 42); the totals are dropped and the catalog's per-stage counts stay canonical.
  • Node-registry and plugin-hook counts re-derived from the upstream contract. NODE_STAGE_MAP carries 42 names, not 43 — corrected on Graph, Node catalog, Audit gates, and Ownership; the catalog also drops two names that were never nodes (workspaceIndex, toolLoopStream) and re-stages garbledContentGuard / repetitionGuard to post-turn. HarnessPlugin exposes 80 hooks (71 contribute* + 9 lifecycle), not 77 (68 + 9) — corrected on Core and Authoring, with Plugin contract now naming its narrower 70-hook audit denominator. Full detail in the combined view.

Added

  • Heartwood — the loader's two-scope resolution and shadow semantics, previously undocumented. createFileHeartwoodLoader resolves first-non-empty-wins across an explicit path, ./HEARTWOOD.md (the per-project soul), and ~/.pleach/heartwood.md (the user-global soul). The scopes shadow, not merge: the first file found wins entirely, so a project ./HEARTWOOD.md fully overrides the user-global file rather than concatenating with it. The page had named only the path override.

  • Prompt builder — the composition-determinism guarantee stated outright. A static or core contribution's content is a pure function of its PromptContext and does not vary with tool-loop depth or turn recursion. contributeRuntimeAwarePrompts is the only channel that varies per turn, and static contributions are held out of the RuntimeStateSnapshot on purpose. Matching pointer added to Prompts.

  • Skills — the create_skill base-tool for agent-authored skills. An agent can name a repeatable procedure as a reusable skill mid-session. create_skill is opt-in (list it in include) and available when the runtime has a durable store to persist to; authored skills surface through the same progressive-disclosure path as loaded skills — an in-prompt index entry plus an on-demand skill_view body.

  • Defensive patterns — a new Architecture page: six design rules the runtime holds itself to, each paired with the shipped @pleach/core API that embodies it. Includes the first docs coverage of the destroy-quiescence surface — AsyncTaskManager.seal(), awaitQuiescence(timeoutMs) and its AsyncTaskQuiescenceReceipt, and SessionRuntimeConfig.destroyQuiescenceTimeoutMs (default 5 s) — landed upstream in dfbbd6f239 + a3ad79dce1.

  • The *_CALL_CLASS constants from @pleach/core/graph/seams/callClassConstants. The page explained that lint:callclass-literals rejects a bare "synthesize" but never said what to write instead when you need the value rather than the type.

  • Six new subpath rows on Subpath exports: graph/defaultAgentGraph (the only published home for buildDefaultAgentGraph), graph/seams/callClassConstants, eventLog/durableFlush (setWaitUntilImpl / deferFlush / awaitPendingFlushes — the serverless durability seam), strategies/schemaCondensation, skills with its two narrower entries, and the four runtime/facets/* client-facet entries.

  • audit:docs-vs-contract gains a fifth check: SYMBOL REACHABILITY. Check (4) asks whether an import path resolves; this asks whether the symbol is exported by it. Correcting only the path yields an import that resolves and binds nothing. 1,265 named imports checked against a committed mirror of 7,166 symbols across 329 subpaths, mutation-proved three ways.

  • Four new @pleach/core subpaths on Subpath exports — attestation/cms, crypto, context, and the corrected wildcard story. @pleach/core/crypto is carried as experimental with its DDL precondition inline: on a schema declaring wrapped_dek or iv as NOT NULL, shred() raises SQLSTATE 23502 while getOrCreateDek still succeeds — so enabling encryption there mints ciphertext that can never be erased, and GDPR Article 17 becomes impossible. Verify nullability before the first DEK.

  • The CMS envelope — @pleach/core/attestation/cms emits a detached CMS (RFC 5652) SignedData over an attestation's canonical bytes, Ed25519 per RFC 8419, alongside AttestationV1.signature rather than instead of it. Documents buildAttestationDetachedCms, why certificateDer is effectively required for an independently-checkable artifact, and the RFC 5652 §5.4 re-tag invariant: signedAttrs is encoded [0] IMPLICIT but signed as a universal SET, and getting it wrong yields a structure that verifies nowhere while staying self-consistent. Core re-reads the emitted DER and throws a named signed-attributes-mismatch at emit time. Detect with isCmsAttestationError, never instanceof.

  • Which merkle construction built the root — MerkleConstruction and the merkleConstruction field on ChainProofV1. generateProof(...) defaults to "legacy" deliberately: switching a chat to "rfc6962" changes every root it produces, and existing anchors countersigned the legacy roots. Names both defects RFC 6962 fixes — no domain separation (leaf and internal node are both bare SHA-256, the second-preimage precondition) and odd-node duplication (the CVE-2012-2459 shape, where [A,B,C] and [A,B,C,C] share a root). Absence of the field means the proof predates it. Separate axis from chain_key_version.

  • session.ended and HARNESS_EVENT_TYPES. session.ended makes a clean close a positive fact rather than an inference from absence — and its own absence proves nothing, since a session whose process dies never writes one. HARNESS_EVENT_TYPES is the runtime mirror of the HarnessEvent union, pinned by an exhaustive Record<HarnessEventType, true> in production source. Membership means emittable, never persistable.

  • Auth events. AuthEventType, AuthMethodClass, AuthEvent, AuthEventSink, and contributeAuthEventSink. Core owns the vocabulary, the host owns the store: the C9 chain is keyed (tenant_id, chat_id) and an auth event has no chat by nature — measured at 4,269 chatless rows, zero chained — so writing them to the harness event log would produce a trail that looks complete while its integrity guarantee stops short of the access events an auditor reads first. Status stated honestly: no collectAuthEventSink() collector yet and no published exports entry for the vocabulary, so implementing the hook today compiles and does nothing.

  • Optional peer dependencies. @peculiar/asn1-{schema,cms,x509} for CMS attestation, @pleach/rbac-contract for @pleach/core/rbac, @modelcontextprotocol/sdk for MCP, @sigstore/core for RFC 3161 receipt verification. Each fails with a named error identifying the package rather than a module-resolution stack.

  • The RFC 3161 surface is now reachable from the paths consumers actually walk, not only from the primitives. createPleachAgent takes a timestampAuthority — before this the quickstart called attestChainState without one and exposed no way to supply it, so the default path produced claim-only attestations permanently. Both the agent and the runtime now expose evidence (runtime.evidence: timestamped, mintAnchorMs, clockInjected), which reports what was wired and deliberately not what is true of the outside world — a configured-but-unreachable authority still reads timestamped: true, and there is no clockAnchored field because core cannot verify that claim.

  • chainAnchorScheduler in @pleach/recipes composes the scheduled anchor-and-verify pass that every consumer previously wrote by hand. It exposes runOnce() and no loop: a convenience start() was written and removed, because a scheduler with no retries, no at-most-once story and no observability is strictly worse than the one a consumer already runs, while looking like a supported way to do this in production. Also documented in Hash chain.

  • Hash chain now documents the anchor-verification surface, closing a promise the page already made — it ended on "the property is only real if something actually performs that check" without saying how. Covers verifyChainAnchors (re-deriving a witnessed merkle root catches a mid-chain deletion whose rows were re-linked cleanly, and therefore passes a prev_hash walk — a chain that verifies is not the same as a chain that matches its witness), evaluateChainAnchorFreshness (grade liveness on staleness, never on row count; anchorCount and timestampedAnchorCount are separate signals because damage and staleness are independent failures), and classifyChainAnchorAlertLevel. Two decisions are written up as generally applicable: liveness never shares a channel with tamper detection, and damaged evidence may withhold an escalation but must never produce a de-escalation.

  • Attestation documents createAnchoredClock (@pleach/core/runtime) — skew-corrected, monotonic, suspension-aware — and is explicit that it reduces honest clock error only. It does not upgrade issuedAt from a claim to a proof; only the TSA countersignature does that. Upstream: ivybiosciences-next RP-340.

  • Comparison adds an agent-forward "usage axis" section, and the homepage "Quick answers" intro is broadened to match. The claim: Pleach's surface is built to be operated by agents — the introspectable facet API (runtime.tenant, runtime.events, runtime.graph.*, CI-gated by audit:facet-coverage), TechArticle JSON-LD from one controlled tag vocabulary, per-page Markdown twins, a live MCP server, and an agent-skills index — not a human console with agent access added on. Distinct from the capability differentiators; no existing value prop changed.

  • Comparison adds a governance-control-plane / AI-gateway category. Frames @pleach/core against governed-proxy control planes (Portkey, Cloudflare AI Gateway, and the enterprise control planes above them) as altitude, not timing: a proxy governs the call at the HTTP boundary; core governs the agent's execution graph and writes the AuditableCall row to a database you own. Relatedly, Landscape row 1 now notes that proxy-advertised "tamper-evident audit trails" live on the vendor's side, not as a hash-chained row you own.

  • Five more @pleach/core public exports documented (720a6e8a3 / de4403b6a). Interrupts adds rewriteToolArguments — the natural-language → structured-args rewrite for approval-with-edit flows; it runs on the utility call class and never throws, resolving to the original arguments on every fallback path. Tools adds the standalone createDiscoverToolsTool / DISCOVER_TOOLS_TOOL_NAME factory for hand-rolled servers. Server adds createPluginToolRegistry — the getter-backed registry behind /tools and /tools/:name. Storage adds composeDurableStore (build a DurableStore from adapters you already hold) plus the optional blob member and createMemoryBlobStore / MemoryBlobStore. Adoption paths names the BrownfieldRuntimeMenu options type; Prompts names the optional PromptDescriptor ({ title?, summary? }) surfaced by runtime.prompts.describe(). Upstream trunk, not yet published to npm.

  • New Pricing math page. Documents the @pleach/core/pricing subpath — computeTurnCost, DEFAULT_PRICING_MATRIX, createPricingResolver, the OpenRouter fetch, and resolveCostEventUsd.

  • React lab component kit documented. The full @pleach/core/react/lab surface — PleachLab's composable panels plus useLatticeState / useEventLogCapture / useChannelSnapshot — with @pleach/core/react/lab named as the canonical subpath.

  • Three prompt- and context-management capabilities documented. Orchestrator middleware adds the optional summaryProvider field on SummarizationConfig — a callback supplying a higher-quality (e.g. LLM-generated) rolling summary in place of the default extractive char-slice summary, falling back to the extractive default when it returns an empty value or throws. Prompt builder adds the "Cache-stable prefix" section: the stability: "stable" | "volatile" tag on BudgetedSection and the stableBoundaryIndex field on ComposedPrompt — composeBudgetedPrompt hoists stable sections into a contiguous prefix (untagged input stays byte-identical) and reports where the volatile suffix begins, so a consumer places its provider cache_control breakpoint after included[stableBoundaryIndex - 1]. Prompts adds "Persisting an agent's role" — put durable persona in the core.role-template slot via replaceCore / prependPersona and tag it stability: "stable". Upstream trunk, not yet published to npm.

  • condenseTools.intentPriorityMap given its real type. Tools replaces the intentPriorityMap placeholder with Record<string, readonly string[]> — an intent name mapped to the tool names that keep their full schema when that intent is active. Upstream trunk, not yet published to npm.

  • condenseTools + the discovery meta-tool documented. Tools gains the createPleachRoute option that trims the tools[] array above a threshold — keeping maxSchemaTools full schemas and surfacing the rest as one-line names — plus the discovery meta-tool (metaToolName) the model calls to pull a dropped schema back. Distinct from the prompt-directive weakToolSchemaBudgetPlugin on the same page. Upstream trunk, not yet published to npm.

  • Model-pipeline surface documented — two new pages plus eight section additions. Model hooks documents the @pleach/core/hooks pre/post-model seam (PreModelHook, PostModelHook, ModelProfile + historyTrimmerHook, lazyModelDefaultsHook, toolCallValidatorHook, repetitionDetectorHook, wired via runtime.model.registerPreHook / registerPostHook). Orchestrator middleware documents @pleach/core/middleware (OrchestratorMiddleware, MiddlewareStack, ResultEvictionMiddleware, SummarizationMiddleware, LanguageModelMiddleware, CHARS_PER_TOKEN, wired via setOrchestratorMiddlewareInit). Both join Subpath exports under a new "Model pipeline" category with @pleach/core/synthesis (SynthesisEmitter, enrichOffloadedToolPreviews). Sections added: finalizeAnswer + the { banner } return on postSynthesisGuard (Plugin authoring); ProviderCatalogContract and the SLOW_COLD_START_PLANNER_FAMILIES / isSlowColdStartPlannerFamily gate (Providers); InterruptManager.requestApprovalWithId (Interrupts); runtime.abort(reason) / AbortCoordinator + TURN_ABORT_REASON (Session runtime); the durable subagent.tool_used audit row (Subagents); the harness_event_log_owner_select per-user RLS policy + harness_event_log_service_role write bypass (Multi-tenant); and MemoryStore as the config.store ship-a-default. Existing published @pleach/core surface; upstream trunk, not yet on npm.

  • Four runtime-behavior sections documented. Each mechanism shipped upstream but had no doc home. Tools gains weakToolSchemaBudgetPlugin (@pleach/core/prompts) plus createWeakToolSchemaPredicate (@pleach/core/modelfamily) — the proactive tool-call budget for weak-tool-schema families (google/deepseek/moonshot, defaults maxBatch: 4 / maxSameTool: 2), gated on PromptContext.family. Turn lifecycle documents the no-first-token dead-air hard-cut (reduceTurnDeadAir; upstream d062ab24d5). Providers documents the stalled-rung cascade pivot, classed SYSTEM_ABORT not USER_ABORT (upstream e1fa3f1487). Fabrication detection documents metadata.uncoveredPlanSteps and the honest "not dispatched" note (upstream bb0f995fce, ca9dc86644). All from published subpaths or documented runtime behavior; upstream tree 8f224cbf42 on trunk, not yet published to npm.

  • Standalone hosted-agent route family — createPleachRoutes, createPleachAgent, DurableStore, MemoryEventLog. @pleach/core now mounts the whole route family from one factory, not just the single execute leg. createPleachRoutes and the three-arm createPleachAgent({ context, tools, db }) façade (both @pleach/core/quickstart) mount sessions CRUD, execute+SSE, interrupts, checkpoints, rollback, events-recall, sync, and fork; createPleachAgent returns { fetch, nextRoutes(), nodeHttp(), store }. DurableStore + createMemoryDurableStore() (@pleach/core/store) unify storage, checkpointer, event-write, event-read, and recall behind one binding, and MemoryEventLog (@pleach/core/query) is the keyless in-memory recall ledger with an asQueryClient() shim — so recall works with no Supabase when db is omitted. createHarnessServer({ runtime, storage, checkpointer?, events? }) is the build-only auto-bind factory over the still- unpublished @pleach/core/server subpath. Known limitation: the default interrupt resolver binds a per-request in-process InterruptManager; a durable multi-request deployment supplies its own via createPleachRoutes(...).capabilities.interrupts. Landed across API routes, Server, Quickstart, Storage, and Query. Upstream: pleachtui Track A, @pleach/core@0.1.0 on trunk (Waves 0b34ab8742, 095d037933, add6562a15, 2b20c9c4be) — on trunk, not yet published to npm.

  • Runtime strategies documents the resolveCallReasoningEffort hook. A new per-call SessionRuntimeConfig strategy slot. The runtime consults it before every provider stream with the resolved { callClass, family, modelId }; the returned ReasoningEffort ("low" | "medium" | "high", "none" to disable the budget, undefined to leave it untouched) threads onto StreamChatOptions.reasoningEffort and the adapter lowers it to the provider request. Capability-gated — a model that doesn't advertise reasoning control ignores the field, so an unsupported model never 400s on the param. ReasoningEffort is opaque to core: the host owns the policy of which call class on which family to cap. Unset ⇒ no field attached, request byte-identical. Fail-soft — a throwing resolver is caught and treated as undefined. Added to the "Newer injection hooks" table plus a per-call cadence note.

  • 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 writes.

Changed

  • Replay-determinism claims reconciled from "byte-identical" to "deterministic; divergence-detected." Determinism, Comparison, and Migrating from Anthropic Enterprise no longer claim a recorded turn re-generates a live model call byte-for-byte. Deterministic replay reproduces the recorded response, tool-call sequence, and terminal state, and names any drift with ReplayDivergenceError; a replay re-serves the recorded response, it does not re-run the provider. "Byte-identical" is kept only where it holds by construction — fold-based state hydration and deterministic channel reducers. Corrects a load-bearing over-claim; the property is reproducible control flow + attributable divergence, not byte-equality. Adds the serial-execution caveat to the replay claim: replay determinism holds only under serial graph execution (one node per superstep).

  • Reasoning-only recovery documented as default-on for the packaged surface. Providers already documented the opt-in SessionRuntime reasoningRecovery DI; it now records that createPleachRoute defaults it on (opt out with recoverReasoningOnly: false), so a reasoning model that emits only chain-of-thought gets one same-model re-elicitation instead of a false family-exhausted. Interrupts gains a pointer from "Cross-request approval" to the interruptResolver option (createInMemorySharedInterruptResolver() / a store-backed DurableInterruptResolver).

Fixed

  • The query layer does not read only tables the runtime writes (content/docs/query.mdx). The page claimed it "reads from the same tables the runtime writes to". ai_chat_messages is the exception: it is host-provided, no code in @pleach/core writes it, and no DDL for it ships in the schema bundle — the three readers (query/usage.ts, query/tools.ts, query/review.ts) only .select(). The table now names the shipped .sql file per row and marks that one No — host-provided. Also recorded: ai_chat_manifests and refresh_chat_manifest_from_events ship in no SKU, so getChatManifest, getManifestJobsByStatus, getConversationState and getSessionReview require consumer-supplied schema.

  • The Graph callout said seven symbols were unreachable; it is now six. Upstream published @pleach/core/graph/defaultAgentGraph the day after the callout was written, giving buildDefaultAgentGraph a home. A documented defect has a shelf life exactly like a documented feature. The six that remain: START, END, DEFAULT_TURN_FLAGS, StateGraphNodeMetadata, AnnotationSchema, InferAnnotationState.

  • The committed export-map snapshot was 32 entries stale one day after being committed. Upstream added 32 named @pleach/core subpaths and removed none between 2026-08-29 and 2026-08-30. Ten carry no previously-unreachable symbol — they narrow an already-reachable surface into a named entry rather than add capability, which is why they get no docs rows.

  • The @pleach/core/* wildcard section described a hatch that no longer exists (Subpath exports, with references on Versioning, Audit gates, and Publishing contract). The bare ./* export was replaced upstream by the scoped ./internal/*, so the page's "It works today" rule of thumb — and both worked examples, @pleach/core/graph/... and @pleach/core/replay/types — named paths that now throw ERR_PACKAGE_PATH_NOT_EXPORTED.

  • Graph and Graph node catalog taught import blocks that cannot compile. Both imported from @pleach/core/graph, which is not a key in the published exports map. Seven of the thirteen symbols in the Graph example had no published import path at all: START, END, buildDefaultAgentGraph, DEFAULT_TURN_FLAGS, StateGraphNodeMetadata, AnnotationSchema, InferAnnotationState. Corrected to import only what resolves, with a callout naming the seven and the workaround. Fixing the path alone would have produced an import that resolves and still fails to bind.

  • Hook-catalogue counts were stale by ~70% (Plugin contract, Runtime inspector). Both cited 41 hooks / 38 wired. Measured from the instrument the claim names, upstream audit:plugin-contract-completeness: 70 hooks — 66 wired end-to-end, 3 baselined awaiting consumer, 1 declared without a collector (contributeAuthEventSink). HarnessPlugin declares 78 members.

  • AuditRecordVersion cited as 19, now 25 (CI enforcement). Six additive versions landed since: outcome.why.stats.servingProvider, sub-agent attribution (parentTurnId / subagentId / subagentDepth), decision.lockedFamily, the provider-exhausted status, decision.fallbackChain becoming additive-optional so an omission is an admission rather than a fabricated [], and decision.rungOrdinal.

  • @pleach/core/audit-ledger is not a subpath (Attestation) — the published path is @pleach/core/audit. @pleach/observability is not a package (OTel observability) — the SKU is @pleach/observe and the shape is a destination. @pleach/core/schema/postgres (Schema) corrected to @pleach/core/schema/postgres/*.sql, the published .sql asset glob.

  • Subpath exports claimed to snapshot @pleach/core@1.x. No @pleach/* SKU has shipped past 0.1.0 — the same version-epoch error corrected across nine files in the entry below, in a tenth file that pass missed.

  • audit:docs-vs-contract gained a fourth check: EXPORT-MAP DRIFT. Every @pleach/... specifier the docs teach is now resolved against a committed mirror of each package's published exports map, so a specifier that would throw ERR_PACKAGE_PATH_NOT_EXPORTED on a fresh npm install fails at the point of drift. The site-side mirror of the upstream audit:doc-example-import-resolution gate. 2,348 specifiers resolved against 19 export maps; every fix above was found by it.

  • Model-resolution matrix — Google's fast tier was stale. Model resolution matrix listed gemini-3-flash-preview for the Google converse and utility rungs; the live matrix resolves both to gemini-3.5-flash-lite. A reader who saw the old id would have keyed a fingerprint or cost estimate off a model that no longer fires.

  • Off-policy example term scrubbed from Prompts. Two gatedPrompt examples keyed on a "modal" tool; both now key on the neutral "sandbox" capability. Example-only — no API or claim change.

  • @pleach/core doc corrections from a ~200-commit upstream sweep. Graph node catalog: core registry corrected 44 → 43 — two domain plugin nodes were listed as core, and the core node syntheticExpansionDetector was missing. Config manifest: added a partial-coverage caveat (only the plugin surface is content-hashed today). Fabrication detection: documented the ~13-detector grounding/offloaded-data cohort the exports table omitted. Air-gapped deployment: corrected from "v2+ roadmap item" to the shipped fail-closed airGapped enforcement. Adoption paths: createBrownfieldRuntime (@pleach/core/runtime) is shipped, no longer "proposed."

  • Four broken root imports corrected to their real subpath. Five code blocks imported a symbol from @pleach/core that the root barrel does not re-export (verified against the package exports map): SupabaseAdapter / createSupabaseAdapter (Research agent) → @pleach/core/sessions; withTenantHeader (Multi-tenant SaaS agent) → @pleach/core/tenant; computeFingerprint (Testing) → @pleach/core/fingerprint; EventLogWriter (Event log and Recipes) → @pleach/core/eventLog. Each would fail to resolve on copy-paste; the subpaths are public per Subpath exports.

  • Two orphaned pages wired into the sidebar — Install (Get Started) and Cycle-break contract packages (Reference) existed but were reachable only by direct URL.

  • Adoption paths "Host adapters" card corrected — it promised "implement OrchestratorAdapter / StorageAdapter", but Host adapter documents the setHarnessModuleLoader module-loader seam. Reworded to match.

  • Stale AuditRecordVersion claim corrected — 18 → 19. CI enforcement stated the locked row shape was "currently 18"; upstream shipped the additive bump to 19. A version claim is load-bearing — a reader who saw "18" needs this row to know it moved. The package's published value is the source of truth; this tracks the shipped bump.

  • Hash-chain verifier home corrected to @pleach/core/eventLog. Audit ledger (two spots) and Recipes said verifyChainForChat / generateProof "ship in @pleach/replay@0.1.0". They are defined in @pleach/core/eventLog; @pleach/replay consumes them. Corrected across Audit ledger (two spots), Recipes, Which SKU, and Regulated-domain agent; now consistent with Hash chain.

  • Error-code range corrected to 1xxx–11xxx. The authoritative band table in Error codes defines 10xxx (caller-auth) and 11xxx (runtime/infra). Stale 1xxx–9xxx (frontmatter + Troubleshooting ×2) and 1xxx–7xxx (Contributing, Stream events) summaries now match the table.

  • maxConcurrentSubagents clamp claim corrected. Research agent said a maxConcurrentSubagents above SUBAGENT_LIMITS.maxConcurrent is clamped to it. They are separate limits on separate paths (default 4 caps the SubagentManager; 3 caps the imperative spawn path), neither clamped to the other — now consistent with Subagents.

  • Unknown safety-policy ids now throw at construction — even with no plugins. 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) 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 — 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 — executeMessage is positional executeMessage(sessionId, "hello"), not the object form executeMessage({ sessionId, content }) (core SessionRuntime.ts). 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

  • Gate failures → fixes — 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 loop.

  • AuditRecordVersion corrected 14 → 18 on CI enforcement to match the source (export type AuditRecordVersion = 18;) and the AuditableCall row.

  • Extending Pleach — the gate-paired authoring map. Pairs each extension point with the interface, the registration call, and the audit gate that fails if the addition is wrong — nodes (audit:graph-stages), event types via contributeEventTypes (audit:c8-union-member-has-producer

    • audit:c8-event-type-allowlist-coverage), scrubbers, plugin hooks (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, channels, and 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, Quickstart, and 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 and Concept clusters now name the engine/ scheduler (SuperstepRunner + its primitives) as the deterministic superstep executor that runs the compiled graph. 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 updated to import SkillLoader, parseSkillFrontmatter, Skill, and SkillFrontmatter from the @pleach/core/skills barrel (the /loader, /parser, /types sub-subpaths do not exist); Agents prose corrected from "wildcard path" to a real emitted @pleach/core/agents/types subpath.

Changed

  • @pleach/core/internal/… subpaths retired from the public docs surface (pkgclean 06 D8). streamSingleTurn and 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

  • More SKU doc-page snippets reconciled with the shipped signatures (devharness FINDING 80–82). 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 — 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 — 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 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 config table gains eventLogWriter + eventReader rows; 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 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 — LangChainProvider takes a POSITIONAL model arg and wires via the top-level provider: slot (not { model } / host.strategies.provider). 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 — 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 and 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 + 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: 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: 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, SessionRuntime, turn lifecycle, and API routes. And 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) and @pleach/core/ai/modelFamily/matrix.ts (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 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 — fork sets parent_id: source.id (not null).
    • 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 — the checkpoint snapshot is authoritative on the live restore path; the event-log canonical reader is a flag-gated shadow today.
    • 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 — dropped the non-existent queryAuditableCalls; corrected getChatUsage / lookupModelCost / getSessionReview / getInterruptChain signatures + return shapes to the real @pleach/core/query surface.
    • 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 — 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

  • 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.
  • 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.
  • 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.
  • @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.
  • 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.

Added

  • CI enforcement of the audit contract — new Reference page indexing the audit-gate catalog 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.
  • Contribution 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 gains a cross-link; Authoring a HarnessPlugin reframes its informal 12-bucket grouping as the scan view of these nine canonical namespaces.
  • 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'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 — 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 (stamping section + EventLogRow field) and Schema (rolling-out harness_config_manifest section).
  • OrchestratorClient — 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 — 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 — 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 — 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 — new top-level page on the Ed25519-signed envelope substrate that sits on top of the 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 — 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 "What you get" — new bullet "Composes under an existing Enterprise contract" after the AuditableCall ledger bullet.

Changed

  • Family-lock page documents the host-internal planner carve-out. family-lock.mdx 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 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 (the singleton section + the LangGraph callout's ProviderSeam<"utility"> type), Graph, Graph node catalog (the llmDecision row's acceptsSeam converse→utility, the Stage 2 intro, and the singleton-invariant wire-check), Call classes (the utility row + section now name the tool-loop decision as the canonical utility call), and 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, Node catalog, Edge catalog, Nodes, Architecture, Ownership boundaries, Plugin contract, and Audit gates. 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. 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 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. The description now names the wire shapes — HTTP+SSE, StreamEvent, AuditableCall, Checkpoint, 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 for the full wire-shape inventory.
  • Cross-docs status-flip pass — rolling correction across ~55 docs pages tracking the alpha-ship of @pleach/compliance, @pleach/gateway, and @pleach/replay. Touched pages include Audit ledger, Event log, Subpath exports, Seams, Prompts, Prompt builder, and Session runtime.

Fixed

  • 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 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

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

Added

  • New page 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 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 with the 15-key HostExtensionBundle list and the host.modules list (eventLogWriter, store, interrupt).
  • C9 hash-chain probe section in Tamper-evident 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.

Changed

  • Packages table corrected to real published version — @pleach/core 1.1.0 (was largely stale "Reserved · placeholder" framing).

Fixed

  • 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 — definePleachPlugin capability fields take values, not thunks. Corrected to the real 8 capability keys (prompts, runtimeAwarePrompts, safetyPolicies, fabricationDetectors, tools, streamObservers, intentToolMap, toolCouplingHints).
  • Host adapter — host.modules corrected to { eventLogWriter, store, interrupt }. host.strategies populated with the real 15-key HostExtensionBundle list.
  • Tamper-evident hash chain — subpath corrected from @pleach/core/event-log to @pleach/core/eventLog (camelCase is the real export).
  • 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).