ultra-builder-pro-cli 0.25.1 → 0.26.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 (241) hide show
  1. package/.ultra-template/contexts/TEMPLATE.md +87 -0
  2. package/.ultra-template/decisions/.gitkeep +1 -0
  3. package/.ultra-template/evidence/.gitkeep +1 -0
  4. package/.ultra-template/north-star.md +46 -0
  5. package/.ultra-template/research/.gitkeep +1 -0
  6. package/.ultra-template/specs/discovery.md +2 -2
  7. package/.ultra-template/tasks.json +3 -0
  8. package/.ultra-template/test-report.json +27 -0
  9. package/CHANGELOG.md +40 -0
  10. package/README.md +217 -579
  11. package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
  12. package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
  13. package/adapters/_shared/file-ops.cjs +76 -2
  14. package/adapters/_shared/host-profile.cjs +97 -0
  15. package/adapters/_shared/plugin-core.cjs +204 -0
  16. package/adapters/_shared/runtime-assets.cjs +31 -96
  17. package/adapters/claude.js +92 -291
  18. package/adapters/codex.js +309 -842
  19. package/adapters/grok.js +143 -1469
  20. package/adapters/kimi.js +124 -1053
  21. package/adapters/opencode.js +230 -742
  22. package/bin/delegate-worker.cjs +323 -0
  23. package/bin/delegate.cjs +456 -0
  24. package/bin/install.js +38 -11
  25. package/docs/ARCHITECTURE.md +122 -216
  26. package/docs/ARTIFACT-AUTHORITY.md +90 -268
  27. package/docs/DECISIONS.md +75 -87
  28. package/docs/PHILOSOPHY.md +177 -0
  29. package/docs/PLUGIN-ISOLATION-CONTRACT.md +62 -39
  30. package/docs/RUNTIME-COMPAT-MATRIX.md +99 -156
  31. package/docs/SKILL-AUTHORING.md +94 -73
  32. package/docs/WORKFLOW-LIFECYCLE.md +93 -189
  33. package/hooks/README.md +17 -37
  34. package/hooks/_common.py +113 -0
  35. package/hooks/adapters/codex.py +9 -16
  36. package/hooks/adapters/grok.py +5 -5
  37. package/hooks/adapters/kimi.py +8 -14
  38. package/hooks/block_dangerous_commands.py +95 -0
  39. package/hooks/compact_context.py +65 -0
  40. package/hooks/mid_workflow_recall.py +25 -0
  41. package/hooks/post_edit_guard.py +105 -0
  42. package/hooks/session_context.py +30 -0
  43. package/package.json +17 -52
  44. package/skills/ultra-change/SKILL.md +97 -50
  45. package/skills/ultra-change/references/change-contract.md +40 -1
  46. package/skills/ultra-delegate/SKILL.md +82 -0
  47. package/skills/ultra-delegate/references/delegation-contract.md +51 -0
  48. package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
  49. package/skills/ultra-deliver/SKILL.md +61 -42
  50. package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
  51. package/skills/ultra-dev/SKILL.md +96 -62
  52. package/skills/ultra-dev/references/debugging.md +22 -0
  53. package/skills/ultra-domain-modeling/SKILL.md +79 -0
  54. package/skills/ultra-grilling/SKILL.md +79 -0
  55. package/skills/ultra-init/SKILL.md +94 -37
  56. package/skills/ultra-init/references/brownfield-adoption.md +33 -83
  57. package/skills/ultra-init/scripts/init_project.cjs +84 -0
  58. package/skills/ultra-plan/SKILL.md +88 -68
  59. package/skills/ultra-research/SKILL.md +62 -48
  60. package/skills/ultra-research/references/00-problem-validation.md +5 -5
  61. package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
  62. package/skills/ultra-research/references/02-market-assessment.md +5 -4
  63. package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
  64. package/skills/ultra-research/references/04-product-strategy.md +3 -3
  65. package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
  66. package/skills/ultra-research/references/10-user-personas.md +3 -2
  67. package/skills/ultra-research/references/11-user-scenarios.md +3 -3
  68. package/skills/ultra-research/references/20-user-stories.md +4 -3
  69. package/skills/ultra-research/references/21-features-scope.md +3 -2
  70. package/skills/ultra-research/references/22-success-metrics.md +3 -3
  71. package/skills/ultra-research/references/30-architecture-context.md +3 -3
  72. package/skills/ultra-research/references/31-solution-strategy.md +3 -3
  73. package/skills/ultra-research/references/32-building-blocks.md +3 -3
  74. package/skills/ultra-research/references/40-deployment.md +3 -3
  75. package/skills/ultra-research/references/41-quality-risks.md +3 -3
  76. package/skills/ultra-research/references/99-synthesis.md +7 -7
  77. package/skills/ultra-review/SKILL.md +59 -32
  78. package/skills/ultra-review/references/code.md +17 -0
  79. package/skills/ultra-review/references/comments.md +12 -0
  80. package/skills/ultra-review/references/design.md +12 -0
  81. package/skills/ultra-review/references/errors.md +13 -0
  82. package/skills/ultra-review/references/spec.md +13 -0
  83. package/skills/ultra-review/references/tests.md +12 -0
  84. package/skills/ultra-review/references/worker-packet.md +40 -0
  85. package/skills/ultra-review/scripts/review_wait.py +26 -15
  86. package/skills/ultra-status/SKILL.md +61 -36
  87. package/skills/ultra-tdd/SKILL.md +81 -0
  88. package/skills/ultra-tdd/references/templates/README.md +20 -0
  89. package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
  90. package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
  91. package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
  92. package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
  93. package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
  94. package/skills/ultra-tdd/references/test-execution.md +18 -0
  95. package/skills/ultra-test/SKILL.md +70 -30
  96. package/skills/ultra-test/references/export-syntax.md +18 -0
  97. package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
  98. package/skills/ultra-think/SKILL.md +49 -27
  99. package/skills/ultra-think/references/autonomy-boundary.md +48 -0
  100. package/skills/ultra-think/references/interaction-boundary.md +18 -33
  101. package/.ultra-template/docs/research/README.md +0 -55
  102. package/.ultra-template/reports/templates/delivery-report.json +0 -17
  103. package/.ultra-template/reports/templates/test-report.json +0 -18
  104. package/.ultra-template/tasks/tasks.json +0 -15
  105. package/.ultra-template/templates/task-context.md +0 -19
  106. package/adapters/_shared/codex-assets.cjs +0 -725
  107. package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
  108. package/adapters/_shared/interaction-contract.cjs +0 -129
  109. package/adapters/_shared/path-rewrite.cjs +0 -79
  110. package/adapters/_shared/runtime-native.cjs +0 -132
  111. package/adapters/_shared/settings-merge.cjs +0 -76
  112. package/agents/code-reviewer.md +0 -42
  113. package/agents/debugger.md +0 -40
  114. package/agents/review-code.md +0 -49
  115. package/agents/review-comments.md +0 -38
  116. package/agents/review-coordinator.md +0 -56
  117. package/agents/review-design.md +0 -40
  118. package/agents/review-errors.md +0 -43
  119. package/agents/review-spec.md +0 -39
  120. package/agents/review-tests.md +0 -40
  121. package/agents/tdd-runner.md +0 -38
  122. package/bin/orchestrator.js +0 -383
  123. package/commands/ultra-change.md +0 -10
  124. package/commands/ultra-deliver.md +0 -10
  125. package/commands/ultra-dev.md +0 -10
  126. package/commands/ultra-doctor.md +0 -10
  127. package/commands/ultra-init.md +0 -10
  128. package/commands/ultra-plan.md +0 -10
  129. package/commands/ultra-research.md +0 -10
  130. package/commands/ultra-review.md +0 -10
  131. package/commands/ultra-status.md +0 -10
  132. package/commands/ultra-test.md +0 -10
  133. package/commands/ultra-think.md +0 -10
  134. package/docs/AGENT-CONTEXT.md +0 -191
  135. package/docs/COMMIT-HASH-BACKFILL.md +0 -51
  136. package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
  137. package/docs/ROADMAP.md +0 -101
  138. package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
  139. package/hooks/active_task_context.py +0 -152
  140. package/hooks/context_envelope.py +0 -100
  141. package/hooks/health_check.py +0 -196
  142. package/hooks/pre_stop_check.py +0 -66
  143. package/hooks/runtime_paths.py +0 -396
  144. package/hooks/subagent_tracker.py +0 -88
  145. package/hooks/workflow_checkpoint.py +0 -76
  146. package/hooks/workflow_context.py +0 -48
  147. package/hooks/workflow_resume.py +0 -84
  148. package/mcp-server/hook-context.cjs +0 -38
  149. package/mcp-server/hook-event.cjs +0 -34
  150. package/mcp-server/lib/archive-journal.cjs +0 -1119
  151. package/mcp-server/lib/archive-mutation-worker.py +0 -365
  152. package/mcp-server/lib/artifact-registry.cjs +0 -1232
  153. package/mcp-server/lib/baseline-workflow.cjs +0 -1193
  154. package/mcp-server/lib/canonical-json.cjs +0 -19
  155. package/mcp-server/lib/change-authority.cjs +0 -26
  156. package/mcp-server/lib/change-packet.cjs +0 -790
  157. package/mcp-server/lib/change-workflow.cjs +0 -65
  158. package/mcp-server/lib/context-envelope.cjs +0 -581
  159. package/mcp-server/lib/context-paths.cjs +0 -142
  160. package/mcp-server/lib/decision-records.cjs +0 -423
  161. package/mcp-server/lib/delivery-transaction.cjs +0 -471
  162. package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
  163. package/mcp-server/lib/doctor.cjs +0 -627
  164. package/mcp-server/lib/git-bootstrap.cjs +0 -651
  165. package/mcp-server/lib/init-project.cjs +0 -1021
  166. package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
  167. package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
  168. package/mcp-server/lib/managed-file-write.cjs +0 -172
  169. package/mcp-server/lib/plan-checkpoint.cjs +0 -177
  170. package/mcp-server/lib/plan-publication-worker.cjs +0 -830
  171. package/mcp-server/lib/plan-store.cjs +0 -794
  172. package/mcp-server/lib/prd-parser.cjs +0 -83
  173. package/mcp-server/lib/pricing.cjs +0 -44
  174. package/mcp-server/lib/project-context.cjs +0 -178
  175. package/mcp-server/lib/projector.cjs +0 -386
  176. package/mcp-server/lib/provider-refs.cjs +0 -42
  177. package/mcp-server/lib/runtime-paths.cjs +0 -1714
  178. package/mcp-server/lib/runtime-state.cjs +0 -222
  179. package/mcp-server/lib/safe-project-file.cjs +0 -385
  180. package/mcp-server/lib/stage-checkpoints.cjs +0 -463
  181. package/mcp-server/lib/state-authority.cjs +0 -95
  182. package/mcp-server/lib/state-db.cjs +0 -1580
  183. package/mcp-server/lib/state-ops.cjs +0 -1428
  184. package/mcp-server/lib/task-contract.cjs +0 -404
  185. package/mcp-server/lib/task-expander.cjs +0 -105
  186. package/mcp-server/lib/task-ledger.cjs +0 -1964
  187. package/mcp-server/lib/telemetry.cjs +0 -85
  188. package/mcp-server/lib/topo.cjs +0 -131
  189. package/mcp-server/lib/ultra-facade.cjs +0 -2684
  190. package/mcp-server/lib/worker-packet.cjs +0 -358
  191. package/mcp-server/server.cjs +0 -437
  192. package/orchestrator/auto-merge.cjs +0 -128
  193. package/orchestrator/daemon.cjs +0 -264
  194. package/orchestrator/dispatch-rules.cjs +0 -78
  195. package/orchestrator/parallel-orchestrator.cjs +0 -903
  196. package/orchestrator/planner/plan-builder.cjs +0 -125
  197. package/orchestrator/recovery.cjs +0 -188
  198. package/orchestrator/session-close-journal-worker.cjs +0 -5
  199. package/orchestrator/session-close-journal.cjs +0 -1014
  200. package/orchestrator/session-runner.cjs +0 -1153
  201. package/orchestrator/worktree-manager.cjs +0 -282
  202. package/settings.json +0 -7
  203. package/skills/cc-collab/SKILL.md +0 -42
  204. package/skills/code-review-expert/SKILL.md +0 -43
  205. package/skills/code-review-expert/references/correctness-reliability.md +0 -30
  206. package/skills/code-review-expert/references/design-boundaries.md +0 -26
  207. package/skills/code-review-expert/references/removal-plan.md +0 -24
  208. package/skills/codex-collab/SKILL.md +0 -49
  209. package/skills/codex-collab/evals/evals.json +0 -35
  210. package/skills/integration-rules/SKILL.md +0 -32
  211. package/skills/security-rules/SKILL.md +0 -38
  212. package/skills/testing-rules/SKILL.md +0 -32
  213. package/skills/ultra-doctor/SKILL.md +0 -41
  214. package/skills/ultra-plan/references/semantic-preflight.md +0 -20
  215. package/skills/ultra-verify/SKILL.md +0 -46
  216. package/skills/ultra-verify/evals/evals.json +0 -46
  217. package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
  218. package/skills/ultra-verify/references/evidence-status.md +0 -21
  219. package/skills/ultra-verify/scripts/verify_wait.py +0 -94
  220. package/spec/cli-protocol.md +0 -102
  221. package/spec/mcp-tools.yaml +0 -312
  222. package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
  223. package/spec/schemas/change-delta.v1.schema.json +0 -153
  224. package/spec/schemas/command-manifest.schema.json +0 -29
  225. package/spec/schemas/context-file.v4.5.schema.json +0 -54
  226. package/spec/schemas/delivery-report.v1.schema.json +0 -66
  227. package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
  228. package/spec/schemas/mcp-tools.schema.json +0 -136
  229. package/spec/schemas/skill-manifest.schema.json +0 -24
  230. package/spec/schemas/state-db.sql +0 -641
  231. package/spec/schemas/task-ledger.v2.schema.json +0 -296
  232. package/spec/schemas/tasks.v4.5.schema.json +0 -96
  233. package/spec/schemas/test-report.v1.schema.json +0 -225
  234. package/ultra-tools/cli.cjs +0 -129
  235. package/ultra-tools/commands/db.cjs +0 -226
  236. package/ultra-tools/commands/legacy-memory.cjs +0 -159
  237. package/ultra-tools/commands/migrate.cjs +0 -1386
  238. package/ultra-tools/commands/session.cjs +0 -213
  239. package/ultra-tools/commands/status.cjs +0 -559
  240. package/ultra-tools/commands/system.cjs +0 -498
  241. package/ultra-tools/commands/task.cjs +0 -105
@@ -1,161 +0,0 @@
1
- # `.ultra/.runtime/state.db` access policy
2
-
3
- > Multi-process access contract. Current authority is defined by
4
- > [`DECISIONS.md`](./DECISIONS.md) and the live database schema.
5
-
6
- `.ultra/.runtime/state.db` is the checkout-local persistence and safety authority for
7
- baselines, Changes, normalized Decision Records, Stage Checkpoints, Context Envelopes,
8
- Worker Packets, Tasks, events, Sessions, incidents, projections, telemetry, and spec
9
- references. Registered digest-bound files carry semantic and evidence
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.
14
-
15
- ## 1. Three-role write matrix
16
-
17
- | Role | tasks | baselines | change/workflow/artifact state | decisions | events | sessions | telemetry | specs_refs | migration_history |
18
- |---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
19
- | **MCP server** (single writer for mutables) | RW | RW | RW | RW | RW | RW | RW | RW | RW |
20
- | **CLI** (`ultra-tools …`) | R | RW (init only) | R | R | RW (append-only) | R | R | R | RW (migration only) |
21
- | **Orchestrator daemon** | R | R | R | R | RW | RW | RW | RW | R |
22
-
23
- - **R** = read-only;
24
- - **RW** = read + write;
25
- - A `(append-only)` qualifier means the role may execute `INSERT INTO`
26
- but never `UPDATE` / `DELETE`.
27
-
28
- The CLI may indirectly mutate ordinary workflow tables by spawning the MCP server over
29
- stdio and calling the public `ultra.*` kernel — but the actual
30
- SQLite write is performed by the MCP server's writer connection. The documented
31
- exceptions are initial schema/baseline creation, migration, and explicitly
32
- authorized backup-first doctor recovery. The CLI never provides a parallel raw
33
- write API for normal task, change, session, or baseline lifecycle updates.
34
-
35
- The append-only carve-out for `events` exists because `events.id INTEGER
36
- PRIMARY KEY AUTOINCREMENT` makes concurrent inserts collision-free under
37
- WAL + `busy_timeout`. This lets hooks and short-lived CLI invocations
38
- record audit observations without paying the cost of a full MCP round trip.
39
- It does not grant lifecycle authority: external writers use the published observation
40
- allowlist, while lifecycle event
41
- names are emitted only inside the mutation that owns the corresponding state change.
42
- Consumers must verify mutable rows rather than infer success from an event name.
43
- Rejected semantic calls are recorded as `ultra_kernel_attempt` observations and shown
44
- by `ultra.context`; they do not create the rejected row or authorize a transition.
45
-
46
- ## 2. Connection discipline
47
-
48
- Every process **must**:
49
-
50
- 1. Open the database with WAL + `busy_timeout=5000` + `foreign_keys=ON`.
51
- Use `mcp-server/lib/state-db.cjs` `openStateDb` so the pragmas are
52
- applied uniformly.
53
- 2. Wrap every mutable state operation with `state-ops.cjs` `tx(db, fn)`. The
54
- helper calls better-sqlite3's immediate transaction variant, so the writer
55
- lock is acquired before user code runs and the whole operation rolls back on
56
- failure.
57
- 3. Retry the complete transaction up to **10 attempts** on `SQLITE_BUSY`, using
58
- decorrelated jitter from a 50 ms base with a 2 s cap. Never retry only a
59
- suffix of a failed transaction or preserve partial state.
60
- 4. Close the connection in a `finally` block. WAL files (`-wal`,
61
- `-shm`) are reclaimed on the last writer's close.
62
-
63
- Reads never need explicit transactions; better-sqlite3 reads observe a
64
- consistent snapshot under WAL.
65
-
66
- ## 3. WAL requirement
67
-
68
- WAL is part of the authority contract, not an optional performance setting.
69
- `openStateDb` asks SQLite to enable WAL and checks the actual returned mode. If
70
- the storage layer returns anything else, startup fails with
71
- `STATE_DB_WAL_UNAVAILABLE`. Ultra does not silently downgrade to a different
72
- concurrency model. Move `.ultra/.runtime/state.db` to storage that supports SQLite WAL or
73
- resolve the mount/runtime constraint before retrying.
74
-
75
- ## 4. Where each table is owned
76
-
77
- | Table | Writer of record |
78
- |--------------------|--------------------------------------------------------|
79
- | `baselines` | MCP server through `ultra.record`, `ultra.checkpoint`, and `ultra.archive`; initialization and legacy migration may create only the first draft or compatibility row |
80
- | `tasks` | MCP server through `ultra.record`, `ultra.checkpoint`, and `ultra.sync` |
81
- | `changes` | MCP server through `ultra.record`, `ultra.checkpoint`, and `ultra.archive` |
82
- | `decision_records` | MCP server through `ultra.record`; normalized accepted intent and applied references are stored, never UI receipts, prompts, or transcripts |
83
- | `stage_checkpoints` | MCP server through `ultra.checkpoint`; mutable drafts and immutable accepted/superseded revisions, never fixed reasoning steps |
84
- | `context_envelopes`, `worker_packets` | MCP server through `ultra.context`, `ultra.checkpoint`, and `ultra.session`; exact digest-bound handoff authority |
85
- | `decision_threads`, `decision_items`, `workflow_runs`, `workflow_steps` | read-only legacy history retained for migration and audit; no current public writer, invalidation mutation, or authorization role |
86
- | `artifacts`, `artifact_edges` | MCP server through `ultra.record` and checkpoint-owned registration |
87
- | `context_snapshots`, `spec_learning_candidates`, `trace_links` | MCP server through `ultra.record` and `ultra.checkpoint` |
88
- | `incidents`, `projection_jobs`, `event_consumers`, `circuit_breaker` | MCP server; backup-first doctor recovery may perform only documented mechanical transitions |
89
- | `events` | MCP server for lifecycle events; approved processes and `ultra.record` for allowlisted append-only observations |
90
- | `sessions` | MCP server through `ultra.session`; orchestrator may write status transitions |
91
- | `telemetry` | MCP server (collected from tool-call wrappers); orchestrator may dump bulk samples |
92
- | `specs_refs` | MCP server (rebuilt on `spec_changed` event); orchestrator may rebuild |
93
- | `migration_history`| schema initializer and `ultra-tools migrate` or doctor repair |
94
- | `schema_version` | schema initializer, `ultra-tools db init`, migration, and doctor repair |
95
-
96
- ## 5. Git team checkpoint
97
-
98
- Only `ultra.sync { action: publish }` may derive `.ultra/tasks/tasks.json` from SQLite,
99
- and only `ultra.sync { action: import }` may merge it into SQLite. Server startup performs one import after
100
- opening project authority; later pulls in a long-running process require an explicit
101
- import. The same checkpoint is idempotent and must not append an event again.
102
-
103
- The checkpoint carries portable baseline, Change, and durable task records with
104
- per-record revisions and digests. It excludes local `in_progress` ownership, sessions,
105
- leases, worktrees, telemetry, and completion commit hashes. Import validates document
106
- ancestry plus record-level common bases. It fast-forwards clean records and fails
107
- before commit on same-record concurrency, parent/owner gaps, non-descendant history, or
108
- an active-task collision. A Git-ready baseline is downgraded to checkout-local
109
- revalidation rather than trusted as execution authority.
110
-
111
- Legacy v4.4/v4.5 task projections are backed up and replaced only when every available
112
- durable field matches current SQLite. Doctor reports checkpoint condition but never
113
- performs semantic import, publication, or conflict selection.
114
-
115
- ## 6. Forbidden patterns
116
-
117
- - **No file copies for state.** Don't `cp .ultra/.runtime/state.db
118
- somewhere/state.db` and edit; use `ultra-tools db backup`.
119
- - **No direct checkpoint edits.** Don't merge or repair
120
- `.ultra/tasks/tasks.json` by hand; use the typed import/publish conflict path.
121
- - **No committed live projections.** `.ultra/.runtime/projections/**` remains ignored
122
- checkout-local output.
123
- - **No long-held writer connections** outside the MCP server. Pop a
124
- short transaction, finish, close.
125
- - **No `PRAGMA journal_mode` toggling at runtime** by any process other
126
- than the orchestrator's boot-time WAL detector.
127
- - **No raw SQL in command md files.** Commands call MCP tools or
128
- `ultra-tools …` subcommands; the SQL lives in `mcp-server/lib`.
129
- - **No legacy authority mode switch.** Public Change callers use the single Kernel
130
- facade. New workflow-owned artifacts and workflow/dialogue writes are rejected.
131
- - **No semantic meaning in projection metadata.** `_ultra.projection_commit` means
132
- only that the generated-view job was durably enqueued and processed; acceptance is
133
- read from the typed tool result and current authority.
134
- - **No `vacuum` / `wal_checkpoint(TRUNCATE)` from inside a transaction.**
135
- Maintenance subcommands open their own connection.
136
-
137
- ## 7. Maintenance windows
138
-
139
- `ultra-tools db checkpoint` and `ultra-tools db vacuum` are safe to run
140
- while the MCP server is up (they take their own connection and obey the
141
- busy timeout). `db backup` uses better-sqlite3's online `.backup` API
142
- and produces a consistent snapshot without blocking writers.
143
-
144
- ## 8. Verification
145
-
146
- The contract on this page is enforced by:
147
-
148
- - `mcp-server/tests/concurrency.test.cjs` — three writer threads
149
- appending to `events` simultaneously; asserts no `SQLITE_BUSY` escapes
150
- the retry loop and the resulting `events.id` sequence has no gaps and
151
- no duplicates.
152
- - `mcp-server/tests/state-ops.test.cjs` — immediate transactions, rollback,
153
- status transitions, task contracts, and event coupling.
154
- - `mcp-server/lib/stage-checkpoints.test.cjs` — mutable drafts, immutable accepted
155
- revisions, supersession, and report-first diagnostics.
156
- - `mcp-server/lib/context-envelope.test.cjs` and
157
- `mcp-server/lib/worker-packet.test.cjs` — content-addressed Context and immutable
158
- delegated handoff.
159
- - `mcp-server/lib/task-ledger.test.cjs` — portable-field filtering, digest ancestry,
160
- idempotent import, baseline/Change/task fast-forward and conflict behavior, legacy
161
- backup, and active-session protection.
@@ -1,152 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Protect managed task files and restate the live Ultra Context Envelope."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- from context_envelope import (
9
- ContextEnvelopeError,
10
- find_root,
11
- read_context_envelope,
12
- render_context_envelope,
13
- )
14
-
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)
18
-
19
-
20
- def tool_paths(data: dict, start: Path):
21
- """Yield absolute tool targets without assuming they belong to ``start``."""
22
- tool_input = data.get("tool_input")
23
- if not isinstance(tool_input, dict):
24
- return
25
- for key in ("file_path", "filePath", "filepath", "path"):
26
- value = tool_input.get(key)
27
- if not isinstance(value, str) or not value.strip():
28
- continue
29
- candidate = Path(value)
30
- yield candidate.resolve() if candidate.is_absolute() else (start / candidate).resolve()
31
-
32
-
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)])
40
- return None
41
-
42
-
43
- def targets_managed_task_file(data: dict, root: Path) -> bool:
44
- for resolved in tool_paths(data, root):
45
- if any(resolved == (root / relative).resolve() for relative in PROTECTED_TASK_FILES):
46
- return True
47
- return False
48
-
49
-
50
- def managed_file_denial(reason: str) -> dict:
51
- return {"hookSpecificOutput": {
52
- "hookEventName": "PreToolUse",
53
- "permissionDecision": "deny",
54
- "permissionDecisionReason": reason,
55
- }}
56
-
57
-
58
- def conflicted_managed_root(data: dict, start: Path) -> Path | None:
59
- for candidate in (start, *start.parents):
60
- if not (candidate / ".ultra").exists():
61
- continue
62
- if targets_managed_task_file(data, candidate):
63
- return candidate
64
- return None
65
-
66
-
67
- def managed_roots(data: dict, start: Path):
68
- """Yield initialized project roots derived from every resolved tool target."""
69
- for target in tool_paths(data, start):
70
- root = protected_root_for_target(target)
71
- if root is not None and (root / ".ultra").exists():
72
- yield root
73
-
74
-
75
- def main() -> None:
76
- try:
77
- data = json.loads(sys.stdin.read() or "{}")
78
- data = data if isinstance(data, dict) else {}
79
- except json.JSONDecodeError as exc:
80
- print(f"[active_task_context] invalid hook input: {exc}", file=sys.stderr)
81
- data = {}
82
- start = Path(data.get("cwd") or Path.cwd()).resolve()
83
- # An absolute tool target carries its own project boundary. Resolve and
84
- # protect that authority before consulting cwd, because hooks may run from
85
- # a host-owned temporary directory or another repository.
86
- for target_root in managed_roots(data, start):
87
- try:
88
- target_authority = find_root(target_root)
89
- except ContextEnvelopeError as exc:
90
- print(json.dumps(managed_file_denial(
91
- "Refusing a direct write to an MCP-managed Ultra task file while the "
92
- f"runtime authority is conflicted: {exc}. Resolve the "
93
- "state.db conflict with ultra-doctor before retrying the owning "
94
- "MCP operation."
95
- )))
96
- return
97
- if target_authority is None:
98
- continue
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 ultra.record for task contracts/outcomes and ultra.sync for the team checkpoint."
104
- )))
105
- return
106
-
107
- try:
108
- root = find_root(start)
109
- except ContextEnvelopeError as exc:
110
- print(
111
- f"[active_task_context] cannot resolve Ultra root: {exc}",
112
- file=sys.stderr,
113
- )
114
- # Ordinary context injection remains fail-open. A direct write to a
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 "
120
- f"runtime authority is conflicted: {exc}. Resolve the "
121
- "state.db conflict with ultra-doctor before retrying the owning "
122
- "public Ultra operation."
123
- )))
124
- return
125
- root = None
126
- if root is None:
127
- print(json.dumps({}))
128
- return
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 ultra.record for task contracts/outcomes and ultra.sync for the team checkpoint."
135
- )))
136
- return
137
- try:
138
- envelope = read_context_envelope(root)
139
- except ContextEnvelopeError as exc:
140
- print(f"[active_task_context] cannot inspect Context Envelope: {exc}", file=sys.stderr)
141
- envelope = None
142
- if envelope:
143
- print(json.dumps({"hookSpecificOutput": {
144
- "hookEventName": "PreToolUse",
145
- "additionalContext": render_context_envelope(root, envelope),
146
- }}))
147
- return
148
- print(json.dumps({}))
149
-
150
-
151
- if __name__ == "__main__":
152
- main()
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Thin read-only bridge to the bundled Ultra Context Envelope reader."""
3
-
4
- import json
5
- import os
6
- import shutil
7
- import subprocess
8
- import sys
9
- from pathlib import Path
10
-
11
- from runtime_paths import RuntimePathError, find_project_root
12
-
13
-
14
- class ContextEnvelopeError(RuntimeError):
15
- """The canonical Context Envelope helper could not be executed or decoded."""
16
-
17
-
18
- class ContextEnvelope(dict):
19
- """Dictionary envelope carrying its canonical bounded injection text."""
20
-
21
- def __init__(self, value: dict, rendered: str):
22
- super().__init__(value)
23
- self.rendered = rendered
24
-
25
-
26
- def find_root(start: Path):
27
- try:
28
- return find_project_root(start)
29
- except RuntimePathError as exc:
30
- raise ContextEnvelopeError(str(exc)) from exc
31
-
32
-
33
- def find_root_for_hook(start: Path, hook_name: str):
34
- """Fail open at a hook boundary while preserving path-conflict evidence."""
35
- try:
36
- return find_root(start)
37
- except ContextEnvelopeError as exc:
38
- print(f"[{hook_name}] cannot resolve Ultra root: {exc}", file=sys.stderr)
39
- return None
40
-
41
-
42
- def _helper_path() -> Path:
43
- hook_root = Path(__file__).resolve().parent
44
- installed = hook_root.parent / "runtime" / "hook-context.cjs"
45
- if installed.is_file():
46
- return installed
47
- source = hook_root.parent / "mcp-server" / "hook-context.cjs"
48
- if source.is_file():
49
- return source
50
- raise ContextEnvelopeError("bundled Context Envelope helper is missing")
51
-
52
-
53
- def _node_binary() -> str:
54
- configured = os.environ.get("UBP_NODE")
55
- if configured:
56
- return configured
57
- discovered = shutil.which("node")
58
- if discovered:
59
- return discovered
60
- raise ContextEnvelopeError("node executable is unavailable")
61
-
62
-
63
- def read_context_envelope(root: Path):
64
- try:
65
- result = subprocess.run(
66
- [_node_binary(), str(_helper_path()), str(root.resolve())],
67
- text=True,
68
- capture_output=True,
69
- check=False,
70
- timeout=5,
71
- )
72
- except (OSError, subprocess.TimeoutExpired) as exc:
73
- raise ContextEnvelopeError(str(exc)) from exc
74
- if result.returncode != 0:
75
- detail = result.stderr.strip() or f"exit {result.returncode}"
76
- raise ContextEnvelopeError(detail)
77
- try:
78
- payload = json.loads(result.stdout)
79
- except json.JSONDecodeError as exc:
80
- raise ContextEnvelopeError(f"invalid Context Envelope response: {exc}") from exc
81
- if not isinstance(payload, dict):
82
- raise ContextEnvelopeError("Context Envelope response must be an object")
83
- value = payload.get("context")
84
- if value is None:
85
- return None
86
- rendered = payload.get("text")
87
- if not isinstance(value, dict) or not isinstance(rendered, str) or not rendered:
88
- raise ContextEnvelopeError(
89
- "Context Envelope response is missing canonical state or text"
90
- )
91
- return ContextEnvelope(value, rendered)
92
-
93
-
94
- def render_context_envelope(_root: Path, context: dict) -> str:
95
- rendered = getattr(context, "rendered", None)
96
- if not isinstance(rendered, str) or not rendered:
97
- raise ContextEnvelopeError(
98
- "Context Envelope was not produced by the canonical reader"
99
- )
100
- return rendered
@@ -1,196 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Fast, read-only Ultra runtime health inspection at session start."""
3
-
4
- import json
5
- import sqlite3
6
- import sys
7
- from pathlib import Path
8
-
9
- from context_envelope import (
10
- ContextEnvelopeError,
11
- find_root_for_hook,
12
- read_context_envelope,
13
- )
14
- from runtime_paths import state_db_path
15
-
16
-
17
- REQUIRED_TABLES = {
18
- "baselines",
19
- "tasks", "events", "sessions", "schema_version", "migration_history",
20
- "telemetry", "specs_refs", "circuit_breaker", "changes", "artifacts",
21
- "artifact_edges", "context_snapshots", "spec_learning_candidates", "trace_links",
22
- "incidents", "projection_jobs", "event_consumers", "stage_checkpoints",
23
- "decision_records", "context_envelopes", "worker_packets",
24
- }
25
-
26
-
27
- def hook_input() -> dict:
28
- try:
29
- value = json.loads(sys.stdin.read() or "{}")
30
- return value if isinstance(value, dict) else {}
31
- except json.JSONDecodeError as exc:
32
- print(f"[health_check] invalid hook input: {exc}", file=sys.stderr)
33
- return {}
34
-
35
-
36
- def inspect(root: Path) -> dict:
37
- db_path = state_db_path(root)
38
- report = {"status": "healthy", "project": str(root), "checks": {}}
39
- issues = []
40
- if not db_path.is_file():
41
- report["checks"]["state_db"] = {"status": "fail", "reason": "missing"}
42
- report["status"] = "degraded"
43
- return report
44
-
45
- try:
46
- context = read_context_envelope(root)
47
- except ContextEnvelopeError as exc:
48
- report["checks"]["authority"] = {"status": "fail", "reason": str(exc)}
49
- report["status"] = "degraded"
50
- return report
51
-
52
- try:
53
- uri = f"file:{db_path}?mode=ro"
54
- with sqlite3.connect(uri, uri=True, timeout=1) as conn:
55
- conn.row_factory = sqlite3.Row
56
- quick = conn.execute("PRAGMA quick_check").fetchone()[0]
57
- tables = {
58
- row[0] for row in conn.execute(
59
- "SELECT name FROM sqlite_master WHERE type='table'"
60
- )
61
- }
62
- missing = sorted(REQUIRED_TABLES - tables)
63
- report["checks"]["state_db"] = {
64
- "status": "pass" if quick == "ok" and not missing else "fail",
65
- "integrity": quick,
66
- "missing_tables": missing,
67
- }
68
- if quick != "ok" or missing:
69
- issues.append("state_db")
70
- if missing:
71
- report["status"] = "degraded"
72
- return report
73
-
74
- envelope = context.get("envelope", {}) if context else {}
75
- diagnostics = envelope.get("diagnostics", {})
76
- baseline_blockers = [
77
- str(item.get("code"))
78
- for item in [
79
- *diagnostics.get("needs_attention", []),
80
- *diagnostics.get("hard_conflicts", []),
81
- ]
82
- if str(item.get("code", "")).startswith("BASELINE_")
83
- ]
84
- baseline_warnings = [
85
- str(item.get("code"))
86
- for item in diagnostics.get("warnings", [])
87
- if str(item.get("code", "")).startswith("BASELINE_")
88
- ]
89
- baseline = envelope.get("baseline")
90
- baseline_is_ready = isinstance(baseline, dict) and baseline.get("status") == "ready"
91
- baseline_status = (
92
- "fail" if baseline_blockers and baseline_is_ready
93
- else "warning" if baseline_blockers or baseline_warnings
94
- else "pass"
95
- )
96
- report["checks"]["baseline"] = {
97
- "baseline": baseline,
98
- "blockers": baseline_blockers,
99
- "warnings": baseline_warnings,
100
- "readiness": "blocked" if baseline_blockers else "ready",
101
- "status": baseline_status,
102
- }
103
- if baseline_status == "fail":
104
- issues.append("baseline")
105
-
106
- incidents = [
107
- {"id": row[0], "code": row[1], "severity": row[2], "retryable": bool(row[3])}
108
- for row in conn.execute(
109
- """SELECT id, code, severity, retryable FROM incidents
110
- WHERE status = 'open' ORDER BY last_seen_at DESC LIMIT 20"""
111
- )
112
- ]
113
- report["checks"]["incidents"] = {
114
- "status": "pass" if not incidents else "fail",
115
- "open": incidents,
116
- }
117
- if incidents:
118
- issues.append("incidents")
119
-
120
- pending = conn.execute(
121
- "SELECT COUNT(*) FROM projection_jobs WHERE status = 'pending'"
122
- ).fetchone()[0]
123
- running = conn.execute(
124
- "SELECT COUNT(*) FROM projection_jobs WHERE status = 'running'"
125
- ).fetchone()[0]
126
- failed = conn.execute(
127
- "SELECT COUNT(*) FROM projection_jobs WHERE status = 'failed'"
128
- ).fetchone()[0]
129
- event_cursor = conn.execute(
130
- "SELECT COALESCE(MAX(id), 0) FROM events"
131
- ).fetchone()[0]
132
- projected_cursor = conn.execute(
133
- """SELECT COALESCE(MAX(event_cursor), 0) FROM projection_jobs
134
- WHERE status = 'completed'"""
135
- ).fetchone()[0]
136
- projection_ok = (
137
- pending == 0 and running == 0 and failed == 0
138
- and event_cursor <= projected_cursor
139
- )
140
- report["checks"]["projections"] = {
141
- "status": "pass" if projection_ok else "fail",
142
- "pending": pending,
143
- "running": running,
144
- "failed": failed,
145
- "event_cursor": event_cursor,
146
- "projected_cursor": projected_cursor,
147
- }
148
- if not projection_ok:
149
- issues.append("projections")
150
-
151
- orphan = conn.execute(
152
- "SELECT COUNT(*) FROM sessions WHERE status = 'orphan'"
153
- ).fetchone()[0]
154
- report["checks"]["sessions"] = {
155
- "status": "pass" if orphan == 0 else "fail", "orphan": orphan,
156
- }
157
- if orphan:
158
- issues.append("sessions")
159
-
160
- missing_artifacts = []
161
- for change_id, artifact_root in conn.execute(
162
- """SELECT id, artifact_root FROM changes
163
- WHERE status IN ('active', 'blocked', 'ready')"""
164
- ):
165
- if artifact_root and not (root / artifact_root).exists():
166
- missing_artifacts.append(change_id)
167
- report["checks"]["change_artifacts"] = {
168
- "status": "pass" if not missing_artifacts else "fail",
169
- "missing": missing_artifacts,
170
- }
171
- if missing_artifacts:
172
- issues.append("change_artifacts")
173
- except sqlite3.Error as exc:
174
- report["checks"]["state_db"] = {"status": "fail", "reason": str(exc)}
175
- issues.append("state_db")
176
-
177
- if issues:
178
- report["status"] = "degraded"
179
- return report
180
-
181
-
182
- def main() -> None:
183
- data = hook_input()
184
- start = Path(data.get("cwd") or Path.cwd()).resolve()
185
- root = find_root_for_hook(start, "health_check")
186
- if root is None:
187
- print(json.dumps({}))
188
- return
189
- report = inspect(root)
190
- if report["status"] != "healthy":
191
- print("[Ultra health] " + json.dumps(report, ensure_ascii=False, sort_keys=True), file=sys.stderr)
192
- print(json.dumps({}))
193
-
194
-
195
- if __name__ == "__main__":
196
- main()
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Report unfinished DB-authoritative Ultra work without trapping session stop."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- from context_envelope import (
9
- ContextEnvelopeError,
10
- find_root_for_hook,
11
- read_context_envelope,
12
- )
13
-
14
-
15
- def allow_stop() -> None:
16
- print(json.dumps({}))
17
-
18
-
19
- def main() -> None:
20
- try:
21
- hook_data = json.loads(sys.stdin.read() or "{}")
22
- hook_data = hook_data if isinstance(hook_data, dict) else {}
23
- except json.JSONDecodeError as exc:
24
- print(f"[pre_stop_check] invalid hook input: {exc}", file=sys.stderr)
25
- allow_stop()
26
- return
27
-
28
- if hook_data.get("stop_hook_active", False):
29
- allow_stop()
30
- return
31
- root = find_root_for_hook(
32
- Path(hook_data.get("cwd") or Path.cwd()).resolve(),
33
- "pre_stop_check",
34
- )
35
- if root is None:
36
- allow_stop()
37
- return
38
- try:
39
- context = read_context_envelope(root)
40
- except ContextEnvelopeError as exc:
41
- print(f"[pre_stop_check] cannot inspect Context Envelope: {exc}", file=sys.stderr)
42
- allow_stop()
43
- return
44
- envelope = context.get("envelope", {}) if context else {}
45
- change = envelope.get("change") or {}
46
- task = envelope.get("task") or {}
47
- if change.get("id") and change.get("status") not in ("archived", "cancelled"):
48
- attention = envelope.get("diagnostics", {}).get("needs_attention", [])
49
- warnings = envelope.get("diagnostics", {}).get("warnings", [])
50
- codes = ", ".join(
51
- str(item.get("code")) for item in [*attention, *warnings] if item.get("code")
52
- ) or "none"
53
- print(
54
- "[Ultra stop advisory] Active Ultra change remains open: "
55
- f"change={change['id']}, "
56
- f"task={task.get('id') or 'none'}, "
57
- f"stage={envelope.get('execution', {}).get('stage') or 'project'}, "
58
- f"diagnostics={codes}. "
59
- "Stop is allowed; read ultra.context before resuming or explicitly archive later.",
60
- file=sys.stderr,
61
- )
62
- allow_stop()
63
-
64
-
65
- if __name__ == "__main__":
66
- main()