driftseal 1.3.2 → 2.0.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 CHANGED
@@ -1,402 +1,303 @@
1
1
  # DriftSeal
2
2
 
3
- > **Seal the intent. Stop the drift.**
3
+ > **Seal the outcome. Stop the drift.**
4
4
 
5
- [简体中文](README.zh-CN.md)
6
-
7
- Agentic coding moves fast. **DriftSeal keeps it honest.**
8
-
9
- Before an agent touches the code, DriftSeal records what this round will accomplish and how completion will be proved. When the work ends, it records what actually happened. The result is a tiny, auditable contract that survives context loss, scope creep, and optimistic “done” claims.
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
- seal intentdo the workprove the result → close the round
12
+ begin an outcome extend the same outcome verify the cumulative contract → close
13
13
  ```
14
14
 
15
- **One open intent. One declared proof. One durable trail.** No service and no database—just local Node.js tools and plain files that travel with the repo.
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
- ## The problem is not speed. It is drift.
21
+ DriftSeal v2 is an outcome log rather than an intent-per-step log.
18
22
 
19
- | Without DriftSeal | With DriftSeal |
20
- | --- | --- |
21
- | Scope quietly expands halfway through a task | One visible intent defines the active round |
22
- | “Done” arrives without meaningful evidence | Verification is declared before implementation |
23
- | Context compaction erases the original goal | `status` and `log` restore the exact intent and history |
24
- | Old architectural debates repeat forever | Selective [MADR](https://adr.github.io/madr/) records preserve the reasoning that matters |
25
- | Concurrent or interrupted writes leave uncertainty | Locks, schema checks, atomic writes, and recovery make failures explicit and recoverable |
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` and `schemaVersion: 1`.
32
+ - The generated `AGENTS.md` protocol series is `2.0`; compatible protocol
33
+ refinements use `2.1`, `2.2`, and so on.
34
+ - The public CLI, Node API, MCP tools, and MCP resources use outcome terminology.
35
+ v1 names and storage paths are not runtime aliases.
26
36
 
27
- DriftSeal complements Git instead of competing with it: the intent says what was planned, the decision log preserves why, and the commit shows what landed.
37
+ ## Install
28
38
 
29
- ## Start in 30 seconds
39
+ DriftSeal requires Node.js 18 or newer.
30
40
 
31
41
  ```sh
32
42
  npm install --global driftseal
33
- cd your-project
34
- driftseal init
43
+ driftseal --version
35
44
  ```
36
45
 
37
- `driftseal init` writes the protocol to `AGENTS.md`, including how to `absorb`
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:
46
+ From a source checkout:
47
47
 
48
48
  ```sh
49
- npm link
49
+ npm install
50
+ node bin/driftseal.js --version
50
51
  ```
51
52
 
52
- ## Recommended agent setup
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:
53
+ Adopt the protocol in a repository:
61
54
 
62
55
  ```sh
63
- driftseal skill install --target codex
64
- driftseal skill install --target kimi-code --scope global
56
+ driftseal init
65
57
  ```
66
58
 
67
- | Target | Project scope | Global scope |
68
- | --- | --- | --- |
69
- | `codex` | `.agents/skills/use-driftseal` | `~/.agents/skills/use-driftseal` |
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` |
59
+ `init` writes or upgrades the managed blocks in `AGENTS.md`, adds the outcome-log
60
+ merge attribute, and configures the local Git merge driver. Run it again in a
61
+ fresh clone because Git config is local to each clone.
74
62
 
75
- Use `--root <repository>` to select a project when running the installer
76
- elsewhere. Repeated installs of identical content are no-ops, and a skill left
77
- by an earlier DriftSeal release is upgraded in place; only a skill this
78
- installer never wrote requires `--force`. MCP and lifecycle hooks are optional
79
- adapters; enable them only for a concrete host constraint or reminder need, not
80
- as additional policy layers.
63
+ Use `driftseal init --lang <BCP-47-tag>` to choose the prose language stored in
64
+ outcome and MADR records. Use `--local-log` only when `.seal/` should remain
65
+ untracked; DriftSeal reports tracked state but does not edit `.gitignore` or the
66
+ Git index.
81
67
 
82
- ## Optional: use DriftSeal through MCP
68
+ ## Core workflow
83
69
 
84
- The same package includes `driftseal-mcp`, a local stdio MCP server. It exposes
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:
70
+ Open the coherent delivery outcome before changing durable project content:
90
71
 
91
72
  ```sh
92
- driftseal-mcp --root /absolute/path/to/repository
73
+ driftseal begin "Ship account recovery" \
74
+ --accept "expired links are rejected" \
75
+ --accept "a valid link resets the password" \
76
+ --verify "npm test"
93
77
  ```
94
78
 
95
- Install the server into the current repository's agent config with one of the
96
- supported targets:
79
+ If another step is still part of that same delivered outcome, append it:
97
80
 
98
81
  ```sh
99
- cd /path/to/repository
100
- driftseal mcp install --target codex
101
- driftseal mcp install --target kimi-code
102
- driftseal mcp install --target opencode
103
- driftseal mcp install --target claude-code
104
- driftseal mcp install --target cursor
82
+ driftseal extend "Document recovery-link expiry" \
83
+ --accept "the expiry behavior is documented" \
84
+ --verify "npm test && npm run docs:check"
105
85
  ```
106
86
 
107
- Project scope is the default because each DriftSeal MCP server belongs to one
108
- repository. Every target pins `--root` to the repository's canonical absolute
109
- path, and repeated installs are idempotent.
110
-
111
- | Target | Project config | Global config |
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` |
87
+ Adding acceptance requires a replacement verifier that proves the complete
88
+ accumulated contract. An extension without new acceptance may keep the existing
89
+ verifier or replace it. Every extension invalidates previous machine evidence.
90
+ If the delivery outcome itself changes, close the current outcome honestly and
91
+ begin another one.
118
92
 
119
- Use `--root <repository>` when running the installer elsewhere, or choose the
120
- agent's user-level config explicitly:
93
+ Before completion:
121
94
 
122
95
  ```sh
123
- driftseal mcp install --target <target> --scope global --root /absolute/path/to/repository
96
+ driftseal status
97
+ driftseal verify
98
+ driftseal end --status completed --note "Shipped recovery with expiry documentation."
124
99
  ```
125
100
 
126
- Global installs remain pinned to the selected repository. If the chosen config
127
- already contains a different DriftSeal server entry, the installer leaves it
128
- untouched unless `--force` is supplied. Other agent settings and MCP servers are
129
- preserved.
130
-
131
- The root is startup configuration, not a tool input. In MCP mode DriftSeal also
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:
101
+ An acceptance-bound outcome can be completed only after fresh successful
102
+ verification. Evidence is bound to both the contract hash and the Git-visible
103
+ workspace fingerprint. A verification command that arrived only through tracked
104
+ log data requires inspection and an explicit `--allow-tracked-command` opt-in.
105
+
106
+ After context loss or handoff, re-anchor before changing durable content:
169
107
 
170
108
  ```sh
171
- cd /path/to/repository
172
- driftseal hook install --target kimi-code --scope global
173
- driftseal hook install --target claude-code
174
- driftseal hook install --target codex
109
+ driftseal status
110
+ driftseal log --last 3
175
111
  ```
176
112
 
177
- | Target | Project config | Global config |
178
- | --- | --- | --- |
179
- | `kimi-code` | Not supported | `~/.kimi-code/config.toml` or `$KIMI_CODE_HOME/config.toml` |
180
- | `claude-code` | `.claude/settings.json` | `~/.claude/settings.json` |
181
- | `codex` | `.codex/hooks.json` | `~/.codex/hooks.json` |
113
+ ## What needs an outcome
114
+
115
+ Record an outcome for durable project-content changes: code, configuration,
116
+ documentation, dependencies, and equivalent files. Git operations, checks,
117
+ temporary auxiliary work, and external state changes are exempt when they do not
118
+ write durable content into the project.
182
119
 
183
- Like `mcp install`, the hook installer accepts `--scope global`,
184
- `--root <repository>`, and `--force`, is idempotent, and preserves unrelated
185
- config entries. Kimi Code documents hooks only in its global `config.toml`, so
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.
120
+ The scope belongs to the worktree, not to the agent process. Agents and subagents
121
+ working in the same worktree resume its matching open outcome. Separate worktrees
122
+ hold separate outcomes.
192
123
 
193
- ## A work round
124
+ ## Decisions and MADR
194
125
 
195
- Declare the round before making non-Git changes:
126
+ Use a MADR only for context that the outcome log and Git cannot recover: a
127
+ rejected or deferred path worth revisiting, non-obvious rationale for a durable
128
+ choice, or a deprecated or superseded decision.
196
129
 
197
130
  ```sh
198
- driftseal begin "add rate limiting to /api/login" \
199
- --accept "the sixth login attempt within one minute receives HTTP 429" \
200
- --verify "npm test test/rate-limit.test.js"
131
+ driftseal decision add "Expire recovery links after one hour" \
132
+ --context "Recovery links are security-sensitive bearer tokens." \
133
+ --outcome "Use a one-hour lifetime and reject older links." \
134
+ --driver "Limit token exposure" \
135
+ --option "No expiry" \
136
+ --option "One-hour expiry" \
137
+ --consequence "Users must request another link after expiry."
201
138
  ```
202
139
 
203
- Do the work, reconcile any linked decisions, inspect the declared command with
204
- `driftseal status`, and only then let DriftSeal run it:
140
+ Link an existing decision from `begin` or `extend` with `--decision <id>`. Before
141
+ closing the outcome as `completed` or `partial`, reconcile every linked decision:
205
142
 
206
143
  ```sh
207
- driftseal verify
144
+ driftseal decision update 1 --status accepted --note "Confirmed by the final implementation."
208
145
  ```
209
146
 
210
- `driftseal verify` passes the exact stored string to the operating-system shell.
211
- The command can therefore read or modify files, access the network, or run any
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.
147
+ ## Command reference
148
+
149
+ | Command | Purpose |
150
+ |---|---|
151
+ | `driftseal begin "<outcome>" [--accept "..."] [--verify "..."] [--decision id] [--force]` | Open one coherent outcome. |
152
+ | `driftseal extend "<addition>" [--accept "..."] [--verify "..."] [--decision id]` | Append scope to the same outcome and invalidate earlier verification. |
153
+ | `driftseal verify [--allow-tracked-command]` | Execute the declared cumulative verifier and bind evidence. |
154
+ | `driftseal end [id] [-s status] [-n note] [-r verify-result]` | Close an outcome honestly. |
155
+ | `driftseal status` | Show the outcome in progress. |
156
+ | `driftseal log [--last N] [--all]` | Review outcome history. |
157
+ | `driftseal reclaim [id ...] --reason "..." [--force]` | Hide meaningless closed records with append-only markers. |
158
+ | `driftseal unreclaim <id> --reason "..."` | Restore a reclaimed record. |
159
+ | `driftseal absorb [other-events.jsonl] [--decisions dir] [--abandon-theirs\|--abandon-ours]` | Merge another lineage and remap colliding outcome or MADR ids. |
160
+ | `driftseal decision add\|update\|list\|show` | Manage MADR records. |
161
+ | `driftseal migrate v1-to-v2 inspect --json [migration paths]` | Normalize v1 state for model-assisted grouping. |
162
+ | `driftseal migrate v1-to-v2 apply --plan <file> [migration paths]` | Validate a grouping plan and stage the v2 seal beside v1. |
163
+ | `driftseal migrate v1-to-v2 check [migration paths]` | Validate the staged result and report the review/deletion gate. |
164
+ | `driftseal init [--lang tag] [--local-log]` | Install or upgrade the repository protocol. |
165
+
166
+ Run `driftseal help` for the complete syntax, including skill, MCP, and hook
167
+ installation targets.
168
+
169
+ ## Migrating from v1
170
+
171
+ Migration is deliberately model-assisted because grouping step-sized intents
172
+ into delivered outcomes is semantic work.
173
+
174
+ When an unmigrated v1 intent log or MADR directory is present, normal v2
175
+ repository commands fail closed instead of silently starting an unrelated
176
+ `.seal` lineage. A MADR-only v1 repository may migrate without creating an
177
+ empty intent log first.
178
+
179
+ 1. Close every v1 intent. A parked v1 intent blocks migration. After upgrading
180
+ the CLI, close it with `driftseal end` (for example `--status abandoned`)
181
+ before `inspect`. Merge or freeze branches that still edit `.intent-log`
182
+ first; `absorb --git` keeps both sides of a v1 log merge instead of dropping
183
+ theirs.
184
+ 2. Inspect the normalized source:
185
+
186
+ ```sh
187
+ driftseal migrate v1-to-v2 inspect --json > /tmp/driftseal-inspection.json
188
+ ```
189
+
190
+ 3. Have the model propose a `driftseal-v1-to-v2-plan` JSON document. Its groups
191
+ must form an ordered, complete partition of all visible v1 records. Only
192
+ records already reclaimed in v1 may be excluded, and every exclusion needs a
193
+ reason. `groups` may be empty when no visible records remain; MADRs are still
194
+ migrated.
195
+ 4. Review the proposed outcomes, then apply the approved plan:
196
+
197
+ ```sh
198
+ driftseal migrate v1-to-v2 apply --plan /tmp/driftseal-plan.json
199
+ driftseal migrate v1-to-v2 check
200
+ ```
201
+
202
+ `apply` fingerprints the source, validates the partition, validates the staged
203
+ v2 log, copies every v1 MADR byte-for-byte, and records a name, size, and hash
204
+ manifest so `check` can still verify them after v1 is removed. Later MADR content
205
+ is accepted only when the latest valid v2 reconciliation attests its current hash. `apply`
206
+ creates `.seal/` beside `.intent-log/` and `.decision-log/`; it never deletes v1
207
+ data. After the user has reviewed and approved the result, remove the old paths
208
+ manually. `check` prints `git rm` when those paths are tracked and `rm -rf` when
209
+ they are local-only. Running `check` afterward reports migration complete.
210
+
211
+ If v1 used custom storage, keep the source and destination explicit for inspect
212
+ and apply. `DRIFTSEAL_DECISION_HOME` is a v1-only default for the MADR source
213
+ and for fail-closed detection; v2 runtime ignores it. The migration marker records repository-local paths as portable
214
+ identities, so later checks recover the source paths from the destination even
215
+ after the repository moves. The destination must not contain, or be contained
216
+ by, the source log file or MADR directory. In particular, an inherited v1
217
+ `DRIFTSEAL_HOME` must not also be used as the v2 destination:
244
218
 
245
219
  ```sh
246
- driftseal end \
247
- --status completed \
248
- --note "Added the limiter and covered the failure path" \
249
- --verify-result "4 tests pass"
220
+ driftseal migrate v1-to-v2 inspect --json \
221
+ --source-log /path/to/v1-intents/events.jsonl \
222
+ --source-decisions /path/to/v1-decisions \
223
+ --destination /path/to/repository/.seal
224
+ driftseal migrate v1-to-v2 apply --plan /tmp/driftseal-plan.json \
225
+ --source-log /path/to/v1-intents/events.jsonl \
226
+ --source-decisions /path/to/v1-decisions \
227
+ --destination /path/to/repository/.seal
228
+ driftseal migrate v1-to-v2 check \
229
+ --destination /path/to/repository/.seal
250
230
  ```
251
231
 
252
- If the scope changes, close the current intent as `partial` or `abandoned`, then start a new one. After context loss, use `driftseal status` and `driftseal log --last 3` to re-anchor.
232
+ After applying, unset the v1 `DRIFTSEAL_HOME` or point it at the new seal root.
233
+ The Node API exposes `sourceLog`, `sourceDecisions`, and `destination`. MCP
234
+ migration tools accept custom v1 sources but always stage into the server's
235
+ fixed repository `.seal`, so ordinary MCP workflow tools immediately see the
236
+ migrated state.
253
237
 
254
- Git operations are entirely outside the intent log because Git maintains their
255
- history. Inspection, branch and worktree management, staging, commits, merges,
256
- rebases, cherry-picks, tags, and pushes never need an intent of their own. They
257
- still require normal authorization and safety checks. A command whose result
258
- can be reconstructed from Git state, such as a patch file regenerated from a
259
- commit range or a scratch harness that re-runs, needs no intent either; content
260
- that will be committed and cannot be reconstructed, such as a `.gitignore`
261
- edit, does. Single-step builds and checks, such as compiling or running tests,
262
- also need no intent. Any other non-Git content change starts a new work round.
238
+ ## Git and merge behavior
263
239
 
264
- ## Commands
240
+ In a Git worktree, `begin` parks the open outcome in Git metadata so it does not
241
+ dirty the tracked log. `end` flushes the lineage to
242
+ `.seal/outcomes/events.jsonl`. The event log is append-only during normal work.
265
243
 
266
- | Command | Purpose |
267
- | --- | --- |
268
- | `driftseal begin "<intent>" [--accept "<outcome>"] [-v "<command>"] [--decision id] [--force]` | Open a work-round intent. Repeat `--accept` for observable completion criteria; acceptance requires a verification command. |
269
- | `driftseal verify [--allow-tracked-command]` | Execute the acceptance-bound intent's predeclared command and bind machine evidence to the current Git-visible workspace contents. Commands without matching local provenance require the explicit opt-in. |
270
- | `driftseal end [id] [-s status] [-n note] [-r verify-result]` | Close an intent honestly. |
271
- | `driftseal status` | Show the intent currently in progress. |
272
- | `driftseal log [-n N] [--all]` | Review intent history (`--all` includes reclaimed records). |
273
- | `driftseal reclaim [id ...] --reason "..." [--older-than days] [--force] [--dry-run]` | Hide meaningless closed records behind append-only markers. |
274
- | `driftseal unreclaim <id> --reason "..."` | Restore a reclaimed record to the visible log. |
275
- | `driftseal absorb [other-events.jsonl] [--decisions dir] [--abandon-theirs \| --abandon-ours] [--dry-run]` | Merge another worktree's logs, remapping colliding intent and decision ids. |
276
- | `driftseal absorb --git <base> <ours> <theirs>` | Git merge driver for `.intent-log/events.jsonl`. |
277
- | `driftseal decision add "<title>" -c "..." -o "..."` | Write a numbered MADR decision. |
278
- | `driftseal decision update <id> [-s status] -n "..."` | Reconcile a linked decision in the open intent. |
279
- | `driftseal decision list [-s status] [--last N \| --count]` | List or count decision records, optionally filtered by status. |
280
- | `driftseal decision show <id>` | Read one decision record. |
281
- | `driftseal skill install --target TARGET [--scope project\|global] [--root path] [--force]` | Install the bundled skill for Codex, Kimi Code, OpenCode, Claude Code, or Cursor. |
282
- | `driftseal mcp install --target TARGET [--scope project\|global] [--root path] [--force]` | Install the repository-pinned MCP server into Codex, Kimi Code, OpenCode, Claude Code, or Cursor. |
283
- | `driftseal hook install --target TARGET [--scope project\|global] [--root path] [--force]` | Install advisory lifecycle reminders into Kimi Code, Claude Code, or Codex. |
284
- | `driftseal hook prompt\|stop [--format plain\|claude-code]` | Emit the reminder a lifecycle hook injects; never blocks. |
285
- | `driftseal init [--lang <tag>] [--local-log]` | Add the adoption protocol to `AGENTS.md` and configure the git merge driver. `--lang` sets the intent/decision log language (BCP 47, default `en`). `--local-log` keeps the logs local and untracked instead of committing them with the code; if the logs are already tracked, init warns with the remediation steps and leaves the index and `.gitignore` untouched. |
286
- | `driftseal --version` or `driftseal -V` | Print the installed DriftSeal version. |
287
- | `driftseal help` | Print CLI usage. |
288
-
289
- When `begin` declares one or more `--decision <id>` links, every linked
290
- decision must be reconciled with `driftseal decision update` before that intent can
291
- close as `completed` or `partial`. The update changes the current status when
292
- requested and appends a timestamped history entry tied to the intent. Intents
293
- without decision links keep the ordinary workflow. For acceptance-bound linked
294
- intents, perform every decision update before `driftseal verify`, because a decision
295
- update changes the workspace fingerprint: reconcile, verify, then end.
296
-
297
- ## Reclaiming noise records
298
-
299
- Some closed records stop mattering: a harness or sandbox failure is recorded
300
- honestly as `failed`, but it says nothing about the project. `driftseal
301
- reclaim` retires such records without rewriting history — it appends a
302
- `reclaim` marker (with a mandatory `--reason`) to the same append-only log,
303
- and reclaimed records disappear from `driftseal log` and `driftseal status`
304
- output while remaining in `events.jsonl` and visible with `log --all`.
305
- `driftseal unreclaim <id> --reason "..."` restores a record that turned out to
306
- matter.
307
-
308
- Without ids, batch mode reclaims only closed `failed`/`abandoned` records that
309
- are not linked to decisions and are older than `--older-than` days (default
310
- 7); use `--dry-run` to preview. `completed` and `partial` records, and any
311
- decision-linked record, can only be reclaimed by explicit id with `--force`.
312
-
313
- ## Consistency and recovery
314
-
315
- DriftSeal serializes mutating commands with locks on the configured intent and
316
- decision-log roots, acquired in a stable order. Decision reconciliation is
317
- journaled as prepare and commit events around an atomic MADR replacement. If
318
- the process stops between those steps, the next linked `decision update` or
319
- successful `end` recovers the transaction from content hashes. A successful
320
- linked-intent close also verifies that the decision file has not changed since
321
- its latest reconciliation. Unlinked intents do not parse the decision log, and
322
- `failed` or `abandoned` remains an escape path when decision recovery cannot
323
- complete. Those terminal statuses cancel recovery for their pending
324
- transactions, and recovery is scoped to the current intent so historical
325
- conflicts cannot block later decision work.
326
-
327
- New events carry a schema version. DriftSeal rejects newer unsupported schemas and
328
- fails closed if a legacy client closes a linked intent without reconciliation.
329
- `driftseal init` writes versioned managed blocks and upgrades only exact, recognized
330
- older blocks. Current-version blocks that differ only by log language are also
331
- recognized, so `--lang` can change the language without rewriting policy by hand.
332
- It refuses newer protocol versions and any unrecognized or customized block
333
- without changing `AGENTS.md`.
334
-
335
- `--count` prints only the number of records remaining after status filtering.
336
- It cannot be combined with `--last`, whose limiting semantics would make the
337
- count ambiguous. Decision filenames form a lightweight in-memory index: `show`
338
- parses only the requested record, and an unfiltered `--count` reads no MADR
339
- contents. Status-filtered listing and counting parse all records because status
340
- is stored in each MADR document; DriftSeal does not maintain a stale-prone sidecar
341
- index.
342
-
343
- ## Merging worktrees
344
-
345
- Two worktrees allocate intent and decision ids from their local logs, so a
346
- same-day parallel `begin` or `decision add` can collide when the branches
347
- merge. `driftseal absorb` rebuilds a valid log by keeping our ids and remapping
348
- the incoming side, then prints the mapping. A single lineage stays append-only;
349
- absorb is the one cross-lineage rewrite.
244
+ After a merge collision, run:
350
245
 
351
246
  ```sh
352
- driftseal absorb ../other-worktree/.intent-log/events.jsonl \
353
- --decisions ../other-worktree/.decision-log
247
+ driftseal absorb
354
248
  ```
355
249
 
356
- With no path, `absorb` repairs the current log after a git conflict or a
357
- concatenated duplicate. If both sides still have an open intent, pass
358
- `--abandon-theirs` or `--abandon-ours`. Concurrent edits of a decision that
359
- already existed in the shared base are not auto-merged.
250
+ Do not edit the JSONL manually. `absorb` remaps colliding outcome and decision
251
+ ids, rebinds affected contract hashes, and refuses concurrent edits of a shared
252
+ MADR. If both lineages remain open, choose explicitly with `--abandon-theirs` or
253
+ `--abandon-ours`.
360
254
 
361
- `driftseal init` writes that absorb rule into `AGENTS.md`, plus `.gitattributes`
362
- and the local git merge driver so `events.jsonl` merges through `absorb --git`.
363
- When decision ids collide, the driver stops the merge before Git can commit an
364
- ambiguous decision catalog. Run `driftseal absorb`, stage the repaired intent
365
- and decision logs, then continue the merge. Clones need `init` again because
366
- the driver lives in local git config.
255
+ ## Node API and MCP
367
256
 
368
- In a Git worktree, `begin` parks the open intent in Git metadata instead of
369
- appending to the tracked `events.jsonl`. Git can merge while that intent is
370
- still in progress, so you do not need a log-only commit just to get a clean
371
- tree. `end` moves the parked records into the tracked log and writes the closing
372
- record there — never into Git metadata — so an interrupted `end` leaves the
373
- intent open in the log and can simply be run again. If the parked intent's id
374
- collides with incoming merged events, DriftSeal remaps it the same way `absorb`
375
- remaps colliding worktree ids.
257
+ ```js
258
+ const { createApi } = require('driftseal');
376
259
 
377
- When a merge brings in a second open intent, `absorb --abandon-ours` closes the
378
- parked one into the tracked log and `absorb --abandon-theirs` closes the
379
- incoming one and leaves yours parked. `end <id>` also works on the incoming
380
- intent directly, and `begin --force` abandons every open intent at once.
260
+ const seal = createApi({ root: process.cwd(), isolateStorage: true });
261
+ seal.begin({
262
+ outcome: 'Ship account recovery',
263
+ acceptance: ['the recovery tests pass'],
264
+ verify: 'npm test',
265
+ });
266
+ seal.extend({ extension: 'Document token expiry' });
267
+ ```
268
+
269
+ The API also exposes `status`, `verify`, `end`, `log`, `absorb`, reclaim,
270
+ decision, init, and migration methods.
271
+
272
+ The stdio MCP server fixes all operations to one repository root. Its v2 tools
273
+ include `driftseal_status`, `driftseal_begin`, `driftseal_extend`,
274
+ `driftseal_verify`, `driftseal_end`, outcome history and absorb tools, MADR
275
+ tools, and the three migration tools. Resources are:
381
276
 
382
- ## Storage
277
+ - `driftseal://outcome/current`
278
+ - `driftseal://outcomes/recent`
279
+ - `driftseal://madr`
383
280
 
384
- - `.intent-log/events.jsonl` is the append-only intent log. All access goes through `driftseal` (CLI or MCP) — never read, edit, move, or delete it directly; use `driftseal reclaim` to retire meaningless records instead of deleting log lines. After a merge collision, use `driftseal absorb` instead of editing the file.
385
- - `.decision-log/` contains numbered MADR decision records.
386
- - Set `DRIFTSEAL_HOME` or `DRIFTSEAL_DECISION_HOME` to store either log outside the current project.
281
+ ## Storage and trust boundary
387
282
 
388
- Together, intent events, selective decision records, and Git commits form a layered project log: intent events capture what a work round set out to do and how it would be verified; decision records preserve rationale, rejected paths, or deferred choices that the other layers cannot reconstruct; commits show the coherent change that actually landed. DriftSeal complements Git history rather than duplicating or replacing it.
283
+ - `.seal/outcomes/events.jsonl` is the append-only outcome log. Access it through
284
+ DriftSeal; use `reclaim`, `unreclaim`, and `absorb` instead of manual edits.
285
+ - `.seal/madr/` stores numbered MADR documents.
286
+ - `$DRIFTSEAL_HOME` replaces the `.seal` root.
287
+ - Advisory hooks remind agents about lifecycle state but never broaden the
288
+ repository's `AGENTS.md` policy.
389
289
 
390
- Keeping the DriftSeal logs in version control makes the project’s working agreements and decision trail travel with the code. The npm package uses an explicit file allowlist, so project-local agent logs are not published to npm.
290
+ DriftSeal does not decide whether a verification command is safe or whether a
291
+ test is adequate. Inspect commands before execution and apply normal repository
292
+ authorization and security rules.
391
293
 
392
294
  ## Development
393
295
 
394
296
  ```sh
395
297
  npm test
298
+ node --check bin/driftseal.js
299
+ node --check bin/driftseal-mcp.js
300
+ npm pack --dry-run
396
301
  ```
397
302
 
398
- Contributions are welcome. Keep changes focused, add regression coverage for behavior changes, and run the test suite before opening a pull request.
399
-
400
- ## License
401
-
402
- MIT. See [`LICENSE`](LICENSE).
303
+ Licensed under the MIT License.