ultra-builder-pro-cli 0.16.0 → 0.17.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 (64) hide show
  1. package/.ultra-template/specs/research-distillate.md +4 -3
  2. package/CHANGELOG.md +86 -0
  3. package/README.md +54 -24
  4. package/adapters/_shared/codex-assets.cjs +10 -5
  5. package/adapters/_shared/handbook.cjs +1 -1
  6. package/adapters/_shared/interaction-contract.cjs +61 -0
  7. package/adapters/claude.js +1 -1
  8. package/adapters/opencode.js +1 -1
  9. package/bin/orchestrator.js +175 -19
  10. package/commands/ultra-change.md +1 -1
  11. package/commands/ultra-deliver.md +3 -3
  12. package/commands/ultra-init.md +1 -1
  13. package/commands/ultra-plan.md +1 -1
  14. package/commands/ultra-research.md +2 -2
  15. package/commands/ultra-review.md +10 -0
  16. package/commands/ultra-status.md +1 -1
  17. package/commands/ultra-test.md +1 -1
  18. package/docs/AGENT-CONTEXT.md +11 -8
  19. package/docs/ARCHITECTURE.md +67 -9
  20. package/docs/LEGACY-CLI-CROSSWALK.md +163 -0
  21. package/docs/PLAN.zh-CN.md +19 -13
  22. package/docs/RUNTIME-COMPAT-MATRIX.md +2 -2
  23. package/docs/WORKFLOW-LIFECYCLE.md +123 -59
  24. package/hooks/pre_stop_check.py +3 -2
  25. package/mcp-server/lib/baseline-workflow.cjs +84 -16
  26. package/mcp-server/lib/change-workflow.cjs +55 -35
  27. package/mcp-server/lib/context-spine.cjs +171 -105
  28. package/mcp-server/lib/git-bootstrap.cjs +240 -0
  29. package/mcp-server/lib/init-project.cjs +178 -41
  30. package/mcp-server/lib/project-breadcrumb.cjs +14 -14
  31. package/mcp-server/lib/state-db.cjs +257 -10
  32. package/mcp-server/lib/state-ops.cjs +64 -1
  33. package/mcp-server/lib/workflow-state.cjs +300 -205
  34. package/mcp-server/server.cjs +58 -34
  35. package/orchestrator/auto-merge.cjs +19 -0
  36. package/orchestrator/daemon.cjs +104 -0
  37. package/orchestrator/parallel-orchestrator.cjs +371 -49
  38. package/orchestrator/session-runner.cjs +354 -25
  39. package/package.json +2 -1
  40. package/skills/ultra-change/SKILL.md +69 -94
  41. package/skills/ultra-deliver/SKILL.md +37 -65
  42. package/skills/ultra-dev/SKILL.md +50 -52
  43. package/skills/ultra-doctor/SKILL.md +34 -49
  44. package/skills/ultra-init/SKILL.md +78 -105
  45. package/skills/ultra-plan/SKILL.md +78 -99
  46. package/skills/ultra-research/SKILL.md +99 -113
  47. package/skills/ultra-review/SKILL.md +38 -53
  48. package/skills/ultra-review/references/review-modes.md +5 -3
  49. package/skills/ultra-review/references/unified-schema.md +26 -6
  50. package/skills/ultra-review/scripts/review_wait.py +12 -0
  51. package/skills/ultra-status/SKILL.md +34 -44
  52. package/skills/ultra-test/SKILL.md +61 -54
  53. package/skills/ultra-think/SKILL.md +37 -51
  54. package/skills/ultra-think/references/decision-dialogue.md +87 -111
  55. package/spec/cli-protocol.md +35 -0
  56. package/spec/mcp-tools.yaml +145 -28
  57. package/spec/schemas/delivery-report.v1.schema.json +1 -46
  58. package/spec/schemas/state-db.sql +8 -2
  59. package/spec/schemas/test-report.v1.schema.json +45 -14
  60. package/ultra-tools/cli.cjs +1 -1
  61. package/ultra-tools/commands/session.cjs +10 -3
  62. package/ultra-tools/commands/status.cjs +19 -14
  63. package/ultra-tools/commands/system.cjs +2 -2
  64. package/ultra-tools/commands/task.cjs +3 -0
@@ -8,7 +8,7 @@ specifications' prose.
8
8
  - Baseline or change id:
9
9
  - Research workflow id and mode:
10
10
  - Repository revision:
11
- - Completed step set:
11
+ - Coverage dispositions and selected evidence work:
12
12
 
13
13
  ## Observed
14
14
 
@@ -24,7 +24,7 @@ specifications' prose.
24
24
 
25
25
  ## Unknowns
26
26
 
27
- | Unknown or gap id | Blocking | Owner or next action |
27
+ | Unknown or gap id | Blocking | Owner and revisit condition |
28
28
  |---|---|---|
29
29
  | | | |
30
30
 
@@ -45,4 +45,5 @@ specifications' prose.
45
45
  - Accepted scope and posture:
46
46
  - Required references:
47
47
  - Blocking gaps:
48
- - Exact next route:
48
+ - Allowed transitions:
49
+ - Host recommendation and rationale:
package/CHANGELOG.md CHANGED
@@ -7,6 +7,92 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.17.0] — 2026-07-24
11
+
12
+ ### Added
13
+
14
+ - Added deterministic Git bootstrap for initialization and resume, including
15
+ explicit `auto`, `initialize`, and `skip` modes, persisted unborn-HEAD state,
16
+ and an owner-authorized first-checkpoint gate.
17
+ - Added a source-backed compatibility crosswalk against the original
18
+ `rocky2431/ultra-builder-pro` workflow and a shell-free `execute-plan`
19
+ consumer for current completed-plan dependency waves.
20
+ - Added schema 18 adaptive transition state, migration of active legacy
21
+ init/change/plan/deliver runs, Context Manifest v3 Change-authority digests,
22
+ and one host-specific interaction contract for every supported runtime.
23
+ - Added research coverage dispositions (`execute`, `verify_existing`, `reuse`,
24
+ `not_applicable`, and accepted `deferred`) and risk-selected verification
25
+ profiles with rationale for every excluded dimension.
26
+
27
+ ### Changed
28
+
29
+ - Changed plan execution into a resumable wave state machine. A wave pauses
30
+ until its DB tasks converge, later dependencies remain pending, and reruns
31
+ skip completed work.
32
+ - Changed MCP session spawning to create the single authoritative Git worktree
33
+ and session record. Session close now preserves work by default, and
34
+ change-owned auto-merge requires current dev and review evidence.
35
+ - Changed session worktrees to bind their ignored `.ultra` entry to the central
36
+ authority and pass non-overridable DB, checkout, and authority-root paths to
37
+ explicit workers.
38
+ - Changed initialization to complete after classification, local Git/scaffold
39
+ setup, and read-back verification. It never starts research implicitly;
40
+ baseline research begins only through an explicit invocation.
41
+ - Changed the workflow from a canonical next-action pipeline to an adaptive
42
+ capability graph. MCP exposes valid alternatives and unique hard-recovery
43
+ transitions; the active host owns semantic recommendations and native user
44
+ interaction.
45
+ - Changed change capture to end after the accepted intent contract, made plan
46
+ approval conditional on a material owner decision, selected test/review work
47
+ by risk, and limited Ultra delivery to local convergence and archive.
48
+
49
+ ### Fixed
50
+
51
+ - Fixed process exit zero, worker spawn errors, and daemon failures bypassing
52
+ task gates or losing session-linked failure and circuit-breaker evidence.
53
+ - Fixed daemon dispatch of stale tasks, unresolved dependencies, or overlapping
54
+ declared files, and made explicit takeover terminate the prior worker before
55
+ replacing its lease.
56
+ - Fixed initialization resume leaving corrected metadata or projections behind
57
+ after a late failure; DB state, generated projections, and newly bootstrapped
58
+ Git now roll back together.
59
+ - Fixed cleanup paths that could remove uncommitted or unintegrated worktrees,
60
+ and fixed dependency waves that could start before prior task convergence.
61
+ - Fixed change-owned auto-merge trusting task status alone; integration now
62
+ requires the exact completion commit, ready dev evidence, and current task review.
63
+ - Fixed `execute-plan` accepting empty, cyclic, duplicate-task, stale, or
64
+ incomplete change plans. Change-owned execution now requires the exact healthy
65
+ completed plan workflow and current DB task graph.
66
+ - Fixed direct MCP and daemon session creation bypassing the plan workflow.
67
+ Admission and spawn now share the same current task-contract, dependency, and
68
+ staleness gate before takeover or worktree mutation.
69
+ - Fixed plan-authority drift between wave selection and spawn being counted as a
70
+ worker failure, and fixed isolated workers creating a second project DB.
71
+ - Fixed an isolated worker recursively spawning, taking over, or prematurely
72
+ closing its parent-owned session; the supervising process now remains the only
73
+ transport-settlement authority.
74
+ - Fixed late resume failure after a schema upgrade restoring the migrated
75
+ intermediate DB instead of the exact pre-migration backup.
76
+ - Fixed the directory-only `.ultra/` Git ignore form failing to protect an
77
+ authority symlink from `git add -A`; fresh initialization uses the symlink-safe
78
+ `.ultra` rule, while legacy repositories receive a repository-local
79
+ `info/exclude` rule without changing their tracked baseline.
80
+ - Fixed semantic `change.update` operations leaving derived tasks and compiled
81
+ contexts apparently current. A changed intent contract now marks tasks stale,
82
+ invalidates Context v3, and records the exact invalidation evidence.
83
+ - Fixed stale tasks remaining usable through plan completion, dev startup,
84
+ verification, delivery, or orchestrator admission after semantic invalidation.
85
+ - Fixed marker-only task reconciliation bypassing semantic invalidation. Clearing
86
+ `stale` now requires a complete execution-contract rebind and records the current
87
+ Change authority digest.
88
+ - Fixed review convergence accepting incomplete or fabricated worker provenance;
89
+ every specialist is now selected or skipped with rationale, and completed workers
90
+ must exactly match persisted specialist artifacts.
91
+ - Fixed MCP workflow contracts omitting public freshness, recovery, review, testing,
92
+ and delivery failure codes needed by host adapters.
93
+ - Fixed runtime specification templates and workflow prompts retaining rigid-route
94
+ wording or omitting durable step and decision-tool handoffs.
95
+
10
96
  ## [0.16.0] — 2026-07-23
11
97
 
12
98
  ### Added
package/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  Multi-runtime plugin suite + autonomous coding factory for the Ultra Builder Pro
4
4
  agent engineering system. It ships native plugins for **Claude Code · OpenCode ·
5
5
  Codex · Kimi Code** and orchestrates PRD →
6
- dependency graph → parallel session execution → auto-merge with a single
7
- authoritative `.ultra/state.db`.
6
+ dependency graph → resumable dependency-wave execution → convergence-gated
7
+ integration with a single authoritative `.ultra/state.db`.
8
8
 
9
9
  <div align="center">
10
10
 
@@ -36,14 +36,20 @@ authoritative `.ultra/state.db`.
36
36
  - **Keeps memory ownership explicit.** Ultra Builder Pro does not collect
37
37
  prompts, transcripts, observations, summaries, or cross-session memory.
38
38
  Install cloud-mem/claude-mem separately if persistent memory is wanted.
39
- - **Runs real PRDs end-to-end without a second model credential.** The current
40
- host derives task objects; `task.parse_prd` validates them → `lib/topo.cjs` waves →
41
- `.ultra/execution-plan.json` → parallel worktree sessions → auto-merge back.
39
+ - **Runs current plans without a second model credential.** The current host
40
+ derives task objects; `task.parse_prd` validates them → `lib/topo.cjs` waves →
41
+ `.ultra/execution-plan.json` → parallel worktree sessions. Execution pauses at
42
+ the first wave whose tasks have not converged through Ultra gates. An explicit
43
+ auto-merge is accepted only for a change task with a matching completion commit,
44
+ ready dev evidence, and a current task review; transport success alone preserves
45
+ the worktree.
42
46
  - **Keeps daily work convergent.** `/ultra-change` creates a bounded delta and
43
47
  complete Change Contract, classifies risk, records whether bounded research is
44
- required, and creates immutable Context Manifest v2 snapshots; the Context Spine assigns a role/gate, verifies required
45
- references, reports fresh-context budget pressure as advisory warnings, exposes one DB-derived breadcrumb, and
46
- routes a single next action. Research stores typed, source-digested semantic records;
48
+ required, and leaves planning and context compilation to their actual consumers.
49
+ Context Manifest v3 assigns a role/gate, verifies required references, reports
50
+ fresh-context budget pressure as advisory warnings, and exposes mechanically valid
51
+ transitions without choosing the semantic route. Research stores typed,
52
+ source-digested semantic records;
47
53
  planning proves Change acceptance coverage; `/ultra-deliver` requires applied specification
48
54
  learning plus docs/test/mode-bound two-axis review evidence and a schema-validated
49
55
  baseline reconciliation manifest before archive. `/ultra-doctor` reports incidents,
@@ -99,9 +105,16 @@ based on delivered-system evidence: application source, tests, deployment, or
99
105
  persisted-state/schema signals make a repository brownfield; a Git repository,
100
106
  manifest, README, or starter documentation alone remains greenfield. An empty project
101
107
  opens a `draft` greenfield baseline and an existing system opens an `adopting`
102
- brownfield baseline. Both execute all seventeen research steps before
103
- `baseline.record` and explicit `baseline.converge`; initialization never treats blank
104
- templates as a trusted baseline and never selects an MVP. Existing active work may
108
+ brownfield baseline. Initialization completes after classification, local Git setup,
109
+ scaffold creation, and read-back verification; it never starts research implicitly.
110
+ When `ultra-research` is explicitly invoked, the host dispositions each of seventeen
111
+ coverage areas as `execute`, `verify_existing`, `reuse`, `not_applicable`, or accepted
112
+ `deferred`. It performs only the necessary work before `baseline.record` and explicit
113
+ `baseline.converge`; initialization never treats blank templates as a trusted baseline
114
+ and never selects an MVP. Auto mode preserves existing
115
+ Git or initializes `main` and adds a symlink-safe `.ultra` ignore rule; an unborn
116
+ repository requires an owner-approved local checkpoint commit before the baseline can
117
+ be recorded. It never creates a remote, tag, or push. Existing active work may
105
118
  continue only when its durable change workflow was bound to a healthy ready baseline;
106
119
  the expected HEAD, worktree, spec, and source-evidence drift created by that change is
107
120
  advisory until delivery reconciliation. Loss of approved-ready status or structural
@@ -110,6 +123,8 @@ approver and leaves a blocking reconciliation gap at archive; new ordinary work
110
123
  requires a healthy ready baseline. See
111
124
  [`docs/WORKFLOW-LIFECYCLE.md`](./docs/WORKFLOW-LIFECYCLE.md) for exact write,
112
125
  transition, invalidation, and recovery semantics.
126
+ The semantic crosswalk to the original Claude Code harness is
127
+ [`docs/LEGACY-CLI-CROSSWALK.md`](./docs/LEGACY-CLI-CROSSWALK.md).
113
128
 
114
129
  ## Three-layer architecture
115
130
 
@@ -117,7 +132,7 @@ transition, invalidation, and recovery semantics.
117
132
  |-------|---------|----------------|
118
133
  | **skill** (`skills/ultra-*/`) | Knowledge — prompts, workflows, prose | Runtime's native skill/prompt loader picks them up after install |
119
134
  | **MCP** (`mcp-server/`) | Authoritative state — reads/writes `.ultra/state.db` via stdio JSON-RPC | Primary path for task / session / event / plan operations |
120
- | **CLI** (`ultra-tools`, `bin/*`) | Selected initialization, recovery, diagnostics, and orchestration surfaces | `ultra-tools task init-project`, `ultra-tools system doctor`, `ubp-orchestrator run` |
135
+ | **CLI** (`ultra-tools`, `bin/*`) | Selected initialization, recovery, diagnostics, and orchestration surfaces | `ultra-tools task init-project`, `ultra-tools system doctor`, `ubp-orchestrator execute-plan` |
121
136
 
122
137
  The layers share one `.ultra/state.db`, but the CLI is not a mirror of every MCP
123
138
  tool: continuous change mutations are MCP-only and fail closed. See
@@ -143,7 +158,7 @@ and framework guidance belong to their original plugins.
143
158
  | Workflow hooks | ✅ (native plugin) | ✅ (native JS plugin) | ✅ (native plugin) | ✅ with documented compact limitation |
144
159
  | Sub-agents | ✅ | ✅ | ✅ (9 native TOML agents) | ✅ via `Agent` / `AgentSwarm` prompt templates |
145
160
  | Session worktree isolation | ✅ (driven by `orchestrator/session-runner.cjs`) | ✅ | ✅ | ✅ |
146
- | Parallel dispatch + auto-merge | ✅ (`ubp-orchestrator run`) | ✅ | ✅ | ✅ |
161
+ | Dependency-wave dispatch + optional verified auto-merge | ✅ (`ubp-orchestrator execute-plan`) | ✅ | ✅ | ✅ |
147
162
 
148
163
  Full details in [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.md)
149
164
  (10 sections, with per-runtime deviations called out).
@@ -151,20 +166,29 @@ Full details in [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.m
151
166
  ## Typical workflow
152
167
 
153
168
  ```bash
154
- # 1. Bootstrap project state (the ultra-init skill completes adoption and approval)
169
+ # 1. Bootstrap deterministic authority. This completes init but not research/baseline.
155
170
  ultra-tools task init-project --project-name myapp --mode auto
156
171
 
157
172
  # Projection-only projects from prior releases use one backup-first import:
158
173
  ultra-tools migrate --from=4.4 --to=4.5 --source-dir .
159
- ultra-tools migrate --from=4.5 --to=16.0 --source-dir .
160
-
161
- # 2. Complete all 17 research steps. When owner judgment is required, the host asks
162
- # one evidence-backed decision at a time and checkpoints the accepted artifacts.
163
- # Record current evidence, obtain explicit baseline approval, then open and align
164
- # an initial change before persisting its task contracts.
165
-
166
- # 3. Run the plan — parallel sessions, auto-merge back to main on success
167
- ubp-orchestrator run
174
+ ultra-tools migrate --from=4.5 --to=18.0 --source-dir .
175
+
176
+ # 2. Invoke ultra-research explicitly. The host dispositions each coverage area,
177
+ # executes/verifies/reuses only what is necessary, and asks one material decision at
178
+ # a time through the host-native question surface. Record evidence and obtain
179
+ # baseline approval only after the selected coverage converges.
180
+
181
+ # 3. Optional worker automation. Configure a real shell-free worker first:
182
+ # {"orchestrator":{"command":"/absolute/path/to/ultra-worker","command_args":[]}}
183
+ # Then resume current dependency waves until the first non-converged wave.
184
+ # A paused result is expected while dev/test/review evidence is still open.
185
+ # Worktrees are preserved by default. Empty, cyclic, stale, duplicate-task,
186
+ # cross-change, and stale or unverified plans fail before a session is created.
187
+ # Each worker checkout resolves `.ultra` to the one central authority; reserved
188
+ # UBP_DB_PATH / UBP_ROOT_DIR / UBP_AUTHORITY_ROOT bindings cannot be overridden.
189
+ ubp-orchestrator execute-plan
190
+ # Add --auto-merge only when the worker records the exact completion commit,
191
+ # ready dev steps, and a current task review before it exits.
168
192
 
169
193
  # 4. Monitor cost, progress, and runtime health
170
194
  ultra-tools status
@@ -184,12 +208,18 @@ Kimi uses the corresponding
184
208
  `/ultra-builder-pro:ultra-*` namespace. Other runtimes retain their native
185
209
  command form.
186
210
 
211
+ The arrows above show a common capability path, not a mandatory pipeline. After
212
+ initialization the user or host explicitly selects research. During daily work,
213
+ `ultra-change` may hand off to bounded research, planning, an already-current task,
214
+ thinking, or status according to intent and evidence. MCP exposes allowed transitions
215
+ and hard recovery requirements; the active host recommends the semantic route.
216
+
187
217
  ## CLI surface
188
218
 
189
219
  | Binary | Purpose |
190
220
  |--------|---------|
191
221
  | `ultra-builder-pro-cli` / `ubp` | Installer — `--claude / --opencode / --codex / --kimi / --all`, `--local / --global`, `--uninstall`; read-only install checks via `--doctor [--json]` |
192
- | `ubp-orchestrator` | Session dispatch daemon — `run`, `start`, `stop`, `status` |
222
+ | `ubp-orchestrator` | Current completed-plan runner (`execute-plan`) and optional explicit-worker daemon (`run`, `start`, `stop`, `status`) |
193
223
  | `ultra-tools` | State-layer CLI — `task`, `session`, `status`, `db`, `migrate`; explicit `legacy-memory` archive/prune migration |
194
224
  | `ubp-handbook` | Preview/apply the managed Ultra contract in `CLAUDE.md` / `AGENTS.md`, with backup |
195
225
 
@@ -21,6 +21,7 @@ const {
21
21
  removeTree,
22
22
  writeAtomic,
23
23
  } = require('./file-ops.cjs');
24
+ const { interactionContract } = require('./interaction-contract.cjs');
24
25
 
25
26
  const PLUGIN_NAME = 'ultra-builder-pro';
26
27
  const MANAGED_MARKER = 'Managed by Ultra Builder Pro Codex adapter.';
@@ -54,12 +55,12 @@ const CODEX_NATIVE_MCP_REPLACEMENTS = Object.freeze({
54
55
  replacement: 'the installed plugin skill SKILL.md and agents/openai.yaml contract',
55
56
  },
56
57
  'ask.question': {
57
- surface: 'direct_user_interaction',
58
- replacement: 'ask the user directly, or request_user_input when that Codex surface is available',
58
+ surface: 'request_user_input',
59
+ replacement: 'use request_user_input when available, otherwise ask one concise direct question',
59
60
  },
60
61
  'ask.menu': {
61
- surface: 'direct_user_interaction',
62
- replacement: 'present a concise choice directly, or request_user_input when that Codex surface is available',
62
+ surface: 'request_user_input',
63
+ replacement: 'use request_user_input when available, otherwise present one concise direct choice',
63
64
  },
64
65
  });
65
66
  const COMMAND_NAMES = Object.freeze(CORE_PUBLIC_SKILLS.filter((name) => name !== 'ultra-review'));
@@ -420,6 +421,10 @@ main().catch((error) => {
420
421
  const specRoot = path.join(pluginRoot, 'spec');
421
422
  ensureDir(specRoot);
422
423
  writeAtomic(path.join(specRoot, 'mcp-tools.yaml'), yaml.dump(liveManifest, { lineWidth: -1, noRefs: true }));
424
+ writeAtomic(
425
+ path.join(specRoot, 'interaction-contract.json'),
426
+ JSON.stringify(interactionContract(runtime), null, 2) + '\n',
427
+ );
423
428
  if (runtime === 'codex') {
424
429
  fs.copyFileSync(sourceToolsFile, path.join(specRoot, 'upstream-mcp-tools.yaml'));
425
430
  writeAtomic(path.join(specRoot, 'codex-capability-map.json'), JSON.stringify({
@@ -519,7 +524,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
519
524
  defaultPrompt: [
520
525
  'Initialize this project with the Ultra Builder Pro workflow.',
521
526
  'Run the Ultra review pipeline on my current changes.',
522
- 'Show the current Ultra project status and next action.',
527
+ 'Show the current Ultra project status, valid transitions, and host recommendation.',
523
528
  ],
524
529
  brandColor: '#111827',
525
530
  },
@@ -62,7 +62,7 @@ function renderHandbook(runtime) {
62
62
  '- Authority: `.ultra/state.db` is the only durable Ultra authority for baselines, changes, decision threads, tasks, sessions, events, incidents, projection state, telemetry, and review evidence. Generated JSON and Markdown are projections or workflow artifacts.',
63
63
  '- Baseline boundary: `task.init_project` classifies greenfield or brownfield work and records repository scope. Projection-only prior state uses the exact supported backup-first import returned by the authority check; migrated compatibility rows require explicit brownfield re-adoption. `baseline.record` owns evidence, verification, repository snapshot, and the gap ledger; `baseline.converge` requires explicit owner approval. New ordinary changes require a healthy ready baseline. Existing active work may continue with warnings, but baseline readiness blocks change convergence; normal drift reconciliation is health-checked atomically at archive. Only an explicitly approved incident break-glass may start without baseline readiness, and its archive creates a blocking reconciliation gap.',
64
64
  '- Decision boundary: project-bound owner choices use one resumable DB-backed decision thread. The host resolves observable facts autonomously, presents only the earliest unresolved decision with evidence and a recommendation, waits for the owner, and records the normalized decision rather than prompts or transcripts. A linked workflow cannot advance until blocking decisions are resolved and its owner-approved checkpoint is current.',
65
- '- Context Spine boundary: `change.context` compiles role/gate readiness, required references, a fresh-context budget, public seam, verification command, and one next action. File, token, and context-share budgets are advisory attention signals, not refusal gates. Hooks inject only the DB-derived `change.breadcrumb`; missing required evidence or stale execution context must be recompiled.',
65
+ '- Context Spine boundary: `change.context` compiles role/gate readiness, required references, a fresh-context budget, public seam, verification command, Change/task authority digests, allowed transitions, and any unique hard-recovery transition. File, token, and context-share budgets are advisory attention signals, not refusal gates. The host owns semantic recommendations. Hooks inject only the DB-derived `change.breadcrumb`; missing required evidence or stale execution context must be recompiled.',
66
66
  '- Specification learning boundary: stable implementation discoveries use approval-gated propose/approve-or-reject/apply transitions. Unresolved learning blocks convergence; review requires independent specification-fidelity and engineering-standards evidence.',
67
67
  '- 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.',
68
68
  '- Hook boundary: Ultra hooks observe workflow/change lifecycle only. Health/context may run when `.ultra/state.db` exists; advisory warnings never reject work, and an incomplete workflow never traps session stop. Direct projection protection remains authoritative; compact and subagent recovery stays active-workflow scoped. Generic command blocking and post-edit policy stay in user or repository governance.',
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const SURFACES = Object.freeze({
4
+ claude: Object.freeze({
5
+ primary: 'AskUserQuestion',
6
+ fallback: 'direct_user_interaction',
7
+ }),
8
+ codex: Object.freeze({
9
+ primary: 'request_user_input',
10
+ fallback: 'direct_user_interaction',
11
+ }),
12
+ opencode: Object.freeze({
13
+ primary: 'question',
14
+ fallback: 'direct_user_interaction',
15
+ }),
16
+ kimi: Object.freeze({
17
+ primary: 'native_question_surface',
18
+ fallback: 'direct_user_interaction',
19
+ }),
20
+ });
21
+
22
+ function interactionContract(runtime) {
23
+ const question = SURFACES[runtime];
24
+ if (!question) throw new Error(`unsupported interaction runtime: ${runtime}`);
25
+ return {
26
+ schema_version: '1.0',
27
+ runtime,
28
+ authority: {
29
+ user: [
30
+ 'product_intent',
31
+ 'material_scope_and_tradeoffs',
32
+ 'destructive_or_external_effect_authorization',
33
+ ],
34
+ host_model: [
35
+ 'fact_finding_and_synthesis',
36
+ 'reversible_implementation_judgment',
37
+ 'coverage_risk_and_route_recommendation',
38
+ ],
39
+ ultra_mcp: [
40
+ 'durable_state_and_evidence_refs',
41
+ 'digests_freshness_locks_and_recovery',
42
+ 'allowed_and_required_transitions',
43
+ ],
44
+ },
45
+ interaction: {
46
+ question_surface: question,
47
+ dependent_decisions: 'one_at_a_time',
48
+ independent_low_load_questions: { maximum: 3 },
49
+ explicit_current_intent: 'normalize_without_reconfirmation',
50
+ unavailable_native_surface: 'use_direct_interaction',
51
+ },
52
+ routing: {
53
+ semantic_recommendation_owner: 'host_model',
54
+ durable_recommendation_authority: false,
55
+ hard_invariant_field: 'required_transition',
56
+ valid_capabilities_field: 'allowed_transitions',
57
+ },
58
+ };
59
+ }
60
+
61
+ module.exports = { SURFACES, interactionContract };
@@ -28,7 +28,7 @@ const PLUGIN_NAME = 'ultra-builder-pro';
28
28
  const MCP_SERVER_NAME = PLUGIN_NAME;
29
29
  const SOURCE_TAG = 'ubp';
30
30
  const SENTINEL_KEY = '_ubp_manifest';
31
- const COMMAND_NAMES = CORE_PUBLIC_SKILLS.filter((name) => name !== 'ultra-review');
31
+ const COMMAND_NAMES = CORE_PUBLIC_SKILLS;
32
32
  const PROVENANCE_FILE = 'provenance.json';
33
33
 
34
34
  function resolveTarget(ctx = {}) {
@@ -36,7 +36,7 @@ const BUNDLE_DIR = '.ultra-builder-pro';
36
36
  const MANAGED_TEXT_MARKER = '<!-- ultra-builder-pro:managed -->';
37
37
  const PLUGIN_MARKER = '// Managed by Ultra Builder Pro.';
38
38
  const PROVENANCE_FILE = 'provenance.json';
39
- const COMMAND_NAMES = CORE_PUBLIC_SKILLS.filter((name) => name !== 'ultra-review');
39
+ const COMMAND_NAMES = CORE_PUBLIC_SKILLS;
40
40
 
41
41
  function resolveTarget(ctx = {}) {
42
42
  if (ctx.configDir) return ctx.configDir;
@@ -4,6 +4,7 @@
4
4
  * ubp-orchestrator — Phase 5.4 daemon CLI.
5
5
  *
6
6
  * Subcommands:
7
+ * execute-plan resume the current plan through the first unfinished dependency wave.
7
8
  * run foreground daemon (debug / test).
8
9
  * start detached background daemon, writes .ultra/orchestrator.pid.
9
10
  * stop reads pidfile, SIGTERM the process, deletes pidfile.
@@ -12,9 +13,8 @@
12
13
  * Opt-in gate: `start` and `run` require settings.json
13
14
  * `orchestrator.auto_dispatch: true` — default off per PLAN Phase 5.4 AC.
14
15
  *
15
- * The daemon never launches real runtime children in this phase — session
16
- * rows + git worktrees are created, external adapters (Phase 4) attach to
17
- * the worktree out-of-band. command/args can be passed via env for testing.
16
+ * Dispatch requires both explicit opt-in and an explicit executable command.
17
+ * A daemon may never reserve a session/worktree without a real worker process.
18
18
  */
19
19
 
20
20
  'use strict';
@@ -56,6 +56,124 @@ function parseRuntimes() {
56
56
  return runtimes;
57
57
  }
58
58
 
59
+ function commandConfigError(code, message) {
60
+ const error = new Error(message);
61
+ error.code = code;
62
+ return error;
63
+ }
64
+
65
+ function resolveDispatchCommand(settings, env = process.env) {
66
+ const configured = settings?.orchestrator || {};
67
+ const fromEnvironment = typeof env.UBP_ORCH_COMMAND === 'string'
68
+ && env.UBP_ORCH_COMMAND.trim() !== '';
69
+ const command = fromEnvironment
70
+ ? env.UBP_ORCH_COMMAND.trim()
71
+ : (typeof configured.command === 'string' ? configured.command.trim() : '');
72
+ if (!command) {
73
+ throw commandConfigError(
74
+ 'ORCHESTRATOR_COMMAND_REQUIRED',
75
+ 'orchestrator dispatch requires orchestrator.command or UBP_ORCH_COMMAND',
76
+ );
77
+ }
78
+ if (/\s/.test(command) && !fs.existsSync(command)) {
79
+ throw commandConfigError(
80
+ 'ORCHESTRATOR_COMMAND_INVALID',
81
+ 'orchestrator.command must contain only the executable; put arguments in command_args',
82
+ );
83
+ }
84
+
85
+ let commandArgs;
86
+ if (fromEnvironment && env.UBP_ORCH_ARGS_JSON !== undefined) {
87
+ try {
88
+ commandArgs = JSON.parse(env.UBP_ORCH_ARGS_JSON);
89
+ } catch (error) {
90
+ throw commandConfigError(
91
+ 'ORCHESTRATOR_COMMAND_INVALID',
92
+ `UBP_ORCH_ARGS_JSON must be a JSON array: ${error.message}`,
93
+ );
94
+ }
95
+ } else {
96
+ commandArgs = configured.command_args === undefined ? [] : configured.command_args;
97
+ }
98
+ if (!Array.isArray(commandArgs) || commandArgs.some((arg) => typeof arg !== 'string')) {
99
+ throw commandConfigError(
100
+ 'ORCHESTRATOR_COMMAND_INVALID',
101
+ 'orchestrator command arguments must be an array of strings',
102
+ );
103
+ }
104
+ return {
105
+ command,
106
+ commandArgs: commandArgs.slice(),
107
+ source: fromEnvironment ? 'environment' : 'settings',
108
+ };
109
+ }
110
+
111
+ function parseExecutePlanArgs(argv = []) {
112
+ const options = {
113
+ planPath: null,
114
+ autoMerge: false,
115
+ mergeBaseBranch: 'main',
116
+ };
117
+ for (let index = 0; index < argv.length; index += 1) {
118
+ const arg = argv[index];
119
+ if (arg === '--auto-merge') {
120
+ options.autoMerge = true;
121
+ continue;
122
+ }
123
+ if (arg === '--plan' || arg === '--base-branch') {
124
+ const value = argv[index + 1];
125
+ if (!value || value.startsWith('--')) {
126
+ throw commandConfigError(
127
+ 'ORCHESTRATOR_ARGUMENT_INVALID',
128
+ `${arg} requires a value`,
129
+ );
130
+ }
131
+ if (arg === '--plan') options.planPath = value;
132
+ else options.mergeBaseBranch = value;
133
+ index += 1;
134
+ continue;
135
+ }
136
+ throw commandConfigError(
137
+ 'ORCHESTRATOR_ARGUMENT_INVALID',
138
+ `unknown execute-plan option: ${arg}`,
139
+ );
140
+ }
141
+ return options;
142
+ }
143
+
144
+ async function cmdExecutePlan(argv = []) {
145
+ const settings = readSettings();
146
+ const dispatch = resolveDispatchCommand(settings);
147
+ const options = parseExecutePlanArgs(argv);
148
+ const { initStateDb, closeStateDb } = require('../mcp-server/lib/state-db.cjs');
149
+ const parallelOrchestrator = require('../orchestrator/parallel-orchestrator.cjs');
150
+ const { db } = initStateDb(path.join(REPO_ROOT, '.ultra', 'state.db'));
151
+ try {
152
+ const result = await parallelOrchestrator.runPlan({
153
+ db,
154
+ repoRoot: REPO_ROOT,
155
+ planPath: options.planPath ? path.resolve(REPO_ROOT, options.planPath) : undefined,
156
+ runtimes: parseRuntimes(),
157
+ command: dispatch.command,
158
+ commandArgs: dispatch.commandArgs,
159
+ autoMerge: options.autoMerge,
160
+ mergeBaseBranch: options.mergeBaseBranch,
161
+ onError: (error) => process.stderr.write(`orchestrator error: ${error.message}\n`),
162
+ });
163
+ process.stdout.write(`${JSON.stringify({
164
+ ok: true,
165
+ data: {
166
+ ...result,
167
+ auto_merge: options.autoMerge,
168
+ merge_base_branch: options.mergeBaseBranch,
169
+ },
170
+ })}\n`);
171
+ return result;
172
+ } finally {
173
+ closeStateDb(db);
174
+ }
175
+ }
176
+
59
177
  function cmdRun(opts = {}) {
60
178
  const settings = readSettings();
61
179
  if (!opts.skipOptIn && !optInAllowed(settings)) {
@@ -68,11 +186,14 @@ function cmdRun(opts = {}) {
68
186
  const { initStateDb } = require('../mcp-server/lib/state-db.cjs');
69
187
  const { runDaemon } = require('../orchestrator/daemon.cjs');
70
188
  const runtimes = parseRuntimes();
189
+ const dispatch = resolveDispatchCommand(settings);
71
190
  const { db } = initStateDb(path.join(REPO_ROOT, '.ultra', 'state.db'));
72
191
  const handle = runDaemon({
73
192
  db,
74
193
  repoRoot: REPO_ROOT,
75
194
  runtimes,
195
+ command: dispatch.command,
196
+ commandArgs: dispatch.commandArgs,
76
197
  pollMs: Number(process.env.UBP_ORCH_POLL_MS || 1000),
77
198
  onError: (err) => process.stderr.write(`orchestrator error: ${err.message}\n`),
78
199
  });
@@ -85,7 +206,10 @@ function cmdRun(opts = {}) {
85
206
  };
86
207
  process.on('SIGTERM', () => shutdown('SIGTERM'));
87
208
  process.on('SIGINT', () => shutdown('SIGINT'));
88
- process.stderr.write(`orchestrator running (pollMs=${Number(process.env.UBP_ORCH_POLL_MS || 1000)}, runtimes=${runtimes.join(',')})\n`);
209
+ process.stderr.write(
210
+ `orchestrator running (pollMs=${Number(process.env.UBP_ORCH_POLL_MS || 1000)}, ` +
211
+ `runtimes=${runtimes.join(',')}, worker=${dispatch.command}, source=${dispatch.source})\n`,
212
+ );
89
213
  // Keep process alive while the setInterval is unref'd.
90
214
  setInterval(() => {}, 60000);
91
215
  }
@@ -96,6 +220,7 @@ function cmdStart() {
96
220
  process.stderr.write('orchestrator.auto_dispatch is not enabled; refusing to start.\n');
97
221
  process.exit(2);
98
222
  }
223
+ resolveDispatchCommand(settings);
99
224
  if (fs.existsSync(PIDFILE)) {
100
225
  const existing = Number(fs.readFileSync(PIDFILE, 'utf8'));
101
226
  try { process.kill(existing, 0); }
@@ -170,8 +295,9 @@ function cmdStatus() {
170
295
 
171
296
  function usage() {
172
297
  process.stderr.write(
173
- 'usage: ubp-orchestrator <run|start|stop|status>\n' +
298
+ 'usage: ubp-orchestrator <execute-plan|run|start|stop|status>\n' +
174
299
  '\n' +
300
+ ' execute-plan resume the first unfinished dependency wave [--plan <path>] [--auto-merge] [--base-branch <name>]\n' +
175
301
  ' run foreground daemon (requires opt-in)\n' +
176
302
  ' start detached background daemon (requires opt-in)\n' +
177
303
  ' stop terminate running daemon\n' +
@@ -179,18 +305,48 @@ function usage() {
179
305
  );
180
306
  }
181
307
 
182
- const argv = process.argv.slice(2);
183
- const subcommand = argv[0];
184
- switch (subcommand) {
185
- case 'run': cmdRun(); break;
186
- case 'start': cmdStart(); break;
187
- case 'stop': cmdStop(); break;
188
- case 'status': cmdStatus(); break;
189
- case '-h':
190
- case '--help':
191
- case 'help':
192
- usage(); process.exit(0);
193
- break;
194
- default:
195
- usage(); process.exit(1);
308
+ async function main(argv = process.argv.slice(2)) {
309
+ const subcommand = argv[0];
310
+ try {
311
+ switch (subcommand) {
312
+ case 'execute-plan': await cmdExecutePlan(argv.slice(1)); break;
313
+ case 'run': cmdRun(); break;
314
+ case 'start': cmdStart(); break;
315
+ case 'stop': cmdStop(); break;
316
+ case 'status': cmdStatus(); break;
317
+ case '-h':
318
+ case '--help':
319
+ case 'help':
320
+ usage(); process.exit(0);
321
+ break;
322
+ default:
323
+ usage(); process.exit(1);
324
+ }
325
+ } catch (error) {
326
+ process.stderr.write(
327
+ `orchestrator configuration error${error.code ? ` [${error.code}]` : ''}: ${error.message}\n`,
328
+ );
329
+ process.exit(2);
330
+ }
331
+ }
332
+
333
+ if (require.main === module) {
334
+ main().catch((error) => {
335
+ process.stderr.write(`orchestrator fatal error: ${error.message}\n`);
336
+ process.exit(2);
337
+ });
196
338
  }
339
+
340
+ module.exports = {
341
+ readSettings,
342
+ optInAllowed,
343
+ parseRuntimes,
344
+ parseExecutePlanArgs,
345
+ resolveDispatchCommand,
346
+ cmdExecutePlan,
347
+ cmdRun,
348
+ cmdStart,
349
+ cmdStop,
350
+ cmdStatus,
351
+ main,
352
+ };