ultra-builder-pro-cli 0.21.1 → 0.23.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 (58) hide show
  1. package/.ultra-template/tasks/tasks.json +10 -3
  2. package/.ultra-template/templates/task-context.md +19 -0
  3. package/CHANGELOG.md +78 -0
  4. package/README.md +210 -36
  5. package/adapters/_shared/codex-assets.cjs +8 -3
  6. package/adapters/_shared/interaction-contract.cjs +13 -6
  7. package/adapters/_shared/runtime-assets.cjs +1 -1
  8. package/adapters/claude.js +1 -1
  9. package/adapters/kimi.js +1 -1
  10. package/adapters/opencode.js +24 -13
  11. package/docs/AGENT-CONTEXT.md +58 -51
  12. package/docs/ARCHITECTURE.md +82 -51
  13. package/docs/ARTIFACT-AUTHORITY.md +83 -44
  14. package/docs/COMMIT-HASH-BACKFILL.md +33 -90
  15. package/docs/DECISIONS.md +9 -7
  16. package/docs/LEGACY-CLI-CROSSWALK.md +10 -2
  17. package/docs/PLUGIN-ISOLATION-CONTRACT.md +3 -2
  18. package/docs/ROADMAP.md +3 -3
  19. package/docs/RUNTIME-COMPAT-MATRIX.md +13 -10
  20. package/docs/STATE-DB-ACCESS-POLICY.md +47 -19
  21. package/docs/WORKFLOW-LIFECYCLE.md +79 -49
  22. package/hooks/README.md +5 -4
  23. package/hooks/active_task_context.py +41 -39
  24. package/mcp-server/lib/artifact-registry.cjs +32 -7
  25. package/mcp-server/lib/baseline-workflow.cjs +60 -24
  26. package/mcp-server/lib/change-workflow.cjs +42 -6
  27. package/mcp-server/lib/context-paths.cjs +34 -3
  28. package/mcp-server/lib/doctor.cjs +28 -0
  29. package/mcp-server/lib/git-bootstrap.cjs +0 -1
  30. package/mcp-server/lib/init-project.cjs +52 -2
  31. package/mcp-server/lib/projector.cjs +43 -8
  32. package/mcp-server/lib/state-authority.cjs +19 -3
  33. package/mcp-server/lib/state-ops.cjs +6 -1
  34. package/mcp-server/lib/task-ledger.cjs +1342 -0
  35. package/mcp-server/lib/ultra-facade.cjs +646 -0
  36. package/mcp-server/lib/workflow-state.cjs +166 -2
  37. package/mcp-server/server.cjs +214 -26
  38. package/package.json +1 -1
  39. package/skills/ultra-change/SKILL.md +38 -90
  40. package/skills/ultra-deliver/SKILL.md +42 -62
  41. package/skills/ultra-dev/SKILL.md +34 -73
  42. package/skills/ultra-doctor/SKILL.md +26 -39
  43. package/skills/ultra-init/SKILL.md +37 -69
  44. package/skills/ultra-plan/SKILL.md +52 -125
  45. package/skills/ultra-research/SKILL.md +41 -101
  46. package/skills/ultra-review/SKILL.md +25 -44
  47. package/skills/ultra-status/SKILL.md +21 -40
  48. package/skills/ultra-test/SKILL.md +25 -60
  49. package/skills/ultra-think/SKILL.md +29 -48
  50. package/skills/ultra-think/references/decision-dialogue.md +6 -4
  51. package/spec/cli-protocol.md +15 -0
  52. package/spec/mcp-tools.yaml +286 -0
  53. package/spec/schemas/context-file.v4.5.schema.json +1 -1
  54. package/spec/schemas/mcp-tools.schema.json +1 -1
  55. package/spec/schemas/state-db.sql +1 -1
  56. package/spec/schemas/task-ledger.v1.schema.json +217 -0
  57. package/ultra-tools/commands/migrate.cjs +5 -2
  58. package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
@@ -3,12 +3,14 @@
3
3
  > Multi-process access contract. Current authority is defined by
4
4
  > [`DECISIONS.md`](./DECISIONS.md) and the live database schema.
5
5
 
6
- `.ultra/.runtime/state.db` is the only lifecycle, index, transition, freshness, and
7
- coordination authority for baselines, changes, decision threads/items, tasks,
6
+ `.ultra/.runtime/state.db` is the checkout-local lifecycle, index, transition,
7
+ freshness, and coordination authority for baselines, changes, decision threads/items, tasks,
8
8
  workflow runs/steps, events, sessions, incidents, projections, telemetry, and spec
9
9
  references (D18, D52, D54). Registered digest-bound files carry semantic and evidence
10
- bodies; the DB owns their references and current digests. Every process
11
- that touches it must follow the rules below; deviations are bugs.
10
+ bodies; the DB owns their references and current digests. The tracked
11
+ `.ultra/tasks/tasks.json` is an MCP-published handoff checkpoint, not a parallel live
12
+ writer. Every process that touches the DB must follow the rules below; deviations are
13
+ bugs.
12
14
 
13
15
  ## 1. Three-role write matrix
14
16
 
@@ -24,7 +26,7 @@ that touches it must follow the rules below; deviations are bugs.
24
26
  but never `UPDATE` / `DELETE`.
25
27
 
26
28
  The CLI may indirectly mutate ordinary workflow tables by spawning the MCP server over
27
- stdio and calling `task.update`, `session.spawn`, etc. — but the actual
29
+ stdio and calling the public `ultra.*` kernel — but the actual
28
30
  SQLite write is performed by the MCP server's writer connection. The documented
29
31
  exceptions are initial schema/baseline creation, migration, and explicitly
30
32
  authorized backup-first doctor recovery. The CLI never provides a parallel raw
@@ -34,8 +36,8 @@ The append-only carve-out for `events` exists because `events.id INTEGER
34
36
  PRIMARY KEY AUTOINCREMENT` makes concurrent inserts collision-free under
35
37
  WAL + `busy_timeout`. This lets hooks and short-lived CLI invocations
36
38
  record audit observations without paying the cost of a full MCP round trip.
37
- It does not grant lifecycle authority: external writers and public
38
- `task.append_event` use the published observation allowlist, while lifecycle event
39
+ It does not grant lifecycle authority: external writers use the published observation
40
+ allowlist, while lifecycle event
39
41
  names are emitted only inside the mutation that owns the corresponding state change.
40
42
  Consumers must verify mutable rows rather than infer success from an event name.
41
43
 
@@ -72,25 +74,48 @@ resolve the mount/runtime constraint before retrying.
72
74
 
73
75
  | Table | Writer of record |
74
76
  |--------------------|--------------------------------------------------------|
75
- | `baselines` | MCP server (`baseline.start` / `baseline.record` / `baseline.converge`); initialization and legacy-projection migration may create only the first draft or compatibility row |
76
- | `tasks` | MCP server (`task.create` / `task.update` / `task.delete`) |
77
- | `changes` | MCP server (`change.create` / `change.update` / `change.converge` / `change.archive`) |
78
- | `decision_threads`, `decision_items` | MCP server (`decision.thread_start` / `decision.open` / `decision.resolve` / `decision.delegate` / `decision.defer` / `decision.supersede` / `decision.complete` / `decision.checkpoint`); pending recovery state, normalized accepted intent, durable effects, typed applied references, lifecycle completion, and optional artifact checkpoints are stored, never UI receipts, prompts, or transcripts |
79
- | `workflow_runs`, `workflow_steps` | MCP server (`workflow.start` / `workflow.step` / `workflow.complete`); skills provide evidence inputs but never write rows directly |
80
- | `artifacts`, `artifact_edges` | MCP server (`artifact.record`); workflow-owned registration may use the same internal registry transaction |
81
- | `context_snapshots`, `spec_learning_candidates`, `trace_links` | MCP server through change lifecycle tools |
77
+ | `baselines` | MCP server through `ultra.record`, `ultra.checkpoint`, and `ultra.archive`; initialization and legacy migration may create only the first draft or compatibility row |
78
+ | `tasks` | MCP server through `ultra.record`, `ultra.checkpoint`, and `ultra.sync` |
79
+ | `changes` | MCP server through `ultra.record`, `ultra.checkpoint`, and `ultra.archive` |
80
+ | `decision_threads`, `decision_items` | MCP server through `ultra.record`; normalized accepted intent and applied references are stored, never UI receipts, prompts, or transcripts |
81
+ | `workflow_runs`, `workflow_steps` | MCP server internally through `ultra.checkpoint`; Skills supply semantic evidence but never drive individual rows |
82
+ | `artifacts`, `artifact_edges` | MCP server through `ultra.record` and checkpoint-owned registration |
83
+ | `context_snapshots`, `spec_learning_candidates`, `trace_links` | MCP server through `ultra.record` and `ultra.checkpoint` |
82
84
  | `incidents`, `projection_jobs`, `event_consumers`, `circuit_breaker` | MCP server; backup-first doctor recovery may perform only documented mechanical transitions |
83
- | `events` | MCP server for lifecycle events; approved processes and `task.append_event` for allowlisted append-only observations |
84
- | `sessions` | MCP server (`session.spawn` / `session.close` / `session.heartbeat`); orchestrator may write status transitions |
85
+ | `events` | MCP server for lifecycle events; approved processes and `ultra.record` for allowlisted append-only observations |
86
+ | `sessions` | MCP server through `ultra.session`; orchestrator may write status transitions |
85
87
  | `telemetry` | MCP server (collected from tool-call wrappers); orchestrator may dump bulk samples |
86
88
  | `specs_refs` | MCP server (rebuilt on `spec_changed` event); orchestrator may rebuild |
87
89
  | `migration_history`| schema initializer and `ultra-tools migrate` or doctor repair |
88
90
  | `schema_version` | schema initializer, `ultra-tools db init`, migration, and doctor repair |
89
91
 
90
- ## 5. Forbidden patterns
92
+ ## 5. Git team checkpoint
93
+
94
+ Only `ultra.sync { action: publish }` may derive `.ultra/tasks/tasks.json` from SQLite,
95
+ and only `ultra.sync { action: import }` may merge it into SQLite. Server startup performs one import after
96
+ opening project authority; later pulls in a long-running process require an explicit
97
+ import. The same checkpoint is idempotent and must not append an event again.
98
+
99
+ The checkpoint carries portable baseline, Change, and durable task records with
100
+ per-record revisions and digests. It excludes local `in_progress` ownership, sessions,
101
+ leases, worktrees, telemetry, and completion commit hashes. Import validates document
102
+ ancestry plus record-level common bases. It fast-forwards clean records and fails
103
+ before commit on same-record concurrency, parent/owner gaps, non-descendant history, or
104
+ an active-task collision. A Git-ready baseline is downgraded to checkout-local
105
+ revalidation rather than trusted as execution authority.
106
+
107
+ Legacy v4.4/v4.5 task projections are backed up and replaced only when every available
108
+ durable field matches current SQLite. Doctor reports checkpoint condition but never
109
+ performs semantic import, publication, or conflict selection.
110
+
111
+ ## 6. Forbidden patterns
91
112
 
92
113
  - **No file copies for state.** Don't `cp .ultra/.runtime/state.db
93
114
  somewhere/state.db` and edit; use `ultra-tools db backup`.
115
+ - **No direct checkpoint edits.** Don't merge or repair
116
+ `.ultra/tasks/tasks.json` by hand; use the typed import/publish conflict path.
117
+ - **No committed live projections.** `.ultra/.runtime/projections/**` remains ignored
118
+ checkout-local output.
94
119
  - **No long-held writer connections** outside the MCP server. Pop a
95
120
  short transaction, finish, close.
96
121
  - **No `PRAGMA journal_mode` toggling at runtime** by any process other
@@ -100,14 +125,14 @@ resolve the mount/runtime constraint before retrying.
100
125
  - **No `vacuum` / `wal_checkpoint(TRUNCATE)` from inside a transaction.**
101
126
  Maintenance subcommands open their own connection.
102
127
 
103
- ## 6. Maintenance windows
128
+ ## 7. Maintenance windows
104
129
 
105
130
  `ultra-tools db checkpoint` and `ultra-tools db vacuum` are safe to run
106
131
  while the MCP server is up (they take their own connection and obey the
107
132
  busy timeout). `db backup` uses better-sqlite3's online `.backup` API
108
133
  and produces a consistent snapshot without blocking writers.
109
134
 
110
- ## 7. Verification
135
+ ## 8. Verification
111
136
 
112
137
  The contract on this page is enforced by:
113
138
 
@@ -119,3 +144,6 @@ The contract on this page is enforced by:
119
144
  status transitions, task contracts, and event coupling.
120
145
  - `mcp-server/lib/workflow-state.test.cjs` — ordered workflow transitions,
121
146
  evidence/output requirements, blocking/resume, and cross-stage gates.
147
+ - `mcp-server/lib/task-ledger.test.cjs` — portable-field filtering, digest ancestry,
148
+ idempotent import, baseline/Change/task fast-forward and conflict behavior, legacy
149
+ backup, and active-session protection.
@@ -11,29 +11,33 @@ host and project owner. It is the operational companion to
11
11
  |---|---|---|
12
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, valid transitions, hard recovery requirements, task contracts, evidence references, output hashes, and gate verdicts | Semantic route selection, research prose, code generation, general memory-provider payloads, or model calls |
15
- | `.ultra/.runtime/state.db` | Lifecycle, index, transition, freshness, and coordination authority for baseline, change, decision, task, workflow, session, event, incident, projection, and evidence references | Prompts, transcripts, general external-memory payloads, semantic prose, or code-graph payloads |
14
+ | MCP | Durable checkpoints, task contracts, evidence references, output hashes, team sync, leases, archives, and mechanical recovery | Semantic route selection, research prose, code generation, general memory-provider payloads, or model calls |
15
+ | `.ultra/.runtime/state.db` | Checkout-local lifecycle, index, transition, freshness, and coordination authority for baseline, change, decision, task, workflow, session, event, incident, projection, and evidence references | Prompts, transcripts, general external-memory payloads, semantic prose, or code-graph payloads |
16
+ | `.ultra/tasks/tasks.json` | MCP-published Git checkpoint for portable baseline, Change, task-contract, dependency, and durable status records | Live sessions, leases, `in_progress` ownership, completion hashes, or a parallel write API |
16
17
  | Digest-bound artifacts | Registered specification, research, Change, context, test, review, delivery, and verification bodies | Independent lifecycle state or content whose digest no longer matches |
17
- | Generated projections and scratch | Human-readable views and temporary working material | Lifecycle, semantic, or evidence authority before supported promotion |
18
- | 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
+ | Generated projections and scratch | Checkout-local views below `.ultra/.runtime/` and temporary working material | Lifecycle, semantic, or evidence authority before supported promotion |
19
+ | Hook | Compact DB-derived lifecycle observation, minimal event metadata, and protection of MCP-owned checkpoint/projection paths | Generic engineering judgment, prompt/transcript capture, parallel lifecycle logs, or arbitrary edit blocking |
19
20
 
20
21
  Together, `.ultra/` is project-local cross-session workflow memory. The detailed
21
22
  artifact classes and promotion rules live in
22
23
  [`ARTIFACT-AUTHORITY.md`](./ARTIFACT-AUTHORITY.md).
23
24
 
24
- Prompt input can supply facts, content paths, reasons, owner decisions, and
25
- evidence references. MCP reads the current repository and DB, validates those
26
- inputs, hashes outputs, and derives status. Prompt input cannot override a task's
27
- public seam, verification command, context references, workflow summary, gate
28
- verdict, output digest, or transition validity. A host recommendation is never stored
29
- as MCP authority.
25
+ Model input can supply facts, content paths, reasons, owner decisions, and evidence
26
+ references. `ultra.record` stores mutable typed drafts and `ultra.checkpoint` attempts
27
+ one durable semantic commit. A semantic rejection returns diagnostics and keeps the
28
+ draft editable. Model input cannot override a task's public seam, evidence digest, or
29
+ accepted checkpoint. A host recommendation is never stored as MCP authority.
30
30
 
31
- Load-bearing owner choices are normalized through `decision.*`; Prompt prose is not
32
- durable authority. The host acquires observable facts autonomously, exposes one
33
- current question, and stops. A workflow cannot advance while a matching decision has
34
- an unanswered blocking question, a blocking deferral, a prepared checkpoint awaiting
35
- confirmation, or a stale confirmed artifact. A normalized answer alone does not require
36
- a ceremonial checkpoint.
31
+ Load-bearing owner choices are normalized through `ultra.record`; prompt prose is not
32
+ durable authority. The host acquires observable facts autonomously, exposes one current
33
+ question, and stops. Unanswered material choices remain warnings or checkpoint
34
+ diagnostics until a capability truly needs their result. A normalized answer alone
35
+ does not require a ceremonial checkpoint.
36
+
37
+ Every host discovers seven public MCP tools: `ultra.context`, `ultra.record`,
38
+ `ultra.checkpoint`, `ultra.sync`, `ultra.session`, `ultra.archive`, and
39
+ `ultra.doctor`. The 0.22 fine-grained operations documented below remain hidden
40
+ compatibility internals for one release. Skills must not drive them one call at a time.
37
41
 
38
42
  Every change-bound research, plan, dev, test, review, and deliver run inherits
39
43
  the baseline id from the owning change workflow. A Prompt cannot switch that
@@ -76,8 +80,10 @@ signals. In a monorepo, the owner selects the repository scope; baseline
76
80
  worktree digests and accepted dirty files are restricted to that scope.
77
81
 
78
82
  Classification and readiness are separate. Initialization immediately creates
79
- or resumes `.ultra/.runtime/state.db`, installs only the required scaffold, records the
80
- classification, and completes an `init` workflow after read-back verification. It
83
+ or resumes `.ultra/.runtime/state.db`, creates or imports the Git team checkpoint,
84
+ installs only the required scaffold, records the classification, and completes an
85
+ `init` workflow after read-back verification. A ready checkpoint baseline is held for
86
+ checkout-local revalidation. Init
81
87
  does not create a research run. A later explicit `ultra-research` invocation selects
82
88
  `full` for greenfield or `adoption` for brownfield, records coverage dispositions, and
83
89
  works toward baseline readiness. Empty templates are not an approved baseline.
@@ -187,8 +193,10 @@ execution failure.
187
193
  ### Workflow run and step
188
194
 
189
195
  ```text
190
- run: active <-> blocked -> ready -> completed
191
- \----> cancelled
196
+ draft: active <-> blocked <-> ready
197
+ | | |
198
+ +----------+---------+----> cancelled
199
+ \----> completed (accepted, immutable)
192
200
 
193
201
  step: pending -> in_progress -> completed
194
202
  | |
@@ -196,10 +204,10 @@ step: pending -> in_progress -> completed
196
204
  blocked ------> in_progress/completed
197
205
  ```
198
206
 
199
- Required steps execute in definition order. A step that requires evidence or an
200
- output cannot complete without it. When the final required step completes, the
201
- run becomes `ready`; `workflow.complete` revalidates the entire stage before
202
- making it `completed`.
207
+ Fine-grained steps are internal evidence bookkeeping. `ultra.checkpoint` records and
208
+ validates them in one public call. A missing output returns mutable diagnostics. Draft
209
+ artifact changes reopen `ready` to `active`; only an accepted `completed` revision is
210
+ immutable.
203
211
 
204
212
  ### Decision thread and item
205
213
 
@@ -226,24 +234,41 @@ stores the conversation transcript.
226
234
 
227
235
  | Stage | Authoritative writes | Completion verification | Available continuation |
228
236
  |---|---|---|---|
229
- | `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` |
237
+ | `init` | Baseline classification/scope, Git bootstrap state, completed init run, initialization event, empty or imported team checkpoint, projection job | DB/schema opens, scaffold/local projection/checkpoint and read-back succeed; existing Git is preserved or missing local Git is initialized; imported ready baseline is not trusted before local revalidation | Explicitly chosen `research`, `status`, or `doctor`; an already-ready project may also enter `change` |
230
238
  | `research` | Accepted coverage dispositions, selected step evidence, normalized material decisions, optional artifact checkpoints, typed semantic records with source digests, immutable reports, output paths and SHA-256 digests | Every included coverage area is dispositioned; only executed/verified/reused areas require work evidence; baseline synthesis binds current specifications and distillate, while Change synthesis stays in its overlay; all material decisions and digests are current | Baseline record/convergence for initial research; bounded Change research records its delta and returns to plan |
231
- | 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 |
232
- | `change` | Complete Change Contract, profile/risk rationale, research disposition, isolated Change root, typed delta, and a completed capture run; a decision thread only when material intent remains unresolved | Intent, acceptance, recovery, classification, research disposition, and exact baseline anchors are current | Model recommends; owner selects bounded `research` or direct `plan`; both routes converge on plan before development |
233
- | `plan` | Accepted planning posture, immutable `plan/planning` context, selected planning evidence, complete change-owned task rows, acceptance coverage matrix, and deterministic `<artifact_root>/plan.json` plus `plan.md`; approval only for a material owner decision | Every Change acceptance id is owned by a task; trace targets, ownership, dependencies, topology, task contracts, planning-context binding, and exported digests match current authority | Any dependency-ready task may enter `dev` |
234
- | `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` |
239
+ | baseline convergence | Spec/source/runtime refs, verification results, known failures, unknowns, gaps, branch/HEAD/worktree, approval, and a published team checkpoint | 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 |
240
+ | `change` | Complete Change Contract, profile/risk rationale, research disposition, isolated Change root, typed delta, completed capture run, and portable Change checkpoint; a decision thread only when material intent remains unresolved | Intent, acceptance, recovery, classification, research disposition, exact baseline anchors, and checkpoint digest are current | Model recommends; owner selects bounded `research` or direct `plan`; both routes converge on plan before development |
241
+ | `plan` | Accepted planning posture, immutable `plan/planning` context, selected planning evidence, complete change-owned task rows, acceptance coverage matrix, deterministic `<artifact_root>/plan.json` plus `plan.md`, and published task contracts; approval only for a material owner decision | Every Change acceptance id is owned by a task; trace targets, ownership, dependencies, topology, task contracts, planning-context binding, exported digests, and checkpoint generation match current authority | Any dependency-ready task may enter `dev` |
242
+ | `dev` | Task/session transitions, real worktree, step evidence, immutable implementation context, completion/review references, and published durable task transitions | Task completed; no live session; task contract unchanged; starting context and review artifacts valid; the single completion commit is integrated and remains an ancestor of current HEAD; its hash is backfilled locally without another commit | Next task, then aggregate `test` |
235
243
  | `test` | Immutable checking context, an explicit risk-selected verification profile, and `<artifact_root>/test/<workflow-id>/report.json` | Selected dimensions and exact Change acceptance ids 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` |
236
244
  | `review` | Immutable review context plus risk-selected specialist and summary artifacts below `<artifact_root>/review/<workflow-id>/` | 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 |
237
245
  | `deliver` | Convergence context, typed delta, documentation reconciliation, recoverable overlay transaction, self-contained archive, and immutable delivery report | Tasks/test/change-review and acceptance are current; every before/after digest and baseline anchor is current; the archive transaction is complete; the report contains no release action | Report local completion or start the next `change`; publish/deploy/push are separate explicitly authorized operations |
238
- | `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 |
239
- | `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 |
246
+ | `status` | None | Reads doctor, team-checkpoint condition, baseline, workflows, breadcrumb, tasks, sessions, Git, and DB-referenced reports | Shows allowed transitions, any hard required transition, and a host-owned recommendation |
247
+ | `doctor` | None by default; explicit repair writes only mechanical recovery state | Re-runs integrity, schema, workflow-output, local projection, team-checkpoint, session, incident, archive, and install checks without choosing import/publish conflict resolution | Mechanically required recovery when unique; otherwise a set of safe recovery capabilities |
240
248
 
241
- Every mutating MCP operation appends an event and enqueues projection work when
242
- the corresponding read-only view can change. Projection failure is recorded as
249
+ Every mutating MCP operation appends an event and enqueues local projection work when
250
+ the corresponding checkout-local read-only view can change. Projection failure is recorded as
243
251
  a retryable incident; it does not silently convert a failed write into success.
252
+ Semantic handoff operations separately publish the Git team checkpoint.
244
253
  `task.append_event` accepts only non-authoritative observations. Lifecycle events are
245
254
  emitted by their owning DB mutation and never act as a substitute state transition.
246
255
 
256
+ ### Team handoff checkpoint
257
+
258
+ The Git checkpoint is intentionally lower-frequency than SQLite. It publishes accepted
259
+ baseline, Change, plan, task-contract, and durable task-status boundaries; it does not
260
+ mirror heartbeats, leases, active processes, telemetry, or provisional completion
261
+ hashes. On MCP startup or explicit post-pull import, the receiving checkout validates
262
+ checkpoint ancestry and record digests. Clean baseline, Change, and task records
263
+ fast-forward independently. Concurrent edits to the same record or a remote edit to a
264
+ locally active task stop without partial import.
265
+
266
+ A checkpoint can restore portable project intent after clone, but cannot establish
267
+ checkout truth by itself. An imported ready baseline becomes a local adoption/draft
268
+ with `team-ledger-revalidation-required`; current scope content, HEAD, verification,
269
+ known failures, and approval must converge again. Because baseline content hashing
270
+ excludes `.ultra/`, committing the checkpoint cannot make its own baseline stale.
271
+
247
272
  ## 5. Human-agent alignment lifecycle
248
273
 
249
274
  The host first reads `decision.list` and resumes matching authority. It starts a new
@@ -295,7 +320,8 @@ opens one replacement.
295
320
 
296
321
  ## 6. Context and prompt follow-through
297
322
 
298
- `change.context` compiles an immutable Context Manifest v3 snapshot. It combines:
323
+ The Context compiler used internally by `ultra.checkpoint` creates an immutable
324
+ Context Manifest v3 snapshot. It combines:
299
325
 
300
326
  - the bound baseline/change/task identity;
301
327
  - a role and lifecycle gate;
@@ -308,11 +334,10 @@ opens one replacement.
308
334
  - optional metadata-only external memory or graph references;
309
335
  - advisory file/token/context-share budgets, including inline Change/task authority;
310
336
  - the current Change and task authority digests;
311
- - readiness blockers, `allowed_transitions`, and a `required_transition` only for a
312
- unique hard-recovery path.
337
+ - readiness warnings and hard mechanical blockers without a semantic route command.
313
338
 
314
339
  Compilation is pure with respect to accepted Change authority. Provider references are
315
- updated only through `change.update`; they cannot be smuggled into `change.context`.
340
+ recorded through `ultra.record`; they cannot be smuggled into a checkpoint context.
316
341
  Context references retain `expected_digest`, `anchor`, `scope`, and
317
342
  `freshness_policy`. `digest` requires byte-current content, `existence` allows an
318
343
  implementation target to change while remaining present, and `advisory` reports drift
@@ -329,9 +354,9 @@ Critical stage workflows bind a matching snapshot as a durable output:
329
354
  | `deliver` | `check` | `convergence` |
330
355
 
331
356
  Every lookup is exact on `change_id + task_id + role + gate`. The snapshot path and
332
- digest are carried into plan, test, review, and delivery artifacts. `plan.export`
333
- writes only the owning Change's `plan.json` and deterministic `plan.md`, and binds the
334
- exact planning snapshot and digest to both the artifact registry and `verify-plan`.
357
+ digest are carried into plan, test, review, and delivery artifacts. The Plan
358
+ `ultra.checkpoint` writes the owning Change's `plan.json` and deterministic `plan.md`
359
+ once, and binds the exact planning snapshot and digest to the artifact registry.
335
360
  The legacy global `.ultra/execution-plan.json` is read-only migration compatibility,
336
361
  not current authority. These rules prevent a later Prompt or a different role packet
337
362
  from claiming evidence gathered under another context. Referenced file content remains
@@ -361,13 +386,17 @@ evidence do block.
361
386
  overwrite historical evidence.
362
387
  - `.ultra` workflow artifacts are excluded from application-worktree drift, but
363
388
  their own recorded SHA-256 digests are still authoritative.
364
- - Generated `tasks.json` and complete task-context files are projections. Manual edits
365
- are overwritten and never repair DB authority.
389
+ - Generated `.ultra/.runtime/projections/tasks.json` and complete runtime task-context
390
+ files are local projections. Manual edits are overwritten and never repair DB
391
+ authority.
392
+ - `.ultra/tasks/tasks.json` is a tracked MCP checkpoint, not a local projection.
393
+ Its full and per-record digests reject manual edits; publish/import performs typed
394
+ fast-forward or conflict handling.
366
395
  - A changed checkpoint artifact digest or superseded decision invalidates the linked
367
396
  decision checkpoint and blocks matching workflow advancement until reconfirmed.
368
- - `workflow.revise` creates a candidate without deleting the completed prior run.
369
- `workflow.supersede` promotes only a completed matching candidate, preserves both
370
- histories, and invalidates only the prior run's transitive registered consumers.
397
+ - A rejected draft is repaired in place or explicitly abandoned. A new accepted
398
+ revision preserves prior accepted history and invalidates only true transitive
399
+ registered consumers.
371
400
 
372
401
  ## 8. Entry flows
373
402
 
@@ -435,13 +464,14 @@ evidence rather than bypassing them.
435
464
  | `ultra-doctor` | read-only diagnosis and authorized mechanical recovery | required recovery only when unique; otherwise safe alternatives |
436
465
 
437
466
  Every row permits `ultra-think` when material owner authority is missing and permits
438
- `ultra-doctor` when state or installed-runtime authority is unhealthy. MCP makes one
439
- of them required only when no safe alternative exists. Neither capability creates a
440
- parallel workflow or substitutes a Prompt summary for DB state.
467
+ `ultra-doctor` when state or installed-runtime authority is unhealthy. The host model
468
+ recommends either from current evidence; hard MCP blockers remain limited to the
469
+ mechanical safety boundary. Neither capability creates a parallel workflow or
470
+ substitutes a prompt summary for DB state.
441
471
 
442
472
  ## 9. Recovery and diagnosis
443
473
 
444
- Use `system.doctor` for project authority and
474
+ Use `ultra.doctor` for project authority and
445
475
  `ubp --all --global --doctor` for installed host assets. Both are read-only
446
476
  unless an explicit project repair command is chosen.
447
477
 
package/hooks/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # Ultra Builder Pro workflow hooks
2
2
 
3
3
  These hooks observe only Ultra-owned state. Session health/context recognizes an initialized
4
- `.ultra/.runtime/state.db` and active continuous changes. Direct projection writes remain blocked;
5
- context, compact, stop, and subagent lifecycle behavior is recovery-oriented and does not turn
6
- advisory workflow conditions into refusal gates.
4
+ `.ultra/.runtime/state.db` and active continuous changes. Direct writes to the MCP-owned Git
5
+ checkpoint and checkout-local projections remain blocked; context, compact, stop, and subagent
6
+ lifecycle behavior is recovery-oriented and does not turn advisory workflow conditions into
7
+ refusal gates.
7
8
 
8
9
  | Lifecycle | Hook | Purpose |
9
10
  |---|---|---|
10
11
  | Session start | `health_check.py` | Read-only integrity, incident, projection, session, and change-artifact checks |
11
12
  | Shared helper | `context_spine.py` | Read the latest role/gate/readiness snapshot from state.db and derive one compact breadcrumb |
12
13
  | Session start | `workflow_context.py` | Inject the DB-derived breadcrumb with bounded normalized accepted intent, never raw interaction or provider payloads |
13
- | Before edit | `active_task_context.py` | Protect `tasks.json` and restate the same DB-derived task breadcrumb |
14
+ | Before edit | `active_task_context.py` | Protect `.ultra/tasks/tasks.json` (Git team checkpoint) and `.ultra/.runtime/projections/tasks.json` (local view), then restate the same DB-derived task breadcrumb |
14
15
  | Before compact | `workflow_checkpoint.py` | Validate and atomically save a minimal workflow checkpoint |
15
16
  | After compact/resume | `workflow_resume.py` | Prefer the DB breadcrumb; restore the minimal file checkpoint only when no active change owns recovery |
16
17
  | Stop | `pre_stop_check.py` | Report an incomplete workflow boundary and allow stop |
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Protect Ultra projections and restate an active task boundary before edits."""
2
+ """Protect Ultra managed task files and restate the active workflow boundary."""
3
3
 
4
4
  import json
5
5
  import sys
@@ -12,7 +12,9 @@ from context_spine import (
12
12
  render_breadcrumb,
13
13
  )
14
14
 
15
- TASKS_PROJECTION = Path(".ultra/tasks/tasks.json")
15
+ TEAM_TASK_LEDGER = Path(".ultra/tasks/tasks.json")
16
+ LIVE_TASK_PROJECTION = Path(".ultra/.runtime/projections/tasks.json")
17
+ PROTECTED_TASK_FILES = (TEAM_TASK_LEDGER, LIVE_TASK_PROJECTION)
16
18
 
17
19
 
18
20
  def tool_paths(data: dict, start: Path):
@@ -28,25 +30,24 @@ def tool_paths(data: dict, start: Path):
28
30
  yield candidate.resolve() if candidate.is_absolute() else (start / candidate).resolve()
29
31
 
30
32
 
31
- def projection_root_for_target(target: Path) -> Path | None:
32
- """Return the project root only for the exact generated tasks projection."""
33
- if (
34
- target.name == "tasks.json"
35
- and target.parent.name == "tasks"
36
- and target.parent.parent.name == ".ultra"
37
- ):
38
- return target.parent.parent.parent
33
+ def protected_root_for_target(target: Path) -> Path | None:
34
+ """Return the project root for an exact MCP-managed task file."""
35
+ parts = target.parts
36
+ for relative in PROTECTED_TASK_FILES:
37
+ suffix = relative.parts
38
+ if len(parts) >= len(suffix) and parts[-len(suffix):] == suffix:
39
+ return Path(*parts[:-len(suffix)])
39
40
  return None
40
41
 
41
42
 
42
- def targets_tasks_projection(data: dict, root: Path) -> bool:
43
+ def targets_managed_task_file(data: dict, root: Path) -> bool:
43
44
  for resolved in tool_paths(data, root):
44
- if resolved == (root / TASKS_PROJECTION).resolve():
45
+ if any(resolved == (root / relative).resolve() for relative in PROTECTED_TASK_FILES):
45
46
  return True
46
47
  return False
47
48
 
48
49
 
49
- def projection_denial(reason: str) -> dict:
50
+ def managed_file_denial(reason: str) -> dict:
50
51
  return {"hookSpecificOutput": {
51
52
  "hookEventName": "PreToolUse",
52
53
  "permissionDecision": "deny",
@@ -54,19 +55,19 @@ def projection_denial(reason: str) -> dict:
54
55
  }}
55
56
 
56
57
 
57
- def conflicted_projection_root(data: dict, start: Path) -> Path | None:
58
+ def conflicted_managed_root(data: dict, start: Path) -> Path | None:
58
59
  for candidate in (start, *start.parents):
59
60
  if not (candidate / ".ultra").exists():
60
61
  continue
61
- if targets_tasks_projection(data, candidate):
62
+ if targets_managed_task_file(data, candidate):
62
63
  return candidate
63
64
  return None
64
65
 
65
66
 
66
- def projection_roots(data: dict, start: Path):
67
+ def managed_roots(data: dict, start: Path):
67
68
  """Yield initialized project roots derived from every resolved tool target."""
68
69
  for target in tool_paths(data, start):
69
- root = projection_root_for_target(target)
70
+ root = protected_root_for_target(target)
70
71
  if root is not None and (root / ".ultra").exists():
71
72
  yield root
72
73
 
@@ -82,23 +83,24 @@ def main() -> None:
82
83
  # An absolute tool target carries its own project boundary. Resolve and
83
84
  # protect that authority before consulting cwd, because hooks may run from
84
85
  # a host-owned temporary directory or another repository.
85
- for target_root in projection_roots(data, start):
86
+ for target_root in managed_roots(data, start):
86
87
  try:
87
88
  target_authority = find_root(target_root)
88
89
  except ContextSpineError as exc:
89
- print(json.dumps(projection_denial(
90
- "Refusing a direct write to an Ultra projection while the "
90
+ print(json.dumps(managed_file_denial(
91
+ "Refusing a direct write to an MCP-managed Ultra task file while the "
91
92
  f"runtime authority is conflicted: {exc}. Resolve the "
92
- "state.db conflict with ultra-doctor before editing generated "
93
- "project artifacts."
93
+ "state.db conflict with ultra-doctor before retrying the owning "
94
+ "MCP operation."
94
95
  )))
95
96
  return
96
97
  if target_authority is None:
97
98
  continue
98
- print(json.dumps(projection_denial(
99
- "Refusing a direct write to the .ultra/tasks/tasks.json projection. "
100
- ".ultra/.runtime/state.db is authoritative; use the Ultra MCP task tools and "
101
- "run ultra-doctor when state or projection health is degraded."
99
+ print(json.dumps(managed_file_denial(
100
+ "Refusing a direct write to an MCP-managed Ultra task file. "
101
+ ".ultra/tasks/tasks.json is the MCP-published team checkpoint and "
102
+ ".ultra/.runtime/projections/tasks.json is the checkout-local DB view. "
103
+ "Use the Ultra MCP task or task.ledger tools."
102
104
  )))
103
105
  return
104
106
 
@@ -110,26 +112,26 @@ def main() -> None:
110
112
  file=sys.stderr,
111
113
  )
112
114
  # Ordinary context injection remains fail-open. A direct write to a
113
- # known generated projection is different: an authority conflict
114
- # means the hook cannot prove which DB would regenerate that file, so
115
- # allowing the write would destroy the projection boundary.
116
- if conflicted_projection_root(data, start) is not None:
117
- print(json.dumps(projection_denial(
118
- "Refusing a direct write to an Ultra projection while the "
115
+ # known managed task file is different: an authority conflict means the
116
+ # hook cannot prove which DB owns the publish or projection operation.
117
+ if conflicted_managed_root(data, start) is not None:
118
+ print(json.dumps(managed_file_denial(
119
+ "Refusing a direct write to an MCP-managed Ultra task file while the "
119
120
  f"runtime authority is conflicted: {exc}. Resolve the "
120
- "state.db conflict with ultra-doctor before editing generated "
121
- "project artifacts."
121
+ "state.db conflict with ultra-doctor before retrying the owning "
122
+ "MCP operation."
122
123
  )))
123
124
  return
124
125
  root = None
125
126
  if root is None:
126
127
  print(json.dumps({}))
127
128
  return
128
- if targets_tasks_projection(data, root):
129
- print(json.dumps(projection_denial(
130
- "Refusing a direct write to the .ultra/tasks/tasks.json projection. "
131
- ".ultra/.runtime/state.db is authoritative; use the Ultra MCP task tools and "
132
- "run ultra-doctor when state or projection health is degraded."
129
+ if targets_managed_task_file(data, root):
130
+ print(json.dumps(managed_file_denial(
131
+ "Refusing a direct write to an MCP-managed Ultra task file. "
132
+ ".ultra/tasks/tasks.json is the MCP-published team checkpoint and "
133
+ ".ultra/.runtime/projections/tasks.json is the checkout-local DB view. "
134
+ "Use the Ultra MCP task or task.ledger tools."
133
135
  )))
134
136
  return
135
137
  try:
@@ -27,7 +27,7 @@ const STATIC_EXEMPT_PATHS = new Set([
27
27
  '.ultra/docs/research/README.md',
28
28
  '.ultra/reports/templates/delivery-report.json',
29
29
  '.ultra/reports/templates/test-report.json',
30
- '.ultra/tasks/contexts/TEMPLATE.md',
30
+ '.ultra/templates/task-context.md',
31
31
  '.ultra/tasks/tasks.json',
32
32
  '.ultra/changes/active/.gitkeep',
33
33
  '.ultra/changes/archive/.gitkeep',
@@ -81,9 +81,10 @@ function isExemptArtifactPath(value) {
81
81
 
82
82
  function isGeneratedProjectionPath(value) {
83
83
  const relative = normalizeRelativePath(value);
84
- return relative === '.ultra/tasks/contexts'
85
- || relative.startsWith('.ultra/tasks/contexts/')
86
- || relative === '.ultra/tasks/tasks.json';
84
+ return relative === '.ultra/.runtime/projections'
85
+ || relative.startsWith('.ultra/.runtime/projections/')
86
+ || relative === '.ultra/tasks/contexts'
87
+ || relative.startsWith('.ultra/tasks/contexts/');
87
88
  }
88
89
 
89
90
  function artifactPathError(message, details = undefined) {
@@ -504,10 +505,34 @@ function invalidateConsumersFromEndpointInTx(db, start, {
504
505
  }
505
506
  if (endpoint.type === 'workflow') {
506
507
  const row = db.prepare(
507
- 'SELECT metadata_json, change_id, task_id FROM workflow_runs WHERE id = ?',
508
+ 'SELECT status, metadata_json, change_id, task_id FROM workflow_runs WHERE id = ?',
508
509
  ).get(endpoint.id);
509
510
  if (row) {
510
511
  const metadata = parseJson(row.metadata_json, {});
512
+ if (['active', 'blocked', 'ready'].includes(row.status)) {
513
+ delete metadata.authority_invalidation;
514
+ metadata.draft_dirty = true;
515
+ db.prepare(
516
+ `UPDATE workflow_runs
517
+ SET status = CASE WHEN status = 'ready' THEN 'active' ELSE status END,
518
+ metadata_json = ?,
519
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
520
+ WHERE id = ?`,
521
+ ).run(JSON.stringify(metadata), endpoint.id);
522
+ ops.appendEventInTx(db, {
523
+ type: 'workflow_draft_changed',
524
+ change_id: row.change_id,
525
+ task_id: row.task_id,
526
+ payload: {
527
+ workflow_id: endpoint.id,
528
+ source_type: start.type,
529
+ source_id: start.id,
530
+ reason,
531
+ },
532
+ });
533
+ queue.push(...outgoingEdges(db, endpoint));
534
+ continue;
535
+ }
511
536
  if (metadata.authority_invalidation?.invalidated !== true) {
512
537
  metadata.authority_invalidation = {
513
538
  invalidated: true,
@@ -950,7 +975,7 @@ function knownArtifactPaths(db) {
950
975
  }
951
976
  for (const row of db.prepare('SELECT id, context_file FROM tasks').all()) {
952
977
  known.add(normalizeRelativePath(
953
- row.context_file || `.ultra/tasks/contexts/task-${row.id}.md`,
978
+ contextPaths.resolveContextPath('.', row.context_file, { taskId: row.id }).relative,
954
979
  ));
955
980
  }
956
981
  return known;
@@ -1078,7 +1103,7 @@ function inspectArtifactHealth(db, { rootDir = process.cwd() } = {}) {
1078
1103
  for (const relative of tree.files) {
1079
1104
  if (isExemptArtifactPath(relative) || known.has(relative)) continue;
1080
1105
  if (!readyBaseline && PROVISIONAL_BASELINE_SPEC_PATHS.has(relative)) continue;
1081
- if (relative.startsWith(`${contextPaths.CONTEXT_ROOT_RELATIVE}/`)) {
1106
+ if (relative.startsWith(`${contextPaths.LEGACY_CONTEXT_ROOT_RELATIVE}/`)) {
1082
1107
  let contents;
1083
1108
  try {
1084
1109
  contents = readStableProjectFile(rootDir, relative).bytes;