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
|
@@ -9,9 +9,9 @@ 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
|
|
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
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 |
|
|
@@ -20,7 +20,8 @@ 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.
|
|
24
25
|
|
|
25
26
|
Load-bearing owner choices are normalized through `decision.*`; Prompt prose is not
|
|
26
27
|
durable authority. The host acquires observable facts autonomously, exposes one
|
|
@@ -34,10 +35,12 @@ authority, and no new stage may start on an archived or cancelled change. Doctor
|
|
|
34
35
|
recovers a legacy change without this link into an explicitly blocked workflow;
|
|
35
36
|
it does not fabricate the missing provenance.
|
|
36
37
|
|
|
37
|
-
Ultra does not select an MVP. Product scope and delivery posture are owner
|
|
38
|
-
decisions.
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
41
44
|
|
|
42
45
|
## 2. How `ultra-init` classifies a repository
|
|
43
46
|
|
|
@@ -65,9 +68,22 @@ worktree digests and accepted dirty files are restricted to that scope.
|
|
|
65
68
|
|
|
66
69
|
Classification and readiness are separate. Initialization immediately creates
|
|
67
70
|
or resumes `.ultra/state.db`, installs only the required scaffold, records the
|
|
68
|
-
classification,
|
|
69
|
-
|
|
70
|
-
|
|
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.
|
|
71
87
|
|
|
72
88
|
## 3. Durable status machines
|
|
73
89
|
|
|
@@ -124,6 +140,40 @@ traceability, slice kind, public seam, exact verification command, acceptance
|
|
|
124
140
|
checks, context references, resolved documentation impact, owner, and valid
|
|
125
141
|
dependencies.
|
|
126
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
|
+
|
|
127
177
|
### Workflow run and step
|
|
128
178
|
|
|
129
179
|
```text
|
|
@@ -160,19 +210,19 @@ case. It never stores the conversation transcript.
|
|
|
160
210
|
|
|
161
211
|
## 4. Write and verification lifecycle
|
|
162
212
|
|
|
163
|
-
| Stage | Authoritative writes | Completion verification |
|
|
213
|
+
| Stage | Authoritative writes | Completion verification | Available continuation |
|
|
164
214
|
|---|---|---|---|
|
|
165
|
-
| `init` | Baseline classification/scope,
|
|
166
|
-
| `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 |
|
|
167
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 |
|
|
168
|
-
| `change` |
|
|
169
|
-
| `plan` |
|
|
170
|
-
| `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` |
|
|
171
|
-
| `test` | Immutable checking context and `.ultra/reports/tests/<workflow-id>.json` |
|
|
172
|
-
| `review` | Immutable review context, `task`/`change`/`plan` mode, worker
|
|
173
|
-
| `deliver` | Convergence context, verified learning resolutions,
|
|
174
|
-
| `status` | None | Reads doctor, baseline, workflows, breadcrumb, tasks, sessions, Git, and DB-referenced reports |
|
|
175
|
-
| `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 |
|
|
176
226
|
|
|
177
227
|
Every mutating MCP operation appends an event and enqueues projection work when
|
|
178
228
|
the corresponding read-only view can change. Projection failure is recorded as
|
|
@@ -183,28 +233,30 @@ emitted by their owning DB mutation and never act as a substitute state transiti
|
|
|
183
233
|
## 5. Human-agent alignment lifecycle
|
|
184
234
|
|
|
185
235
|
The host first reads `decision.list` and resumes matching authority. It starts a new
|
|
186
|
-
thread only for a new decision cluster.
|
|
236
|
+
thread only for a new material decision cluster. Before asking, it must:
|
|
187
237
|
|
|
188
238
|
1. inspect repository, runtime, tests, specifications, and primary-source evidence;
|
|
189
239
|
2. resolve evidence-answerable facts without asking the owner;
|
|
190
|
-
3.
|
|
191
|
-
|
|
192
|
-
4.
|
|
193
|
-
|
|
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
|
|
194
249
|
delegation with `decision.delegate`, or a consequence-bearing deferral with
|
|
195
250
|
`decision.defer`;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
change-bound threads must bind an artifact. If a shared specification will keep
|
|
199
|
-
evolving, bind a stable per-thread decision projection rather than the mutable
|
|
200
|
-
working draft.
|
|
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.
|
|
201
253
|
|
|
202
254
|
`ultra-think` owns this interaction protocol. `ultra-research`, `ultra-change`, and
|
|
203
255
|
`ultra-plan` invoke it at their decision boundaries. `ultra-dev`, `ultra-test`,
|
|
204
256
|
`ultra-review`, and `ultra-deliver` stop and route back to the same thread when new
|
|
205
257
|
owner authority is required; they never answer on the owner's behalf. `ultra-status`
|
|
206
|
-
shows only the current decision and
|
|
207
|
-
checkpoint artifacts and recovery state.
|
|
258
|
+
shows only the current decision and transition set, while `ultra-doctor` diagnoses
|
|
259
|
+
stale checkpoint artifacts and recovery state.
|
|
208
260
|
|
|
209
261
|
When only a bound artifact changes, the same resolved thread is prepared and approved
|
|
210
262
|
again against the current digest; this is checkpoint renewal, not a fabricated decision
|
|
@@ -213,7 +265,7 @@ opens one replacement.
|
|
|
213
265
|
|
|
214
266
|
## 6. Context and prompt follow-through
|
|
215
267
|
|
|
216
|
-
`change.context` compiles an immutable Context Manifest
|
|
268
|
+
`change.context` compiles an immutable Context Manifest v3 snapshot. It combines:
|
|
217
269
|
|
|
218
270
|
- the bound baseline/change/task identity;
|
|
219
271
|
- a role and lifecycle gate;
|
|
@@ -221,13 +273,14 @@ opens one replacement.
|
|
|
221
273
|
- local file hashes, current HEAD, branch, and worktree digest;
|
|
222
274
|
- optional metadata-only external memory or graph references;
|
|
223
275
|
- advisory file/token/context-share budgets;
|
|
224
|
-
-
|
|
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.
|
|
225
279
|
|
|
226
280
|
Critical stage workflows bind a matching snapshot as a durable output:
|
|
227
281
|
|
|
228
282
|
| Workflow | Required role | Required gate |
|
|
229
283
|
|---|---|---|
|
|
230
|
-
| `change` | `plan` or `implement` | `planning` or `implementation` |
|
|
231
284
|
| `dev` | `implement` | `implementation` |
|
|
232
285
|
| `test` | `check` | `verification` |
|
|
233
286
|
| `review` | `review` | `review` |
|
|
@@ -236,13 +289,18 @@ Critical stage workflows bind a matching snapshot as a durable output:
|
|
|
236
289
|
The snapshot path and digest are then carried into test, review, and delivery
|
|
237
290
|
reports. This prevents a later Prompt from claiming evidence gathered under a
|
|
238
291
|
different context. File and token budgets warn; they do not block legitimate
|
|
239
|
-
work. Missing authority, required refs, task contracts, outputs,
|
|
292
|
+
work. Missing authority, required refs, task contracts, outputs, required owner
|
|
293
|
+
decisions, or
|
|
240
294
|
current gate evidence do block.
|
|
241
295
|
|
|
242
296
|
## 7. Invalidation rules
|
|
243
297
|
|
|
244
298
|
- Changing a task contract invalidates contexts and gates that depended on the
|
|
245
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.
|
|
246
304
|
- Changing required source/spec content invalidates the recorded output or
|
|
247
305
|
context digest.
|
|
248
306
|
- Changing a research semantic source invalidates that workflow even when its immutable
|
|
@@ -266,21 +324,25 @@ current gate evidence do block.
|
|
|
266
324
|
### New project
|
|
267
325
|
|
|
268
326
|
```text
|
|
269
|
-
ultra-init ->
|
|
270
|
-
->
|
|
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
|
|
271
332
|
```
|
|
272
333
|
|
|
273
334
|
### Existing project
|
|
274
335
|
|
|
275
336
|
```text
|
|
276
|
-
ultra-init ->
|
|
277
|
-
->
|
|
278
|
-
->
|
|
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
|
|
279
341
|
```
|
|
280
342
|
|
|
281
343
|
Adoption records the system that exists. It does not force the owner to recreate
|
|
282
344
|
market, persona, or roadmap claims that cannot be supported; those remain
|
|
283
|
-
`Unknown` or explicit gaps
|
|
345
|
+
`Unknown`, `not_applicable`, accepted `deferred`, or explicit gaps with evidence.
|
|
284
346
|
|
|
285
347
|
### Earlier Ultra project
|
|
286
348
|
|
|
@@ -294,8 +356,9 @@ Migration preserves prior files and rows but never carries old approval forward.
|
|
|
294
356
|
### Daily maintenance
|
|
295
357
|
|
|
296
358
|
```text
|
|
297
|
-
ultra-status -> ultra-change capture
|
|
298
|
-
->
|
|
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
|
|
299
362
|
```
|
|
300
363
|
|
|
301
364
|
Quick changes shorten the artifact burden only where the machine contract allows;
|
|
@@ -307,21 +370,22 @@ evidence rather than bypassing them.
|
|
|
307
370
|
|
|
308
371
|
| Entry | Owns | Normal handoff |
|
|
309
372
|
|---|---|---|
|
|
310
|
-
| `ultra-init` | classification, scope, scaffold,
|
|
311
|
-
| `ultra-research` | evidence acquisition,
|
|
312
|
-
| `ultra-think` | one
|
|
313
|
-
| `ultra-change` | outcome capture,
|
|
314
|
-
| `ultra-plan` |
|
|
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` |
|
|
315
378
|
| `ultra-dev` | one task/session vertical slice and focused review | next `ultra-dev` task or aggregate `ultra-test` |
|
|
316
|
-
| `ultra-test` |
|
|
317
|
-
| `ultra-review` | independent specification and engineering
|
|
318
|
-
| `ultra-deliver` | convergence, reconciliation, archive,
|
|
319
|
-
| `ultra-status` | read-only
|
|
320
|
-
| `ultra-doctor` | read-only diagnosis and authorized mechanical recovery |
|
|
321
|
-
|
|
322
|
-
Every row
|
|
323
|
-
`ultra-doctor` when state or installed-runtime authority is unhealthy.
|
|
324
|
-
|
|
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.
|
|
325
389
|
|
|
326
390
|
## 9. Recovery and diagnosis
|
|
327
391
|
|
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,
|