ultra-builder-pro-cli 0.14.0 → 0.16.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 (163) hide show
  1. package/.ultra-template/docs/research/README.md +11 -7
  2. package/.ultra-template/specs/architecture.md +36 -36
  3. package/.ultra-template/specs/discovery.md +46 -33
  4. package/.ultra-template/specs/product.md +38 -29
  5. package/.ultra-template/specs/research-distillate.md +48 -0
  6. package/.ultra-template/tasks/contexts/TEMPLATE.md +11 -28
  7. package/CHANGELOG.md +77 -1
  8. package/README.md +46 -24
  9. package/adapters/_shared/codex-assets.cjs +6 -2
  10. package/adapters/_shared/file-ops.cjs +6 -2
  11. package/adapters/_shared/handbook.cjs +2 -1
  12. package/adapters/claude.js +3 -2
  13. package/adapters/codex.js +1 -0
  14. package/adapters/kimi.js +3 -2
  15. package/adapters/opencode.js +3 -2
  16. package/agents/review-coordinator.md +9 -6
  17. package/commands/ultra-change.md +1 -1
  18. package/commands/ultra-plan.md +1 -1
  19. package/commands/ultra-research.md +1 -1
  20. package/commands/ultra-think.md +2 -2
  21. package/docs/AGENT-CONTEXT.md +43 -19
  22. package/docs/ARCHITECTURE.md +54 -13
  23. package/docs/PLAN.zh-CN.md +4 -2
  24. package/docs/ROADMAP.md +10 -4
  25. package/docs/RUNTIME-COMPAT-MATRIX.md +4 -4
  26. package/docs/STATE-DB-ACCESS-POLICY.md +33 -29
  27. package/docs/USER-HANDBOOK-CONTRACT.md +8 -2
  28. package/docs/WORKFLOW-LIFECYCLE.md +342 -0
  29. package/hooks/README.md +6 -1
  30. package/hooks/health_check.py +12 -3
  31. package/hooks/subagent_tracker.py +52 -77
  32. package/mcp-server/hook-event.cjs +34 -0
  33. package/mcp-server/lib/archive-journal.cjs +17 -5
  34. package/mcp-server/lib/baseline-workflow.cjs +158 -18
  35. package/mcp-server/lib/change-workflow.cjs +598 -85
  36. package/mcp-server/lib/context-spine.cjs +305 -25
  37. package/mcp-server/lib/decision-dialogue.cjs +713 -0
  38. package/mcp-server/lib/doctor.cjs +42 -3
  39. package/mcp-server/lib/init-project.cjs +210 -16
  40. package/mcp-server/lib/plan-store.cjs +4 -1
  41. package/mcp-server/lib/pricing.cjs +7 -10
  42. package/mcp-server/lib/project-breadcrumb.cjs +15 -0
  43. package/mcp-server/lib/projector.cjs +57 -3
  44. package/mcp-server/lib/spec-learning.cjs +79 -2
  45. package/mcp-server/lib/state-db.cjs +168 -8
  46. package/mcp-server/lib/state-ops.cjs +160 -9
  47. package/mcp-server/lib/workflow-state.cjs +2305 -0
  48. package/mcp-server/server.cjs +215 -15
  49. package/orchestrator/daemon.cjs +1 -2
  50. package/orchestrator/dispatch-rules.cjs +5 -31
  51. package/orchestrator/planner/plan-builder.cjs +21 -12
  52. package/package.json +13 -3
  53. package/skills/ultra-change/SKILL.md +94 -80
  54. package/skills/ultra-change/references/change-contract.md +36 -0
  55. package/skills/ultra-deliver/SKILL.md +76 -79
  56. package/skills/ultra-deliver/references/baseline-reconciliation.md +20 -0
  57. package/skills/ultra-dev/SKILL.md +62 -59
  58. package/skills/ultra-doctor/SKILL.md +61 -82
  59. package/skills/ultra-init/SKILL.md +101 -103
  60. package/skills/ultra-init/references/brownfield-adoption.md +2 -2
  61. package/skills/ultra-plan/SKILL.md +99 -85
  62. package/skills/ultra-plan/references/semantic-preflight.md +18 -0
  63. package/skills/ultra-research/SKILL.md +102 -60
  64. package/skills/ultra-research/references/00-problem-validation.md +36 -0
  65. package/skills/ultra-research/references/01-opportunity-discovery.md +33 -0
  66. package/skills/ultra-research/references/02-market-assessment.md +30 -0
  67. package/skills/ultra-research/references/03-competitive-landscape.md +31 -0
  68. package/skills/ultra-research/references/04-product-strategy.md +32 -0
  69. package/skills/ultra-research/references/05-assumptions-validation.md +31 -0
  70. package/skills/ultra-research/references/10-user-personas.md +29 -0
  71. package/skills/ultra-research/references/11-user-scenarios.md +28 -0
  72. package/skills/ultra-research/references/20-user-stories.md +30 -0
  73. package/skills/ultra-research/references/21-features-scope.md +29 -0
  74. package/skills/ultra-research/references/22-success-metrics.md +28 -0
  75. package/skills/ultra-research/references/30-architecture-context.md +30 -0
  76. package/skills/ultra-research/references/31-solution-strategy.md +28 -0
  77. package/skills/ultra-research/references/32-building-blocks.md +28 -0
  78. package/skills/ultra-research/references/40-deployment.md +29 -0
  79. package/skills/ultra-research/references/41-quality-risks.md +28 -0
  80. package/skills/ultra-research/references/99-synthesis.md +35 -0
  81. package/skills/ultra-review/SKILL.md +63 -73
  82. package/skills/ultra-review/references/review-modes.md +27 -0
  83. package/skills/ultra-review/references/unified-schema.md +25 -0
  84. package/skills/ultra-review/scripts/review_wait.py +40 -3
  85. package/skills/ultra-status/SKILL.md +41 -41
  86. package/skills/ultra-test/SKILL.md +54 -55
  87. package/skills/ultra-think/SKILL.md +26 -12
  88. package/skills/ultra-think/references/decision-dialogue.md +130 -0
  89. package/skills/ultra-verify/SKILL.md +1 -1
  90. package/skills/ultra-verify/references/{confidence-system.md → evidence-status.md} +3 -3
  91. package/spec/cli-protocol.md +17 -3
  92. package/spec/mcp-tools.yaml +683 -64
  93. package/spec/schemas/baseline-reconciliation.v1.schema.json +73 -0
  94. package/spec/schemas/delivery-report.v1.schema.json +111 -0
  95. package/spec/schemas/mcp-tools.schema.json +1 -1
  96. package/spec/schemas/state-db.sql +140 -1
  97. package/spec/schemas/tasks.v4.5.schema.json +8 -1
  98. package/spec/schemas/test-report.v1.schema.json +194 -0
  99. package/ultra-tools/cli.cjs +1 -1
  100. package/ultra-tools/commands/migrate.cjs +3 -3
  101. package/ultra-tools/commands/status.cjs +228 -12
  102. package/.ultra-template/delivery-report.json +0 -9
  103. package/.ultra-template/test-report.json +0 -8
  104. package/adapters/_shared/tests/file-ops.test.cjs +0 -54
  105. package/adapters/_shared/tests/frontmatter.test.cjs +0 -47
  106. package/adapters/_shared/tests/handbook.test.cjs +0 -101
  107. package/adapters/_shared/tests/path-rewrite.test.cjs +0 -60
  108. package/adapters/_shared/tests/provenance.test.cjs +0 -133
  109. package/adapters/_shared/tests/resolve-target.test.cjs +0 -75
  110. package/adapters/_shared/tests/runtime-assets.test.cjs +0 -149
  111. package/adapters/_shared/tests/settings-merge.test.cjs +0 -85
  112. package/adapters/_shared/tests/validate.test.cjs +0 -30
  113. package/adapters/tests/claude.test.cjs +0 -138
  114. package/adapters/tests/codex-hook.test.cjs +0 -142
  115. package/adapters/tests/codex.test.cjs +0 -430
  116. package/adapters/tests/kimi-hook.test.cjs +0 -149
  117. package/adapters/tests/kimi.test.cjs +0 -259
  118. package/adapters/tests/opencode.test.cjs +0 -357
  119. package/mcp-server/lib/archive-journal.test.cjs +0 -35
  120. package/mcp-server/lib/baseline-workflow.test.cjs +0 -477
  121. package/mcp-server/lib/change-workflow.test.cjs +0 -971
  122. package/mcp-server/lib/context-spine.test.cjs +0 -93
  123. package/mcp-server/lib/doctor.test.cjs +0 -148
  124. package/mcp-server/lib/plan-store.test.cjs +0 -132
  125. package/mcp-server/lib/prd-parser.test.cjs +0 -82
  126. package/mcp-server/lib/project-breadcrumb.test.cjs +0 -112
  127. package/mcp-server/lib/runtime-state.test.cjs +0 -92
  128. package/mcp-server/lib/spec-learning.test.cjs +0 -67
  129. package/mcp-server/lib/state-db.test.cjs +0 -314
  130. package/mcp-server/lib/task-expander.test.cjs +0 -169
  131. package/mcp-server/lib/topo.test.cjs +0 -158
  132. package/mcp-server/tests/circuit-breaker.test.cjs +0 -125
  133. package/mcp-server/tests/commit-hash-flow.test.cjs +0 -117
  134. package/mcp-server/tests/concurrency-update.test.cjs +0 -96
  135. package/mcp-server/tests/concurrency.test.cjs +0 -112
  136. package/mcp-server/tests/db-ops.test.cjs +0 -121
  137. package/mcp-server/tests/init-project.test.cjs +0 -393
  138. package/mcp-server/tests/migrate.test.cjs +0 -232
  139. package/mcp-server/tests/projector.test.cjs +0 -162
  140. package/mcp-server/tests/server-contract.test.cjs +0 -1467
  141. package/mcp-server/tests/staleness.test.cjs +0 -190
  142. package/mcp-server/tests/state-db.test.cjs +0 -106
  143. package/mcp-server/tests/state-ops.test.cjs +0 -449
  144. package/mcp-server/tests/tagged-tasks.test.cjs +0 -163
  145. package/mcp-server/tests/telemetry.test.cjs +0 -219
  146. package/orchestrator/planner/plan-builder.test.cjs +0 -165
  147. package/orchestrator/tests/auto-merge.test.cjs +0 -216
  148. package/orchestrator/tests/daemon.test.cjs +0 -311
  149. package/orchestrator/tests/dispatch-rules.test.cjs +0 -124
  150. package/orchestrator/tests/parallel-orchestrator.test.cjs +0 -233
  151. package/orchestrator/tests/phase-8b-gate.test.cjs +0 -183
  152. package/orchestrator/tests/recovery.test.cjs +0 -190
  153. package/orchestrator/tests/session-runner.test.cjs +0 -314
  154. package/orchestrator/tests/skill-miner.test.cjs +0 -147
  155. package/orchestrator/tests/worktree-manager.test.cjs +0 -148
  156. package/skills/ultra-research/references/architecture.md +0 -38
  157. package/skills/ultra-research/references/problem-product.md +0 -39
  158. package/skills/ultra-research/references/quality-delivery.md +0 -34
  159. package/skills/ultra-research/references/synthesis.md +0 -38
  160. package/skills/ultra-research/references/users-scope.md +0 -40
  161. package/ultra-tools/legacy-memory.test.cjs +0 -80
  162. package/ultra-tools/status-command.test.cjs +0 -181
  163. package/ultra-tools/system-command.test.cjs +0 -309
@@ -1,147 +0,0 @@
1
- 'use strict';
2
-
3
- // Phase 7.3 — Skill miner (heuristic, no LLM):
4
- // • mineSession scans a session's events + task state for "solved
5
- // something non-trivial" signals and writes skills/learned/
6
- // <ts>_<sid>_unverified.md drafts.
7
- // • Idempotent: same sid twice → only one draft per signal.
8
-
9
- const { test } = require('node:test');
10
- const assert = require('node:assert/strict');
11
- const fs = require('node:fs');
12
- const os = require('node:os');
13
- const path = require('node:path');
14
-
15
- const { initStateDb, closeStateDb } = require('../../mcp-server/lib/state-db.cjs');
16
- const ops = require('../../mcp-server/lib/state-ops.cjs');
17
- const skillMiner = require('../skill-miner.cjs');
18
-
19
- function mkFixture() {
20
- const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-skill-'));
21
- const dbPath = path.join(repoRoot, '.ultra', 'state.db');
22
- const { db } = initStateDb(dbPath);
23
- const skillsRoot = path.join(repoRoot, 'skills');
24
- fs.mkdirSync(skillsRoot, { recursive: true });
25
- return { repoRoot, db, skillsRoot };
26
- }
27
-
28
- function teardown(repoRoot, db) {
29
- try { closeStateDb(db); } catch (_) { /* ignore */ }
30
- try { fs.rmSync(repoRoot, { recursive: true, force: true }); } catch (_) { /* ignore */ }
31
- }
32
-
33
- function seed(db, { sid, task_id, title = 'demo', tag = null }) {
34
- ops.createTask(db, { id: task_id, title, type: 'feature', priority: 'P1', tag });
35
- ops.createSession(db, {
36
- sid, task_id, runtime: 'claude',
37
- worktree_path: `/tmp/${sid}/wt`, artifact_dir: `/tmp/${sid}/art`,
38
- });
39
- }
40
-
41
- test('mineSession: task_completed signal → generates draft', () => {
42
- const { repoRoot, db, skillsRoot } = mkFixture();
43
- try {
44
- seed(db, { sid: 's-done', task_id: 't-done', title: 'ship auth flow' });
45
- ops.patchTask(db, 't-done', { status: 'in_progress' });
46
- ops.appendEvent(db, { type: 'task_completed', task_id: 't-done', session_id: 's-done', payload: { to: 'completed' } });
47
- ops.patchTask(db, 't-done', { status: 'completed' });
48
-
49
- const out = skillMiner.mineSession(db, { sid: 's-done', skillsRoot });
50
- assert.equal(out.drafts.length, 1);
51
- assert.match(out.drafts[0], /_unverified\.md$/);
52
- const draft = fs.readFileSync(out.drafts[0], 'utf8');
53
- assert.match(draft, /kind: task-completion/);
54
- assert.match(draft, /unverified: true/);
55
- assert.match(draft, /ship auth flow/);
56
- } finally { teardown(repoRoot, db); }
57
- });
58
-
59
- test('mineSession: task_circuit_broken signal → debug-pattern draft', () => {
60
- const { repoRoot, db, skillsRoot } = mkFixture();
61
- try {
62
- seed(db, { sid: 's-cb', task_id: 't-cb', title: 'flaky integration' });
63
- ops.appendEvent(db, { type: 'task_circuit_broken', task_id: 't-cb', session_id: 's-cb', payload: { threshold: 3 } });
64
- const out = skillMiner.mineSession(db, { sid: 's-cb', skillsRoot });
65
- assert.equal(out.drafts.length, 1);
66
- const draft = fs.readFileSync(out.drafts[0], 'utf8');
67
- assert.match(draft, /kind: debug-pattern/);
68
- } finally { teardown(repoRoot, db); }
69
- });
70
-
71
- test('mineSession: session_crashed signal → recovery-pattern draft', () => {
72
- const { repoRoot, db, skillsRoot } = mkFixture();
73
- try {
74
- seed(db, { sid: 's-crash', task_id: 't-crash', title: 'crashing task' });
75
- ops.appendEvent(db, { type: 'session_crashed', task_id: 't-crash', session_id: 's-crash', payload: { to: 'crashed' } });
76
- const out = skillMiner.mineSession(db, { sid: 's-crash', skillsRoot });
77
- assert.equal(out.drafts.length, 1);
78
- const draft = fs.readFileSync(out.drafts[0], 'utf8');
79
- assert.match(draft, /kind: recovery-pattern/);
80
- } finally { teardown(repoRoot, db); }
81
- });
82
-
83
- test('mineSession: no trigger signals → no draft', () => {
84
- const { repoRoot, db, skillsRoot } = mkFixture();
85
- try {
86
- seed(db, { sid: 's-quiet', task_id: 't-quiet', title: 'nothing happened' });
87
- const out = skillMiner.mineSession(db, { sid: 's-quiet', skillsRoot });
88
- assert.equal(out.drafts.length, 0);
89
- } finally { teardown(repoRoot, db); }
90
- });
91
-
92
- test('mineSession: idempotent — running twice on same sid yields no new drafts', () => {
93
- const { repoRoot, db, skillsRoot } = mkFixture();
94
- try {
95
- seed(db, { sid: 's-idem', task_id: 't-idem', title: 'shipped' });
96
- ops.patchTask(db, 't-idem', { status: 'in_progress' });
97
- ops.appendEvent(db, { type: 'task_completed', task_id: 't-idem', session_id: 's-idem', payload: { to: 'completed' } });
98
- ops.patchTask(db, 't-idem', { status: 'completed' });
99
-
100
- const first = skillMiner.mineSession(db, { sid: 's-idem', skillsRoot });
101
- const second = skillMiner.mineSession(db, { sid: 's-idem', skillsRoot });
102
- assert.equal(first.drafts.length, 1);
103
- assert.equal(second.drafts.length, 0, 'second mining should see existing draft and skip');
104
- const learned = fs.readdirSync(path.join(skillsRoot, 'learned'));
105
- assert.equal(learned.length, 1);
106
- } finally { teardown(repoRoot, db); }
107
- });
108
-
109
- test('mineSession: missing session → empty drafts array', () => {
110
- const { repoRoot, db, skillsRoot } = mkFixture();
111
- try {
112
- const out = skillMiner.mineSession(db, { sid: 's-nope', skillsRoot });
113
- assert.deepEqual(out.drafts, []);
114
- } finally { teardown(repoRoot, db); }
115
- });
116
-
117
- test('AC flow: 5 mock sessions (3 with signal, 2 empty) → ≥3 drafts', () => {
118
- const { repoRoot, db, skillsRoot } = mkFixture();
119
- try {
120
- // 3 with a trigger signal
121
- for (const [i, kind] of [['ac1', 'completed'], ['ac2', 'crashed'], ['ac3', 'broken']]) {
122
- seed(db, { sid: `s-${i}`, task_id: `t-${i}`, title: `task ${i}` });
123
- if (kind === 'completed') {
124
- ops.patchTask(db, `t-${i}`, { status: 'in_progress' });
125
- ops.appendEvent(db, { type: 'task_completed', task_id: `t-${i}`, session_id: `s-${i}`, payload: {} });
126
- ops.patchTask(db, `t-${i}`, { status: 'completed' });
127
- } else if (kind === 'crashed') {
128
- ops.appendEvent(db, { type: 'session_crashed', task_id: `t-${i}`, session_id: `s-${i}`, payload: {} });
129
- } else if (kind === 'broken') {
130
- ops.appendEvent(db, { type: 'task_circuit_broken', task_id: `t-${i}`, session_id: `s-${i}`, payload: { threshold: 3 } });
131
- }
132
- }
133
- // 2 empty
134
- for (const i of ['ac4', 'ac5']) {
135
- seed(db, { sid: `s-${i}`, task_id: `t-${i}`, title: `task ${i}` });
136
- }
137
-
138
- let totalDrafts = 0;
139
- for (const i of ['ac1', 'ac2', 'ac3', 'ac4', 'ac5']) {
140
- const out = skillMiner.mineSession(db, { sid: `s-${i}`, skillsRoot });
141
- totalDrafts += out.drafts.length;
142
- }
143
- assert.ok(totalDrafts >= 3, `expected ≥3 drafts across 5 sessions, got ${totalDrafts}`);
144
- const learned = fs.readdirSync(path.join(skillsRoot, 'learned'));
145
- assert.ok(learned.length >= 3);
146
- } finally { teardown(repoRoot, db); }
147
- });
@@ -1,148 +0,0 @@
1
- 'use strict';
2
-
3
- // Phase 8B.3 — N-concurrent git worktree management.
4
- //
5
- // Session-runner (Phase 4.5) already encapsulates `git worktree add/remove`
6
- // for a single session. worktree-manager is the registry / batch layer:
7
- // allocate N, track them via `git worktree list --porcelain`, release one
8
- // or all (for crash recovery), detect filesystem orphans.
9
- //
10
- // AC (from PLAN §6 Phase 8B.3):
11
- // - 3 concurrent slices → 3 independent worktrees, git worktree list
12
- // shows them; main branch checkout untouched
13
- // - 5 rapid allocations in one tick → no .git/config.lock contention
14
- // (Node single-thread execFileSync is naturally serialized)
15
- // - releaseAll cleans filesystem leftovers even when git forgot them
16
-
17
- const { test } = require('node:test');
18
- const assert = require('node:assert/strict');
19
- const fs = require('node:fs');
20
- const os = require('node:os');
21
- const path = require('node:path');
22
- const { execFileSync } = require('node:child_process');
23
-
24
- const wm = require('../worktree-manager.cjs');
25
-
26
- function mkRepo() {
27
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-wtmgr-'));
28
- execFileSync('git', ['init', '-q'], { cwd: dir });
29
- execFileSync('git', ['config', 'user.email', 'test@ubp.dev'], { cwd: dir });
30
- execFileSync('git', ['config', 'user.name', 'ubp-test'], { cwd: dir });
31
- fs.writeFileSync(path.join(dir, 'README.md'), '# seed\n');
32
- execFileSync('git', ['add', '-A'], { cwd: dir });
33
- execFileSync('git', ['commit', '-q', '-m', 'seed'], { cwd: dir });
34
- return dir;
35
- }
36
-
37
- function cleanup(dir) {
38
- try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_) { /* best-effort */ }
39
- }
40
-
41
- test('allocate: creates worktree under .ultra/worktrees/<sid> and returns path', () => {
42
- const repo = mkRepo();
43
- try {
44
- const { worktree_path } = wm.allocate({ repoRoot: repo, sid: 'sess-abc' });
45
- assert.ok(worktree_path.endsWith('.ultra/worktrees/sess-abc'));
46
- assert.ok(fs.existsSync(worktree_path), 'worktree dir should exist');
47
- assert.ok(fs.existsSync(path.join(worktree_path, 'README.md')), 'seed file visible');
48
- } finally { cleanup(repo); }
49
- });
50
-
51
- test('release: removes worktree dir and unregisters from git', () => {
52
- const repo = mkRepo();
53
- try {
54
- const { worktree_path } = wm.allocate({ repoRoot: repo, sid: 'sess-rel' });
55
- wm.release({ repoRoot: repo, worktree_path });
56
- assert.equal(fs.existsSync(worktree_path), false, 'dir should be removed');
57
- const active = wm.listActive(repo);
58
- assert.equal(active.length, 0, 'no slices should remain');
59
- } finally { cleanup(repo); }
60
- });
61
-
62
- test('3 concurrent allocates → 3 independent worktrees visible via listActive', () => {
63
- const repo = mkRepo();
64
- try {
65
- const sids = ['sess-a', 'sess-b', 'sess-c'];
66
- const paths = sids.map((sid) => wm.allocate({ repoRoot: repo, sid }).worktree_path);
67
- for (const p of paths) assert.ok(fs.existsSync(p), `${p} missing`);
68
- const active = wm.listActive(repo);
69
- const activeSids = active.map((a) => a.sid).sort();
70
- assert.deepEqual(activeSids, sids.slice().sort());
71
- // Every entry has a distinct worktree_path
72
- const uniquePaths = new Set(active.map((a) => a.worktree_path));
73
- assert.equal(uniquePaths.size, 3);
74
- } finally { cleanup(repo); }
75
- });
76
-
77
- test('5 rapid allocates in one tick → no .git/config.lock contention', () => {
78
- const repo = mkRepo();
79
- try {
80
- const sids = ['s1', 's2', 's3', 's4', 's5'];
81
- // Execute synchronously back-to-back; Node single-thread guarantees
82
- // serialized git calls, so no file-lock race.
83
- for (const sid of sids) {
84
- wm.allocate({ repoRoot: repo, sid });
85
- }
86
- assert.equal(wm.listActive(repo).length, 5);
87
- } finally { cleanup(repo); }
88
- });
89
-
90
- test('listActive: only returns worktrees under .ultra/worktrees/ (main excluded)', () => {
91
- const repo = mkRepo();
92
- try {
93
- // Manually add a worktree OUTSIDE .ultra/worktrees — should be ignored.
94
- const outsideDir = path.join(repo, 'other-wt');
95
- execFileSync('git', ['worktree', 'add', '--detach', outsideDir, 'HEAD'], {
96
- cwd: repo, stdio: 'pipe',
97
- });
98
- wm.allocate({ repoRoot: repo, sid: 'sess-domain' });
99
- const active = wm.listActive(repo);
100
- assert.equal(active.length, 1);
101
- assert.equal(active[0].sid, 'sess-domain');
102
- } finally { cleanup(repo); }
103
- });
104
-
105
- test('listActive: empty repo → []', () => {
106
- const repo = mkRepo();
107
- try {
108
- assert.deepEqual(wm.listActive(repo), []);
109
- } finally { cleanup(repo); }
110
- });
111
-
112
- test('releaseAll: clears all allocated worktrees + filesystem orphans', () => {
113
- const repo = mkRepo();
114
- try {
115
- wm.allocate({ repoRoot: repo, sid: 'x1' });
116
- wm.allocate({ repoRoot: repo, sid: 'x2' });
117
- // Create an orphan dir: git doesn't know about it, but fs exists.
118
- const orphanDir = path.join(repo, '.ultra', 'worktrees', 'orphan-dir');
119
- fs.mkdirSync(orphanDir, { recursive: true });
120
- fs.writeFileSync(path.join(orphanDir, 'junk.txt'), 'x');
121
-
122
- const result = wm.releaseAll(repo);
123
- assert.equal(result.cleaned, 2, 'should report 2 git-tracked cleanups');
124
- assert.equal(wm.listActive(repo).length, 0);
125
- assert.equal(fs.existsSync(orphanDir), false, 'orphan must be swept');
126
- } finally { cleanup(repo); }
127
- });
128
-
129
- test('allocate: baseRef honored — worktree checked out at given ref', () => {
130
- const repo = mkRepo();
131
- try {
132
- // Make a second commit so we have two distinct SHAs.
133
- fs.writeFileSync(path.join(repo, 'second.md'), '# second\n');
134
- execFileSync('git', ['add', '-A'], { cwd: repo });
135
- execFileSync('git', ['commit', '-q', '-m', 'second'], { cwd: repo });
136
- const firstSha = execFileSync('git', ['rev-parse', 'HEAD~1'], {
137
- cwd: repo, stdio: ['ignore', 'pipe', 'pipe'],
138
- }).toString().trim();
139
-
140
- const { worktree_path } = wm.allocate({
141
- repoRoot: repo, sid: 'sess-ref', baseRef: firstSha,
142
- });
143
- const wtSha = execFileSync('git', ['rev-parse', 'HEAD'], {
144
- cwd: worktree_path, stdio: ['ignore', 'pipe', 'pipe'],
145
- }).toString().trim();
146
- assert.equal(wtSha, firstSha);
147
- } finally { cleanup(repo); }
148
- });
@@ -1,38 +0,0 @@
1
- # Architecture research
2
-
3
- Read this reference when the system boundary, quality attributes, integration seams,
4
- technology choice, or implementation constraints are unresolved.
5
-
6
- ## Current system evidence
7
-
8
- Start from the current checkout:
9
-
10
- - entry points and external interfaces;
11
- - domain boundaries and authoritative state;
12
- - data flows and side effects;
13
- - existing dependencies and deployment topology;
14
- - failure, recovery, authorization, and observability paths.
15
-
16
- Do not design a replacement architecture before identifying the live path and the
17
- constraint that requires change.
18
-
19
- ## Decision method
20
-
21
- For each material architecture decision:
22
-
23
- 1. state the required capability and quality constraints;
24
- 2. identify credible options already compatible with the system when possible;
25
- 3. use official primary documentation for library or platform behavior;
26
- 4. evaluate integration cost, operability, failure modes, migration, and rollback;
27
- 5. record the selected option, rejected alternatives that may recur, and the evidence
28
- that would reopen the decision.
29
-
30
- Do not compare technologies for ceremony. A single established repository pattern is
31
- the default when it satisfies the contract.
32
-
33
- ## Record the result
34
-
35
- Update the architecture specification with system context, boundaries, building
36
- blocks, runtime flow, authoritative state, external contracts, quality scenarios,
37
- deployment constraints, and accepted decisions. Connect each required behavior to a
38
- reachable public seam and a verification approach.
@@ -1,39 +0,0 @@
1
- # Problem and product research
2
-
3
- Read this reference when the problem, value, market context, or product direction is
4
- uncertain.
5
-
6
- ## Establish the problem
7
-
8
- - Identify the affected actor, current behavior, observable cost, and desired outcome.
9
- - Distinguish user evidence from stakeholder belief and solution preference.
10
- - Find current alternatives, including manual work or doing nothing.
11
- - State what evidence would disprove the problem framing.
12
-
13
- Use repository artifacts, support data, interviews, analytics, or primary external
14
- sources that actually exist. Do not invent quotes, demand, or market size.
15
-
16
- ## Decide how much market work is needed
17
-
18
- Market sizing and competitive research are optional. Use them only when they affect a
19
- funding, positioning, pricing, or build-versus-buy decision.
20
-
21
- When needed:
22
-
23
- 1. define the market boundary and time period;
24
- 2. show the calculation inputs and sources;
25
- 3. separate direct, adjacent, and substitute alternatives;
26
- 4. explain how the evidence changes the product decision.
27
-
28
- Do not present unsourced top-down totals or call a category competitor-free.
29
-
30
- ## Record the result
31
-
32
- Update the discovery specification with:
33
-
34
- - problem and affected actors;
35
- - observed evidence and source quality;
36
- - desired outcome and non-goals;
37
- - material alternatives and tradeoffs;
38
- - assumptions, validation method, and blocking questions;
39
- - the accepted product decision and why it follows from the evidence.
@@ -1,34 +0,0 @@
1
- # Quality and delivery research
2
-
3
- Read this reference when acceptance depends on operational risk, security, migration,
4
- deployment, recovery, or release constraints.
5
-
6
- ## Risk-driven quality
7
-
8
- Identify failures that would materially affect users, data, authorization, money,
9
- availability, or recoverability. For each relevant risk, record:
10
-
11
- - trigger and affected boundary;
12
- - expected behavior and observable signal;
13
- - prevention, detection, and recovery;
14
- - verification method and owner.
15
-
16
- Apply security, performance, accessibility, and resilience checks according to the
17
- actual product surface. Do not impose generic categories or coverage percentages when
18
- they do not define acceptance.
19
-
20
- ## Delivery path
21
-
22
- Capture the environments, configuration authority, migration order, compatibility
23
- window, health and readiness checks, observability, rollout, rollback, and operator
24
- actions needed for this product.
25
-
26
- Prefer existing repository and platform workflows. Add a new service, dependency, or
27
- deployment mechanism only when the accepted architecture requires it.
28
-
29
- ## Record the result
30
-
31
- Update the architecture and product specifications with testable quality scenarios,
32
- delivery constraints, failure behavior, recovery evidence, and release acceptance.
33
- Keep exact volatile commands in the repository's runbook when they are operational
34
- procedure rather than durable architecture.
@@ -1,38 +0,0 @@
1
- # Baseline synthesis and validation
2
-
3
- Read this reference after the selected research lanes are complete.
4
-
5
- ## Traceability check
6
-
7
- Verify that each accepted outcome has:
8
-
9
- 1. supporting problem, user, or constraint evidence;
10
- 2. an explicit decision and scope boundary;
11
- 3. testable acceptance behavior;
12
- 4. a compatible architecture or delivery path;
13
- 5. a named unresolved blocker when any link is missing.
14
-
15
- Check the reverse direction as well: every proposed component, dependency, and quality
16
- requirement must support an accepted outcome or constraint.
17
-
18
- ## Consistency check
19
-
20
- - Use one term for each actor, boundary, and authoritative data source.
21
- - Remove duplicate narratives and stale alternatives.
22
- - Keep source links next to the claims they support.
23
- - Separate durable decisions from volatile external facts.
24
- - Preserve explicit non-goals and rejected alternatives only when they prevent a
25
- likely future mistake.
26
-
27
- ## Handoff
28
-
29
- Report:
30
-
31
- - validated scope and decisive evidence;
32
- - specification files changed;
33
- - accepted decisions and material uncertainty;
34
- - blockers, or an explicit statement that none remain;
35
- - one next action.
36
-
37
- Do not create a parallel summary document solely for model consumption. The planning
38
- workflow should read the smallest authoritative baseline sections it needs.
@@ -1,40 +0,0 @@
1
- # Users, behavior, and scope
2
-
3
- Read this reference when actors, workflows, acceptance, boundaries, or success signals
4
- are unclear.
5
-
6
- ## Actors and scenarios
7
-
8
- Model only actors supported by evidence. Prefer roles and jobs over fictional persona
9
- biographies. For each material scenario, capture:
10
-
11
- - trigger and preconditions;
12
- - actor goal;
13
- - main flow and system response;
14
- - important failure or recovery path;
15
- - observable completion signal.
16
-
17
- ## Acceptance and scope
18
-
19
- Turn scenarios into testable behavior. Each requirement should name the observable
20
- outcome, relevant constraints, and how success can be verified.
21
-
22
- Define:
23
-
24
- - included behavior required for the accepted outcome;
25
- - explicit non-goals;
26
- - dependencies and assumptions;
27
- - deferred work only when a real sequencing decision exists.
28
-
29
- Avoid arbitrary release buckets or a fixed number of stories. Scope follows the
30
- validated outcome and delivery constraint.
31
-
32
- ## Success signals
33
-
34
- Choose metrics only when they will guide a decision. Define the event, population,
35
- window, source, owner, and interpretation. Do not invent target values; record a
36
- baseline, a user-approved target, or the experiment needed to establish one.
37
-
38
- Update the product specification with actors, scenarios, acceptance, scope, non-goals,
39
- and decision-relevant success signals. Keep business facts in their source system and
40
- link them rather than copying volatile datasets into the specification.
@@ -1,80 +0,0 @@
1
- 'use strict';
2
-
3
- const { test } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const fs = require('node:fs');
6
- const os = require('node:os');
7
- const path = require('node:path');
8
- const Database = require('better-sqlite3');
9
-
10
- const legacy = require('./commands/legacy-memory.cjs');
11
-
12
- function fixture() {
13
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-legacy-memory-'));
14
- const ultra = path.join(root, '.ultra');
15
- fs.mkdirSync(path.join(ultra, 'memory'), { recursive: true });
16
- fs.writeFileSync(path.join(ultra, 'memory', 'memory.db'), 'legacy-hook-store');
17
- const db = new Database(path.join(ultra, 'state.db'));
18
- db.exec(`
19
- CREATE TABLE memory_entries (
20
- id INTEGER PRIMARY KEY, kind TEXT, content TEXT, task_id TEXT,
21
- session_id TEXT, tag TEXT, source TEXT, ts TEXT
22
- );
23
- INSERT INTO memory_entries
24
- (kind, content, task_id, session_id, tag, source, ts)
25
- VALUES ('decision', 'legacy decision', 't1', 's1', 'arch', 'test', '2026-01-01T00:00:00Z');
26
- `);
27
- db.close();
28
- return root;
29
- }
30
-
31
- test('inspect reports both legacy stores without mutating them', () => {
32
- const root = fixture();
33
- try {
34
- const result = legacy.inspectLegacy(root);
35
- assert.equal(result.hook_memory_dir, true);
36
- assert.equal(result.state_memory_table, true);
37
- assert.equal(result.state_memory_entries, 1);
38
- assert.ok(fs.existsSync(path.join(root, '.ultra', 'memory', 'memory.db')));
39
- } finally {
40
- fs.rmSync(root, { recursive: true, force: true });
41
- }
42
- });
43
- test('archive exports legacy state rows and copies hook memory without deletion', () => {
44
- const root = fixture();
45
- try {
46
- const result = legacy.archiveLegacy(root, { timestamp: '2026-07-15T00-00-00-000Z' });
47
- assert.equal(result.archived, true);
48
- assert.ok(fs.existsSync(path.join(result.archive_dir, 'hook-memory', 'memory.db')));
49
- const exported = JSON.parse(fs.readFileSync(path.join(result.archive_dir, 'state-memory-entries.json'), 'utf8'));
50
- assert.equal(exported.entries.length, 1);
51
- assert.equal(exported.entries[0].content, 'legacy decision');
52
- assert.ok(fs.existsSync(path.join(root, '.ultra', 'memory', 'memory.db')));
53
- const db = new Database(path.join(root, '.ultra', 'state.db'), { readonly: true });
54
- assert.ok(db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'memory_entries'").get());
55
- db.close();
56
- } finally {
57
- fs.rmSync(root, { recursive: true, force: true });
58
- }
59
- });
60
-
61
- test('prune requires an exact confirmation and archives before deleting', () => {
62
- const root = fixture();
63
- try {
64
- assert.throws(() => legacy.pruneLegacy(root, { confirm: 'yes' }), /DELETE_ULTRA_LEGACY_MEMORY/);
65
- assert.ok(fs.existsSync(path.join(root, '.ultra', 'memory', 'memory.db')));
66
-
67
- const result = legacy.pruneLegacy(root, {
68
- confirm: 'DELETE_ULTRA_LEGACY_MEMORY',
69
- timestamp: '2026-07-15T00-00-01-000Z',
70
- });
71
- assert.equal(result.pruned, true);
72
- assert.ok(fs.existsSync(result.archive_dir));
73
- assert.ok(!fs.existsSync(path.join(root, '.ultra', 'memory')));
74
- const db = new Database(path.join(root, '.ultra', 'state.db'), { readonly: true });
75
- assert.equal(db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'memory_entries'").get(), undefined);
76
- db.close();
77
- } finally {
78
- fs.rmSync(root, { recursive: true, force: true });
79
- }
80
- });