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.
- package/.ultra-template/specs/research-distillate.md +4 -3
- package/CHANGELOG.md +86 -0
- package/README.md +54 -24
- package/adapters/_shared/codex-assets.cjs +10 -5
- package/adapters/_shared/handbook.cjs +1 -1
- package/adapters/_shared/interaction-contract.cjs +61 -0
- package/adapters/claude.js +1 -1
- package/adapters/opencode.js +1 -1
- package/bin/orchestrator.js +175 -19
- package/commands/ultra-change.md +1 -1
- package/commands/ultra-deliver.md +3 -3
- package/commands/ultra-init.md +1 -1
- package/commands/ultra-plan.md +1 -1
- package/commands/ultra-research.md +2 -2
- package/commands/ultra-review.md +10 -0
- package/commands/ultra-status.md +1 -1
- package/commands/ultra-test.md +1 -1
- package/docs/AGENT-CONTEXT.md +11 -8
- package/docs/ARCHITECTURE.md +67 -9
- package/docs/LEGACY-CLI-CROSSWALK.md +163 -0
- package/docs/PLAN.zh-CN.md +19 -13
- package/docs/RUNTIME-COMPAT-MATRIX.md +2 -2
- package/docs/WORKFLOW-LIFECYCLE.md +123 -59
- package/hooks/pre_stop_check.py +3 -2
- package/mcp-server/lib/baseline-workflow.cjs +84 -16
- package/mcp-server/lib/change-workflow.cjs +55 -35
- package/mcp-server/lib/context-spine.cjs +171 -105
- package/mcp-server/lib/git-bootstrap.cjs +240 -0
- package/mcp-server/lib/init-project.cjs +178 -41
- package/mcp-server/lib/project-breadcrumb.cjs +14 -14
- package/mcp-server/lib/state-db.cjs +257 -10
- package/mcp-server/lib/state-ops.cjs +64 -1
- package/mcp-server/lib/workflow-state.cjs +300 -205
- package/mcp-server/server.cjs +58 -34
- package/orchestrator/auto-merge.cjs +19 -0
- package/orchestrator/daemon.cjs +104 -0
- package/orchestrator/parallel-orchestrator.cjs +371 -49
- package/orchestrator/session-runner.cjs +354 -25
- package/package.json +2 -1
- package/skills/ultra-change/SKILL.md +69 -94
- package/skills/ultra-deliver/SKILL.md +37 -65
- package/skills/ultra-dev/SKILL.md +50 -52
- package/skills/ultra-doctor/SKILL.md +34 -49
- package/skills/ultra-init/SKILL.md +78 -105
- package/skills/ultra-plan/SKILL.md +78 -99
- package/skills/ultra-research/SKILL.md +99 -113
- package/skills/ultra-review/SKILL.md +38 -53
- package/skills/ultra-review/references/review-modes.md +5 -3
- package/skills/ultra-review/references/unified-schema.md +26 -6
- package/skills/ultra-review/scripts/review_wait.py +12 -0
- package/skills/ultra-status/SKILL.md +34 -44
- package/skills/ultra-test/SKILL.md +61 -54
- package/skills/ultra-think/SKILL.md +37 -51
- package/skills/ultra-think/references/decision-dialogue.md +87 -111
- package/spec/cli-protocol.md +35 -0
- package/spec/mcp-tools.yaml +145 -28
- package/spec/schemas/delivery-report.v1.schema.json +1 -46
- package/spec/schemas/state-db.sql +8 -2
- package/spec/schemas/test-report.v1.schema.json +45 -14
- package/ultra-tools/cli.cjs +1 -1
- package/ultra-tools/commands/session.cjs +10 -3
- package/ultra-tools/commands/status.cjs +19 -14
- package/ultra-tools/commands/system.cjs +2 -2
- 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
|
-
-
|
|
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
|
|
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
|
-
-
|
|
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 →
|
|
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
|
|
40
|
-
|
|
41
|
-
`.ultra/execution-plan.json` → parallel worktree sessions
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
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.
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
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
|
-
|
|
|
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
|
|
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=
|
|
160
|
-
|
|
161
|
-
# 2.
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
# 3.
|
|
167
|
-
|
|
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` |
|
|
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: '
|
|
58
|
-
replacement: '
|
|
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: '
|
|
62
|
-
replacement: '
|
|
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
|
|
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
|
|
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 };
|
package/adapters/claude.js
CHANGED
|
@@ -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
|
|
31
|
+
const COMMAND_NAMES = CORE_PUBLIC_SKILLS;
|
|
32
32
|
const PROVENANCE_FILE = 'provenance.json';
|
|
33
33
|
|
|
34
34
|
function resolveTarget(ctx = {}) {
|
package/adapters/opencode.js
CHANGED
|
@@ -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
|
|
39
|
+
const COMMAND_NAMES = CORE_PUBLIC_SKILLS;
|
|
40
40
|
|
|
41
41
|
function resolveTarget(ctx = {}) {
|
|
42
42
|
if (ctx.configDir) return ctx.configDir;
|
package/bin/orchestrator.js
CHANGED
|
@@ -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
|
-
*
|
|
16
|
-
*
|
|
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(
|
|
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
|
-
|
|
183
|
-
const subcommand = argv[0];
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
+
};
|