ultra-builder-pro-cli 0.5.3 → 0.7.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 (91) hide show
  1. package/.ultra-template/changes/active/.gitkeep +1 -0
  2. package/.ultra-template/changes/archive/.gitkeep +1 -0
  3. package/CHANGELOG.md +90 -14
  4. package/README.md +38 -32
  5. package/adapters/_shared/codex-assets.cjs +32 -151
  6. package/adapters/_shared/frontmatter.cjs +2 -2
  7. package/adapters/_shared/handbook.cjs +10 -10
  8. package/adapters/_shared/path-rewrite.cjs +0 -3
  9. package/adapters/_shared/runtime-assets.cjs +13 -4
  10. package/adapters/_shared/tests/handbook.test.cjs +4 -1
  11. package/adapters/_shared/tests/path-rewrite.test.cjs +1 -3
  12. package/adapters/_shared/tests/resolve-target.test.cjs +1 -11
  13. package/adapters/_shared/tests/runtime-assets.test.cjs +7 -6
  14. package/adapters/opencode.js +82 -26
  15. package/adapters/tests/claude.test.cjs +1 -4
  16. package/adapters/tests/codex.test.cjs +9 -8
  17. package/adapters/tests/opencode.test.cjs +17 -11
  18. package/bin/install.js +6 -28
  19. package/bin/orchestrator.js +12 -30
  20. package/commands/ultra-change.md +47 -0
  21. package/commands/ultra-deliver.md +14 -7
  22. package/commands/ultra-dev.md +6 -3
  23. package/commands/ultra-doctor.md +36 -0
  24. package/commands/ultra-init.md +4 -3
  25. package/commands/ultra-status.md +7 -5
  26. package/commands/ultra-test.md +2 -1
  27. package/docs/AGENT-CONTEXT.md +35 -28
  28. package/docs/ARCHITECTURE.md +49 -21
  29. package/docs/PLAN.zh-CN.md +67 -68
  30. package/docs/ROADMAP.md +17 -17
  31. package/docs/RUNTIME-COMPAT-MATRIX.md +63 -62
  32. package/hooks/README.md +8 -7
  33. package/hooks/active_task_context.py +22 -15
  34. package/hooks/health_check.py +136 -38
  35. package/hooks/workflow_context.py +120 -30
  36. package/mcp-server/lib/change-workflow.cjs +556 -0
  37. package/mcp-server/lib/change-workflow.test.cjs +259 -0
  38. package/mcp-server/lib/doctor.cjs +112 -0
  39. package/mcp-server/lib/doctor.test.cjs +86 -0
  40. package/mcp-server/lib/init-project.cjs +32 -6
  41. package/mcp-server/lib/pricing.cjs +0 -6
  42. package/mcp-server/lib/runtime-state.cjs +222 -0
  43. package/mcp-server/lib/runtime-state.test.cjs +92 -0
  44. package/mcp-server/lib/state-db.cjs +31 -8
  45. package/mcp-server/lib/state-ops.cjs +24 -3
  46. package/mcp-server/lib/telemetry.cjs +4 -0
  47. package/mcp-server/server.cjs +102 -10
  48. package/mcp-server/tests/db-ops.test.cjs +5 -1
  49. package/mcp-server/tests/init-project.test.cjs +45 -0
  50. package/mcp-server/tests/server-contract.test.cjs +140 -1
  51. package/mcp-server/tests/state-db.test.cjs +44 -7
  52. package/mcp-server/tests/state-ops.test.cjs +42 -0
  53. package/mcp-server/tests/telemetry.test.cjs +14 -0
  54. package/orchestrator/daemon.cjs +39 -0
  55. package/orchestrator/dispatch-rules.cjs +15 -4
  56. package/orchestrator/tests/daemon.test.cjs +63 -12
  57. package/orchestrator/tests/dispatch-rules.test.cjs +12 -10
  58. package/package.json +5 -7
  59. package/settings.json +1 -2
  60. package/skills/ultra-change/SKILL.md +140 -0
  61. package/skills/ultra-deliver/SKILL.md +81 -15
  62. package/skills/ultra-dev/SKILL.md +32 -12
  63. package/skills/ultra-doctor/SKILL.md +90 -0
  64. package/skills/ultra-init/SKILL.md +8 -6
  65. package/skills/ultra-review/SKILL.md +16 -0
  66. package/skills/ultra-status/SKILL.md +30 -7
  67. package/skills/ultra-test/SKILL.md +14 -2
  68. package/skills/ultra-verify/SKILL.md +22 -41
  69. package/skills/ultra-verify/evals/evals.json +22 -24
  70. package/skills/ultra-verify/references/confidence-system.md +15 -32
  71. package/skills/ultra-verify/references/cross-verify-modes.md +14 -59
  72. package/skills/ultra-verify/scripts/verify_wait.py +44 -81
  73. package/spec/cli-protocol.md +12 -1
  74. package/spec/mcp-tools.yaml +340 -4
  75. package/spec/schemas/mcp-tools.schema.json +1 -1
  76. package/spec/schemas/skill-manifest.schema.json +2 -2
  77. package/spec/schemas/state-db.sql +119 -2
  78. package/ultra-tools/cli.cjs +3 -0
  79. package/ultra-tools/commands/system.cjs +57 -0
  80. package/ultra-tools/system-command.test.cjs +53 -0
  81. package/adapters/_shared/md-to-toml.cjs +0 -54
  82. package/adapters/_shared/rtk-detect.cjs +0 -86
  83. package/adapters/_shared/tests/md-to-toml.test.cjs +0 -48
  84. package/adapters/_shared/tests/rtk-detect.test.cjs +0 -116
  85. package/adapters/gemini.js +0 -204
  86. package/adapters/tests/gemini.test.cjs +0 -112
  87. package/docs/LEGACY-HERMES.md +0 -1175
  88. package/orchestrator/code-graph-watcher.cjs +0 -121
  89. package/orchestrator/tests/code-graph-watcher.test.cjs +0 -183
  90. package/skills/gemini-collab/SKILL.md +0 -51
  91. package/skills/gemini-collab/evals/evals.json +0 -42
package/docs/ROADMAP.md CHANGED
@@ -1,20 +1,19 @@
1
1
  # ultra-builder-pro-cli — Roadmap
2
2
 
3
- > **Authoritative roadmap**: [`PLAN.zh-CN.md`](./PLAN.zh-CN.md) (v0.3.1).
4
- > This file is a one-page summary; details, decisions, and time estimates
5
- > live in PLAN. If they disagree, PLAN wins.
3
+ > **Current roadmap**. [`PLAN.zh-CN.md`](./PLAN.zh-CN.md) preserves the original
4
+ > phase plan and decision history. Current executable contracts live in
5
+ > `spec/`, `AGENT-CONTEXT.md`, and `RUNTIME-COMPAT-MATRIX.md`; those sources win
6
+ > when historical plan text differs.
6
7
 
7
8
  **Goal**: distribute the Ultra Builder Pro engineering loop as native Claude
8
- Code, OpenCode, and Codex plugins, retain a Gemini compatibility extension, and
9
- run the loop with isolated sessions sharing one authoritative workflow store.
9
+ Code, OpenCode, and Codex plugins, and run it with isolated sessions sharing one
10
+ authoritative workflow store.
10
11
 
11
12
  **First-class runtimes**: Claude Code · OpenCode · Codex.
12
13
 
13
14
  **Distribution channels (v1.0)**: npm · Homebrew · pip.
14
15
 
15
- **Confidence**: 86% (PLAN §10).
16
-
17
- **Timeline**: 14–18 weeks AI-assisted (PLAN §11).
16
+ **Release line**: `0.7.x`; every release is gated by `npm run verify:release`.
18
17
 
19
18
  ---
20
19
 
@@ -23,7 +22,7 @@ run the loop with isolated sessions sharing one authoritative workflow store.
23
22
  | Release | Week | Contents |
24
23
  |---------|-----:|---------------------------------------------------------------------------|
25
24
  | **v0.1**| 8 | Rule layer + execution-lite (session isolation + admission + event subscribe + active-session visibility) — solves the core "independent conversations don't pollute each other" pain |
26
- | **v0.2**| 11 | Auto-recovery + monitoring + real-time code-review-graph + full conformance |
25
+ | **v0.2**| 11 | Auto-recovery + monitoring + full conformance |
27
26
  | **v0.3**| 16 | PRD → execution-plan artifact → parallel dispatch / merge — coding factory |
28
27
  | **v1.0**| 17–18| Three-channel publish (npm + Homebrew + pip) |
29
28
 
@@ -38,30 +37,31 @@ run the loop with isolated sessions sharing one authoritative workflow store.
38
37
  | 4 | Cross-runtime distribution + 4.6a smoke flow | ✅ done (D41, `5aa1fd0`) |
39
38
  | 4.5 | Execution-lite (session + admission + events) | ✅ done (D42, `0d3e5ed`) — **v0.1 ready** |
40
39
  | 5 | Recovery + staleness + auto-routing | ✅ done (D43) → v0.2 |
41
- | 6 | Monitoring + code-review-graph live watcher | ✅ done (D44) → v0.2 |
40
+ | 6 | Monitoring + telemetry | ✅ done (D44) → v0.2 |
42
41
  | 4.6b | Full conformance suite | ✅ done (D45) — 20 conformance + 21 resolveTarget tests |
43
42
  | 7 | tagged tasks + skill mining; retired Ultra memory handed to cloud-mem/claude-mem | ✅ superseded by D50 boundary cleanup |
44
43
  | 8A | Plan automation (parse / topo / expand + artifact + human gate) | ✅ done (D47, `a932cb8`) → v0.3 |
45
44
  | 8B | Execution automation (dispatch / parallel worktree / merge) | ✅ done (D48, `8224159`) — **v0.3 ready** |
46
- | 9 | Release pipeline (npm / Homebrew / pip) | pending → v1.0 |
45
+ | 8C | Continuous change packets + context compiler + convergence + doctor | ✅ done (D52) |
46
+ | 9 | Release pipeline | npm tag publishing live; Homebrew / pip not implemented |
47
47
 
48
48
  ## What is in the repo today
49
49
 
50
50
  ```
51
51
  spec/ ← Phase 1 single source of truth
52
- ├── mcp-tools.yaml (30 declared tools across 7 families; 21 live)
52
+ ├── mcp-tools.yaml (29 live tools across 5 families)
53
53
  ├── cli-protocol.md (CLI ↔ MCP mapping table)
54
54
  ├── schemas/ (state-db.sql + 4 JSON schemas)
55
55
  ├── fixtures/{valid,invalid}/ (+ v4.4-project for migration)
56
- └── scripts/test-all.cjs (npm run test:spec — 5 validators)
56
+ └── scripts/test-all.cjs (npm run test:spec — 7 validation stages)
57
57
 
58
58
  mcp-server/ ← Phase 2 authoritative state layer
59
- ├── server.cjs (stdio MCP server, 21 task/session/plan tools)
59
+ ├── server.cjs (stdio MCP server, 29 task/session/change/system/plan tools)
60
60
  ├── lib/
61
61
  │ ├── state-db.cjs (SQLite + WAL + pragmas)
62
62
  │ ├── state-ops.cjs (full write API, status state machine)
63
63
  │ └── projector.cjs (state.db → tasks.json + context md)
64
- └── tests/ (npm run test:state — 44 tests)
64
+ └── tests/ (npm run test:state)
65
65
 
66
66
  ultra-tools/ ← CLI fallback, migration, and diagnostics
67
67
  ├── cli.cjs
@@ -72,11 +72,11 @@ ultra-tools/ ← CLI fallback, migration, and diagnostics
72
72
 
73
73
  bin/install.js ← multi-runtime installer
74
74
  bin/handbook.js ← explicit managed user-handbook sync
75
- adapters/ ← native Claude/OpenCode/Codex plugin builders + Gemini compatibility
75
+ adapters/ ← native Claude/OpenCode/Codex plugin builders
76
76
  skills/ ← allowlisted Ultra workflows, internal rules, and collab companions
77
77
  hooks/ ← 7 workflow-only Python hooks; OpenCode uses native JavaScript hooks
78
78
  docs/
79
- ├── PLAN.zh-CN.md authoritative plan (1670+ lines)
79
+ ├── PLAN.zh-CN.md historical phase plan + decision log
80
80
  ├── ARCHITECTURE.md Phase 1 single-page entry point
81
81
  ├── AGENT-CONTEXT.md Phase 3 canonical runtime context contract
82
82
  ├── USER-HANDBOOK-CONTRACT.md managed CLAUDE.md / AGENTS.md boundary
@@ -1,89 +1,88 @@
1
1
  # Runtime Compatibility Matrix
2
2
 
3
3
  Current presentation contract for Ultra Builder Pro. Claude Code, OpenCode, and
4
- Codex are first-class native plugins; Gemini CLI remains a compatibility
5
- extension. The canonical asset boundary lives in
6
- `adapters/_shared/runtime-assets.cjs`.
4
+ Codex are the supported native plugin hosts. The canonical asset boundary lives
5
+ in `adapters/_shared/runtime-assets.cjs`.
7
6
 
8
- Legend: **FULL** = native host surface, **DEGRADED** = compatible but missing an
7
+ Legend: **FULL** = native host surface, **DEGRADED** = supported but missing an
9
8
  equivalent lifecycle/control surface, **N/A** = intentionally not installed.
10
9
 
11
10
  ## 1. Plugin and workflow surface
12
11
 
13
- | Capability | Claude Code | OpenCode | Codex | Gemini CLI |
14
- |---|---|---|---|---|
15
- | Package form | `.claude-plugin` native plugin | native config bundle + JS plugin | personal `.codex-plugin` | compatibility extension |
16
- | Public entry | `/ultra-*`, `/learn` | `/ultra-*`, `/learn` | `$ultra-builder-pro:<skill>` | generated command TOML |
17
- | Public Ultra workflows | 10 | 10 | 10 | 10 |
18
- | Internal agent-rule skills | 4, non-user-facing | 4, non-user-facing | 4 with implicit invocation disabled | 4 prompt dependencies |
19
- | Collaboration companions | `codex-collab`, `gemini-collab`, `ultra-verify` | `cc-collab`, `codex-collab`, `gemini-collab`, `ultra-verify` | `cc-collab`, `gemini-collab`, `ultra-verify` | `cc-collab`, `codex-collab`, `ultra-verify` |
20
- | External browser/deploy/framework skills | N/A | N/A | N/A | N/A |
12
+ | Capability | Claude Code | OpenCode | Codex |
13
+ |---|---|---|---|
14
+ | Package form | `.claude-plugin` native plugin | native config bundle + JS plugin | personal `.codex-plugin` |
15
+ | Public entry | `/ultra-*`, `/learn` | `/ultra-*`, `/learn` | `$ultra-builder-pro:<skill>` |
16
+ | Public Ultra workflows | 12 | 12 | 12 |
17
+ | Internal agent-rule skills | 4, non-user-facing | 4, non-user-facing | 4 with implicit invocation disabled |
18
+ | Collaboration companions | `codex-collab`, `ultra-verify` | `cc-collab`, `codex-collab`, `ultra-verify` | `cc-collab`, `ultra-verify` |
19
+ | External browser/deploy/framework skills | N/A | N/A | N/A |
21
20
 
22
- Codex does not emulate deprecated custom slash prompts. Its adapter converts
23
- the workflows into namespaced skills and records nine legacy command mappings
24
- in `command-map.json`; `ultra-review` is directly invocable as a skill.
21
+ Codex converts the workflows into namespaced skills and records eleven legacy
22
+ command mappings in `command-map.json`; `ultra-review` remains directly
23
+ invocable as a skill.
25
24
 
26
25
  ## 2. Agents and collaboration
27
26
 
28
- | Capability | Claude Code | OpenCode | Codex | Gemini CLI |
29
- |---|---|---|---|---|
30
- | Bundled review/debug agents | FULL, native Markdown agents | FULL, lowercased native agent frontmatter | FULL, nine managed TOML custom agents | N/A |
31
- | Native bounded delegation | `Task`/agent surface | native agent surface | native Codex subagents | evolving host surface, not packaged |
32
- | Cross-host advisor | explicit collab skill; read-only | explicit collab skill; read-only | explicit collab skill; read-only | explicit collab skill; read-only |
33
- | Primary-agent ownership | FULL | FULL | FULL | FULL |
27
+ | Capability | Claude Code | OpenCode | Codex |
28
+ |---|---|---|---|
29
+ | Bundled review/debug agents | FULL, native Markdown agents | FULL, native agent frontmatter | FULL, nine managed TOML custom agents |
30
+ | Native bounded delegation | native agent surface | native agent surface | native Codex subagents |
31
+ | Cross-host advisor | explicit and read-only | explicit and read-only | explicit and read-only |
32
+ | Primary-agent ownership | FULL | FULL | FULL |
34
33
 
35
- No bundled agent declares private persistent memory. It receives current
36
- checkout evidence and bounded context from the primary host, then returns a
37
- result for primary verification.
34
+ No bundled agent owns private persistent memory. It receives current-checkout
35
+ evidence and bounded context from the primary host, then returns a result for
36
+ primary verification.
38
37
 
39
38
  ## 3. Workflow hooks
40
39
 
41
- | Lifecycle | Claude Code | OpenCode | Codex | Gemini CLI |
42
- |---|---|---|---|---|
43
- | Session context/health | FULL, native `SessionStart` | FULL, system transform + event refresh | FULL, native `SessionStart` | N/A |
44
- | Active edit boundary | FULL, `PreToolUse Edit|Write` | FULL, `tool.execute.before` rejects projection writes | FULL, `PreToolUse Edit|Write|apply_patch` | N/A |
45
- | Compaction recovery | FULL, `PreCompact` + compact resume matcher | FULL, native compacting context | FULL, `PreCompact` + `PostCompact` | N/A |
46
- | Incomplete-stop gate | FULL, native blocking `Stop` hook | DEGRADED, no equivalent blocking stop hook | FULL, native blocking `Stop` hook | N/A |
47
- | Subagent lifecycle evidence | FULL | DEGRADED, no equivalent packaged event | FULL | N/A |
48
-
49
- All reachable hooks are workflow-only and no-op without an active non-terminal
50
- `.ultra/workflow-state.json`. No runtime receives Ultra prompt capture,
51
- transcript capture, observation journal, session-summary memory, generic command
40
+ | Lifecycle | Claude Code | OpenCode | Codex |
41
+ |---|---|---|---|
42
+ | Session context/health | FULL, native `SessionStart` | DEGRADED, native context; health via `system.doctor` | FULL, native `SessionStart` |
43
+ | Active edit boundary | FULL, `PreToolUse Edit|Write` | FULL, `tool.execute.before` rejects projection writes | FULL, `PreToolUse Edit|Write|apply_patch` |
44
+ | Compaction recovery | FULL, `PreCompact` + resume matcher | FULL, native compacting context | FULL, `PreCompact` + `PostCompact` |
45
+ | Incomplete-stop gate | FULL, native blocking `Stop` | DEGRADED, no equivalent blocking stop hook | FULL, native blocking `Stop` |
46
+ | Subagent lifecycle evidence | FULL | DEGRADED, no equivalent packaged event | FULL |
47
+
48
+ Health/context hooks inspect initialized projects; projection protection also
49
+ applies at baseline, while compact/stop/subagent enforcement remains active-workflow scoped.
50
+ No host receives prompt capture, transcript
51
+ capture, observation journaling, session-summary memory, generic command
52
52
  blocking, or generic post-edit policy.
53
53
 
54
54
  ## 4. MCP and state
55
55
 
56
- | Capability | Claude Code | OpenCode | Codex | Gemini CLI |
57
- |---|---|---|---|---|
58
- | stdio MCP registration | plugin `.mcp.json` | `opencode.json` local MCP entry | plugin `.mcp.json` | extension manifest |
59
- | Live contracts | 21 | 21 | 21 | 21 |
60
- | Declared upstream contracts | 30 | 30 | 30 | 30 |
61
- | Nine non-live contracts | host-native review/discovery/ask surfaces | host-native review/discovery/ask surfaces | explicit `codex-capability-map.json` | compatibility guidance |
62
- | Durable authority | project `.ultra/state.db` | project `.ultra/state.db` | project `.ultra/state.db` | project workflow state when invoked from the project |
63
- | Ultra memory API | N/A | N/A | N/A | N/A |
56
+ | Capability | Claude Code | OpenCode | Codex |
57
+ |---|---|---|---|
58
+ | stdio MCP registration | plugin `.mcp.json` | `opencode.json` local MCP entry | plugin `.mcp.json` |
59
+ | Live/declared contracts | 29 | 29 | 29 |
60
+ | Host-native review/discovery/ask | native | native | documented in `codex-capability-map.json` |
61
+ | Durable authority | project `.ultra/state.db` | project `.ultra/state.db` | project `.ultra/state.db` |
62
+ | Ultra memory API | N/A | N/A | N/A |
64
63
 
65
- The seven declared tool families are `task`, `session`, `plan`, `review`,
66
- `impact`, `skill`, and `ask`. Only the 21 `task.*`, `session.*`, and `plan.*`
67
- operations registered by `mcp-server/server.cjs` are advertised as live.
64
+ All 29 `task.*`, `session.*`, `change.*`, `system.*`, and `plan.*` operations
65
+ registered by `mcp-server/server.cjs` are live. Review, impact discovery, skill
66
+ loading, and user interaction remain host-native surfaces.
68
67
 
69
68
  ## 5. User handbook presentation
70
69
 
71
- | Capability | Claude Code | OpenCode | Codex | Gemini CLI |
72
- |---|---|---|---|---|
73
- | Durable user file | `~/.claude/CLAUDE.md` | `~/.config/opencode/AGENTS.md` | `~/.codex/AGENTS.md` | extension `GEMINI.md` context |
74
- | Automatic overwrite by plugin install | no | no | no | extension-owned context only |
75
- | Explicit managed-block sync | `ubp-handbook --runtime claude` | `ubp-handbook --runtime opencode` | `ubp-handbook --runtime codex` | N/A |
76
- | Backup before change | FULL | FULL | FULL | N/A |
70
+ | Capability | Claude Code | OpenCode | Codex |
71
+ |---|---|---|---|
72
+ | Durable user file | `~/.claude/CLAUDE.md` | `~/.config/opencode/AGENTS.md` | `~/.codex/AGENTS.md` |
73
+ | Automatic overwrite by plugin install | no | no | no |
74
+ | Explicit managed-block sync | `ubp-handbook --runtime claude` | `ubp-handbook --runtime opencode` | `ubp-handbook --runtime codex` |
75
+ | Backup before change | FULL | FULL | FULL |
77
76
 
78
77
  ## 6. Install, update, and uninstall
79
78
 
80
- | Capability | Claude Code | OpenCode | Codex | Gemini CLI |
81
- |---|---|---|---|---|
82
- | Adapter install | FULL | FULL | FULL | FULL compatibility |
83
- | Native registration/update | host loads plugin directory on next session | host loads local JS plugin on restart | cachebuster + `codex plugin add`; start a new task | host loads extension |
84
- | Removes stale managed assets | FULL | FULL | FULL | FULL while preserving extension runtime state |
85
- | Preserves unrelated user config | FULL | FULL | FULL | FULL inside user config; extension directory is managed |
86
- | Uninstall ownership guard | managed plugin root | sentinels + owned MCP entry | managed root/manifest/agent headers | extension `_ubp.source` |
79
+ | Capability | Claude Code | OpenCode | Codex |
80
+ |---|---|---|---|
81
+ | Adapter install | FULL | FULL | FULL |
82
+ | Native update | next host session | host restart | cachebuster + `codex plugin add`, then new task |
83
+ | Removes stale managed assets | FULL | FULL | FULL |
84
+ | Preserves unrelated config | FULL | FULL | FULL |
85
+ | Uninstall ownership guard | managed plugin root | sentinels + owned MCP entry | managed root/manifest/agent headers |
87
86
 
88
87
  ## 7. Verification sources
89
88
 
@@ -91,8 +90,10 @@ operations registered by `mcp-server/server.cjs` are advertised as live.
91
90
  - `tests/conformance/<runtime>/*.test.cjs` verifies command/skill, hook, MCP,
92
91
  idempotency, and smoke presentation.
93
92
  - `adapters/_shared/tests/runtime-assets.test.cjs` prevents retired or external
94
- skills and generic/memory hooks from re-entering any package.
95
- - `adapters/_shared/tests/handbook.test.cjs` verifies host rendering, safe
96
- legacy-section migration, backup, and idempotency.
93
+ skills and generic/memory hooks from re-entering a package.
94
+ - `tests/retired-runtime.test.cjs` prevents retired runtime code or prompt text
95
+ from re-entering active product surfaces.
96
+ - `adapters/_shared/tests/handbook.test.cjs` verifies host rendering, backup,
97
+ migration, and idempotency.
97
98
 
98
99
  Any capability claim elsewhere must match current adapter code and these tests.
package/hooks/README.md CHANGED
@@ -1,21 +1,22 @@
1
1
  # Ultra Builder Pro workflow hooks
2
2
 
3
- These hooks protect only a currently active Ultra workflow. Every hook is a no-op unless the
4
- project contains `.ultra/workflow-state.json` with a non-terminal status.
3
+ These hooks observe only Ultra-owned state. Session health/context recognizes an initialized
4
+ `.ultra/state.db` and active continuous changes; edit/compact/stop/subagent enforcement remains
5
+ bounded to the relevant Ultra projection or a non-terminal workflow.
5
6
 
6
7
  | Lifecycle | Hook | Purpose |
7
8
  |---|---|---|
8
- | Session start | `health_check.py` | Advisory check of `.ultra/state.db` core tables |
9
- | Session start | `workflow_context.py` | Inject current command, task, step, and authority |
10
- | Before edit | `active_task_context.py` | Restate the active task boundary |
9
+ | Session start | `health_check.py` | Read-only integrity, incident, projection, session, and change-artifact checks |
10
+ | Session start | `workflow_context.py` | Inject baseline/active workflow/change context and filtered provider metadata references |
11
+ | Before edit | `active_task_context.py` | Protect `tasks.json` projection and restate an active task boundary |
11
12
  | Before compact | `workflow_checkpoint.py` | Atomically save a minimal workflow checkpoint |
12
13
  | After compact/resume | `workflow_resume.py` | Re-inject the current workflow boundary |
13
14
  | Stop | `pre_stop_check.py` | Block once when the Ultra workflow is incomplete |
14
15
  | Subagent lifecycle | `subagent_tracker.py` | Append lifecycle evidence under `.ultra/runtime/` |
15
16
 
16
17
  The plugin deliberately does not capture prompts, tool observations, transcripts, summaries, or
17
- cross-session memory. Generic command blocking and post-edit policy are user/repository governance,
18
- not Ultra plugin hooks. Persistent memory belongs to the separately installed memory provider.
18
+ cross-session memory or code-graph content. Generic command blocking and post-edit policy are
19
+ user/repository governance. Persistent Memory/graph content belongs to separately installed providers.
19
20
 
20
21
  Runtime wiring:
21
22
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Remind an active Ultra workflow of its task boundary before edits."""
2
+ """Protect Ultra projections and restate an active task boundary before edits."""
3
3
 
4
4
  import json
5
5
  import sys
@@ -33,9 +33,28 @@ def main() -> None:
33
33
  data = {}
34
34
  start = Path(data.get("cwd") or Path.cwd()).resolve()
35
35
  for root in (start, *start.parents):
36
- state_file = root / ".ultra" / "workflow-state.json"
37
- if not state_file.is_file():
36
+ ultra = root / ".ultra"
37
+ state_file = ultra / "workflow-state.json"
38
+ initialized = (
39
+ (ultra / "state.db").is_file()
40
+ or state_file.is_file()
41
+ or (ultra / "changes" / "active").is_dir()
42
+ )
43
+ if not initialized:
38
44
  continue
45
+ if targets_tasks_projection(data, root):
46
+ print(json.dumps({"hookSpecificOutput": {
47
+ "hookEventName": "PreToolUse",
48
+ "permissionDecision": "deny",
49
+ "permissionDecisionReason": (
50
+ "Refusing a direct write to the .ultra/tasks/tasks.json projection. "
51
+ ".ultra/state.db is authoritative; use the Ultra MCP task tools and "
52
+ "run ultra-doctor when state or projection health is degraded."
53
+ ),
54
+ }}))
55
+ return
56
+ if not state_file.is_file():
57
+ break
39
58
  try:
40
59
  state = json.loads(state_file.read_text(encoding="utf-8"))
41
60
  except (OSError, json.JSONDecodeError) as exc:
@@ -45,18 +64,6 @@ def main() -> None:
45
64
  break
46
65
  task = state.get("task_id", state.get("task", "unknown"))
47
66
  step = state.get("step", "unknown")
48
- if targets_tasks_projection(data, root):
49
- print(json.dumps({"hookSpecificOutput": {
50
- "hookEventName": "PreToolUse",
51
- "permissionDecision": "deny",
52
- "permissionDecisionReason": (
53
- "Refusing a direct write to .ultra/tasks/tasks.json during an active Ultra "
54
- "workflow. .ultra/state.db is authoritative; use the Ultra MCP task tools. "
55
- "If they report LEGACY_STATE_MIGRATION_REQUIRED, run the v4.4 to v4.5 "
56
- "migration before continuing."
57
- ),
58
- }}))
59
- return
60
67
  print(json.dumps({"hookSpecificOutput": {
61
68
  "hookEventName": "PreToolUse",
62
69
  "additionalContext": (
@@ -1,59 +1,157 @@
1
1
  #!/usr/bin/env python3
2
- """Advisory health check for an active Ultra workflow.
3
-
4
- No memory, prompt, transcript, or generic host policy is inspected here.
5
- """
2
+ """Fast, read-only Ultra runtime health inspection at session start."""
6
3
 
7
4
  import json
8
5
  import sqlite3
9
6
  import sys
10
7
  from pathlib import Path
11
8
 
12
- TERMINAL = {"committed", "completed", "done", "cancelled"}
13
- REQUIRED_TABLES = {"tasks", "events", "sessions", "schema_version"}
9
+
10
+ REQUIRED_TABLES = {
11
+ "tasks", "events", "sessions", "schema_version", "migration_history",
12
+ "telemetry", "specs_refs", "circuit_breaker", "changes", "artifacts",
13
+ "context_snapshots", "trace_links", "incidents", "projection_jobs",
14
+ "event_consumers",
15
+ }
14
16
 
15
17
 
16
- def main():
18
+ def hook_input() -> dict:
17
19
  try:
18
- raw = sys.stdin.read()
19
- data = json.loads(raw) if raw.strip() else {}
20
- except (json.JSONDecodeError, OSError) as exc:
20
+ value = json.loads(sys.stdin.read() or "{}")
21
+ return value if isinstance(value, dict) else {}
22
+ except json.JSONDecodeError as exc:
21
23
  print(f"[health_check] invalid hook input: {exc}", file=sys.stderr)
22
- data = {}
23
- start = Path(data.get("cwd") or Path.cwd()).resolve()
24
- root = None
25
- state = None
24
+ return {}
25
+
26
+
27
+ def find_root(start: Path):
26
28
  for candidate in (start, *start.parents):
27
- state_file = candidate / ".ultra" / "workflow-state.json"
28
- if state_file.is_file():
29
- try:
30
- value = json.loads(state_file.read_text(encoding="utf-8"))
31
- except (OSError, json.JSONDecodeError) as exc:
32
- print(f"[health_check] cannot read {state_file}: {exc}", file=sys.stderr)
33
- value = None
34
- if isinstance(value, dict) and value.get("status") not in TERMINAL:
35
- root, state = candidate, value
36
- break
37
- if root is None or state is None:
38
- print(json.dumps({}))
39
- return
40
- issues = []
29
+ ultra = candidate / ".ultra"
30
+ if (
31
+ (ultra / "state.db").is_file()
32
+ or (ultra / "workflow-state.json").is_file()
33
+ or (ultra / "changes" / "active").is_dir()
34
+ ):
35
+ return candidate
36
+ return None
37
+
38
+
39
+ def inspect(root: Path) -> dict:
41
40
  db_path = root / ".ultra" / "state.db"
41
+ report = {"status": "healthy", "project": str(root), "checks": {}}
42
+ issues = []
42
43
  if not db_path.is_file():
43
- issues.append(".ultra/state.db is missing")
44
- else:
45
- try:
46
- with sqlite3.connect(str(db_path), timeout=1) as conn:
47
- tables = {row[0] for row in conn.execute(
44
+ report["checks"]["state_db"] = {"status": "fail", "reason": "missing"}
45
+ report["status"] = "degraded"
46
+ return report
47
+
48
+ try:
49
+ uri = f"file:{db_path}?mode=ro"
50
+ with sqlite3.connect(uri, uri=True, timeout=1) as conn:
51
+ quick = conn.execute("PRAGMA quick_check").fetchone()[0]
52
+ tables = {
53
+ row[0] for row in conn.execute(
48
54
  "SELECT name FROM sqlite_master WHERE type='table'"
49
- )}
55
+ )
56
+ }
50
57
  missing = sorted(REQUIRED_TABLES - tables)
58
+ report["checks"]["state_db"] = {
59
+ "status": "pass" if quick == "ok" and not missing else "fail",
60
+ "integrity": quick,
61
+ "missing_tables": missing,
62
+ }
63
+ if quick != "ok" or missing:
64
+ issues.append("state_db")
51
65
  if missing:
52
- issues.append(f"state.db missing tables: {', '.join(missing)}")
53
- except sqlite3.Error as exc:
54
- issues.append(f"state.db unreadable: {exc}")
66
+ report["status"] = "degraded"
67
+ return report
68
+
69
+ incidents = [
70
+ {"id": row[0], "code": row[1], "severity": row[2], "retryable": bool(row[3])}
71
+ for row in conn.execute(
72
+ """SELECT id, code, severity, retryable FROM incidents
73
+ WHERE status = 'open' ORDER BY last_seen_at DESC LIMIT 20"""
74
+ )
75
+ ]
76
+ report["checks"]["incidents"] = {
77
+ "status": "pass" if not incidents else "fail",
78
+ "open": incidents,
79
+ }
80
+ if incidents:
81
+ issues.append("incidents")
82
+
83
+ pending = conn.execute(
84
+ "SELECT COUNT(*) FROM projection_jobs WHERE status = 'pending'"
85
+ ).fetchone()[0]
86
+ running = conn.execute(
87
+ "SELECT COUNT(*) FROM projection_jobs WHERE status = 'running'"
88
+ ).fetchone()[0]
89
+ failed = conn.execute(
90
+ "SELECT COUNT(*) FROM projection_jobs WHERE status = 'failed'"
91
+ ).fetchone()[0]
92
+ event_cursor = conn.execute(
93
+ "SELECT COALESCE(MAX(id), 0) FROM events"
94
+ ).fetchone()[0]
95
+ projected_cursor = conn.execute(
96
+ """SELECT COALESCE(MAX(event_cursor), 0) FROM projection_jobs
97
+ WHERE status = 'completed'"""
98
+ ).fetchone()[0]
99
+ projection_ok = (
100
+ pending == 0 and running == 0 and failed == 0
101
+ and event_cursor <= projected_cursor
102
+ )
103
+ report["checks"]["projections"] = {
104
+ "status": "pass" if projection_ok else "fail",
105
+ "pending": pending,
106
+ "running": running,
107
+ "failed": failed,
108
+ "event_cursor": event_cursor,
109
+ "projected_cursor": projected_cursor,
110
+ }
111
+ if not projection_ok:
112
+ issues.append("projections")
113
+
114
+ orphan = conn.execute(
115
+ "SELECT COUNT(*) FROM sessions WHERE status = 'orphan'"
116
+ ).fetchone()[0]
117
+ report["checks"]["sessions"] = {
118
+ "status": "pass" if orphan == 0 else "fail", "orphan": orphan,
119
+ }
120
+ if orphan:
121
+ issues.append("sessions")
122
+
123
+ missing_artifacts = []
124
+ for change_id, artifact_root in conn.execute(
125
+ """SELECT id, artifact_root FROM changes
126
+ WHERE status IN ('active', 'blocked', 'ready')"""
127
+ ):
128
+ if artifact_root and not (root / artifact_root).exists():
129
+ missing_artifacts.append(change_id)
130
+ report["checks"]["change_artifacts"] = {
131
+ "status": "pass" if not missing_artifacts else "fail",
132
+ "missing": missing_artifacts,
133
+ }
134
+ if missing_artifacts:
135
+ issues.append("change_artifacts")
136
+ except sqlite3.Error as exc:
137
+ report["checks"]["state_db"] = {"status": "fail", "reason": str(exc)}
138
+ issues.append("state_db")
139
+
55
140
  if issues:
56
- print("[Ultra health] " + "; ".join(issues), file=sys.stderr)
141
+ report["status"] = "degraded"
142
+ return report
143
+
144
+
145
+ def main() -> None:
146
+ data = hook_input()
147
+ start = Path(data.get("cwd") or Path.cwd()).resolve()
148
+ root = find_root(start)
149
+ if root is None:
150
+ print(json.dumps({}))
151
+ return
152
+ report = inspect(root)
153
+ if report["status"] != "healthy":
154
+ print("[Ultra health] " + json.dumps(report, ensure_ascii=False, sort_keys=True), file=sys.stderr)
57
155
  print(json.dumps({}))
58
156
 
59
157