ultra-builder-pro-cli 0.15.0 → 0.16.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/CHANGELOG.md +34 -1
- package/README.md +18 -11
- package/adapters/_shared/handbook.cjs +2 -1
- package/commands/ultra-change.md +1 -1
- package/commands/ultra-plan.md +1 -1
- package/commands/ultra-research.md +1 -1
- package/commands/ultra-think.md +2 -2
- package/docs/AGENT-CONTEXT.md +30 -13
- package/docs/ARCHITECTURE.md +24 -4
- package/docs/PLAN.zh-CN.md +3 -2
- 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 +89 -13
- package/mcp-server/lib/change-workflow.cjs +52 -4
- package/mcp-server/lib/context-spine.cjs +65 -3
- package/mcp-server/lib/decision-dialogue.cjs +713 -0
- package/mcp-server/lib/doctor.cjs +9 -0
- package/mcp-server/lib/project-breadcrumb.cjs +9 -0
- package/mcp-server/lib/state-db.cjs +29 -6
- package/mcp-server/lib/workflow-state.cjs +13 -0
- package/mcp-server/server.cjs +54 -0
- package/package.json +1 -1
- package/skills/ultra-change/SKILL.md +41 -12
- package/skills/ultra-deliver/SKILL.md +10 -3
- package/skills/ultra-dev/SKILL.md +11 -3
- package/skills/ultra-doctor/SKILL.md +10 -0
- package/skills/ultra-init/SKILL.md +6 -1
- package/skills/ultra-plan/SKILL.md +25 -5
- package/skills/ultra-research/SKILL.md +38 -23
- package/skills/ultra-review/SKILL.md +9 -3
- package/skills/ultra-status/SKILL.md +8 -4
- package/skills/ultra-test/SKILL.md +9 -3
- package/skills/ultra-think/SKILL.md +26 -12
- package/skills/ultra-think/references/decision-dialogue.md +130 -0
- package/spec/cli-protocol.md +11 -2
- package/spec/mcp-tools.yaml +299 -1
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +58 -0
- package/ultra-tools/cli.cjs +1 -1
- package/ultra-tools/commands/status.cjs +15 -0
|
@@ -12,7 +12,7 @@ host and project owner. It is the operational companion to
|
|
|
12
12
|
| Active host and owner | Product judgment, research content, implementation, review findings, release authorization | 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
14
|
| MCP | IDs, transitions, task contracts, evidence references, output hashes, gate verdicts, next action | Research prose, code generation, memory, or model calls |
|
|
15
|
-
| `.ultra/state.db` | Baseline, change, task, workflow, session, event, incident, projection, and evidence authority |
|
|
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
|
|
|
@@ -22,6 +22,12 @@ inputs, hashes outputs, and derives status. Prompt input cannot override a task'
|
|
|
22
22
|
public seam, verification command, context references, workflow summary, gate
|
|
23
23
|
verdict, output digest, or next action.
|
|
24
24
|
|
|
25
|
+
Load-bearing owner choices are normalized through `decision.*`; Prompt prose is not
|
|
26
|
+
durable authority. The host acquires observable facts autonomously, exposes one
|
|
27
|
+
current question, and stops. A workflow cannot advance while a matching decision
|
|
28
|
+
thread is active, waiting for checkpoint confirmation, or carrying a blocking
|
|
29
|
+
deferral.
|
|
30
|
+
|
|
25
31
|
Every change-bound research, plan, dev, test, review, and deliver run inherits
|
|
26
32
|
the baseline id from the owning change workflow. A Prompt cannot switch that
|
|
27
33
|
authority, and no new stage may start on an archived or cancelled change. Doctor
|
|
@@ -135,15 +141,32 @@ output cannot complete without it. When the final required step completes, the
|
|
|
135
141
|
run becomes `ready`; `workflow.complete` revalidates the entire stage before
|
|
136
142
|
making it `completed`.
|
|
137
143
|
|
|
144
|
+
### Decision thread and item
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
thread: active -> checkpoint_ready -> confirmed
|
|
148
|
+
^ |
|
|
149
|
+
|--------------+ revision/supersession
|
|
150
|
+
|
|
151
|
+
item: open -> answered | delegated | deferred | superseded
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A thread is bound to baseline, change, or workflow authority. A partial unique index
|
|
155
|
+
allows only one `open` item per thread. Resolved items remain immutable history;
|
|
156
|
+
changed evidence or intent uses `decision.supersede` and reopens the checkpoint.
|
|
157
|
+
`decision.checkpoint` first freezes a normalized decision digest, then binds explicit
|
|
158
|
+
owner approval to current artifact digests or a justified standalone no-artifact
|
|
159
|
+
case. It never stores the conversation transcript.
|
|
160
|
+
|
|
138
161
|
## 4. Write and verification lifecycle
|
|
139
162
|
|
|
140
163
|
| Stage | Authoritative writes | Completion verification | Next route |
|
|
141
164
|
|---|---|---|---|
|
|
142
165
|
| `init` | Baseline classification/scope, init run, research run, initialization event, projection job | DB/schema opens, scaffold/projection succeeds; baseline convergence later completes init | `research`, re-adoption, or `change` for an already-ready baseline |
|
|
143
|
-
| `research` | Ordered step evidence,
|
|
166
|
+
| `research` | Ordered step evidence, decision threads/checkpoints, typed semantic records with source digests, one immutable report per selected step, output paths and SHA-256 digests | Every load-bearing owner decision is confirmed; every report has the required structure; semantic kinds/attributes/source anchors are valid and current; full/adoption synthesis binds the current three specifications and distillate; all digests remain current | `baseline.record`, then approval/convergence; bounded change research returns to its change |
|
|
144
167
|
| 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` |
|
|
146
|
-
| `plan` | Plan-step evidence, complete change-owned task rows, acceptance coverage matrix, change-bound `.ultra/execution-plan.json`, explicit plan approval |
|
|
168
|
+
| `change` | Baseline-bound alignment checkpoint, complete Change Contract, profile/risk rationale, research disposition, intent/delta artifacts, linked change run, initial context snapshot | The linked alignment thread is confirmed; selected change research is current; current plan task set matches DB; task-bound context is ready; one next action is derived | selected `research`, then `plan`, or `dev` for an already approved current plan |
|
|
169
|
+
| `plan` | Plan decision checkpoint, plan-step evidence, complete change-owned task rows, acceptance coverage matrix, change-bound `.ultra/execution-plan.json`, explicit plan approval | Blocking planning choices are confirmed; every Change acceptance id is owned by a task; task trace targets resolve; quick has one task; ownership/dependencies valid; no cycles; exported topology and digest exactly match the current change task set | `dev` for the first dependency-ready task |
|
|
147
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` |
|
|
148
171
|
| `test` | Immutable checking context and `.ultra/reports/tests/<workflow-id>.json` | Report change/task set, full HEAD, worktree digest, context digest, commands, public seam, blockers, and all acceptance/regression/integration/static/build/performance/security/recovery dimensions agree; every omission has a not-applicable rationale | `review` when passing, otherwise resume `dev`/`test` |
|
|
149
172
|
| `review` | Immutable review context, `task`/`change`/`plan` mode, worker selection provenance, two specialist axes, coordinated summary | Current diff/HEAD and context match; selected/skipped workers match recorded rationale; both axes complete; P0/P1 and axis verdicts deterministically derive final verdict | `deliver` only from approved `change` mode, otherwise resume implementation |
|
|
@@ -157,7 +180,38 @@ a retryable incident; it does not silently convert a failed write into success.
|
|
|
157
180
|
`task.append_event` accepts only non-authoritative observations. Lifecycle events are
|
|
158
181
|
emitted by their owning DB mutation and never act as a substitute state transition.
|
|
159
182
|
|
|
160
|
-
## 5.
|
|
183
|
+
## 5. Human-agent alignment lifecycle
|
|
184
|
+
|
|
185
|
+
The host first reads `decision.list` and resumes matching authority. It starts a new
|
|
186
|
+
thread only for a new decision cluster. For every current decision it must:
|
|
187
|
+
|
|
188
|
+
1. inspect repository, runtime, tests, specifications, and primary-source evidence;
|
|
189
|
+
2. resolve evidence-answerable facts without asking the owner;
|
|
190
|
+
3. call `decision.open` for the earliest unresolved owner choice with a recommendation,
|
|
191
|
+
no more than three credible alternatives, evidence refs, and durable effects;
|
|
192
|
+
4. present that one choice and end the turn;
|
|
193
|
+
5. normalize the next owner response with `decision.resolve`, explicit reversible
|
|
194
|
+
delegation with `decision.delegate`, or a consequence-bearing deferral with
|
|
195
|
+
`decision.defer`;
|
|
196
|
+
6. prepare one compact checkpoint, obtain one owner approval, update the affected
|
|
197
|
+
artifact, and confirm the checkpoint with its current digest. Workflow- and
|
|
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.
|
|
201
|
+
|
|
202
|
+
`ultra-think` owns this interaction protocol. `ultra-research`, `ultra-change`, and
|
|
203
|
+
`ultra-plan` invoke it at their decision boundaries. `ultra-dev`, `ultra-test`,
|
|
204
|
+
`ultra-review`, and `ultra-deliver` stop and route back to the same thread when new
|
|
205
|
+
owner authority is required; they never answer on the owner's behalf. `ultra-status`
|
|
206
|
+
shows only the current decision and exact route, while `ultra-doctor` diagnoses stale
|
|
207
|
+
checkpoint artifacts and recovery state.
|
|
208
|
+
|
|
209
|
+
When only a bound artifact changes, the same resolved thread is prepared and approved
|
|
210
|
+
again against the current digest; this is checkpoint renewal, not a fabricated decision
|
|
211
|
+
revision. When the owner choice itself changes, supersession preserves the old item and
|
|
212
|
+
opens one replacement.
|
|
213
|
+
|
|
214
|
+
## 6. Context and prompt follow-through
|
|
161
215
|
|
|
162
216
|
`change.context` compiles an immutable Context Manifest v2 snapshot. It combines:
|
|
163
217
|
|
|
@@ -185,7 +239,7 @@ different context. File and token budgets warn; they do not block legitimate
|
|
|
185
239
|
work. Missing authority, required refs, task contracts, outputs, approval, or
|
|
186
240
|
current gate evidence do block.
|
|
187
241
|
|
|
188
|
-
##
|
|
242
|
+
## 7. Invalidation rules
|
|
189
243
|
|
|
190
244
|
- Changing a task contract invalidates contexts and gates that depended on the
|
|
191
245
|
former contract.
|
|
@@ -204,22 +258,24 @@ current gate evidence do block.
|
|
|
204
258
|
their own recorded SHA-256 digests are still authoritative.
|
|
205
259
|
- Generated `tasks.json` and task-context headers are projections. Manual edits
|
|
206
260
|
are overwritten and never repair DB authority.
|
|
261
|
+
- A changed checkpoint artifact digest or superseded decision invalidates the linked
|
|
262
|
+
decision checkpoint and blocks matching workflow advancement until reconfirmed.
|
|
207
263
|
|
|
208
|
-
##
|
|
264
|
+
## 8. Entry flows
|
|
209
265
|
|
|
210
266
|
### New project
|
|
211
267
|
|
|
212
268
|
```text
|
|
213
|
-
ultra-init -> full research (17 steps) -> baseline approval
|
|
214
|
-
-> initial change
|
|
269
|
+
ultra-init -> full research (17 steps with one-decision checkpoints) -> baseline approval
|
|
270
|
+
-> initial change alignment -> plan checkpoint -> dev per task -> test -> review -> deliver
|
|
215
271
|
```
|
|
216
272
|
|
|
217
273
|
### Existing project
|
|
218
274
|
|
|
219
275
|
```text
|
|
220
|
-
ultra-init -> brownfield adoption research (17 steps)
|
|
276
|
+
ultra-init -> brownfield adoption research (17 steps with one-decision checkpoints)
|
|
221
277
|
-> characterization and known-red evidence -> gap ledger -> owner approval
|
|
222
|
-
-> first selected change -> plan -> dev -> test -> review -> deliver
|
|
278
|
+
-> first selected change alignment -> plan checkpoint -> dev -> test -> review -> deliver
|
|
223
279
|
```
|
|
224
280
|
|
|
225
281
|
Adoption records the system that exists. It does not force the owner to recreate
|
|
@@ -238,7 +294,7 @@ Migration preserves prior files and rows but never carries old approval forward.
|
|
|
238
294
|
### Daily maintenance
|
|
239
295
|
|
|
240
296
|
```text
|
|
241
|
-
ultra-status -> ultra-change -> plan if task contracts are absent
|
|
297
|
+
ultra-status -> ultra-change capture/alignment -> plan if task contracts are absent
|
|
242
298
|
-> dev -> test -> review -> deliver -> reconciled baseline
|
|
243
299
|
```
|
|
244
300
|
|
|
@@ -247,7 +303,27 @@ they do not skip task ownership, executable acceptance, current checking, review
|
|
|
247
303
|
or baseline reconciliation. Incidents add structured diagnosis and recovery
|
|
248
304
|
evidence rather than bypassing them.
|
|
249
305
|
|
|
250
|
-
|
|
306
|
+
### Command handoff graph
|
|
307
|
+
|
|
308
|
+
| Entry | Owns | Normal handoff |
|
|
309
|
+
|---|---|---|
|
|
310
|
+
| `ultra-init` | classification, scope, scaffold, baseline/run creation | `ultra-research`; an already-ready project routes to `ultra-change` |
|
|
311
|
+
| `ultra-research` | evidence acquisition, seventeen-step semantic coverage, research decisions | baseline approval then `ultra-change`; bounded research returns to `ultra-plan` |
|
|
312
|
+
| `ultra-think` | one current owner decision or read-only diagnosis | exact invoking workflow and step after checkpoint confirmation |
|
|
313
|
+
| `ultra-change` | outcome capture, alignment, Change Contract, risk/research routing | selected `ultra-research`, then `ultra-plan`; approved existing plan may route to `ultra-dev` |
|
|
314
|
+
| `ultra-plan` | plan decisions, task contracts, dependencies, approval | first ready task through `ultra-dev` |
|
|
315
|
+
| `ultra-dev` | one task/session vertical slice and focused review | next `ultra-dev` task or aggregate `ultra-test` |
|
|
316
|
+
| `ultra-test` | change-level acceptance and regression gate | `ultra-review` on pass, `ultra-dev` on implementation failure |
|
|
317
|
+
| `ultra-review` | independent specification and engineering verdicts | `ultra-deliver` on approval; otherwise `ultra-dev` or `ultra-test` |
|
|
318
|
+
| `ultra-deliver` | convergence, reconciliation, archive, authorized release evidence | `ultra-status` or the next `ultra-change` |
|
|
319
|
+
| `ultra-status` | read-only authoritative position and one route | whichever exact workflow owns the current step |
|
|
320
|
+
| `ultra-doctor` | read-only diagnosis and authorized mechanical recovery | exact recovered workflow, `ultra-init`, or `ultra-think` |
|
|
321
|
+
|
|
322
|
+
Every row routes to `ultra-think` when owner authority is missing and to
|
|
323
|
+
`ultra-doctor` when state or installed-runtime authority is unhealthy. Neither route
|
|
324
|
+
creates a parallel workflow or substitutes a Prompt summary for the DB state.
|
|
325
|
+
|
|
326
|
+
## 9. Recovery and diagnosis
|
|
251
327
|
|
|
252
328
|
Use `system.doctor` for project authority and `ubp --doctor` for installed host
|
|
253
329
|
assets. Both are read-only unless an explicit project repair command is chosen.
|
|
@@ -13,6 +13,7 @@ const providerRefs = require('./provider-refs.cjs');
|
|
|
13
13
|
const baselines = require('./baseline-workflow.cjs');
|
|
14
14
|
const archiveJournal = require('./archive-journal.cjs');
|
|
15
15
|
const workflows = require('./workflow-state.cjs');
|
|
16
|
+
const decisions = require('./decision-dialogue.cjs');
|
|
16
17
|
const reconciliationSchema = require('../../spec/schemas/baseline-reconciliation.v1.schema.json');
|
|
17
18
|
|
|
18
19
|
const reconciliationAjv = new Ajv({ allErrors: true, strict: false });
|
|
@@ -437,6 +438,7 @@ function writeIntent(file, change) {
|
|
|
437
438
|
`- Base commit: \`${change.base_commit || 'unavailable'}\``,
|
|
438
439
|
`- Documentation impact: \`${impact.status}\``,
|
|
439
440
|
`- Research disposition: \`${change.research_disposition.status}\``,
|
|
441
|
+
`- Alignment checkpoint: \`${change.alignment_thread_id || 'not-required'}\``,
|
|
440
442
|
'',
|
|
441
443
|
'## Intent',
|
|
442
444
|
'',
|
|
@@ -685,11 +687,46 @@ function createChange(db, input, { rootDir = process.cwd() } = {}) {
|
|
|
685
687
|
'VALIDATION_ERROR', 'baseline_bypass is not valid while the project baseline is healthy',
|
|
686
688
|
);
|
|
687
689
|
}
|
|
690
|
+
const alignmentGate = decisions.decisionGate(
|
|
691
|
+
db, { baseline_id: baselineHealth.baseline?.id || null }, { rootDir },
|
|
692
|
+
);
|
|
693
|
+
if (!alignmentGate.ready) {
|
|
694
|
+
throw new ChangeWorkflowError(
|
|
695
|
+
'CHANGE_ALIGNMENT_REQUIRED',
|
|
696
|
+
`resolve and confirm the active baseline alignment before creating a change: ${alignmentGate.blockers.join(', ')}`,
|
|
697
|
+
{ blockers: alignmentGate.blockers, thread_id: alignmentGate.thread?.id || null },
|
|
698
|
+
);
|
|
699
|
+
}
|
|
688
700
|
const docsImpact = normalizeDocsImpact(input.docs_impact);
|
|
689
701
|
const providers = normalizeProviderRefs(input.provider_refs);
|
|
690
702
|
const contract = normalizeChangeContract(input.contract);
|
|
691
703
|
const classification = normalizeClassification(input.classification, input.kind);
|
|
692
704
|
const researchDisposition = normalizeResearchDisposition(input.research_disposition, input.kind);
|
|
705
|
+
let alignmentThread = null;
|
|
706
|
+
if (input.alignment_thread_id !== undefined) {
|
|
707
|
+
try {
|
|
708
|
+
alignmentThread = decisions.assertConfirmedDecisionCheckpoint(
|
|
709
|
+
db, input.alignment_thread_id, { rootDir, requireArtifact: true },
|
|
710
|
+
);
|
|
711
|
+
} catch (error) {
|
|
712
|
+
throw new ChangeWorkflowError(
|
|
713
|
+
'CHANGE_ALIGNMENT_REQUIRED',
|
|
714
|
+
`alignment_thread_id must name a current artifact-bound checkpoint: ${error.message}`,
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
if (alignmentThread.change_id) {
|
|
718
|
+
throw new ChangeWorkflowError(
|
|
719
|
+
'CHANGE_ALIGNMENT_REQUIRED', `alignment thread already belongs to ${alignmentThread.change_id}`,
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
if (!baselineHealth.baseline?.id || alignmentThread.baseline_id !== baselineHealth.baseline.id
|
|
723
|
+
|| alignmentThread.workflow_run_id) {
|
|
724
|
+
throw new ChangeWorkflowError(
|
|
725
|
+
'CHANGE_ALIGNMENT_REQUIRED',
|
|
726
|
+
'change alignment must be bound only to the current baseline before change creation',
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
693
730
|
const artifactRoot = path.join('.ultra', 'changes', 'active', input.id);
|
|
694
731
|
const artifactDir = path.resolve(rootDir, artifactRoot);
|
|
695
732
|
if (fs.existsSync(artifactDir)) {
|
|
@@ -700,6 +737,7 @@ function createChange(db, input, { rootDir = process.cwd() } = {}) {
|
|
|
700
737
|
id: input.id, title, kind: input.kind, status: 'active',
|
|
701
738
|
intent, docs_impact: docsImpact, provider_refs: providers, baseline_bypass: baselineBypass,
|
|
702
739
|
contract, classification, research_disposition: researchDisposition,
|
|
740
|
+
alignment_thread_id: alignmentThread?.id || null,
|
|
703
741
|
base_commit: input.base_commit || gitHead(rootDir), artifact_root: artifactRoot,
|
|
704
742
|
created_at: ts, updated_at: ts,
|
|
705
743
|
};
|
|
@@ -709,15 +747,22 @@ function createChange(db, input, { rootDir = process.cwd() } = {}) {
|
|
|
709
747
|
`INSERT INTO changes
|
|
710
748
|
(id, title, kind, status, intent, docs_impact_json, provider_refs_json,
|
|
711
749
|
baseline_bypass_json, contract_json, classification_json,
|
|
712
|
-
research_disposition_json, base_commit, artifact_root,
|
|
713
|
-
|
|
750
|
+
research_disposition_json, alignment_thread_id, base_commit, artifact_root,
|
|
751
|
+
created_at, updated_at)
|
|
752
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
714
753
|
).run(
|
|
715
754
|
row.id, row.title, row.kind, row.status, row.intent, JSON.stringify(row.docs_impact),
|
|
716
755
|
JSON.stringify(row.provider_refs), row.baseline_bypass
|
|
717
756
|
? JSON.stringify(row.baseline_bypass) : null,
|
|
718
757
|
JSON.stringify(row.contract), JSON.stringify(row.classification),
|
|
719
|
-
JSON.stringify(row.research_disposition), row.
|
|
758
|
+
JSON.stringify(row.research_disposition), row.alignment_thread_id,
|
|
759
|
+
row.base_commit, row.artifact_root, ts, ts,
|
|
720
760
|
);
|
|
761
|
+
if (row.alignment_thread_id) {
|
|
762
|
+
db.prepare(
|
|
763
|
+
'UPDATE decision_threads SET change_id = ?, updated_at = ? WHERE id = ?',
|
|
764
|
+
).run(row.id, ts, row.alignment_thread_id);
|
|
765
|
+
}
|
|
721
766
|
fs.mkdirSync(artifactDir, { recursive: true });
|
|
722
767
|
const intentPath = path.join(artifactDir, 'intent.md');
|
|
723
768
|
writeIntent(intentPath, row);
|
|
@@ -738,7 +783,10 @@ function createChange(db, input, { rootDir = process.cwd() } = {}) {
|
|
|
738
783
|
}
|
|
739
784
|
ops.appendEventInTx(db, {
|
|
740
785
|
type: 'change_created', change_id: row.id,
|
|
741
|
-
payload: {
|
|
786
|
+
payload: {
|
|
787
|
+
kind: row.kind, artifact_root: row.artifact_root,
|
|
788
|
+
alignment_thread_id: row.alignment_thread_id,
|
|
789
|
+
},
|
|
742
790
|
});
|
|
743
791
|
let workflow = workflows.startWorkflow(db, {
|
|
744
792
|
kind: 'change', baseline_id: baselineHealth.baseline?.id || null,
|
|
@@ -6,6 +6,7 @@ const path = require('node:path');
|
|
|
6
6
|
const { spawnSync } = require('node:child_process');
|
|
7
7
|
const baselines = require('./baseline-workflow.cjs');
|
|
8
8
|
const workflows = require('./workflow-state.cjs');
|
|
9
|
+
const decisions = require('./decision-dialogue.cjs');
|
|
9
10
|
const ops = require('./state-ops.cjs');
|
|
10
11
|
|
|
11
12
|
const ROLES = new Set(['plan', 'implement', 'check', 'review']);
|
|
@@ -452,6 +453,38 @@ function workflowNextAction(run) {
|
|
|
452
453
|
return `Continue ${run.kind} workflow ${run.id} at ${run.current_step}.`;
|
|
453
454
|
}
|
|
454
455
|
|
|
456
|
+
function summarizeDecision(gate) {
|
|
457
|
+
if (!gate?.thread) return null;
|
|
458
|
+
const current = gate.current_decision;
|
|
459
|
+
return {
|
|
460
|
+
thread_id: gate.thread.id,
|
|
461
|
+
status: gate.thread.status,
|
|
462
|
+
mode: gate.thread.mode,
|
|
463
|
+
current: current ? {
|
|
464
|
+
id: current.id,
|
|
465
|
+
phase: current.phase,
|
|
466
|
+
question: current.question,
|
|
467
|
+
why_now: current.why_now,
|
|
468
|
+
recommendation: current.recommendation,
|
|
469
|
+
options: current.options,
|
|
470
|
+
effects: current.effects,
|
|
471
|
+
} : null,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function decisionNextAction(gate) {
|
|
476
|
+
if (gate.blockers?.some((blocker) => blocker.startsWith('DECISION_CHECKPOINT_ARTIFACT_STALE:'))) {
|
|
477
|
+
return `Reprepare and confirm decision checkpoint ${gate.thread?.id || 'for the active alignment thread'} against the current artifact.`;
|
|
478
|
+
}
|
|
479
|
+
if (gate.current_decision) {
|
|
480
|
+
return `Answer decision ${gate.current_decision.id}: ${gate.current_decision.question}`;
|
|
481
|
+
}
|
|
482
|
+
if (gate.thread?.status === 'checkpoint_ready') {
|
|
483
|
+
return `Confirm decision checkpoint ${gate.thread.id} after reviewing its durable effects.`;
|
|
484
|
+
}
|
|
485
|
+
return `Prepare the confirmed decision checkpoint for ${gate.thread?.id || 'the active alignment thread'}.`;
|
|
486
|
+
}
|
|
487
|
+
|
|
455
488
|
function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
456
489
|
const baselineHealth = baselines.inspectBaseline(db, { rootDir });
|
|
457
490
|
const change = id
|
|
@@ -464,6 +497,23 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
464
497
|
}
|
|
465
498
|
if (!change) {
|
|
466
499
|
const baseline = baselineHealth.baseline;
|
|
500
|
+
const decisionGate = decisions.decisionGate(
|
|
501
|
+
db, { baseline_id: baseline?.id || null }, { rootDir },
|
|
502
|
+
);
|
|
503
|
+
if (!decisionGate.ready) {
|
|
504
|
+
return {
|
|
505
|
+
change_id: null, task_id: null, role: 'plan', gate: 'alignment', readiness: 'blocked',
|
|
506
|
+
blockers: [...new Set([...baselineHealth.blockers, ...decisionGate.blockers])],
|
|
507
|
+
warnings: baselineHealth.warnings,
|
|
508
|
+
next_action: decisionNextAction(decisionGate), recommended_workflow: 'ultra-think',
|
|
509
|
+
workflow: null, decision: summarizeDecision(decisionGate),
|
|
510
|
+
context_manifest_path: null, context_manifest_hash: null, git_head: currentHead(rootDir),
|
|
511
|
+
baseline: baseline ? {
|
|
512
|
+
id: baseline.id, mode: baseline.mode, status: baseline.status,
|
|
513
|
+
repository_revision: baseline.repository_revision,
|
|
514
|
+
} : null,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
467
517
|
if (baselineHealth.status !== 'pass') {
|
|
468
518
|
const activeWorkflow = actionableBaselineWorkflow(db, baseline?.id, rootDir);
|
|
469
519
|
const nextAction = activeWorkflow
|
|
@@ -475,7 +525,7 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
475
525
|
change_id: null, task_id: null, role: 'plan', gate: 'alignment', readiness: 'blocked',
|
|
476
526
|
blockers: baselineHealth.blockers, warnings: baselineHealth.warnings, next_action: nextAction,
|
|
477
527
|
recommended_workflow: activeWorkflow ? `ultra-${activeWorkflow.kind}` : 'ultra-init',
|
|
478
|
-
workflow: summarizeWorkflow(activeWorkflow), context_manifest_path: null,
|
|
528
|
+
workflow: summarizeWorkflow(activeWorkflow), decision: null, context_manifest_path: null,
|
|
479
529
|
context_manifest_hash: null, git_head: currentHead(rootDir),
|
|
480
530
|
baseline: baseline ? {
|
|
481
531
|
id: baseline.id, mode: baseline.mode, status: baseline.status,
|
|
@@ -487,6 +537,7 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
487
537
|
change_id: null, task_id: null, role: 'plan', gate: 'alignment', readiness: 'ready',
|
|
488
538
|
next_action: 'Start daily work with ultra-change.', recommended_workflow: 'ultra-change',
|
|
489
539
|
workflow: null,
|
|
540
|
+
decision: null,
|
|
490
541
|
context_manifest_path: null, context_manifest_hash: null, git_head: currentHead(rootDir),
|
|
491
542
|
blockers: [], warnings: baselineHealth.warnings,
|
|
492
543
|
baseline: {
|
|
@@ -527,8 +578,14 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
527
578
|
);
|
|
528
579
|
const blockers = [];
|
|
529
580
|
const baselineGate = baselineGateForChange(db, change, baselineHealth);
|
|
581
|
+
const decisionGate = decisions.decisionGate(db, {
|
|
582
|
+
baseline_id: baselineHealth.baseline?.id || null,
|
|
583
|
+
change_id: change.id,
|
|
584
|
+
workflow_run_id: actionableChangeWorkflow(db, change.id, rootDir)?.id || null,
|
|
585
|
+
}, { rootDir });
|
|
530
586
|
const warnings = [...baselineGate.warnings];
|
|
531
587
|
blockers.push(...baselineGate.blockers);
|
|
588
|
+
blockers.push(...decisionGate.blockers);
|
|
532
589
|
if (snapshotMissing) blockers.push('CONTEXT_NOT_COMPILED');
|
|
533
590
|
else {
|
|
534
591
|
if (legacyContext) blockers.push('CONTEXT_SNAPSHOT_UPGRADE_REQUIRED');
|
|
@@ -554,6 +611,8 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
554
611
|
change, tasks, task, role: snapshot?.role || 'plan', readiness: 'blocked',
|
|
555
612
|
blockers: baselineGate.blockers,
|
|
556
613
|
});
|
|
614
|
+
} else if (!decisionGate.ready) {
|
|
615
|
+
nextAction = decisionNextAction(decisionGate);
|
|
557
616
|
} else if (activeWorkflow?.status === 'blocked') {
|
|
558
617
|
nextAction = workflowNextAction(activeWorkflow);
|
|
559
618
|
} else if (snapshotMissing) {
|
|
@@ -583,14 +642,16 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
583
642
|
task_status: task?.status || null,
|
|
584
643
|
session_id: task?.session_id || null,
|
|
585
644
|
role: snapshot?.role || 'plan',
|
|
586
|
-
gate: snapshot?.gate || 'alignment',
|
|
645
|
+
gate: decisionGate.ready ? (snapshot?.gate || 'alignment') : 'alignment',
|
|
587
646
|
readiness,
|
|
588
647
|
blockers,
|
|
589
648
|
warnings: [...new Set(warnings)],
|
|
590
649
|
next_action: nextAction,
|
|
591
650
|
recommended_workflow: baselineGate.blockers.length > 0
|
|
592
651
|
? (baselineHealth.baseline?.status === 'ready' ? 'ultra-doctor' : 'ultra-init')
|
|
593
|
-
:
|
|
652
|
+
: !decisionGate.ready
|
|
653
|
+
? 'ultra-think'
|
|
654
|
+
: activeWorkflow
|
|
594
655
|
? `ultra-${activeWorkflow.kind}`
|
|
595
656
|
: recommendedWorkflow(change, task, tasks, readiness),
|
|
596
657
|
context_manifest_path: snapshot?.manifest_path || null,
|
|
@@ -602,6 +663,7 @@ function readBreadcrumb(db, { id } = {}, { rootDir = process.cwd() } = {}) {
|
|
|
602
663
|
repository_revision: baselineHealth.baseline.repository_revision,
|
|
603
664
|
} : null,
|
|
604
665
|
workflow: summarizeWorkflow(activeWorkflow),
|
|
666
|
+
decision: summarizeDecision(decisionGate),
|
|
605
667
|
};
|
|
606
668
|
}
|
|
607
669
|
|