diff --git a/README.md b/README.md
index a278dbad..3547772c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
A Git-native runtime for shared causal history.
-
Write intents. Read timelines. Keep receipts.
+
Write intents. Observe lanes. Keep receipts.
+
A runtime for shared causal history.
Offline-first, multi-writer, deterministic, and built for provenance-aware applications.
@@ -16,7 +16,8 @@
-`git-warp` is a Git-native system for storing and reading **causal history**.
+`git-warp` is a runtime for storing and observing **causal history**. Its
+current durable storage substrate is Git.
Instead of treating a graph as one big in-memory object, `git-warp` treats history as the source of truth. Reads are bounded views over that history, and writes are appended as patches. It is one runtime in the [Continuum](https://github.com/flyingrobots/continuum) stack ([more below](#continuum)).
@@ -52,11 +53,13 @@ replay-backed so callers receive complete diff and provenance data.
See [CHANGELOG.md](CHANGELOG.md) for the full in-repository release notes.
-## v19 First-Use API
+## Transitional v19 First-Use API
-This is the public contract new application code should start from. The
-v18 graph-first package exports are removed rather than carried as a second
-compatibility API.
+The currently implemented v19 facade below is transitional. The accepted
+[`Runtime` / `Lane` / `Observer` vocabulary checkpoint](docs/topics/api/) is
+the release target and must not be described as implemented until its runtime
+and boundary tests land. The v18 graph-first package exports remain removed
+rather than carried as a second compatibility API.
```typescript
import { openWarp, intent, reading } from '@git-stunts/git-warp';
@@ -147,7 +150,7 @@ actually used through receipts. The removed `openWarpGraph()` and
`openWarpWorldline()` paths are migration source material, not a second
application API.
-See the [v19 API reflection](docs/topics/api/), the
+See the [v19 API vocabulary checkpoint](docs/topics/api/), the
[v19 migration guide](docs/migrations/v19/), and
[Optic reads](docs/topics/optic-reads.md) for the model.
diff --git a/bin/cli/commands/trust.ts b/bin/cli/commands/trust.ts
index f835c631..17b0a21f 100644
--- a/bin/cli/commands/trust.ts
+++ b/bin/cli/commands/trust.ts
@@ -11,10 +11,10 @@ import { EXIT_CODES, parseCommandArgs, getEnvVar } from '../infrastructure.ts';
import { trustSchema } from '../schemas.ts';
import { createPersistence, resolveGraphName } from '../shared.ts';
import defaultCodec from '../../../src/infrastructure/codecs/CborCodec.ts';
+import { DEFAULT_COMMIT_MESSAGE_CODEC } from '../../../src/infrastructure/adapters/TrailerCommitMessageCodecAdapter.ts';
import AuditVerifierService from '../../../src/domain/services/audit/AuditVerifierService.ts';
import TrustCryptoAdapter from '../../../src/infrastructure/adapters/TrustCryptoAdapter.ts';
import WebCryptoAdapter from '../../../src/infrastructure/adapters/WebCryptoAdapter.ts';
-import type { CorePersistence } from '../../../src/domain/types/WarpPersistence.ts';
import type { CliOptions, Persistence } from '../types.ts';
const TRUST_OPTIONS = {
@@ -53,11 +53,16 @@ async function discoverWriterIds(persistence: Persistence, graphName: string): P
/** Handles the `git warp trust` command: evaluates writer trust against signed evidence. */
export default async function handleTrust({ options, args }: { options: CliOptions; args: string[] }): Promise<{ payload: unknown; exitCode: number }> {
const { mode, trustPin } = parseTrustArgs(args);
- const { persistence, createTrustChain } = await createPersistence(options.repo);
+ const { persistence, runtimeStorage, createTrustChain } = await createPersistence(options.repo);
const graphName = await resolveGraphName(persistence, options.graph);
+ const storage = await runtimeStorage.createRuntimeStorageServices({
+ timelineName: graphName,
+ codec: defaultCodec,
+ commitMessageCodec: DEFAULT_COMMIT_MESSAGE_CODEC,
+ });
const trustChain = createTrustChain(new WebCryptoAdapter());
const verifier = new AuditVerifierService({
- persistence: persistence as unknown as CorePersistence,
+ auditLog: storage.auditLog,
codec: defaultCodec,
trustChain,
trustCrypto: new TrustCryptoAdapter(),
diff --git a/bin/cli/commands/verify-audit.ts b/bin/cli/commands/verify-audit.ts
index 58202ecc..316effc2 100644
--- a/bin/cli/commands/verify-audit.ts
+++ b/bin/cli/commands/verify-audit.ts
@@ -1,6 +1,6 @@
import AuditVerifierService from '../../../src/domain/services/audit/AuditVerifierService.ts';
import defaultCodec from '../../../src/infrastructure/codecs/CborCodec.ts';
-import type { CorePersistence } from '../../../src/domain/types/WarpPersistence.ts';
+import { DEFAULT_COMMIT_MESSAGE_CODEC } from '../../../src/infrastructure/adapters/TrailerCommitMessageCodecAdapter.ts';
import { EXIT_CODES, parseCommandArgs, getEnvVar } from '../infrastructure.ts';
import { verifyAuditSchema } from '../schemas.ts';
import { createPersistence, resolveGraphName } from '../shared.ts';
@@ -47,10 +47,15 @@ export function parseVerifyAuditArgs(args: string[]): { since: string | undefine
/** Handles the verify-audit command: verifies audit receipt chain integrity. */
export default async function handleVerifyAudit({ options, args }: { options: CliOptions; args: string[] }): Promise<{ payload: unknown; exitCode: number }> {
const { since, writerFilter, trustMode, trustPin } = parseVerifyAuditArgs(args);
- const { persistence } = await createPersistence(options.repo);
+ const { persistence, runtimeStorage } = await createPersistence(options.repo);
const graphName = await resolveGraphName(persistence, options.graph);
+ const storage = await runtimeStorage.createRuntimeStorageServices({
+ timelineName: graphName,
+ codec: defaultCodec,
+ commitMessageCodec: DEFAULT_COMMIT_MESSAGE_CODEC,
+ });
const verifier = new AuditVerifierService({
- persistence: persistence as unknown as CorePersistence,
+ auditLog: storage.auditLog,
codec: defaultCodec,
});
diff --git a/docs/migrations/v19/README.md b/docs/migrations/v19/README.md
index 8f876ce1..1e5b6803 100644
--- a/docs/migrations/v19/README.md
+++ b/docs/migrations/v19/README.md
@@ -1,5 +1,12 @@
# v19 Public API Migration Plan
+> **Transitional implementation note:** This guide describes the first v19
+> timeline facade that currently exists in source. The accepted
+> [v19 public vocabulary checkpoint](../../topics/api/) supersedes it as the
+> release target. Do not treat this guide as final v19 migration evidence until
+> it is rewritten for Runtime, Lane, Observer, Observation, Reading, and
+> Receipt.
+
This migration plan covers consumers moving from v18 or earlier public
surfaces to the v19 public API. It is intentionally explicit because
v19 is a major-version boundary: root exports become small and application
diff --git a/docs/topics/README.md b/docs/topics/README.md
index 9943834a..79dea4de 100644
--- a/docs/topics/README.md
+++ b/docs/topics/README.md
@@ -16,8 +16,8 @@ replay-backed. Operator workflows live outside the topic shelf in
- [Getting started](getting-started.md): install the package, open a worldline,
write a patch, read it back, and sync WARP refs.
-- [v19 public API reflection](api/): understand the planned root API shift to
- intents, readings, timelines, ticks, and receipts.
+- [v19 public vocabulary checkpoint](api/): follow the accepted Runtime, Lane,
+ Intent, Observer, Observation, Reading, and Receipt contract.
- [Querying](querying.md): choose between worldlines, observers, optic reads,
query builders, and strand sources.
diff --git a/docs/topics/api/README.md b/docs/topics/api/README.md
index 97709935..980496d7 100644
--- a/docs/topics/api/README.md
+++ b/docs/topics/api/README.md
@@ -1,481 +1,575 @@
-# v19 Public API Reflection
+# v19 Public Vocabulary Checkpoint
-This note records the implemented v19 public API line. The target is
-not to make `git-warp` less precise. The target is to stop making first-use
-application code learn substrate language before it can do useful work.
+> **Status:** Accepted target for `v19.0.0`.
+>
+> This document is the normative product vocabulary and public-surface design.
+> It is not implementation evidence. Until the Runtime, Lane, Observer, and
+> Observation contracts described here are implemented and covered by boundary
+> tests, the currently exported timeline facade remains transitional.
-The slogan is:
+The product doctrine is:
```text
-Write intents. Read timelines. Keep receipts.
+Write intents. Observe lanes. Keep receipts.
```
-That is the public story. The architecture underneath can keep its exact WARP
-terms, but the package boundary should be small, boring, and difficult to misuse.
+That sentence is also the API test. Ordinary application work must not require
+users to learn Git objects, refs, CAS retention, graph materialization, patches,
+worldlines, braids, or holograms.
-## Why Change
+## Canonical Grammar
-The v18 public story is already moving in the right direction. It describes
-`git-warp` as causal history first, with bounded reads, append-only writes,
-provenance, and deterministic multi-writer behavior.
+The public programming model is:
-The problem is that the first-use API still makes users meet too much of the
-implementation too early:
+```text
+A Runtime owns access to causal history.
+A Lane is one admitted or counterfactual line of causal progression.
+An Intent proposes a write to a Lane.
+An Observer runs against a Lane.
+That execution is an Observation.
+An Observation emits Readings and leaves a Receipt.
+```
-- `openWarpWorldline()`;
-- `GitGraphAdapter`;
-- `commit((patch) => ...)`;
-- `coordinate()`;
-- `optic()`;
-- `openWarpGraph()`;
-- graph-shaped entities and operation builders exported from root.
+The four observation nouns are not aliases:
-Those names are not all wrong. Many are accurate. The issue is that they are
-not the best package boundary for application authors, agents, and higher-level
-runtimes such as XYPH and Continuum.
+| Noun | Responsibility |
+| --- | --- |
+| `Observer` | Reusable immutable executable observation plan |
+| `Observation` | One resource-bounded execution of an Observer against a Lane |
+| `Reading` | One bounded semantic result emitted by an Observation |
+| `Receipt` | Durable terminal record of an operation |
-The root API should say what users are doing:
+The canonical sentence is:
-- write an intent;
-- read a timeline;
-- inspect a receipt.
+> An Observer runs against a Lane, producing an Observation that emits
+> Readings and leaves a Receipt.
-The root API should not make users think in graph substrate terms.
+## Disclosure Order
-## Public Vocabulary
+Documentation, generated APIs, CLI help, and MCP descriptions must disclose
+concepts in this order:
-The preferred v19 root vocabulary is:
+### Beginner
```text
-Warp
-Timeline
-Intent
-Reading
-Tick
-Receipt
-ReceiptOutcome
+Runtime -> Lane -> Intent / Observation -> Receipt
```
-`Reader` and `Writer` remain useful role or port names, but they should not be
-the headline README characters. Most users should not need to create a reader
-and writer before they understand the simpler timeline model.
-
-The preferred first-use imports are:
-
-```typescript
-import { openWarp, intent, reading } from '@git-stunts/git-warp';
-```
+### Application Author
-Storage constructors belong behind an explicit storage subpath:
-
-```typescript
-import { GitStorage } from '@git-stunts/git-warp/storage';
+```text
+Observer -> Optic -> Reading -> Support
```
-Advanced WARP vocabulary remains available where it is genuinely the right
-tool:
+### Formal And Diagnostic
```text
-Optic
-Coordinate
-Witness
+Coordinate -> Witness -> Hologram -> Braid -> Transport
```
-Worldline, Strand, Braid, Hologram, Observer, and Continuum families remain
-internal. They are not moved to `advanced` merely because they left root.
+This is a dependency discipline, not merely a documentation preference. A
+shallower operation must not require a deeper-layer noun unless that operation
+actually needs the deeper concept.
-## Root Shape
+## Package Root
-The v19 quick-start shape should look like this:
+The only root runtime value is `Runtime`:
```typescript
-import { openWarp, intent, reading } from '@git-stunts/git-warp';
-import { GitStorage } from '@git-stunts/git-warp/storage';
+import { Runtime } from '@git-stunts/git-warp';
-const storage = await GitStorage.open({ cwd: '.' });
-const warp = await openWarp({
- storage,
+const runtime = await Runtime.open({
+ at: '.',
writer: 'agent-1',
});
+```
-const timeline = await warp.timeline('events');
+The root may export the TypeScript types needed to describe the core contract,
+but it must not export competing factories, adapters, graph substrate, or
+generic intent and observer registries as runtime values.
-await timeline.write(
- intent.node.add({
- subject: 'user:alice',
- })
-);
+`Runtime.open()` is the production composition root. It constructs and owns the
+default history and artifact adapters internally. Application code does not
+construct Git plumbing, git-cas, cache, retention, or graph persistence
+adapters.
-const write = await timeline.write(
- intent.property.set({
- subject: 'user:alice',
- key: 'role',
- value: 'admin',
- })
-);
+Internal architecture keeps those concerns behind semantic ports such as
+`HistoryPort` and `ArtifactPort`. Test-specific dependency injection belongs on
+the testing surface, not in the first-use constructor.
-switch (write.outcome) {
- case 'accepted':
- console.log(write.evidence?.basis.id);
- break;
- case 'obstructed':
- case 'conflicted':
- case 'underdetermined':
- case 'rejected':
- console.log(write.reason ?? write.outcome);
- break;
-}
+### Lifecycle
-const role = await timeline.read(
- reading.property({
- subject: 'user:alice',
- key: 'role',
- })
-);
+The introductory contract for `Runtime.close()` is exactly:
-if (role.receipt.outcome === 'accepted') {
- console.log(role.value);
- console.log(role.receipt.evidence);
-} else {
- console.log(role.receipt.reason, role.receipt.repairHints);
-}
+> Releases local resources only.
+
+In particular, `close()` does not delete lanes, rewrite history, revoke
+receipts, remove retained artifacts, or perform causal admission. It stops new
+local work, allows already admitted operations to reach a defined terminal
+state, and releases process-owned resources. Repeated close requests are
+idempotent.
+
+## Lanes
+
+`Lane` replaces `Timeline` in public vocabulary. A timeline suggests a total
+order. A lane permits independent progression, concurrency, strands, and
+worldlines without claiming that all history is globally linear.
+
+A lane has exactly one kind:
+
+```typescript
+type LaneDescriptor =
+ | {
+ kind: 'worldline';
+ name: string;
+ }
+ | {
+ kind: 'strand';
+ name: string;
+ parent: LaneReference;
+ forkedAt: CoordinateReference;
+ };
```
-The read path should return a result object, not a naked value. Provenance is
-the normal path, not an afterthought:
+The implementation must use runtime-backed lane classes or descriptors that
+enforce this invariant. It must not model kind using overlapping booleans such
+as `speculative`, `draft`, or `canonical`. A "speculative worldline" is not a
+valid state.
+
+Single-lane operations live on `Lane`. Cross-lane operations live on `Runtime`.
```typescript
-const result = await timeline.read(
- reading.property({
+const events = await runtime.lane('events');
+const draft = await runtime.fork(events, { name: 'try-admin-role' });
+```
+
+## Intents And Generated SDKs
+
+Application authors should normally use Wesley-generated domain SDKs:
+
+```typescript
+import { users } from './generated/users.js';
+
+const receipt = await events.write(
+ users.intents.assignRole({
subject: 'user:alice',
- key: 'role',
+ role: 'admin',
})
);
-
-result.value;
-result.receipt;
```
-A convenience method such as `readValue()` can exist, but it should be clearly
-documented as the provenance-light path.
+Generated builders return validated runtime-backed `Intent` values. They do
+not return loose JSON envelopes that rely on conventions for correctness.
+
+Generic and graph-shaped builders may exist on explicit expert surfaces for
+migration, tooling, and schema authorship. They are not the root tutorial.
-## Opaque Evidence
+## Observers And Optics
-Receipts expose storage-neutral evidence handles:
+Generated application code names reusable plans as observers:
```typescript
-receipt.evidence?.basis.id;
-receipt.evidence?.support.map((handle) => handle.id);
-receipt.evidence?.tick;
+const observer = users.observers.roleOf({
+ subject: 'user:alice',
+});
```
-`basis` identifies the causal basis used for the operation. `support` contains
-opaque handles for the supporting objects; the same support has the same ID
-across write, preview, join, and read receipts, so callers can correlate it
-without learning a substrate identifier. A historical read also carries the
-exact public `Tick` used for that read. Live reads do not invent a historical
-tick.
+An Observer contains or references the formal machinery needed to execute the
+request honestly:
+
+```text
+Generated Observer
+ contains an Optic
+ declares an Aperture and bounds
+ requires Capabilities
+ obeys a Law
+```
-Evidence IDs are deliberately opaque. Do not parse them. Operator code that
-needs exact substrate provenance must call
-`inspectReceipt(receipt, { storage })` from the diagnostics subpath with the
-same storage handle that issued the receipt.
+Application code asks to observe a role. It does not need to assemble the
+optic, aperture, support plan, retention contract, and capability declaration
+manually. `Optic` remains public for advanced composition, diagnostics, and
+generated-code infrastructure without becoming first-use vocabulary.
-## Intent Builders
+## Observation Execution
-Generic intent envelopes are useful, but they should not be the only happy
-path. This is too easy to turn into typed JSON by convention:
+`Lane.observe()` is synchronous. It performs only local construction and
+invariant validation, then returns a dormant `Observation`:
```typescript
-intent({
- type: 'user.role.assign',
- subject: 'user:alice',
- payload: { role: 'admin' },
-});
+const observation = events.observe(observer);
```
-The root should ship semantic builders for common causal writes:
+An Observation starts when any of the following first demands execution:
-```typescript
-intent.property.set({
- subject: 'user:alice',
- key: 'role',
- value: 'admin',
-});
+1. Its async iterator is advanced.
+2. A lawful convenience consumer is invoked.
+3. Its `receipt` is awaited.
-intent.node.add({
- subject: 'user:alice',
-});
+All three paths join the same execution. They must never start duplicate
+runtime work.
-intent.edge.add({
- from: 'user:alice',
- to: 'team:ops',
- label: 'memberOf',
-});
-```
+The first demand also selects Reading delivery:
-Domain-specific builders can be layered by application code, but the root API
-should keep returning runtime-backed `Intent` values:
+- iteration or a convenience consumer becomes the sole Reading consumer;
+- awaiting `receipt` first selects drain-and-discard delivery;
+- awaiting `receipt` after a Reading consumer starts waits for that consumer's
+ execution and does not steal or duplicate Readings;
+- attempting to attach a Reading consumer after drain-and-discard begins is a
+ typed local lifecycle error.
```typescript
-function assignRole(subject: string, role: string) {
- return intent.property.set({
- subject,
- key: 'role',
- value: role,
- });
+const observation = events.observe(observer);
+
+for await (const reading of observation) {
+ console.log(reading.value);
}
-await timeline.write(assignRole('user:alice', 'admin'));
+const receipt = await observation.receipt;
```
-The builder output should be a runtime-backed value, not loose shape trust.
+Awaiting `observation.receipt` without an existing Reading consumer starts the
+operation and drains its readings with backpressure while discarding their
+values. It must not collect the stream in memory.
-## Readings And Optics
+If no consumer advances the iterator, invokes a convenience consumer, or
+awaits the receipt, the Observation remains dormant and owns no active runtime
+operation.
-The public API should lead with `reading`.
+An Observation represents one causal operation, not a generic JavaScript
+stream wrapper. Its contract owns:
-```typescript
-await timeline.read(
- reading.property({
- subject: 'user:alice',
- key: 'role',
- })
-);
-```
+- one execution identity;
+- one declared or pinned basis;
+- capability scope;
+- resource budget;
+- single-consumer reading delivery;
+- cancellation and early-termination policy;
+- terminal outcome;
+- receipt production.
-The formal model remains:
+Operational uncertainty terminates through the receipt outcome. Immediate
+throws are reserved for invalid local construction, corruption, violated
+invariants, and implementation defects.
-```text
-A Reading is the bounded question.
-An Optic is the formal execution and proof shape.
+### Convenience Consumers
+
+Convenience consumers are capability-specific, not universal decorations on
+every Observation:
+
+```typescript
+await observation.one();
+await observation.first();
+await observation.all();
```
-In v18, users often need to call `prepareOpticBasis()`, capture a coordinate,
-and read through `optic()`. That discipline is correct, but it should be
-internal ceremony for the first-use path.
+Their semantics are strict:
-The v19 `timeline.read(reading)` flow is the public shape. Advanced bounded
-read paths can still lower readings to optics internally:
+- `one()` proves that exactly one Reading was emitted. It is not an alias for
+ the first available result.
+- `first()` is exposed only when the Observer law permits witnessed early
+ termination.
+- `all()` is exposed only for finite Observers with a declared collection
+ bound and budget.
-1. validate the `Reading`;
-2. lower `Reading` to `Optic`;
-3. check an optic basis without broad materialization;
-4. capture the observer position;
-5. execute the bounded read;
-6. return `ReadingResult` and `ReadReceipt`.
+The base v19 Observation contract does not promise all three methods. The
+runtime and generated SDK expose only the consumers whose termination and
+cardinality semantics they can represent honestly. Cardinality or budget
+failure is typed operational failure and is recorded in the terminal Receipt.
-Operational uncertainty should return receipt outcomes where possible.
-Programmer errors, corruption, impossible states, and violated invariants may
-still throw typed errors.
+Stopping ordinary async iteration early follows the Observer's declared
+cancellation policy. It must not silently turn a partial reading set into a
+successful complete Observation.
-## Receipts As Control Flow
+## Readings
-Receipts should become the normal control-flow object.
+The canonical application property is `Reading.value`:
```typescript
-const receipt = await timeline.write(assignRole('user:alice', 'admin'));
-
-switch (receipt.outcome) {
- case 'accepted':
- console.log(receipt.evidence?.basis.id);
- break;
- case 'obstructed':
- case 'conflicted':
- case 'underdetermined':
- case 'rejected':
- console.log(receipt.reason ?? receipt.outcome);
- break;
+for await (const reading of observation) {
+ consume(reading.value);
}
```
-The receipt outcome axis should stay clean:
+`payload` is reserved for encoded transport envelopes and adapter-level bytes.
+A public Reading is a typed semantic result, not a packet.
-```text
-accepted
-obstructed
-conflicted
-underdetermined
-rejected
+Conceptually, a Reading carries:
+
+```typescript
+interface Reading