ultra-builder-pro-cli 0.24.2 → 0.25.1

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 (41) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/adapters/claude.js +104 -0
  3. package/adapters/codex.js +363 -40
  4. package/adapters/grok.js +1254 -58
  5. package/adapters/kimi.js +682 -13
  6. package/adapters/opencode.js +132 -11
  7. package/docs/AGENT-CONTEXT.md +14 -0
  8. package/docs/ARTIFACT-AUTHORITY.md +13 -0
  9. package/docs/COMMIT-HASH-BACKFILL.md +25 -18
  10. package/docs/RUNTIME-COMPAT-MATRIX.md +18 -2
  11. package/mcp-server/lib/artifact-registry.cjs +181 -51
  12. package/mcp-server/lib/baseline-workflow.cjs +160 -23
  13. package/mcp-server/lib/change-workflow.cjs +21 -16
  14. package/mcp-server/lib/context-envelope.cjs +124 -51
  15. package/mcp-server/lib/decision-records.cjs +164 -78
  16. package/mcp-server/lib/kernel-change-workflow.cjs +2029 -0
  17. package/mcp-server/lib/legacy-change-workflow.cjs +294 -71
  18. package/mcp-server/lib/managed-file-write.cjs +56 -0
  19. package/mcp-server/lib/prd-parser.cjs +2 -2
  20. package/mcp-server/lib/runtime-paths.cjs +43 -16
  21. package/mcp-server/lib/stage-checkpoints.cjs +163 -9
  22. package/mcp-server/lib/state-db.cjs +186 -8
  23. package/mcp-server/lib/state-ops.cjs +214 -31
  24. package/mcp-server/lib/task-contract.cjs +404 -0
  25. package/mcp-server/lib/task-expander.cjs +2 -2
  26. package/mcp-server/lib/task-ledger.cjs +175 -62
  27. package/mcp-server/lib/ultra-facade.cjs +1786 -169
  28. package/mcp-server/lib/worker-packet.cjs +2 -2
  29. package/mcp-server/server.cjs +23 -4
  30. package/orchestrator/parallel-orchestrator.cjs +114 -27
  31. package/orchestrator/session-runner.cjs +25 -136
  32. package/package.json +1 -1
  33. package/skills/ultra-change/SKILL.md +6 -1
  34. package/skills/ultra-dev/SKILL.md +9 -4
  35. package/skills/ultra-plan/SKILL.md +13 -4
  36. package/skills/ultra-plan/references/semantic-preflight.md +6 -4
  37. package/spec/mcp-tools.yaml +114 -9
  38. package/spec/schemas/context-file.v4.5.schema.json +2 -2
  39. package/spec/schemas/state-db.sql +10 -6
  40. package/spec/schemas/task-ledger.v2.schema.json +23 -8
  41. package/spec/schemas/tasks.v4.5.schema.json +34 -12
@@ -109,8 +109,64 @@ function writeManagedJson(rootDir, relativePath, value) {
109
109
  return writeManagedFile(rootDir, relativePath, `${JSON.stringify(value, null, 2)}\n`);
110
110
  }
111
111
 
112
+ function snapshotManagedFile(rootDir, relativePath) {
113
+ const relative = normalizeProjectRelative(relativePath);
114
+ const directoryRelative = path.posix.dirname(relative);
115
+ const basename = path.posix.basename(relative);
116
+ const prepared = ensureDirectory(rootDir, directoryRelative);
117
+ const target = path.join(prepared.directory, basename);
118
+ const stat = fs.lstatSync(target, { throwIfNoEntry: false });
119
+ if (!stat) return { path: relative, existed: false, bytes: null };
120
+ if (!stat.isFile() || stat.isSymbolicLink()) {
121
+ throw new ManagedFileWriteError(
122
+ 'PATH_AUTHORITY_VIOLATION',
123
+ `managed artifact target is unsafe: ${relative}`,
124
+ );
125
+ }
126
+ return {
127
+ path: relative,
128
+ existed: true,
129
+ bytes: fs.readFileSync(target),
130
+ };
131
+ }
132
+
133
+ function removeManagedFile(rootDir, relativePath) {
134
+ const relative = normalizeProjectRelative(relativePath);
135
+ const directoryRelative = path.posix.dirname(relative);
136
+ const basename = path.posix.basename(relative);
137
+ const prepared = ensureDirectory(rootDir, directoryRelative);
138
+ const target = path.join(prepared.directory, basename);
139
+ const stat = fs.lstatSync(target, { throwIfNoEntry: false });
140
+ if (!stat) return false;
141
+ if (!stat.isFile() || stat.isSymbolicLink()) {
142
+ throw new ManagedFileWriteError(
143
+ 'PATH_AUTHORITY_VIOLATION',
144
+ `managed artifact rollback target is unsafe: ${relative}`,
145
+ );
146
+ }
147
+ fs.unlinkSync(target);
148
+ return true;
149
+ }
150
+
151
+ function restoreManagedFile(rootDir, snapshot) {
152
+ if (!snapshot || typeof snapshot.path !== 'string') {
153
+ throw new ManagedFileWriteError(
154
+ 'PATH_AUTHORITY_VIOLATION',
155
+ 'managed artifact rollback snapshot is invalid',
156
+ );
157
+ }
158
+ if (snapshot.existed) {
159
+ return writeManagedFile(rootDir, snapshot.path, snapshot.bytes);
160
+ }
161
+ removeManagedFile(rootDir, snapshot.path);
162
+ return { path: snapshot.path, removed: true };
163
+ }
164
+
112
165
  module.exports = {
113
166
  ManagedFileWriteError,
167
+ removeManagedFile,
168
+ restoreManagedFile,
169
+ snapshotManagedFile,
114
170
  writeManagedFile,
115
171
  writeManagedJson,
116
172
  };
@@ -19,8 +19,8 @@ const TASK_LIST_SCHEMA = Object.freeze({
19
19
  properties: {
20
20
  id: { type: 'string', pattern: '^[a-zA-Z0-9_\\-]+$' },
21
21
  title: { type: 'string', minLength: 3, maxLength: 200 },
22
- type: { type: 'string', enum: ['architecture', 'feature', 'bugfix'] },
23
- priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'] },
22
+ type: { type: 'string', minLength: 1, maxLength: 80 },
23
+ priority: { type: 'string', minLength: 1, maxLength: 80 },
24
24
  complexity: { type: 'integer', minimum: 1, maximum: 10 },
25
25
  deps: { type: 'array', items: { type: 'string' } },
26
26
  files_modified: { type: 'array', items: { type: 'string' } },
@@ -599,15 +599,37 @@ function processIsAlive(pid) {
599
599
 
600
600
  function readMigrationGate(gatePath) {
601
601
  const stat = lstatOrNull(gatePath);
602
- if (!stat || stat.isSymbolicLink() || !stat.isFile()) return null;
602
+ if (!stat) return { missing: true };
603
+ if (stat.isSymbolicLink() || !stat.isFile()) return null;
603
604
  try {
604
605
  const owner = JSON.parse(fs.readFileSync(gatePath, 'utf8'));
605
606
  return { owner, stat };
606
- } catch {
607
+ } catch (error) {
608
+ if (error.code === 'ENOENT') return { missing: true };
607
609
  return null;
608
610
  }
609
611
  }
610
612
 
613
+ function createMigrationGateCandidate(gatePath, owner) {
614
+ while (true) {
615
+ const candidatePath = `${gatePath}.candidate-${process.pid}-${randomUUID()}`;
616
+ let descriptor;
617
+ try {
618
+ descriptor = fs.openSync(candidatePath, 'wx', 0o600);
619
+ fs.writeFileSync(descriptor, `${JSON.stringify(owner)}\n`);
620
+ fs.fsyncSync(descriptor);
621
+ return { candidatePath, descriptor };
622
+ } catch (error) {
623
+ if (descriptor !== undefined) {
624
+ try { fs.closeSync(descriptor); } catch { /* best effort */ }
625
+ }
626
+ fs.rmSync(candidatePath, { force: true });
627
+ if (error.code === 'EEXIST') continue;
628
+ throw error;
629
+ }
630
+ }
631
+ }
632
+
611
633
  function reclaimDeadMigrationGate(gatePath, observed) {
612
634
  const quarantine = `${gatePath}.stale-${process.pid}-${randomUUID()}`;
613
635
  fs.renameSync(gatePath, quarantine);
@@ -635,28 +657,28 @@ function acquireStateMigrationGate(paths, {
635
657
  } = {}) {
636
658
  const gatePath = path.join(paths.runtimeDir, 'state-migration.lock');
637
659
  const token = randomUUID();
660
+ const owner = {
661
+ version: 2,
662
+ pid: process.pid,
663
+ owner_started_at: processStartMarker(process.pid),
664
+ token,
665
+ legacy_state_db: paths.legacyStateDbPath,
666
+ runtime_state_db: paths.stateDbPath,
667
+ };
638
668
  const deadline = Date.now() + timeoutMs;
639
669
  let descriptor;
640
670
  while (descriptor === undefined) {
671
+ const candidate = createMigrationGateCandidate(gatePath, owner);
672
+ let published = false;
641
673
  try {
642
- descriptor = fs.openSync(gatePath, 'wx', 0o600);
643
- fs.writeFileSync(descriptor, `${JSON.stringify({
644
- version: 2,
645
- pid: process.pid,
646
- owner_started_at: processStartMarker(process.pid),
647
- token,
648
- legacy_state_db: paths.legacyStateDbPath,
649
- runtime_state_db: paths.stateDbPath,
650
- })}\n`);
651
- fs.fsyncSync(descriptor);
674
+ fs.linkSync(candidate.candidatePath, gatePath);
675
+ descriptor = candidate.descriptor;
676
+ published = true;
652
677
  break;
653
678
  } catch (error) {
654
- if (descriptor !== undefined) {
655
- try { fs.closeSync(descriptor); } catch { /* best effort */ }
656
- descriptor = undefined;
657
- }
658
679
  if (error.code !== 'EEXIST') throw error;
659
680
  const observed = readMigrationGate(gatePath);
681
+ if (observed?.missing) continue;
660
682
  const ownerPid = Number(observed?.owner?.pid);
661
683
  const live = processIsAlive(ownerPid);
662
684
  const currentStart = live ? processStartMarker(ownerPid) : null;
@@ -684,6 +706,11 @@ function acquireStateMigrationGate(paths, {
684
706
  continue;
685
707
  }
686
708
  reclaimDeadMigrationGate(gatePath, observed);
709
+ } finally {
710
+ fs.rmSync(candidate.candidatePath, { force: true });
711
+ if (!published) {
712
+ try { fs.closeSync(candidate.descriptor); } catch { /* best effort */ }
713
+ }
687
714
  }
688
715
  }
689
716
  return () => {
@@ -55,10 +55,10 @@ function normalizeDiagnostics(value) {
55
55
  if (!item || typeof item !== 'object' || Array.isArray(item)) {
56
56
  throw new StageCheckpointError('VALIDATION_ERROR', `diagnostics[${index}] must be an object`);
57
57
  }
58
- const code = String(item.code || '').trim();
59
- if (!code) {
58
+ if (typeof item.code !== 'string' || !item.code.trim()) {
60
59
  throw new StageCheckpointError('VALIDATION_ERROR', `diagnostics[${index}].code is required`);
61
60
  }
61
+ const code = item.code.trim();
62
62
  const severity = item.severity || 'needs_attention';
63
63
  if (!['warning', 'needs_attention', 'hard_conflict'].includes(severity)) {
64
64
  throw new StageCheckpointError(
@@ -66,15 +66,79 @@ function normalizeDiagnostics(value) {
66
66
  `diagnostics[${index}].severity is invalid`,
67
67
  );
68
68
  }
69
+ if (item.message !== undefined && typeof item.message !== 'string') {
70
+ throw new StageCheckpointError(
71
+ 'VALIDATION_ERROR',
72
+ `diagnostics[${index}].message must be a string`,
73
+ );
74
+ }
69
75
  return {
70
76
  code,
71
77
  severity,
72
- ...(item.message ? { message: String(item.message) } : {}),
78
+ ...(item.message ? { message: item.message } : {}),
73
79
  ...(item.details === undefined ? {} : { details: item.details }),
74
80
  };
75
81
  });
76
82
  }
77
83
 
84
+ function normalizePayload(value) {
85
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
86
+ throw new StageCheckpointError('VALIDATION_ERROR', 'checkpoint payload must be an object');
87
+ }
88
+ return value;
89
+ }
90
+
91
+ function normalizeEvidence(value) {
92
+ if (!Array.isArray(value)) {
93
+ throw new StageCheckpointError('VALIDATION_ERROR', 'evidence must be an array');
94
+ }
95
+ return value.map((item, index) => {
96
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
97
+ throw new StageCheckpointError('VALIDATION_ERROR', `evidence[${index}] must be an object`);
98
+ }
99
+ return item;
100
+ });
101
+ }
102
+
103
+ function idempotencyConflict(idempotencyKey, priorDigest, requestedDigest) {
104
+ return new StageCheckpointError(
105
+ 'IDEMPOTENCY_KEY_CONFLICT',
106
+ `idempotency key ${idempotencyKey} was already used for another checkpoint request`,
107
+ {
108
+ idempotency_key: idempotencyKey,
109
+ prior_request_digest: priorDigest,
110
+ requested_request_digest: requestedDigest,
111
+ },
112
+ );
113
+ }
114
+
115
+ function draftRequestDigest({
116
+ stage,
117
+ scopeType,
118
+ scopeId,
119
+ payload,
120
+ }) {
121
+ const requestPayload = stage === 'plan' && payload && typeof payload === 'object'
122
+ ? Object.fromEntries(Object.entries(payload).filter(([key]) => key !== 'plan'))
123
+ : payload;
124
+ return canonical.digest({
125
+ operation: 'checkpoint:draft',
126
+ stage,
127
+ scope_type: scopeType,
128
+ scope_id: scopeId,
129
+ payload: requestPayload,
130
+ });
131
+ }
132
+
133
+ function checkpointDraftRequestDigest(checkpoint) {
134
+ return draftRequestDigest({
135
+ stage: checkpoint.stage,
136
+ scopeType: checkpoint.scope_type,
137
+ scopeId: checkpoint.scope_id,
138
+ payload: checkpoint.payload,
139
+ });
140
+ }
141
+
78
142
  function checkpointDigest(value) {
79
143
  return canonical.digest({
80
144
  stage: value.stage,
@@ -151,6 +215,28 @@ function existingByIdempotency(db, idempotencyKey) {
151
215
  );
152
216
  }
153
217
 
218
+ function findIdempotentDraft(db, input = {}) {
219
+ validateStage(input.stage);
220
+ const scope = normalizeScope(input.scope);
221
+ const idempotencyKey = String(input.idempotency_key || '').trim();
222
+ if (!idempotencyKey) {
223
+ throw new StageCheckpointError('VALIDATION_ERROR', 'idempotency_key is required');
224
+ }
225
+ const existing = existingByIdempotency(db, idempotencyKey);
226
+ if (!existing) return null;
227
+ const requestedDigest = draftRequestDigest({
228
+ stage: input.stage,
229
+ scopeType: scope.type,
230
+ scopeId: scope.id,
231
+ payload: normalizePayload(input.payload),
232
+ });
233
+ const priorDigest = checkpointDraftRequestDigest(existing);
234
+ if (priorDigest !== requestedDigest) {
235
+ throw idempotencyConflict(idempotencyKey, priorDigest, requestedDigest);
236
+ }
237
+ return existing;
238
+ }
239
+
154
240
  function saveDraft(db, input = {}) {
155
241
  validateStage(input.stage);
156
242
  const scope = normalizeScope(input.scope);
@@ -158,12 +244,23 @@ function saveDraft(db, input = {}) {
158
244
  if (!idempotencyKey) {
159
245
  throw new StageCheckpointError('VALIDATION_ERROR', 'idempotency_key is required');
160
246
  }
247
+ const payload = normalizePayload(input.payload);
248
+ const evidence = normalizeEvidence(input.evidence);
249
+ const diagnostics = normalizeDiagnostics(input.diagnostics);
250
+ const requestedDigest = draftRequestDigest({
251
+ stage: input.stage,
252
+ scopeType: scope.type,
253
+ scopeId: scope.id,
254
+ payload,
255
+ });
161
256
  const existingRetry = existingByIdempotency(db, idempotencyKey);
162
- if (existingRetry) return existingRetry;
163
- const payload = input.payload && typeof input.payload === 'object' && !Array.isArray(input.payload)
164
- ? input.payload : {};
165
- const evidence = Array.isArray(input.evidence) ? input.evidence : [];
166
- const diagnostics = normalizeDiagnostics(input.diagnostics || []);
257
+ if (existingRetry) {
258
+ const priorDigest = checkpointDraftRequestDigest(existingRetry);
259
+ if (priorDigest !== requestedDigest) {
260
+ throw idempotencyConflict(idempotencyKey, priorDigest, requestedDigest);
261
+ }
262
+ return existingRetry;
263
+ }
167
264
  return ops.tx(db, () => {
168
265
  const draft = db.prepare(
169
266
  `SELECT * FROM stage_checkpoints
@@ -241,6 +338,33 @@ function saveDraft(db, input = {}) {
241
338
  });
242
339
  }
243
340
 
341
+ function acceptanceReceipt(db, idempotencyKey) {
342
+ if (!idempotencyKey) return null;
343
+ const rows = db.prepare(
344
+ `SELECT payload_json FROM events
345
+ WHERE type = 'ultra_checkpoint_accepted'
346
+ ORDER BY id DESC`,
347
+ ).all();
348
+ for (const row of rows) {
349
+ const payload = parseJson(row.payload_json, {});
350
+ if (payload.idempotency_key === idempotencyKey) return payload;
351
+ }
352
+ return null;
353
+ }
354
+
355
+ function acceptanceReceiptForCheckpoint(db, checkpointId) {
356
+ const rows = db.prepare(
357
+ `SELECT payload_json FROM events
358
+ WHERE type = 'ultra_checkpoint_accepted'
359
+ ORDER BY id DESC`,
360
+ ).all();
361
+ for (const row of rows) {
362
+ const payload = parseJson(row.payload_json, {});
363
+ if (payload.checkpoint_id === checkpointId && payload.idempotency_key) return payload;
364
+ }
365
+ return null;
366
+ }
367
+
244
368
  function acceptDraft(db, input = {}) {
245
369
  const idempotencyKey = String(input.idempotency_key || '').trim();
246
370
  if (!idempotencyKey) {
@@ -249,7 +373,34 @@ function acceptDraft(db, input = {}) {
249
373
  return ops.tx(db, () => {
250
374
  const row = db.prepare('SELECT * FROM stage_checkpoints WHERE id = ?').get(input.id);
251
375
  if (!row) throw new StageCheckpointError('CHECKPOINT_NOT_FOUND', `checkpoint not found: ${input.id}`);
252
- if (row.status === 'accepted') return rowToCheckpoint(row);
376
+ const checkpoint = rowToCheckpoint(row);
377
+ const requestedDigest = canonical.digest({
378
+ operation: 'checkpoint:accept',
379
+ checkpoint_id: checkpoint.id,
380
+ checkpoint_digest: checkpoint.digest,
381
+ });
382
+ const prior = acceptanceReceipt(db, idempotencyKey);
383
+ if (prior) {
384
+ if (prior.request_digest !== requestedDigest) {
385
+ throw idempotencyConflict(
386
+ idempotencyKey,
387
+ prior.request_digest || null,
388
+ requestedDigest,
389
+ );
390
+ }
391
+ return checkpoint;
392
+ }
393
+ if (row.status === 'accepted') {
394
+ const checkpointReceipt = acceptanceReceiptForCheckpoint(db, checkpoint.id);
395
+ if (checkpointReceipt) {
396
+ throw idempotencyConflict(
397
+ idempotencyKey,
398
+ checkpointReceipt.request_digest || null,
399
+ requestedDigest,
400
+ );
401
+ }
402
+ return checkpoint;
403
+ }
253
404
  if (row.status !== 'draft') {
254
405
  throw new StageCheckpointError(
255
406
  'CHECKPOINT_NOT_MUTABLE',
@@ -279,6 +430,8 @@ function acceptDraft(db, input = {}) {
279
430
  stage: accepted.stage,
280
431
  revision: accepted.revision,
281
432
  digest: accepted.digest,
433
+ idempotency_key: idempotencyKey,
434
+ request_digest: requestedDigest,
282
435
  },
283
436
  });
284
437
  return accepted;
@@ -303,6 +456,7 @@ module.exports = {
303
456
  readCheckpoint,
304
457
  listCheckpoints,
305
458
  saveDraft,
459
+ findIdempotentDraft,
306
460
  acceptDraft,
307
461
  currentCheckpoint,
308
462
  checkpointDigest,