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 +223 -322
- package/README.zh-CN.md +199 -287
- package/bin/driftseal-mcp.js +156 -62
- package/bin/driftseal.js +1971 -352
- package/index.js +3 -0
- package/package.json +4 -3
- package/skills/use-driftseal/SKILL.md +19 -13
package/README.md
CHANGED
|
@@ -1,402 +1,303 @@
|
|
|
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` 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
|
-
|
|
37
|
+
## Install
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
DriftSeal requires Node.js 18 or newer.
|
|
30
40
|
|
|
31
41
|
```sh
|
|
32
42
|
npm install --global driftseal
|
|
33
|
-
|
|
34
|
-
driftseal init
|
|
43
|
+
driftseal --version
|
|
35
44
|
```
|
|
36
45
|
|
|
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:
|
|
46
|
+
From a source checkout:
|
|
47
47
|
|
|
48
48
|
```sh
|
|
49
|
-
npm
|
|
49
|
+
npm install
|
|
50
|
+
node bin/driftseal.js --version
|
|
50
51
|
```
|
|
51
52
|
|
|
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:
|
|
53
|
+
Adopt the protocol in a repository:
|
|
61
54
|
|
|
62
55
|
```sh
|
|
63
|
-
driftseal
|
|
64
|
-
driftseal skill install --target kimi-code --scope global
|
|
56
|
+
driftseal init
|
|
65
57
|
```
|
|
66
58
|
|
|
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` |
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
##
|
|
68
|
+
## Core workflow
|
|
83
69
|
|
|
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:
|
|
70
|
+
Open the coherent delivery outcome before changing durable project content:
|
|
90
71
|
|
|
91
72
|
```sh
|
|
92
|
-
driftseal
|
|
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
|
-
|
|
96
|
-
supported targets:
|
|
79
|
+
If another step is still part of that same delivered outcome, append it:
|
|
97
80
|
|
|
98
81
|
```sh
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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` |
|
|
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
|
-
|
|
120
|
-
agent's user-level config explicitly:
|
|
93
|
+
Before completion:
|
|
121
94
|
|
|
122
95
|
```sh
|
|
123
|
-
driftseal
|
|
96
|
+
driftseal status
|
|
97
|
+
driftseal verify
|
|
98
|
+
driftseal end --status completed --note "Shipped recovery with expiry documentation."
|
|
124
99
|
```
|
|
125
100
|
|
|
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:
|
|
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
|
-
|
|
172
|
-
driftseal
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
##
|
|
124
|
+
## Decisions and MADR
|
|
194
125
|
|
|
195
|
-
|
|
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
|
|
199
|
-
--
|
|
200
|
-
--
|
|
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
|
-
|
|
204
|
-
|
|
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
|
|
144
|
+
driftseal decision update 1 --status accepted --note "Confirmed by the final implementation."
|
|
208
145
|
```
|
|
209
146
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
`driftseal
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
|
247
|
-
--
|
|
248
|
-
--
|
|
249
|
-
--
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
353
|
-
--decisions ../other-worktree/.decision-log
|
|
247
|
+
driftseal absorb
|
|
354
248
|
```
|
|
355
249
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
277
|
+
- `driftseal://outcome/current`
|
|
278
|
+
- `driftseal://outcomes/recent`
|
|
279
|
+
- `driftseal://madr`
|
|
383
280
|
|
|
384
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
## License
|
|
401
|
-
|
|
402
|
-
MIT. See [`LICENSE`](LICENSE).
|
|
303
|
+
Licensed under the MIT License.
|