This is the builder's guide.
Use it when you are writing an app, an agent workflow, or a local-first tool on top of git-warp and you want the main patterns without reading a substrate manual.
- If you are brand new, start with Getting Started.
- If you want exhaustive method-by-method detail, use the API Reference.
- If you want replay, trust, performance, and substrate internals, use the Advanced Guide.
- If you want terminal workflows, use the CLI Guide.
The most important thing to understand is state before methods.
WarpAppis the root you build on.- A
Worldlineis a pinned read coordinate. - An
Aperturedefines what is visible. - An
Observeris a filtered read-only view through that aperture. - A
Strandis a speculative write lane branched from an observation.
If you understand those nouns, the rest of the API becomes much easier to reason about.
import GitPlumbing from '@git-stunts/plumbing';
import WarpApp, { GitGraphAdapter } from '@git-stunts/git-warp';
const plumbing = new GitPlumbing({ cwd: './team-repo' });
const persistence = new GitGraphAdapter({ plumbing });
const app = await WarpApp.open({
persistence,
graphName: 'team',
writerId: 'alice',
});
// app is the root handle for this graphUse app.patch(...) for normal live writes.
const patchSha = await app.patch((p) => {
p.addNode('task:auth')
.setProperty('task:auth', 'title', 'Implement OAuth2')
.setProperty('task:auth', 'status', 'in-progress');
});
// patchSha = 'abc123...'This commits one atomic WARP patch after the callback finishes. It updates refs/warp/<graph>/writers/<writerId>. It does not touch your normal Git worktree or create a source-tree commit on the current branch.
Use the writer API when you want a multi-step session before committing.
const writer = await app.writer();
const session = await writer.beginPatch();
session.addNode('task:review');
session.setProperty('task:review', 'status', 'todo');
session.addEdge('task:review', 'task:auth', 'depends-on');
const patchSha = await session.commit();
// patchSha = 'def456...'Nothing is written until session.commit() runs.
Use a Strand when you want reviewable or transferable work that should not land in live truth yet.
const strand = await app.createStrand({
strandId: 'review-auth',
owner: 'alice',
scope: 'OAuth review',
});
// strand = { strandId: 'review-auth', ... }
await app.patchStrand('review-auth', (p) => {
p.setProperty('task:auth', 'status', 'ready-for-review');
});
const reviewLane = app.worldline({
source: { kind: 'strand', strandId: 'review-auth' },
});
// reviewLane is a Worldline pinned to the strand overlayUse strands for speculative work. Use ordinary patches for live truth.
For the deeper substrate story behind strands, braids, and transfer planning, use Advanced Guide → Strands and braids.
Start from a worldline when you want stable application reads.
const worldline = app.worldline();
const task = await worldline.getNodeProps('task:auth');
// { title: 'Implement OAuth2', status: 'in-progress' }Add an observer when the caller should not see everything.
const userAperture = {
match: ['user:*', 'task:*'],
redact: ['email', 'ssn'],
};
const view = await worldline.observer('public-users', userAperture);
const users = await view.query().match('user:*').run();
// users = {
// stateHash: 'abc123...',
// nodes: [
// { id: 'user:alice', props: { name: 'Alice', role: 'lead' } },
// ],
// }Pin an explicit coordinate when you need to ask what the graph looked like earlier.
const historical = app.worldline({
source: {
kind: 'coordinate',
frontier: { alice: 'patch-tip-sha' },
ceiling: 12,
},
});
const taskAtTick12 = await historical.getNodeProps('task:auth');
// { title: 'Implement OAuth2', status: 'todo' }Read a strand through the same worldline abstraction you use for live truth.
const reviewLane = app.worldline({
source: { kind: 'strand', strandId: 'review-auth' },
});
const reviewTask = await reviewLane.getNodeProps('task:auth');
// { title: 'Implement OAuth2', status: 'ready-for-review' }Use one canonical graph example when you are learning the query and traversal surface:
flowchart TD
epic["epic:auth"]
task1["task:oauth<br />status=in-progress<br />points=5"]
task2["task:review<br />status=todo<br />points=2"]
task3["task:docs<br />status=todo<br />points=1"]
user1["user:alice"]
user2["user:bob"]
epic -->|"contains"| task1
epic -->|"contains"| task2
epic -->|"contains"| task3
task1 -->|"assigned-to"| user2
task2 -->|"assigned-to"| user1
task2 -->|"depends-on"| task1
task3 -->|"depends-on"| task2
const tasks = await worldline.query()
.match('task:*')
.run();
// tasks = {
// stateHash: 'abc123...',
// nodes: [
// { id: 'task:oauth', props: { status: 'in-progress', points: 5 } },
// { id: 'task:review', props: { status: 'todo', points: 2 } },
// { id: 'task:docs', props: { status: 'todo', points: 1 } },
// ],
// }const downstream = await worldline.query()
.match('epic:auth')
.outgoing('contains', { depth: [1, 2] })
.run();
// downstream = {
// stateHash: 'abc123...',
// nodes: [
// { id: 'task:oauth', props: { status: 'in-progress', points: 5 } },
// { id: 'task:review', props: { status: 'todo', points: 2 } },
// { id: 'task:docs', props: { status: 'todo', points: 1 } },
// { id: 'user:bob', props: {} },
// { id: 'user:alice', props: {} },
// ],
// }const summary = await worldline.query()
.match('task:*')
.where({ status: 'todo' })
.aggregate({ count: true, sum: 'props.points', avg: 'props.points' })
.run();
// summary = {
// stateHash: 'abc123...',
// count: 2,
// sum: 3,
// avg: 1.5,
// }const dependencyPath = await worldline.traverse.shortestPath('task:docs', 'task:oauth', {
dir: 'out',
labelFilter: 'depends-on',
});
// dependencyPath = {
// found: true,
// path: ['task:docs', 'task:review', 'task:oauth'],
// length: 2,
// }For the exhaustive query surface, use the API Reference.
The simplest sync is Git push and pull plus the WARP refspecs for your graph:
git fetch origin 'refs/warp/team/*:refs/warp/team/*'
git push origin 'refs/warp/team/*:refs/warp/team/*'Automate those refspecs in team tooling or Git config once the workflow is established.
The easiest way to understand CRDT behavior is to look at outcomes, not theory.
| Alice writes | Bob writes | Outcome |
|---|---|---|
add node task:auth |
add node task:auth |
one visible node; duplicate adds converge |
set task:auth.status = "todo" |
set task:auth.status = "done" |
one winning value by Lamport order, then writer tie-break |
add edge task:review -> task:auth |
remove same edge without seeing add | concurrent add wins |
| remove node after observing current edge set | set property on removed node concurrently | tombstoned node stays hidden in live view |
The inspection APIs are valid tools here. What you should avoid is rebuilding your own graph engine above git-warp when the substrate already knows how to replay, query, and traverse.
If you know a write was superseded and need the reason, inspect receipts through WarpCore instead of guessing from app state.
const { receipts } = await app.core().materialize({ receipts: true });
const supersededOps = receipts.flatMap((receipt) =>
receipt.ops
.filter((op) => op.result === 'superseded')
.map((op) => ({
patchSha: receipt.patchSha,
lamport: receipt.lamport,
writer: receipt.writer,
target: op.target,
reason: op.reason ?? 'superseded by deterministic replay order',
})),
);
// supersededOps = [
// {
// patchSha: 'abc123...',
// lamport: 14,
// writer: 'alice',
// target: 'task:auth',
// reason: 'superseded by deterministic replay order',
// },
// ]Use this pattern when you need to explain a lost race or build higher-level conflict UX. For the deeper replay and provenance model behind receipts, use Advanced Guide → How replay converges.
Reach for app.core() when you intentionally need:
- whole-visible-state inspection
- materialization and replay receipts
- provenance and patch inspection
- coordinate comparison and transfer planning
- debugger or operator tooling
What to avoid is not the inspection API itself. The thing to avoid is exporting that data into a second app-local graph or writing your own traversal/query semantics above the substrate.
- API Reference: exhaustive methods, flags, and examples
- Advanced Guide: patch anatomy, replay, trust, GC, and performance
- CLI Guide: operator workflows and live-repo inspection
- Conceptual Overview: the WARP mental model and Git substrate story