Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 53 additions & 77 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@ If you are learning the product for the first time, start with:

## Release posture

`v18.2.1` is the current release target. Architecturally, it keeps the v18
read-model closeout from the current `main` release state while correcting the
state-cache materialization path: `git-warp` owns frontier coordinates,
snapshot eligibility, replay, and publication policy; `git-cas` remains the
storage substrate. This patch reduces redundant live replay through
coordinate-addressed snapshots, but it does not make full materialization a
bounded optic read.
`v18.2.1` is the current published release. Mainline development is preparing
the v19 application boundary: callers open an opaque storage handle, write
intents, read timelines, and keep receipts. Git history and git-cas remain
separate infrastructure concerns composed behind that handle.

The longer release notes live in [CHANGELOG.md](CHANGELOG.md). The runtime
architecture below describes current implementation boundaries, not aspirational
Expand All @@ -26,34 +23,24 @@ roadmap state.

```text
┌──────────────────────────────────────────────────┐
│ openWarpWorldline() / openWarpGraph() │
│ app worldline handle / advanced capability bag │
├──────────┬───────────┬───────────┬───────────────┤
│ openWarp() -> Warp -> Timeline │
│ intent · reading · tick · receipt │
├──────────────────────────────────────────────────┤
│ Opaque storage composition │
│ GitStorage.open() · MemoryStorage.create() │
├──────────┬───────────┬────────────┬──────────────┤
│ Query │ Patch │ Materialize│ Sync │
│Controller│Controller │ Controller │ Controller │
│ │ │ │ │
│ Strand │Checkpoint │ Provenance │ Comparison │
│Controller│Controller │ Controller │ Engine │
├──────────┴───────────┴───────────┴───────────────┤
│ Domain Services │
│ JoinReducer · OpStrategy · Frontier · GCPolicy │
│ StrandCoordinator · ConflictAnalyzer · BTR │
│ StateHashService · MaterializedViewService │
├──────────────────────────────────────────────────┤
│ Ports │
│ GraphPersistencePort · BlobPort · TreePort │
│ CommitPort · RefPort · CodecPort · CryptoPort │
│ ClockPort · LoggerPort · SeekCachePort │
├──────────────────────────────────────────────────┤
│ Infrastructure Adapters │
│ GitGraphAdapter · InMemoryGraphAdapter │
│ CborCodec · NodeCrypto · WebCrypto · ClockAdapter│
│ CasBlobAdapter · CasSeekCacheAdapter │
├──────────────────────────────────────────────────┤
│ Git substrate │
│ @git-stunts/plumbing · @git-stunts/git-cas │
│ @git-stunts/alfred · @git-stunts/trailer-codec │
└──────────────────────────────────────────────────┘
├──────────┴───────────┴────────────┴──────────────┤
│ Domain services and semantic storage ports │
│ CorePersistence · RuntimeStorageProviderPort │
├───────────────────────┬──────────────────────────┤
│ GitTimelineHistory │ GitCasRepositoryAdapter │
│ Adapter │ content/cache/retention │
├───────────────────────┼──────────────────────────┤
│ @git-stunts/plumbing │ @git-stunts/git-cas │
└───────────────────────┴──────────────────────────┘
```

## Architectural principles
Expand All @@ -72,10 +59,9 @@ The system decomposes into three moments:
- **Folding** — re-expresses admitted history (checkpoints, materialization)
- **Revelation** — exposes truth under bounded rights (queries, observers)

`openWarpWorldline()` gives application code the first-use handle over one
named admitted causal lane. `openWarpGraph()` returns the advanced frozen
capability bag organized by these moments, plus governance (sync) for
distributed admission.
`openWarp()` gives application code a `Warp` handle. `warp.timeline(name)`
opens one named admitted causal lane without exposing the internal worldline,
graph, persistence, or CAS vocabulary.

### Graph-shaped readings

Expand Down Expand Up @@ -123,51 +109,40 @@ Full standard: [Systems-Style TypeScript](docs/SYSTEMS_STYLE_TYPESCRIPT.md).

## Public API surface

### `openWarpWorldline()` (v18+)
### `openWarp()`

The recommended application entry point. Returns a frozen Worldline-first
handle:
The package root accepts an opaque `WarpStorage` and returns a frozen `Warp`:

```text
const team = await openWarpWorldline({ persistence, worldlineName, writerId });
```typescript
import { openWarp } from '@git-stunts/git-warp';
import { GitStorage } from '@git-stunts/git-warp/storage';

team.commit(...) // commitment: write one atomic patch
team.live() // revelation: current admitted worldline
team.seek(...) // revelation: pinned coordinate read
team.observer(...) // revelation: bounded aperture
team.optic() // revelation: bounded optic question
const storage = await GitStorage.open({ cwd: '.' });
const warp = await openWarp({ storage, writer: 'agent-1' });
const team = await warp.timeline('team');
```

### `openWarpGraph()` (compatibility and diagnostics)
Application code writes with `timeline.write(intent)` and reads with
`timeline.read(reading)`. Formal coordinate reads and receipt inspection live
on the explicit `advanced` and `diagnostics` subpaths.

The advanced compatibility entry point. Returns a frozen capability bag for
tooling, diagnostics, and graph-first integrations that intentionally need the
lower-level surface:
### Storage composition

The flat aliases are canonical for user-facing examples. Moment-scoped names
are available for explicit architecture code and point at the same objects:
`graph.patches === graph.commitment.patches`,
`graph.query === graph.revelation.query`, and
`graph.checkpoint === graph.folding.checkpoint`.
`GitStorage` is the package composition root, not a Git-shaped persistence
port. Its runtime storage boundary owns two sibling adapters:

```text
const graph = await openWarpGraph({ persistence, graphName, writerId });

graph.query.* // revelation: read state
graph.patches.* // commitment: write patches
graph.sync.* // governance: distributed sync
graph.strands.* // commitment: speculative lanes
graph.checkpoint.* // folding: history folding
graph.provenance.* // revelation: witness access
graph.comparison.* // commitment: braid comparison
graph.subscriptions.* // revelation: reactive state
```
- `GitTimelineHistoryAdapter` implements append-only causal history through
`@git-stunts/plumbing`.
- `GitCasRepositoryAdapter` owns one repository-scoped git-cas facade and
supplies semantic content, patch-journal, checkpoint, index, state, seek,
trie, and trust storage services.

### `WarpApp` / `WarpCore` (legacy, v16 compat)
The same composition also supplies repository tooling, such as hook-path
resolution, through narrow ports backed by the same plumbing instance.

Still exported for backward compatibility and advanced tooling. New application
code should prefer `openWarpWorldline()`, and lower-level tooling should prefer
`openWarpGraph()` unless it deliberately needs the legacy facade shape.
The domain receives `CorePersistence` and `RuntimeStorageProviderPort`. It does
not inspect plumbing fields, dynamically import adapters, or construct CAS
services.

## Internal engine

Expand Down Expand Up @@ -240,7 +215,9 @@ Abstract contracts between domain and infrastructure:

Concrete implementations of ports:

- **GitGraphAdapter** — Git plumbing commands via @git-stunts/plumbing
- **GitTimelineHistoryAdapter** — timeline-history Git commands via
@git-stunts/plumbing
- **GitCasRepositoryAdapter** — repository-scoped git-cas service composition
- **InMemoryGraphAdapter** — in-memory Maps for testing
- **CborCodec** — CBOR encoding via cbor-x
- **NodeCryptoAdapter / WebCryptoAdapter** — hash/sign via node:crypto or SubtleCrypto
Expand All @@ -266,8 +243,7 @@ refs/warp/events/checkpoint → checkpoint-sha
refs/warp/events/coverage → coverage-sha
```

Reads open a live, pinned, observer, or optic basis over those refs. Diagnostic
materialization walks visible writer chains, applies patches through
`JoinReducer` (CRDT merge), and produces a frozen `WarpState`; application code
should prefer worldlines, observers, query builders, and optics before whole
graph replay.
Reads open a bounded timeline basis over those refs. Diagnostic materialization
walks visible writer chains, applies patches through `JoinReducer` (CRDT merge),
and produces a frozen `WarpState`; application code should prefer readings and
receipts before whole-graph replay.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,11 @@ compatibility API.

```typescript
import { openWarp, intent, reading } from '@git-stunts/git-warp';
import { GitStorageAdapter } from '@git-stunts/git-warp/storage';
import GitPlumbing from '@git-stunts/plumbing';
import { GitStorage } from '@git-stunts/git-warp/storage';

const plumbing = new GitPlumbing({ cwd: '.' });
const storage = await GitStorage.open({ cwd: '.' });
const warp = await openWarp({
storage: new GitStorageAdapter({ plumbing }),
storage,
writer: 'agent-1',
});

Expand Down Expand Up @@ -400,7 +399,7 @@ Yes — independently, even fully offline. Histories converge deterministically
### How do I install and set it up?

```bash
npm install @git-stunts/git-warp @git-stunts/plumbing
npm install @git-stunts/git-warp
```

Works alongside any existing Git repo. Full setup: [Getting started](docs/topics/getting-started.md).
Expand Down
9 changes: 5 additions & 4 deletions bin/cli/commands/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { buildCheckpointRef, buildCoverageRef } from '../../../src/domain/utils/
import { EXIT_CODES } from '../infrastructure.ts';
import { openGraph, applyCursorCeiling, emitCursorWarning, readCheckpointDate, createHookInstaller } from '../shared.ts';
import type { CliOptions, Persistence, WarpGraphInstance } from '../types.ts';
import type HookPathPort from '../../../src/ports/HookPathPort.ts';

/** Performs a health check on the graph persistence. */
async function getHealth(persistence: Persistence): Promise<{ status: string; components: { repository: { status: string; latencyMs: number }; index: { status: string; loaded: boolean; shardCount?: number } }; cachedAt?: string }> {
Expand Down Expand Up @@ -115,9 +116,9 @@ function buildCheckPayload(input: CheckPayloadInput): Record<string, unknown> {
}

/** Returns the status of WARP git hooks for a repository. */
async function getHookStatusForCheck(repoPath: string): Promise<{ installed: boolean; version?: string; current?: boolean; foreign?: boolean; hookPath: string } | null> {
async function getHookStatusForCheck(repoPath: string, hookPaths: HookPathPort): Promise<{ installed: boolean; version?: string; current?: boolean; foreign?: boolean; hookPath: string } | null> {
try {
const installer = createHookInstaller();
const installer = createHookInstaller(hookPaths);
return await installer.getHookStatus(repoPath);
} catch {
return null;
Expand All @@ -126,7 +127,7 @@ async function getHookStatusForCheck(repoPath: string): Promise<{ installed: boo

/** Handles the `check` command: reports graph health, GC, and hook status. */
export async function handleCheck({ options }: { options: CliOptions }): Promise<{ payload: unknown; exitCode: number }> {
const { graph, graphName, persistence } = await openGraph(options);
const { graph, graphName, persistence, hookPaths } = await openGraph(options);
const cursorInfo = await applyCursorCeiling(graph, persistence, graphName);
emitCursorWarning(cursorInfo, null);
const health = await getHealth(persistence);
Expand All @@ -135,7 +136,7 @@ export async function handleCheck({ options }: { options: CliOptions }): Promise
const writerHeads = await collectWriterHeads(graph);
const checkpoint = await loadCheckpointInfo(persistence, graphName);
const coverage = await loadCoverageInfo(persistence, graphName, writerHeads);
const hook = await getHookStatusForCheck(options.repo);
const hook = await getHookStatusForCheck(options.repo, hookPaths);

return {
payload: buildCheckPayload({
Expand Down
2 changes: 1 addition & 1 deletion bin/cli/commands/doctor/checksAux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function checkClockSkew(ctx: DoctorContext): Promise<DoctorFinding>
/** Check whether the warp post-merge hook is installed and current. */
export async function checkHooksInstalled(ctx: DoctorContext): Promise<DoctorFinding> {
try {
const installer = createHookInstaller();
const installer = createHookInstaller(ctx.hookPaths);
const s = await installer.getHookStatus(ctx.repoPath);
return buildHookFinding(s);
} catch (err) {
Expand Down
6 changes: 3 additions & 3 deletions bin/cli/commands/doctor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ export default async function handleDoctor({ options, args }: { options: CliOpti
const { values } = parseCommandArgs(args, DOCTOR_OPTIONS, doctorSchema);
const commandValues = normalizeCommandValues(values);
const startMs = Date.now();
const { persistence } = await createPersistence(options.repo);
const { persistence, runtimeStorage, hookPaths } = await createPersistence(options.repo);
const graphName = await resolveGraphName(persistence, options.graph);
const policy = { ...DEFAULT_POLICY, strict: commandValues.strict };
const writerHeads = await collectWriterHeads(persistence, graphName);

const stateCache = await resolveStateCache(persistence, graphName);
const ctx: DoctorContext = { persistence, stateCache, graphName, writerHeads, policy, repoPath: options.repo };
const stateCache = await resolveStateCache(runtimeStorage, graphName);
const ctx: DoctorContext = { persistence, stateCache, graphName, writerHeads, policy, repoPath: options.repo, hookPaths };

const memoryFindings = memoryBudgetFindings(commandValues);
const repairFinding = await repairStateCache(commandValues[DOCTOR_OPTION_REPAIR_STATE_CACHE], stateCache);
Expand Down
13 changes: 9 additions & 4 deletions bin/cli/commands/doctor/stateCacheCapability.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type WarpStateCachePort from '../../../../src/ports/WarpStateCachePort.ts';
import type WarpStateCacheRetentionPort from '../../../../src/ports/WarpStateCacheRetentionPort.ts';
import defaultCodec from '../../../../src/infrastructure/codecs/CborCodec.ts';
import type { Persistence } from '../../types.ts';
import { DEFAULT_COMMIT_MESSAGE_CODEC } from '../../../../src/infrastructure/adapters/TrailerCommitMessageCodecAdapter.ts';
import type RuntimeStorageProviderPort from '../../../../src/ports/RuntimeStorageProviderPort.ts';
import type { DoctorFinding } from './types.ts';
import {
stateCacheRepairFailureFinding,
Expand All @@ -11,11 +12,15 @@ import {
export type DoctorStateCache = WarpStateCachePort & WarpStateCacheRetentionPort;

export async function resolveStateCache(
persistence: Persistence,
runtimeStorage: RuntimeStorageProviderPort,
graphName: string,
): Promise<DoctorStateCache | null> {
if (typeof persistence.createRuntimeStateCache !== 'function') { return null; }
return await persistence.createRuntimeStateCache({ graphName, codec: defaultCodec });
const services = await runtimeStorage.createRuntimeStorageServices({
timelineName: graphName,
codec: defaultCodec,
commitMessageCodec: DEFAULT_COMMIT_MESSAGE_CODEC,
});
return services.stateSnapshots ?? null;
}

export async function repairStateCache(
Expand Down
2 changes: 2 additions & 0 deletions bin/cli/commands/doctor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import type { Persistence } from '../../types.ts';
import type WarpStateCachePort from '../../../../src/ports/WarpStateCachePort.ts';
import type WarpStateCacheRetentionPort from '../../../../src/ports/WarpStateCacheRetentionPort.ts';
import type HookPathPort from '../../../../src/ports/HookPathPort.ts';

// ── JSON-safe recursive value type ──────────────────────────────────────────

Expand Down Expand Up @@ -70,6 +71,7 @@ export interface DoctorContext {
writerHeads: Array<{ writerId: string; sha: string | null; ref: string }>;
policy: DoctorPolicy;
repoPath: string;
hookPaths: HookPathPort;
}

export type DoctorCheck = (ctx: DoctorContext) => Promise<DoctorFinding | DoctorFinding[] | null>;
Expand Down
8 changes: 5 additions & 3 deletions bin/cli/commands/info.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import WebCryptoAdapter from '../../../src/infrastructure/adapters/WebCryptoAdapter.ts';
import type { CorePersistence } from '../../../src/domain/types/WarpPersistence.ts';
import { openRuntimeHostProduct } from '../../../src/domain/warp/RuntimeHostProduct.ts';
import type RuntimeStorageProviderPort from '../../../src/ports/RuntimeStorageProviderPort.ts';
import {
buildCheckpointRef,
buildCoverageRef,
Expand All @@ -12,7 +13,7 @@ import { createPersistence, listGraphNames, readActiveCursor, readCheckpointDate
import type { CliOptions, Persistence, GraphInfoResult } from '../types.ts';

/** Collects metadata about a single graph (writer count, refs, patches, checkpoint). */
async function getGraphInfo(persistence: Persistence, graphName: string, {
async function getGraphInfo(persistence: Persistence, runtimeStorage: RuntimeStorageProviderPort, graphName: string, {
includeWriterIds = false,
includeRefs = false,
includeWriterPatches = false,
Expand Down Expand Up @@ -61,6 +62,7 @@ async function getGraphInfo(persistence: Persistence, graphName: string, {
if (includeWriterPatches && writerIds.length > 0) {
const graph = await openRuntimeHostProduct({
persistence: persistence as unknown as CorePersistence,
runtimeStorage,
graphName,
writerId: 'cli',
crypto: new WebCryptoAdapter(),
Expand All @@ -78,7 +80,7 @@ async function getGraphInfo(persistence: Persistence, graphName: string, {

/** Handles the `info` command: summarizes graphs in the repository. */
export default async function handleInfo({ options }: { options: CliOptions }): Promise<{ repo: string; graphs: GraphInfoResult[] }> {
const { persistence } = await createPersistence(options.repo);
const { persistence, runtimeStorage } = await createPersistence(options.repo);
const graphNames = await listGraphNames(persistence);

if (typeof options.graph === 'string' && options.graph.length > 0 && !graphNames.includes(options.graph)) {
Expand All @@ -98,7 +100,7 @@ export default async function handleInfo({ options }: { options: CliOptions }):
const graphs: GraphInfoResult[] = [];
for (const name of graphNames) {
const includeDetails = detailGraphs.has(name);
const info = await getGraphInfo(persistence, name, {
const info = await getGraphInfo(persistence, runtimeStorage, name, {
includeWriterIds: includeDetails || isViewMode,
includeRefs: includeDetails || isViewMode,
includeWriterPatches: isViewMode,
Expand Down
Loading
Loading