ultra-builder-pro-cli 0.20.0 → 0.22.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 (109) hide show
  1. package/.ultra-template/docs/research/README.md +26 -5
  2. package/.ultra-template/specs/discovery.md +1 -1
  3. package/.ultra-template/tasks/tasks.json +10 -3
  4. package/.ultra-template/templates/task-context.md +19 -0
  5. package/CHANGELOG.md +106 -0
  6. package/README.md +215 -41
  7. package/adapters/_shared/codex-assets.cjs +69 -11
  8. package/adapters/_shared/interaction-contract.cjs +36 -3
  9. package/adapters/_shared/runtime-assets.cjs +41 -1
  10. package/adapters/claude.js +7 -5
  11. package/adapters/codex.js +1 -1
  12. package/adapters/kimi.js +2 -8
  13. package/adapters/opencode.js +43 -30
  14. package/bin/orchestrator.js +64 -24
  15. package/docs/AGENT-CONTEXT.md +89 -49
  16. package/docs/ARCHITECTURE.md +112 -45
  17. package/docs/ARTIFACT-AUTHORITY.md +274 -0
  18. package/docs/COMMIT-HASH-BACKFILL.md +33 -90
  19. package/docs/DECISIONS.md +14 -6
  20. package/docs/LEGACY-CLI-CROSSWALK.md +16 -6
  21. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -4
  22. package/docs/ROADMAP.md +20 -6
  23. package/docs/RUNTIME-COMPAT-MATRIX.md +29 -16
  24. package/docs/STATE-DB-ACCESS-POLICY.md +43 -13
  25. package/docs/WORKFLOW-LIFECYCLE.md +105 -50
  26. package/hooks/README.md +10 -8
  27. package/hooks/active_task_context.py +105 -18
  28. package/hooks/context_spine.py +16 -4
  29. package/hooks/health_check.py +5 -4
  30. package/hooks/pre_stop_check.py +5 -2
  31. package/hooks/runtime_paths.py +396 -0
  32. package/hooks/subagent_tracker.py +5 -2
  33. package/hooks/workflow_checkpoint.py +16 -3
  34. package/hooks/workflow_context.py +11 -3
  35. package/hooks/workflow_resume.py +7 -4
  36. package/mcp-server/breadcrumb.cjs +20 -2
  37. package/mcp-server/lib/archive-journal.cjs +1026 -111
  38. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  39. package/mcp-server/lib/artifact-registry.cjs +1127 -0
  40. package/mcp-server/lib/baseline-workflow.cjs +69 -28
  41. package/mcp-server/lib/change-packet.cjs +818 -0
  42. package/mcp-server/lib/change-workflow.cjs +932 -176
  43. package/mcp-server/lib/context-paths.cjs +142 -0
  44. package/mcp-server/lib/context-spine.cjs +511 -45
  45. package/mcp-server/lib/decision-dialogue.cjs +345 -3
  46. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  47. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  48. package/mcp-server/lib/doctor.cjs +401 -11
  49. package/mcp-server/lib/git-bootstrap.cjs +458 -47
  50. package/mcp-server/lib/init-project.cjs +240 -95
  51. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  52. package/mcp-server/lib/plan-store.cjs +700 -5
  53. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  54. package/mcp-server/lib/projector.cjs +211 -42
  55. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  56. package/mcp-server/lib/safe-project-file.cjs +385 -0
  57. package/mcp-server/lib/spec-learning.cjs +28 -12
  58. package/mcp-server/lib/state-authority.cjs +19 -3
  59. package/mcp-server/lib/state-db.cjs +263 -9
  60. package/mcp-server/lib/state-ops.cjs +254 -50
  61. package/mcp-server/lib/task-ledger.cjs +1342 -0
  62. package/mcp-server/lib/telemetry.cjs +12 -3
  63. package/mcp-server/lib/workflow-state.cjs +610 -58
  64. package/mcp-server/server.cjs +423 -42
  65. package/orchestrator/daemon.cjs +2 -10
  66. package/orchestrator/parallel-orchestrator.cjs +314 -50
  67. package/orchestrator/planner/plan-builder.cjs +1 -1
  68. package/orchestrator/recovery.cjs +125 -7
  69. package/orchestrator/session-close-journal-worker.cjs +5 -0
  70. package/orchestrator/session-close-journal.cjs +1014 -0
  71. package/orchestrator/session-runner.cjs +679 -69
  72. package/orchestrator/worktree-manager.cjs +186 -35
  73. package/package.json +2 -3
  74. package/skills/cc-collab/SKILL.md +4 -1
  75. package/skills/codex-collab/SKILL.md +5 -2
  76. package/skills/ultra-change/SKILL.md +34 -8
  77. package/skills/ultra-deliver/SKILL.md +26 -11
  78. package/skills/ultra-dev/SKILL.md +52 -10
  79. package/skills/ultra-doctor/SKILL.md +16 -3
  80. package/skills/ultra-init/SKILL.md +22 -11
  81. package/skills/ultra-plan/SKILL.md +58 -18
  82. package/skills/ultra-research/SKILL.md +37 -35
  83. package/skills/ultra-review/SKILL.md +14 -8
  84. package/skills/ultra-status/SKILL.md +18 -9
  85. package/skills/ultra-test/SKILL.md +11 -5
  86. package/skills/ultra-think/SKILL.md +6 -2
  87. package/skills/ultra-think/references/decision-dialogue.md +17 -6
  88. package/skills/ultra-verify/SKILL.md +4 -1
  89. package/spec/cli-protocol.md +21 -7
  90. package/spec/mcp-tools.yaml +629 -44
  91. package/spec/schemas/change-delta.v1.schema.json +153 -0
  92. package/spec/schemas/context-file.v4.5.schema.json +1 -1
  93. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  94. package/spec/schemas/mcp-tools.schema.json +1 -1
  95. package/spec/schemas/state-db.sql +51 -14
  96. package/spec/schemas/task-ledger.v1.schema.json +217 -0
  97. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  98. package/ultra-tools/cli.cjs +3 -2
  99. package/ultra-tools/commands/db.cjs +76 -25
  100. package/ultra-tools/commands/migrate.cjs +1000 -82
  101. package/ultra-tools/commands/session.cjs +34 -8
  102. package/ultra-tools/commands/status.cjs +32 -6
  103. package/ultra-tools/commands/system.cjs +129 -22
  104. package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
  105. package/commands/learn.md +0 -10
  106. package/orchestrator/skill-miner.cjs +0 -133
  107. package/output-styles/coding-vibes.md +0 -67
  108. package/output-styles/structural-thinking.md +0 -61
  109. package/skills/learn/SKILL.md +0 -58
@@ -5,10 +5,15 @@ const fs = require('node:fs');
5
5
  const path = require('node:path');
6
6
 
7
7
  const ops = require('./state-ops.cjs');
8
+ const artifactRegistry = require('./artifact-registry.cjs');
9
+ const { readStableProjectFile } = require('./safe-project-file.cjs');
8
10
 
9
11
  const ID = /^[a-zA-Z0-9_-]+$/;
10
12
  const MODES = new Set(['guided', 'fast', 'autonomous', 'diagnostic']);
11
13
  const ACTIVE_STATUSES = new Set(['active', 'checkpoint_ready']);
14
+ const APPLIED_REF_KINDS = new Set([
15
+ 'baseline', 'change', 'workflow', 'task', 'spec', 'artifact',
16
+ ]);
12
17
 
13
18
  class DecisionDialogueError extends Error {
14
19
  constructor(code, message, details) {
@@ -53,6 +58,281 @@ function plainObject(value, field, { allowEmpty = true } = {}) {
53
58
  return JSON.parse(JSON.stringify(value));
54
59
  }
55
60
 
61
+ function normalizeAppliedRefs(value) {
62
+ if (value === undefined) return [];
63
+ if (!Array.isArray(value) || value.length > 20) {
64
+ throw new DecisionDialogueError(
65
+ 'VALIDATION_ERROR', 'applied_refs must contain at most twenty authority references',
66
+ );
67
+ }
68
+ const seen = new Set();
69
+ return value.map((item, index) => {
70
+ const ref = plainObject(item, `applied_refs[${index}]`, { allowEmpty: false });
71
+ const kind = requiredText(ref.kind, `applied_refs[${index}].kind`);
72
+ if (!APPLIED_REF_KINDS.has(kind)) {
73
+ throw new DecisionDialogueError(
74
+ 'VALIDATION_ERROR', `unsupported applied authority kind: ${kind}`,
75
+ );
76
+ }
77
+ const target = requiredText(ref.ref, `applied_refs[${index}].ref`);
78
+ const field = ref.field === undefined
79
+ ? undefined
80
+ : requiredText(ref.field, `applied_refs[${index}].field`);
81
+ const digest = ref.digest === undefined
82
+ ? undefined
83
+ : requiredText(ref.digest, `applied_refs[${index}].digest`);
84
+ const hasValue = Object.hasOwn(ref, 'value');
85
+ const expectedValue = hasValue ? JSON.parse(JSON.stringify(ref.value)) : undefined;
86
+ if (digest !== undefined && !/^[0-9a-f]{64}$/.test(digest)) {
87
+ throw new DecisionDialogueError(
88
+ 'VALIDATION_ERROR', `applied_refs[${index}].digest must be a SHA-256 digest`,
89
+ );
90
+ }
91
+ const key = `${kind}\0${target}\0${field || ''}`;
92
+ if (seen.has(key)) {
93
+ throw new DecisionDialogueError('VALIDATION_ERROR', `duplicate applied authority ref: ${kind}:${target}`);
94
+ }
95
+ seen.add(key);
96
+ return {
97
+ kind,
98
+ ref: target,
99
+ ...(field === undefined ? {} : { field }),
100
+ ...(hasValue ? { value: expectedValue } : {}),
101
+ ...(digest === undefined ? {} : { digest }),
102
+ };
103
+ });
104
+ }
105
+
106
+ function canonicalJson(value) {
107
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
108
+ if (value && typeof value === 'object') {
109
+ return `{${Object.keys(value).sort().map(
110
+ (key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`,
111
+ ).join(',')}}`;
112
+ }
113
+ return JSON.stringify(value);
114
+ }
115
+
116
+ function valueDigest(value) {
117
+ return crypto.createHash('sha256').update(canonicalJson(value)).digest('hex');
118
+ }
119
+
120
+ function valueAtField(value, field) {
121
+ const segments = field.split('.').map((item) => item.trim());
122
+ if (segments.some((item) => !item || ['__proto__', 'prototype', 'constructor'].includes(item))) {
123
+ throw new DecisionDialogueError('VALIDATION_ERROR', `invalid applied authority field: ${field}`);
124
+ }
125
+ let current = value;
126
+ for (const segment of segments) {
127
+ if (current === null || current === undefined || typeof current !== 'object'
128
+ || !Object.hasOwn(current, segment)) {
129
+ throw new DecisionDialogueError(
130
+ 'DECISION_APPLIED_REF_FIELD_NOT_FOUND',
131
+ `applied authority field does not exist: ${field}`,
132
+ );
133
+ }
134
+ current = current[segment];
135
+ }
136
+ return current;
137
+ }
138
+
139
+ const ROW_FIELD_ALIASES = Object.freeze({
140
+ baseline: {
141
+ scope: 'scope_json',
142
+ worktree_files: 'worktree_files_json',
143
+ spec_refs: 'spec_refs_json',
144
+ evidence: 'evidence_json',
145
+ verification: 'verification_json',
146
+ unknowns: 'unknowns_json',
147
+ gaps: 'gaps_json',
148
+ classification: 'classification_json',
149
+ provider_refs: 'provider_refs_json',
150
+ },
151
+ change: {
152
+ docs_impact: 'docs_impact_json',
153
+ provider_refs: 'provider_refs_json',
154
+ baseline_bypass: 'baseline_bypass_json',
155
+ contract: 'contract_json',
156
+ classification: 'classification_json',
157
+ research_disposition: 'research_disposition_json',
158
+ },
159
+ workflow: {
160
+ metadata: 'metadata_json',
161
+ blockers: 'blockers_json',
162
+ approval: 'approval_json',
163
+ summary: 'summary_json',
164
+ },
165
+ task: {
166
+ acceptance: 'acceptance_json',
167
+ context_refs: 'context_refs_json',
168
+ docs_impact: 'docs_impact_json',
169
+ ownership: 'ownership_json',
170
+ },
171
+ });
172
+
173
+ function rowFieldValue(row, kind, field) {
174
+ const [head, ...tail] = field.split('.');
175
+ const column = ROW_FIELD_ALIASES[kind]?.[head] || head;
176
+ if (!Object.hasOwn(row, column)) {
177
+ throw new DecisionDialogueError(
178
+ 'DECISION_APPLIED_REF_FIELD_NOT_FOUND',
179
+ `applied ${kind} authority field does not exist: ${field}`,
180
+ );
181
+ }
182
+ let value = row[column];
183
+ if (column.endsWith('_json') && typeof value === 'string') {
184
+ try { value = JSON.parse(value); }
185
+ catch (error) {
186
+ throw new DecisionDialogueError(
187
+ 'STATE_CORRUPT', `invalid ${kind}.${column}: ${error.message}`,
188
+ );
189
+ }
190
+ }
191
+ return tail.length === 0 ? value : valueAtField(value, tail.join('.'));
192
+ }
193
+
194
+ function verifyAppliedValue(applied, actual) {
195
+ if (!Object.hasOwn(applied, 'value')) {
196
+ throw new DecisionDialogueError(
197
+ 'VALIDATION_ERROR',
198
+ `applied ${applied.kind} references require the exact field value`,
199
+ );
200
+ }
201
+ if (canonicalJson(actual) !== canonicalJson(applied.value)) {
202
+ throw new DecisionDialogueError(
203
+ 'DECISION_APPLIED_REF_VALUE_MISMATCH',
204
+ `applied ${applied.kind} field value does not match current authority: ${applied.ref}#${applied.field}`,
205
+ { expected: applied.value, actual },
206
+ );
207
+ }
208
+ if (applied.digest && applied.digest !== valueDigest(actual)) {
209
+ throw new DecisionDialogueError(
210
+ 'DECISION_APPLIED_REF_STALE',
211
+ `applied ${applied.kind} field digest is stale: ${applied.ref}#${applied.field}`,
212
+ { expected: applied.digest, actual: valueDigest(actual) },
213
+ );
214
+ }
215
+ }
216
+
217
+ function projectFile(rootDir, relative, field) {
218
+ if (path.isAbsolute(relative)) {
219
+ throw new DecisionDialogueError('VALIDATION_ERROR', `${field} must be project-relative`);
220
+ }
221
+ const root = path.resolve(rootDir);
222
+ const file = path.resolve(root, relative);
223
+ if (file !== root && !file.startsWith(`${root}${path.sep}`)) {
224
+ throw new DecisionDialogueError('VALIDATION_ERROR', `${field} escapes project root`);
225
+ }
226
+ return file;
227
+ }
228
+
229
+ function readAppliedProjectFile(rootDir, relative, label) {
230
+ try {
231
+ return readStableProjectFile(rootDir, relative);
232
+ } catch (error) {
233
+ if (error?.code === 'PROJECT_FILE_MISSING') {
234
+ throw new DecisionDialogueError(
235
+ 'DECISION_APPLIED_REF_NOT_FOUND',
236
+ `${label} is missing: ${relative}`,
237
+ );
238
+ }
239
+ throw new DecisionDialogueError(
240
+ 'DECISION_APPLIED_REF_UNSAFE',
241
+ `${label} cannot be read as physical project authority: ${relative}`,
242
+ { cause: error.code || error.message },
243
+ );
244
+ }
245
+ }
246
+
247
+ function registeredArtifactForRef(db, ref) {
248
+ const byId = db.prepare('SELECT id FROM artifacts WHERE id = ?').get(ref);
249
+ if (byId) return artifactRegistry.getArtifact(db, { id: byId.id });
250
+ try {
251
+ return artifactRegistry.getArtifact(db, { path: ref });
252
+ } catch (error) {
253
+ if (error?.code === 'ARTIFACT_NOT_FOUND') return null;
254
+ throw new DecisionDialogueError(
255
+ 'DECISION_APPLIED_REF_AUTHORITY_CONFLICT',
256
+ `applied artifact authority is ambiguous or invalid: ${ref}`,
257
+ { cause: error.code || error.message },
258
+ );
259
+ }
260
+ }
261
+
262
+ function verifyAppliedFileDigest(applied, read, registered = null) {
263
+ if (!applied.digest) {
264
+ throw new DecisionDialogueError(
265
+ 'VALIDATION_ERROR',
266
+ `applied ${applied.kind} references require the exact content digest`,
267
+ );
268
+ }
269
+ if (registered?.status === 'stale'
270
+ || (registered?.digest && registered.digest !== read.digest)
271
+ || applied.digest !== read.digest) {
272
+ throw new DecisionDialogueError(
273
+ 'DECISION_APPLIED_REF_STALE',
274
+ `applied ${applied.kind} digest is stale: ${registered?.path || applied.ref}`,
275
+ {
276
+ registered_digest: registered?.digest || null,
277
+ expected: applied.digest,
278
+ actual: read.digest,
279
+ },
280
+ );
281
+ }
282
+ }
283
+
284
+ function validateAppliedRefs(db, refs, { rootDir = process.cwd() } = {}) {
285
+ const tableByKind = {
286
+ baseline: 'baselines',
287
+ change: 'changes',
288
+ workflow: 'workflow_runs',
289
+ task: 'tasks',
290
+ };
291
+ for (const applied of refs) {
292
+ const table = tableByKind[applied.kind];
293
+ if (table) {
294
+ if (!applied.field) {
295
+ throw new DecisionDialogueError(
296
+ 'VALIDATION_ERROR',
297
+ `applied ${applied.kind} references require an exact field`,
298
+ );
299
+ }
300
+ const row = db.prepare(`SELECT * FROM ${table} WHERE id = ?`).get(applied.ref);
301
+ if (!row) {
302
+ throw new DecisionDialogueError(
303
+ 'DECISION_APPLIED_REF_NOT_FOUND',
304
+ `applied ${applied.kind} authority does not exist: ${applied.ref}`,
305
+ );
306
+ }
307
+ verifyAppliedValue(applied, rowFieldValue(row, applied.kind, applied.field));
308
+ continue;
309
+ }
310
+
311
+ if (applied.kind === 'artifact') {
312
+ const artifact = registeredArtifactForRef(db, applied.ref);
313
+ if (!artifact) {
314
+ throw new DecisionDialogueError(
315
+ 'DECISION_APPLIED_REF_NOT_FOUND',
316
+ `applied artifact authority is not registered: ${applied.ref}`,
317
+ );
318
+ }
319
+ const read = readAppliedProjectFile(
320
+ rootDir, artifact.path, 'applied artifact file',
321
+ );
322
+ verifyAppliedFileDigest(applied, read, artifact);
323
+ continue;
324
+ }
325
+
326
+ const registered = registeredArtifactForRef(db, applied.ref);
327
+ const read = readAppliedProjectFile(
328
+ rootDir,
329
+ registered?.path || applied.ref,
330
+ 'applied specification file',
331
+ );
332
+ verifyAppliedFileDigest(applied, read, registered);
333
+ }
334
+ }
335
+
56
336
  function normalizeOptions(value) {
57
337
  if (value === undefined) return [];
58
338
  if (!Array.isArray(value) || value.length > 3) {
@@ -385,9 +665,10 @@ function deferDecision(db, input = {}) {
385
665
  });
386
666
  }
387
667
 
388
- function completeDecisionThread(db, input = {}) {
668
+ function completeDecisionThread(db, input = {}, options = {}) {
389
669
  const id = requiredText(input.id, 'id');
390
670
  const summary = requiredText(input.summary, 'summary');
671
+ const appliedRefs = normalizeAppliedRefs(input.applied_refs);
391
672
  return ops.tx(db, () => {
392
673
  const thread = readDecisionThread(db, id);
393
674
  if (!thread) {
@@ -416,6 +697,7 @@ function completeDecisionThread(db, input = {}) {
416
697
  { blockers },
417
698
  );
418
699
  }
700
+ validateAppliedRefs(db, appliedRefs, options);
419
701
  const ts = nowIso();
420
702
  db.prepare(
421
703
  `UPDATE decision_threads
@@ -424,17 +706,76 @@ function completeDecisionThread(db, input = {}) {
424
706
  ).run(JSON.stringify({
425
707
  text: summary,
426
708
  completion_kind: 'normalized_state',
709
+ applied_refs: appliedRefs,
427
710
  completed_at: ts,
428
711
  }), ts, ts, id);
429
712
  ops.appendEventInTx(db, {
430
713
  type: 'decision_thread_completed',
431
714
  change_id: thread.change_id,
432
- payload: { thread_id: id, item_count: thread.items.length },
715
+ payload: {
716
+ thread_id: id,
717
+ item_count: thread.items.length,
718
+ applied_ref_count: appliedRefs.length,
719
+ },
433
720
  });
434
721
  return readDecisionThread(db, id);
435
722
  });
436
723
  }
437
724
 
725
+ function acceptedIntent(db, bindings = {}, { limit = 10 } = {}) {
726
+ const clauses = [];
727
+ const params = {};
728
+ if (bindings.workflow_run_id) {
729
+ clauses.push('workflow_run_id = @workflow_run_id');
730
+ params.workflow_run_id = bindings.workflow_run_id;
731
+ }
732
+ if (bindings.change_id) {
733
+ clauses.push('change_id = @change_id');
734
+ params.change_id = bindings.change_id;
735
+ }
736
+ if (bindings.baseline_id) {
737
+ clauses.push('(baseline_id = @baseline_id AND change_id IS NULL)');
738
+ params.baseline_id = bindings.baseline_id;
739
+ }
740
+ if (clauses.length === 0) return [];
741
+ const threadRows = db.prepare(
742
+ `SELECT id FROM decision_threads
743
+ WHERE status IN ('completed', 'confirmed')
744
+ AND (${clauses.join(' OR ')})
745
+ ORDER BY updated_at DESC, rowid DESC
746
+ LIMIT 100`,
747
+ ).all(params);
748
+ const items = [];
749
+ for (const row of threadRows) {
750
+ const thread = readDecisionThread(db, row.id);
751
+ const appliedRefs = Array.isArray(thread.summary.applied_refs)
752
+ ? thread.summary.applied_refs
753
+ : [];
754
+ for (const item of thread.items) {
755
+ if (!['answered', 'delegated', 'deferred'].includes(item.status)) continue;
756
+ const resolution = item.resolution || {};
757
+ items.push({
758
+ thread_id: thread.id,
759
+ decision_id: item.id,
760
+ status: item.status,
761
+ authority: resolution.authority || null,
762
+ decision: resolution.decision || null,
763
+ rationale: resolution.rationale || null,
764
+ decided_by: resolution.decided_by || null,
765
+ delegated_to: resolution.delegated_to || null,
766
+ consequences: resolution.consequences || null,
767
+ revisit_condition: resolution.revisit_condition || null,
768
+ effects: item.effects,
769
+ applied_refs: appliedRefs,
770
+ resolved_at: item.resolved_at,
771
+ });
772
+ }
773
+ }
774
+ return items
775
+ .sort((left, right) => String(right.resolved_at || '').localeCompare(String(left.resolved_at || '')))
776
+ .slice(0, Math.min(Math.max(Number(limit) || 10, 1), 20));
777
+ }
778
+
438
779
  function supersedeDecision(db, input = {}) {
439
780
  return ops.tx(db, () => {
440
781
  const id = requiredText(input.id, 'id');
@@ -663,7 +1004,7 @@ function decisionGate(db, bindings = {}, { rootDir = process.cwd() } = {}) {
663
1004
  params.change_id = bindings.change_id;
664
1005
  }
665
1006
  if (bindings.baseline_id) {
666
- clauses.push('(baseline_id = @baseline_id AND change_id IS NULL AND workflow_run_id IS NULL)');
1007
+ clauses.push('(baseline_id = @baseline_id AND change_id IS NULL)');
667
1008
  params.baseline_id = bindings.baseline_id;
668
1009
  }
669
1010
  if (clauses.length === 0) return { ready: true, blockers: [], thread: null, current_decision: null };
@@ -779,6 +1120,7 @@ module.exports = {
779
1120
  completeDecisionThread,
780
1121
  supersedeDecision,
781
1122
  checkpointDecisionThread,
1123
+ acceptedIntent,
782
1124
  decisionGate,
783
1125
  assertDecisionGate,
784
1126
  assertConfirmedDecisionCheckpoint,