ultra-builder-pro-cli 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.ultra-template/docs/research/README.md +11 -7
  2. package/.ultra-template/specs/architecture.md +36 -36
  3. package/.ultra-template/specs/discovery.md +46 -33
  4. package/.ultra-template/specs/product.md +38 -29
  5. package/.ultra-template/specs/research-distillate.md +48 -0
  6. package/.ultra-template/tasks/contexts/TEMPLATE.md +11 -28
  7. package/CHANGELOG.md +86 -1
  8. package/README.md +40 -26
  9. package/adapters/_shared/codex-assets.cjs +29 -165
  10. package/adapters/_shared/file-ops.cjs +6 -2
  11. package/adapters/_shared/runtime-assets.cjs +1 -0
  12. package/adapters/claude.js +3 -2
  13. package/adapters/codex.js +1 -0
  14. package/adapters/kimi.js +3 -2
  15. package/adapters/opencode.js +33 -122
  16. package/agents/review-coordinator.md +9 -6
  17. package/docs/AGENT-CONTEXT.md +21 -11
  18. package/docs/ARCHITECTURE.md +41 -13
  19. package/docs/PLAN.zh-CN.md +3 -2
  20. package/docs/ROADMAP.md +9 -5
  21. package/docs/RUNTIME-COMPAT-MATRIX.md +6 -6
  22. package/docs/STATE-DB-ACCESS-POLICY.md +27 -25
  23. package/docs/WORKFLOW-LIFECYCLE.md +266 -0
  24. package/hooks/README.md +6 -1
  25. package/hooks/active_task_context.py +22 -50
  26. package/hooks/context_spine.py +61 -332
  27. package/hooks/health_check.py +29 -18
  28. package/hooks/pre_stop_check.py +28 -33
  29. package/hooks/subagent_tracker.py +54 -85
  30. package/hooks/workflow_checkpoint.py +39 -47
  31. package/hooks/workflow_context.py +4 -9
  32. package/hooks/workflow_resume.py +48 -128
  33. package/mcp-server/breadcrumb.cjs +22 -0
  34. package/mcp-server/hook-event.cjs +34 -0
  35. package/mcp-server/lib/archive-journal.cjs +17 -5
  36. package/mcp-server/lib/baseline-workflow.cjs +158 -18
  37. package/mcp-server/lib/change-workflow.cjs +550 -85
  38. package/mcp-server/lib/context-spine.cjs +242 -24
  39. package/mcp-server/lib/doctor.cjs +33 -3
  40. package/mcp-server/lib/init-project.cjs +210 -16
  41. package/mcp-server/lib/plan-store.cjs +4 -1
  42. package/mcp-server/lib/prd-parser.cjs +14 -41
  43. package/mcp-server/lib/pricing.cjs +7 -10
  44. package/mcp-server/lib/project-breadcrumb.cjs +145 -0
  45. package/mcp-server/lib/projector.cjs +57 -3
  46. package/mcp-server/lib/spec-learning.cjs +79 -2
  47. package/mcp-server/lib/state-db.cjs +145 -8
  48. package/mcp-server/lib/state-ops.cjs +160 -9
  49. package/mcp-server/lib/task-expander.cjs +9 -38
  50. package/mcp-server/lib/workflow-state.cjs +2292 -0
  51. package/mcp-server/server.cjs +182 -50
  52. package/orchestrator/daemon.cjs +1 -2
  53. package/orchestrator/dispatch-rules.cjs +5 -31
  54. package/orchestrator/planner/plan-builder.cjs +21 -12
  55. package/package.json +14 -6
  56. package/skills/cc-collab/SKILL.md +2 -2
  57. package/skills/codex-collab/SKILL.md +2 -3
  58. package/skills/ultra-change/SKILL.md +53 -68
  59. package/skills/ultra-change/references/change-contract.md +36 -0
  60. package/skills/ultra-deliver/SKILL.md +69 -79
  61. package/skills/ultra-deliver/references/baseline-reconciliation.md +20 -0
  62. package/skills/ultra-dev/SKILL.md +54 -59
  63. package/skills/ultra-doctor/SKILL.md +40 -71
  64. package/skills/ultra-init/SKILL.md +96 -103
  65. package/skills/ultra-init/references/brownfield-adoption.md +2 -2
  66. package/skills/ultra-plan/SKILL.md +79 -75
  67. package/skills/ultra-plan/references/semantic-preflight.md +18 -0
  68. package/skills/ultra-research/SKILL.md +87 -60
  69. package/skills/ultra-research/references/00-problem-validation.md +36 -0
  70. package/skills/ultra-research/references/01-opportunity-discovery.md +33 -0
  71. package/skills/ultra-research/references/02-market-assessment.md +30 -0
  72. package/skills/ultra-research/references/03-competitive-landscape.md +31 -0
  73. package/skills/ultra-research/references/04-product-strategy.md +32 -0
  74. package/skills/ultra-research/references/05-assumptions-validation.md +31 -0
  75. package/skills/ultra-research/references/10-user-personas.md +29 -0
  76. package/skills/ultra-research/references/11-user-scenarios.md +28 -0
  77. package/skills/ultra-research/references/20-user-stories.md +30 -0
  78. package/skills/ultra-research/references/21-features-scope.md +29 -0
  79. package/skills/ultra-research/references/22-success-metrics.md +28 -0
  80. package/skills/ultra-research/references/30-architecture-context.md +30 -0
  81. package/skills/ultra-research/references/31-solution-strategy.md +28 -0
  82. package/skills/ultra-research/references/32-building-blocks.md +28 -0
  83. package/skills/ultra-research/references/40-deployment.md +29 -0
  84. package/skills/ultra-research/references/41-quality-risks.md +28 -0
  85. package/skills/ultra-research/references/99-synthesis.md +35 -0
  86. package/skills/ultra-review/SKILL.md +57 -73
  87. package/skills/ultra-review/references/review-modes.md +27 -0
  88. package/skills/ultra-review/references/unified-schema.md +25 -0
  89. package/skills/ultra-review/scripts/review_wait.py +40 -3
  90. package/skills/ultra-status/SKILL.md +41 -45
  91. package/skills/ultra-test/SKILL.md +48 -55
  92. package/skills/ultra-verify/SKILL.md +22 -31
  93. package/skills/ultra-verify/references/{confidence-system.md → evidence-status.md} +3 -3
  94. package/skills/ultra-verify/scripts/verify_wait.py +54 -54
  95. package/spec/cli-protocol.md +7 -2
  96. package/spec/mcp-tools.yaml +422 -73
  97. package/spec/schemas/baseline-reconciliation.v1.schema.json +73 -0
  98. package/spec/schemas/delivery-report.v1.schema.json +111 -0
  99. package/spec/schemas/mcp-tools.schema.json +1 -1
  100. package/spec/schemas/state-db.sql +82 -1
  101. package/spec/schemas/tasks.v4.5.schema.json +8 -1
  102. package/spec/schemas/test-report.v1.schema.json +194 -0
  103. package/ultra-tools/cli.cjs +1 -1
  104. package/ultra-tools/commands/migrate.cjs +3 -3
  105. package/ultra-tools/commands/status.cjs +213 -12
  106. package/.ultra-template/delivery-report.json +0 -9
  107. package/.ultra-template/test-report.json +0 -8
  108. package/adapters/_shared/tests/file-ops.test.cjs +0 -54
  109. package/adapters/_shared/tests/frontmatter.test.cjs +0 -47
  110. package/adapters/_shared/tests/handbook.test.cjs +0 -101
  111. package/adapters/_shared/tests/path-rewrite.test.cjs +0 -60
  112. package/adapters/_shared/tests/provenance.test.cjs +0 -133
  113. package/adapters/_shared/tests/resolve-target.test.cjs +0 -75
  114. package/adapters/_shared/tests/runtime-assets.test.cjs +0 -131
  115. package/adapters/_shared/tests/settings-merge.test.cjs +0 -85
  116. package/adapters/_shared/tests/validate.test.cjs +0 -30
  117. package/adapters/tests/claude.test.cjs +0 -137
  118. package/adapters/tests/codex-hook.test.cjs +0 -115
  119. package/adapters/tests/codex.test.cjs +0 -423
  120. package/adapters/tests/kimi-hook.test.cjs +0 -122
  121. package/adapters/tests/kimi.test.cjs +0 -259
  122. package/adapters/tests/opencode.test.cjs +0 -371
  123. package/mcp-server/lib/archive-journal.test.cjs +0 -35
  124. package/mcp-server/lib/baseline-workflow.test.cjs +0 -477
  125. package/mcp-server/lib/change-workflow.test.cjs +0 -971
  126. package/mcp-server/lib/context-spine.test.cjs +0 -93
  127. package/mcp-server/lib/doctor.test.cjs +0 -148
  128. package/mcp-server/lib/llm-client.cjs +0 -157
  129. package/mcp-server/lib/llm-client.test.cjs +0 -156
  130. package/mcp-server/lib/plan-store.test.cjs +0 -132
  131. package/mcp-server/lib/prd-parser.test.cjs +0 -171
  132. package/mcp-server/lib/runtime-state.test.cjs +0 -92
  133. package/mcp-server/lib/spec-learning.test.cjs +0 -67
  134. package/mcp-server/lib/state-db.test.cjs +0 -314
  135. package/mcp-server/lib/task-expander.test.cjs +0 -324
  136. package/mcp-server/lib/topo.test.cjs +0 -158
  137. package/mcp-server/tests/circuit-breaker.test.cjs +0 -125
  138. package/mcp-server/tests/commit-hash-flow.test.cjs +0 -117
  139. package/mcp-server/tests/concurrency-update.test.cjs +0 -96
  140. package/mcp-server/tests/concurrency.test.cjs +0 -112
  141. package/mcp-server/tests/db-ops.test.cjs +0 -121
  142. package/mcp-server/tests/init-project.test.cjs +0 -393
  143. package/mcp-server/tests/migrate.test.cjs +0 -232
  144. package/mcp-server/tests/projector.test.cjs +0 -162
  145. package/mcp-server/tests/server-contract.test.cjs +0 -1471
  146. package/mcp-server/tests/staleness.test.cjs +0 -190
  147. package/mcp-server/tests/state-db.test.cjs +0 -106
  148. package/mcp-server/tests/state-ops.test.cjs +0 -449
  149. package/mcp-server/tests/tagged-tasks.test.cjs +0 -163
  150. package/mcp-server/tests/telemetry.test.cjs +0 -219
  151. package/orchestrator/planner/expand-prompt.cjs +0 -62
  152. package/orchestrator/planner/expand-prompt.test.cjs +0 -52
  153. package/orchestrator/planner/plan-builder.test.cjs +0 -165
  154. package/orchestrator/planner/prd-prompt.cjs +0 -44
  155. package/orchestrator/planner/prd-prompt.test.cjs +0 -30
  156. package/orchestrator/tests/auto-merge.test.cjs +0 -216
  157. package/orchestrator/tests/daemon.test.cjs +0 -311
  158. package/orchestrator/tests/dispatch-rules.test.cjs +0 -124
  159. package/orchestrator/tests/parallel-orchestrator.test.cjs +0 -233
  160. package/orchestrator/tests/phase-8b-gate.test.cjs +0 -183
  161. package/orchestrator/tests/recovery.test.cjs +0 -190
  162. package/orchestrator/tests/session-runner.test.cjs +0 -314
  163. package/orchestrator/tests/skill-miner.test.cjs +0 -147
  164. package/orchestrator/tests/worktree-manager.test.cjs +0 -148
  165. package/skills/ultra-research/references/architecture.md +0 -38
  166. package/skills/ultra-research/references/problem-product.md +0 -39
  167. package/skills/ultra-research/references/quality-delivery.md +0 -34
  168. package/skills/ultra-research/references/synthesis.md +0 -38
  169. package/skills/ultra-research/references/users-scope.md +0 -40
  170. package/ultra-tools/legacy-memory.test.cjs +0 -80
  171. package/ultra-tools/status-command.test.cjs +0 -181
  172. package/ultra-tools/system-command.test.cjs +0 -309
@@ -4,13 +4,13 @@
4
4
  > risks R21 / R25.
5
5
 
6
6
  `.ultra/state.db` is the only authoritative state store for baselines, changes,
7
- tasks, events, sessions, incidents, projections, telemetry, and spec references
7
+ tasks, workflow runs/steps, events, sessions, incidents, projections, telemetry, and spec references
8
8
  (D18, D52, D54). Every process
9
9
  that touches it must follow the rules below; deviations are bugs.
10
10
 
11
11
  ## 1. Three-role write matrix
12
12
 
13
- | Role | tasks | baselines | change/artifact state | events | sessions | telemetry | specs_refs | migration_history |
13
+ | Role | tasks | baselines | change/workflow/artifact state | events | sessions | telemetry | specs_refs | migration_history |
14
14
  |---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
15
15
  | **MCP server** (single writer for mutables) | RW | RW | RW | RW | RW | RW | RW | RW |
16
16
  | **CLI** (`ultra-tools …`) | R | RW (init only) | R | RW (append-only) | R | R | R | RW (migration only) |
@@ -31,7 +31,11 @@ write API for normal task, change, session, or baseline lifecycle updates.
31
31
  The append-only carve-out for `events` exists because `events.id INTEGER
32
32
  PRIMARY KEY AUTOINCREMENT` makes concurrent inserts collision-free under
33
33
  WAL + `busy_timeout`. This lets hooks and short-lived CLI invocations
34
- record audit events without paying the cost of a full MCP round trip.
34
+ record audit observations without paying the cost of a full MCP round trip.
35
+ It does not grant lifecycle authority: external writers and public
36
+ `task.append_event` use the published observation allowlist, while lifecycle event
37
+ names are emitted only inside the mutation that owns the corresponding state change.
38
+ Consumers must verify mutable rows rather than infer success from an event name.
35
39
 
36
40
  ## 2. Connection discipline
37
41
 
@@ -40,31 +44,27 @@ Every process **must**:
40
44
  1. Open the database with WAL + `busy_timeout=5000` + `foreign_keys=ON`.
41
45
  Use `mcp-server/lib/state-db.cjs` `openStateDb` so the pragmas are
42
46
  applied uniformly.
43
- 2. Wrap every write in `BEGIN IMMEDIATE` (use the helper `withWrite(db,
44
- fn)` once Phase 2.3 lands; until then call `db.transaction(fn)()` and
45
- know that better-sqlite3 promotes to IMMEDIATE on the first write).
46
- 3. Retry up to **3 times** on `SQLITE_BUSY`, with exponential backoff
47
- starting at 25 ms. Each retry must re-issue the full transaction —
48
- never partially commit.
47
+ 2. Wrap every mutable state operation with `state-ops.cjs` `tx(db, fn)`. The
48
+ helper calls better-sqlite3's immediate transaction variant, so the writer
49
+ lock is acquired before user code runs and the whole operation rolls back on
50
+ failure.
51
+ 3. Retry the complete transaction up to **10 attempts** on `SQLITE_BUSY`, using
52
+ decorrelated jitter from a 50 ms base with a 2 s cap. Never retry only a
53
+ suffix of a failed transaction or preserve partial state.
49
54
  4. Close the connection in a `finally` block. WAL files (`-wal`,
50
55
  `-shm`) are reclaimed on the last writer's close.
51
56
 
52
57
  Reads never need explicit transactions; better-sqlite3 reads observe a
53
58
  consistent snapshot under WAL.
54
59
 
55
- ## 3. WAL fallback
60
+ ## 3. WAL requirement
56
61
 
57
- WAL is **unsafe on NFS / SMB / certain Docker bind mounts** (R21). When
58
- `.ultra/state.db` resides on such a filesystem the orchestrator must
59
- either:
60
-
61
- - refuse to start (preferred — surfaces the deployment problem early), or
62
- - downgrade to `PRAGMA journal_mode = DELETE` and warn loudly. Concurrent
63
- writers fall back to short-burst contention; throughput drops but
64
- correctness holds.
65
-
66
- Detection happens at boot: `statvfs` of `.ultra/state.db`'s mount;
67
- `f_type` ∈ `{NFS_SUPER_MAGIC, SMB_SUPER_MAGIC}` ⇒ refuse / downgrade.
62
+ WAL is part of the authority contract, not an optional performance setting.
63
+ `openStateDb` asks SQLite to enable WAL and checks the actual returned mode. If
64
+ the storage layer returns anything else, startup fails with
65
+ `STATE_DB_WAL_UNAVAILABLE`. Ultra does not silently downgrade to a different
66
+ concurrency model. Move `.ultra/state.db` to storage that supports SQLite WAL or
67
+ resolve the mount/runtime constraint before retrying.
68
68
 
69
69
  ## 4. Where each table is owned
70
70
 
@@ -73,9 +73,10 @@ Detection happens at boot: `statvfs` of `.ultra/state.db`'s mount;
73
73
  | `baselines` | MCP server (`baseline.start` / `baseline.record` / `baseline.converge`); initialization and legacy-projection migration may create only the first draft or compatibility row |
74
74
  | `tasks` | MCP server (`task.create` / `task.update` / `task.delete`) |
75
75
  | `changes` | MCP server (`change.create` / `change.update` / `change.converge` / `change.archive`) |
76
+ | `workflow_runs`, `workflow_steps` | MCP server (`workflow.start` / `workflow.step` / `workflow.complete`); skills provide evidence inputs but never write rows directly |
76
77
  | `artifacts`, `context_snapshots`, `spec_learning_candidates`, `trace_links` | MCP server through change lifecycle tools |
77
78
  | `incidents`, `projection_jobs`, `event_consumers`, `circuit_breaker` | MCP server; backup-first doctor recovery may perform only documented mechanical transitions |
78
- | `events` | MCP server **and** any process via append-only INSERT |
79
+ | `events` | MCP server for lifecycle events; approved processes and `task.append_event` for allowlisted append-only observations |
79
80
  | `sessions` | MCP server (`session.spawn` / `session.close` / `session.heartbeat`); orchestrator may write status transitions |
80
81
  | `telemetry` | MCP server (collected from tool-call wrappers); orchestrator may dump bulk samples |
81
82
  | `specs_refs` | MCP server (rebuilt on `spec_changed` event); orchestrator may rebuild |
@@ -110,6 +111,7 @@ The contract on this page is enforced by:
110
111
  appending to `events` simultaneously; asserts no `SQLITE_BUSY` escapes
111
112
  the retry loop and the resulting `events.id` sequence has no gaps and
112
113
  no duplicates.
113
- - Phase 2.3 will add `concurrency-update.test.cjs` — 20 worker threads
114
- driving `task.update` against disjoint task ids, verifying serialized
115
- writes through the MCP server.
114
+ - `mcp-server/tests/state-ops.test.cjs` — immediate transactions, rollback,
115
+ status transitions, task contracts, and event coupling.
116
+ - `mcp-server/lib/workflow-state.test.cjs` — ordered workflow transitions,
117
+ evidence/output requirements, blocking/resume, and cross-stage gates.
@@ -0,0 +1,266 @@
1
+ # Ultra Builder Pro workflow lifecycle
2
+
3
+ This document defines when Ultra writes authoritative state, how each workflow
4
+ advances, what invalidates evidence, and which decisions remain with the active
5
+ host and project owner. It is the operational companion to
6
+ [`ARCHITECTURE.md`](./ARCHITECTURE.md) and the machine contracts under `spec/`.
7
+
8
+ ## 1. Authority boundaries
9
+
10
+ | Surface | Owns | Must not own |
11
+ |---|---|---|
12
+ | Active host and owner | Product judgment, research content, implementation, review findings, release authorization | Durable workflow status or fabricated evidence digests |
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, 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 | External memory or code-graph payloads |
16
+ | Generated JSON and Markdown | Read-only projections and durable artifacts | Independent lifecycle authority |
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
+
19
+ Prompt input can supply facts, content paths, reasons, owner decisions, and
20
+ evidence references. MCP reads the current repository and DB, validates those
21
+ inputs, hashes outputs, and derives status. Prompt input cannot override a task's
22
+ public seam, verification command, context references, workflow summary, gate
23
+ verdict, output digest, or next action.
24
+
25
+ Every change-bound research, plan, dev, test, review, and deliver run inherits
26
+ the baseline id from the owning change workflow. A Prompt cannot switch that
27
+ authority, and no new stage may start on an archived or cancelled change. Doctor
28
+ recovers a legacy change without this link into an explicitly blocked workflow;
29
+ it does not fabricate the missing provenance.
30
+
31
+ Ultra does not select an MVP. Product scope and delivery posture are owner
32
+ decisions. A reduced research mode is valid only for an explicitly bounded active
33
+ change with a recorded selection reason; initial greenfield and brownfield
34
+ baselines always use all seventeen research steps.
35
+
36
+ ## 2. How `ultra-init` classifies a repository
37
+
38
+ `task.init_project` scans a bounded repository view while ignoring `.git`,
39
+ `.ultra`, dependencies, virtual environments, generated build output, and other
40
+ known non-source directories.
41
+
42
+ `auto` classification is deterministic:
43
+
44
+ - `brownfield` when delivered-system evidence exists: application source, tests,
45
+ deployment configuration, or persisted-state/schema evidence;
46
+ - `greenfield` when none of those signals exists. A Git repository, README,
47
+ package manifest, workspace manifest, or starter documentation alone does not
48
+ prove delivered behavior;
49
+ - `migrated` is never inferred from source. It is a compatibility state created
50
+ when older Ultra authority is preserved and upgraded. It requires a new
51
+ evidence-backed brownfield adoption before normal work.
52
+
53
+ The scan also records bounded signal paths, detected verification commands,
54
+ evidence-backed project type and technology signals, monorepo markers, candidate
55
+ workspace roots, and whether the scan limit was reached. Explicit owner-supplied
56
+ project type or stack metadata overrides detection without deleting the observed
57
+ signals. In a monorepo, the owner selects the repository scope; baseline
58
+ worktree digests and accepted dirty files are restricted to that scope.
59
+
60
+ Classification and readiness are separate. Initialization immediately creates
61
+ or resumes `.ultra/state.db`, installs only the required scaffold, records the
62
+ classification, opens an `init` workflow, and opens `full` research for
63
+ greenfield or `adoption` research for brownfield. Empty templates are not an
64
+ approved baseline.
65
+
66
+ ## 3. Durable status machines
67
+
68
+ ### Baseline
69
+
70
+ ```text
71
+ draft -> adopting -> ready -> superseded
72
+ |
73
+ -> blocked -> adopting
74
+ ```
75
+
76
+ - `draft`: greenfield authority exists but has not been established.
77
+ - `adopting`: brownfield or migrated evidence is being recorded.
78
+ - `blocked`: current baseline evidence, verification, scope, or approval cannot
79
+ converge.
80
+ - `ready`: research, evidence, repository snapshot, gaps, and approval converged.
81
+ - `superseded`: preserved historical baseline after an explicitly authorized
82
+ replacement.
83
+
84
+ `ready` is not trusted as a label alone. Every baseline/status/doctor/change gate
85
+ revalidates its persisted fields, current files, research outputs, revision and
86
+ worktree, known-red acceptance, blocking gaps, and approval provenance. An ordinary
87
+ active change may downgrade only the expected drift created after its completed
88
+ `bind-baseline` step to warnings; it cannot downgrade adoption, migration, missing
89
+ evidence, or structural blockers. Approved incident break-glass is the only other
90
+ execution exception and creates a mandatory reconciliation gap.
91
+
92
+ ### Change
93
+
94
+ ```text
95
+ active <-> blocked -> ready -> archived
96
+ \--------------------------> cancelled
97
+ ```
98
+
99
+ Only a healthy `ready` baseline may authorize a new ordinary change. An incident
100
+ may bypass an unhealthy baseline only with a stored reason and approver; archive
101
+ then creates a blocking baseline-reconciliation gap.
102
+
103
+ ### Task
104
+
105
+ ```text
106
+ pending -> in_progress -> completed
107
+ | |
108
+ v v
109
+ blocked <------+
110
+ |
111
+ -> pending
112
+
113
+ pending -> expanded -> completed
114
+ ```
115
+
116
+ A task is executable only when its DB contract contains an observable outcome,
117
+ traceability, slice kind, public seam, exact verification command, acceptance
118
+ checks, context references, resolved documentation impact, owner, and valid
119
+ dependencies.
120
+
121
+ ### Workflow run and step
122
+
123
+ ```text
124
+ run: active <-> blocked -> ready -> completed
125
+ \----> cancelled
126
+
127
+ step: pending -> in_progress -> completed
128
+ | |
129
+ v v
130
+ blocked ------> in_progress/completed
131
+ ```
132
+
133
+ Required steps execute in definition order. A step that requires evidence or an
134
+ output cannot complete without it. When the final required step completes, the
135
+ run becomes `ready`; `workflow.complete` revalidates the entire stage before
136
+ making it `completed`.
137
+
138
+ ## 4. Write and verification lifecycle
139
+
140
+ | Stage | Authoritative writes | Completion verification | Next route |
141
+ |---|---|---|---|
142
+ | `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, decisions, typed semantic records with source digests, one immutable report per selected step, output paths and SHA-256 digests | 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
+ | 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, linked change run, initial context snapshot | Blocking decisions resolved; selected change research 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 |
146
+ | `plan` | Plan-step evidence, complete change-owned task rows, acceptance coverage matrix, change-bound `.ultra/execution-plan.json`, explicit plan approval | 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
+ | `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` | 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
+ | `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 |
150
+ | `deliver` | Convergence context, verified learning resolutions, schema-validated semantic reconciliation manifest, change archive, durable release-authorization decision, immutable delivery report | Tasks/test/change-review current; every baseline update has an anchored before/after semantic record; `change.converge` derives evidence from DB and accepts no Prompt verdicts; archive and baseline transaction agree; external actions have evidence | Report completion or start the next `change` |
151
+ | `status` | None | Reads doctor, baseline, workflows, breadcrumb, tasks, sessions, Git, and DB-referenced reports | One exact current route |
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 | Exact repair, restore/rebaseline decision, or workflow route |
153
+
154
+ Every mutating MCP operation appends an event and enqueues projection work when
155
+ the corresponding read-only view can change. Projection failure is recorded as
156
+ a retryable incident; it does not silently convert a failed write into success.
157
+ `task.append_event` accepts only non-authoritative observations. Lifecycle events are
158
+ emitted by their owning DB mutation and never act as a substitute state transition.
159
+
160
+ ## 5. Context and prompt follow-through
161
+
162
+ `change.context` compiles an immutable Context Manifest v2 snapshot. It combines:
163
+
164
+ - the bound baseline/change/task identity;
165
+ - a role and lifecycle gate;
166
+ - DB-backed task outcome, public seam, verification command, and required refs;
167
+ - local file hashes, current HEAD, branch, and worktree digest;
168
+ - optional metadata-only external memory or graph references;
169
+ - advisory file/token/context-share budgets;
170
+ - readiness blockers and one DB-derived next action.
171
+
172
+ Critical stage workflows bind a matching snapshot as a durable output:
173
+
174
+ | Workflow | Required role | Required gate |
175
+ |---|---|---|
176
+ | `change` | `plan` or `implement` | `planning` or `implementation` |
177
+ | `dev` | `implement` | `implementation` |
178
+ | `test` | `check` | `verification` |
179
+ | `review` | `review` | `review` |
180
+ | `deliver` | `check` | `convergence` |
181
+
182
+ The snapshot path and digest are then carried into test, review, and delivery
183
+ reports. This prevents a later Prompt from claiming evidence gathered under a
184
+ different context. File and token budgets warn; they do not block legitimate
185
+ work. Missing authority, required refs, task contracts, outputs, approval, or
186
+ current gate evidence do block.
187
+
188
+ ## 6. Invalidation rules
189
+
190
+ - Changing a task contract invalidates contexts and gates that depended on the
191
+ former contract.
192
+ - Changing required source/spec content invalidates the recorded output or
193
+ context digest.
194
+ - Changing a research semantic source invalidates that workflow even when its immutable
195
+ step report remains unchanged.
196
+ - Changing full HEAD or the worktree invalidates aggregate test, review, and
197
+ delivery evidence.
198
+ - A completed dev run remains valid across later task commits when its bound
199
+ task contract is unchanged and its recorded commits remain ancestors of the
200
+ current HEAD.
201
+ - Test, review, and delivery reports use workflow-id paths so a later run cannot
202
+ overwrite historical evidence.
203
+ - `.ultra` workflow artifacts are excluded from application-worktree drift, but
204
+ their own recorded SHA-256 digests are still authoritative.
205
+ - Generated `tasks.json` and task-context headers are projections. Manual edits
206
+ are overwritten and never repair DB authority.
207
+
208
+ ## 7. Entry flows
209
+
210
+ ### New project
211
+
212
+ ```text
213
+ ultra-init -> full research (17 steps) -> baseline approval
214
+ -> initial change + plan -> dev per task -> test -> review -> deliver
215
+ ```
216
+
217
+ ### Existing project
218
+
219
+ ```text
220
+ ultra-init -> brownfield adoption research (17 steps)
221
+ -> characterization and known-red evidence -> gap ledger -> owner approval
222
+ -> first selected change -> plan -> dev -> test -> review -> deliver
223
+ ```
224
+
225
+ Adoption records the system that exists. It does not force the owner to recreate
226
+ market, persona, or roadmap claims that cannot be supported; those remain
227
+ `Unknown` or explicit gaps while every semantic step is still processed.
228
+
229
+ ### Earlier Ultra project
230
+
231
+ ```text
232
+ doctor/migrate with backup -> migrated compatibility authority
233
+ -> explicit brownfield re-adoption -> full convergence -> normal change loop
234
+ ```
235
+
236
+ Migration preserves prior files and rows but never carries old approval forward.
237
+
238
+ ### Daily maintenance
239
+
240
+ ```text
241
+ ultra-status -> ultra-change -> plan if task contracts are absent
242
+ -> dev -> test -> review -> deliver -> reconciled baseline
243
+ ```
244
+
245
+ Quick changes shorten the artifact burden only where the machine contract allows;
246
+ they do not skip task ownership, executable acceptance, current checking, review,
247
+ or baseline reconciliation. Incidents add structured diagnosis and recovery
248
+ evidence rather than bypassing them.
249
+
250
+ ## 8. Recovery and diagnosis
251
+
252
+ Use `system.doctor` for project authority and `ubp --doctor` for installed host
253
+ assets. Both are read-only unless an explicit project repair command is chosen.
254
+
255
+ During an explicit repair, a legacy active change that predates durable workflows gets
256
+ one blocked change run at `bind-baseline` with
257
+ `LEGACY_CHANGE_PROVENANCE_REQUIRED`; repair does not invent its evidence. Rebind it to
258
+ the re-adopted baseline and resume the same run before planning. Every plan must use
259
+ the exact baseline id stored by its owning change run; only an approved break-glass
260
+ incident may legitimately bind no baseline id.
261
+
262
+ Resume the same blocked workflow and current step after resolving its blocker.
263
+ Do not create parallel runs to escape state. Supported schema and projection
264
+ repairs are backup-first. A corrupt DB is preserved and requires an explicit
265
+ restore or rebaseline decision; neither action can manufacture research,
266
+ verification, or owner approval.
package/hooks/README.md CHANGED
@@ -14,12 +14,17 @@ advisory workflow conditions into refusal gates.
14
14
  | Before compact | `workflow_checkpoint.py` | Validate and atomically save a minimal workflow checkpoint |
15
15
  | After compact/resume | `workflow_resume.py` | Prefer the DB breadcrumb; restore the minimal file checkpoint only when no active change owns recovery |
16
16
  | Stop | `pre_stop_check.py` | Report an incomplete workflow boundary and allow stop |
17
- | Subagent lifecycle | `subagent_tracker.py` | Append lifecycle evidence under `.ultra/runtime/` |
17
+ | Subagent lifecycle | `subagent_tracker.py` | Append minimal start/stop metadata to the authoritative DB event stream |
18
18
 
19
19
  The plugin deliberately does not capture prompts, tool observations, transcripts, summaries, or
20
20
  cross-session memory or code-graph content. Generic command blocking and post-edit policy are
21
21
  user/repository governance. Persistent Memory/graph content belongs to separately installed providers.
22
22
 
23
+ `subagent_tracker.py` passes only agent id/type and the host session id to the bundled
24
+ `hook-event.cjs` helper. The helper binds the current change/task and appends
25
+ `subagent_started` or `subagent_stopped` to `state.db`; it never stores transcript
26
+ paths, messages, or a parallel JSONL lifecycle log.
27
+
23
28
  `context_spine.py` is an imported library, not an eighth lifecycle registration.
24
29
  Registering it separately would duplicate hook execution. The checkpoint is a recovery artifact, not a second authority. A valid newer
25
30
  live workflow wins; a newer valid checkpoint may restore a missing or older
@@ -2,13 +2,11 @@
2
2
  """Protect Ultra projections and restate an active task boundary before edits."""
3
3
 
4
4
  import json
5
- import sqlite3
6
5
  import sys
7
6
  from pathlib import Path
8
7
 
9
- from context_spine import read_breadcrumb, render_breadcrumb
8
+ from context_spine import ContextSpineError, find_root, read_breadcrumb, render_breadcrumb
10
9
 
11
- TERMINAL = {"committed", "completed", "done", "cancelled"}
12
10
  TASKS_PROJECTION = Path(".ultra/tasks/tasks.json")
13
11
 
14
12
 
@@ -34,58 +32,32 @@ def main() -> None:
34
32
  except json.JSONDecodeError as exc:
35
33
  print(f"[active_task_context] invalid hook input: {exc}", file=sys.stderr)
36
34
  data = {}
37
- start = Path(data.get("cwd") or Path.cwd()).resolve()
38
- for root in (start, *start.parents):
39
- ultra = root / ".ultra"
40
- state_file = ultra / "workflow-state.json"
41
- initialized = (
42
- (ultra / "state.db").is_file()
43
- or state_file.is_file()
44
- or (ultra / "changes" / "active").is_dir()
45
- )
46
- if not initialized:
47
- continue
48
- if targets_tasks_projection(data, root):
49
- print(json.dumps({"hookSpecificOutput": {
50
- "hookEventName": "PreToolUse",
51
- "permissionDecision": "deny",
52
- "permissionDecisionReason": (
53
- "Refusing a direct write to the .ultra/tasks/tasks.json projection. "
54
- ".ultra/state.db is authoritative; use the Ultra MCP task tools and "
55
- "run ultra-doctor when state or projection health is degraded."
56
- ),
57
- }}))
58
- return
59
- try:
60
- breadcrumb = read_breadcrumb(root)
61
- except (sqlite3.Error, OSError) as exc:
62
- print(f"[active_task_context] cannot inspect Context Spine: {exc}", file=sys.stderr)
63
- breadcrumb = None
64
- if breadcrumb and breadcrumb.get("task_id"):
65
- print(json.dumps({"hookSpecificOutput": {
66
- "hookEventName": "PreToolUse",
67
- "additionalContext": render_breadcrumb(root, breadcrumb),
68
- }}))
69
- return
70
- if not state_file.is_file():
71
- break
72
- try:
73
- state = json.loads(state_file.read_text(encoding="utf-8"))
74
- except (OSError, json.JSONDecodeError) as exc:
75
- print(f"[active_task_context] cannot read {state_file}: {exc}", file=sys.stderr)
76
- break
77
- if not isinstance(state, dict) or state.get("status") in TERMINAL:
78
- break
79
- task = state.get("task_id", state.get("task", "unknown"))
80
- step = state.get("step", "unknown")
35
+ root = find_root(Path(data.get("cwd") or Path.cwd()).resolve())
36
+ if root is None:
37
+ print(json.dumps({}))
38
+ return
39
+ if targets_tasks_projection(data, root):
81
40
  print(json.dumps({"hookSpecificOutput": {
82
41
  "hookEventName": "PreToolUse",
83
- "additionalContext": (
84
- f"Active Ultra task {task}, step {step}. Keep this edit inside the approved "
85
- "task and preserve .ultra/state.db as workflow authority."
42
+ "permissionDecision": "deny",
43
+ "permissionDecisionReason": (
44
+ "Refusing a direct write to the .ultra/tasks/tasks.json projection. "
45
+ ".ultra/state.db is authoritative; use the Ultra MCP task tools and "
46
+ "run ultra-doctor when state or projection health is degraded."
86
47
  ),
87
48
  }}))
88
49
  return
50
+ try:
51
+ breadcrumb = read_breadcrumb(root)
52
+ except ContextSpineError as exc:
53
+ print(f"[active_task_context] cannot inspect Context Spine: {exc}", file=sys.stderr)
54
+ breadcrumb = None
55
+ if breadcrumb and breadcrumb.get("change_id"):
56
+ print(json.dumps({"hookSpecificOutput": {
57
+ "hookEventName": "PreToolUse",
58
+ "additionalContext": render_breadcrumb(root, breadcrumb),
59
+ }}))
60
+ return
89
61
  print(json.dumps({}))
90
62
 
91
63