ultra-builder-pro-cli 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/.ultra-template/docs/research/README.md +26 -5
  2. package/.ultra-template/specs/discovery.md +1 -1
  3. package/.ultra-template/tasks/tasks.json +10 -3
  4. package/.ultra-template/templates/task-context.md +19 -0
  5. package/CHANGELOG.md +106 -0
  6. package/README.md +215 -41
  7. package/adapters/_shared/codex-assets.cjs +69 -11
  8. package/adapters/_shared/interaction-contract.cjs +36 -3
  9. package/adapters/_shared/runtime-assets.cjs +41 -1
  10. package/adapters/claude.js +7 -5
  11. package/adapters/codex.js +1 -1
  12. package/adapters/kimi.js +2 -8
  13. package/adapters/opencode.js +43 -30
  14. package/bin/orchestrator.js +64 -24
  15. package/docs/AGENT-CONTEXT.md +89 -49
  16. package/docs/ARCHITECTURE.md +112 -45
  17. package/docs/ARTIFACT-AUTHORITY.md +274 -0
  18. package/docs/COMMIT-HASH-BACKFILL.md +33 -90
  19. package/docs/DECISIONS.md +14 -6
  20. package/docs/LEGACY-CLI-CROSSWALK.md +16 -6
  21. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -4
  22. package/docs/ROADMAP.md +20 -6
  23. package/docs/RUNTIME-COMPAT-MATRIX.md +29 -16
  24. package/docs/STATE-DB-ACCESS-POLICY.md +43 -13
  25. package/docs/WORKFLOW-LIFECYCLE.md +105 -50
  26. package/hooks/README.md +10 -8
  27. package/hooks/active_task_context.py +105 -18
  28. package/hooks/context_spine.py +16 -4
  29. package/hooks/health_check.py +5 -4
  30. package/hooks/pre_stop_check.py +5 -2
  31. package/hooks/runtime_paths.py +396 -0
  32. package/hooks/subagent_tracker.py +5 -2
  33. package/hooks/workflow_checkpoint.py +16 -3
  34. package/hooks/workflow_context.py +11 -3
  35. package/hooks/workflow_resume.py +7 -4
  36. package/mcp-server/breadcrumb.cjs +20 -2
  37. package/mcp-server/lib/archive-journal.cjs +1026 -111
  38. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  39. package/mcp-server/lib/artifact-registry.cjs +1127 -0
  40. package/mcp-server/lib/baseline-workflow.cjs +69 -28
  41. package/mcp-server/lib/change-packet.cjs +818 -0
  42. package/mcp-server/lib/change-workflow.cjs +932 -176
  43. package/mcp-server/lib/context-paths.cjs +142 -0
  44. package/mcp-server/lib/context-spine.cjs +511 -45
  45. package/mcp-server/lib/decision-dialogue.cjs +345 -3
  46. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  47. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  48. package/mcp-server/lib/doctor.cjs +401 -11
  49. package/mcp-server/lib/git-bootstrap.cjs +458 -47
  50. package/mcp-server/lib/init-project.cjs +240 -95
  51. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  52. package/mcp-server/lib/plan-store.cjs +700 -5
  53. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  54. package/mcp-server/lib/projector.cjs +211 -42
  55. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  56. package/mcp-server/lib/safe-project-file.cjs +385 -0
  57. package/mcp-server/lib/spec-learning.cjs +28 -12
  58. package/mcp-server/lib/state-authority.cjs +19 -3
  59. package/mcp-server/lib/state-db.cjs +263 -9
  60. package/mcp-server/lib/state-ops.cjs +254 -50
  61. package/mcp-server/lib/task-ledger.cjs +1342 -0
  62. package/mcp-server/lib/telemetry.cjs +12 -3
  63. package/mcp-server/lib/workflow-state.cjs +610 -58
  64. package/mcp-server/server.cjs +423 -42
  65. package/orchestrator/daemon.cjs +2 -10
  66. package/orchestrator/parallel-orchestrator.cjs +314 -50
  67. package/orchestrator/planner/plan-builder.cjs +1 -1
  68. package/orchestrator/recovery.cjs +125 -7
  69. package/orchestrator/session-close-journal-worker.cjs +5 -0
  70. package/orchestrator/session-close-journal.cjs +1014 -0
  71. package/orchestrator/session-runner.cjs +679 -69
  72. package/orchestrator/worktree-manager.cjs +186 -35
  73. package/package.json +2 -3
  74. package/skills/cc-collab/SKILL.md +4 -1
  75. package/skills/codex-collab/SKILL.md +5 -2
  76. package/skills/ultra-change/SKILL.md +34 -8
  77. package/skills/ultra-deliver/SKILL.md +26 -11
  78. package/skills/ultra-dev/SKILL.md +52 -10
  79. package/skills/ultra-doctor/SKILL.md +16 -3
  80. package/skills/ultra-init/SKILL.md +22 -11
  81. package/skills/ultra-plan/SKILL.md +58 -18
  82. package/skills/ultra-research/SKILL.md +37 -35
  83. package/skills/ultra-review/SKILL.md +14 -8
  84. package/skills/ultra-status/SKILL.md +18 -9
  85. package/skills/ultra-test/SKILL.md +11 -5
  86. package/skills/ultra-think/SKILL.md +6 -2
  87. package/skills/ultra-think/references/decision-dialogue.md +17 -6
  88. package/skills/ultra-verify/SKILL.md +4 -1
  89. package/spec/cli-protocol.md +21 -7
  90. package/spec/mcp-tools.yaml +629 -44
  91. package/spec/schemas/change-delta.v1.schema.json +153 -0
  92. package/spec/schemas/context-file.v4.5.schema.json +1 -1
  93. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  94. package/spec/schemas/mcp-tools.schema.json +1 -1
  95. package/spec/schemas/state-db.sql +51 -14
  96. package/spec/schemas/task-ledger.v1.schema.json +217 -0
  97. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  98. package/ultra-tools/cli.cjs +3 -2
  99. package/ultra-tools/commands/db.cjs +76 -25
  100. package/ultra-tools/commands/migrate.cjs +1000 -82
  101. package/ultra-tools/commands/session.cjs +34 -8
  102. package/ultra-tools/commands/status.cjs +32 -6
  103. package/ultra-tools/commands/system.cjs +129 -22
  104. package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
  105. package/commands/learn.md +0 -10
  106. package/orchestrator/skill-miner.cjs +0 -133
  107. package/output-styles/coding-vibes.md +0 -67
  108. package/output-styles/structural-thinking.md +0 -61
  109. package/skills/learn/SKILL.md +0 -58
@@ -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.20.0",
3
+ "version": "0.22.0",
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,10 @@ 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`, `task.ledger_get`, `baseline.get`, active changes, breadcrumb
17
+ `accepted_intent`, decisions, and `change.breadcrumb`. Import a newer descendant
18
+ checkpoint before capturing intent. Stop on a typed baseline, Change, task,
19
+ ancestry, or active-session conflict.
18
20
  2. Resume an existing change that matches the request. Do not duplicate authority.
19
21
  3. Ordinary work requires a healthy ready baseline. An incident may use only an
20
22
  explicit `baseline_bypass` reason and approver; urgency is not approval.
@@ -34,6 +36,10 @@ together with the research disposition. A quick change may have no material risk
34
36
  research obligation, or more than one task. MCP may reject a profile that contradicts
35
37
  hard risk invariants; it does not choose a replacement.
36
38
 
39
+ After `change.create` or `change.update`, read back the complete Change Contract. When
40
+ that write applies a durable decision, complete the decision thread with the change
41
+ reference in `applied_refs`.
42
+
37
43
  Ask only when a choice changes accepted product intent, scope, public behavior,
38
44
  compatibility, security, material cost, external effects, or recovery. Use the host's
39
45
  native question UI when available. Normalize clear choices already in the user's
@@ -55,9 +61,25 @@ Call `change.create`. It records and completes exactly:
55
61
  3. `record-intent`
56
62
 
57
63
  The returned change workflow must be `completed`; research, plan, context, dev, test,
58
- review, and deliver remain independent capabilities. Standard and major changes may
59
- store delta artifacts under `.ultra/changes/active/<id>/delta/`. Incidents maintain
60
- the structured diagnosis artifact.
64
+ review, and deliver remain independent capabilities. Keep every Change-owned semantic
65
+ artifact, finding, plan, context, test result, review result, documentation update,
66
+ and progress projection below `.ultra/changes/active/<id>/`. Incidents maintain the
67
+ structured diagnosis artifact there as well.
68
+
69
+ `change.create` and `change.update` publish the portable Change Contract to
70
+ `.ultra/tasks/tasks.json`, even before the Change owns tasks. The tracked Change
71
+ artifacts carry semantic bodies; the checkpoint carries normalized baseline, Change,
72
+ and task records with digest ancestry. Never edit either generated checkpoint or
73
+ checkout-local projection directly.
74
+
75
+ Before planning a standard or major change, write the accepted specification updates
76
+ to the Change overlay and call `change.delta` with exact baseline id, repository
77
+ revision, specification digests, decisions, non-goals, acceptance, documentation
78
+ impact, unknowns, and mutation before/after digests. If accepted behavior truly does
79
+ not change baseline semantics, record an empty typed delta with a specific
80
+ `no_semantic_change_reason`; do not manufacture a mutation. Read back the registered
81
+ delta and deterministic `progress.md`. This records proposed authority only and must
82
+ not edit baseline specifications.
61
83
 
62
84
  Use `change.update` when accepted intent or evidence changes. Update the Change
63
85
  Contract, classification, research disposition, and docs impact together when their
@@ -66,9 +88,11 @@ change must become stale and be regenerated; never keep old evidence green by pr
66
88
  assertion.
67
89
 
68
90
  If research is required, recommend `ultra-research` with the recorded disposition. If
69
- the contract is sufficiently evidenced, recommend `ultra-plan`. Return and wait for an
70
- explicit user invocation; do not start either workflow. `change.context` is compiled
71
- only for an actual plan, implementation, test, or review consumer.
91
+ the contract is sufficiently evidenced, finish the typed delta and recommend
92
+ `ultra-plan`. Direct Build skips Research only; it never skips Plan, DB-backed task
93
+ contracts, or fresh task context. Return and wait for an explicit user invocation; do
94
+ not start another workflow. `change.context` is compiled only for an actual plan,
95
+ implementation, test, or review consumer.
72
96
 
73
97
  Stable implementation discoveries use the approval-gated specification-learning
74
98
  transitions. External memory and graph providers retain their own content; Ultra stores
@@ -79,3 +103,5 @@ and `allowed_transitions`. Recommend a semantic next action from those transitio
79
103
  If current intent does not already select it, present the recommendation and credible
80
104
  alternatives through the interaction protocol, then wait. MCP does not encode a
81
105
  semantic next action.
106
+
107
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -10,8 +10,9 @@ 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, `task.ledger_get`, baseline, change, breadcrumb `accepted_intent`,
14
+ decisions, tasks, test, review, learning, and current checkout. Import a newer
15
+ descendant checkpoint before convergence; stop on any typed merge conflict.
15
16
  2. Resume or start a deliver workflow bound to the change and baseline.
16
17
  3. Require complete task evidence, a current test report, both review axes, current
17
18
  context, and resolved material decisions. Record `bind-evidence`.
@@ -21,25 +22,37 @@ Evidence from another revision, task set, or change is invalid.
21
22
  ## Reconcile specifications
22
23
 
23
24
  Resolve every specification-learning candidate. Apply approved learning to its target
24
- with before/after digests and verification; preserve rejection reasons.
25
+ inside the Change overlay; preserve rejection reasons. Read the current registered
26
+ `change.delta` and verify every baseline anchor, payload digest, acceptance reference,
27
+ and unknown.
25
28
 
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`.
29
+ Write documentation updates below
30
+ `.ultra/changes/active/<change-id>/documentation/`. Call
31
+ `change.documentation_reconcile` for every Change: bind the current delta id and
32
+ digest, exact before/after document digests, delta and acceptance references,
33
+ verification, and each verified consumer. When no documentation changes, record an
34
+ empty reconciliation with a specific `no_change_reason`. An unexplained orphan
35
+ document is blocking. Record `reconcile-specifications`.
30
36
 
31
37
  Compile final convergence context and run the risk-selected candidate checks, install
32
38
  or doctor smoke when this change modifies distributed assets, public-seam verification,
33
39
  and recovery sanity. Record the immutable context under `verify-candidate`.
34
40
 
35
41
  Call `change.converge`; MCP derives readiness from durable dev, test, review,
36
- specification, docs, diagnosis, and current checkout evidence. Record
42
+ typed delta, documentation reconciliation, diagnosis, and current checkout evidence.
43
+ Successful convergence publishes the current portable team checkpoint.
44
+ Record
37
45
  `converge-authority` only when ready.
38
46
 
39
- Call `change.archive` with the reconciliation manifest and summary. Verify the refreshed
40
- baseline and archived packet, then record `archive-change`.
47
+ Call `change.archive` with the summary. MCP preflights all target before states and
48
+ overlay digests, applies the complete packet atomically, refreshes baseline authority,
49
+ rebinds every registered artifact into the self-contained archive, and rolls back or
50
+ resumes from its transaction journal after interruption. A partial success is not
51
+ delivery. Archive publication refreshes the checkpoint with the archived Change and
52
+ new baseline summary. Verify the refreshed baseline and archived packet, then record
53
+ `archive-change`.
41
54
 
42
- Write `.ultra/reports/delivery/<workflow-id>.json` with
55
+ Write `<archived-change-root>/delivery/<workflow-id>/report.json` with
43
56
  `ultra-delivery-report-v1`: archived change, baseline, HEAD, worktree and context
44
57
  digests, local checks, rollback notes, and timestamp. The report must not contain a
45
58
  release decision. Record it under `verify-delivery` and complete the workflow.
@@ -54,3 +67,5 @@ state.
54
67
 
55
68
  Return archive and baseline state, evidence digests, recovery notes, residual risks,
56
69
  and allowed transitions.
70
+
71
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -10,14 +10,25 @@ 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, `task.ledger_get`, breadcrumb `accepted_intent`, active decisions, task
14
+ contract, dependencies, dev runs, sessions, HEAD, and worktree. Import a newer
15
+ descendant team checkpoint before taking a lease. Stop on a typed baseline, Change,
16
+ task, ancestry, or active-session conflict; do not merge authority by editing JSON.
15
17
  2. Resume the matching dev run or start one with `change_id` and `task_id`.
16
18
  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`.
19
+ 4. Compile `change.context` with `role: implement` and `gate: implementation`. The
20
+ public seam, verification command, and context refs come from the DB-backed task
21
+ contract; update that contract before compilation instead of overriding it in the
22
+ call. Persist any changed external-provider metadata with `change.update` first.
23
+ Context compilation itself is read-only with respect to Change and provider
24
+ authority.
25
+ 5. Confirm the manifest contains the selected task, its direct dependencies, direct
26
+ dependents needed for integration, and relevant integration checkpoints—not the
27
+ entire task graph. Referenced file bodies stay lazy. The token estimate includes
28
+ inline Change/task authority as well as file estimates; budget overflow is
29
+ advisory, while a missing or stale required reference is blocking. Record the
30
+ immutable manifest as `compile-context`.
31
+ 6. Reuse an existing valid session. Otherwise pass admission and call `session.spawn`.
21
32
  Work only in the returned worktree and preserve unrelated changes.
22
33
 
23
34
  Do not spawn recursively from a parent-owned worker session. Process exit is transport
@@ -38,8 +49,30 @@ evidence, never task completion.
38
49
  5. Run a task-scoped independent review on specification fidelity and relevant
39
50
  engineering risks. Record `review-slice`. Fix blocking findings and refresh affected
40
51
  evidence.
41
- 6. Create the authorized local task commit. Do not push, publish, tag, or deploy unless
42
- the user separately authorized that external effect.
52
+ 6. In direct mode, update the task to `completed` only after verification and review
53
+ are current. This publishes the durable team ledger checkpoint. Create one
54
+ authorized local task commit containing code, semantic artifacts, and that
55
+ checkpoint. Read the resulting `HEAD`, then record it as `completion_commit`;
56
+ that hash remains checkout-local and must not create another Git commit. Do not
57
+ push, publish, tag, or deploy unless the user separately authorized that external
58
+ effect.
59
+
60
+ The tracked team checkpoint contains durable task status and contract evidence, not
61
+ live ownership. `in_progress`, `session_id`, leases, worktrees, telemetry, and
62
+ `completion_commit` remain checkout-local. The local generated task view lives under
63
+ `.ultra/.runtime/projections/` and is never committed.
64
+
65
+ Treat the compiled task-context contract as the handoff boundary: purpose, why,
66
+ constraints, non-goals, target seams/files, pattern refs, acceptance, documentation
67
+ impact, recovery, and definition of drift. If accepted intent, the task contract, or a
68
+ digest-bound reference changes, update the owning authority and recompile rather than
69
+ continuing from a stale packet.
70
+
71
+ Keep implementation findings and documentation candidates inside the owning Change
72
+ root. When code changes invalidate or extend maintained documentation, update the
73
+ Change documentation overlay and its evidence rather than editing accepted baseline
74
+ specifications. Hooks may remind the host to reconcile documentation; they must not
75
+ block ordinary development or choose the semantic update.
43
76
 
44
77
  Task-level tests and review belong to this inner loop. They do not replace aggregate
45
78
  change testing or review when multiple slices, integrations, or broader risks require
@@ -48,8 +81,15 @@ those capabilities.
48
81
  ## Complete or recover
49
82
 
50
83
  In direct mode, integrate the task commit through the repository's accepted path,
51
- verify ancestry and the public seam, close the session, update the task to `completed`,
52
- record `record-completion`, and complete the dev workflow.
84
+ verify ancestry and the public seam, close the session, record `record-completion`,
85
+ and complete the dev workflow. If commit creation fails after the pre-commit
86
+ completion checkpoint, move the still-session-owned task to `blocked`, preserve the
87
+ worktree, and record the failure. A completion with a recorded commit remains
88
+ terminal.
89
+
90
+ Change convergence revalidates the exact latest
91
+ `change_id + task_id + implement + implementation` snapshot for every executable
92
+ task. A newer review, test, or plan packet cannot substitute for it.
53
93
 
54
94
  In a parent-supervised worker, record completion evidence and exit without closing the
55
95
  parent-owned lease. The primary host verifies the exact session commit, performs any
@@ -62,3 +102,5 @@ worktree and evidence, and resume the same run later.
62
102
 
63
103
  Return outcome, changed paths, public seam, exact checks, review axes, commit and
64
104
  session state, residual risk, and allowed transitions.
105
+
106
+ Never invoke the recommended capability here; wait for an explicit user invocation.
@@ -13,10 +13,14 @@ 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;
22
+ - Git team-checkpoint schema, ancestry, per-baseline, per-Change, and per-task drift,
23
+ plus checkout-local baseline revalidation requirements;
20
24
  - projection jobs and event cursors;
21
25
  - sessions, preserved worktrees, incidents, circuit state, and archive journals;
22
26
  - external provider boundaries.
@@ -30,21 +34,30 @@ by doctor.
30
34
  With explicit repair authorization, `system.doctor` may:
31
35
 
32
36
  - apply backup-first supported schema migrations;
33
- - migrate active pre-18 rigid workflows into recoverable adaptive state;
37
+ - migrate incompatible rigid workflows into recoverable adaptive state;
34
38
  - requeue or regenerate projections from healthy DB authority;
35
39
  - reconcile orphan sessions without deleting unrelated or dirty worktrees;
36
40
  - resume supported archive journals;
37
41
  - recover missing workflow provenance as a blocked run requiring real evidence.
38
42
 
39
43
  It may not create a Git checkpoint, replace a healthy baseline, accept known failures,
40
- resolve decisions, regenerate semantic content, or edit application code.
44
+ resolve decisions, regenerate semantic content, import or publish the team checkpoint,
45
+ or edit application code. A missing or drifted valid checkpoint is a warning; invalid
46
+ content or conflicting ancestry is degraded authority. Resolve semantic checkpoint
47
+ state explicitly with `task.ledger_import` or `task.ledger_publish` after reviewing
48
+ the typed conflict.
41
49
 
42
50
  Corrupt SQLite requires preservation of DB, WAL, and SHM followed by an explicit
43
51
  restore-or-rebaseline choice. Missing hook adapters or stale cache paths require
44
52
  `ubp --doctor` and adapter reinstall; never patch plugin cache contents by hand.
53
+ A legacy task projection is never rewritten by doctor. The supported publication path
54
+ first proves its durable task fields match SQLite and preserves its original bytes in
55
+ the local runtime backup directory.
45
56
 
46
57
  After repair, rerun doctor read-only and verify backup paths, schema, installed hashes,
47
58
  entry points, MCP startup, and hook smoke tests as applicable.
48
59
 
49
60
  Return health first, failed checks, mechanical repairs performed, preserved recovery
50
61
  paths, unresolved owners, and allowed transitions plus any unique required transition.
62
+
63
+ Never invoke the recommended capability here; wait for an explicit user invocation.