ultra-builder-pro-cli 0.24.1 → 0.25.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +10 -0
  3. package/adapters/claude.js +104 -0
  4. package/adapters/codex.js +363 -40
  5. package/adapters/grok.js +1254 -58
  6. package/adapters/kimi.js +682 -13
  7. package/adapters/opencode.js +132 -11
  8. package/agents/code-reviewer.md +4 -5
  9. package/agents/debugger.md +8 -8
  10. package/docs/AGENT-CONTEXT.md +30 -4
  11. package/docs/ARCHITECTURE.md +24 -5
  12. package/docs/ARTIFACT-AUTHORITY.md +13 -0
  13. package/docs/COMMIT-HASH-BACKFILL.md +25 -18
  14. package/docs/RUNTIME-COMPAT-MATRIX.md +18 -2
  15. package/docs/STATE-DB-ACCESS-POLICY.md +8 -1
  16. package/mcp-server/lib/artifact-registry.cjs +188 -107
  17. package/mcp-server/lib/baseline-workflow.cjs +160 -23
  18. package/mcp-server/lib/change-packet.cjs +4 -32
  19. package/mcp-server/lib/change-workflow.cjs +36 -2755
  20. package/mcp-server/lib/context-envelope.cjs +124 -51
  21. package/mcp-server/lib/decision-records.cjs +164 -78
  22. package/mcp-server/lib/kernel-change-workflow.cjs +2029 -0
  23. package/mcp-server/lib/legacy-change-workflow.cjs +3032 -0
  24. package/mcp-server/lib/managed-file-write.cjs +56 -0
  25. package/mcp-server/lib/prd-parser.cjs +2 -2
  26. package/mcp-server/lib/stage-checkpoints.cjs +163 -9
  27. package/mcp-server/lib/state-db.cjs +186 -8
  28. package/mcp-server/lib/state-ops.cjs +214 -31
  29. package/mcp-server/lib/task-contract.cjs +404 -0
  30. package/mcp-server/lib/task-expander.cjs +8 -3
  31. package/mcp-server/lib/task-ledger.cjs +175 -62
  32. package/mcp-server/lib/ultra-facade.cjs +1895 -172
  33. package/mcp-server/lib/worker-packet.cjs +2 -2
  34. package/mcp-server/server.cjs +24 -5
  35. package/orchestrator/parallel-orchestrator.cjs +114 -27
  36. package/orchestrator/session-runner.cjs +25 -136
  37. package/package.json +1 -1
  38. package/skills/ultra-change/SKILL.md +6 -1
  39. package/skills/ultra-dev/SKILL.md +9 -4
  40. package/skills/ultra-plan/SKILL.md +13 -4
  41. package/skills/ultra-plan/references/semantic-preflight.md +6 -4
  42. package/spec/mcp-tools.yaml +114 -9
  43. package/spec/schemas/context-file.v4.5.schema.json +2 -2
  44. package/spec/schemas/state-db.sql +10 -6
  45. package/spec/schemas/task-ledger.v2.schema.json +23 -8
  46. package/spec/schemas/tasks.v4.5.schema.json +34 -12
@@ -19,6 +19,22 @@ const GAP_CATEGORIES = new Set([
19
19
  'technical_debt', 'unknown', 'future_change',
20
20
  ]);
21
21
  const GAP_STATUSES = new Set(['open', 'accepted', 'resolved', 'deferred']);
22
+ const BASELINE_START_FIELDS = new Set([
23
+ 'id', 'project_name', 'project_type', 'stack', 'mode', 'repository_revision',
24
+ 'replace_migrated', 'replace_ready', 'replacement_authorization', 'scope',
25
+ 'provider_refs', 'classification',
26
+ ]);
27
+ const BASELINE_REFRESH_FIELDS = new Set([
28
+ 'id', 'project_name', 'project_type', 'stack', 'classification',
29
+ ]);
30
+ const BASELINE_RECORD_FIELDS = new Set([
31
+ 'id', 'repository_revision', 'scope', 'spec_refs', 'evidence', 'verification',
32
+ 'unknowns', 'gaps', 'classification', 'provider_refs',
33
+ ]);
34
+ const BASELINE_CONVERGE_FIELDS = new Set([
35
+ 'id', 'expected_revision', 'approved_by', 'approval_note',
36
+ 'accept_dirty_worktree', 'accept_known_red',
37
+ ]);
22
38
 
23
39
  class BaselineWorkflowError extends Error {
24
40
  constructor(code, message, details) {
@@ -199,6 +215,46 @@ function nonEmpty(value, field) {
199
215
  return text;
200
216
  }
201
217
 
218
+ function optionalText(value, field, { nullable = false, emptyAsNull = false } = {}) {
219
+ if (value === undefined) return undefined;
220
+ if (value === null && nullable) return null;
221
+ if (typeof value !== 'string') {
222
+ throw new BaselineWorkflowError(
223
+ 'VALIDATION_ERROR',
224
+ `${field} must be a string${nullable ? ' or null' : ''}`,
225
+ );
226
+ }
227
+ const text = value.trim();
228
+ return emptyAsNull && !text ? null : text;
229
+ }
230
+
231
+ function optionalBoolean(input, field) {
232
+ if (Object.hasOwn(input, field) && typeof input[field] !== 'boolean') {
233
+ throw new BaselineWorkflowError('VALIDATION_ERROR', `${field} must be a boolean`);
234
+ }
235
+ }
236
+
237
+ function optionalBaselineId(value) {
238
+ if (value === undefined) return undefined;
239
+ const id = nonEmpty(value, 'id');
240
+ if (!BASELINE_ID.test(id)) {
241
+ throw new BaselineWorkflowError('VALIDATION_ERROR', 'baseline id is invalid');
242
+ }
243
+ return id;
244
+ }
245
+
246
+ function assertExactFields(value, allowed, field) {
247
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
248
+ throw new BaselineWorkflowError('VALIDATION_ERROR', `${field} must be an object`);
249
+ }
250
+ const unknown = Object.keys(value).filter((key) => !allowed.has(key));
251
+ if (unknown.length > 0) {
252
+ throw new BaselineWorkflowError(
253
+ 'VALIDATION_ERROR', `${field}.${unknown[0]} is not allowed`,
254
+ );
255
+ }
256
+ }
257
+
202
258
  function normalizeReplacementAuthorization(value) {
203
259
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
204
260
  throw new BaselineWorkflowError(
@@ -206,6 +262,13 @@ function normalizeReplacementAuthorization(value) {
206
262
  'replacing a ready baseline requires approved_by and reason',
207
263
  );
208
264
  }
265
+ const unknown = Object.keys(value).filter((key) => !['approved_by', 'reason'].includes(key));
266
+ if (unknown.length > 0) {
267
+ throw new BaselineWorkflowError(
268
+ 'VALIDATION_ERROR',
269
+ `replacement_authorization.${unknown[0]} is not allowed`,
270
+ );
271
+ }
209
272
  let approvedBy;
210
273
  let reason;
211
274
  try {
@@ -245,13 +308,14 @@ function normalizeScope(value, rootDir) {
245
308
  throw new BaselineWorkflowError('VALIDATION_ERROR', 'scope must be a non-empty array');
246
309
  }
247
310
  return [...new Set(scope.map((item, index) => {
311
+ const relative = nonEmpty(item, `scope[${index}]`);
248
312
  const resolved = safeRelativePath(rootDir, item, `scope[${index}]`);
249
313
  if (!fs.existsSync(resolved)) {
250
314
  throw new BaselineWorkflowError(
251
- 'BASELINE_SCOPE_MISSING', `baseline scope does not exist: ${String(item).trim()}`,
315
+ 'BASELINE_SCOPE_MISSING', `baseline scope does not exist: ${relative}`,
252
316
  );
253
317
  }
254
- return String(item).trim();
318
+ return relative;
255
319
  }))];
256
320
  }
257
321
 
@@ -291,6 +355,11 @@ function normalizeSpecRefs(value, rootDir) {
291
355
  if (!item || typeof item !== 'object' || Array.isArray(item) || !SPEC_KINDS.has(item.kind)) {
292
356
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid spec_refs[${index}]`);
293
357
  }
358
+ assertExactFields(
359
+ item,
360
+ new Set(['kind', 'path', 'digest']),
361
+ `spec_refs[${index}]`,
362
+ );
294
363
  const specPath = nonEmpty(item.path, `spec_refs[${index}].path`);
295
364
  const file = safeRelativePath(rootDir, specPath, `spec_refs[${index}].path`);
296
365
  if (!fs.existsSync(file) || !fs.statSync(file).isFile()) {
@@ -306,6 +375,11 @@ function normalizeEvidence(value, rootDir) {
306
375
  if (!item || typeof item !== 'object' || Array.isArray(item) || !EVIDENCE_KINDS.has(item.kind)) {
307
376
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid evidence[${index}]`);
308
377
  }
378
+ assertExactFields(
379
+ item,
380
+ new Set(['kind', 'ref', 'summary', 'digest']),
381
+ `evidence[${index}]`,
382
+ );
309
383
  const normalized = {
310
384
  kind: item.kind,
311
385
  ref: nonEmpty(item.ref, `evidence[${index}].ref`),
@@ -330,6 +404,11 @@ function normalizeVerification(value) {
330
404
  if (!item || typeof item !== 'object' || Array.isArray(item) || !VERIFICATION_STATUSES.has(item.status)) {
331
405
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid verification[${index}]`);
332
406
  }
407
+ assertExactFields(
408
+ item,
409
+ new Set(['name', 'command', 'status', 'evidence', 'rationale']),
410
+ `verification[${index}]`,
411
+ );
333
412
  const normalized = {
334
413
  name: nonEmpty(item.name, `verification[${index}].name`),
335
414
  command: nonEmpty(item.command, `verification[${index}].command`),
@@ -347,6 +426,16 @@ function normalizeUnknowns(value) {
347
426
  if (!item || typeof item !== 'object' || Array.isArray(item)) {
348
427
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid unknowns[${index}]`);
349
428
  }
429
+ assertExactFields(
430
+ item,
431
+ new Set(['summary', 'blocking', 'owner']),
432
+ `unknowns[${index}]`,
433
+ );
434
+ if (item.blocking !== undefined && typeof item.blocking !== 'boolean') {
435
+ throw new BaselineWorkflowError(
436
+ 'VALIDATION_ERROR', `unknowns[${index}].blocking must be a boolean`,
437
+ );
438
+ }
350
439
  const normalized = {
351
440
  summary: nonEmpty(item.summary, `unknowns[${index}].summary`),
352
441
  blocking: item.blocking === true,
@@ -363,6 +452,14 @@ function normalizeGaps(value) {
363
452
  if (!item || typeof item !== 'object' || Array.isArray(item)) {
364
453
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid gaps[${index}]`);
365
454
  }
455
+ assertExactFields(
456
+ item,
457
+ new Set([
458
+ 'id', 'category', 'status', 'blocking', 'summary',
459
+ 'evidence_refs', 'owner', 'resolution',
460
+ ]),
461
+ `gaps[${index}]`,
462
+ );
366
463
  const id = nonEmpty(item.id, `gaps[${index}].id`);
367
464
  if (!BASELINE_ID.test(id) || ids.has(id)) {
368
465
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid or duplicate gap id: ${id}`);
@@ -371,7 +468,12 @@ function normalizeGaps(value) {
371
468
  if (!GAP_CATEGORIES.has(item.category)) {
372
469
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid gap category: ${item.category}`);
373
470
  }
374
- const status = item.status || 'open';
471
+ if (item.blocking !== undefined && typeof item.blocking !== 'boolean') {
472
+ throw new BaselineWorkflowError(
473
+ 'VALIDATION_ERROR', `gaps[${index}].blocking must be a boolean`,
474
+ );
475
+ }
476
+ const status = item.status === undefined ? 'open' : item.status;
375
477
  if (!GAP_STATUSES.has(status)) {
376
478
  throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid gap status: ${status}`);
377
479
  }
@@ -418,9 +520,20 @@ function summarizeGaps(gaps = []) {
418
520
  }
419
521
 
420
522
  function startBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = true } = {}) {
421
- const id = input.id || 'project-baseline';
422
- if (!BASELINE_ID.test(id)) throw new BaselineWorkflowError('VALIDATION_ERROR', 'baseline id is invalid');
523
+ assertExactFields(input, BASELINE_START_FIELDS, 'baseline.start');
524
+ const id = optionalBaselineId(input.id) || 'project-baseline';
423
525
  const projectName = nonEmpty(input.project_name, 'project_name');
526
+ const projectType = optionalText(
527
+ input.project_type, 'project_type', { nullable: true, emptyAsNull: true },
528
+ ) ?? null;
529
+ const stack = optionalText(input.stack, 'stack', { nullable: true, emptyAsNull: true }) ?? null;
530
+ const repositoryRevision = optionalText(
531
+ input.repository_revision,
532
+ 'repository_revision',
533
+ { nullable: true, emptyAsNull: true },
534
+ ) ?? null;
535
+ optionalBoolean(input, 'replace_migrated');
536
+ optionalBoolean(input, 'replace_ready');
424
537
  if (!MODES.has(input.mode)) throw new BaselineWorkflowError('VALIDATION_ERROR', `invalid baseline mode: ${input.mode}`);
425
538
  if (readBaseline(db, id)) throw new BaselineWorkflowError('DUPLICATE_BASELINE_ID', `baseline ${id} exists`);
426
539
  const current = readBaseline(db);
@@ -464,7 +577,7 @@ function startBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = tr
464
577
  const providers = providerRefs.normalizeProviderRefs(input.provider_refs, BaselineWorkflowError);
465
578
  const classification = normalizeClassification(input.classification);
466
579
  const snapshot = gitWorktreeSnapshot(rootDir, scope);
467
- if (snapshot.head && input.repository_revision && input.repository_revision !== snapshot.head) {
580
+ if (snapshot.head && repositoryRevision && repositoryRevision !== snapshot.head) {
468
581
  throw new BaselineWorkflowError(
469
582
  'BASELINE_REVISION_MISMATCH', 'repository_revision does not match current Git HEAD',
470
583
  );
@@ -483,8 +596,8 @@ function startBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = tr
483
596
  worktree_files_json, classification_json, provider_refs_json, started_at, updated_at)
484
597
  VALUES (?, ?, ?, ?, ?, ?, '.', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
485
598
  ).run(
486
- id, projectName, input.project_type || null, input.stack || null, input.mode, status,
487
- JSON.stringify(scope), snapshot.head || input.repository_revision || null, snapshot.branch,
599
+ id, projectName, projectType, stack, input.mode, status,
600
+ JSON.stringify(scope), snapshot.head || repositoryRevision, snapshot.branch,
488
601
  snapshot.state, snapshot.digest, JSON.stringify(snapshot.files), JSON.stringify(classification),
489
602
  JSON.stringify(providers), ts, ts,
490
603
  );
@@ -509,7 +622,9 @@ function startBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = tr
509
622
  function refreshInProgressBaseline(db, input = {}, {
510
623
  rootDir = process.cwd(), emitEvent = true,
511
624
  } = {}) {
512
- const current = readBaseline(db, input.id);
625
+ assertExactFields(input, BASELINE_REFRESH_FIELDS, 'baseline.refresh');
626
+ const id = optionalBaselineId(input.id);
627
+ const current = readBaseline(db, id);
513
628
  if (!current) {
514
629
  throw new BaselineWorkflowError('BASELINE_NOT_FOUND', `baseline ${input.id || '(current)'} not found`);
515
630
  }
@@ -517,8 +632,12 @@ function refreshInProgressBaseline(db, input = {}, {
517
632
  const projectName = input.project_name === undefined
518
633
  ? current.project_name
519
634
  : nonEmpty(input.project_name, 'project_name');
520
- const projectType = input.project_type === undefined ? current.project_type : input.project_type;
521
- const stack = input.stack === undefined ? current.stack : input.stack;
635
+ const projectType = input.project_type === undefined
636
+ ? current.project_type
637
+ : optionalText(input.project_type, 'project_type', { nullable: true, emptyAsNull: true });
638
+ const stack = input.stack === undefined
639
+ ? current.stack
640
+ : optionalText(input.stack, 'stack', { nullable: true, emptyAsNull: true });
522
641
  const classification = input.classification === undefined
523
642
  ? current.classification
524
643
  : normalizeClassification(input.classification);
@@ -533,7 +652,7 @@ function refreshInProgressBaseline(db, input = {}, {
533
652
  worktree_digest = ?, worktree_files_json = ?, classification_json = ?,
534
653
  updated_at = ? WHERE id = ?`,
535
654
  ).run(
536
- projectName, projectType || null, stack || null,
655
+ projectName, projectType, stack,
537
656
  repositoryRevision, snapshot.branch, snapshot.state, snapshot.digest,
538
657
  JSON.stringify(snapshot.files), JSON.stringify(classification), ts, current.id,
539
658
  );
@@ -543,8 +662,8 @@ function refreshInProgressBaseline(db, input = {}, {
543
662
  payload: {
544
663
  baseline_id: current.id,
545
664
  project_name: projectName,
546
- project_type: projectType || null,
547
- stack: stack || null,
665
+ project_type: projectType,
666
+ stack,
548
667
  repository_revision: repositoryRevision,
549
668
  repository_branch: snapshot.branch,
550
669
  worktree_state: snapshot.state,
@@ -556,7 +675,14 @@ function refreshInProgressBaseline(db, input = {}, {
556
675
  }
557
676
 
558
677
  function recordBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = true } = {}) {
559
- const current = readBaseline(db, input.id);
678
+ assertExactFields(input, BASELINE_RECORD_FIELDS, 'baseline.record');
679
+ const id = optionalBaselineId(input.id);
680
+ const suppliedRevision = optionalText(
681
+ input.repository_revision,
682
+ 'repository_revision',
683
+ { nullable: true, emptyAsNull: true },
684
+ );
685
+ const current = readBaseline(db, id);
560
686
  if (!current) throw new BaselineWorkflowError('BASELINE_NOT_FOUND', `baseline ${input.id} not found`);
561
687
  if (['ready', 'superseded'].includes(current.status)) {
562
688
  throw new BaselineWorkflowError('BASELINE_NOT_MUTABLE', `baseline ${current.id} is ${current.status}`);
@@ -581,14 +707,14 @@ function recordBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = t
581
707
  'Git is initialized but has no commit; create an owner-authorized local checkpoint commit before recording the baseline',
582
708
  );
583
709
  }
584
- if (snapshot.head && input.repository_revision && input.repository_revision !== snapshot.head) {
710
+ if (snapshot.head && suppliedRevision && suppliedRevision !== snapshot.head) {
585
711
  throw new BaselineWorkflowError(
586
712
  'BASELINE_REVISION_MISMATCH', 'repository_revision does not match current Git HEAD',
587
713
  );
588
714
  }
589
- const revision = snapshot.head || (input.repository_revision === undefined
715
+ const revision = snapshot.head || (suppliedRevision === undefined
590
716
  ? (current.repository_revision || workspaceRevision({ scope, specs, evidence }, rootDir))
591
- : nonEmpty(input.repository_revision, 'repository_revision'));
717
+ : (suppliedRevision || workspaceRevision({ scope, specs, evidence }, rootDir)));
592
718
  const status = current.mode === 'brownfield' ? 'adopting' : 'draft';
593
719
  const ts = nowIso();
594
720
  return ops.tx(db, () => {
@@ -823,12 +949,22 @@ function convergenceWarnings(db, baseline) {
823
949
  }
824
950
 
825
951
  function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = true } = {}) {
826
- const current = readBaseline(db, input.id);
952
+ assertExactFields(input, BASELINE_CONVERGE_FIELDS, 'baseline.converge');
953
+ const normalizedInput = {
954
+ ...input,
955
+ id: optionalBaselineId(input.id),
956
+ expected_revision: optionalText(input.expected_revision, 'expected_revision'),
957
+ approved_by: optionalText(input.approved_by, 'approved_by'),
958
+ approval_note: optionalText(input.approval_note, 'approval_note'),
959
+ };
960
+ optionalBoolean(input, 'accept_dirty_worktree');
961
+ optionalBoolean(input, 'accept_known_red');
962
+ const current = readBaseline(db, normalizedInput.id);
827
963
  if (!current) throw new BaselineWorkflowError('BASELINE_NOT_FOUND', `baseline ${input.id} not found`);
828
964
  if (!['draft', 'adopting', 'blocked'].includes(current.status)) {
829
965
  throw new BaselineWorkflowError('BASELINE_NOT_CONVERGEABLE', `baseline ${current.id} is ${current.status}`);
830
966
  }
831
- const blockers = convergenceBlockers(db, current, input, rootDir);
967
+ const blockers = convergenceBlockers(db, current, normalizedInput, rootDir);
832
968
  const warnings = convergenceWarnings(db, current);
833
969
  const ts = nowIso();
834
970
  if (blockers.length > 0) {
@@ -857,9 +993,10 @@ function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent =
857
993
  research_run_id = NULL,
858
994
  converged_at = ?, updated_at = ? WHERE id = ?`,
859
995
  ).run(
860
- String(input.approved_by).trim(), String(input.approval_note).trim(),
861
- input.accept_dirty_worktree === true ? 1 : 0,
862
- input.accept_known_red === true ? 1 : 0, research?.id || null, ts, ts, current.id,
996
+ normalizedInput.approved_by, normalizedInput.approval_note,
997
+ normalizedInput.accept_dirty_worktree === true ? 1 : 0,
998
+ normalizedInput.accept_known_red === true ? 1 : 0,
999
+ research?.id || null, ts, ts, current.id,
863
1000
  );
864
1001
  if (emitEvent) {
865
1002
  ops.appendEventInTx(db, {
@@ -77,25 +77,8 @@ function requireInside(rootDir, relative, prefix, field) {
77
77
  return resolved;
78
78
  }
79
79
 
80
- function latestWorkflowConsumer(db, changeId) {
81
- const row = db.prepare(
82
- `SELECT id FROM workflow_runs
83
- WHERE change_id = ?
84
- ORDER BY CASE
85
- WHEN status IN ('active', 'blocked', 'ready') THEN 0
86
- WHEN status = 'completed' THEN 1
87
- ELSE 2 END,
88
- CASE kind WHEN 'deliver' THEN 0 WHEN 'plan' THEN 1 WHEN 'research' THEN 2 ELSE 3 END,
89
- updated_at DESC, rowid DESC
90
- LIMIT 1`,
91
- ).get(changeId);
92
- if (!row) {
93
- throw new ChangePacketError(
94
- 'ARTIFACT_CONSUMER_MISSING',
95
- `change ${changeId} has no workflow consumer`,
96
- );
97
- }
98
- return { type: 'workflow', id: row.id, relation: 'consumed_by' };
80
+ function deliveryConsumer() {
81
+ return { type: 'external', id: 'ultra-deliver', relation: 'consumed_by' };
99
82
  }
100
83
 
101
84
  function readCurrentArtifact(db, changeId, kind) {
@@ -118,17 +101,6 @@ function exactBaselineAnchor(db, change, input) {
118
101
  'typed Change delta requires one approved ready baseline',
119
102
  );
120
103
  }
121
- const changeRun = db.prepare(
122
- `SELECT baseline_id FROM workflow_runs
123
- WHERE kind = 'change' AND change_id = ?
124
- ORDER BY started_at DESC, rowid DESC LIMIT 1`,
125
- ).get(change.id);
126
- if (!changeRun || changeRun.baseline_id !== baseline.id) {
127
- throw new ChangePacketError(
128
- 'CHANGE_BASELINE_AUTHORITY_MISMATCH',
129
- `change ${change.id} is not bound to baseline ${baseline.id}`,
130
- );
131
- }
132
104
  const anchor = input.baseline_anchor;
133
105
  if (!anchor || anchor.baseline_id !== baseline.id
134
106
  || (anchor.repository_revision || null) !== (baseline.repository_revision || null)) {
@@ -371,7 +343,7 @@ function recordDelta(db, change, input, { rootDir = process.cwd() } = {}) {
371
343
  let rollbackProgress = null;
372
344
  try {
373
345
  const result = ops.tx(db, () => {
374
- const consumer = latestWorkflowConsumer(db, change.id);
346
+ const consumer = deliveryConsumer();
375
347
  const payloads = delta.mutations.map((mutation) => (
376
348
  artifacts.recordArtifactInTx(db, {
377
349
  id: `change-${change.id}-delta-${mutation.id}`,
@@ -578,7 +550,7 @@ function recordDocumentationReconciliation(
578
550
  let rollbackProgress = null;
579
551
  try {
580
552
  return ops.tx(db, () => {
581
- const fallbackConsumer = latestWorkflowConsumer(db, change.id);
553
+ const fallbackConsumer = deliveryConsumer();
582
554
  const payloads = normalized.reconciliation.documents.map((document, index) => (
583
555
  artifacts.recordArtifactInTx(db, {
584
556
  id: `change-${change.id}-documentation-${index + 1}`,