ultra-builder-pro-cli 0.15.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 +120 -1
- package/README.md +67 -30
- package/adapters/_shared/codex-assets.cjs +10 -5
- package/adapters/_shared/handbook.cjs +3 -2
- 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/commands/ultra-think.md +2 -2
- package/docs/AGENT-CONTEXT.md +38 -18
- package/docs/ARCHITECTURE.md +91 -13
- package/docs/LEGACY-CLI-CROSSWALK.md +163 -0
- package/docs/PLAN.zh-CN.md +19 -12
- package/docs/ROADMAP.md +5 -3
- package/docs/RUNTIME-COMPAT-MATRIX.md +4 -4
- package/docs/STATE-DB-ACCESS-POLICY.md +8 -6
- package/docs/USER-HANDBOOK-CONTRACT.md +8 -2
- package/docs/WORKFLOW-LIFECYCLE.md +179 -39
- package/hooks/pre_stop_check.py +3 -2
- package/mcp-server/lib/baseline-workflow.cjs +84 -16
- package/mcp-server/lib/change-workflow.cjs +107 -39
- package/mcp-server/lib/context-spine.cjs +215 -87
- package/mcp-server/lib/decision-dialogue.cjs +713 -0
- package/mcp-server/lib/doctor.cjs +9 -0
- 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 +23 -14
- package/mcp-server/lib/state-db.cjs +279 -9
- package/mcp-server/lib/state-ops.cjs +64 -1
- package/mcp-server/lib/workflow-state.cjs +313 -205
- package/mcp-server/server.cjs +112 -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 -65
- package/skills/ultra-deliver/SKILL.md +37 -58
- package/skills/ultra-dev/SKILL.md +55 -49
- package/skills/ultra-doctor/SKILL.md +44 -49
- package/skills/ultra-init/SKILL.md +78 -100
- package/skills/ultra-plan/SKILL.md +78 -79
- package/skills/ultra-research/SKILL.md +99 -98
- package/skills/ultra-review/SKILL.md +38 -47
- 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 -40
- package/skills/ultra-test/SKILL.md +61 -48
- package/skills/ultra-think/SKILL.md +47 -47
- package/skills/ultra-think/references/decision-dialogue.md +106 -0
- package/spec/cli-protocol.md +46 -2
- package/spec/mcp-tools.yaml +444 -29
- package/spec/schemas/delivery-report.v1.schema.json +1 -46
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +66 -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 +34 -14
- package/ultra-tools/commands/system.cjs +2 -2
- package/ultra-tools/commands/task.cjs +3 -0
|
@@ -9,10 +9,10 @@ host and project owner. It is the operational companion to
|
|
|
9
9
|
|
|
10
10
|
| Surface | Owns | Must not own |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| Active host and owner | Product judgment, research content, implementation, review findings,
|
|
12
|
+
| Active host and owner | Product judgment, research content, implementation, review findings, and authorization for destructive or external effects | Durable workflow status or fabricated evidence digests |
|
|
13
13
|
| Skill | One reusable procedure and its evidence requirements | Project state, static product doctrine, host-specific tool fiction |
|
|
14
|
-
| MCP | IDs, transitions, task contracts, evidence references, output hashes, gate verdicts
|
|
15
|
-
| `.ultra/state.db` | Baseline, change, task, workflow, session, event, incident, projection, and evidence authority |
|
|
14
|
+
| MCP | IDs, valid transitions, hard recovery requirements, task contracts, evidence references, output hashes, and gate verdicts | Semantic route selection, research prose, code generation, memory, or model calls |
|
|
15
|
+
| `.ultra/state.db` | Baseline, change, decision, task, workflow, session, event, incident, projection, and evidence authority | Prompts, transcripts, external memory, or code-graph payloads |
|
|
16
16
|
| Generated JSON and Markdown | Read-only projections and durable artifacts | Independent lifecycle authority |
|
|
17
17
|
| Hook | Compact DB-derived lifecycle observation, minimal event metadata, and projection protection | Generic engineering judgment, prompt/transcript capture, parallel lifecycle logs, or arbitrary edit blocking |
|
|
18
18
|
|
|
@@ -20,7 +20,14 @@ Prompt input can supply facts, content paths, reasons, owner decisions, and
|
|
|
20
20
|
evidence references. MCP reads the current repository and DB, validates those
|
|
21
21
|
inputs, hashes outputs, and derives status. Prompt input cannot override a task's
|
|
22
22
|
public seam, verification command, context references, workflow summary, gate
|
|
23
|
-
verdict, output digest, or
|
|
23
|
+
verdict, output digest, or transition validity. A host recommendation is never stored
|
|
24
|
+
as MCP authority.
|
|
25
|
+
|
|
26
|
+
Load-bearing owner choices are normalized through `decision.*`; Prompt prose is not
|
|
27
|
+
durable authority. The host acquires observable facts autonomously, exposes one
|
|
28
|
+
current question, and stops. A workflow cannot advance while a matching decision
|
|
29
|
+
thread is active, waiting for checkpoint confirmation, or carrying a blocking
|
|
30
|
+
deferral.
|
|
24
31
|
|
|
25
32
|
Every change-bound research, plan, dev, test, review, and deliver run inherits
|
|
26
33
|
the baseline id from the owning change workflow. A Prompt cannot switch that
|
|
@@ -28,10 +35,12 @@ authority, and no new stage may start on an archived or cancelled change. Doctor
|
|
|
28
35
|
recovers a legacy change without this link into an explicitly blocked workflow;
|
|
29
36
|
it does not fabricate the missing provenance.
|
|
30
37
|
|
|
31
|
-
Ultra does not select an MVP. Product scope and delivery posture are owner
|
|
32
|
-
decisions.
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
Ultra does not select an MVP. Product scope and material delivery posture are owner
|
|
39
|
+
decisions. The seventeen research areas are a coverage catalog, not a forced
|
|
40
|
+
questionnaire. Initial greenfield and brownfield research must disposition every area
|
|
41
|
+
as `execute`, `verify_existing`, `reuse`, `not_applicable`, or accepted `deferred`;
|
|
42
|
+
only selected necessary work executes. Bounded change research records the same
|
|
43
|
+
selection rationale over its smaller scope.
|
|
35
44
|
|
|
36
45
|
## 2. How `ultra-init` classifies a repository
|
|
37
46
|
|
|
@@ -59,9 +68,22 @@ worktree digests and accepted dirty files are restricted to that scope.
|
|
|
59
68
|
|
|
60
69
|
Classification and readiness are separate. Initialization immediately creates
|
|
61
70
|
or resumes `.ultra/state.db`, installs only the required scaffold, records the
|
|
62
|
-
classification,
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
classification, and completes an `init` workflow after read-back verification. It
|
|
72
|
+
does not create a research run. A later explicit `ultra-research` invocation selects
|
|
73
|
+
`full` for greenfield or `adoption` for brownfield, records coverage dispositions, and
|
|
74
|
+
works toward baseline readiness. Empty templates are not an approved baseline.
|
|
75
|
+
|
|
76
|
+
With `git_mode: auto`, initialization preserves an existing repository and HEAD.
|
|
77
|
+
When Git is absent it initializes `main` and adds the symlink-safe `.ultra` rule to
|
|
78
|
+
`.gitignore`; an existing repository keeps its tracked ignore file unchanged. Init
|
|
79
|
+
does not create a commit, remote, tag, or push. The resulting `unborn` worktree state
|
|
80
|
+
is durable authority, not equivalent to a non-Git workspace. Full/adoption research
|
|
81
|
+
must obtain explicit approval for one local checkpoint commit before
|
|
82
|
+
`baseline.record`; until then `BASELINE_GIT_HEAD_REQUIRED` blocks convergence.
|
|
83
|
+
|
|
84
|
+
Init completion means only that local authority, Git state, scaffold, and
|
|
85
|
+
classification are usable. Baseline readiness is a separate research and convergence
|
|
86
|
+
outcome; failure there never reopens or blocks the completed init workflow.
|
|
65
87
|
|
|
66
88
|
## 3. Durable status machines
|
|
67
89
|
|
|
@@ -118,6 +140,40 @@ traceability, slice kind, public seam, exact verification command, acceptance
|
|
|
118
140
|
checks, context references, resolved documentation impact, owner, and valid
|
|
119
141
|
dependencies.
|
|
120
142
|
|
|
143
|
+
A semantic Change update marks all derived tasks stale. Clearing that marker requires
|
|
144
|
+
one complete execution-contract rebind through `task.update`; a marker-only clear is
|
|
145
|
+
rejected. MCP validates the resulting contract and appends
|
|
146
|
+
`task_contract_reconciled` with the current Change authority digest before a new plan
|
|
147
|
+
may converge.
|
|
148
|
+
|
|
149
|
+
A session process and a task have different status machines. Process exit zero closes
|
|
150
|
+
the execution transport but leaves the task `in_progress` until development,
|
|
151
|
+
integration, test, and review gates converge. Process failure marks the task `blocked`
|
|
152
|
+
and records failure/circuit evidence. `session.close` preserves the worktree by
|
|
153
|
+
default. Explicit removal succeeds only after Git proves it is clean and its commit is
|
|
154
|
+
integrated into the current checkout.
|
|
155
|
+
|
|
156
|
+
Dependency-wave automation advances only when every task in the current wave is
|
|
157
|
+
`completed` or `expanded` in the DB. A successful worker exit with open gates emits
|
|
158
|
+
`wave_paused` and `plan_paused`; it cannot start dependent work. Re-running the plan
|
|
159
|
+
resumes the first unfinished wave. Explicit auto-merge is additionally gated by task
|
|
160
|
+
completion, an exact completion commit, a ready dev workflow, a current task review,
|
|
161
|
+
clean committed work, and successful integration; otherwise the worktree remains
|
|
162
|
+
recoverable. Legacy tasks without change ownership retain their compatibility path,
|
|
163
|
+
but cannot satisfy the current change workflow contract.
|
|
164
|
+
|
|
165
|
+
Every session worktree resolves its ignored `.ultra` entry to the project authority
|
|
166
|
+
directory. Automated workers receive the central DB path and the isolated checkout
|
|
167
|
+
root separately; their supplied environment cannot override those bindings. If a
|
|
168
|
+
legacy `.ultra/` rule does not ignore the link itself, Ultra adds a repository-local
|
|
169
|
+
`info/exclude` entry without changing tracked files. A missing, tracked, still
|
|
170
|
+
unignored, or conflicting authority entry fails before the session row or worker is
|
|
171
|
+
created and the provisional worktree is removed. A process that already has
|
|
172
|
+
`UBP_SESSION_ID` must reuse that lease and worktree; nested spawn and worker-side close
|
|
173
|
+
are rejected, so the supervising parent alone settles the lease from the real process
|
|
174
|
+
exit. Authority drift detected after wave selection pauses the plan without recording
|
|
175
|
+
execution failure.
|
|
176
|
+
|
|
121
177
|
### Workflow run and step
|
|
122
178
|
|
|
123
179
|
```text
|
|
@@ -135,21 +191,38 @@ output cannot complete without it. When the final required step completes, the
|
|
|
135
191
|
run becomes `ready`; `workflow.complete` revalidates the entire stage before
|
|
136
192
|
making it `completed`.
|
|
137
193
|
|
|
194
|
+
### Decision thread and item
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
thread: active -> checkpoint_ready -> confirmed
|
|
198
|
+
^ |
|
|
199
|
+
|--------------+ revision/supersession
|
|
200
|
+
|
|
201
|
+
item: open -> answered | delegated | deferred | superseded
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A thread is bound to baseline, change, or workflow authority. A partial unique index
|
|
205
|
+
allows only one `open` item per thread. Resolved items remain immutable history;
|
|
206
|
+
changed evidence or intent uses `decision.supersede` and reopens the checkpoint.
|
|
207
|
+
`decision.checkpoint` first freezes a normalized decision digest, then binds explicit
|
|
208
|
+
owner approval to current artifact digests or a justified standalone no-artifact
|
|
209
|
+
case. It never stores the conversation transcript.
|
|
210
|
+
|
|
138
211
|
## 4. Write and verification lifecycle
|
|
139
212
|
|
|
140
|
-
| Stage | Authoritative writes | Completion verification |
|
|
213
|
+
| Stage | Authoritative writes | Completion verification | Available continuation |
|
|
141
214
|
|---|---|---|---|
|
|
142
|
-
| `init` | Baseline classification/scope,
|
|
143
|
-
| `research` |
|
|
215
|
+
| `init` | Baseline classification/scope, Git bootstrap state, completed init run, initialization event, projection job | DB/schema opens, scaffold/projection and read-back succeed; existing Git is preserved or missing local Git is initialized | Explicitly chosen `research`, `status`, or `doctor`; an already-ready project may also enter `change` |
|
|
216
|
+
| `research` | Coverage dispositions, selected step evidence, material decision checkpoints, typed semantic records with source digests, immutable reports, output paths and SHA-256 digests | Every coverage area is dispositioned; only executed/verified/reused areas require work evidence; synthesis binds current specifications and distillate; all material decisions and digests are current | Baseline record/convergence for initial research; bounded change research returns to its owning change |
|
|
144
217
|
| baseline convergence | Spec/source/runtime refs, verification results, known failures, unknowns, gaps, branch/HEAD/worktree, approval | Full/adoption research complete; required discovery/product/architecture refs current; revision/worktree exact; blocking gaps resolved or explicitly accepted where allowed | `change` for every initial or daily outcome |
|
|
145
|
-
| `change` | Complete Change Contract, profile/risk rationale, research disposition, intent/delta artifacts,
|
|
146
|
-
| `plan` |
|
|
147
|
-
| `dev` | Task/session transitions, step evidence, immutable implementation context, completion/review references | Task completed; no live session; task contract unchanged; starting context and review artifacts valid; completion commits remain ancestors of current HEAD | Next task, then aggregate `test` |
|
|
148
|
-
| `test` | Immutable checking context and `.ultra/reports/tests/<workflow-id>.json` |
|
|
149
|
-
| `review` | Immutable review context, `task`/`change`/`plan` mode, worker
|
|
150
|
-
| `deliver` | Convergence context, verified learning resolutions,
|
|
151
|
-
| `status` | None | Reads doctor, baseline, workflows, breadcrumb, tasks, sessions, Git, and DB-referenced reports |
|
|
152
|
-
| `doctor` | None by default; explicit repair writes only mechanical recovery state | Re-runs integrity, schema, workflow-output, projection, session, incident, archive, and install checks |
|
|
218
|
+
| `change` | Complete Change Contract, profile/risk rationale, research disposition, intent/delta artifacts, and a completed capture run; a decision thread only when material intent remains unresolved | Intent, acceptance, recovery, classification, and research disposition are complete and bound to the baseline | Host selects among bounded `research`, `plan`, current `dev`, `think`, or `status` using allowed transitions |
|
|
219
|
+
| `plan` | Selected planning evidence, complete change-owned task rows, acceptance coverage matrix, and change-bound `.ultra/execution-plan.json`; approval only for a material owner decision | Every Change acceptance id is owned by a task; trace targets, ownership, dependencies, topology, task contracts, and exported digest match current authority | Any dependency-ready task may enter `dev` |
|
|
220
|
+
| `dev` | Task/session transitions, real worktree, step evidence, immutable implementation context, completion/review references | Task completed; no live session; task contract unchanged; starting context and review artifacts valid; completion commits are integrated and remain ancestors of current HEAD | Next task, then aggregate `test` |
|
|
221
|
+
| `test` | Immutable checking context, an explicit risk-selected verification profile, and `.ultra/reports/tests/<workflow-id>.json` | Selected dimensions and acceptance pass; every excluded dimension has a rationale; report change/task set, HEAD, worktree, context, commands, blockers, and digests agree | `review` when passing, otherwise resume `dev`/`test` |
|
|
222
|
+
| `review` | Immutable review context, `task`/`change`/`plan` mode, risk-selected worker provenance, two mandatory specialist axes, and coordinated summary | Current diff/HEAD and context match; the complete worker roster is selected or skipped with rationale; completed workers match specialist artifacts; both axes complete; findings and axis verdicts derive the final verdict | `deliver` only from a passing `change` review, otherwise resume implementation or checking |
|
|
223
|
+
| `deliver` | Convergence context, verified learning resolutions, semantic reconciliation manifest, local change archive, and immutable delivery report | Tasks/test/change-review are current; baseline semantic updates are anchored; archive and baseline transaction agree; the report contains no release action | Report local completion or start the next `change`; publish/deploy/push are separate explicitly authorized operations |
|
|
224
|
+
| `status` | None | Reads doctor, baseline, workflows, breadcrumb, tasks, sessions, Git, and DB-referenced reports | Shows allowed transitions, any hard required transition, and a host-owned recommendation |
|
|
225
|
+
| `doctor` | None by default; explicit repair writes only mechanical recovery state | Re-runs integrity, schema, workflow-output, projection, session, incident, archive, and install checks | Mechanically required recovery when unique; otherwise a set of safe recovery capabilities |
|
|
153
226
|
|
|
154
227
|
Every mutating MCP operation appends an event and enqueues projection work when
|
|
155
228
|
the corresponding read-only view can change. Projection failure is recorded as
|
|
@@ -157,9 +230,42 @@ a retryable incident; it does not silently convert a failed write into success.
|
|
|
157
230
|
`task.append_event` accepts only non-authoritative observations. Lifecycle events are
|
|
158
231
|
emitted by their owning DB mutation and never act as a substitute state transition.
|
|
159
232
|
|
|
160
|
-
## 5.
|
|
161
|
-
|
|
162
|
-
`
|
|
233
|
+
## 5. Human-agent alignment lifecycle
|
|
234
|
+
|
|
235
|
+
The host first reads `decision.list` and resumes matching authority. It starts a new
|
|
236
|
+
thread only for a new material decision cluster. Before asking, it must:
|
|
237
|
+
|
|
238
|
+
1. inspect repository, runtime, tests, specifications, and primary-source evidence;
|
|
239
|
+
2. resolve evidence-answerable facts without asking the owner;
|
|
240
|
+
3. normalize an unambiguous decision already present in the current user request
|
|
241
|
+
instead of asking for redundant confirmation;
|
|
242
|
+
4. ask only when the remaining choice changes intent, public behavior, compatibility,
|
|
243
|
+
security, material cost, external effects, or recovery. Use the host-native
|
|
244
|
+
question surface declared by `spec/interaction-contract.json`;
|
|
245
|
+
5. present the earliest dependent choice with a recommendation, credible alternatives,
|
|
246
|
+
evidence refs, and durable effects. Up to three independent low-load facts may be
|
|
247
|
+
grouped when that reduces inconsistency;
|
|
248
|
+
6. normalize the owner response with `decision.resolve`, explicit reversible
|
|
249
|
+
delegation with `decision.delegate`, or a consequence-bearing deferral with
|
|
250
|
+
`decision.defer`;
|
|
251
|
+
7. prepare a compact checkpoint only when a material decision cluster changes a
|
|
252
|
+
durable contract and interruption recovery needs an artifact-bound boundary.
|
|
253
|
+
|
|
254
|
+
`ultra-think` owns this interaction protocol. `ultra-research`, `ultra-change`, and
|
|
255
|
+
`ultra-plan` invoke it at their decision boundaries. `ultra-dev`, `ultra-test`,
|
|
256
|
+
`ultra-review`, and `ultra-deliver` stop and route back to the same thread when new
|
|
257
|
+
owner authority is required; they never answer on the owner's behalf. `ultra-status`
|
|
258
|
+
shows only the current decision and transition set, while `ultra-doctor` diagnoses
|
|
259
|
+
stale checkpoint artifacts and recovery state.
|
|
260
|
+
|
|
261
|
+
When only a bound artifact changes, the same resolved thread is prepared and approved
|
|
262
|
+
again against the current digest; this is checkpoint renewal, not a fabricated decision
|
|
263
|
+
revision. When the owner choice itself changes, supersession preserves the old item and
|
|
264
|
+
opens one replacement.
|
|
265
|
+
|
|
266
|
+
## 6. Context and prompt follow-through
|
|
267
|
+
|
|
268
|
+
`change.context` compiles an immutable Context Manifest v3 snapshot. It combines:
|
|
163
269
|
|
|
164
270
|
- the bound baseline/change/task identity;
|
|
165
271
|
- a role and lifecycle gate;
|
|
@@ -167,13 +273,14 @@ emitted by their owning DB mutation and never act as a substitute state transiti
|
|
|
167
273
|
- local file hashes, current HEAD, branch, and worktree digest;
|
|
168
274
|
- optional metadata-only external memory or graph references;
|
|
169
275
|
- advisory file/token/context-share budgets;
|
|
170
|
-
-
|
|
276
|
+
- the current Change and task authority digests;
|
|
277
|
+
- readiness blockers, `allowed_transitions`, and a `required_transition` only for a
|
|
278
|
+
unique hard-recovery path.
|
|
171
279
|
|
|
172
280
|
Critical stage workflows bind a matching snapshot as a durable output:
|
|
173
281
|
|
|
174
282
|
| Workflow | Required role | Required gate |
|
|
175
283
|
|---|---|---|
|
|
176
|
-
| `change` | `plan` or `implement` | `planning` or `implementation` |
|
|
177
284
|
| `dev` | `implement` | `implementation` |
|
|
178
285
|
| `test` | `check` | `verification` |
|
|
179
286
|
| `review` | `review` | `review` |
|
|
@@ -182,13 +289,18 @@ Critical stage workflows bind a matching snapshot as a durable output:
|
|
|
182
289
|
The snapshot path and digest are then carried into test, review, and delivery
|
|
183
290
|
reports. This prevents a later Prompt from claiming evidence gathered under a
|
|
184
291
|
different context. File and token budgets warn; they do not block legitimate
|
|
185
|
-
work. Missing authority, required refs, task contracts, outputs,
|
|
292
|
+
work. Missing authority, required refs, task contracts, outputs, required owner
|
|
293
|
+
decisions, or
|
|
186
294
|
current gate evidence do block.
|
|
187
295
|
|
|
188
|
-
##
|
|
296
|
+
## 7. Invalidation rules
|
|
189
297
|
|
|
190
298
|
- Changing a task contract invalidates contexts and gates that depended on the
|
|
191
299
|
former contract.
|
|
300
|
+
- Changing accepted Change intent, contract, classification, research disposition,
|
|
301
|
+
provider evidence, or documentation impact marks all derived tasks stale and
|
|
302
|
+
invalidates compiled contexts. Planning or task reconciliation must explicitly
|
|
303
|
+
clear staleness before execution.
|
|
192
304
|
- Changing required source/spec content invalidates the recorded output or
|
|
193
305
|
context digest.
|
|
194
306
|
- Changing a research semantic source invalidates that workflow even when its immutable
|
|
@@ -204,27 +316,33 @@ current gate evidence do block.
|
|
|
204
316
|
their own recorded SHA-256 digests are still authoritative.
|
|
205
317
|
- Generated `tasks.json` and task-context headers are projections. Manual edits
|
|
206
318
|
are overwritten and never repair DB authority.
|
|
319
|
+
- A changed checkpoint artifact digest or superseded decision invalidates the linked
|
|
320
|
+
decision checkpoint and blocks matching workflow advancement until reconfirmed.
|
|
207
321
|
|
|
208
|
-
##
|
|
322
|
+
## 8. Entry flows
|
|
209
323
|
|
|
210
324
|
### New project
|
|
211
325
|
|
|
212
326
|
```text
|
|
213
|
-
ultra-init ->
|
|
214
|
-
->
|
|
327
|
+
ultra-init -> Git/scaffold/authority verification -> init completed
|
|
328
|
+
-> explicit ultra-research -> disposition 17 coverage areas -> selected evidence work
|
|
329
|
+
-> local checkpoint when Git is unborn -> baseline approval
|
|
330
|
+
-> ultra-change -> optional bounded research/plan -> dev -> risk-selected test/review
|
|
331
|
+
-> local deliver/archive
|
|
215
332
|
```
|
|
216
333
|
|
|
217
334
|
### Existing project
|
|
218
335
|
|
|
219
336
|
```text
|
|
220
|
-
ultra-init -> brownfield
|
|
221
|
-
->
|
|
222
|
-
->
|
|
337
|
+
ultra-init -> preserve Git/classify brownfield -> init completed
|
|
338
|
+
-> explicit adoption research -> disposition 17 coverage areas
|
|
339
|
+
-> selected characterization/known-red evidence -> gap ledger -> owner approval
|
|
340
|
+
-> first ultra-change -> optional bounded research/plan -> dev -> test/review -> deliver
|
|
223
341
|
```
|
|
224
342
|
|
|
225
343
|
Adoption records the system that exists. It does not force the owner to recreate
|
|
226
344
|
market, persona, or roadmap claims that cannot be supported; those remain
|
|
227
|
-
`Unknown` or explicit gaps
|
|
345
|
+
`Unknown`, `not_applicable`, accepted `deferred`, or explicit gaps with evidence.
|
|
228
346
|
|
|
229
347
|
### Earlier Ultra project
|
|
230
348
|
|
|
@@ -238,8 +356,9 @@ Migration preserves prior files and rows but never carries old approval forward.
|
|
|
238
356
|
### Daily maintenance
|
|
239
357
|
|
|
240
358
|
```text
|
|
241
|
-
ultra-status -> ultra-change
|
|
242
|
-
->
|
|
359
|
+
ultra-status -> ultra-change intent capture
|
|
360
|
+
-> host chooses bounded research, plan, current task, think, or status
|
|
361
|
+
-> dev -> risk-selected test -> review -> deliver -> reconciled baseline
|
|
243
362
|
```
|
|
244
363
|
|
|
245
364
|
Quick changes shorten the artifact burden only where the machine contract allows;
|
|
@@ -247,7 +366,28 @@ they do not skip task ownership, executable acceptance, current checking, review
|
|
|
247
366
|
or baseline reconciliation. Incidents add structured diagnosis and recovery
|
|
248
367
|
evidence rather than bypassing them.
|
|
249
368
|
|
|
250
|
-
|
|
369
|
+
### Command handoff graph
|
|
370
|
+
|
|
371
|
+
| Entry | Owns | Normal handoff |
|
|
372
|
+
|---|---|---|
|
|
373
|
+
| `ultra-init` | classification, scope, Git/scaffold bootstrap, and init verification | stops completed; exposes research/status/doctor and any already-valid change capability |
|
|
374
|
+
| `ultra-research` | coverage disposition, selected evidence acquisition, synthesis, and material research decisions | baseline convergence or return to the owning change |
|
|
375
|
+
| `ultra-think` | one material owner decision or read-only diagnosis | returns to the invoking capability after any needed checkpoint |
|
|
376
|
+
| `ultra-change` | outcome capture, Change Contract, risk/research routing | host selects research, plan, current dev work, think, or status |
|
|
377
|
+
| `ultra-plan` | task contracts, dependencies, acceptance coverage, optional material approval | any dependency-ready task through `ultra-dev` |
|
|
378
|
+
| `ultra-dev` | one task/session vertical slice and focused review | next `ultra-dev` task or aggregate `ultra-test` |
|
|
379
|
+
| `ultra-test` | risk-selected change acceptance and verification profile | `ultra-review` on pass, `ultra-dev`/`ultra-test` on failure |
|
|
380
|
+
| `ultra-review` | independent specification and engineering axes plus risk-selected workers | `ultra-deliver` on approval; otherwise implementation or checking |
|
|
381
|
+
| `ultra-deliver` | convergence, reconciliation, local archive, and delivery evidence | `ultra-status` or the next `ultra-change`; external release is separate |
|
|
382
|
+
| `ultra-status` | read-only authority and transition set | host recommendation among allowed transitions |
|
|
383
|
+
| `ultra-doctor` | read-only diagnosis and authorized mechanical recovery | required recovery only when unique; otherwise safe alternatives |
|
|
384
|
+
|
|
385
|
+
Every row permits `ultra-think` when material owner authority is missing and permits
|
|
386
|
+
`ultra-doctor` when state or installed-runtime authority is unhealthy. MCP makes one
|
|
387
|
+
of them required only when no safe alternative exists. Neither capability creates a
|
|
388
|
+
parallel workflow or substitutes a Prompt summary for DB state.
|
|
389
|
+
|
|
390
|
+
## 9. Recovery and diagnosis
|
|
251
391
|
|
|
252
392
|
Use `system.doctor` for project authority and `ubp --doctor` for installed host
|
|
253
393
|
assets. Both are read-only unless an explicit project repair command is chosen.
|
package/hooks/pre_stop_check.py
CHANGED
|
@@ -35,13 +35,14 @@ def main() -> None:
|
|
|
35
35
|
allow_stop()
|
|
36
36
|
return
|
|
37
37
|
if breadcrumb and breadcrumb.get("change_id"):
|
|
38
|
+
allowed = ", ".join(breadcrumb.get("allowed_transitions") or []) or "none"
|
|
39
|
+
required = breadcrumb.get("required_transition") or "none"
|
|
38
40
|
print(
|
|
39
41
|
"[Ultra stop advisory] Active Ultra change remains open: "
|
|
40
42
|
f"change={breadcrumb['change_id']}, "
|
|
41
43
|
f"task={breadcrumb.get('task_id') or 'none'}, "
|
|
42
44
|
f"gate={breadcrumb.get('gate') or 'alignment'}, "
|
|
43
|
-
f"
|
|
44
|
-
f"Next: {breadcrumb.get('next_action') or 'Inspect Ultra status.'} "
|
|
45
|
+
f"allowed_transitions={allowed}, required_transition={required}. "
|
|
45
46
|
"Stop is allowed; resume or explicitly archive the change later.",
|
|
46
47
|
file=sys.stderr,
|
|
47
48
|
)
|
|
@@ -40,20 +40,33 @@ function gitHead(rootDir) {
|
|
|
40
40
|
return result.status === 0 ? result.stdout.trim() : null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function gitRepositoryRoot(rootDir) {
|
|
44
|
+
const result = spawnSync('git', ['rev-parse', '--show-toplevel'], {
|
|
45
|
+
cwd: rootDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
|
|
46
|
+
});
|
|
47
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
function gitBranch(rootDir) {
|
|
44
|
-
|
|
51
|
+
let result = spawnSync('git', ['symbolic-ref', '--quiet', '--short', 'HEAD'], {
|
|
45
52
|
cwd: rootDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
|
|
46
53
|
});
|
|
54
|
+
if (result.status !== 0) {
|
|
55
|
+
result = spawnSync('git', ['branch', '--show-current'], {
|
|
56
|
+
cwd: rootDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
47
59
|
return result.status === 0 ? (result.stdout.trim() || null) : null;
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
function gitWorktreeSnapshot(rootDir, scope = ['.']) {
|
|
51
|
-
const
|
|
52
|
-
if (!
|
|
63
|
+
const repositoryRoot = gitRepositoryRoot(rootDir);
|
|
64
|
+
if (!repositoryRoot) {
|
|
53
65
|
return {
|
|
54
66
|
head: null, branch: null, state: 'unavailable', digest: null, files: [],
|
|
55
67
|
};
|
|
56
68
|
}
|
|
69
|
+
const head = gitHead(rootDir);
|
|
57
70
|
const selectedScope = Array.isArray(scope) && scope.length > 0 ? scope : ['.'];
|
|
58
71
|
const pathspecs = selectedScope.map((item) => (
|
|
59
72
|
item === '.' ? '.' : `:(literal)${item}`
|
|
@@ -63,10 +76,12 @@ function gitWorktreeSnapshot(rootDir, scope = ['.']) {
|
|
|
63
76
|
'git', ['status', '--porcelain=v1', '-z', '--untracked-files=all', ...args],
|
|
64
77
|
{ cwd: rootDir, encoding: 'buffer', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 32 * 1024 * 1024 },
|
|
65
78
|
);
|
|
66
|
-
const diff =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
const diff = head
|
|
80
|
+
? spawnSync(
|
|
81
|
+
'git', ['diff', '--binary', '--no-ext-diff', 'HEAD', ...args],
|
|
82
|
+
{ cwd: rootDir, encoding: 'buffer', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 32 * 1024 * 1024 },
|
|
83
|
+
)
|
|
84
|
+
: { status: 0, stdout: Buffer.alloc(0) };
|
|
70
85
|
const untracked = spawnSync(
|
|
71
86
|
'git', ['ls-files', '--others', '--exclude-standard', '-z', ...args],
|
|
72
87
|
{ cwd: rootDir, encoding: 'buffer', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 32 * 1024 * 1024 },
|
|
@@ -79,7 +94,7 @@ function gitWorktreeSnapshot(rootDir, scope = ['.']) {
|
|
|
79
94
|
const statusText = status.stdout.toString('utf8');
|
|
80
95
|
const files = statusText.split('\0').filter(Boolean);
|
|
81
96
|
const digest = crypto.createHash('sha256');
|
|
82
|
-
digest.update(head).update('\0').update(gitBranch(rootDir) || '').update('\0');
|
|
97
|
+
digest.update(head || 'unborn').update('\0').update(gitBranch(rootDir) || '').update('\0');
|
|
83
98
|
digest.update(status.stdout).update(diff.stdout);
|
|
84
99
|
for (const relative of untracked.stdout.toString('utf8').split('\0').filter(Boolean).sort()) {
|
|
85
100
|
digest.update(relative).update('\0');
|
|
@@ -93,7 +108,7 @@ function gitWorktreeSnapshot(rootDir, scope = ['.']) {
|
|
|
93
108
|
return {
|
|
94
109
|
head,
|
|
95
110
|
branch: gitBranch(rootDir),
|
|
96
|
-
state: files.length > 0 ? 'dirty' : 'clean',
|
|
111
|
+
state: head ? (files.length > 0 ? 'dirty' : 'clean') : 'unborn',
|
|
97
112
|
digest: digest.digest('hex'),
|
|
98
113
|
files,
|
|
99
114
|
};
|
|
@@ -464,6 +479,55 @@ function startBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = tr
|
|
|
464
479
|
});
|
|
465
480
|
}
|
|
466
481
|
|
|
482
|
+
function refreshInProgressBaseline(db, input = {}, {
|
|
483
|
+
rootDir = process.cwd(), emitEvent = true,
|
|
484
|
+
} = {}) {
|
|
485
|
+
const current = readBaseline(db, input.id);
|
|
486
|
+
if (!current) {
|
|
487
|
+
throw new BaselineWorkflowError('BASELINE_NOT_FOUND', `baseline ${input.id || '(current)'} not found`);
|
|
488
|
+
}
|
|
489
|
+
if (!['draft', 'adopting', 'blocked'].includes(current.status)) return current;
|
|
490
|
+
const projectName = input.project_name === undefined
|
|
491
|
+
? current.project_name
|
|
492
|
+
: nonEmpty(input.project_name, 'project_name');
|
|
493
|
+
const projectType = input.project_type === undefined ? current.project_type : input.project_type;
|
|
494
|
+
const stack = input.stack === undefined ? current.stack : input.stack;
|
|
495
|
+
const classification = input.classification === undefined
|
|
496
|
+
? current.classification
|
|
497
|
+
: normalizeClassification(input.classification);
|
|
498
|
+
const snapshot = gitWorktreeSnapshot(rootDir, current.scope);
|
|
499
|
+
const repositoryRevision = snapshot.head
|
|
500
|
+
|| (snapshot.state === 'unborn' ? null : current.repository_revision);
|
|
501
|
+
const ts = nowIso();
|
|
502
|
+
return ops.tx(db, () => {
|
|
503
|
+
db.prepare(
|
|
504
|
+
`UPDATE baselines SET project_name = ?, project_type = ?, stack = ?,
|
|
505
|
+
repository_revision = ?, repository_branch = ?, worktree_state = ?,
|
|
506
|
+
worktree_digest = ?, worktree_files_json = ?, classification_json = ?,
|
|
507
|
+
updated_at = ? WHERE id = ?`,
|
|
508
|
+
).run(
|
|
509
|
+
projectName, projectType || null, stack || null,
|
|
510
|
+
repositoryRevision, snapshot.branch, snapshot.state, snapshot.digest,
|
|
511
|
+
JSON.stringify(snapshot.files), JSON.stringify(classification), ts, current.id,
|
|
512
|
+
);
|
|
513
|
+
if (emitEvent) {
|
|
514
|
+
ops.appendEventInTx(db, {
|
|
515
|
+
type: 'baseline_metadata_refreshed',
|
|
516
|
+
payload: {
|
|
517
|
+
baseline_id: current.id,
|
|
518
|
+
project_name: projectName,
|
|
519
|
+
project_type: projectType || null,
|
|
520
|
+
stack: stack || null,
|
|
521
|
+
repository_revision: repositoryRevision,
|
|
522
|
+
repository_branch: snapshot.branch,
|
|
523
|
+
worktree_state: snapshot.state,
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return readBaseline(db, current.id);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
467
531
|
function recordBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = true } = {}) {
|
|
468
532
|
const current = readBaseline(db, input.id);
|
|
469
533
|
if (!current) throw new BaselineWorkflowError('BASELINE_NOT_FOUND', `baseline ${input.id} not found`);
|
|
@@ -484,6 +548,12 @@ function recordBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent = t
|
|
|
484
548
|
? current.provider_refs
|
|
485
549
|
: providerRefs.normalizeProviderRefs(input.provider_refs, BaselineWorkflowError);
|
|
486
550
|
const snapshot = gitWorktreeSnapshot(rootDir, scope);
|
|
551
|
+
if (snapshot.state === 'unborn') {
|
|
552
|
+
throw new BaselineWorkflowError(
|
|
553
|
+
'BASELINE_GIT_HEAD_REQUIRED',
|
|
554
|
+
'Git is initialized but has no commit; create an owner-authorized local checkpoint commit before recording the baseline',
|
|
555
|
+
);
|
|
556
|
+
}
|
|
487
557
|
if (snapshot.head && input.repository_revision && input.repository_revision !== snapshot.head) {
|
|
488
558
|
throw new BaselineWorkflowError(
|
|
489
559
|
'BASELINE_REVISION_MISMATCH', 'repository_revision does not match current Git HEAD',
|
|
@@ -667,6 +737,7 @@ function convergenceBlockers(db, baseline, input, rootDir) {
|
|
|
667
737
|
if (!expected) blockers.add('BASELINE_REVISION_REQUIRED');
|
|
668
738
|
else if (baseline.repository_revision !== expected) blockers.add('BASELINE_REVISION_MISMATCH');
|
|
669
739
|
const snapshot = gitWorktreeSnapshot(rootDir, baseline.scope);
|
|
740
|
+
if (snapshot.state === 'unborn') blockers.add('BASELINE_GIT_HEAD_REQUIRED');
|
|
670
741
|
if (snapshot.head && expected && snapshot.head !== expected) blockers.add('BASELINE_HEAD_STALE');
|
|
671
742
|
if (baseline.repository_branch && snapshot.branch
|
|
672
743
|
&& baseline.repository_branch !== snapshot.branch) blockers.add('BASELINE_BRANCH_STALE');
|
|
@@ -725,7 +796,6 @@ function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent =
|
|
|
725
796
|
type: 'baseline_blocked', payload: { baseline_id: current.id, blockers },
|
|
726
797
|
});
|
|
727
798
|
}
|
|
728
|
-
workflows.blockInitializationInTx(db, current.id, blockers);
|
|
729
799
|
});
|
|
730
800
|
return { ready: false, status: 'blocked', blockers, baseline: readBaseline(db, current.id) };
|
|
731
801
|
}
|
|
@@ -745,12 +815,6 @@ function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent =
|
|
|
745
815
|
type: 'baseline_converged', payload: { baseline_id: current.id, revision: current.repository_revision },
|
|
746
816
|
});
|
|
747
817
|
}
|
|
748
|
-
workflows.completeInitializationInTx(db, {
|
|
749
|
-
...current,
|
|
750
|
-
approved_by: String(input.approved_by).trim(),
|
|
751
|
-
research_run_id: research.id,
|
|
752
|
-
converged_at: ts,
|
|
753
|
-
});
|
|
754
818
|
});
|
|
755
819
|
return { ready: true, status: 'ready', blockers: [], baseline: readBaseline(db, current.id) };
|
|
756
820
|
}
|
|
@@ -786,6 +850,9 @@ function inspectBaseline(db, { rootDir = process.cwd(), id } = {}) {
|
|
|
786
850
|
const snapshot = gitWorktreeSnapshot(
|
|
787
851
|
rootDir, Array.isArray(baseline.scope) && baseline.scope.length > 0 ? baseline.scope : ['.'],
|
|
788
852
|
);
|
|
853
|
+
if (snapshot.state === 'unborn') {
|
|
854
|
+
blockers.push('BASELINE_GIT_HEAD_REQUIRED');
|
|
855
|
+
}
|
|
789
856
|
if (snapshot.head && baseline.repository_revision && snapshot.head !== baseline.repository_revision) {
|
|
790
857
|
blockers.push('BASELINE_HEAD_STALE');
|
|
791
858
|
} else if (!snapshot.head && baseline.repository_revision?.startsWith('workspace:')) {
|
|
@@ -905,6 +972,7 @@ function appendGap(db, input = {}, options = {}) {
|
|
|
905
972
|
module.exports = {
|
|
906
973
|
BaselineWorkflowError,
|
|
907
974
|
startBaseline,
|
|
975
|
+
refreshInProgressBaseline,
|
|
908
976
|
recordBaseline,
|
|
909
977
|
convergeBaseline,
|
|
910
978
|
readBaseline,
|