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.
- package/.ultra-template/docs/research/README.md +26 -5
- package/.ultra-template/specs/discovery.md +1 -1
- package/.ultra-template/tasks/tasks.json +1 -1
- package/CHANGELOG.md +66 -0
- package/README.md +54 -24
- package/adapters/_shared/codex-assets.cjs +69 -11
- package/adapters/_shared/interaction-contract.cjs +36 -3
- package/adapters/_shared/runtime-assets.cjs +41 -1
- package/adapters/claude.js +7 -5
- package/adapters/codex.js +1 -1
- package/adapters/kimi.js +2 -8
- package/adapters/opencode.js +30 -28
- package/bin/orchestrator.js +64 -24
- package/docs/AGENT-CONTEXT.md +74 -43
- package/docs/ARCHITECTURE.md +63 -31
- package/docs/ARTIFACT-AUTHORITY.md +234 -0
- package/docs/DECISIONS.md +10 -4
- package/docs/LEGACY-CLI-CROSSWALK.md +6 -4
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +4 -3
- package/docs/ROADMAP.md +19 -5
- package/docs/RUNTIME-COMPAT-MATRIX.md +24 -13
- package/docs/STATE-DB-ACCESS-POLICY.md +11 -9
- package/docs/WORKFLOW-LIFECYCLE.md +70 -39
- package/hooks/README.md +6 -5
- package/hooks/active_task_context.py +97 -12
- package/hooks/context_spine.py +16 -4
- package/hooks/health_check.py +5 -4
- package/hooks/pre_stop_check.py +5 -2
- package/hooks/runtime_paths.py +396 -0
- package/hooks/subagent_tracker.py +5 -2
- package/hooks/workflow_checkpoint.py +16 -3
- package/hooks/workflow_context.py +11 -3
- package/hooks/workflow_resume.py +7 -4
- package/mcp-server/breadcrumb.cjs +20 -2
- package/mcp-server/lib/archive-journal.cjs +1026 -111
- package/mcp-server/lib/archive-mutation-worker.py +365 -0
- package/mcp-server/lib/artifact-registry.cjs +1126 -0
- package/mcp-server/lib/baseline-workflow.cjs +9 -4
- package/mcp-server/lib/change-packet.cjs +818 -0
- package/mcp-server/lib/change-workflow.cjs +932 -176
- package/mcp-server/lib/context-paths.cjs +111 -0
- package/mcp-server/lib/context-spine.cjs +511 -45
- package/mcp-server/lib/decision-dialogue.cjs +345 -3
- package/mcp-server/lib/delivery-transaction.cjs +471 -0
- package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
- package/mcp-server/lib/doctor.cjs +373 -11
- package/mcp-server/lib/git-bootstrap.cjs +459 -47
- package/mcp-server/lib/init-project.cjs +190 -95
- package/mcp-server/lib/plan-publication-worker.cjs +830 -0
- package/mcp-server/lib/plan-store.cjs +700 -5
- package/mcp-server/lib/project-breadcrumb.cjs +23 -4
- package/mcp-server/lib/projector.cjs +174 -40
- package/mcp-server/lib/runtime-paths.cjs +1687 -0
- package/mcp-server/lib/safe-project-file.cjs +385 -0
- package/mcp-server/lib/spec-learning.cjs +28 -12
- package/mcp-server/lib/state-db.cjs +263 -9
- package/mcp-server/lib/state-ops.cjs +248 -49
- package/mcp-server/lib/telemetry.cjs +12 -3
- package/mcp-server/lib/workflow-state.cjs +610 -58
- package/mcp-server/server.cjs +278 -33
- package/orchestrator/daemon.cjs +2 -10
- package/orchestrator/parallel-orchestrator.cjs +314 -50
- package/orchestrator/planner/plan-builder.cjs +1 -1
- package/orchestrator/recovery.cjs +125 -7
- package/orchestrator/session-close-journal-worker.cjs +5 -0
- package/orchestrator/session-close-journal.cjs +1014 -0
- package/orchestrator/session-runner.cjs +679 -69
- package/orchestrator/worktree-manager.cjs +186 -35
- package/package.json +2 -3
- package/skills/cc-collab/SKILL.md +4 -1
- package/skills/codex-collab/SKILL.md +5 -2
- package/skills/ultra-change/SKILL.md +26 -8
- package/skills/ultra-deliver/SKILL.md +23 -11
- package/skills/ultra-dev/SKILL.md +33 -6
- package/skills/ultra-doctor/SKILL.md +6 -2
- package/skills/ultra-init/SKILL.md +4 -2
- package/skills/ultra-plan/SKILL.md +49 -16
- package/skills/ultra-research/SKILL.md +30 -27
- package/skills/ultra-review/SKILL.md +9 -5
- package/skills/ultra-status/SKILL.md +9 -4
- package/skills/ultra-test/SKILL.md +10 -5
- package/skills/ultra-think/SKILL.md +6 -2
- package/skills/ultra-think/references/decision-dialogue.md +17 -6
- package/skills/ultra-verify/SKILL.md +4 -1
- package/spec/cli-protocol.md +18 -7
- package/spec/mcp-tools.yaml +502 -44
- package/spec/schemas/change-delta.v1.schema.json +153 -0
- package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +50 -13
- package/spec/schemas/tasks.v4.5.schema.json +1 -1
- package/ultra-tools/cli.cjs +3 -2
- package/ultra-tools/commands/db.cjs +76 -25
- package/ultra-tools/commands/migrate.cjs +995 -80
- package/ultra-tools/commands/session.cjs +34 -8
- package/ultra-tools/commands/status.cjs +32 -6
- package/ultra-tools/commands/system.cjs +129 -22
- package/commands/learn.md +0 -10
- package/orchestrator/skill-miner.cjs +0 -133
- package/output-styles/coding-vibes.md +0 -67
- package/output-styles/structural-thinking.md +0 -61
- package/skills/learn/SKILL.md +0 -58
package/adapters/opencode.js
CHANGED
|
@@ -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 =
|
|
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,
|
|
366
|
-
cwd:
|
|
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
|
-
|
|
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
|
|
385
|
+
function projectionRootForTarget(baseRoot, candidate) {
|
|
396
386
|
const target = path.isAbsolute(candidate)
|
|
397
387
|
? path.resolve(candidate)
|
|
398
|
-
: path.resolve(
|
|
399
|
-
|
|
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(
|
|
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
|
},
|
package/bin/orchestrator.js
CHANGED
|
@@ -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
|
|
29
|
-
const
|
|
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(
|
|
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(
|
|
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
|
-
|
|
225
|
-
|
|
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(
|
|
228
|
-
if (fs.existsSync(
|
|
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(
|
|
234
|
-
const
|
|
235
|
-
const
|
|
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
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
272
|
-
|
|
273
|
-
|
|
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 =
|
|
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,
|
package/docs/AGENT-CONTEXT.md
CHANGED
|
@@ -7,9 +7,9 @@ and Kimi Code plugins.
|
|
|
7
7
|
|
|
8
8
|
Ultra Builder Pro owns only:
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
`ultra-dev`, `ultra-test`, `ultra-review`, `ultra-deliver`, `ultra-status`,
|
|
12
|
-
`ultra-think`,
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
48
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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`.
|
|
97
|
-
|
|
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,
|
|
105
|
-
They must not inject
|
|
106
|
-
|
|
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
|
|
123
|
-
| Codex | personal plugin with `.codex-plugin/plugin.json`, namespaced skills, TOML agents, `.mcp.json` | `$ultra-builder-pro:ultra
|
|
124
|
-
| OpenCode | config bundle plus native JavaScript plugin | `/ultra
|
|
125
|
-
| Kimi Code 0.26+ | managed `kimi.plugin.json` plugin with commands, skills, hooks, and MCP | `/ultra-builder-pro:ultra
|
|
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.
|
|
189
|
+
## 8. Workflow-memory boundary
|
|
160
190
|
|
|
161
|
-
Ultra Builder Pro has no memory MCP family, recall skill, prompt
|
|
162
|
-
transcript capture, observation journal, or session-summary hook.
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
├──
|
|
182
|
-
├──
|
|
214
|
+
├── .runtime/
|
|
215
|
+
│ ├── state.db # authoritative SQLite lifecycle/index store
|
|
183
216
|
│ └── checkpoint.json # advisory breadcrumb recovery projection
|
|
184
217
|
├── changes/
|
|
185
|
-
│ ├── active/<id>/ #
|
|
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
|
-
|
|
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
|