driftseal 3.3.0 → 3.4.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
@@ -2,11 +2,14 @@
2
2
 
3
3
  > **Seal the outcome. Stop the drift.**
4
4
 
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.
5
+ ![DriftSeal: a precision metal seal anchors engineering documents to one delivery outcome.](docs/images/driftseal-hero.png)
6
+
7
+ DriftSeal is an engineering protocol and toolchain for coding agents executing
8
+ long tasks independently, with state kept in the repository. It keeps work
9
+ anchored to a coherent delivery outcome: record the outcome before durable work
10
+ begins, append extensions toward that same outcome, bind verification to the
11
+ accumulated contract and workspace, and preserve the decisions that need durable
12
+ rationale.
10
13
 
11
14
  ```text
12
15
  begin an outcome → extend the same outcome → verify the cumulative contract → close
@@ -16,6 +19,19 @@ One worktree owns one open outcome. Git records what landed; DriftSeal records
16
19
  what the work was meant to achieve, how completion was proved, and why durable
17
20
  decisions were made.
18
21
 
22
+ ## When to use DriftSeal
23
+
24
+ Use DriftSeal when an agent needs to carry a multi-step engineering task through
25
+ to completion without continuous human guidance. Explicit acceptance criteria,
26
+ cumulative verification, and decision reconciliation provide a structured way to
27
+ track delivery and resume the same contract after context loss or handoff.
28
+
29
+ That rigor adds process overhead. When a person is actively reviewing progress,
30
+ clarifying scope, and guiding the agent, we recommend
31
+ [Inkan](https://github.com/rowan-hiro/inkan) as the lighter option. It records
32
+ delivery intent, changes, and declared results while leaving evaluation to the
33
+ person and the repository's normal tests.
34
+
19
35
  ## What changed in v2
20
36
 
21
37
  DriftSeal v2 is an outcome log rather than an intent-per-step log.
@@ -30,8 +46,8 @@ DriftSeal v2 is an outcome log rather than an intent-per-step log.
30
46
  and MADR reconciliation.
31
47
  - Stored events use `logVersion: 2`. Compatible clients accept `schemaVersion`
32
48
  `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.
49
+ - The generated `AGENTS.md` protocol series is `2.2`. `driftseal init` upgrades
50
+ recognized `2.0` and `2.1` blocks.
35
51
  - Named lanes partition outcome history on the same WAL. The default lane is
36
52
  `main`; `driftseal log` follows the current lane.
37
53
  - The public CLI, Node API, MCP tools, and MCP resources use outcome terminology.
@@ -75,6 +91,8 @@ may still write `.seal/outcomes/.gitignore` so derived sidecars stay untracked.
75
91
 
76
92
  ## Core workflow
77
93
 
94
+ ![One outcome progresses through begin, extend, verify, and end, accumulating changes before verification.](docs/images/driftseal-workflow.png)
95
+
78
96
  Open the coherent delivery outcome before changing durable project content:
79
97
 
80
98
  ```sh
@@ -98,6 +116,13 @@ verifier or replace it. Every extension invalidates previous machine evidence.
98
116
  If the delivery outcome itself changes, close the current outcome honestly and
99
117
  begin another one.
100
118
 
119
+ When the agent is in plan mode, write every `driftseal` command this work will
120
+ run into the plan file as an explicit command. Closing commands may leave
121
+ status and note as placeholders. As soon as plan mode ends, re-anchor before
122
+ any other action. End an unrelated open outcome before any lane switch. Switch
123
+ lanes when this work belongs to another lane. Then `begin` or `extend` to
124
+ match that re-anchored state.
125
+
101
126
  Before completion:
102
127
 
103
128
  ```sh
@@ -118,6 +143,8 @@ driftseal status
118
143
  driftseal log --last 3
119
144
  ```
120
145
 
146
+ ![Resume the same outcome across a context boundary by reading driftseal status and driftseal log --last 3.](docs/images/driftseal-continuity.png)
147
+
121
148
  ## What needs an outcome
122
149
 
123
150
  Record an outcome for durable project-content changes: code, configuration,
package/README.zh-CN.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  > **Seal the outcome. Stop the drift.**
4
4
 
5
- DriftSeal 是一套跟随 repo 保存的协议与工具,用来让 coding agent 始终围绕一个
6
- 完整的交付 outcome 工作。它要求在持久改动开始前记录 outcome,允许以 append-only
7
- 方式补充同一 outcome 的后续步骤,把验证结果绑定到累计 contract,并只为确实需要
8
- 长期保留理由的选择建立 MADR。
5
+ ![DriftSeal:精密金属封印将工程文档锚定在同一个交付目标上。](docs/images/driftseal-hero.png)
6
+
7
+ DriftSeal 是一套面向 Agent 独立执行长任务的工程化协议与工具链,状态随 repo 保存。
8
+ 它让 coding agent 始终围绕一个完整的交付 outcome 工作:在持久改动开始前记录
9
+ outcome,以 append-only 方式补充同一 outcome 的后续步骤,把验证结果绑定到累计
10
+ contract 和工作区,并只为确实需要长期保留理由的选择建立 MADR。
9
11
 
10
12
  ```text
11
13
  开启 outcome → 扩展同一 outcome → 验证累计 contract → 关闭
@@ -14,6 +16,16 @@ DriftSeal 是一套跟随 repo 保存的协议与工具,用来让 coding agent
14
16
  一个 worktree 只持有一个 open outcome。Git 记录最终落地了什么;DriftSeal 记录这轮
15
17
  工作想交付什么、如何证明完成,以及长期 decision 背后的理由。
16
18
 
19
+ ## 什么时候使用 DriftSeal
20
+
21
+ 当 Agent 需要在缺少人持续引导的情况下,独立推进并完成多步骤工程任务时,适合使用
22
+ DriftSeal。明确的验收标准、累计验证和 decision reconciliation 为交付提供结构化
23
+ 约束,也让 Agent 在上下文丢失或交接后,可以回到同一份交付约定继续执行。
24
+
25
+ 这些工程化约束也会增加流程成本。如果有人持续审阅进展、澄清范围并引导 Agent,
26
+ 推荐使用更轻量的 [Inkan](https://github.com/rowan-hiro/inkan)。它记录交付意图、
27
+ 过程中的变更和结束时声明的结果,把结果判断留给人和仓库原有的测试流程。
28
+
17
29
  ## v2 的变化
18
30
 
19
31
  DriftSeal v2 从“按步骤记录 intent”改为“按交付记录 outcome”。
@@ -25,7 +37,7 @@ DriftSeal v2 从“按步骤记录 intent”改为“按交付记录 outcome”
25
37
  - 每次 extend 都会改变 contract hash,并让之前的 verification 与 MADR reconciliation 失效。
26
38
  - event 使用 `logVersion: 2`。兼容客户端接受 `schemaVersion` `1` 或 `2`;lane
27
39
  事件以及非默认的 `begin.lane` 使用 `schemaVersion: 2`。
28
- - `AGENTS.md` 的新协议版本是 `2.1`。`driftseal init` 会升级可识别的 `2.0` block。
40
+ - `AGENTS.md` 的新协议版本是 `2.2`。`driftseal init` 会升级可识别的 `2.0` 和 `2.1` block。
29
41
  - 具名 lane 在同一条 WAL 上切分 outcome 历史。默认 lane 是 `main`;`driftseal log`
30
42
  跟随当前 lane。
31
43
  - CLI、Node API、MCP tool 与 resource 全部使用 outcome 命名;v1 名称和路径不会作为
@@ -68,6 +80,8 @@ attribute,并配置本地 Git merge driver。Git config 不会随 clone 传播
68
80
 
69
81
  ## 基本工作流
70
82
 
83
+ ![同一个 outcome 依次经过 begin、extend、verify 和 end,累积变更后统一验证。](docs/images/driftseal-workflow.png)
84
+
71
85
  在修改持久项目内容前,先开启完整的交付 outcome:
72
86
 
73
87
  ```sh
@@ -89,6 +103,11 @@ driftseal extend "Document recovery-link expiry" \
89
103
  acceptance 的 extend 可以沿用原 verifier,也可以替换它。任何 extend 都会让之前的
90
104
  machine evidence 失效。如果交付目标本身变了,应诚实关闭当前 outcome,再开启新的。
91
105
 
106
+ Agent 处于 plan 模式时,要把这次工作将要运行的每条 `driftseal` 命令作为显式命令
107
+ 写进计划文件。结束命令的 status 和 note 可以写成占位符。plan 模式一结束,先重新
108
+ 锚定,再做其他动作。若已有无关的 open outcome,在切换 lane 之前结束它。工作属于
109
+ 另一条 lane 时再切换。然后按重新锚定后的状态执行 `begin` 或 `extend`。
110
+
92
111
  完成前依次执行:
93
112
 
94
113
  ```sh
@@ -109,6 +128,8 @@ driftseal status
109
128
  driftseal log --last 3
110
129
  ```
111
130
 
131
+ ![通过 driftseal status 和 driftseal log --last 3 读取持久记录,在上下文中断后继续同一个交付目标。](docs/images/driftseal-continuity.png)
132
+
112
133
  ## 哪些工作需要 outcome
113
134
 
114
135
  准备长期留在项目中的代码、配置、文档、依赖及同类文件改动需要 outcome。Git 操作、
package/bin/driftseal.js CHANGED
@@ -56,7 +56,7 @@ const LOG_VERSION = 2;
56
56
  const EVENT_SCHEMA_VERSION = 2;
57
57
  const DEFAULT_WRITE_SCHEMA_VERSION = 1;
58
58
  const LEGACY_EVENT_SCHEMA_VERSION = 4;
59
- const PROTOCOL_VERSION = '2.1';
59
+ const PROTOCOL_VERSION = '2.2';
60
60
  const DEFAULT_LOG_LANGUAGE = 'en';
61
61
  const DEFAULT_LANE = 'main';
62
62
  const LANE_NAME_RE = /^[a-z][a-z0-9-]{0,62}$/;
@@ -2698,17 +2698,40 @@ Seal root: \`.seal/\` (override with \`$DRIFTSEAL_HOME\`); outcome log:
2698
2698
  ${INTENT_PROTOCOL_END}`;
2699
2699
  }
2700
2700
 
2701
- function intentProtocolBlockV21(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
2702
- return intentProtocolBlockV21Text(version, language, localLog, { historyRepair: true });
2701
+ function intentProtocolBlockV21(version = '2.1', language = DEFAULT_LOG_LANGUAGE, localLog = false) {
2702
+ if (String(version) !== '2.1') fail(`protocol block 2.1 cannot be stamped as version ${version}`);
2703
+ return intentProtocolBlockV21Text('2.1', language, localLog, { historyRepair: true });
2703
2704
  }
2704
2705
 
2705
- function intentProtocolBlockV21AbsorbCollisions(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
2706
- return intentProtocolBlockV21Text(version, language, localLog, { historyRepair: false });
2706
+ function intentProtocolBlockV21AbsorbCollisions(version = '2.1', language = DEFAULT_LOG_LANGUAGE, localLog = false) {
2707
+ if (String(version) !== '2.1') fail(`protocol block 2.1 cannot be stamped as version ${version}`);
2708
+ return intentProtocolBlockV21Text('2.1', language, localLog, { historyRepair: false });
2709
+ }
2710
+
2711
+ const PLAN_MODE_PROTOCOL_STEP = `5. **Plan mode writes the commands, then runs them first.** When the agent is
2712
+ in plan mode, write every \`driftseal\` command this work will run into the
2713
+ plan file as an explicit command. Closing commands may leave status and note
2714
+ as placeholders. As soon as plan mode ends, re-anchor before any other
2715
+ action. End an unrelated open outcome before any lane switch. Switch lanes
2716
+ when this work belongs to another lane. Then \`begin\` or \`extend\` to match
2717
+ that re-anchored state.`;
2718
+
2719
+ function intentProtocolBlockV22(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
2720
+ if (String(version) !== String(PROTOCOL_VERSION)) {
2721
+ fail(`protocol block ${PROTOCOL_VERSION} cannot be stamped as version ${version}`);
2722
+ }
2723
+ return intentProtocolBlockV21Text(PROTOCOL_VERSION, language, localLog, { historyRepair: true }).replace(
2724
+ '\n\n**Log access goes only through DriftSeal.**',
2725
+ `\n${PLAN_MODE_PROTOCOL_STEP}\n\n**Log access goes only through DriftSeal.**`
2726
+ );
2707
2727
  }
2708
2728
 
2709
2729
  function intentProtocolBlock(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
2710
- if (String(version) === '2.0') return intentProtocolBlockV20(language, localLog);
2711
- return intentProtocolBlockV21(version, language, localLog);
2730
+ const requested = String(version);
2731
+ if (requested === '2.0') return intentProtocolBlockV20(language, localLog);
2732
+ if (requested === '2.1') return intentProtocolBlockV21('2.1', language, localLog);
2733
+ if (requested === String(PROTOCOL_VERSION)) return intentProtocolBlockV22(PROTOCOL_VERSION, language, localLog);
2734
+ fail(`unsupported outcome protocol version "${version}"`);
2712
2735
  }
2713
2736
 
2714
2737
  function v1IntentProtocolBlock(version = 14, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
@@ -3002,13 +3025,22 @@ function protocolEol(content, eol) {
3002
3025
  return eol === '\n' ? content : content.replace(/\n/g, eol);
3003
3026
  }
3004
3027
 
3005
- function decisionProtocolBlockAbsorbCollisions(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
3006
- return decisionProtocolBlockText(version, language, localLog, { historyRepair: false });
3028
+ function decisionProtocolBlockAbsorbCollisions(version = '2.1', language = DEFAULT_LOG_LANGUAGE, localLog = false) {
3029
+ const requested = String(version);
3030
+ if (requested !== '2.0' && requested !== '2.1') {
3031
+ fail(`absorb-collisions decision protocol cannot be stamped as version ${version}`);
3032
+ }
3033
+ return decisionProtocolBlockText(requested, language, localLog, { historyRepair: false });
3007
3034
  }
3008
3035
 
3009
3036
  function decisionProtocolBlock(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false) {
3010
- if (String(version) === '2.0') return decisionProtocolBlockAbsorbCollisions(version, language, localLog);
3011
- return decisionProtocolBlockText(version, language, localLog, { historyRepair: true });
3037
+ const requested = String(version);
3038
+ if (requested === '2.0') return decisionProtocolBlockAbsorbCollisions('2.0', language, localLog);
3039
+ if (requested === '2.1') return decisionProtocolBlockText('2.1', language, localLog, { historyRepair: true });
3040
+ if (requested === String(PROTOCOL_VERSION)) {
3041
+ return decisionProtocolBlockText(PROTOCOL_VERSION, language, localLog, { historyRepair: true });
3042
+ }
3043
+ fail(`unsupported decision protocol version "${version}"`);
3012
3044
  }
3013
3045
 
3014
3046
  function decisionProtocolBlockText(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE, localLog = false, { historyRepair = true } = {}) {
@@ -3132,6 +3164,14 @@ function previousDecisionProtocolBlock(version, language = DEFAULT_LOG_LANGUAGE,
3132
3164
  return v8.replace(' --driver "<decision driver>"', '');
3133
3165
  }
3134
3166
 
3167
+ function customizedProtocolBlockError(marker) {
3168
+ return (
3169
+ `cannot safely upgrade customized protocol block beginning with ${marker}; ` +
3170
+ 'the block may come from a newer DriftSeal release even when its protocol version matches ' +
3171
+ `(current client: ${PACKAGE_VERSION}); upgrade DriftSeal and retry`
3172
+ );
3173
+ }
3174
+
3135
3175
  function upgradeManagedBlock({
3136
3176
  content,
3137
3177
  marker,
@@ -3182,7 +3222,7 @@ function upgradeManagedBlock({
3182
3222
  key === protocolBlockKey(replacement) ||
3183
3223
  knownManagedBlocks.some((known) => key === protocolBlockKey(known));
3184
3224
  if (!recognized) {
3185
- fail(`cannot safely upgrade customized protocol block beginning with ${marker}`);
3225
+ fail(customizedProtocolBlockError(marker));
3186
3226
  }
3187
3227
  return {
3188
3228
  content: content.slice(0, start) + replacement + content.slice(after),
@@ -3197,7 +3237,7 @@ function upgradeManagedBlock({
3197
3237
  found: true,
3198
3238
  };
3199
3239
  }
3200
- fail(`cannot safely upgrade customized protocol block beginning with ${marker}`);
3240
+ fail(customizedProtocolBlockError(marker));
3201
3241
  }
3202
3242
 
3203
3243
  const MCP_TARGETS = ['codex', 'kimi-code', 'opencode', 'claude-code', 'cursor'];
@@ -6845,8 +6885,10 @@ const commands = {
6845
6885
  ...sourceLanguages.flatMap((source) => [
6846
6886
  protocolEol(intentProtocolBlock(PROTOCOL_VERSION, source), eol),
6847
6887
  protocolEol(intentProtocolBlock(PROTOCOL_VERSION, source, true), eol),
6848
- protocolEol(intentProtocolBlockV21AbsorbCollisions(PROTOCOL_VERSION, source), eol),
6849
- protocolEol(intentProtocolBlockV21AbsorbCollisions(PROTOCOL_VERSION, source, true), eol),
6888
+ protocolEol(intentProtocolBlock('2.1', source), eol),
6889
+ protocolEol(intentProtocolBlock('2.1', source, true), eol),
6890
+ protocolEol(intentProtocolBlockV21AbsorbCollisions('2.1', source), eol),
6891
+ protocolEol(intentProtocolBlockV21AbsorbCollisions('2.1', source, true), eol),
6850
6892
  protocolEol(intentProtocolBlockV20(source), eol),
6851
6893
  protocolEol(intentProtocolBlockV20(source, true), eol),
6852
6894
  protocolEol(v1IntentProtocolBlock(14, source), eol),
@@ -6881,8 +6923,10 @@ const commands = {
6881
6923
  ...sourceLanguages.flatMap((source) => [
6882
6924
  protocolEol(decisionProtocolBlock(PROTOCOL_VERSION, source), eol),
6883
6925
  protocolEol(decisionProtocolBlock(PROTOCOL_VERSION, source, true), eol),
6884
- protocolEol(decisionProtocolBlockAbsorbCollisions(PROTOCOL_VERSION, source), eol),
6885
- protocolEol(decisionProtocolBlockAbsorbCollisions(PROTOCOL_VERSION, source, true), eol),
6926
+ protocolEol(decisionProtocolBlock('2.1', source), eol),
6927
+ protocolEol(decisionProtocolBlock('2.1', source, true), eol),
6928
+ protocolEol(decisionProtocolBlockAbsorbCollisions('2.1', source), eol),
6929
+ protocolEol(decisionProtocolBlockAbsorbCollisions('2.1', source, true), eol),
6886
6930
  protocolEol(decisionProtocolBlock('2.0', source), eol),
6887
6931
  protocolEol(decisionProtocolBlock('2.0', source, true), eol),
6888
6932
  protocolEol(v1DecisionProtocolBlock(14, source), eol),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "driftseal",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "Seal outcomes, verification, and decisions into an auditable workflow for agentic coding",
5
5
  "keywords": [
6
6
  "driftseal",