ultra-builder-pro-cli 0.19.1 → 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 (103) 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 +90 -1
  5. package/README.md +62 -29
  6. package/adapters/_shared/codex-assets.cjs +69 -11
  7. package/adapters/_shared/interaction-contract.cjs +60 -5
  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/commands/ultra-research.md +1 -1
  15. package/docs/AGENT-CONTEXT.md +79 -45
  16. package/docs/ARCHITECTURE.md +75 -38
  17. package/docs/ARTIFACT-AUTHORITY.md +234 -0
  18. package/docs/DECISIONS.md +20 -10
  19. package/docs/LEGACY-CLI-CROSSWALK.md +15 -11
  20. package/docs/PLUGIN-ISOLATION-CONTRACT.md +4 -3
  21. package/docs/ROADMAP.md +21 -6
  22. package/docs/RUNTIME-COMPAT-MATRIX.md +30 -13
  23. package/docs/STATE-DB-ACCESS-POLICY.md +11 -9
  24. package/docs/WORKFLOW-LIFECYCLE.md +105 -54
  25. package/hooks/README.md +6 -5
  26. package/hooks/active_task_context.py +97 -12
  27. package/hooks/context_spine.py +16 -4
  28. package/hooks/health_check.py +5 -4
  29. package/hooks/pre_stop_check.py +5 -2
  30. package/hooks/runtime_paths.py +396 -0
  31. package/hooks/subagent_tracker.py +5 -2
  32. package/hooks/workflow_checkpoint.py +16 -3
  33. package/hooks/workflow_context.py +11 -3
  34. package/hooks/workflow_resume.py +7 -4
  35. package/mcp-server/breadcrumb.cjs +20 -2
  36. package/mcp-server/lib/archive-journal.cjs +1026 -111
  37. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  38. package/mcp-server/lib/artifact-registry.cjs +1126 -0
  39. package/mcp-server/lib/baseline-workflow.cjs +9 -4
  40. package/mcp-server/lib/change-packet.cjs +818 -0
  41. package/mcp-server/lib/change-workflow.cjs +932 -176
  42. package/mcp-server/lib/context-paths.cjs +111 -0
  43. package/mcp-server/lib/context-spine.cjs +511 -45
  44. package/mcp-server/lib/decision-dialogue.cjs +427 -12
  45. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  46. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  47. package/mcp-server/lib/doctor.cjs +375 -12
  48. package/mcp-server/lib/git-bootstrap.cjs +459 -47
  49. package/mcp-server/lib/init-project.cjs +190 -95
  50. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  51. package/mcp-server/lib/plan-store.cjs +700 -5
  52. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  53. package/mcp-server/lib/projector.cjs +174 -40
  54. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  55. package/mcp-server/lib/safe-project-file.cjs +385 -0
  56. package/mcp-server/lib/spec-learning.cjs +28 -12
  57. package/mcp-server/lib/state-db.cjs +397 -13
  58. package/mcp-server/lib/state-ops.cjs +248 -49
  59. package/mcp-server/lib/telemetry.cjs +12 -3
  60. package/mcp-server/lib/workflow-state.cjs +614 -62
  61. package/mcp-server/server.cjs +282 -33
  62. package/orchestrator/daemon.cjs +2 -10
  63. package/orchestrator/parallel-orchestrator.cjs +314 -50
  64. package/orchestrator/planner/plan-builder.cjs +1 -1
  65. package/orchestrator/recovery.cjs +125 -7
  66. package/orchestrator/session-close-journal-worker.cjs +5 -0
  67. package/orchestrator/session-close-journal.cjs +1014 -0
  68. package/orchestrator/session-runner.cjs +679 -69
  69. package/orchestrator/worktree-manager.cjs +186 -35
  70. package/package.json +2 -3
  71. package/skills/cc-collab/SKILL.md +4 -1
  72. package/skills/codex-collab/SKILL.md +5 -2
  73. package/skills/ultra-change/SKILL.md +38 -13
  74. package/skills/ultra-deliver/SKILL.md +23 -11
  75. package/skills/ultra-dev/SKILL.md +33 -6
  76. package/skills/ultra-doctor/SKILL.md +6 -2
  77. package/skills/ultra-init/SKILL.md +4 -2
  78. package/skills/ultra-plan/SKILL.md +68 -18
  79. package/skills/ultra-research/SKILL.md +45 -32
  80. package/skills/ultra-review/SKILL.md +9 -5
  81. package/skills/ultra-status/SKILL.md +9 -4
  82. package/skills/ultra-test/SKILL.md +15 -7
  83. package/skills/ultra-think/SKILL.md +6 -0
  84. package/skills/ultra-think/references/decision-dialogue.md +61 -14
  85. package/skills/ultra-verify/SKILL.md +4 -1
  86. package/spec/cli-protocol.md +19 -7
  87. package/spec/mcp-tools.yaml +531 -46
  88. package/spec/schemas/change-delta.v1.schema.json +153 -0
  89. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  90. package/spec/schemas/mcp-tools.schema.json +1 -1
  91. package/spec/schemas/state-db.sql +57 -17
  92. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  93. package/ultra-tools/cli.cjs +3 -2
  94. package/ultra-tools/commands/db.cjs +76 -25
  95. package/ultra-tools/commands/migrate.cjs +995 -80
  96. package/ultra-tools/commands/session.cjs +34 -8
  97. package/ultra-tools/commands/status.cjs +34 -7
  98. package/ultra-tools/commands/system.cjs +129 -22
  99. package/commands/learn.md +0 -10
  100. package/orchestrator/skill-miner.cjs +0 -133
  101. package/output-styles/coding-vibes.md +0 -67
  102. package/output-styles/structural-thinking.md +0 -61
  103. package/skills/learn/SKILL.md +0 -58
@@ -3,7 +3,7 @@
3
3
  // Phase 8B.3 — N-concurrent git worktree registry.
4
4
  //
5
5
  // Wraps session-runner's single-session gitWorktreeAdd/Remove with:
6
- // • allocate({repoRoot, sid, baseRef}) — create .ultra/worktrees/<sid>
6
+ // • allocate({repoRoot, sid, baseRef}) — create .ultra/.runtime/worktrees/<sid>
7
7
  // • release({repoRoot, worktree_path}) — remove one
8
8
  // • listActive(repoRoot) — scan git + filter our domain
9
9
  // • releaseAll(repoRoot) — batch cleanup (crash recovery)
@@ -16,27 +16,91 @@
16
16
  const fs = require('node:fs');
17
17
  const path = require('node:path');
18
18
  const { execFileSync } = require('node:child_process');
19
+ const Database = require('better-sqlite3');
19
20
 
20
- const { _internal } = require('./session-runner.cjs');
21
+ const runtimePaths = require('../mcp-server/lib/runtime-paths.cjs');
22
+ const gitBootstrap = require('../mcp-server/lib/git-bootstrap.cjs');
23
+ const { SessionRunnerError, _internal } = require('./session-runner.cjs');
21
24
 
22
- const WORKTREE_DOMAIN = path.join('.ultra', 'worktrees');
25
+ const WORKTREE_DOMAIN = path.join(runtimePaths.RUNTIME_RELATIVE_DIR, 'worktrees');
23
26
 
24
27
  function worktreePath(repoRoot, sid) {
25
- return path.join(repoRoot, WORKTREE_DOMAIN, sid);
28
+ _internal.assertSessionId(sid);
29
+ return _internal.assertManagedWorktreePath(
30
+ repoRoot,
31
+ path.join(repoRoot, WORKTREE_DOMAIN, sid),
32
+ { sid },
33
+ );
26
34
  }
27
35
 
28
36
  function allocate({ repoRoot, sid, baseRef = 'HEAD' }) {
29
37
  if (!repoRoot) throw new Error('allocate: repoRoot required');
30
38
  if (!sid) throw new Error('allocate: sid required');
39
+ const authority = runtimePaths.ensureRuntimeState(repoRoot, {
40
+ admitStorageBoundary: () => gitBootstrap.ensureExistingProjectStorageBoundary(repoRoot),
41
+ });
31
42
  const wt = worktreePath(repoRoot, sid);
32
43
  _internal.gitWorktreeAdd(repoRoot, wt, baseRef); // throws WORKTREE_FAILED
33
- return { worktree_path: wt };
44
+ try {
45
+ _internal.linkAuthorityIntoWorktree(repoRoot, wt, { authority });
46
+ return { worktree_path: wt, authority_db_path: authority.stateDbPath };
47
+ } catch (error) {
48
+ try { fs.rmSync(path.join(wt, '.ultra', '.runtime'), { force: true }); }
49
+ catch { /* best effort */ }
50
+ try { _internal.gitWorktreeRemove(repoRoot, wt); }
51
+ catch { /* original authority error is more actionable */ }
52
+ throw error;
53
+ }
34
54
  }
35
55
 
36
- function release({ repoRoot, worktree_path }) {
56
+ function sessionRemovalBlocker(repoRoot, sid, worktreePath) {
57
+ const dbPath = runtimePaths.pathsFor(repoRoot).stateDbPath;
58
+ if (!fs.existsSync(dbPath)) return null;
59
+ const db = new Database(dbPath, { readonly: true, fileMustExist: true });
60
+ try {
61
+ const rows = db.prepare(
62
+ 'SELECT sid, status, worktree_path FROM sessions WHERE sid = ?',
63
+ ).all(sid);
64
+ for (const row of rows) {
65
+ let recorded;
66
+ let requested;
67
+ try { recorded = fs.realpathSync(row.worktree_path); }
68
+ catch { recorded = path.resolve(row.worktree_path); }
69
+ try { requested = fs.realpathSync(worktreePath); }
70
+ catch { requested = path.resolve(worktreePath); }
71
+ if (recorded !== requested) {
72
+ return `session ${sid} is bound to a different worktree`;
73
+ }
74
+ if (!['completed', 'crashed'].includes(row.status)) {
75
+ return `session ${sid} is nonterminal (${row.status})`;
76
+ }
77
+ }
78
+ return null;
79
+ } finally {
80
+ db.close();
81
+ }
82
+ }
83
+
84
+ function release({ repoRoot, worktree_path, sid = null }) {
37
85
  if (!repoRoot) throw new Error('release: repoRoot required');
38
86
  if (!worktree_path) throw new Error('release: worktree_path required');
39
- _internal.gitWorktreeRemove(repoRoot, worktree_path);
87
+ const expectedSid = sid === null ? path.basename(worktree_path) : sid;
88
+ _internal.assertSessionId(expectedSid);
89
+ const managedPath = _internal.assertManagedWorktreePath(
90
+ repoRoot, worktree_path, { sid: expectedSid },
91
+ );
92
+ const sessionBlocker = sessionRemovalBlocker(
93
+ repoRoot, expectedSid, managedPath,
94
+ );
95
+ const integrationBlocker = _internal.worktreeRemovalBlocker(repoRoot, managedPath);
96
+ const blocker = sessionBlocker || integrationBlocker;
97
+ if (blocker) {
98
+ throw new SessionRunnerError(
99
+ 'WORKTREE_NOT_INTEGRATED',
100
+ `refusing to remove ${managedPath}: ${blocker}`,
101
+ );
102
+ }
103
+ _internal.gitWorktreeRemove(repoRoot, managedPath);
40
104
  }
41
105
 
42
106
  function parsePorcelain(text) {
@@ -62,63 +126,150 @@ function parsePorcelain(text) {
62
126
  return entries;
63
127
  }
64
128
 
65
- function listActive(repoRoot) {
129
+ function gitDiscoveryError(message, cause) {
130
+ return new SessionRunnerError(
131
+ 'WORKTREE_DISCOVERY_FAILED',
132
+ `cannot inspect Git worktrees safely: ${message}`,
133
+ { cause },
134
+ );
135
+ }
136
+
137
+ function hasGitMetadata(repoRoot) {
138
+ let current = path.resolve(repoRoot);
139
+ while (true) {
140
+ if (fs.lstatSync(path.join(current, '.git'), { throwIfNoEntry: false })) return true;
141
+ const parent = path.dirname(current);
142
+ if (parent === current) return false;
143
+ current = parent;
144
+ }
145
+ }
146
+
147
+ function runGitText(repoRoot, args, execGit) {
148
+ const value = execGit('git', args, {
149
+ cwd: repoRoot, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8',
150
+ });
151
+ if (typeof value !== 'string' && !Buffer.isBuffer(value)) {
152
+ throw gitDiscoveryError(`Git returned no output for ${args.join(' ')}`);
153
+ }
154
+ return String(value);
155
+ }
156
+
157
+ function listActive(repoRoot, { execGit = execFileSync } = {}) {
158
+ const metadataPresent = hasGitMetadata(repoRoot);
159
+ let inside;
160
+ try {
161
+ inside = runGitText(repoRoot, ['rev-parse', '--is-inside-work-tree'], execGit).trim();
162
+ } catch (error) {
163
+ const detail = String(error?.stderr || error?.message || error);
164
+ if (!metadataPresent && /not a git repository/i.test(detail)) return [];
165
+ if (error instanceof SessionRunnerError) throw error;
166
+ throw gitDiscoveryError(detail.trim(), error);
167
+ }
168
+ if (inside !== 'true') {
169
+ if (!metadataPresent && inside === 'false') return [];
170
+ throw gitDiscoveryError(`unexpected repository probe result: ${inside || '(empty)'}`);
171
+ }
172
+
66
173
  let out;
67
174
  try {
68
- out = execFileSync('git', ['worktree', 'list', '--porcelain'], {
69
- cwd: repoRoot, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8',
70
- });
175
+ out = runGitText(repoRoot, ['worktree', 'list', '--porcelain'], execGit);
71
176
  } catch (err) {
72
- // Non-git or git failure — surface nothing but log via stderr so callers
73
- // can trace. Return empty list so releaseAll is idempotent.
74
- process.stderr.write(`worktree-manager: listActive git call failed: ${err.message}\n`);
75
- return [];
177
+ if (err instanceof SessionRunnerError) throw err;
178
+ throw gitDiscoveryError(String(err?.stderr || err?.message || err).trim(), err);
76
179
  }
77
- const rawDomainRoot = path.resolve(repoRoot, WORKTREE_DOMAIN);
180
+ const rawDomainRoot = _internal.assertManagedWorktreeRoot(repoRoot);
78
181
  // On macOS tmpdir is a symlink (/var → /private/var); git returns realpath.
79
182
  // Canonicalize both sides so prefix matching is robust.
80
183
  const domainRoot = (fs.existsSync(rawDomainRoot)
81
184
  ? fs.realpathSync(rawDomainRoot)
82
185
  : rawDomainRoot) + path.sep;
83
- return parsePorcelain(out)
84
- .filter((e) => {
85
- if (!e.worktree) return false;
86
- const canon = fs.existsSync(e.worktree) ? fs.realpathSync(e.worktree) : path.resolve(e.worktree);
87
- return (canon + path.sep).startsWith(domainRoot);
88
- })
89
- .map((e) => ({
90
- sid: path.basename(e.worktree),
91
- worktree_path: e.worktree,
92
- head: e.head || null,
93
- branch: e.branch || null,
94
- detached: !!e.detached,
95
- }));
186
+ const active = [];
187
+ for (const entry of parsePorcelain(out)) {
188
+ if (!entry.worktree) continue;
189
+ const physical = fs.existsSync(entry.worktree)
190
+ ? fs.realpathSync(entry.worktree)
191
+ : path.resolve(entry.worktree);
192
+ if (!(physical + path.sep).startsWith(domainRoot)) continue;
193
+ const sid = path.basename(entry.worktree);
194
+ const canonical = _internal.assertManagedWorktreePath(
195
+ repoRoot, entry.worktree, { sid },
196
+ );
197
+ active.push({
198
+ sid,
199
+ worktree_path: canonical,
200
+ head: entry.head || null,
201
+ branch: entry.branch || null,
202
+ detached: !!entry.detached,
203
+ });
204
+ }
205
+ return active;
96
206
  }
97
207
 
98
- function releaseAll(repoRoot) {
99
- const active = listActive(repoRoot);
208
+ function releaseAll(repoRoot, options = {}) {
209
+ const active = listActive(repoRoot, options);
210
+ const releaseWorktree = options.releaseWorktree || release;
100
211
  let cleaned = 0;
212
+ const protectedSids = new Set();
213
+ const preserved = [];
214
+ const quarantined = [];
101
215
  for (const entry of active) {
102
216
  try {
103
- release({ repoRoot, worktree_path: entry.worktree_path });
217
+ releaseWorktree({
218
+ repoRoot,
219
+ worktree_path: entry.worktree_path,
220
+ sid: entry.sid,
221
+ });
104
222
  cleaned += 1;
105
223
  } catch (err) {
224
+ protectedSids.add(entry.sid);
225
+ preserved.push({
226
+ sid: entry.sid,
227
+ worktree_path: entry.worktree_path,
228
+ reason: err.message,
229
+ });
106
230
  // Best-effort: one bad worktree shouldn't stop the sweep.
107
231
  process.stderr.write(`worktree-manager: release(${entry.sid}) failed: ${err.message}\n`);
108
232
  }
109
233
  }
110
- // Sweep filesystem orphans that git no longer tracks.
234
+ // Filesystem entries Git no longer owns are recovery evidence, not trash.
235
+ // Move real managed children atomically into quarantine and report the path.
111
236
  const domainRoot = path.join(repoRoot, WORKTREE_DOMAIN);
112
237
  if (fs.existsSync(domainRoot)) {
113
238
  for (const name of fs.readdirSync(domainRoot)) {
114
239
  const full = path.join(domainRoot, name);
115
- try { fs.rmSync(full, { recursive: true, force: true }); }
240
+ try {
241
+ if (protectedSids.has(name)) continue;
242
+ _internal.assertSessionId(name);
243
+ const managedPath = _internal.assertManagedWorktreePath(
244
+ repoRoot, full, { sid: name },
245
+ );
246
+ const recoveryRoot = path.join(
247
+ runtimePaths.pathsFor(repoRoot).runtimeDir,
248
+ 'recovery',
249
+ 'worktrees',
250
+ );
251
+ fs.mkdirSync(recoveryRoot, { recursive: true });
252
+ const token = new Date().toISOString().replace(/[:.]/g, '-');
253
+ let recoveryPath = path.join(recoveryRoot, `${token}-${name}`);
254
+ let attempt = 0;
255
+ while (fs.lstatSync(recoveryPath, { throwIfNoEntry: false })) {
256
+ attempt += 1;
257
+ recoveryPath = path.join(recoveryRoot, `${token}-${name}-${attempt}`);
258
+ }
259
+ fs.renameSync(managedPath, recoveryPath);
260
+ quarantined.push({
261
+ sid: name,
262
+ source_path: managedPath,
263
+ recovery_path: recoveryPath,
264
+ });
265
+ }
116
266
  catch (err) {
267
+ preserved.push({ sid: name, worktree_path: full, reason: err.message });
117
268
  process.stderr.write(`worktree-manager: orphan sweep failed for ${full}: ${err.message}\n`);
118
269
  }
119
270
  }
120
271
  }
121
- return { cleaned };
272
+ return { cleaned, preserved, quarantined };
122
273
  }
123
274
 
124
275
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra-builder-pro-cli",
3
- "version": "0.19.1",
3
+ "version": "0.21.1",
4
4
  "description": "Explicitly invoked Ultra Builder Pro workflows for Claude Code, OpenCode, Codex CLI, and Kimi Code, with project-local MCP authority and lifecycle recovery.",
5
5
  "bin": {
6
6
  "ultra-builder-pro-cli": "bin/install.js",
@@ -16,7 +16,6 @@
16
16
  "orchestrator",
17
17
  "commands",
18
18
  "agents",
19
- "skills/learn",
20
19
  "skills/ultra-init",
21
20
  "skills/ultra-research",
22
21
  "skills/ultra-plan",
@@ -38,7 +37,6 @@
38
37
  "hooks/*.py",
39
38
  "hooks/adapters/*.py",
40
39
  "hooks/README.md",
41
- "output-styles",
42
40
  "spec/mcp-tools.yaml",
43
41
  "spec/cli-protocol.md",
44
42
  "spec/schemas",
@@ -48,6 +46,7 @@
48
46
  ".ultra-template",
49
47
  "docs/ROADMAP.md",
50
48
  "docs/ARCHITECTURE.md",
49
+ "docs/ARTIFACT-AUTHORITY.md",
51
50
  "docs/WORKFLOW-LIFECYCLE.md",
52
51
  "docs/LEGACY-CLI-CROSSWALK.md",
53
52
  "docs/AGENT-CONTEXT.md",
@@ -27,10 +27,13 @@ claude --safe-mode -p "<bounded prompt>" \
27
27
  --no-session-persistence
28
28
  ```
29
29
 
30
- Store large output under `.ultra/collab/<session-id>/claude-output.md`. A missing CLI,
30
+ Store large output under `.ultra/.runtime/collab/<session-id>/claude-output.md`. A missing CLI,
31
31
  failed authentication, timeout, or empty response degrades to host-only analysis and
32
32
  never blocks the underlying task.
33
33
 
34
+ Treat `.ultra/.runtime/collab/` as working scratch, not Ultra authority. Promote only verified
35
+ conclusions into the invoking DB-bound workflow artifact or report.
36
+
34
37
  ## Synthesis
35
38
 
36
39
  Verify consequential claims against the current checkout, runtime, tests, or primary
@@ -17,10 +17,10 @@ evidence, edits, verification, and the final answer.
17
17
 
18
18
  ## Invocation
19
19
 
20
- Create a session directory under `.ultra/collab/`, then run in a read-only sandbox:
20
+ Create a session directory under `.ultra/.runtime/collab/`, then run in a read-only sandbox:
21
21
 
22
22
  ```bash
23
- SESSION_PATH=".ultra/collab/$(date +%Y%m%d-%H%M%S)-codex"
23
+ SESSION_PATH=".ultra/.runtime/collab/$(date +%Y%m%d-%H%M%S)-codex"
24
24
  mkdir -p "${SESSION_PATH}"
25
25
  codex exec -s read-only \
26
26
  --ephemeral \
@@ -35,6 +35,9 @@ Do not enable write-capable automation or permission bypass. The isolation flags
35
35
  the advisory run independent from the user's Codex runtime configuration and rules;
36
36
  the normal Codex authentication store remains available.
37
37
 
38
+ Treat `.ultra/.runtime/collab/` as working scratch, not Ultra authority. Promote only verified
39
+ conclusions into the invoking DB-bound workflow artifact or report.
40
+
38
41
  ## Synthesis
39
42
 
40
43
  Read output only after the process exits and the file is non-empty. Verify
@@ -13,8 +13,8 @@ Read `../ultra-think/references/decision-dialogue.md` before asking a material q
13
13
 
14
14
  ## Inspect and align
15
15
 
16
- 1. Read `system.doctor`, `baseline.get`, active changes, decisions, and
17
- `change.breadcrumb`.
16
+ 1. Read `system.doctor`, `baseline.get`, active changes, breadcrumb `accepted_intent`,
17
+ decisions, and `change.breadcrumb`.
18
18
  2. Resume an existing change that matches the request. Do not duplicate authority.
19
19
  3. Ordinary work requires a healthy ready baseline. An incident may use only an
20
20
  explicit `baseline_bypass` reason and approver; urgency is not approval.
@@ -29,13 +29,22 @@ Read `../ultra-think/references/decision-dialogue.md` before asking a material q
29
29
  - change kind and evidence-backed risk flags;
30
30
  - research disposition.
31
31
 
32
- Select `quick`, `standard`, `major`, or `incident` from actual scope and risk. A quick
33
- change may have no material risk, research obligation, or more than one task.
32
+ Recommend `quick`, `standard`, `major`, or `incident` from actual scope and risk,
33
+ together with the research disposition. A quick change may have no material risk,
34
+ research obligation, or more than one task. MCP may reject a profile that contradicts
35
+ hard risk invariants; it does not choose a replacement.
36
+
37
+ After `change.create` or `change.update`, read back the complete Change Contract. When
38
+ that write applies a durable decision, complete the decision thread with the change
39
+ reference in `applied_refs`.
34
40
 
35
41
  Ask only when a choice changes accepted product intent, scope, public behavior,
36
42
  compatibility, security, material cost, external effects, or recovery. Use the host's
37
43
  native question UI when available. Normalize clear choices already in the user's
38
- request. The model owns reversible implementation detail inside the accepted contract.
44
+ request. When profile or research posture remains unresolved, present the recommended
45
+ route and credible alternatives through that same UI, then stop. The user selects,
46
+ modifies, delegates, or defers the semantic route. The model owns reversible
47
+ implementation detail inside the accepted contract.
39
48
 
40
49
  ## Persist the contract
41
50
 
@@ -50,9 +59,19 @@ Call `change.create`. It records and completes exactly:
50
59
  3. `record-intent`
51
60
 
52
61
  The returned change workflow must be `completed`; research, plan, context, dev, test,
53
- review, and deliver remain independent capabilities. Standard and major changes may
54
- store delta artifacts under `.ultra/changes/active/<id>/delta/`. Incidents maintain
55
- the structured diagnosis artifact.
62
+ review, and deliver remain independent capabilities. Keep every Change-owned semantic
63
+ artifact, finding, plan, context, test result, review result, documentation update,
64
+ and progress projection below `.ultra/changes/active/<id>/`. Incidents maintain the
65
+ structured diagnosis artifact there as well.
66
+
67
+ Before planning a standard or major change, write the accepted specification updates
68
+ to the Change overlay and call `change.delta` with exact baseline id, repository
69
+ revision, specification digests, decisions, non-goals, acceptance, documentation
70
+ impact, unknowns, and mutation before/after digests. If accepted behavior truly does
71
+ not change baseline semantics, record an empty typed delta with a specific
72
+ `no_semantic_change_reason`; do not manufacture a mutation. Read back the registered
73
+ delta and deterministic `progress.md`. This records proposed authority only and must
74
+ not edit baseline specifications.
56
75
 
57
76
  Use `change.update` when accepted intent or evidence changes. Update the Change
58
77
  Contract, classification, research disposition, and docs impact together when their
@@ -61,14 +80,20 @@ change must become stale and be regenerated; never keep old evidence green by pr
61
80
  assertion.
62
81
 
63
82
  If research is required, recommend `ultra-research` with the recorded disposition. If
64
- the contract is sufficiently evidenced, recommend `ultra-plan`. Return and wait for an
65
- explicit user invocation; do not start either workflow. `change.context` is compiled
66
- only for an actual plan, implementation, test, or review consumer.
83
+ the contract is sufficiently evidenced, finish the typed delta and recommend
84
+ `ultra-plan`. Direct Build skips Research only; it never skips Plan, DB-backed task
85
+ contracts, or fresh task context. Return and wait for an explicit user invocation; do
86
+ not start another workflow. `change.context` is compiled only for an actual plan,
87
+ implementation, test, or review consumer.
67
88
 
68
89
  Stable implementation discoveries use the approval-gated specification-learning
69
90
  transitions. External memory and graph providers retain their own content; Ultra stores
70
91
  metadata references only.
71
92
 
72
93
  Return the normalized contract, change id and kind, research disposition, blockers,
73
- and `allowed_transitions`. The host selects a recommendation from those transitions;
74
- MCP does not encode a semantic next action.
94
+ and `allowed_transitions`. Recommend a semantic next action from those transitions.
95
+ If current intent does not already select it, present the recommendation and credible
96
+ alternatives through the interaction protocol, then wait. MCP does not encode a
97
+ semantic next action.
98
+
99
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -10,8 +10,8 @@ push, tag, registry publication, deployment, or another external effect.
10
10
 
11
11
  ## Bind current evidence
12
12
 
13
- 1. Read doctor, baseline, change, breadcrumb, decisions, tasks, test, review, learning,
14
- and current checkout.
13
+ 1. Read doctor, baseline, change, breadcrumb `accepted_intent`, decisions, tasks, test,
14
+ review, learning, and current checkout.
15
15
  2. Resume or start a deliver workflow bound to the change and baseline.
16
16
  3. Require complete task evidence, a current test report, both review axes, current
17
17
  context, and resolved material decisions. Record `bind-evidence`.
@@ -21,25 +21,35 @@ Evidence from another revision, task set, or change is invalid.
21
21
  ## Reconcile specifications
22
22
 
23
23
  Resolve every specification-learning candidate. Apply approved learning to its target
24
- with before/after digests and verification; preserve rejection reasons.
24
+ inside the Change overlay; preserve rejection reasons. Read the current registered
25
+ `change.delta` and verify every baseline anchor, payload digest, acceptance reference,
26
+ and unknown.
25
27
 
26
- Read `references/baseline-reconciliation.md` and write the reconciliation manifest
27
- inside the active change directory. Record semantic additions or updates, resolved
28
- gaps and unknowns, and verification. If baseline semantics did not change, record an
29
- evidenced no-change reason. Record `reconcile-specifications`.
28
+ Write documentation updates below
29
+ `.ultra/changes/active/<change-id>/documentation/`. Call
30
+ `change.documentation_reconcile` for every Change: bind the current delta id and
31
+ digest, exact before/after document digests, delta and acceptance references,
32
+ verification, and each verified consumer. When no documentation changes, record an
33
+ empty reconciliation with a specific `no_change_reason`. An unexplained orphan
34
+ document is blocking. Record `reconcile-specifications`.
30
35
 
31
36
  Compile final convergence context and run the risk-selected candidate checks, install
32
37
  or doctor smoke when this change modifies distributed assets, public-seam verification,
33
38
  and recovery sanity. Record the immutable context under `verify-candidate`.
34
39
 
35
40
  Call `change.converge`; MCP derives readiness from durable dev, test, review,
36
- specification, docs, diagnosis, and current checkout evidence. Record
41
+ typed delta, documentation reconciliation, diagnosis, and current checkout evidence.
42
+ Record
37
43
  `converge-authority` only when ready.
38
44
 
39
- Call `change.archive` with the reconciliation manifest and summary. Verify the refreshed
40
- baseline and archived packet, then record `archive-change`.
45
+ Call `change.archive` with the summary. MCP preflights all target before states and
46
+ overlay digests, applies the complete packet atomically, refreshes baseline authority,
47
+ rebinds every registered artifact into the self-contained archive, and rolls back or
48
+ resumes from its transaction journal after interruption. A partial success is not
49
+ delivery. Verify the refreshed baseline and archived packet, then record
50
+ `archive-change`.
41
51
 
42
- Write `.ultra/reports/delivery/<workflow-id>.json` with
52
+ Write `<archived-change-root>/delivery/<workflow-id>/report.json` with
43
53
  `ultra-delivery-report-v1`: archived change, baseline, HEAD, worktree and context
44
54
  digests, local checks, rollback notes, and timestamp. The report must not contain a
45
55
  release decision. Record it under `verify-delivery` and complete the workflow.
@@ -54,3 +64,5 @@ state.
54
64
 
55
65
  Return archive and baseline state, evidence digests, recovery notes, residual risks,
56
66
  and allowed transitions.
67
+
68
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -10,14 +10,23 @@ fresh context contract, session lease, evidence references, and recovery.
10
10
 
11
11
  ## Bind and resume
12
12
 
13
- 1. Read doctor, breadcrumb, active decisions, task contract, dependencies, dev runs,
14
- sessions, HEAD, and worktree.
13
+ 1. Read doctor, breadcrumb `accepted_intent`, active decisions, task contract,
14
+ dependencies, dev runs, sessions, HEAD, and worktree.
15
15
  2. Resume the matching dev run or start one with `change_id` and `task_id`.
16
16
  3. Record `bind-task` with current authority and dependency evidence.
17
- 4. Compile `change.context` for `implement` using DB-backed public seam, verification
18
- command, and refs. Context budgets are advisory; missing required evidence is
19
- blocking. Record the immutable manifest as `compile-context`.
20
- 5. Reuse an existing valid session. Otherwise pass admission and call `session.spawn`.
17
+ 4. Compile `change.context` with `role: implement` and `gate: implementation`. The
18
+ public seam, verification command, and context refs come from the DB-backed task
19
+ contract; update that contract before compilation instead of overriding it in the
20
+ call. Persist any changed external-provider metadata with `change.update` first.
21
+ Context compilation itself is read-only with respect to Change and provider
22
+ authority.
23
+ 5. Confirm the manifest contains the selected task, its direct dependencies, direct
24
+ dependents needed for integration, and relevant integration checkpoints—not the
25
+ entire task graph. Referenced file bodies stay lazy. The token estimate includes
26
+ inline Change/task authority as well as file estimates; budget overflow is
27
+ advisory, while a missing or stale required reference is blocking. Record the
28
+ immutable manifest as `compile-context`.
29
+ 6. Reuse an existing valid session. Otherwise pass admission and call `session.spawn`.
21
30
  Work only in the returned worktree and preserve unrelated changes.
22
31
 
23
32
  Do not spawn recursively from a parent-owned worker session. Process exit is transport
@@ -41,6 +50,18 @@ evidence, never task completion.
41
50
  6. Create the authorized local task commit. Do not push, publish, tag, or deploy unless
42
51
  the user separately authorized that external effect.
43
52
 
53
+ Treat the compiled task-context contract as the handoff boundary: purpose, why,
54
+ constraints, non-goals, target seams/files, pattern refs, acceptance, documentation
55
+ impact, recovery, and definition of drift. If accepted intent, the task contract, or a
56
+ digest-bound reference changes, update the owning authority and recompile rather than
57
+ continuing from a stale packet.
58
+
59
+ Keep implementation findings and documentation candidates inside the owning Change
60
+ root. When code changes invalidate or extend maintained documentation, update the
61
+ Change documentation overlay and its evidence rather than editing accepted baseline
62
+ specifications. Hooks may remind the host to reconcile documentation; they must not
63
+ block ordinary development or choose the semantic update.
64
+
44
65
  Task-level tests and review belong to this inner loop. They do not replace aggregate
45
66
  change testing or review when multiple slices, integrations, or broader risks require
46
67
  those capabilities.
@@ -51,6 +72,10 @@ In direct mode, integrate the task commit through the repository's accepted path
51
72
  verify ancestry and the public seam, close the session, update the task to `completed`,
52
73
  record `record-completion`, and complete the dev workflow.
53
74
 
75
+ Change convergence revalidates the exact latest
76
+ `change_id + task_id + implement + implementation` snapshot for every executable
77
+ task. A newer review, test, or plan packet cannot substitute for it.
78
+
54
79
  In a parent-supervised worker, record completion evidence and exit without closing the
55
80
  parent-owned lease. The primary host verifies the exact session commit, performs any
56
81
  integration, refreshes review if the reviewed checkout changed, settles the lease, and
@@ -62,3 +87,5 @@ worktree and evidence, and resume the same run later.
62
87
 
63
88
  Return outcome, changed paths, public seam, exact checks, review axes, commit and
64
89
  session state, residual risk, and allowed transitions.
90
+
91
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -13,7 +13,9 @@ work complete.
13
13
 
14
14
  Run `system.doctor` without repair and inspect:
15
15
 
16
- - SQLite integrity, schema 18 migration, required tables, and backups;
16
+ - SQLite integrity, schema 20 migration, required tables, and backups;
17
+ - artifact registration, ownership, digest freshness, dependency edges, consumers,
18
+ and orphan or duplicate authority;
17
19
  - baseline and research provenance, gaps, digests, and Git/worktree drift;
18
20
  - workflow status, obsolete-step migration, blockers, and output freshness;
19
21
  - decisions and checkpoint artifact freshness;
@@ -30,7 +32,7 @@ by doctor.
30
32
  With explicit repair authorization, `system.doctor` may:
31
33
 
32
34
  - apply backup-first supported schema migrations;
33
- - migrate active pre-18 rigid workflows into recoverable adaptive state;
35
+ - migrate incompatible rigid workflows into recoverable adaptive state;
34
36
  - requeue or regenerate projections from healthy DB authority;
35
37
  - reconcile orphan sessions without deleting unrelated or dirty worktrees;
36
38
  - resume supported archive journals;
@@ -48,3 +50,5 @@ entry points, MCP startup, and hook smoke tests as applicable.
48
50
 
49
51
  Return health first, failed checks, mechanical repairs performed, preserved recovery
50
52
  paths, unresolved owners, and allowed transitions plus any unique required transition.
53
+
54
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -13,7 +13,7 @@ the next semantic workflow.
13
13
 
14
14
  1. Identify the repository root, existing `.ultra` state, Git state, meaningful source,
15
15
  monorepo boundaries, and current owner request.
16
- 2. If `.ultra/state.db` exists, run `system.doctor` read-only and resume with
16
+ 2. If `.ultra/.runtime/state.db` exists, run `system.doctor` read-only and resume with
17
17
  `task.init_project` only when its report permits it.
18
18
  3. Otherwise call `task.init_project` with `mode: "auto"`, the project name, and an
19
19
  explicit scope only when the repository contains multiple plausible roots.
@@ -48,7 +48,7 @@ unresolved. Normalize clear intent already present in the request without asking
48
48
 
49
49
  The operation must:
50
50
 
51
- - create or migrate `.ultra/state.db` with a backup before schema migration;
51
+ - create or migrate `.ultra/.runtime/state.db` with a backup before schema migration;
52
52
  - install only missing scaffold assets on resume;
53
53
  - preserve existing artifacts and unrelated work;
54
54
  - preserve an existing Git repository and HEAD;
@@ -81,3 +81,5 @@ Read the returned `allowed_transitions` and `required_transition`:
81
81
  Report classification, scope, schema and backup result, Git state, init workflow id,
82
82
  baseline state, and allowed transitions. Do not claim that a baseline is ready until a
83
83
  separate research/adoption workflow has recorded and converged it.
84
+
85
+ Never invoke the recommended capability here; wait for an explicit user invocation.