ultra-builder-pro-cli 0.20.0 → 0.21.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 (102) 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 +1 -1
  4. package/CHANGELOG.md +66 -0
  5. package/README.md +54 -24
  6. package/adapters/_shared/codex-assets.cjs +69 -11
  7. package/adapters/_shared/interaction-contract.cjs +36 -3
  8. package/adapters/_shared/runtime-assets.cjs +41 -1
  9. package/adapters/claude.js +7 -5
  10. package/adapters/codex.js +1 -1
  11. package/adapters/kimi.js +2 -8
  12. package/adapters/opencode.js +30 -28
  13. package/bin/orchestrator.js +64 -24
  14. package/docs/AGENT-CONTEXT.md +74 -43
  15. package/docs/ARCHITECTURE.md +63 -31
  16. package/docs/ARTIFACT-AUTHORITY.md +234 -0
  17. package/docs/DECISIONS.md +10 -4
  18. package/docs/LEGACY-CLI-CROSSWALK.md +6 -4
  19. package/docs/PLUGIN-ISOLATION-CONTRACT.md +4 -3
  20. package/docs/ROADMAP.md +19 -5
  21. package/docs/RUNTIME-COMPAT-MATRIX.md +24 -13
  22. package/docs/STATE-DB-ACCESS-POLICY.md +11 -9
  23. package/docs/WORKFLOW-LIFECYCLE.md +70 -39
  24. package/hooks/README.md +6 -5
  25. package/hooks/active_task_context.py +97 -12
  26. package/hooks/context_spine.py +16 -4
  27. package/hooks/health_check.py +5 -4
  28. package/hooks/pre_stop_check.py +5 -2
  29. package/hooks/runtime_paths.py +396 -0
  30. package/hooks/subagent_tracker.py +5 -2
  31. package/hooks/workflow_checkpoint.py +16 -3
  32. package/hooks/workflow_context.py +11 -3
  33. package/hooks/workflow_resume.py +7 -4
  34. package/mcp-server/breadcrumb.cjs +20 -2
  35. package/mcp-server/lib/archive-journal.cjs +1026 -111
  36. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  37. package/mcp-server/lib/artifact-registry.cjs +1126 -0
  38. package/mcp-server/lib/baseline-workflow.cjs +9 -4
  39. package/mcp-server/lib/change-packet.cjs +818 -0
  40. package/mcp-server/lib/change-workflow.cjs +932 -176
  41. package/mcp-server/lib/context-paths.cjs +111 -0
  42. package/mcp-server/lib/context-spine.cjs +511 -45
  43. package/mcp-server/lib/decision-dialogue.cjs +345 -3
  44. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  45. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  46. package/mcp-server/lib/doctor.cjs +373 -11
  47. package/mcp-server/lib/git-bootstrap.cjs +459 -47
  48. package/mcp-server/lib/init-project.cjs +190 -95
  49. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  50. package/mcp-server/lib/plan-store.cjs +700 -5
  51. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  52. package/mcp-server/lib/projector.cjs +174 -40
  53. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  54. package/mcp-server/lib/safe-project-file.cjs +385 -0
  55. package/mcp-server/lib/spec-learning.cjs +28 -12
  56. package/mcp-server/lib/state-db.cjs +263 -9
  57. package/mcp-server/lib/state-ops.cjs +248 -49
  58. package/mcp-server/lib/telemetry.cjs +12 -3
  59. package/mcp-server/lib/workflow-state.cjs +610 -58
  60. package/mcp-server/server.cjs +278 -33
  61. package/orchestrator/daemon.cjs +2 -10
  62. package/orchestrator/parallel-orchestrator.cjs +314 -50
  63. package/orchestrator/planner/plan-builder.cjs +1 -1
  64. package/orchestrator/recovery.cjs +125 -7
  65. package/orchestrator/session-close-journal-worker.cjs +5 -0
  66. package/orchestrator/session-close-journal.cjs +1014 -0
  67. package/orchestrator/session-runner.cjs +679 -69
  68. package/orchestrator/worktree-manager.cjs +186 -35
  69. package/package.json +2 -3
  70. package/skills/cc-collab/SKILL.md +4 -1
  71. package/skills/codex-collab/SKILL.md +5 -2
  72. package/skills/ultra-change/SKILL.md +26 -8
  73. package/skills/ultra-deliver/SKILL.md +23 -11
  74. package/skills/ultra-dev/SKILL.md +33 -6
  75. package/skills/ultra-doctor/SKILL.md +6 -2
  76. package/skills/ultra-init/SKILL.md +4 -2
  77. package/skills/ultra-plan/SKILL.md +49 -16
  78. package/skills/ultra-research/SKILL.md +30 -27
  79. package/skills/ultra-review/SKILL.md +9 -5
  80. package/skills/ultra-status/SKILL.md +9 -4
  81. package/skills/ultra-test/SKILL.md +10 -5
  82. package/skills/ultra-think/SKILL.md +6 -2
  83. package/skills/ultra-think/references/decision-dialogue.md +17 -6
  84. package/skills/ultra-verify/SKILL.md +4 -1
  85. package/spec/cli-protocol.md +18 -7
  86. package/spec/mcp-tools.yaml +502 -44
  87. package/spec/schemas/change-delta.v1.schema.json +153 -0
  88. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  89. package/spec/schemas/mcp-tools.schema.json +1 -1
  90. package/spec/schemas/state-db.sql +50 -13
  91. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  92. package/ultra-tools/cli.cjs +3 -2
  93. package/ultra-tools/commands/db.cjs +76 -25
  94. package/ultra-tools/commands/migrate.cjs +995 -80
  95. package/ultra-tools/commands/session.cjs +34 -8
  96. package/ultra-tools/commands/status.cjs +32 -6
  97. package/ultra-tools/commands/system.cjs +129 -22
  98. package/commands/learn.md +0 -10
  99. package/orchestrator/skill-miner.cjs +0 -133
  100. package/output-styles/coding-vibes.md +0 -67
  101. package/output-styles/structural-thinking.md +0 -61
  102. package/skills/learn/SKILL.md +0 -58
@@ -15,6 +15,7 @@ const {
15
15
  writeAtomic,
16
16
  } = require('./_shared/file-ops.cjs');
17
17
  const { buildMcpRuntime } = require('./_shared/codex-assets.cjs');
18
+ const { adaptInteractionGuidance } = require('./_shared/interaction-contract.cjs');
18
19
  const provenance = require('./_shared/provenance.cjs');
19
20
  const {
20
21
  CORE_PUBLIC_SKILLS,
@@ -67,7 +68,7 @@ function lowercaseFrontmatterTransform(buf, relPath) {
67
68
  }
68
69
 
69
70
  function openCodeTextTransform(input, assetName = '') {
70
- let text = String(input);
71
+ let text = adaptInteractionGuidance(input, 'opencode');
71
72
  text = text.replaceAll('$CLAUDE_PLUGIN_ROOT/skills', '~/.config/opencode/skills');
72
73
  text = text.replaceAll('~/.claude/skills', '~/.config/opencode/skills');
73
74
  text = text.replaceAll('~/.claude/hooks', '~/.config/opencode/.ultra-builder-pro/hooks');
@@ -96,9 +97,6 @@ function openCodeTextTransform(input, assetName = '') {
96
97
  'the OpenCode `task` tool using the installed bounded review agents',
97
98
  );
98
99
  }
99
- if (assetName === 'learn') {
100
- text = text.replaceAll("current host's user skill directory", '`~/.config/opencode/skills`');
101
- }
102
100
  return text;
103
101
  }
104
102
 
@@ -335,7 +333,6 @@ function preflightAssets(repoRoot, target) {
335
333
 
336
334
  function pluginSource() {
337
335
  return `// Managed by Ultra Builder Pro. Rebuild through the adapter; do not edit in place.
338
- import fs from "node:fs";
339
336
  import path from "node:path";
340
337
  import { execFileSync } from "node:child_process";
341
338
  import { fileURLToPath } from "node:url";
@@ -347,36 +344,29 @@ const BREADCRUMB_CLI = path.resolve(
347
344
  PLUGIN_DIR, "..", ${JSON.stringify(BUNDLE_DIR)}, "runtime", "breadcrumb.cjs",
348
345
  );
349
346
 
350
- function findUltraRoot(directory) {
351
- let current = path.resolve(directory);
352
- while (true) {
353
- const stateDb = path.join(current, ".ultra", "state.db");
354
- if (fs.existsSync(stateDb) && fs.statSync(stateDb).isFile()) return current;
355
- const parent = path.dirname(current);
356
- if (parent === current) return null;
357
- current = parent;
358
- }
359
- }
360
-
361
347
  function readUltraContext(directory) {
362
- const root = findUltraRoot(directory);
363
- if (!root) return null;
364
348
  try {
365
- const raw = execFileSync(NODE_BINARY, [BREADCRUMB_CLI, root], {
366
- cwd: root,
349
+ const raw = execFileSync(NODE_BINARY, [BREADCRUMB_CLI, "--discover", directory], {
350
+ cwd: directory,
367
351
  encoding: "utf8",
368
352
  stdio: ["ignore", "pipe", "pipe"],
369
353
  timeout: 5000,
370
354
  });
371
355
  const value = JSON.parse(raw);
356
+ const root = value?.root;
357
+ if (typeof root !== "string" || !root) return null;
372
358
  // A canonical breadcrumb is injectable only while DB authority proves an active workflow.
373
359
  if (!value?.breadcrumb || !value.breadcrumb.workflow
374
360
  || typeof value.text !== "string" || !value.text) {
375
361
  return { root, breadcrumb: null, text: null };
376
362
  }
377
363
  return { root, breadcrumb: value.breadcrumb, text: value.text };
378
- } catch {
379
- return { root, breadcrumb: null, text: null };
364
+ } catch (error) {
365
+ const detail = String(error?.stderr || error?.message || error);
366
+ if (/RUNTIME_(?:STATE_CONFLICT|PATH_UNSAFE|ORPHAN_SIDECAR|ROOT_INVALID|AUTHORITY_MISMATCH)|both legacy .*runtime.*state\\.db/i.test(detail)) {
367
+ throw new Error(detail.trim());
368
+ }
369
+ return null;
380
370
  }
381
371
  }
382
372
 
@@ -392,11 +382,22 @@ function targetPaths(tool, args) {
392
382
  return [...new Set(paths)];
393
383
  }
394
384
 
395
- function isTasksProjection(active, candidate) {
385
+ function projectionRootForTarget(baseRoot, candidate) {
396
386
  const target = path.isAbsolute(candidate)
397
387
  ? path.resolve(candidate)
398
- : path.resolve(active.root, candidate);
399
- return target === path.resolve(active.root, TASKS_PROJECTION);
388
+ : path.resolve(baseRoot, candidate);
389
+ if (path.basename(target) !== "tasks.json"
390
+ || path.basename(path.dirname(target)) !== "tasks"
391
+ || path.basename(path.dirname(path.dirname(target))) !== ".ultra") {
392
+ return null;
393
+ }
394
+ return path.dirname(path.dirname(path.dirname(target)));
395
+ }
396
+
397
+ function protectsTasksProjection(baseRoot, candidate) {
398
+ const targetRoot = projectionRootForTarget(baseRoot, candidate);
399
+ if (!targetRoot) return false;
400
+ return Boolean(readUltraContext(targetRoot));
400
401
  }
401
402
 
402
403
  export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
@@ -417,13 +418,14 @@ export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
417
418
  },
418
419
  "tool.execute.before": async (input, output) => {
419
420
  refresh();
420
- if (!active) return;
421
421
  const tool = String(input?.tool ?? "").toLowerCase();
422
422
  if (!["write", "edit", "apply_patch"].includes(tool)) return;
423
- if (targetPaths(tool, output?.args).some((candidate) => isTasksProjection(active, candidate))) {
423
+ if (targetPaths(tool, output?.args).some(
424
+ (candidate) => protectsTasksProjection(root, candidate),
425
+ )) {
424
426
  throw new Error(
425
427
  "Ultra Builder Pro refused a direct write to .ultra/tasks/tasks.json. " +
426
- ".ultra/state.db is authoritative; use MCP task tools and run ultra-doctor when state or projection health is degraded."
428
+ ".ultra/.runtime/state.db is authoritative; use MCP task tools and run ultra-doctor when state or projection health is degraded."
427
429
  );
428
430
  }
429
431
  },
@@ -6,7 +6,7 @@
6
6
  * Subcommands:
7
7
  * execute-plan resume the current plan through the first unfinished dependency wave.
8
8
  * run foreground daemon (debug / test).
9
- * start detached background daemon, writes .ultra/orchestrator.pid.
9
+ * start detached background daemon, writes .ultra/.runtime/orchestrator.pid.
10
10
  * stop reads pidfile, SIGTERM the process, deletes pidfile.
11
11
  * status prints pidfile + running session summary.
12
12
  *
@@ -23,10 +23,12 @@ const fs = require('node:fs');
23
23
  const path = require('node:path');
24
24
  const { spawn } = require('node:child_process');
25
25
  const { isSupportedRuntime } = require('../adapters/_shared/runtime-assets.cjs');
26
+ const runtimePaths = require('../mcp-server/lib/runtime-paths.cjs');
26
27
 
27
28
  const REPO_ROOT = process.env.UBP_REPO_ROOT || process.cwd();
28
- const PIDFILE = path.join(REPO_ROOT, '.ultra', 'orchestrator.pid');
29
- const LOGFILE = path.join(REPO_ROOT, '.ultra', 'orchestrator.log');
29
+ const RUNTIME_PATHS = runtimePaths.pathsFor(REPO_ROOT);
30
+ const PIDFILE = RUNTIME_PATHS.orchestratorPidPath;
31
+ const LOGFILE = RUNTIME_PATHS.orchestratorLogPath;
30
32
  const SETTINGS_FILES = [
31
33
  path.join(REPO_ROOT, 'settings.json'),
32
34
  path.join(REPO_ROOT, '.claude', 'settings.json'),
@@ -111,6 +113,7 @@ function resolveDispatchCommand(settings, env = process.env) {
111
113
  function parseExecutePlanArgs(argv = []) {
112
114
  const options = {
113
115
  planPath: null,
116
+ changeId: null,
114
117
  autoMerge: false,
115
118
  mergeBaseBranch: 'main',
116
119
  };
@@ -120,7 +123,7 @@ function parseExecutePlanArgs(argv = []) {
120
123
  options.autoMerge = true;
121
124
  continue;
122
125
  }
123
- if (arg === '--plan' || arg === '--base-branch') {
126
+ if (arg === '--plan' || arg === '--change' || arg === '--base-branch') {
124
127
  const value = argv[index + 1];
125
128
  if (!value || value.startsWith('--')) {
126
129
  throw commandConfigError(
@@ -129,6 +132,7 @@ function parseExecutePlanArgs(argv = []) {
129
132
  );
130
133
  }
131
134
  if (arg === '--plan') options.planPath = value;
135
+ else if (arg === '--change') options.changeId = value;
132
136
  else options.mergeBaseBranch = value;
133
137
  index += 1;
134
138
  continue;
@@ -138,21 +142,43 @@ function parseExecutePlanArgs(argv = []) {
138
142
  `unknown execute-plan option: ${arg}`,
139
143
  );
140
144
  }
145
+ if (options.planPath && options.changeId) {
146
+ throw commandConfigError(
147
+ 'ORCHESTRATOR_ARGUMENT_INVALID',
148
+ '--plan is explicit legacy compatibility and cannot be combined with --change',
149
+ );
150
+ }
141
151
  return options;
142
152
  }
143
153
 
154
+ function validateOrchestratorRuntime(rootDir = REPO_ROOT, {
155
+ forMutation = false,
156
+ } = {}) {
157
+ return runtimePaths.validateProjectLayout(rootDir, {
158
+ env: {},
159
+ forMutation,
160
+ validateRuntimeTree: true,
161
+ });
162
+ }
163
+
164
+ function openNoFollow(file, flags) {
165
+ const noFollow = fs.constants.O_NOFOLLOW || 0;
166
+ return fs.openSync(file, flags | noFollow, 0o600);
167
+ }
168
+
144
169
  async function cmdExecutePlan(argv = []) {
145
170
  const settings = readSettings();
146
171
  const dispatch = resolveDispatchCommand(settings);
147
172
  const options = parseExecutePlanArgs(argv);
148
173
  const { initStateDb, closeStateDb } = require('../mcp-server/lib/state-db.cjs');
149
174
  const parallelOrchestrator = require('../orchestrator/parallel-orchestrator.cjs');
150
- const { db } = initStateDb(path.join(REPO_ROOT, '.ultra', 'state.db'));
175
+ const { db } = initStateDb(runtimePaths.ensureRuntimeState(REPO_ROOT).stateDbPath);
151
176
  try {
152
177
  const result = await parallelOrchestrator.runPlan({
153
178
  db,
154
179
  repoRoot: REPO_ROOT,
155
180
  planPath: options.planPath ? path.resolve(REPO_ROOT, options.planPath) : undefined,
181
+ changeId: options.changeId,
156
182
  runtimes: parseRuntimes(),
157
183
  command: dispatch.command,
158
184
  commandArgs: dispatch.commandArgs,
@@ -187,7 +213,7 @@ function cmdRun(opts = {}) {
187
213
  const { runDaemon } = require('../orchestrator/daemon.cjs');
188
214
  const runtimes = parseRuntimes();
189
215
  const dispatch = resolveDispatchCommand(settings);
190
- const { db } = initStateDb(path.join(REPO_ROOT, '.ultra', 'state.db'));
216
+ const { db } = initStateDb(runtimePaths.ensureRuntimeState(REPO_ROOT).stateDbPath);
191
217
  const handle = runDaemon({
192
218
  db,
193
219
  repoRoot: REPO_ROOT,
@@ -221,39 +247,51 @@ function cmdStart() {
221
247
  process.exit(2);
222
248
  }
223
249
  resolveDispatchCommand(settings);
224
- if (fs.existsSync(PIDFILE)) {
225
- const existing = Number(fs.readFileSync(PIDFILE, 'utf8'));
250
+ const layout = validateOrchestratorRuntime(REPO_ROOT, { forMutation: true });
251
+ const pidfile = layout.orchestratorPidPath;
252
+ const logfile = layout.orchestratorLogPath;
253
+ if (fs.existsSync(pidfile)) {
254
+ const existing = Number(fs.readFileSync(pidfile, 'utf8'));
226
255
  try { process.kill(existing, 0); }
227
- catch (_) { fs.unlinkSync(PIDFILE); }
228
- if (fs.existsSync(PIDFILE)) {
256
+ catch (_) { fs.unlinkSync(pidfile); }
257
+ if (fs.existsSync(pidfile)) {
229
258
  process.stderr.write(`orchestrator already running (pid=${existing}).\n`);
230
259
  process.exit(1);
231
260
  }
232
261
  }
233
- fs.mkdirSync(path.dirname(PIDFILE), { recursive: true });
234
- const out = fs.openSync(LOGFILE, 'a');
235
- const err = fs.openSync(LOGFILE, 'a');
262
+ fs.mkdirSync(layout.orchestratorDir, { recursive: true });
263
+ const appendFlags = fs.constants.O_APPEND | fs.constants.O_CREAT | fs.constants.O_WRONLY;
264
+ const out = openNoFollow(logfile, appendFlags);
265
+ const err = openNoFollow(logfile, appendFlags);
236
266
  const child = spawn(process.execPath, [__filename, 'run'], {
237
267
  detached: true,
238
268
  stdio: ['ignore', out, err],
239
269
  env: { ...process.env, UBP_REPO_ROOT: REPO_ROOT },
240
270
  });
241
271
  child.unref();
242
- fs.writeFileSync(PIDFILE, String(child.pid));
243
- process.stdout.write(`orchestrator started (pid=${child.pid}, log=${LOGFILE})\n`);
272
+ const pidFd = openNoFollow(
273
+ pidfile,
274
+ fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_WRONLY,
275
+ );
276
+ try { fs.writeFileSync(pidFd, String(child.pid)); }
277
+ finally { fs.closeSync(pidFd); }
278
+ process.stdout.write(`orchestrator started (pid=${child.pid}, log=${logfile})\n`);
244
279
  }
245
280
 
246
281
  function cmdStop() {
247
- if (!fs.existsSync(PIDFILE)) {
282
+ const { orchestratorPidPath: pidfile } = validateOrchestratorRuntime(REPO_ROOT, {
283
+ forMutation: true,
284
+ });
285
+ if (!fs.existsSync(pidfile)) {
248
286
  process.stderr.write('no pidfile found; orchestrator not running.\n');
249
287
  process.exit(1);
250
288
  }
251
- const pid = Number(fs.readFileSync(PIDFILE, 'utf8'));
289
+ const pid = Number(fs.readFileSync(pidfile, 'utf8'));
252
290
  try { process.kill(pid, 'SIGTERM'); }
253
291
  catch (err) {
254
292
  if (err.code === 'ESRCH') {
255
293
  process.stderr.write(`pid ${pid} already dead; cleaning pidfile.\n`);
256
- fs.unlinkSync(PIDFILE);
294
+ fs.unlinkSync(pidfile);
257
295
  return;
258
296
  }
259
297
  throw err;
@@ -262,22 +300,23 @@ function cmdStop() {
262
300
  setTimeout(() => {
263
301
  try { process.kill(pid, 0); }
264
302
  catch (_) { /* dead; good */ }
265
- if (fs.existsSync(PIDFILE)) fs.unlinkSync(PIDFILE);
303
+ if (fs.existsSync(pidfile)) fs.unlinkSync(pidfile);
266
304
  process.stdout.write(`orchestrator stopped (pid=${pid})\n`);
267
305
  }, 200);
268
306
  }
269
307
 
270
308
  function cmdStatus() {
271
- const out = { pidfile: PIDFILE, running: false };
272
- if (fs.existsSync(PIDFILE)) {
273
- const pid = Number(fs.readFileSync(PIDFILE, 'utf8'));
309
+ const { orchestratorPidPath: pidfile } = validateOrchestratorRuntime(REPO_ROOT);
310
+ const out = { pidfile, running: false };
311
+ if (fs.existsSync(pidfile)) {
312
+ const pid = Number(fs.readFileSync(pidfile, 'utf8'));
274
313
  try { process.kill(pid, 0); out.pid = pid; out.running = true; }
275
314
  catch (_) { out.pid = pid; out.running = false; out.note = 'pidfile stale'; }
276
315
  }
277
316
  // Session summary.
278
317
  try {
279
318
  const Database = require('better-sqlite3');
280
- const dbPath = path.join(REPO_ROOT, '.ultra', 'state.db');
319
+ const dbPath = runtimePaths.locateStateDb(REPO_ROOT);
281
320
  if (fs.existsSync(dbPath)) {
282
321
  const db = new Database(dbPath, { readonly: true });
283
322
  const counts = db.prepare(
@@ -297,7 +336,7 @@ function usage() {
297
336
  process.stderr.write(
298
337
  'usage: ubp-orchestrator <execute-plan|run|start|stop|status>\n' +
299
338
  '\n' +
300
- ' execute-plan resume the first unfinished dependency wave [--plan <path>] [--auto-merge] [--base-branch <name>]\n' +
339
+ ' execute-plan resume the first unfinished dependency wave [--change <id>] [--plan <legacy-path>] [--auto-merge] [--base-branch <name>]\n' +
301
340
  ' run foreground daemon (requires opt-in)\n' +
302
341
  ' start detached background daemon (requires opt-in)\n' +
303
342
  ' stop terminate running daemon\n' +
@@ -343,6 +382,7 @@ module.exports = {
343
382
  parseRuntimes,
344
383
  parseExecutePlanArgs,
345
384
  resolveDispatchCommand,
385
+ validateOrchestratorRuntime,
346
386
  cmdExecutePlan,
347
387
  cmdRun,
348
388
  cmdStart,
@@ -7,9 +7,9 @@ and Kimi Code plugins.
7
7
 
8
8
  Ultra Builder Pro owns only:
9
9
 
10
- - twelve public workflows: `learn`, `ultra-init`, `ultra-research`, `ultra-plan`,
11
- `ultra-dev`, `ultra-test`, `ultra-review`, `ultra-deliver`, `ultra-status`, and
12
- `ultra-think`, plus the daily `ultra-change` and diagnostic `ultra-doctor`;
10
+ - eleven public capabilities: `ultra-init`, `ultra-research`, `ultra-plan`,
11
+ `ultra-dev`, `ultra-test`, `ultra-review`, `ultra-deliver`, `ultra-status`,
12
+ `ultra-think`, `ultra-change`, and `ultra-doctor`;
13
13
  - four internal agent-only rule skills: `code-review-expert`, `security-rules`,
14
14
  `integration-rules`, and `testing-rules`;
15
15
  - the host-specific collaboration companions, `ultra-verify`, and host-native
@@ -25,7 +25,7 @@ not put it in a plugin until that allowlist classifies it.
25
25
  ## 2. Three-layer architecture
26
26
 
27
27
  ```text
28
- native command or skill -> MCP workflow-state operation -> .ultra/state.db
28
+ native command or skill -> MCP workflow-state operation -> .ultra/.runtime/state.db
29
29
  ^
30
30
  |
31
31
  selected CLI init / doctor / diagnostics / orchestration
@@ -34,27 +34,33 @@ native command or skill -> MCP workflow-state operation -> .ultra/state.db
34
34
  | Layer | Artifact | Contract |
35
35
  |---|---|---|
36
36
  | Skill/command | `skills/*/SKILL.md`, `commands/*.md` | Model-facing workflow and host-native entry point |
37
- | MCP | `mcp-server/server.cjs` | Authoritative typed operations over `.ultra/state.db` |
37
+ | MCP | `mcp-server/server.cjs` | Authoritative typed operations over `.ultra/.runtime/state.db` |
38
38
  | CLI | `ultra-tools`, `ubp-orchestrator` | Selected initialization, backup-first recovery, automation, and diagnostics; not a change-state mirror |
39
39
 
40
- `.ultra/state.db` is the only durable Ultra authority for baselines, changes, decisions, tasks,
41
- workflow runs/steps, sessions, events, incidents, projection jobs/cursors, telemetry, review evidence,
42
- and circuit-breaker state. `tasks.json`, context Markdown, execution plans, and
43
- reports are projections or workflow artifacts.
40
+ `.ultra/` is project-local cross-session workflow memory for normalized intent,
41
+ progress, tasks, bounded context, specifications, evidence, provenance, and recovery.
42
+ `.ultra/.runtime/state.db` is the only lifecycle, index, transition, freshness, and
43
+ coordination authority for baselines, changes, decisions, tasks, workflow runs/steps,
44
+ sessions, events, incidents, projection jobs/cursors, telemetry, review evidence, and
45
+ circuit-breaker state. Registered digest-bound specifications and reports carry
46
+ semantic or evidence bodies. `tasks.json`, generated task-context files, runtime
47
+ checkpoints, and collaboration scratch are not independent authority. See
48
+ [`ARTIFACT-AUTHORITY.md`](./ARTIFACT-AUTHORITY.md).
44
49
 
45
50
  ## 3. Live MCP and declared contracts
46
51
 
47
- `spec/mcp-tools.yaml` declares and the bundled server registers 51 tools across
48
- eight families:
52
+ `spec/mcp-tools.yaml` declares and the bundled server registers 57 tools across
53
+ nine families:
49
54
 
50
55
  | Family | Live tools |
51
56
  |---|---|
52
57
  | `baseline.*` | start, record, get, converge |
53
58
  | `task.*` | create, update, list, get, switch_tag, delete, init_project, expand, parse_prd, dependency_topo, append_event, subscribe_events |
54
59
  | `session.*` | spawn, close, get, list, admission_check, heartbeat, subscribe_events |
55
- | `change.*` | create, update, get, list, context, breadcrumb, learning_propose, learning_resolve, converge, archive |
60
+ | `change.*` | create, update, delta, documentation_reconcile, get, list, context, breadcrumb, learning_propose, learning_resolve, converge, archive |
56
61
  | `decision.*` | thread_start, get, list, open, resolve, delegate, defer, supersede, complete, checkpoint |
57
- | `workflow.*` | start, get, list, step, complete |
62
+ | `workflow.*` | start, get, list, step, revise, supersede, complete |
63
+ | `artifact.*` | record, get |
58
64
  | `system.*` | doctor |
59
65
  | `plan.*` | export, get |
60
66
 
@@ -81,35 +87,55 @@ review, and deliver route back to the same thread instead of deciding for the ow
81
87
  One partial unique index permits only one open item per thread. `decision.resolve`,
82
88
  `decision.delegate`, and `decision.defer` preserve the source of authority;
83
89
  `decision.supersede` preserves history when evidence or intent changes.
84
- `decision.complete` closes settled normalized state without manufacturing an approval
85
- receipt. Prepare and confirm checkpointing remain optional and bind only a material
86
- accepted cluster to current artifact digests when interruption recovery needs that
87
- boundary. Status and breadcrumb return only the current question plus allowed and
88
- mechanically required transitions, so recovery does not require replaying conversation
89
- history.
90
+ After normalization, the host applies accepted intent through the owning MCP operation
91
+ or digest-bound artifact, reads it back, and records typed `applied_refs` when another
92
+ authority changed. Row-backed references require the exact field and canonical value;
93
+ specification and artifact references require the exact file digest. Active proposals
94
+ are never recalled as accepted intent. `decision.complete` then closes settled state without manufacturing
95
+ an approval receipt. Prepare and confirm checkpointing remain optional and bind only a
96
+ material accepted cluster to current artifact digests when interruption recovery needs
97
+ that boundary. Status and breadcrumb return the current unresolved question, accepted
98
+ intent relevant to the active authority, and allowed or mechanically required
99
+ transitions, so recovery does not require replaying conversation history.
90
100
 
91
101
  ## 5. Context Spine contract
92
102
 
93
103
  Context Manifest v3 is an immutable DB-backed role handoff, not a static codebase summary.
94
104
  `change.context` compiles required references, digests, readiness, context budget,
95
105
  public seam, exact verification command, Change/task authority digests, and valid
96
- transitions for `plan`, `implement`, `check`, or `review`. The default 12-file,
97
- about 12k-token, and 40%
106
+ transitions for `plan`, `implement`, `check`, or `review`. Compilation never updates
107
+ Change or provider authority; provider metadata changes through `change.update`.
108
+ Snapshots are selected exactly by `change_id`, nullable `task_id`, `role`, and `gate`,
109
+ so a review packet cannot satisfy implementation or planning evidence.
110
+
111
+ Implementation packets inline only the selected task, direct dependency/integration
112
+ neighborhood, and the restored task-context contract. Referenced file bodies remain
113
+ lazy. The token estimate includes inline Change/task authority plus referenced-file
114
+ estimates. Context refs preserve `expected_digest`, `anchor`, `scope`, and one of
115
+ `digest`, `existence`, or `advisory` freshness. Current consumers revalidate these
116
+ policies instead of trusting an old manifest.
117
+
118
+ The default 12-file, about 12k-token, and 40%
98
119
  fresh-context values are attention guidance. Overflow produces warnings; it does
99
120
  not block work or require raising a threshold. Prefer direct reads, bounded
100
121
  excerpts, or a smaller slice when they preserve correctness, and retain all
101
122
  necessary context when they do not.
102
123
 
103
124
  `change.breadcrumb` is the only compact router. Hooks may inject its change/task,
104
- role, gate, readiness, blockers, `allowed_transitions`, and `required_transition`.
105
- They must not inject intent
106
- bodies, transcripts, external memory, or graph payloads. Missing references,
107
- digest drift, HEAD drift, or a missing execution seam blocks readiness. Context
125
+ role, gate, readiness, blockers, bounded normalized accepted intent,
126
+ `allowed_transitions`, and `required_transition`. They must not inject raw prompts,
127
+ interaction transcripts, external-memory payloads, or graph payloads. Missing references,
128
+ required digest drift, HEAD drift, or a missing execution seam blocks readiness. Context
108
129
  size and baseline drift are advisory for a change that is already active. New ordinary
109
130
  work requires a healthy baseline. Only an explicitly approved incident break-glass may
110
131
  start without it; incident archive records a blocking reconciliation gap. Revision and
111
132
  tracked-spec integrity are reconciled and rechecked atomically for ordinary archive.
112
133
 
134
+ The plan workflow records a `plan/planning` manifest before design and exports only
135
+ `<artifact_root>/plan.json` plus deterministic `plan.md`. Both bind the planning
136
+ snapshot digest. The global `.ultra/execution-plan.json` is legacy read-only
137
+ compatibility and must not receive new plan writes.
138
+
113
139
  Stable discoveries use `change.learning_propose`; they reach the baseline only
114
140
  through approve/reject/apply transitions in `change.learning_resolve`. Unresolved
115
141
  learning blocks convergence. Review contributes two independent axes,
@@ -119,10 +145,14 @@ learning blocks convergence. Review contributes two independent axes,
119
145
 
120
146
  | Host | Plugin form | Workflow entry | Hook form | Collaboration companions |
121
147
  |---|---|---|---|---|
122
- | Claude Code | `.claude-plugin/plugin.json`, native commands/skills/agents, `.mcp.json` | `/ultra-*`, `/learn` | native `hooks/hooks.json` | `codex-collab`, `ultra-verify` |
123
- | Codex | personal plugin with `.codex-plugin/plugin.json`, namespaced skills, TOML agents, `.mcp.json` | `$ultra-builder-pro:ultra-*`, `$ultra-builder-pro:learn` | native `hooks/hooks.json` through the Codex wire adapter | `cc-collab`, `ultra-verify` |
124
- | OpenCode | config bundle plus native JavaScript plugin | `/ultra-*`, `/learn` | `event`, system transform, compaction, and tool lifecycle handlers | `cc-collab`, `codex-collab`, `ultra-verify` |
125
- | Kimi Code 0.26+ | managed `kimi.plugin.json` plugin with commands, skills, hooks, and MCP | `/ultra-builder-pro:ultra-*`, `/ultra-builder-pro:learn` | native hooks through the Kimi wire adapter | `cc-collab`, `codex-collab`, `ultra-verify` |
148
+ | Claude Code | `.claude-plugin/plugin.json`, native commands/skills/agents, `.mcp.json` | `/ultra-builder-pro:ultra-*` | native `hooks/hooks.json` | `codex-collab`, `ultra-verify` |
149
+ | Codex | personal plugin with `.codex-plugin/plugin.json`, namespaced skills, TOML agents, `.mcp.json` | `$ultra-builder-pro:ultra-*` | native `hooks/hooks.json` through the Codex wire adapter | `cc-collab`, `ultra-verify` |
150
+ | OpenCode | config bundle plus native JavaScript plugin | `/ultra-*` | `event`, system transform, compaction, and tool lifecycle handlers | `cc-collab`, `codex-collab`, `ultra-verify` |
151
+ | Kimi Code 0.26+ | managed `kimi.plugin.json` plugin with commands, skills, hooks, and MCP | `/ultra-builder-pro:ultra-*` | native hooks through the Kimi wire adapter | `cc-collab`, `codex-collab`, `ultra-verify` |
152
+
153
+ `spec/interaction-contract.json` carries the same exact public-capability graph for
154
+ every host. Adapters translate the question and invocation surfaces; they never own
155
+ semantic selection or durable state.
126
156
 
127
157
  The current host remains primary. Collaboration skills call another runtime
128
158
  only when explicitly requested, use it as a read-only advisor, and return the
@@ -156,12 +186,13 @@ reject an edit, stop, or tool call.
156
186
  Generic command blocking, post-edit governance, and unrelated user hooks are not
157
187
  copied into Ultra Builder Pro.
158
188
 
159
- ## 8. Memory boundary
189
+ ## 8. Workflow-memory boundary
160
190
 
161
- Ultra Builder Pro has no memory MCP family, recall skill, prompt capture,
162
- transcript capture, observation journal, or session-summary hook. Persistent
163
- cross-session memory belongs to a separately installed provider such as
164
- cloud-mem/claude-mem. Code graph content is equally external. A change context
191
+ Ultra Builder Pro has no general memory MCP family, general recall skill, prompt
192
+ capture, transcript capture, observation journal, or session-summary hook. It does
193
+ retain its own project-local cross-session workflow memory under `.ultra/`.
194
+ General conversational or episodic memory belongs to a separately installed provider
195
+ such as cloud-mem/claude-mem. Code-graph content is equally external. A change context
165
196
  may contain only provider metadata references, never provider payload content.
166
197
 
167
198
  Old Ultra data is never deleted during install. The explicit migration path is:
@@ -173,25 +204,25 @@ ultra-tools legacy-memory prune --confirm DELETE_ULTRA_LEGACY_MEMORY
173
204
  ```
174
205
 
175
206
  Archive before prune; the confirmation token is intentionally required.
207
+ This CLI is an operator-invoked migration cleanup surface only. Hooks, MCP, and
208
+ sessions never call it, and it cannot collect, recall, or reflect memory.
176
209
 
177
210
  ## 9. Project state layout
178
211
 
179
212
  ```text
180
213
  .ultra/
181
- ├── state.db # authoritative SQLite store
182
- ├── runtime/
214
+ ├── .runtime/
215
+ │ ├── state.db # authoritative SQLite lifecycle/index store
183
216
  │ └── checkpoint.json # advisory breadcrumb recovery projection
184
217
  ├── changes/
185
- │ ├── active/<id>/ # intent, delta, plan, context v3, learning, verification
218
+ │ ├── active/<id>/ # all Change semantics and evidence
219
+ │ │ ├── intent.md, findings.md, progress.md
220
+ │ │ ├── research/, delta/, documentation/, plan.json, plan.md
221
+ │ │ └── contexts/, test/, review/, delivery/
186
222
  │ └── archive/ # converged packets after baseline reconciliation
187
223
  ├── tasks/ # projections and task contexts
188
224
  ├── specs/ # research/product/architecture artifacts
189
- ├── sessions/ # bounded runtime artifacts
190
- ├── reviews/ # immutable review evidence by session id
191
- └── reports/
192
- ├── templates/ # blank report schemas; never evidence
193
- ├── tests/<workflow-id>.json
194
- └── delivery/<workflow-id>.json
225
+ └── reports/templates/ # blank report schemas; never evidence
195
226
  ```
196
227
 
197
228
  ## 10. Plugin and user-instruction isolation