driftseal 1.4.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +274 -340
- package/README.zh-CN.md +239 -293
- package/bin/driftseal-mcp.js +269 -62
- package/bin/driftseal.js +5420 -3062
- package/index.js +3 -0
- package/package.json +4 -3
- package/skills/use-driftseal/SKILL.md +22 -13
package/README.md
CHANGED
|
@@ -1,419 +1,353 @@
|
|
|
1
1
|
# DriftSeal
|
|
2
2
|
|
|
3
|
-
> **Seal the
|
|
3
|
+
> **Seal the outcome. Stop the drift.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
DriftSeal is a repository-local protocol and toolchain for keeping coding agents
|
|
6
|
+
anchored to a coherent delivery outcome. It records the outcome before durable
|
|
7
|
+
work begins, permits append-only extensions toward that same outcome, binds
|
|
8
|
+
verification to the accumulated contract, and preserves only the decisions that
|
|
9
|
+
need durable rationale.
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
|
|
12
|
+
begin an outcome → extend the same outcome → verify the cumulative contract → close
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
One worktree owns one open outcome. Git records what landed; DriftSeal records
|
|
16
|
+
what the work was meant to achieve, how completion was proved, and why durable
|
|
17
|
+
decisions were made.
|
|
18
|
+
|
|
19
|
+
## What changed in v2
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
DriftSeal v2 is an outcome log rather than an intent-per-step log.
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
- State lives under one seal root: `.seal/outcomes/events.jsonl` and `.seal/madr/`.
|
|
24
|
+
- `DRIFTSEAL_HOME` overrides the whole v2 `.seal` root. A value inherited from
|
|
25
|
+
v1 still points at an intent-log directory; pass that legacy location to the
|
|
26
|
+
migration command explicitly, then unset or replace the variable.
|
|
27
|
+
- `driftseal extend` appends another step, acceptance criterion, verifier, or
|
|
28
|
+
decision link to the currently open outcome.
|
|
29
|
+
- Every extension changes the contract hash and invalidates earlier verification
|
|
30
|
+
and MADR reconciliation.
|
|
31
|
+
- Stored events use `logVersion: 2`. Compatible clients accept `schemaVersion`
|
|
32
|
+
`1` or `2`; lane events and non-default `begin.lane` use `schemaVersion: 2`.
|
|
33
|
+
- The generated `AGENTS.md` protocol series is `2.1`. `driftseal init` upgrades
|
|
34
|
+
recognized `2.0` blocks.
|
|
35
|
+
- Named lanes partition outcome history on the same WAL. The default lane is
|
|
36
|
+
`main`; `driftseal log` follows the current lane.
|
|
37
|
+
- The public CLI, Node API, MCP tools, and MCP resources use outcome terminology.
|
|
38
|
+
v1 names and storage paths are not runtime aliases.
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
## Install
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
DriftSeal requires Node.js 18 or newer.
|
|
30
43
|
|
|
31
44
|
```sh
|
|
32
45
|
npm install --global driftseal
|
|
33
|
-
|
|
34
|
-
driftseal init
|
|
46
|
+
driftseal --version
|
|
35
47
|
```
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
colliding worktree logs, and configures the local git merge driver. It can be
|
|
39
|
-
run again without duplicating either. Pass `--lang zh-CN` (or another
|
|
40
|
-
[BCP 47](https://www.rfc-editor.org/rfc/rfc5646.html) tag) to declare the
|
|
41
|
-
language agents should use for intent and decision prose; the default is `en`.
|
|
42
|
-
Command names, flags, status tokens, ids, and MADR section headings stay in
|
|
43
|
-
English. Re-running `init` without `--lang` preserves the declared language
|
|
44
|
-
while upgrading the protocol. DriftSeal requires Node.js 18+.
|
|
45
|
-
|
|
46
|
-
For local development from this checkout:
|
|
49
|
+
From a source checkout:
|
|
47
50
|
|
|
48
51
|
```sh
|
|
49
|
-
npm
|
|
52
|
+
npm install
|
|
53
|
+
node bin/driftseal.js --version
|
|
50
54
|
```
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Use `AGENTS.md` + the companion skill + the CLI as the default integration:
|
|
55
|
-
|
|
56
|
-
- `AGENTS.md`, installed by `driftseal init`, is the authoritative policy.
|
|
57
|
-
- `skills/use-driftseal` is a small, agent-agnostic discovery and recovery guide.
|
|
58
|
-
- `driftseal` is the default execution surface.
|
|
59
|
-
|
|
60
|
-
Install the bundled skill for one platform. Project scope is the default:
|
|
56
|
+
Adopt the protocol in a repository:
|
|
61
57
|
|
|
62
58
|
```sh
|
|
63
|
-
driftseal
|
|
64
|
-
driftseal skill install --target kimi-code --scope global
|
|
59
|
+
driftseal init
|
|
65
60
|
```
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| `kimi-code` | `.kimi-code/skills/use-driftseal` | `~/.kimi-code/skills/use-driftseal` |
|
|
71
|
-
| `opencode` | `.opencode/skills/use-driftseal` | `~/.config/opencode/skills/use-driftseal` |
|
|
72
|
-
| `claude-code` | `.claude/skills/use-driftseal` | `~/.claude/skills/use-driftseal` |
|
|
73
|
-
| `cursor` | `.cursor/skills/use-driftseal` | `~/.cursor/skills/use-driftseal` |
|
|
62
|
+
`init` writes or upgrades the managed blocks in `AGENTS.md`, adds the outcome-log
|
|
63
|
+
merge attribute, and configures the local Git merge driver. Run it again in a
|
|
64
|
+
fresh clone because Git config is local to each clone.
|
|
74
65
|
|
|
75
|
-
Use
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
adapters; enable them only for a concrete host constraint or reminder need, not
|
|
80
|
-
as additional policy layers.
|
|
66
|
+
Use `driftseal init --lang <BCP-47-tag>` to choose the prose language stored in
|
|
67
|
+
outcome and MADR records. Use `--local-log` only when `.seal/` should remain
|
|
68
|
+
untracked; DriftSeal reports tracked state but does not edit `.gitignore` or the
|
|
69
|
+
Git index.
|
|
81
70
|
|
|
82
|
-
##
|
|
71
|
+
## Core workflow
|
|
83
72
|
|
|
84
|
-
|
|
85
|
-
structured tools for the complete intent and decision workflow while reusing the
|
|
86
|
-
same locking, WAL, atomic-write, schema, and recovery implementation as the CLI.
|
|
87
|
-
The server never shells out to `driftseal` and does not parse CLI output.
|
|
88
|
-
|
|
89
|
-
Fix the server to one repository when starting it:
|
|
73
|
+
Open the coherent delivery outcome before changing durable project content:
|
|
90
74
|
|
|
91
75
|
```sh
|
|
92
|
-
driftseal
|
|
76
|
+
driftseal begin "Ship account recovery" \
|
|
77
|
+
--accept "expired links are rejected" \
|
|
78
|
+
--accept "a valid link resets the password" \
|
|
79
|
+
--verify "npm test"
|
|
93
80
|
```
|
|
94
81
|
|
|
95
|
-
|
|
96
|
-
supported targets:
|
|
82
|
+
If another step is still part of that same delivered outcome, append it:
|
|
97
83
|
|
|
98
84
|
```sh
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
driftseal mcp install --target opencode
|
|
103
|
-
driftseal mcp install --target claude-code
|
|
104
|
-
driftseal mcp install --target cursor
|
|
85
|
+
driftseal extend "Document recovery-link expiry" \
|
|
86
|
+
--accept "the expiry behavior is documented" \
|
|
87
|
+
--verify "npm test && npm run docs:check"
|
|
105
88
|
```
|
|
106
89
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
| --- | --- | --- |
|
|
113
|
-
| `codex` | `.codex/config.toml` | `~/.codex/config.toml` |
|
|
114
|
-
| `kimi-code` | `.kimi-code/mcp.json` | `~/.kimi-code/mcp.json` or `$KIMI_CODE_HOME/mcp.json` |
|
|
115
|
-
| `opencode` | `opencode.json` | `~/.config/opencode/opencode.json` |
|
|
116
|
-
| `claude-code` | `.mcp.json` | `~/.claude.json` |
|
|
117
|
-
| `cursor` | `.cursor/mcp.json` | `~/.cursor/mcp.json` |
|
|
90
|
+
Adding acceptance requires a replacement verifier that proves the complete
|
|
91
|
+
accumulated contract. An extension without new acceptance may keep the existing
|
|
92
|
+
verifier or replace it. Every extension invalidates previous machine evidence.
|
|
93
|
+
If the delivery outcome itself changes, close the current outcome honestly and
|
|
94
|
+
begin another one.
|
|
118
95
|
|
|
119
|
-
|
|
120
|
-
agent's user-level config explicitly:
|
|
96
|
+
Before completion:
|
|
121
97
|
|
|
122
98
|
```sh
|
|
123
|
-
driftseal
|
|
99
|
+
driftseal status
|
|
100
|
+
driftseal verify
|
|
101
|
+
driftseal end --status completed --note "Shipped recovery with expiry documentation."
|
|
124
102
|
```
|
|
125
103
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
ignores inherited `DRIFTSEAL_HOME` and `DRIFTSEAL_DECISION_HOME` overrides, so a
|
|
133
|
-
tool call cannot redirect writes outside the selected repository.
|
|
134
|
-
|
|
135
|
-
The v1 server provides:
|
|
136
|
-
|
|
137
|
-
| MCP capability | Purpose |
|
|
138
|
-
| --- | --- |
|
|
139
|
-
| `driftseal_status`, `driftseal_log` | Read the current intent and intent history. |
|
|
140
|
-
| `driftseal_begin`, `driftseal_verify`, `driftseal_end` | Open a work round, capture machine verification evidence, and honestly close it. |
|
|
141
|
-
| `driftseal_absorb` | Repair merge collisions or absorb another worktree's logs while remapping colliding IDs. |
|
|
142
|
-
| `driftseal_reclaim`, `driftseal_unreclaim` | Hide meaningless closed records behind append-only markers, or restore them. |
|
|
143
|
-
| `driftseal_decision_list`, `driftseal_decision_show` | Find and read MADR records. |
|
|
144
|
-
| `driftseal_decision_add`, `driftseal_decision_update` | Add selective decisions and reconcile linked ones. |
|
|
145
|
-
| `driftseal://intent/current` | Read the current intent as a JSON resource. |
|
|
146
|
-
| `driftseal://intents/recent` | Read the ten most recent intents as a JSON resource. |
|
|
147
|
-
| `driftseal://decisions` | Read the decision catalog as a JSON resource. |
|
|
148
|
-
|
|
149
|
-
`driftseal_absorb` accepts optional incoming intent-log and decision-directory
|
|
150
|
-
paths, an `ours` or `theirs` abandon strategy, and a dry-run mode. Incoming
|
|
151
|
-
paths are read-only sources; all repaired output stays under the repository
|
|
152
|
-
fixed at server startup. The Git merge-driver form remains a CLI-only plumbing
|
|
153
|
-
command.
|
|
154
|
-
|
|
155
|
-
MCP changes only the execution surface. It does not add policy beyond the
|
|
156
|
-
repository's `AGENTS.md`, and the companion skill remains limited to discovery
|
|
157
|
-
and recovery guidance.
|
|
158
|
-
|
|
159
|
-
## Optional: keep the agent reminded through hooks
|
|
160
|
-
|
|
161
|
-
Agents that support lifecycle hooks can inject a short DriftSeal reminder before
|
|
162
|
-
the agent starts answering (`UserPromptSubmit`) and surface a warning when it
|
|
163
|
-
finishes (`Stop`). The reminders are advisory — they ask whether the round needs
|
|
164
|
-
an intent and whether an open intent still needs verification and
|
|
165
|
-
`driftseal end`; they never force another model turn, and they stay silent in
|
|
166
|
-
repositories without an intent log.
|
|
167
|
-
|
|
168
|
-
Install them with:
|
|
104
|
+
An acceptance-bound outcome can be completed only after fresh successful
|
|
105
|
+
verification. Evidence is bound to both the contract hash and the Git-visible
|
|
106
|
+
workspace fingerprint. A verification command that arrived only through tracked
|
|
107
|
+
log data requires inspection and an explicit `--allow-tracked-command` opt-in.
|
|
108
|
+
|
|
109
|
+
After context loss or handoff, re-anchor before changing durable content:
|
|
169
110
|
|
|
170
111
|
```sh
|
|
171
|
-
|
|
172
|
-
driftseal
|
|
173
|
-
driftseal hook install --target claude-code
|
|
174
|
-
driftseal hook install --target codex
|
|
112
|
+
driftseal status
|
|
113
|
+
driftseal log --last 3
|
|
175
114
|
```
|
|
176
115
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
116
|
+
## What needs an outcome
|
|
117
|
+
|
|
118
|
+
Record an outcome for durable project-content changes: code, configuration,
|
|
119
|
+
documentation, dependencies, and equivalent files. Git operations, checks,
|
|
120
|
+
temporary auxiliary work, and external state changes are exempt when they do not
|
|
121
|
+
write durable content into the project.
|
|
182
122
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
its target requires `--scope global`. Claude Code receives prompt context through
|
|
187
|
-
`hookSpecificOutput.additionalContext`; its `Stop` reminder uses a UI-only
|
|
188
|
-
`systemMessage`, avoiding a continuation loop. Codex installs only the prompt
|
|
189
|
-
hook because its `Stop` event has no advisory context channel. Hook commands
|
|
190
|
-
search the current directory and its ancestors for an intent log. OpenCode and
|
|
191
|
-
Cursor have no supported hook surface for this yet.
|
|
123
|
+
The scope belongs to the worktree, not to the agent process. Agents and subagents
|
|
124
|
+
working in the same worktree resume its matching open outcome. Separate worktrees
|
|
125
|
+
hold separate outcomes.
|
|
192
126
|
|
|
193
|
-
##
|
|
127
|
+
## Lanes
|
|
194
128
|
|
|
195
|
-
|
|
129
|
+
Orthogonal long-lived capabilities can share one append-only log without sharing
|
|
130
|
+
narrative context. Each outcome belongs to one named lane. Untagged history lives
|
|
131
|
+
on `main`. `status` and `log --last 3` follow the current lane, which is local to
|
|
132
|
+
the worktree.
|
|
196
133
|
|
|
197
134
|
```sh
|
|
198
|
-
driftseal
|
|
199
|
-
|
|
200
|
-
|
|
135
|
+
driftseal lane add index --desc "On-disk inverted index"
|
|
136
|
+
driftseal lane switch index
|
|
137
|
+
driftseal begin "Ship the inverted index" --accept "lookups return stored postings" --verify "npm test"
|
|
201
138
|
```
|
|
202
139
|
|
|
203
|
-
|
|
204
|
-
|
|
140
|
+
Close the open outcome before switching. Later work on the same capability
|
|
141
|
+
switches back and begins a new outcome on that lane; it does not reopen a closed
|
|
142
|
+
record. `driftseal lane assign <id> <name>` moves a closed outcome. Cross-cutting
|
|
143
|
+
work stays on `main`. An open outcome stays visible in `log` even when it belongs
|
|
144
|
+
to another lane; `status` prints that lane when it differs from the current one.
|
|
145
|
+
|
|
146
|
+
Lanes cannot be renamed or removed. A typo in `lane add` stays in `driftseal lane`
|
|
147
|
+
output; add the intended name and stop using the old one.
|
|
148
|
+
|
|
149
|
+
If a `begin` names a lane whose `lane_add` is missing, DriftSeal infers the lane
|
|
150
|
+
so `status`, `log`, and `lane` still work, and `lane add` can write the missing
|
|
151
|
+
event. Duplicate `lane_add` records last-write the description instead of failing
|
|
152
|
+
the fold. A `lane_assign` that names an unknown lane infers it the same way.
|
|
153
|
+
If the worktree's current-lane pointer names a lane the WAL no longer
|
|
154
|
+
has, `status`, `log`, and `lane` fall back to `main` with a warning; `begin`
|
|
155
|
+
still refuses until you `lane switch main` or add the lane. `log --last N` can
|
|
156
|
+
return more than N records when an open outcome sits on another lane.
|
|
157
|
+
|
|
158
|
+
A derived lane index caches fold state in Git metadata (or beside a custom
|
|
159
|
+
seal). Incremental rebuilds follow `indexedThrough` and `indexedLines`; a full
|
|
160
|
+
rebuild happens when the log identity changes. Per-lane heads, reverse links,
|
|
161
|
+
and WAL byte ranges are stored for a seek path that is not consumed yet. The
|
|
162
|
+
index is reconstructable and is not committed with the log. Custom-home
|
|
163
|
+
sidecars sit next to `events.jsonl`. When that directory is inside a Git
|
|
164
|
+
worktree, they are listed in its `.gitignore`.
|
|
165
|
+
|
|
166
|
+
## Decisions and MADR
|
|
167
|
+
|
|
168
|
+
Use a MADR only for context that the outcome log and Git cannot recover: a
|
|
169
|
+
rejected or deferred path worth revisiting, non-obvious rationale for a durable
|
|
170
|
+
choice, or a deprecated or superseded decision.
|
|
205
171
|
|
|
206
172
|
```sh
|
|
207
|
-
driftseal
|
|
173
|
+
driftseal decision add "Expire recovery links after one hour" \
|
|
174
|
+
--context "Recovery links are security-sensitive bearer tokens." \
|
|
175
|
+
--outcome "Use a one-hour lifetime and reject older links." \
|
|
176
|
+
--driver "Limit token exposure" \
|
|
177
|
+
--option "No expiry" \
|
|
178
|
+
--option "One-hour expiry" \
|
|
179
|
+
--consequence "Users must request another link after expiry."
|
|
208
180
|
```
|
|
209
181
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
other program available to the current user. Treat it as executable code, not as
|
|
213
|
-
passive log data. DriftSeal records local provenance when an intent is opened:
|
|
214
|
-
the default Git workflow parks the intent in Git metadata, while non-Git and
|
|
215
|
-
custom `DRIFTSEAL_HOME` workflows keep a small local marker outside the intent
|
|
216
|
-
log. Those locally created intents run normally. If an open intent arrives only
|
|
217
|
-
through an intent log, without matching local provenance, DriftSeal cannot confirm
|
|
218
|
-
who chose its command. It prints the command to stderr and refuses to execute it
|
|
219
|
-
until you inspect it and explicitly run
|
|
220
|
-
`driftseal verify --allow-tracked-command`. The programmatic API and MCP tool
|
|
221
|
-
expose the equivalent `allowTrackedCommand` opt-in. Local provenance state is
|
|
222
|
-
removed when the intent closes. Non-Git markers are bound to the local log
|
|
223
|
-
file's identity, so copying a marker with the log does not transfer trust. If
|
|
224
|
-
local provenance is lost or no longer matches, verification fails safe and
|
|
225
|
-
requires the same explicit opt-in.
|
|
226
|
-
|
|
227
|
-
The verification event records the command's exit status, duration, output
|
|
228
|
-
digest and byte counts, Git HEAD, and a fingerprint of every tracked or
|
|
229
|
-
untracked non-ignored workspace file except the intent event log. A successful
|
|
230
|
-
result becomes stale if those workspace contents change. DriftSeal therefore
|
|
231
|
-
rejects `completed` until the command passes again on the current workspace.
|
|
232
|
-
Command output is spooled to temporary files instead of a fixed in-memory
|
|
233
|
-
buffer, then replayed after the command exits and removed. Output size therefore
|
|
234
|
-
has no DriftSeal-defined limit, though it remains bounded by available disk space.
|
|
235
|
-
Ignored files are deliberately outside this fingerprint. Outside a Git
|
|
236
|
-
worktree the fingerprint is unavailable, so the gate proves only the command's
|
|
237
|
-
recorded exit status and cannot detect later content changes.
|
|
238
|
-
|
|
239
|
-
This proves that the declared command passed on recorded contents; it does not
|
|
240
|
-
prove that the acceptance criterion or test is adequate. Existing intents
|
|
241
|
-
without `--accept` retain the manual verification workflow for compatibility.
|
|
242
|
-
Use protected CI, independent review, or human approval when the verifier was
|
|
243
|
-
written by the same agent, the outcome is subjective, or the change is high risk.
|
|
182
|
+
Link an existing decision from `begin` or `extend` with `--decision <id>`. Before
|
|
183
|
+
closing the outcome as `completed` or `partial`, reconcile every linked decision:
|
|
244
184
|
|
|
245
185
|
```sh
|
|
246
|
-
driftseal
|
|
247
|
-
--status completed \
|
|
248
|
-
--note "Added the limiter and covered the failure path" \
|
|
249
|
-
--verify-result "4 tests pass"
|
|
186
|
+
driftseal decision update 1 --status accepted --note "Confirmed by the final implementation."
|
|
250
187
|
```
|
|
251
188
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
Record an intent for changes intended to persist in the project: edits to code,
|
|
255
|
-
configuration, documentation, dependencies, and equivalent project files. The
|
|
256
|
-
boundary does not depend on Git: inside a worktree it includes content intended
|
|
257
|
-
for commit, while outside Git it includes durable project files. Everything else
|
|
258
|
-
is exempt. Git operations are entirely outside the intent log because Git
|
|
259
|
-
maintains their history; inspection, branch and worktree management, staging,
|
|
260
|
-
commits, merges, rebases, cherry-picks, tags, and pushes never need an intent of
|
|
261
|
-
their own, though they still require normal authorization and safety checks.
|
|
262
|
-
Single-step builds and checks, such as compiling or running tests, need no
|
|
263
|
-
intent. Auxiliary file or shell operations whose results remain outside durable
|
|
264
|
-
project content — an `rsync` scratch copy, temp scaffolding — need none either.
|
|
265
|
-
State changes to a remote machine or the local environment are also exempt when
|
|
266
|
-
they do not write durable project content into this workspace. When an external
|
|
267
|
-
operation does bring durable content into the project, record the intent for
|
|
268
|
-
that project-content change, not for the external operation itself.
|
|
269
|
-
|
|
270
|
-
In multi-agent work the scope belongs to the worktree, not the writer. One
|
|
271
|
-
worktree holds one open intent; every agent or subagent changing durable project
|
|
272
|
-
content there re-anchors and continues that matching intent. Agents in separate
|
|
273
|
-
worktrees hold separate intents. A configured project root outside Git follows
|
|
274
|
-
the same single-intent rule. An agent that only receives another agent's changes
|
|
275
|
-
through Git or into a shared worktree records no receiving intent and lets
|
|
276
|
-
`verify` expose misalignment. Handoff files are exempt while ignored or
|
|
277
|
-
otherwise kept outside durable project content and require an intent when
|
|
278
|
-
promoted into it. Taking over work in the same root is a re-anchor, not a
|
|
279
|
-
boundary: resume the open intent when its objective still matches the task.
|
|
280
|
-
|
|
281
|
-
## Commands
|
|
189
|
+
## Command reference
|
|
282
190
|
|
|
283
191
|
| Command | Purpose |
|
|
284
|
-
|
|
285
|
-
| `driftseal begin "<
|
|
286
|
-
| `driftseal
|
|
287
|
-
| `driftseal
|
|
288
|
-
| `driftseal status` |
|
|
289
|
-
| `driftseal
|
|
290
|
-
| `driftseal
|
|
291
|
-
| `driftseal
|
|
292
|
-
| `driftseal
|
|
293
|
-
| `driftseal
|
|
294
|
-
| `driftseal
|
|
295
|
-
| `driftseal decision update
|
|
296
|
-
| `driftseal
|
|
297
|
-
| `driftseal
|
|
298
|
-
| `driftseal
|
|
299
|
-
| `driftseal
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
It cannot be combined with `--last`, whose limiting semantics would make the
|
|
354
|
-
count ambiguous. Decision filenames form a lightweight in-memory index: `show`
|
|
355
|
-
parses only the requested record, and an unfiltered `--count` reads no MADR
|
|
356
|
-
contents. Status-filtered listing and counting parse all records because status
|
|
357
|
-
is stored in each MADR document; DriftSeal does not maintain a stale-prone sidecar
|
|
358
|
-
index.
|
|
359
|
-
|
|
360
|
-
## Merging worktrees
|
|
361
|
-
|
|
362
|
-
Two worktrees allocate intent and decision ids from their local logs, so a
|
|
363
|
-
same-day parallel `begin` or `decision add` can collide when the branches
|
|
364
|
-
merge. `driftseal absorb` rebuilds a valid log by keeping our ids and remapping
|
|
365
|
-
the incoming side, then prints the mapping. A single lineage stays append-only;
|
|
366
|
-
absorb is the one cross-lineage rewrite.
|
|
192
|
+
|---|---|
|
|
193
|
+
| `driftseal begin "<outcome>" [--accept "..."] [--verify "..."] [--decision id] [--force]` | Open one coherent outcome. |
|
|
194
|
+
| `driftseal extend "<addition>" [--accept "..."] [--verify "..."] [--decision id]` | Append scope to the same outcome and invalidate earlier verification. |
|
|
195
|
+
| `driftseal verify [--allow-tracked-command]` | Execute the declared cumulative verifier and bind evidence. |
|
|
196
|
+
| `driftseal end [id] [-s status] [-n note] [-r verify-result]` | Close an outcome honestly. |
|
|
197
|
+
| `driftseal status` | Show the outcome in progress and the current lane. |
|
|
198
|
+
| `driftseal log [--last N] [--all] [--all-lanes]` | Review outcome history (current lane unless `--all-lanes`). |
|
|
199
|
+
| `driftseal lane add\|switch\|assign\|show` | Partition history by long-lived capability. |
|
|
200
|
+
| `driftseal reclaim [id ...] --reason "..." [--force]` | Hide meaningless closed records with append-only markers. |
|
|
201
|
+
| `driftseal unreclaim <id> --reason "..."` | Restore a reclaimed record. |
|
|
202
|
+
| `driftseal absorb [other-events.jsonl] [--decisions dir] [--abandon-theirs\|--abandon-ours]` | Merge another lineage and remap colliding outcome or MADR ids. |
|
|
203
|
+
| `driftseal decision add\|update\|list\|show` | Manage MADR records. |
|
|
204
|
+
| `driftseal migrate v1-to-v2 inspect --json [migration paths]` | Normalize v1 state for model-assisted grouping. |
|
|
205
|
+
| `driftseal migrate v1-to-v2 apply --plan <file> [migration paths]` | Validate a grouping plan and stage the v2 seal beside v1. |
|
|
206
|
+
| `driftseal migrate v1-to-v2 check [migration paths]` | Validate the staged result and report the review/deletion gate. |
|
|
207
|
+
| `driftseal init [--lang tag] [--local-log]` | Install or upgrade the repository protocol. |
|
|
208
|
+
|
|
209
|
+
Run `driftseal help` for the complete syntax, including skill, MCP, and hook
|
|
210
|
+
installation targets.
|
|
211
|
+
|
|
212
|
+
## Migrating from v1
|
|
213
|
+
|
|
214
|
+
Migration is deliberately model-assisted because grouping step-sized intents
|
|
215
|
+
into delivered outcomes is semantic work.
|
|
216
|
+
|
|
217
|
+
When an unmigrated v1 intent log or MADR directory is present, normal v2
|
|
218
|
+
repository commands fail closed instead of silently starting an unrelated
|
|
219
|
+
`.seal` lineage. A MADR-only v1 repository may migrate without creating an
|
|
220
|
+
empty intent log first.
|
|
221
|
+
|
|
222
|
+
1. Close every v1 intent. A parked v1 intent blocks migration. After upgrading
|
|
223
|
+
the CLI, close it with `driftseal end` (for example `--status abandoned`)
|
|
224
|
+
before `inspect`. Merge or freeze branches that still edit `.intent-log`
|
|
225
|
+
first; `absorb --git` keeps both sides of a v1 log merge instead of dropping
|
|
226
|
+
theirs.
|
|
227
|
+
2. Inspect the normalized source:
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
driftseal migrate v1-to-v2 inspect --json > /tmp/driftseal-inspection.json
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
3. Have the model propose a `driftseal-v1-to-v2-plan` JSON document. Its groups
|
|
234
|
+
must form an ordered, complete partition of all visible v1 records. Only
|
|
235
|
+
records already reclaimed in v1 may be excluded, and every exclusion needs a
|
|
236
|
+
reason. `groups` may be empty when no visible records remain; MADRs are still
|
|
237
|
+
migrated.
|
|
238
|
+
4. Review the proposed outcomes, then apply the approved plan:
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
driftseal migrate v1-to-v2 apply --plan /tmp/driftseal-plan.json
|
|
242
|
+
driftseal migrate v1-to-v2 check
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`apply` fingerprints the source, validates the partition, validates the staged
|
|
246
|
+
v2 log, copies every v1 MADR byte-for-byte, and records a name, size, and hash
|
|
247
|
+
manifest so `check` can still verify them after v1 is removed. Later MADR content
|
|
248
|
+
is accepted only when the latest valid v2 reconciliation attests its current hash. `apply`
|
|
249
|
+
creates `.seal/` beside `.intent-log/` and `.decision-log/`; it never deletes v1
|
|
250
|
+
data. After the user has reviewed and approved the result, remove the old paths
|
|
251
|
+
manually. `check` prints `git rm` when those paths are tracked and `rm -rf` when
|
|
252
|
+
they are local-only. Running `check` afterward reports migration complete.
|
|
253
|
+
|
|
254
|
+
If v1 used custom storage, keep the source and destination explicit for inspect
|
|
255
|
+
and apply. `DRIFTSEAL_DECISION_HOME` is a v1-only default for the MADR source
|
|
256
|
+
and for fail-closed detection; v2 runtime ignores it. The migration marker records repository-local paths as portable
|
|
257
|
+
identities, so later checks recover the source paths from the destination even
|
|
258
|
+
after the repository moves. The destination must not contain, or be contained
|
|
259
|
+
by, the source log file or MADR directory. In particular, an inherited v1
|
|
260
|
+
`DRIFTSEAL_HOME` must not also be used as the v2 destination:
|
|
367
261
|
|
|
368
262
|
```sh
|
|
369
|
-
driftseal
|
|
370
|
-
--
|
|
263
|
+
driftseal migrate v1-to-v2 inspect --json \
|
|
264
|
+
--source-log /path/to/v1-intents/events.jsonl \
|
|
265
|
+
--source-decisions /path/to/v1-decisions \
|
|
266
|
+
--destination /path/to/repository/.seal
|
|
267
|
+
driftseal migrate v1-to-v2 apply --plan /tmp/driftseal-plan.json \
|
|
268
|
+
--source-log /path/to/v1-intents/events.jsonl \
|
|
269
|
+
--source-decisions /path/to/v1-decisions \
|
|
270
|
+
--destination /path/to/repository/.seal
|
|
271
|
+
driftseal migrate v1-to-v2 check \
|
|
272
|
+
--destination /path/to/repository/.seal
|
|
371
273
|
```
|
|
372
274
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
275
|
+
After applying, unset the v1 `DRIFTSEAL_HOME` or point it at the new seal root.
|
|
276
|
+
The Node API exposes `sourceLog`, `sourceDecisions`, and `destination`. MCP
|
|
277
|
+
migration tools accept custom v1 sources but always stage into the server's
|
|
278
|
+
fixed repository `.seal`, so ordinary MCP workflow tools immediately see the
|
|
279
|
+
migrated state.
|
|
377
280
|
|
|
378
|
-
|
|
379
|
-
and the local git merge driver so `events.jsonl` merges through `absorb --git`.
|
|
380
|
-
When decision ids collide, the driver stops the merge before Git can commit an
|
|
381
|
-
ambiguous decision catalog. Run `driftseal absorb`, stage the repaired intent
|
|
382
|
-
and decision logs, then continue the merge. Clones need `init` again because
|
|
383
|
-
the driver lives in local git config.
|
|
281
|
+
## Git and merge behavior
|
|
384
282
|
|
|
385
|
-
In a Git worktree, `begin` parks the open
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
tree. `end` moves the parked records into the tracked log and writes the closing
|
|
389
|
-
record there — never into Git metadata — so an interrupted `end` leaves the
|
|
390
|
-
intent open in the log and can simply be run again. If the parked intent's id
|
|
391
|
-
collides with incoming merged events, DriftSeal remaps it the same way `absorb`
|
|
392
|
-
remaps colliding worktree ids.
|
|
283
|
+
In a Git worktree, `begin` parks the open outcome in Git metadata so it does not
|
|
284
|
+
dirty the tracked log. `end` flushes the lineage to
|
|
285
|
+
`.seal/outcomes/events.jsonl`. The event log is append-only during normal work.
|
|
393
286
|
|
|
394
|
-
|
|
395
|
-
parked one into the tracked log and `absorb --abandon-theirs` closes the
|
|
396
|
-
incoming one and leaves yours parked. `end <id>` also works on the incoming
|
|
397
|
-
intent directly, and `begin --force` abandons every open intent at once.
|
|
287
|
+
After a merge collision, run:
|
|
398
288
|
|
|
399
|
-
|
|
289
|
+
```sh
|
|
290
|
+
driftseal absorb
|
|
291
|
+
```
|
|
400
292
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
293
|
+
Do not edit the JSONL manually. `absorb` remaps colliding outcome and decision
|
|
294
|
+
ids, rebinds affected contract hashes, and refuses concurrent edits of a shared
|
|
295
|
+
MADR. If both lineages remain open, choose explicitly with `--abandon-theirs` or
|
|
296
|
+
`--abandon-ours`.
|
|
404
297
|
|
|
405
|
-
|
|
298
|
+
## Node API and MCP
|
|
406
299
|
|
|
407
|
-
|
|
300
|
+
```js
|
|
301
|
+
const { createApi } = require('driftseal');
|
|
302
|
+
|
|
303
|
+
const seal = createApi({ root: process.cwd(), isolateStorage: true });
|
|
304
|
+
seal.begin({
|
|
305
|
+
outcome: 'Ship account recovery',
|
|
306
|
+
acceptance: ['the recovery tests pass'],
|
|
307
|
+
verify: 'npm test',
|
|
308
|
+
});
|
|
309
|
+
seal.extend({ extension: 'Document token expiry' });
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The API also exposes `status`, `verify`, `end`, `log`, `lane`, `laneAdd`,
|
|
313
|
+
`laneSwitch`, `laneAssign`, `absorb`, reclaim, decision, init, and migration
|
|
314
|
+
methods.
|
|
315
|
+
|
|
316
|
+
The stdio MCP server fixes all operations to one repository root. Its v2 tools
|
|
317
|
+
include `driftseal_status`, `driftseal_begin`, `driftseal_extend`,
|
|
318
|
+
`driftseal_verify`, `driftseal_end`, outcome history, lane, and absorb tools,
|
|
319
|
+
MADR tools, and the three migration tools. Resources are:
|
|
320
|
+
|
|
321
|
+
- `driftseal://outcome/current`
|
|
322
|
+
- `driftseal://outcomes/recent`
|
|
323
|
+
- `driftseal://lanes`
|
|
324
|
+
- `driftseal://madr`
|
|
325
|
+
|
|
326
|
+
## Storage and trust boundary
|
|
327
|
+
|
|
328
|
+
- `.seal/outcomes/events.jsonl` is the append-only outcome log. Access it through
|
|
329
|
+
DriftSeal; use `reclaim`, `unreclaim`, `lane`, and `absorb` instead of manual edits.
|
|
330
|
+
- `.seal/madr/` stores numbered MADR documents.
|
|
331
|
+
- `$DRIFTSEAL_HOME` replaces the `.seal` root.
|
|
332
|
+
- The current lane and derived lane index live in Git metadata for a default
|
|
333
|
+
repository seal, or beside a custom seal (`outcomes/.current-lane` and
|
|
334
|
+
`outcomes/.lane-index.json`). When the custom seal sits inside a Git
|
|
335
|
+
worktree, those sidecars are gitignored. They are reconstructable and are not
|
|
336
|
+
part of the committed WAL.
|
|
337
|
+
- Advisory hooks remind agents about lifecycle state but never broaden the
|
|
338
|
+
repository's `AGENTS.md` policy.
|
|
339
|
+
|
|
340
|
+
DriftSeal does not decide whether a verification command is safe or whether a
|
|
341
|
+
test is adequate. Inspect commands before execution and apply normal repository
|
|
342
|
+
authorization and security rules.
|
|
408
343
|
|
|
409
344
|
## Development
|
|
410
345
|
|
|
411
346
|
```sh
|
|
412
347
|
npm test
|
|
348
|
+
node --check bin/driftseal.js
|
|
349
|
+
node --check bin/driftseal-mcp.js
|
|
350
|
+
npm pack --dry-run
|
|
413
351
|
```
|
|
414
352
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
## License
|
|
418
|
-
|
|
419
|
-
MIT. See [`LICENSE`](LICENSE).
|
|
353
|
+
Licensed under the MIT License.
|