ultra-builder-pro-cli 0.6.0 → 0.7.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 (68) hide show
  1. package/.ultra-template/changes/active/.gitkeep +1 -0
  2. package/.ultra-template/changes/archive/.gitkeep +1 -0
  3. package/CHANGELOG.md +62 -11
  4. package/README.md +26 -18
  5. package/adapters/_shared/handbook.cjs +7 -7
  6. package/adapters/_shared/runtime-assets.cjs +2 -0
  7. package/adapters/_shared/tests/handbook.test.cjs +4 -1
  8. package/adapters/_shared/tests/runtime-assets.test.cjs +2 -0
  9. package/adapters/opencode.js +81 -20
  10. package/adapters/tests/codex.test.cjs +5 -3
  11. package/adapters/tests/opencode.test.cjs +16 -9
  12. package/bin/install.js +0 -19
  13. package/bin/orchestrator.js +1 -26
  14. package/commands/ultra-change.md +47 -0
  15. package/commands/ultra-deliver.md +14 -7
  16. package/commands/ultra-dev.md +6 -3
  17. package/commands/ultra-doctor.md +36 -0
  18. package/commands/ultra-init.md +4 -3
  19. package/commands/ultra-status.md +7 -5
  20. package/commands/ultra-test.md +2 -1
  21. package/docs/AGENT-CONTEXT.md +31 -23
  22. package/docs/ARCHITECTURE.md +46 -17
  23. package/docs/PLAN.zh-CN.md +35 -29
  24. package/docs/ROADMAP.md +14 -14
  25. package/docs/RUNTIME-COMPAT-MATRIX.md +11 -11
  26. package/hooks/README.md +8 -7
  27. package/hooks/active_task_context.py +22 -15
  28. package/hooks/health_check.py +136 -38
  29. package/hooks/workflow_context.py +120 -30
  30. package/mcp-server/lib/change-workflow.cjs +556 -0
  31. package/mcp-server/lib/change-workflow.test.cjs +259 -0
  32. package/mcp-server/lib/doctor.cjs +112 -0
  33. package/mcp-server/lib/doctor.test.cjs +86 -0
  34. package/mcp-server/lib/init-project.cjs +32 -6
  35. package/mcp-server/lib/runtime-state.cjs +222 -0
  36. package/mcp-server/lib/runtime-state.test.cjs +92 -0
  37. package/mcp-server/lib/state-db.cjs +31 -8
  38. package/mcp-server/lib/state-ops.cjs +17 -3
  39. package/mcp-server/server.cjs +102 -10
  40. package/mcp-server/tests/db-ops.test.cjs +5 -1
  41. package/mcp-server/tests/init-project.test.cjs +45 -0
  42. package/mcp-server/tests/server-contract.test.cjs +123 -1
  43. package/mcp-server/tests/state-db.test.cjs +44 -7
  44. package/mcp-server/tests/state-ops.test.cjs +19 -0
  45. package/orchestrator/daemon.cjs +39 -0
  46. package/orchestrator/tests/daemon.test.cjs +50 -0
  47. package/package.json +3 -3
  48. package/settings.json +1 -2
  49. package/skills/ultra-change/SKILL.md +140 -0
  50. package/skills/ultra-deliver/SKILL.md +81 -15
  51. package/skills/ultra-dev/SKILL.md +32 -12
  52. package/skills/ultra-doctor/SKILL.md +90 -0
  53. package/skills/ultra-init/SKILL.md +8 -5
  54. package/skills/ultra-review/SKILL.md +16 -0
  55. package/skills/ultra-status/SKILL.md +30 -7
  56. package/skills/ultra-test/SKILL.md +14 -2
  57. package/spec/cli-protocol.md +12 -1
  58. package/spec/mcp-tools.yaml +337 -1
  59. package/spec/schemas/mcp-tools.schema.json +1 -1
  60. package/spec/schemas/state-db.sql +117 -0
  61. package/ultra-tools/cli.cjs +3 -0
  62. package/ultra-tools/commands/system.cjs +57 -0
  63. package/ultra-tools/system-command.test.cjs +53 -0
  64. package/adapters/_shared/rtk-detect.cjs +0 -86
  65. package/adapters/_shared/tests/rtk-detect.test.cjs +0 -116
  66. package/docs/LEGACY-HERMES.md +0 -1099
  67. package/orchestrator/code-graph-watcher.cjs +0 -121
  68. package/orchestrator/tests/code-graph-watcher.test.cjs +0 -183
package/CHANGELOG.md CHANGED
@@ -7,6 +7,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.0] — 2026-07-17
11
+
12
+ ### Added
13
+
14
+ - Added `ultra-change` and `ultra-doctor` as native workflows on Claude Code,
15
+ OpenCode, and Codex. Daily fixes, features, redesigns, and incidents now stay
16
+ attached to an explicit change packet after the initial baseline delivery.
17
+ - Added seven `change.*` MCP tools and `system.doctor`, advancing the live MCP
18
+ contract to 29 tools and `.ultra/state.db` to schema `9.0` with 15 tables for
19
+ changes, artifacts, context snapshots, trace links, incidents, projection
20
+ jobs, and durable event-consumer cursors.
21
+ - Added deterministic convergence and archive gates for task completion, spec
22
+ deltas, tests, documentation impact, review evidence, context freshness, and
23
+ baseline reconciliation.
24
+
25
+ ### Changed
26
+
27
+ - `task.init_project` now initializes `state.db` immediately and creates active
28
+ and archived change roots. `tasks.json` remains a generated projection.
29
+ - MCP mutations now commit authority first, enqueue a durable projection job,
30
+ process it, and expose projection status in MCP metadata instead of silently
31
+ swallowing projection failures.
32
+ - Session-start and edit hooks now understand initialized baselines and active
33
+ changes. Health checks surface incidents, projection lag, orphan sessions,
34
+ and missing change artifacts; direct writes to the task projection remain
35
+ blocked even outside a command-scoped workflow.
36
+ - Memory and code-graph content are exclusively owned by separately installed
37
+ providers. Ultra accepts only provider metadata references in compiled change
38
+ contexts and never stores recalled content, graph payloads, or embeddings.
39
+
40
+ ### Fixed
41
+
42
+ - Wired spec-staleness consumption, crash recovery, projection processing, and
43
+ durable cursors into the real orchestrator loop instead of leaving production
44
+ helpers unconsumed.
45
+ - Kept `intent.md` synchronized with authoritative change updates, linked task
46
+ status events to their newly assigned change, and included `plan.export`
47
+ events in projection accounting.
48
+ - Added atomic projection claims and backup-first recovery for failed or stale
49
+ interrupted jobs so a crashed projector cannot remain invisibly `running`.
50
+ - Kept all three native plugin bundles, MCP launchers, agents, hooks, commands,
51
+ and user-handbook renderings on one explicit Ultra-owned asset allowlist.
52
+
53
+ ### Removed
54
+
55
+ - Removed the internal filesystem graph watcher and its dependency; external
56
+ graph providers own indexing and refresh.
57
+ - Removed the retired command-proxy detector, installer option, managed handbook
58
+ block, cache artifacts, prompts, configuration, and documentation surface.
59
+ - Stopped publishing the archived pre-CLI manual in the npm package; it remains
60
+ repository history and cannot be mistaken for an active plugin contract.
61
+
10
62
  ## [0.6.0] — 2026-07-17
11
63
 
12
64
  ### Changed
@@ -21,9 +73,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
73
 
22
74
  ### Removed
23
75
 
24
- - Removed the Gemini CLI adapter, `--gemini` installer surface, extension and
25
- conformance packaging, pricing entries, `gemini-collab`, and all active Ultra
26
- prompts and documentation that offered Gemini as an advisor or runtime.
76
+ - Removed the retired fourth-runtime adapter, installer surface, extension and
77
+ conformance packaging, pricing entries, collaboration companion, and all
78
+ active Ultra prompts and documentation that offered it as an advisor.
27
79
 
28
80
  ### Fixed
29
81
 
@@ -189,8 +241,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
189
241
 
190
242
  - D49 tech-debt sweep: stale Phase comments (`"scheduled for Phase 1"`,
191
243
  `"not implemented in Phase 3.1"`) replaced with accurate `unknown verb`
192
- messages; `docs/ROADMAP.md` status table synchronized; `.gitignore`
193
- excludes `.rtk/` local filter cache.
244
+ messages; `docs/ROADMAP.md` status table synchronized.
194
245
 
195
246
  ### Tests
196
247
 
@@ -201,13 +252,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
201
252
 
202
253
  ### Added
203
254
 
204
- - **Phase 6 — monitoring + live code graph** (D44): RTK soft-dependency hook
205
- (`adapters/_shared/rtk-detect.cjs`) with `--skip-rtk` flag and install hint;
206
- token / cost telemetry with per-runtime pricing table, `telemetry` table +
255
+ - **Phase 6 — monitoring + live code graph** (D44): token / cost telemetry
256
+ with per-runtime pricing table, `telemetry` table +
207
257
  daily `.ultra/telemetry/YYYY-MM-DD.jsonl`; `ultra-tools status --cost`
208
258
  panel with by-runtime / by-task / by-session aggregation; chokidar-based
209
- code-graph watcher with debounce / awaitWriteFinish / batch thresholds,
210
- opt-in via `--with-graph-watcher`.
259
+ then-experimental incremental graph watcher with debounce and batch thresholds.
211
260
  - **Phase 5 — execution resilience** (D43): `recovery.cjs` boot-time orphan
212
261
  scan using live PID probe; `circuit_breaker` table with `recordTaskFailure`
213
262
  counter and `resetCircuitBreaker` escape hatch; spec-change staleness
@@ -257,7 +306,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
257
306
  skill manifest, CLI protocol + mapping table; 5 spec validators.
258
307
  - **Phase 0 — skeleton**: multi-runtime installer scaffolding.
259
308
 
260
- [Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.3...HEAD
309
+ [Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.7.0...HEAD
310
+ [0.7.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.6.0...v0.7.0
311
+ [0.6.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.3...v0.6.0
261
312
  [0.5.3]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.2...v0.5.3
262
313
  [0.5.2]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.1...v0.5.2
263
314
  [0.5.1]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.0...v0.5.1
package/README.md CHANGED
@@ -8,7 +8,7 @@ authoritative `.ultra/state.db`.
8
8
 
9
9
  <div align="center">
10
10
 
11
- [![Version](https://img.shields.io/badge/version-0.6.0-blue)](./CHANGELOG.md)
11
+ [![Version](https://img.shields.io/badge/version-0.7.0-blue)](./CHANGELOG.md)
12
12
  [![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](#verification)
13
13
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
14
14
  [![Node](https://img.shields.io/badge/node-%E2%89%A522-informational)](./package.json)
@@ -25,16 +25,21 @@ authoritative `.ultra/state.db`.
25
25
  Codex receive their own command/skill, agent, hook, and MCP representation.
26
26
  Install and uninstall are symmetric on every supported host.
27
27
  - **Shares state across runtimes.** `.ultra/state.db` (SQLite + WAL) is the
28
- authoritative source for tasks, sessions, events, and telemetry. `tasks.json`
29
- and context markdown are generated projections, not handwritten.
28
+ authoritative source for changes, tasks, sessions, events, incidents,
29
+ projection jobs, and telemetry. `tasks.json` and context markdown are
30
+ generated projections, not handwritten.
30
31
  - **Keeps memory ownership explicit.** Ultra Builder Pro does not collect
31
32
  prompts, transcripts, observations, summaries, or cross-session memory.
32
33
  Install cloud-mem/claude-mem separately if persistent memory is wanted.
33
34
  - **Runs real PRDs end-to-end.** `task.parse_prd` → `lib/topo.cjs` waves →
34
35
  `.ultra/execution-plan.json` → parallel worktree sessions → auto-merge back.
35
- - **Observes without overhead.** Per-task / per-session / per-runtime token
36
- and cost telemetry; live code-review-graph watcher on file save;
37
- subscribe-events cursor for real-time dashboards.
36
+ - **Keeps daily work convergent.** `/ultra-change` creates a bounded delta and
37
+ context manifest; `/ultra-deliver` requires docs/test/review evidence and
38
+ baseline reconciliation before archive. `/ultra-doctor` reports incidents,
39
+ projection lag, orphan sessions, and backup-first mechanical recovery.
40
+ - **Keeps external context external.** Memory and code-graph providers retain
41
+ their own content; Ultra stores only provider/project/revision/status metadata
42
+ references in a compiled change context.
38
43
 
39
44
  ## Quickstart
40
45
 
@@ -57,7 +62,7 @@ npx ultra-builder-pro-cli --all --local --uninstall
57
62
  After install, start a new host session/task and point it at the project.
58
63
  Claude Code and OpenCode expose native command forms. Codex exposes the same workflows as namespaced plugin
59
64
  skills such as `$ultra-builder-pro:ultra-init`, `$ultra-builder-pro:ultra-plan`,
60
- and `$ultra-builder-pro:ultra-dev`; `command-map.json` records the nine legacy
65
+ and `$ultra-builder-pro:ultra-dev`; `command-map.json` records the eleven legacy
61
66
  command mappings (`ultra-review` remains a directly invocable skill).
62
67
  See [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.md)
63
68
  for per-runtime capabilities.
@@ -68,16 +73,17 @@ for per-runtime capabilities.
68
73
  |-------|---------|----------------|
69
74
  | **skill** (`skills/ultra-*/`) | Knowledge — prompts, workflows, prose | Runtime's native skill/prompt loader picks them up after install |
70
75
  | **MCP** (`mcp-server/`) | Authoritative state — reads/writes `.ultra/state.db` via stdio JSON-RPC | Primary path for task / session / event / plan operations |
71
- | **CLI** (`ultra-tools`, `bin/*`) | Shell fallback for CI and non-MCP contexts | `ultra-tools task init-project`, `ubp-orchestrator run`, `ultra-tools status --cost` |
76
+ | **CLI** (`ultra-tools`, `bin/*`) | Selected initialization, recovery, diagnostics, and orchestration surfaces | `ultra-tools task init-project`, `ultra-tools system doctor`, `ubp-orchestrator run` |
72
77
 
73
- The three layers share one `.ultra/state.db`. See
78
+ The layers share one `.ultra/state.db`, but the CLI is not a mirror of every MCP
79
+ tool: continuous change mutations are MCP-only and fail closed. See
74
80
  [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) for the full contract and
75
- [`spec/cli-protocol.md`](./spec/cli-protocol.md) for the 21 live contracts.
81
+ [`spec/cli-protocol.md`](./spec/cli-protocol.md) for the 29 live contracts.
76
82
  Review, impact discovery, skill resolution, and user interaction stay on each
77
83
  Host's native surfaces; the generated Codex capability map documents those
78
84
  replacements without advertising non-existent MCP tools.
79
85
 
80
- The package boundary is deliberate: ten public Ultra workflow skills, four
86
+ The package boundary is deliberate: twelve public Ultra workflow skills, four
81
87
  internal review-rule skills, and host-specific collaboration companions. Browser,
82
88
  deployment, skill-discovery, and framework guidance belong to their original plugins.
83
89
 
@@ -99,30 +105,33 @@ Full details in [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.m
99
105
  ## Typical workflow
100
106
 
101
107
  ```bash
102
- # 1. Initialize a project (writes .ultra/ skeleton; state.db stays lazy)
108
+ # 1. Initialize a project (writes .ultra/ skeleton and initializes state.db)
103
109
  ultra-tools task init-project --name myapp
104
110
 
105
111
  # 2. Turn a PRD into a task graph + execution plan (human-gate via dry-run)
106
112
  # (invoked by /ultra-plan skill or via MCP task.parse_prd + plan.export)
107
113
 
108
114
  # 3. Run the plan — parallel sessions, auto-merge back to main on success
109
- ubp-orchestrator run --with-graph-watcher
115
+ ubp-orchestrator run
110
116
 
111
- # 4. Monitor cost and progress
117
+ # 4. Monitor cost, progress, and runtime health
112
118
  ultra-tools status
113
119
  ultra-tools status --cost --since 24h
114
120
  ultra-tools session list --json
121
+ ultra-tools system doctor
115
122
  ```
116
123
 
117
124
  Or let the skills drive it. In Codex, invoke
118
125
  `$ultra-builder-pro:ultra-plan` → `$ultra-builder-pro:ultra-dev` →
119
- `$ultra-builder-pro:ultra-status`; other runtimes retain their native command form.
126
+ `$ultra-builder-pro:ultra-status`; after the baseline is delivered, daily work
127
+ starts with `$ultra-builder-pro:ultra-change`. Other runtimes retain their
128
+ native command form.
120
129
 
121
130
  ## CLI surface
122
131
 
123
132
  | Binary | Purpose |
124
133
  |--------|---------|
125
- | `ultra-builder-pro-cli` / `ubp` | Installer — `--claude / --opencode / --codex / --all`, `--local / --global`, `--uninstall`, `--skip-rtk` |
134
+ | `ultra-builder-pro-cli` / `ubp` | Installer — `--claude / --opencode / --codex / --all`, `--local / --global`, `--uninstall` |
126
135
  | `ubp-orchestrator` | Session dispatch daemon — `run`, `start`, `stop`, `status` |
127
136
  | `ultra-tools` | State-layer CLI — `task`, `session`, `status`, `db`, `migrate`; explicit `legacy-memory` archive/prune migration |
128
137
  | `ubp-handbook` | Preview/apply the managed Ultra contract in `CLAUDE.md` / `AGENTS.md`, with backup |
@@ -169,7 +178,7 @@ Individual suites: `test:state`, `test:orch`, `test:spec`, `test:rest`.
169
178
 
170
179
  | Doc | What's in it |
171
180
  |-----|--------------|
172
- | [`docs/PLAN.zh-CN.md`](./docs/PLAN.zh-CN.md) | Authoritative 12-phase execution plan (zh-CN) — decisions, risks, timeline |
181
+ | [`docs/PLAN.zh-CN.md`](./docs/PLAN.zh-CN.md) | Historical 12-phase plan and decision log with a current-boundary overlay |
173
182
  | [`docs/ROADMAP.md`](./docs/ROADMAP.md) | One-page English roadmap + phase status |
174
183
  | [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) | Single-page system architecture |
175
184
  | [`docs/AGENT-CONTEXT.md`](./docs/AGENT-CONTEXT.md) | Canonical runtime context contract |
@@ -177,7 +186,6 @@ Individual suites: `test:state`, `test:orch`, `test:spec`, `test:rest`.
177
186
  | [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.md) | Per-runtime capability matrix |
178
187
  | [`docs/STATE-DB-ACCESS-POLICY.md`](./docs/STATE-DB-ACCESS-POLICY.md) | Multi-process write contract |
179
188
  | [`docs/COMMIT-HASH-BACKFILL.md`](./docs/COMMIT-HASH-BACKFILL.md) | Two-commit task-completion flow |
180
- | [`docs/LEGACY-HERMES.md`](./docs/LEGACY-HERMES.md) | Archived pre-CLI "Hermes 6.6" documentation |
181
189
  | [`CHANGELOG.md`](./CHANGELOG.md) | v0.1 → current release notes |
182
190
 
183
191
  ## License
@@ -14,21 +14,21 @@ const HOSTS = Object.freeze({
14
14
  claude: Object.freeze({
15
15
  name: 'Claude Code',
16
16
  handbook: ['.claude', 'CLAUDE.md'],
17
- invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status` and `/ultra-think`',
17
+ invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-change`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status`, `/ultra-think`, and `/ultra-doctor`',
18
18
  collaboration: '`/codex-collab` is an explicitly requested read-only advisor',
19
19
  coordination: 'Claude Code task UI is host coordination; it does not replace Ultra task state',
20
20
  }),
21
21
  codex: Object.freeze({
22
22
  name: 'Codex',
23
23
  handbook: ['.codex', 'AGENTS.md'],
24
- invocation: '`$ultra-builder-pro:ultra-init`, `$ultra-builder-pro:ultra-research`, `$ultra-builder-pro:ultra-plan`, `$ultra-builder-pro:ultra-dev`, `$ultra-builder-pro:ultra-test`, `$ultra-builder-pro:ultra-review`, and `$ultra-builder-pro:ultra-deliver`; diagnostics use `$ultra-builder-pro:ultra-status` and `$ultra-builder-pro:ultra-think`',
24
+ invocation: '`$ultra-builder-pro:ultra-init`, `$ultra-builder-pro:ultra-research`, `$ultra-builder-pro:ultra-plan`, `$ultra-builder-pro:ultra-change`, `$ultra-builder-pro:ultra-dev`, `$ultra-builder-pro:ultra-test`, `$ultra-builder-pro:ultra-review`, and `$ultra-builder-pro:ultra-deliver`; diagnostics use `$ultra-builder-pro:ultra-status`, `$ultra-builder-pro:ultra-think`, and `$ultra-builder-pro:ultra-doctor`',
25
25
  collaboration: '`$ultra-builder-pro:cc-collab` is an explicitly requested read-only advisor',
26
26
  coordination: 'The Codex plan is turn-facing coordination; it does not replace Ultra task state',
27
27
  }),
28
28
  opencode: Object.freeze({
29
29
  name: 'OpenCode',
30
30
  handbook: ['.config', 'opencode', 'AGENTS.md'],
31
- invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status` and `/ultra-think`',
31
+ invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-change`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status`, `/ultra-think`, and `/ultra-doctor`',
32
32
  collaboration: '`/cc-collab` and `/codex-collab` are explicitly requested read-only advisors',
33
33
  coordination: 'OpenCode session coordination does not replace Ultra task state',
34
34
  }),
@@ -52,11 +52,11 @@ function renderHandbook(runtime) {
52
52
  `- Workflow entry points: ${value.invocation}.`,
53
53
  `- Collaboration boundary: ${value.collaboration}. The current host remains primary and owns final verification.`,
54
54
  `- Coordination boundary: ${value.coordination}.`,
55
- '- Authority: `.ultra/state.db` is the only durable Ultra authority for tasks, sessions, events, telemetry, and review evidence. Generated JSON and Markdown are projections or workflow artifacts.',
56
- '- Memory boundary: Ultra Builder Pro does not capture prompts, transcripts, observations, summaries, or cross-session memory. Persistent memory belongs to a separately installed provider such as cloud-mem or claude-mem.',
57
- '- Hook boundary: Ultra hooks are workflow-only lifecycle adapters and are no-ops unless `.ultra/workflow-state.json` describes an active non-terminal workflow. Generic command blocking and post-edit policy stay in user or repository governance.',
55
+ '- Authority: `.ultra/state.db` is the only durable Ultra authority for changes, tasks, sessions, events, incidents, projection state, telemetry, and review evidence. Generated JSON and Markdown are projections or workflow artifacts.',
56
+ '- Memory and graph boundary: Ultra Builder Pro does not capture prompts, transcripts, observations, summaries, cross-session memory, or code-graph content. Separately installed providers own that data; Ultra may store only their metadata references in a change context manifest.',
57
+ '- Hook boundary: Ultra hooks observe workflow/change lifecycle only. Health/context may run when `.ultra/state.db` exists; projection protection is always authoritative, while edit/compact/stop/subagent enforcement remains active-workflow scoped. Generic command blocking and post-edit policy stay in user or repository governance.',
58
58
  '- Agent boundary: the bundled review and debugging agents are bounded workers. They use the current checkout and parent-supplied context, do not own private persistent state, and never replace the primary agent.',
59
- '- Package boundary: only the ten Ultra workflows, four internal review-rule skills, and host-specific collaboration companions belong to this plugin. General browser, deployment, discovery, and framework skills must be installed from their owners.',
59
+ '- Package boundary: only the twelve Ultra workflows, four internal review-rule skills, and host-specific collaboration companions belong to this plugin. General browser, deployment, discovery, and framework skills must be installed from their owners.',
60
60
  END_MARKER,
61
61
  ].join('\n');
62
62
  }
@@ -18,6 +18,8 @@ const CORE_PUBLIC_SKILLS = Object.freeze([
18
18
  'ultra-deliver',
19
19
  'ultra-status',
20
20
  'ultra-think',
21
+ 'ultra-change',
22
+ 'ultra-doctor',
21
23
  ]);
22
24
 
23
25
  const INTERNAL_AGENT_SKILLS = Object.freeze([
@@ -25,7 +25,10 @@ test('renders one common contract with host-native invocation syntax', () => {
25
25
  assert.match(opencode, /\/ultra-plan/);
26
26
  for (const rendered of [claude, codex, opencode]) {
27
27
  assert.match(rendered, /\.ultra\/state\.db/);
28
- assert.match(rendered, /claude-mem|cloud-mem/);
28
+ assert.match(rendered, /Separately installed providers/);
29
+ assert.match(rendered, /ultra-change/);
30
+ assert.match(rendered, /ultra-doctor/);
31
+ assert.match(rendered, /metadata references/);
29
32
  assert.doesNotMatch(rendered, /\.ultra\/memory|memory\.retain|memory\.recall/);
30
33
  assert.match(rendered, new RegExp(BEGIN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
31
34
  assert.match(rendered, new RegExp(END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
@@ -27,6 +27,8 @@ const CORE = [
27
27
  'ultra-deliver',
28
28
  'ultra-status',
29
29
  'ultra-think',
30
+ 'ultra-change',
31
+ 'ultra-doctor',
30
32
  ];
31
33
 
32
34
  const INTERNAL = [
@@ -337,20 +337,55 @@ import path from "node:path";
337
337
 
338
338
  const TERMINAL = new Set(["committed", "completed", "done", "cancelled"]);
339
339
  const TASKS_PROJECTION = ".ultra/tasks/tasks.json";
340
+ const PROVIDER_FIELDS = new Set(["provider", "project", "reference", "revision", "indexed_head", "status"]);
341
+
342
+ function providerMetadata(value) {
343
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
344
+ const clean = {};
345
+ for (const [kind, ref] of Object.entries(value)) {
346
+ if (!["memory", "code_graph"].includes(kind) || !ref || typeof ref !== "object" || Array.isArray(ref)) continue;
347
+ clean[kind] = Object.fromEntries(Object.entries(ref).filter(([key]) => PROVIDER_FIELDS.has(key)));
348
+ }
349
+ return clean;
350
+ }
340
351
 
341
- function findActiveWorkflow(directory) {
352
+ function findUltraContext(directory) {
342
353
  let current = path.resolve(directory);
343
354
  while (true) {
344
- const file = path.join(current, ".ultra", "workflow-state.json");
345
- if (fs.existsSync(file)) {
355
+ const ultra = path.join(current, ".ultra");
356
+ const workflowFile = path.join(ultra, "workflow-state.json");
357
+ const statePresent = fs.existsSync(path.join(ultra, "state.db"));
358
+ let workflow = null;
359
+ if (fs.existsSync(workflowFile)) {
346
360
  try {
347
- const state = JSON.parse(fs.readFileSync(file, "utf8"));
348
- return state && !TERMINAL.has(state.status) ? { root: current, state } : null;
361
+ const state = JSON.parse(fs.readFileSync(workflowFile, "utf8"));
362
+ workflow = state && !TERMINAL.has(state.status) ? state : null;
349
363
  } catch (error) {
350
- process.stderr.write(\`[ultra-builder-pro] cannot read \${file}: \${error.message}\\n\`);
351
- return null;
364
+ process.stderr.write(\`[ultra-builder-pro] cannot read \${workflowFile}: \${error.message}\\n\`);
365
+ }
366
+ }
367
+ const changes = [];
368
+ const changeRoot = path.join(ultra, "changes", "active");
369
+ if (fs.existsSync(changeRoot)) {
370
+ for (const entry of fs.readdirSync(changeRoot, { withFileTypes: true })) {
371
+ if (!entry.isDirectory()) continue;
372
+ const manifestPath = path.join(changeRoot, entry.name, "context-manifest.json");
373
+ if (!fs.existsSync(manifestPath)) continue;
374
+ try {
375
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
376
+ if (manifest?.change) changes.push({
377
+ ...manifest.change,
378
+ providers: providerMetadata(manifest.providers),
379
+ manifestPath: path.relative(current, manifestPath),
380
+ });
381
+ } catch (error) {
382
+ process.stderr.write(\`[ultra-builder-pro] cannot read \${manifestPath}: \${error.message}\\n\`);
383
+ }
352
384
  }
353
385
  }
386
+ if (statePresent || workflow || changes.length > 0) {
387
+ return { root: current, workflow, changes, statePresent };
388
+ }
354
389
  const parent = path.dirname(current);
355
390
  if (parent === current) return null;
356
391
  current = parent;
@@ -358,16 +393,42 @@ function findActiveWorkflow(directory) {
358
393
  }
359
394
 
360
395
  function contextText(active) {
361
- const state = active.state;
362
- return [
363
- "[Ultra active workflow]",
364
- \`Project: \${active.root}\`,
365
- \`Command: \${state.command ?? "unknown"}\`,
366
- \`Task: \${state.task_id ?? state.task ?? "unknown"}\`,
367
- \`Step: \${state.step ?? "unknown"}\`,
368
- \`Status: \${state.status ?? "active"}\`,
369
- "Authority: .ultra/state.db; recovery: .ultra/workflow-state.json.",
370
- ].join("\\n");
396
+ const lines = [];
397
+ if (active.workflow) {
398
+ const state = active.workflow;
399
+ lines.push(
400
+ "[Ultra active workflow]",
401
+ \`Project: \${active.root}\`,
402
+ \`Command: \${state.command ?? "unknown"}\`,
403
+ \`Task: \${state.task_id ?? state.task ?? "unknown"}\`,
404
+ \`Step: \${state.step ?? "unknown"}\`,
405
+ \`Status: \${state.status ?? "active"}\`,
406
+ );
407
+ }
408
+ if (active.changes.length > 0) {
409
+ const change = active.changes[0];
410
+ lines.push(
411
+ "[Ultra continuous change]",
412
+ \`Project: \${active.root}\`,
413
+ \`Change: \${change.id} (\${change.kind ?? "unknown"}, \${change.status ?? "active"})\`,
414
+ \`Title: \${change.title ?? "unknown"}\`,
415
+ \`Intent: \${change.intent ?? "unknown"}\`,
416
+ \`Context manifest: \${change.manifestPath}\`,
417
+ \`External providers (metadata references only): \${JSON.stringify(change.providers)}\`,
418
+ );
419
+ if (active.changes.length > 1) lines.push(
420
+ \`Active change count: \${active.changes.length}; resolve scope with change.list before mutation.\`,
421
+ );
422
+ }
423
+ if (!active.workflow && active.changes.length === 0) {
424
+ lines.push(
425
+ "[Ultra baseline]",
426
+ \`Project: \${active.root}\`,
427
+ "No active continuous change. Start daily work with the ultra-change workflow.",
428
+ );
429
+ }
430
+ lines.push("Authority: .ultra/state.db; generated JSON/Markdown are projections or artifacts.");
431
+ return lines.join("\\n");
371
432
  }
372
433
 
373
434
  function targetPaths(tool, args) {
@@ -391,8 +452,8 @@ function isTasksProjection(active, candidate) {
391
452
 
392
453
  export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
393
454
  const root = worktree || directory;
394
- let active = findActiveWorkflow(root);
395
- const refresh = () => { active = findActiveWorkflow(root); };
455
+ let active = findUltraContext(root);
456
+ const refresh = () => { active = findUltraContext(root); };
396
457
  return {
397
458
  event: async ({ event }) => {
398
459
  if (["session.created", "session.compacted", "session.idle"].includes(event?.type)) refresh();
@@ -413,7 +474,7 @@ export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
413
474
  if (targetPaths(tool, output?.args).some((candidate) => isTasksProjection(active, candidate))) {
414
475
  throw new Error(
415
476
  "Ultra Builder Pro refused a direct write to .ultra/tasks/tasks.json. " +
416
- ".ultra/state.db is authoritative; use MCP task tools or run the required v4.4 to v4.5 migration."
477
+ ".ultra/state.db is authoritative; use MCP task tools and run ultra-doctor when state or projection health is degraded."
417
478
  );
418
479
  }
419
480
  },
@@ -21,8 +21,10 @@ const {
21
21
  const REPO_ROOT = path.resolve(__dirname, '..', '..');
22
22
  const COMMANDS = [
23
23
  'learn',
24
+ 'ultra-change',
24
25
  'ultra-deliver',
25
26
  'ultra-dev',
27
+ 'ultra-doctor',
26
28
  'ultra-init',
27
29
  'ultra-plan',
28
30
  'ultra-research',
@@ -89,7 +91,7 @@ test('install builds one Codex-native plugin with complete skill and command cov
89
91
 
90
92
  const expectedSkills = skillsForRuntime('codex').sort();
91
93
  assert.deepEqual(skillNames(layout.pluginRoot), expectedSkills);
92
- assert.equal(expectedSkills.length, 16);
94
+ assert.equal(expectedSkills.length, 18);
93
95
  assert.ok(!fs.existsSync(path.join(layout.pluginRoot, 'skills', 'codex-collab')));
94
96
  assert.ok(!fs.existsSync(path.join(layout.pluginRoot, 'skills', 'learned')));
95
97
 
@@ -229,8 +231,8 @@ test('plugin declares current Codex hooks and a project-local Ultra MCP server',
229
231
  const liveSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'mcp-tools.yaml'), 'utf8'));
230
232
  const upstreamSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'upstream-mcp-tools.yaml'), 'utf8'));
231
233
  const capabilityMap = JSON.parse(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'codex-capability-map.json'), 'utf8'));
232
- assert.equal(liveSpec.tools.length, 21);
233
- assert.equal(upstreamSpec.tools.length, 21);
234
+ assert.equal(liveSpec.tools.length, 29);
235
+ assert.equal(upstreamSpec.tools.length, 29);
234
236
  assert.deepEqual(upstreamSpec.tools.map((tool) => tool.name).sort(), liveSpec.tools.map((tool) => tool.name).sort());
235
237
  assert.deepEqual(capabilityMap.live_mcp_tools.sort(), liveSpec.tools.map((tool) => tool.name).sort());
236
238
  assert.equal(Object.keys(capabilityMap.codex_native_replacements).length, 9);
@@ -171,13 +171,14 @@ test('install writes a schema-safe opencode.json and keeps ownership outside hos
171
171
  assert.match(plugin, /\.ultra[\\/]tasks[\\/]tasks\.json/);
172
172
  assert.match(plugin, /throw new Error/);
173
173
  assert.match(plugin, /session\.compacted/);
174
- assert.doesNotMatch(plugin, /memory|recall|journal|observation/);
174
+ assert.doesNotMatch(plugin, /memory\.(?:retain|recall|reflect)|journal|observation|prompt[_ -]?capture/);
175
+ assert.match(plugin, /metadata references only/);
175
176
  } finally {
176
177
  fs.rmSync(target, { recursive: true, force: true });
177
178
  }
178
179
  });
179
180
 
180
- test('OpenCode before-tool hook blocks tasks.json writes only during an active workflow', async () => {
181
+ test('OpenCode context covers active changes and projection writes are blocked for Ultra projects', async () => {
181
182
  const target = mkTarget();
182
183
  const project = mkTarget();
183
184
  try {
@@ -186,15 +187,21 @@ test('OpenCode before-tool hook blocks tasks.json writes only during an active w
186
187
  const module = await import(pathToFileURL(pluginFile).href);
187
188
  const plugin = await module.UltraBuilderProPlugin({ directory: project });
188
189
 
189
- await plugin['tool.execute.before'](
190
- { tool: 'edit' },
191
- { args: { filePath: '.ultra/tasks/tasks.json' } },
192
- );
193
-
194
190
  fs.mkdirSync(path.join(project, '.ultra'), { recursive: true });
195
- fs.writeFileSync(path.join(project, '.ultra', 'workflow-state.json'), JSON.stringify({
196
- command: 'ultra-plan', task_id: 'plan', step: 'persist', status: 'active',
191
+ fs.writeFileSync(path.join(project, '.ultra', 'state.db'), 'owned-by-ultra');
192
+ const changeRoot = path.join(project, '.ultra', 'changes', 'active', 'daily-fix');
193
+ fs.mkdirSync(changeRoot, { recursive: true });
194
+ fs.writeFileSync(path.join(changeRoot, 'context-manifest.json'), JSON.stringify({
195
+ change: { id: 'daily-fix', title: 'Daily fix', kind: 'quick', status: 'active', intent: 'Fix drift' },
196
+ providers: { memory: { provider: 'cloud-mem', status: 'available' } },
197
197
  }));
198
+
199
+ const output = { system: [] };
200
+ await plugin['experimental.chat.system.transform']({}, output);
201
+ assert.match(output.system.join('\n'), /Ultra continuous change/);
202
+ assert.match(output.system.join('\n'), /daily-fix/);
203
+ assert.match(output.system.join('\n'), /metadata references only/);
204
+
198
205
  await assert.rejects(
199
206
  plugin['tool.execute.before'](
200
207
  { tool: 'apply_patch' },
package/bin/install.js CHANGED
@@ -17,7 +17,6 @@
17
17
  * -l, --local install into current working directory
18
18
  * -u, --uninstall remove installed assets
19
19
  * -c, --config-dir <path> override config directory
20
- * --skip-rtk do not detect / install RTK hook
21
20
  * -h, --help show help
22
21
  */
23
22
 
@@ -79,7 +78,6 @@ function printHelp() {
79
78
  ${paint('yellow', 'Other:')}
80
79
  ${paint('cyan', '-u, --uninstall')} remove installed assets
81
80
  ${paint('cyan', '-c, --config-dir')} override runtime's config directory (string path)
82
- ${paint('cyan', '--skip-rtk')} skip RTK hook registration (RTK is a soft dependency)
83
81
  ${paint('cyan', '-h, --help')} show this help
84
82
  ${paint('cyan', '-v, --version')} show CLI version
85
83
 
@@ -105,7 +103,6 @@ function parseArgs(argv) {
105
103
  help: false,
106
104
  version: false,
107
105
  configDir: null,
108
- skipRtk: false,
109
106
  };
110
107
 
111
108
  for (let i = 0; i < args.length; i++) {
@@ -122,7 +119,6 @@ function parseArgs(argv) {
122
119
  case '-u': case '--uninstall': flags.uninstall = true; break;
123
120
  case '-h': case '--help': flags.help = true; break;
124
121
  case '-v': case '--version': flags.version = true; break;
125
- case '--skip-rtk': flags.skipRtk = true; break;
126
122
  case '-c': case '--config-dir':
127
123
  flags.configDir = args[++i];
128
124
  if (!flags.configDir || flags.configDir.startsWith('-')) {
@@ -226,21 +222,6 @@ async function main() {
226
222
  process.exit(1);
227
223
  }
228
224
 
229
- // Phase 6.1 — RTK hook (soft dependency; install continues on absence).
230
- if (mode === 'install') {
231
- const rtk = require('../adapters/_shared/rtk-detect.cjs');
232
- const result = rtk.installHook({ skip: flags.skipRtk, scope });
233
- if (result.skipped) {
234
- console.log(` ${paint('dim', '⤼ rtk skipped (--skip-rtk)')}`);
235
- } else if (result.available && result.initialized) {
236
- console.log(` ${paint('green', '✓')} rtk hook registered (${result.version})`);
237
- } else if (result.available && !result.initialized) {
238
- console.log(` ${paint('yellow', '!')} rtk present but init failed: ${result.init_error}`);
239
- } else {
240
- console.log(` ${paint('dim', '⤼ ' + result.hint.replace(/\n/g, '\n '))}`);
241
- }
242
- }
243
-
244
225
  console.log(` ${paint('green', 'Done.')}`);
245
226
  }
246
227
 
@@ -77,33 +77,9 @@ function cmdRun(opts = {}) {
77
77
  onError: (err) => process.stderr.write(`orchestrator error: ${err.message}\n`),
78
78
  });
79
79
 
80
- // Phase 6.4 — optional code-graph watcher (opt-in via settings or CLI).
81
- const watcherEnabled =
82
- opts.withGraphWatcher === true ||
83
- !!(settings && settings.orchestrator && settings.orchestrator.graph_watcher === true);
84
- let watcherHandle = null;
85
- if (watcherEnabled) {
86
- try {
87
- const { startWatcher } = require('../orchestrator/code-graph-watcher.cjs');
88
- const jsonlPath = path.join(REPO_ROOT, '.ultra', 'code-graph-events.jsonl');
89
- fs.mkdirSync(path.dirname(jsonlPath), { recursive: true });
90
- watcherHandle = startWatcher({
91
- repoRoot: REPO_ROOT,
92
- onBatch: (batch) => {
93
- try { fs.appendFileSync(jsonlPath, JSON.stringify(batch) + '\n'); }
94
- catch (err) { process.stderr.write(`graph-watcher write error: ${err.message}\n`); }
95
- },
96
- });
97
- process.stderr.write(`orchestrator: graph-watcher enabled → ${jsonlPath}\n`);
98
- } catch (err) {
99
- process.stderr.write(`orchestrator: graph-watcher disabled (${err.message})\n`);
100
- }
101
- }
102
-
103
80
  const shutdown = (signal) => {
104
81
  process.stderr.write(`orchestrator received ${signal}, stopping\n`);
105
82
  handle.stop();
106
- if (watcherHandle) watcherHandle.stop();
107
83
  try { db.close(); } catch (_) { /* ignore */ }
108
84
  process.exit(0);
109
85
  };
@@ -205,9 +181,8 @@ function usage() {
205
181
 
206
182
  const argv = process.argv.slice(2);
207
183
  const subcommand = argv[0];
208
- const runOpts = { withGraphWatcher: argv.includes('--with-graph-watcher') };
209
184
  switch (subcommand) {
210
- case 'run': cmdRun(runOpts); break;
185
+ case 'run': cmdRun(); break;
211
186
  case 'start': cmdStart(); break;
212
187
  case 'stop': cmdStop(); break;
213
188
  case 'status': cmdStatus(); break;