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,396 +0,0 @@
1
- """Read-only canonical project and state-path resolution for Ultra hooks."""
2
-
3
- import json
4
- import os
5
- import sqlite3
6
- import subprocess
7
- from pathlib import Path
8
-
9
-
10
- class RuntimePathError(RuntimeError):
11
- """Ultra runtime paths are invalid or have competing authorities."""
12
-
13
- STATE_TOMBSTONE_MARKER = "MIGRATED_TO_RUNTIME.json"
14
- STATE_TOMBSTONE_KIND = "ultra-state-migration-tombstone"
15
-
16
-
17
- def _absolute(path: Path) -> Path:
18
- expanded = path.expanduser()
19
- return expanded if expanded.is_absolute() else Path.cwd() / expanded
20
-
21
-
22
- def _assert_root(root: Path, *, explicit: bool) -> Path:
23
- absolute = _absolute(root)
24
- try:
25
- stat = absolute.lstat()
26
- except FileNotFoundError as error:
27
- raise RuntimePathError(f"UBP_ROOT_DIR does not exist: {absolute}") from error
28
- if (explicit and absolute.is_symlink()) or not absolute.is_dir():
29
- raise RuntimePathError(
30
- f"UBP_ROOT_DIR must be a real directory, not a symlink or special entry: {absolute}"
31
- )
32
- return absolute.resolve()
33
-
34
-
35
- def _assert_directory(path: Path, label: str, *, allow_runtime_link_to: Path | None = None) -> None:
36
- try:
37
- path.lstat()
38
- except FileNotFoundError:
39
- return
40
- if path.is_symlink():
41
- if allow_runtime_link_to is not None:
42
- try:
43
- if (
44
- path.resolve(strict=True) == allow_runtime_link_to.parent.resolve(strict=True)
45
- and (path / "state.db").resolve(strict=True)
46
- == allow_runtime_link_to.resolve(strict=True)
47
- ):
48
- return
49
- except FileNotFoundError:
50
- pass
51
- raise RuntimePathError(f"{label} may not be a symlink: {path}")
52
- if not path.is_dir():
53
- raise RuntimePathError(f"{label} must be a directory: {path}")
54
-
55
-
56
- def _is_managed_legacy_tombstone(main: Path) -> bool:
57
- try:
58
- stat = main.lstat()
59
- except FileNotFoundError:
60
- return False
61
- if main.is_symlink():
62
- return False
63
- marker = main / STATE_TOMBSTONE_MARKER if main.is_dir() else main
64
- try:
65
- if main.is_dir():
66
- if [entry.name for entry in main.iterdir()] != [STATE_TOMBSTONE_MARKER]:
67
- return False
68
- elif not main.is_file():
69
- return False
70
- marker_stat = marker.lstat()
71
- if marker.is_symlink() or not marker_stat.st_mode:
72
- return False
73
- payload = json.loads(marker.read_text(encoding="utf-8"))
74
- except (FileNotFoundError, OSError, ValueError, json.JSONDecodeError):
75
- return False
76
- canonical = (main.parent / str(payload.get("canonical_state_db", ""))).resolve()
77
- expected = (main.parent / ".runtime" / "state.db").resolve()
78
- return (
79
- payload.get("version") == 1
80
- and payload.get("kind") == STATE_TOMBSTONE_KIND
81
- and marker.is_file()
82
- and canonical == expected
83
- )
84
-
85
-
86
- def _assert_state_set(
87
- main: Path, label: str, *, allow_managed_tombstone: bool = False
88
- ) -> None:
89
- present: dict[str, bool] = {}
90
- for suffix in ("", "-wal", "-shm"):
91
- candidate = Path(f"{main}{suffix}")
92
- try:
93
- candidate.lstat()
94
- except FileNotFoundError:
95
- present[suffix] = False
96
- continue
97
- tombstone = (
98
- suffix == ""
99
- and allow_managed_tombstone
100
- and _is_managed_legacy_tombstone(candidate)
101
- )
102
- if not tombstone and (candidate.is_symlink() or not candidate.is_file()):
103
- raise RuntimePathError(
104
- f"{label} state entry must be a regular file and may not be a symlink: {candidate}"
105
- )
106
- present[suffix] = not tombstone
107
- if not present[""] and (present["-wal"] or present["-shm"]):
108
- raise RuntimePathError(f"{label} SQLite sidecar exists without state.db authority")
109
-
110
-
111
- def _physical_state_paths(root: Path) -> tuple[Path, Path]:
112
- resolved = root.resolve()
113
- return (
114
- resolved / ".ultra" / ".runtime" / "state.db",
115
- resolved / ".ultra" / "state.db",
116
- )
117
-
118
-
119
- def _same_physical_authority(left: Path, right: Path) -> bool:
120
- if left == right:
121
- return True
122
- try:
123
- return left.resolve(strict=True) == right.resolve(strict=True)
124
- except FileNotFoundError:
125
- return False
126
-
127
-
128
- def _is_task_authority_link(
129
- runtime: Path, candidate: Path, physical_runtime: Path, runtime_state: Path
130
- ) -> bool:
131
- try:
132
- parts = candidate.relative_to(runtime).parts
133
- except ValueError:
134
- return False
135
- if (
136
- len(parts) != 4
137
- or parts[0] != "worktrees"
138
- or not parts[1]
139
- or len(parts[1]) > 128
140
- or not parts[1][0].isalnum()
141
- or any(not (char.isalnum() or char in "._-") for char in parts[1])
142
- or parts[2:] != (".ultra", ".runtime")
143
- ):
144
- return False
145
- try:
146
- return (
147
- candidate.resolve(strict=True) == physical_runtime
148
- and (candidate / "state.db").resolve(strict=True)
149
- == runtime_state.resolve(strict=True)
150
- )
151
- except FileNotFoundError:
152
- return False
153
-
154
-
155
- def _has_git_metadata(root: Path) -> bool:
156
- for candidate in (root, *root.parents):
157
- if (candidate / ".git").exists() or (candidate / ".git").is_symlink():
158
- return True
159
- return False
160
-
161
-
162
- def _registered_worktrees(root: Path) -> set[Path]:
163
- probe = subprocess.run(
164
- ["git", "rev-parse", "--is-inside-work-tree"],
165
- cwd=root,
166
- text=True,
167
- capture_output=True,
168
- check=False,
169
- )
170
- if probe.returncode != 0 or probe.stdout.strip() != "true":
171
- detail = (probe.stderr or probe.stdout or "unknown Git probe failure").strip()
172
- if not _has_git_metadata(root) and "not a git repository" in detail.lower():
173
- return set()
174
- raise RuntimePathError(f"cannot classify Git worktree authority: {detail}")
175
- result = subprocess.run(
176
- ["git", "worktree", "list", "--porcelain"],
177
- cwd=root,
178
- text=True,
179
- capture_output=True,
180
- check=False,
181
- )
182
- if result.returncode != 0:
183
- detail = (result.stderr or result.stdout or "unknown Git probe failure").strip()
184
- raise RuntimePathError(f"cannot inspect registered Git worktrees: {detail}")
185
- registered: set[Path] = set()
186
- for line in result.stdout.splitlines():
187
- if line.startswith("worktree "):
188
- registered.add(Path(line.removeprefix("worktree ")).resolve())
189
- return registered
190
-
191
-
192
- def _assert_registered_worktree_boundary(
193
- runtime: Path, candidate: Path, physical_runtime: Path
194
- ) -> None:
195
- sid = candidate.name
196
- if (
197
- not sid
198
- or len(sid) > 128
199
- or not sid[0].isalnum()
200
- or any(not (char.isalnum() or char in "._-") for char in sid)
201
- ):
202
- raise RuntimePathError(f"registered worktree has an unsafe session id: {candidate}")
203
- authority_link = candidate / ".ultra" / ".runtime"
204
- if (
205
- not authority_link.is_symlink()
206
- or not _is_task_authority_link(
207
- runtime, authority_link, physical_runtime, runtime / "state.db"
208
- )
209
- ):
210
- raise RuntimePathError(
211
- "registered worktree must contain the exact central Ultra "
212
- f"authority link: {authority_link}"
213
- )
214
-
215
-
216
- def _assert_runtime_tree(root: Path, runtime: Path) -> None:
217
- try:
218
- runtime.lstat()
219
- except FileNotFoundError:
220
- return
221
- try:
222
- physical_runtime = runtime.resolve(strict=True)
223
- except FileNotFoundError as error:
224
- raise RuntimePathError(
225
- f"canonical runtime root cannot be resolved: {runtime}"
226
- ) from error
227
- pending = list(runtime.iterdir())
228
- runtime_state = runtime / "state.db"
229
- registered: set[Path] | None = None
230
- while pending:
231
- candidate = pending.pop()
232
- try:
233
- candidate.lstat()
234
- except FileNotFoundError:
235
- continue
236
- if candidate.is_symlink():
237
- if _is_task_authority_link(
238
- runtime, candidate, physical_runtime, runtime_state
239
- ):
240
- continue
241
- raise RuntimePathError(
242
- f"canonical runtime entry may not be a symlink: {candidate}"
243
- )
244
- if not candidate.is_dir() and not candidate.is_file():
245
- raise RuntimePathError(
246
- "canonical runtime entry must be a regular file or real "
247
- f"directory: {candidate}"
248
- )
249
- try:
250
- physical_candidate = candidate.resolve(strict=True)
251
- physical_candidate.relative_to(physical_runtime)
252
- except (FileNotFoundError, ValueError) as error:
253
- raise RuntimePathError(
254
- f"canonical runtime entry escapes its physical root: {candidate}"
255
- ) from error
256
- try:
257
- relative_parts = candidate.relative_to(runtime).parts
258
- except ValueError:
259
- relative_parts = ()
260
- if (
261
- candidate.is_dir()
262
- and len(relative_parts) == 2
263
- and relative_parts[0] == "worktrees"
264
- ):
265
- if registered is None:
266
- registered = _registered_worktrees(root)
267
- if physical_candidate in registered:
268
- _assert_registered_worktree_boundary(
269
- runtime, candidate, physical_runtime
270
- )
271
- continue
272
- if candidate.is_dir():
273
- pending.extend(candidate.iterdir())
274
-
275
-
276
- def _project_root_from_state_db(configured: Path) -> Path:
277
- if (
278
- configured.name == "state.db"
279
- and configured.parent.name == ".runtime"
280
- and configured.parent.parent.name == ".ultra"
281
- ):
282
- return configured.parent.parent.parent
283
- raise RuntimePathError(
284
- f"configured DB is not a canonical project runtime authority: {configured}"
285
- )
286
-
287
-
288
- def _assert_task_authority_binding(root: Path, configured: Path) -> None:
289
- try:
290
- authority_root = _project_root_from_state_db(configured).resolve(strict=True)
291
- canonical_db = authority_root / ".ultra" / ".runtime" / "state.db"
292
- if configured.resolve(strict=True) != canonical_db.resolve(strict=True):
293
- raise RuntimePathError("configured DB is not the canonical authority")
294
- task_root = root.resolve(strict=True)
295
- worktrees_root = (
296
- authority_root / ".ultra" / ".runtime" / "worktrees"
297
- ).resolve(strict=True)
298
- relative = task_root.relative_to(worktrees_root)
299
- if len(relative.parts) != 1:
300
- raise RuntimePathError("task root is not a direct managed worktree child")
301
- sid = relative.parts[0]
302
- if task_root not in _registered_worktrees(authority_root):
303
- raise RuntimePathError("task root is not a registered Git worktree")
304
- runtime_link = root / ".ultra" / ".runtime"
305
- if (
306
- not runtime_link.is_symlink()
307
- or runtime_link.resolve(strict=True) != canonical_db.parent.resolve(strict=True)
308
- ):
309
- raise RuntimePathError("task authority link does not resolve to the configured DB")
310
- uri = f"{configured.resolve(strict=True).as_uri()}?mode=ro"
311
- with sqlite3.connect(uri, uri=True) as connection:
312
- row = connection.execute(
313
- "SELECT worktree_path FROM sessions WHERE sid = ?", (sid,)
314
- ).fetchone()
315
- if row is None:
316
- raise RuntimePathError(f"authority has no session binding for {sid}")
317
- if Path(row[0]).resolve(strict=True) != task_root:
318
- raise RuntimePathError(f"session {sid} is bound to a different worktree")
319
- except (FileNotFoundError, ValueError, sqlite3.Error) as error:
320
- raise RuntimePathError(
321
- f"task root is not bound to the configured Ultra authority: {error}"
322
- ) from error
323
-
324
-
325
- def validate_project_layout(
326
- root: Path, *, validate_runtime_tree: bool = False
327
- ) -> tuple[Path, Path, Path | None]:
328
- runtime, legacy = _physical_state_paths(root)
329
- configured_value = os.environ.get("UBP_DB_PATH", "").strip()
330
- configured = _absolute(Path(configured_value)) if configured_value else None
331
- if configured is not None:
332
- _assert_state_set(configured, "configured")
333
- _assert_directory(root / ".ultra", ".ultra")
334
- _assert_directory(
335
- root / ".ultra" / ".runtime",
336
- ".ultra/.runtime",
337
- allow_runtime_link_to=configured,
338
- )
339
- _assert_state_set(runtime, "runtime")
340
- _assert_state_set(legacy, "legacy", allow_managed_tombstone=True)
341
- if validate_runtime_tree:
342
- _assert_runtime_tree(root, root.resolve() / ".ultra" / ".runtime")
343
- return runtime, legacy, configured
344
-
345
-
346
- def _assert_no_competing_state(root: Path) -> tuple[Path, Path]:
347
- runtime, legacy, configured = validate_project_layout(root)
348
- legacy_active = legacy.is_file() and not _is_managed_legacy_tombstone(legacy)
349
- if runtime.is_file() and legacy_active:
350
- raise RuntimePathError(
351
- "both legacy .ultra/state.db and runtime "
352
- ".ultra/.runtime/state.db exist; refusing to choose either authority"
353
- )
354
- if configured is not None:
355
- active = runtime if runtime.is_file() else (legacy if legacy_active else None)
356
- allowed = (
357
- _same_physical_authority(configured, active)
358
- if active is not None
359
- else configured == runtime
360
- )
361
- if not allowed:
362
- raise RuntimePathError(
363
- "UBP_DB_PATH does not name this project's canonical or "
364
- f"task-linked authority: {configured}"
365
- )
366
- if (root / ".ultra" / ".runtime").is_symlink():
367
- _assert_task_authority_binding(root, configured)
368
- return runtime, legacy
369
-
370
-
371
- def state_db_path(root: Path) -> Path:
372
- """Return configured, runtime, or legacy state without mutating the project."""
373
- runtime, legacy = _assert_no_competing_state(root)
374
- configured = os.environ.get("UBP_DB_PATH", "").strip()
375
- if configured:
376
- return _absolute(Path(configured)).resolve()
377
- if runtime.is_file():
378
- return runtime
379
- if legacy.is_file() and not _is_managed_legacy_tombstone(legacy):
380
- return legacy
381
- return runtime
382
-
383
-
384
- def find_project_root(start: Path) -> Path | None:
385
- """Resolve one project root without letting UBP_DB_PATH invent a root."""
386
- configured_root = os.environ.get("UBP_ROOT_DIR", "").strip()
387
- if configured_root:
388
- root = _assert_root(Path(configured_root), explicit=True)
389
- return root if state_db_path(root).is_file() else None
390
-
391
- current = _assert_root(start, explicit=False)
392
- for root in (current, *current.parents):
393
- runtime, legacy = _assert_no_competing_state(root)
394
- if runtime.is_file() or legacy.is_file():
395
- return root
396
- return None
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Append minimal Ultra subagent lifecycle metadata to state.db events.
3
-
4
- The hook never stores prompts, messages, transcript paths, or a parallel JSONL log.
5
- It is advisory: a failed lifecycle event is reported on stderr but never blocks the
6
- host's subagent operation.
7
- """
8
-
9
- import json
10
- import os
11
- import shutil
12
- import subprocess
13
- import sys
14
- from pathlib import Path
15
-
16
- from context_envelope import find_root_for_hook
17
-
18
-
19
- def helper_path() -> Path:
20
- hook_root = Path(__file__).resolve().parent
21
- installed = hook_root.parent / "runtime" / "hook-event.cjs"
22
- if installed.is_file():
23
- return installed
24
- source = hook_root.parent / "mcp-server" / "hook-event.cjs"
25
- if source.is_file():
26
- return source
27
- raise RuntimeError("bundled hook lifecycle helper is missing")
28
-
29
-
30
- def node_binary() -> str:
31
- configured = os.environ.get("UBP_NODE")
32
- if configured:
33
- return configured
34
- discovered = shutil.which("node")
35
- if discovered:
36
- return discovered
37
- raise RuntimeError("node executable is unavailable")
38
-
39
-
40
- def append_event(root: Path, action: str, hook_input: dict) -> None:
41
- payload = {
42
- "agent_id": hook_input.get("agent_id", "unknown"),
43
- "agent_type": hook_input.get("agent_type", "unknown"),
44
- "session_id": hook_input.get("session_id", ""),
45
- }
46
- result = subprocess.run(
47
- [node_binary(), str(helper_path()), str(root.resolve()), action],
48
- input=json.dumps(payload),
49
- text=True,
50
- capture_output=True,
51
- check=False,
52
- timeout=5,
53
- )
54
- if result.returncode != 0:
55
- detail = result.stderr.strip() or f"exit {result.returncode}"
56
- raise RuntimeError(detail)
57
-
58
-
59
- def main() -> None:
60
- action = sys.argv[1] if len(sys.argv) > 1 else ""
61
- if action not in ("start", "stop"):
62
- print(json.dumps({}))
63
- return
64
-
65
- try:
66
- raw = sys.stdin.read()
67
- hook_input = json.loads(raw) if raw.strip() else {}
68
- if not isinstance(hook_input, dict):
69
- hook_input = {}
70
- except (json.JSONDecodeError, EOFError) as exc:
71
- print(f"[subagent_tracker] invalid hook input: {exc}", file=sys.stderr)
72
- hook_input = {}
73
-
74
- root = find_root_for_hook(
75
- Path(hook_input.get("cwd") or Path.cwd()).resolve(),
76
- "subagent_tracker",
77
- )
78
- if root is not None:
79
- try:
80
- append_event(root, action, hook_input)
81
- except (OSError, RuntimeError, subprocess.TimeoutExpired) as exc:
82
- print(f"[subagent_tracker] lifecycle event unavailable: {exc}", file=sys.stderr)
83
-
84
- print(json.dumps({}))
85
-
86
-
87
- if __name__ == "__main__":
88
- main()
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Write an advisory recovery copy of the live Context Envelope before compaction."""
3
-
4
- import json
5
- import os
6
- import sys
7
- import tempfile
8
- from datetime import datetime, timezone
9
- from pathlib import Path
10
-
11
- from context_envelope import (
12
- ContextEnvelopeError,
13
- find_root_for_hook,
14
- read_context_envelope,
15
- render_context_envelope,
16
- )
17
- from runtime_paths import RuntimePathError, validate_project_layout
18
-
19
-
20
- def main() -> None:
21
- try:
22
- data = json.loads(sys.stdin.read() or "{}")
23
- data = data if isinstance(data, dict) else {}
24
- except json.JSONDecodeError as exc:
25
- print(f"[workflow_checkpoint] invalid hook input: {exc}", file=sys.stderr)
26
- data = {}
27
- root = find_root_for_hook(
28
- Path(data.get("cwd") or Path.cwd()).resolve(),
29
- "workflow_checkpoint",
30
- )
31
- if root is None:
32
- print(json.dumps({}))
33
- return
34
- try:
35
- envelope = read_context_envelope(root)
36
- except ContextEnvelopeError as exc:
37
- print(f"[workflow_checkpoint] cannot inspect Context Envelope: {exc}", file=sys.stderr)
38
- print(json.dumps({}))
39
- return
40
- if not envelope:
41
- print(json.dumps({}))
42
- return
43
-
44
- try:
45
- validate_project_layout(root, validate_runtime_tree=True)
46
- except RuntimePathError as exc:
47
- print(
48
- f"[workflow_checkpoint] unsafe Ultra runtime: {exc}",
49
- file=sys.stderr,
50
- )
51
- print(json.dumps({}))
52
- return
53
- runtime_dir = root / ".ultra" / ".runtime"
54
- runtime_dir.mkdir(parents=True, exist_ok=True)
55
- checkpoint = {
56
- "schema": 3,
57
- "captured_at": datetime.now(timezone.utc).isoformat(),
58
- "session_id": data.get("session_id", ""),
59
- "context_digest": envelope.get("digest"),
60
- "context": dict(envelope),
61
- "rendered": render_context_envelope(root, envelope),
62
- }
63
- fd, temp_name = tempfile.mkstemp(prefix="checkpoint.", suffix=".json", dir=runtime_dir)
64
- try:
65
- with os.fdopen(fd, "w", encoding="utf-8") as handle:
66
- json.dump(checkpoint, handle, ensure_ascii=False, indent=2)
67
- handle.write("\n")
68
- os.replace(temp_name, runtime_dir / "checkpoint.json")
69
- finally:
70
- if os.path.exists(temp_name):
71
- os.unlink(temp_name)
72
- print(json.dumps({"systemMessage": "Ultra Context Envelope checkpoint saved."}))
73
-
74
-
75
- if __name__ == "__main__":
76
- main()
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Inject one compact, read-only 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_for_hook,
11
- read_context_envelope,
12
- render_context_envelope,
13
- )
14
-
15
-
16
- def payload() -> dict:
17
- try:
18
- value = json.loads(sys.stdin.read() or "{}")
19
- return value if isinstance(value, dict) else {}
20
- except json.JSONDecodeError as exc:
21
- print(f"[workflow_context] invalid hook input: {exc}", file=sys.stderr)
22
- return {}
23
-
24
-
25
- def main() -> None:
26
- data = payload()
27
- root = find_root_for_hook(
28
- Path(data.get("cwd") or Path.cwd()).resolve(),
29
- "workflow_context",
30
- )
31
- if root is None:
32
- print(json.dumps({}))
33
- return
34
- try:
35
- envelope = read_context_envelope(root)
36
- except ContextEnvelopeError as exc:
37
- print(f"[workflow_context] cannot inspect Context Envelope: {exc}", file=sys.stderr)
38
- envelope = None
39
- if not envelope:
40
- print(json.dumps({}))
41
- return
42
- context = render_context_envelope(root, envelope)
43
- print(json.dumps({"hookSpecificOutput": {
44
- "hookEventName": "SessionStart",
45
- "additionalContext": context,
46
- }}))
47
- if __name__ == "__main__":
48
- main()
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Re-inject the live Context Envelope after compaction; checkpoints are advisory."""
3
-
4
- import json
5
- import sys
6
- from datetime import datetime
7
- from pathlib import Path
8
-
9
- from context_envelope import (
10
- ContextEnvelopeError,
11
- find_root_for_hook,
12
- read_context_envelope,
13
- render_context_envelope,
14
- )
15
-
16
-
17
- def _hook_output(event: str, text: str) -> dict:
18
- if event == "SessionStart":
19
- return {"hookSpecificOutput": {
20
- "hookEventName": "SessionStart",
21
- "additionalContext": text,
22
- }}
23
- return {"additionalContext": text}
24
-
25
-
26
- def _checkpoint_advisory(root: Path):
27
- file = root / ".ultra" / ".runtime" / "checkpoint.json"
28
- if not file.is_file():
29
- return None
30
- try:
31
- value = json.loads(file.read_text(encoding="utf-8"))
32
- datetime.fromisoformat(str(value.get("captured_at", "")).replace("Z", "+00:00"))
33
- except (OSError, json.JSONDecodeError, ValueError, AttributeError) as exc:
34
- print(f"[workflow_resume] cannot read checkpoint {file}: {exc}", file=sys.stderr)
35
- return None
36
- if (
37
- not isinstance(value, dict)
38
- or value.get("schema") != 3
39
- or not isinstance(value.get("context"), dict)
40
- or not isinstance(value.get("rendered"), str)
41
- or not value["rendered"].strip()
42
- or value.get("context_digest") != value["context"].get("digest")
43
- ):
44
- print(f"[workflow_resume] invalid checkpoint schema in {file}", file=sys.stderr)
45
- return None
46
- return "\n".join([
47
- "[Ultra checkpoint advisory]",
48
- "Live .ultra/.runtime/state.db could not be read; this checkpoint is not authority.",
49
- value["rendered"],
50
- "Run ultra.doctor before mutation.",
51
- ])
52
-
53
-
54
- def main() -> None:
55
- try:
56
- data = json.loads(sys.stdin.read() or "{}")
57
- data = data if isinstance(data, dict) else {}
58
- except json.JSONDecodeError as exc:
59
- print(f"[workflow_resume] invalid hook input: {exc}", file=sys.stderr)
60
- data = {}
61
- root = find_root_for_hook(
62
- Path(data.get("cwd") or Path.cwd()).resolve(),
63
- "workflow_resume",
64
- )
65
- if root is None:
66
- print(json.dumps({}))
67
- return
68
- text = None
69
- try:
70
- envelope = read_context_envelope(root)
71
- if envelope:
72
- text = render_context_envelope(root, envelope)
73
- except ContextEnvelopeError as exc:
74
- print(f"[workflow_resume] cannot inspect Context Envelope: {exc}", file=sys.stderr)
75
- text = _checkpoint_advisory(root)
76
- if not text:
77
- print(json.dumps({}))
78
- return
79
- event = data.get("hook_event_name", "SessionStart")
80
- print(json.dumps(_hook_output(event, text)))
81
-
82
-
83
- if __name__ == "__main__":
84
- main()