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.
@@ -79,14 +79,25 @@ function registerTools(server, api, z) {
79
79
  stdoutBytes: z.number().int().nonnegative(),
80
80
  stderrBytes: z.number().int().nonnegative(),
81
81
  workspace: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
82
+ contractHash: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
82
83
  head: z.string().nullable(),
83
84
  ranAt: z.string(),
84
85
  });
85
- const intentRecord = z.object({
86
+ const extensionRecord = z.object({
87
+ extension: z.string(),
88
+ acceptance: z.array(z.string()),
89
+ verify: z.string().nullable(),
90
+ decisions: z.array(z.string()),
91
+ extendedAt: z.string(),
92
+ head: z.string().nullable(),
93
+ });
94
+ const outcomeRecord = z.object({
86
95
  id: z.string(),
87
- intent: z.string(),
96
+ outcome: z.string(),
97
+ extensions: z.array(extensionRecord),
88
98
  acceptance: z.array(z.string()),
89
99
  verify: z.string().nullable(),
100
+ contractHash: z.string().regex(/^[a-f0-9]{64}$/),
90
101
  verification: verificationRecord.nullable(),
91
102
  decisions: z.array(z.string()),
92
103
  status: z.enum(END_STATUSES).or(z.literal('in_progress')),
@@ -99,6 +110,10 @@ function registerTools(server, api, z) {
99
110
  reclaimed: z.boolean(),
100
111
  reclaimReason: z.string().nullable(),
101
112
  reclaimedAt: z.string().nullable(),
113
+ imported: z.object({
114
+ sourceIds: z.array(z.string()),
115
+ sourceFingerprint: z.string().regex(/^[a-f0-9]{64}$/),
116
+ }).nullable(),
102
117
  readOnly: z.boolean().optional(),
103
118
  });
104
119
  const decisionRecord = z.object({
@@ -111,7 +126,7 @@ function registerTools(server, api, z) {
111
126
  const absorbResult = z.object({
112
127
  mappings: z.array(
113
128
  z.object({
114
- kind: z.enum(['intent', 'decision']),
129
+ kind: z.enum(['outcome', 'decision']),
115
130
  from: z.string(),
116
131
  to: z.string(),
117
132
  })
@@ -131,27 +146,27 @@ function registerTools(server, api, z) {
131
146
  server.registerTool(
132
147
  'driftseal_status',
133
148
  {
134
- title: 'Get current DriftSeal intent',
149
+ title: 'Get current DriftSeal outcome',
135
150
  description:
136
- 'Inspect the one intent currently in progress before repository work or after context loss. Returns null when no intent is open.',
151
+ 'Inspect the one outcome currently in progress before repository work or after context loss. Returns null when no outcome is open.',
137
152
  inputSchema: {},
138
153
  outputSchema: {
139
154
  root: z.string(),
140
- intent: intentRecord.nullable(),
155
+ outcome: outcomeRecord.nullable(),
141
156
  readOnly: z.boolean().optional(),
142
157
  },
143
158
  annotations: readOnly,
144
159
  },
145
160
  async () =>
146
161
  guarded(() => {
147
- const intent = api.status();
162
+ const outcome = api.status();
148
163
  const readOnly = api.readOnly;
149
- const snapshot = intent && readOnly ? { ...intent, readOnly: true } : intent;
164
+ const snapshot = outcome && readOnly ? { ...outcome, readOnly: true } : outcome;
150
165
  const summary = snapshot
151
- ? `Intent ${snapshot.id} is ${snapshot.status}.`
152
- : 'No DriftSeal intent is in progress.';
166
+ ? `Outcome ${snapshot.id} is ${snapshot.status}.`
167
+ : 'No DriftSeal outcome is in progress.';
153
168
  return success(
154
- { root: api.root, intent: snapshot, ...(readOnly ? { readOnly: true } : {}) },
169
+ { root: api.root, outcome: snapshot, ...(readOnly ? { readOnly: true } : {}) },
155
170
  readOnly ? `${summary} ${READ_ONLY_SUFFIX}` : summary
156
171
  );
157
172
  })
@@ -160,11 +175,11 @@ function registerTools(server, api, z) {
160
175
  server.registerTool(
161
176
  'driftseal_begin',
162
177
  {
163
- title: 'Begin a DriftSeal intent',
178
+ title: 'Begin a DriftSeal outcome',
164
179
  description:
165
- 'Open one focused work-round intent before making repository changes. Fails if another intent is already open; close it explicitly first.',
180
+ 'Open one coherent delivery outcome before making durable project changes. Fails if another outcome is already open.',
166
181
  inputSchema: {
167
- intent: nonEmpty.describe('Outcome this work round will accomplish.'),
182
+ outcome: nonEmpty.describe('Coherent delivery outcome this work will accomplish.'),
168
183
  acceptance: z
169
184
  .array(nonEmpty)
170
185
  .default([])
@@ -175,13 +190,35 @@ function registerTools(server, api, z) {
175
190
  .default([])
176
191
  .describe('Existing decision IDs this round may change or explicitly confirm.'),
177
192
  },
178
- outputSchema: { root: z.string(), intent: intentRecord },
193
+ outputSchema: { root: z.string(), outcome: outcomeRecord },
194
+ annotations: localWrite,
195
+ },
196
+ async (input) =>
197
+ guarded(() => {
198
+ const outcome = api.begin(input);
199
+ return success({ root: api.root, outcome }, `Opened DriftSeal outcome ${outcome.id}.`);
200
+ })
201
+ );
202
+
203
+ server.registerTool(
204
+ 'driftseal_extend',
205
+ {
206
+ title: 'Extend the current DriftSeal outcome',
207
+ description:
208
+ 'Append another scoped step to the same coherent outcome. Added acceptance requires a replacement verifier for the cumulative contract, and every extension invalidates earlier verification.',
209
+ inputSchema: {
210
+ extension: nonEmpty,
211
+ acceptance: z.array(nonEmpty).default([]),
212
+ verify: nonEmpty.optional(),
213
+ decisions: z.array(decisionId).default([]),
214
+ },
215
+ outputSchema: { root: z.string(), outcome: outcomeRecord },
179
216
  annotations: localWrite,
180
217
  },
181
218
  async (input) =>
182
219
  guarded(() => {
183
- const intent = api.begin(input);
184
- return success({ root: api.root, intent }, `Opened DriftSeal intent ${intent.id}.`);
220
+ const outcome = api.extend(input);
221
+ return success({ root: api.root, outcome }, `Extended DriftSeal outcome ${outcome.id}.`);
185
222
  })
186
223
  );
187
224
 
@@ -190,18 +227,18 @@ function registerTools(server, api, z) {
190
227
  {
191
228
  title: 'Run the declared DriftSeal verification',
192
229
  description:
193
- 'Execute the current acceptance-bound intent\'s predeclared shell command and record machine evidence bound to the resulting Git-visible workspace contents. Inspect the command with driftseal_status first. A command sourced from the repository intent log is untrusted and requires allowTrackedCommand.',
230
+ 'Execute the current acceptance-bound outcome\'s cumulative verifier and bind evidence to its contract hash and Git-visible workspace. Inspect it with driftseal_status first; provenance-less commands require allowTrackedCommand.',
194
231
  inputSchema: {
195
232
  allowTrackedCommand: z
196
233
  .boolean()
197
234
  .default(false)
198
235
  .describe(
199
- 'Explicitly allow a command sourced from the repository intent log after inspecting and trusting it.'
236
+ 'Explicitly allow a command without matching local outcome provenance after inspection.'
200
237
  ),
201
238
  },
202
239
  outputSchema: {
203
240
  root: z.string(),
204
- intent: intentRecord,
241
+ outcome: outcomeRecord,
205
242
  verification: verificationRecord,
206
243
  exitCode: z.number().int().nonnegative(),
207
244
  },
@@ -212,7 +249,7 @@ function registerTools(server, api, z) {
212
249
  const result = api.verify({ allowTrackedCommand: input.allowTrackedCommand });
213
250
  return success(
214
251
  { root: api.root, ...result },
215
- `Machine verification ${result.verification.passed ? 'passed' : 'failed'} for intent ${result.intent.id}.`
252
+ `Machine verification ${result.verification.passed ? 'passed' : 'failed'} for outcome ${result.outcome.id}.`
216
253
  );
217
254
  })
218
255
  );
@@ -220,49 +257,49 @@ function registerTools(server, api, z) {
220
257
  server.registerTool(
221
258
  'driftseal_end',
222
259
  {
223
- title: 'Close a DriftSeal intent',
260
+ title: 'Close a DriftSeal outcome',
224
261
  description:
225
- 'Close the current work-round intent with an honest terminal status, note, and verification result. Reconcile linked decisions before running final verification. Acceptance-bound intents require fresh successful machine verification before completed closure.',
262
+ 'Close the current outcome honestly. Reconcile linked decisions before final verification; completed acceptance-bound outcomes require fresh contract- and workspace-bound evidence.',
226
263
  inputSchema: {
227
- id: z.string().optional().describe('Intent ID; omit to close the current open intent.'),
264
+ id: z.string().optional().describe('Outcome ID; omit to close the current open outcome.'),
228
265
  status: closedStatus.default('completed'),
229
266
  note: nonEmpty.optional().describe('What actually happened in the round.'),
230
267
  verifyResult: nonEmpty.optional().describe('Concise, honest result of the declared verification.'),
231
268
  },
232
- outputSchema: { root: z.string(), intent: intentRecord },
269
+ outputSchema: { root: z.string(), outcome: outcomeRecord },
233
270
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
234
271
  },
235
272
  async (input) =>
236
273
  guarded(() => {
237
- const intent = api.end(input);
238
- return success({ root: api.root, intent }, `Closed DriftSeal intent ${intent.id} as ${intent.status}.`);
274
+ const outcome = api.end(input);
275
+ return success({ root: api.root, outcome }, `Closed DriftSeal outcome ${outcome.id} as ${outcome.status}.`);
239
276
  })
240
277
  );
241
278
 
242
279
  server.registerTool(
243
280
  'driftseal_log',
244
281
  {
245
- title: 'List DriftSeal intent history',
282
+ title: 'List DriftSeal outcome history',
246
283
  description:
247
- 'Review recent or complete DriftSeal intent history to re-anchor work and understand prior outcomes. Reclaimed records are hidden unless includeReclaimed is set.',
284
+ 'Review recent or complete DriftSeal outcome history. Reclaimed records are hidden unless includeReclaimed is set.',
248
285
  inputSchema: {
249
286
  last: z.number().int().positive().max(100).optional(),
250
287
  includeReclaimed: z.boolean().default(false),
251
288
  },
252
289
  outputSchema: {
253
290
  root: z.string(),
254
- intents: z.array(intentRecord),
291
+ outcomes: z.array(outcomeRecord),
255
292
  readOnly: z.boolean().optional(),
256
293
  },
257
294
  annotations: readOnly,
258
295
  },
259
296
  async (input) =>
260
297
  guarded(() => {
261
- const intents = api.log({ last: input.last, all: input.includeReclaimed });
298
+ const outcomes = api.log({ last: input.last, all: input.includeReclaimed });
262
299
  const readOnly = api.readOnly;
263
- const summary = `Found ${intents.length} DriftSeal intent records.`;
300
+ const summary = `Found ${outcomes.length} DriftSeal outcome records.`;
264
301
  return success(
265
- { root: api.root, intents, ...(readOnly ? { readOnly: true } : {}) },
302
+ { root: api.root, outcomes, ...(readOnly ? { readOnly: true } : {}) },
266
303
  readOnly ? `${summary} ${READ_ONLY_SUFFIX}` : summary
267
304
  );
268
305
  })
@@ -273,7 +310,7 @@ function registerTools(server, api, z) {
273
310
  {
274
311
  title: 'Absorb another DriftSeal lineage',
275
312
  description:
276
- 'Repair the fixed repository after a merge collision or absorb another worktree\'s intent and decision logs, remapping colliding IDs. Omit otherLog to repair the current repository. This rewrites only the fixed repository; incoming paths are read-only sources.',
313
+ 'Repair the fixed repository after a merge collision or absorb another worktree\'s outcome and MADR logs, remapping colliding IDs.',
277
314
  inputSchema: {
278
315
  otherLog: z
279
316
  .string()
@@ -286,7 +323,7 @@ function registerTools(server, api, z) {
286
323
  abandon: z
287
324
  .enum(['ours', 'theirs'])
288
325
  .optional()
289
- .describe('Side whose open intent to abandon when both lineages have one in progress.'),
326
+ .describe('Side whose open outcome to abandon when both lineages have one in progress.'),
290
327
  dryRun: z.boolean().default(false),
291
328
  },
292
329
  outputSchema: { root: z.string(), result: absorbResult },
@@ -306,14 +343,14 @@ function registerTools(server, api, z) {
306
343
  server.registerTool(
307
344
  'driftseal_reclaim',
308
345
  {
309
- title: 'Reclaim DriftSeal intent records',
346
+ title: 'Reclaim DriftSeal outcome records',
310
347
  description:
311
- 'Hide meaningless closed intent records (for example harness- or sandbox-caused failures) by appending reclaim markers. Never deletes log lines. Without ids, reclaims failed/abandoned, decision-unlinked records older than olderThan days.',
348
+ 'Hide meaningless closed outcome records behind append-only markers. Never deletes log lines.',
312
349
  inputSchema: {
313
350
  ids: z
314
351
  .array(z.string())
315
352
  .default([])
316
- .describe('Intent IDs to reclaim; omit for batch mode by age.'),
353
+ .describe('Outcome IDs to reclaim; omit for batch mode by age.'),
317
354
  reason: nonEmpty.describe('Why these records are meaningless (required, kept in the log).'),
318
355
  olderThan: z
319
356
  .number()
@@ -327,12 +364,12 @@ function registerTools(server, api, z) {
327
364
  .describe('Allow reclaiming partial/completed or decision-linked records by explicit id.'),
328
365
  dryRun: z.boolean().default(false),
329
366
  },
330
- outputSchema: { root: z.string(), intents: z.array(intentRecord) },
367
+ outputSchema: { root: z.string(), outcomes: z.array(outcomeRecord) },
331
368
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
332
369
  },
333
370
  async (input) =>
334
371
  guarded(() => {
335
- const intents = api.reclaim({
372
+ const outcomes = api.reclaim({
336
373
  ids: input.ids,
337
374
  reason: input.reason,
338
375
  olderThan: input.olderThan,
@@ -340,10 +377,10 @@ function registerTools(server, api, z) {
340
377
  dryRun: input.dryRun,
341
378
  });
342
379
  return success(
343
- { root: api.root, intents },
380
+ { root: api.root, outcomes },
344
381
  input.dryRun
345
- ? `${intents.length} DriftSeal intent records match.`
346
- : `Reclaimed ${intents.length} DriftSeal intent records.`
382
+ ? `${outcomes.length} DriftSeal outcome records match.`
383
+ : `Reclaimed ${outcomes.length} DriftSeal outcome records.`
347
384
  );
348
385
  })
349
386
  );
@@ -351,19 +388,19 @@ function registerTools(server, api, z) {
351
388
  server.registerTool(
352
389
  'driftseal_unreclaim',
353
390
  {
354
- title: 'Restore a reclaimed DriftSeal intent record',
355
- description: 'Restore one reclaimed intent record to the visible log by appending an unreclaim marker.',
391
+ title: 'Restore a reclaimed DriftSeal outcome record',
392
+ description: 'Restore one reclaimed outcome record to the visible log by appending an unreclaim marker.',
356
393
  inputSchema: {
357
394
  id: z.string(),
358
395
  reason: nonEmpty.describe('Why this record is being restored (required, kept in the log).'),
359
396
  },
360
- outputSchema: { root: z.string(), intent: intentRecord },
397
+ outputSchema: { root: z.string(), outcome: outcomeRecord },
361
398
  annotations: localWrite,
362
399
  },
363
400
  async (input) =>
364
401
  guarded(() => {
365
- const intent = api.unreclaim(input);
366
- return success({ root: api.root, intent }, `Restored DriftSeal intent ${intent.id}.`);
402
+ const outcome = api.unreclaim(input);
403
+ return success({ root: api.root, outcome }, `Restored DriftSeal outcome ${outcome.id}.`);
367
404
  })
368
405
  );
369
406
 
@@ -408,7 +445,7 @@ function registerTools(server, api, z) {
408
445
  {
409
446
  title: 'Add a DriftSeal decision',
410
447
  description:
411
- 'Create a MADR record only for durable rationale, rejected paths, deferred choices, or costly-to-reverse decisions that Git and the intent log cannot recover.',
448
+ 'Create a MADR record only for durable rationale, rejected paths, deferred choices, or costly-to-reverse decisions that Git and the outcome log cannot recover.',
412
449
  inputSchema: {
413
450
  title: nonEmpty,
414
451
  context: nonEmpty,
@@ -433,7 +470,7 @@ function registerTools(server, api, z) {
433
470
  {
434
471
  title: 'Reconcile a DriftSeal decision',
435
472
  description:
436
- 'Reconcile one decision linked to the current open intent, updating or explicitly confirming its status with a history note.',
473
+ 'Reconcile one decision linked to the current open outcome, updating or explicitly confirming its status with a history note.',
437
474
  inputSchema: {
438
475
  id: decisionId,
439
476
  status: decisionStatus.optional(),
@@ -448,6 +485,63 @@ function registerTools(server, api, z) {
448
485
  return success({ root: api.root, decision }, `Reconciled DriftSeal decision ${decision.id}.`);
449
486
  })
450
487
  );
488
+
489
+ const migrationPlan = z.object({
490
+ format: z.literal('driftseal-v1-to-v2-plan'),
491
+ sourceFingerprint: z.string().regex(/^[a-f0-9]{64}$/),
492
+ groups: z.array(z.object({
493
+ outcome: nonEmpty,
494
+ summary: nonEmpty,
495
+ sourceIds: z.array(z.string()).min(1),
496
+ })),
497
+ excluded: z.array(z.object({ sourceId: z.string(), reason: nonEmpty })).default([]),
498
+ });
499
+ const migrationSources = {
500
+ sourceLog: nonEmpty.optional().describe('Path to the v1 events.jsonl source.'),
501
+ sourceDecisions: nonEmpty.optional().describe('Path to the v1 MADR directory.'),
502
+ };
503
+ server.registerTool(
504
+ 'driftseal_migration_inspect',
505
+ {
506
+ title: 'Inspect a DriftSeal v1 repository for v2 migration',
507
+ description: 'Read and normalize custom or repository-default v1 logs for model-assisted grouping. The v2 destination is the fixed repository .seal root. Makes no changes.',
508
+ inputSchema: migrationSources,
509
+ outputSchema: { root: z.string(), inspection: z.unknown() },
510
+ annotations: readOnly,
511
+ },
512
+ async (locations) => guarded(() => {
513
+ const inspection = api.migrationInspect(locations);
514
+ return success({ root: api.root, inspection }, `Found ${inspection.records.length} closed v1 intent records.`);
515
+ })
516
+ );
517
+ server.registerTool(
518
+ 'driftseal_migration_apply',
519
+ {
520
+ title: 'Stage a validated DriftSeal v1-to-v2 migration',
521
+ description: 'Validate a model-generated ordered grouping plan, create .seal side-by-side, copy MADRs byte-for-byte, and never delete v1 data.',
522
+ inputSchema: { plan: migrationPlan, ...migrationSources },
523
+ outputSchema: { root: z.string(), result: z.unknown() },
524
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
525
+ },
526
+ async ({ plan, ...locations }) => guarded(() => {
527
+ const result = api.migrationApply({ plan, ...locations });
528
+ return success({ root: api.root, result }, 'Staged DriftSeal v2 without deleting v1 data.');
529
+ })
530
+ );
531
+ server.registerTool(
532
+ 'driftseal_migration_check',
533
+ {
534
+ title: 'Check a staged DriftSeal v1-to-v2 migration',
535
+ description: 'Validate the staged outcome log and manifest-backed MADRs, then report whether v1 still awaits manual removal.',
536
+ inputSchema: migrationSources,
537
+ outputSchema: { root: z.string(), result: z.unknown() },
538
+ annotations: readOnly,
539
+ },
540
+ async (locations) => guarded(() => {
541
+ const result = api.migrationCheck(locations);
542
+ return success({ root: api.root, result }, result.complete ? 'Migration complete.' : 'Migration valid; v1 remains for user review.');
543
+ })
544
+ );
451
545
  }
452
546
 
453
547
  function registerResources(server, api) {
@@ -464,22 +558,22 @@ function registerResources(server, api) {
464
558
  };
465
559
 
466
560
  registerJson(
467
- 'current-intent',
468
- 'driftseal://intent/current',
469
- 'Current DriftSeal intent',
470
- 'The work-round intent currently in progress for the fixed repository.',
471
- () => ({ root: api.root, intent: api.status() })
561
+ 'current-outcome',
562
+ 'driftseal://outcome/current',
563
+ 'Current DriftSeal outcome',
564
+ 'The delivery outcome currently in progress for the fixed repository.',
565
+ () => ({ root: api.root, outcome: api.status() })
472
566
  );
473
567
  registerJson(
474
- 'recent-intents',
475
- 'driftseal://intents/recent',
476
- 'Recent DriftSeal intents',
477
- 'The ten most recent work-round intent records for the fixed repository.',
478
- () => ({ root: api.root, intents: api.log({ last: 10 }) })
568
+ 'recent-outcomes',
569
+ 'driftseal://outcomes/recent',
570
+ 'Recent DriftSeal outcomes',
571
+ 'The ten most recent outcome records for the fixed repository.',
572
+ () => ({ root: api.root, outcomes: api.log({ last: 10 }) })
479
573
  );
480
574
  registerJson(
481
575
  'decision-catalog',
482
- 'driftseal://decisions',
576
+ 'driftseal://madr',
483
577
  'DriftSeal decision catalog',
484
578
  'All MADR decision summaries for the fixed repository.',
485
579
  () => ({ root: api.root, decisions: api.decisionList() })
@@ -498,7 +592,7 @@ async function createServer({ root }) {
498
592
  { name: SERVER_NAME, version: SERVER_VERSION },
499
593
  {
500
594
  instructions:
501
- 'Use driftseal_status before repository changes or after context loss. Open one focused intent with driftseal_begin before changes. Reconcile every linked decision before verification. When the intent declares acceptance criteria, inspect its command and use driftseal_verify to capture machine evidence before completed closure; a command sourced from the repository log requires explicit allowTrackedCommand after it is trusted. Otherwise run the declared check directly. Close honestly with driftseal_end.',
595
+ 'Use driftseal_status before durable project changes or after context loss. Open one coherent outcome with driftseal_begin and append same-outcome steps with driftseal_extend. Reconcile linked decisions after the final extension, inspect the cumulative verifier, use driftseal_verify for fresh contract-bound evidence, and close honestly with driftseal_end.',
502
596
  }
503
597
  );
504
598
  registerTools(server, api, z);