ultra-builder-pro-cli 0.23.0 → 0.24.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/docs/research/README.md +4 -4
  2. package/CHANGELOG.md +45 -0
  3. package/README.md +21 -18
  4. package/adapters/_shared/codex-assets.cjs +121 -65
  5. package/adapters/_shared/interaction-contract.cjs +7 -1
  6. package/adapters/_shared/provenance.cjs +7 -2
  7. package/adapters/_shared/runtime-assets.cjs +3 -2
  8. package/adapters/_shared/runtime-native.cjs +132 -0
  9. package/adapters/claude.js +60 -30
  10. package/adapters/codex.js +178 -81
  11. package/adapters/grok.js +372 -0
  12. package/adapters/kimi.js +7 -3
  13. package/adapters/opencode.js +210 -32
  14. package/agents/code-reviewer.md +4 -2
  15. package/agents/debugger.md +4 -1
  16. package/agents/review-code.md +7 -3
  17. package/agents/review-comments.md +7 -4
  18. package/agents/review-coordinator.md +11 -7
  19. package/agents/review-design.md +8 -4
  20. package/agents/review-errors.md +8 -4
  21. package/agents/review-spec.md +8 -5
  22. package/agents/review-tests.md +7 -4
  23. package/agents/tdd-runner.md +5 -2
  24. package/bin/install.js +9 -4
  25. package/bin/orchestrator.js +4 -13
  26. package/docs/AGENT-CONTEXT.md +141 -229
  27. package/docs/ARCHITECTURE.md +203 -494
  28. package/docs/ARTIFACT-AUTHORITY.md +26 -22
  29. package/docs/PLUGIN-ISOLATION-CONTRACT.md +12 -9
  30. package/docs/ROADMAP.md +85 -110
  31. package/docs/RUNTIME-COMPAT-MATRIX.md +139 -175
  32. package/docs/SKILL-AUTHORING.md +3 -3
  33. package/docs/STATE-DB-ACCESS-POLICY.md +13 -8
  34. package/docs/WORKFLOW-LIFECYCLE.md +174 -448
  35. package/hooks/README.md +11 -8
  36. package/hooks/active_task_context.py +16 -16
  37. package/hooks/adapters/grok.py +134 -0
  38. package/hooks/{context_spine.py → context_envelope.py} +26 -22
  39. package/hooks/health_check.py +19 -29
  40. package/hooks/pre_stop_check.py +22 -12
  41. package/hooks/subagent_tracker.py +1 -1
  42. package/hooks/workflow_checkpoint.py +14 -13
  43. package/hooks/workflow_context.py +11 -12
  44. package/hooks/workflow_resume.py +14 -13
  45. package/mcp-server/{breadcrumb.cjs → hook-context.cjs} +7 -9
  46. package/mcp-server/lib/baseline-workflow.cjs +36 -38
  47. package/mcp-server/lib/canonical-json.cjs +19 -0
  48. package/mcp-server/lib/change-workflow.cjs +596 -44
  49. package/mcp-server/lib/context-envelope.cjs +508 -0
  50. package/mcp-server/lib/decision-records.cjs +337 -0
  51. package/mcp-server/lib/doctor.cjs +91 -33
  52. package/mcp-server/lib/init-project.cjs +78 -39
  53. package/mcp-server/lib/managed-file-write.cjs +116 -0
  54. package/mcp-server/lib/plan-checkpoint.cjs +177 -0
  55. package/mcp-server/lib/project-context.cjs +178 -0
  56. package/mcp-server/lib/stage-checkpoints.cjs +309 -0
  57. package/mcp-server/lib/state-db.cjs +155 -24
  58. package/mcp-server/lib/task-ledger.cjs +522 -13
  59. package/mcp-server/lib/ultra-facade.cjs +630 -442
  60. package/mcp-server/lib/worker-packet.cjs +358 -0
  61. package/mcp-server/server.cjs +200 -1103
  62. package/orchestrator/daemon.cjs +27 -9
  63. package/orchestrator/parallel-orchestrator.cjs +111 -76
  64. package/orchestrator/session-runner.cjs +135 -34
  65. package/package.json +10 -3
  66. package/skills/ultra-change/SKILL.md +9 -8
  67. package/skills/ultra-deliver/SKILL.md +3 -3
  68. package/skills/ultra-dev/SKILL.md +18 -11
  69. package/skills/ultra-doctor/SKILL.md +2 -2
  70. package/skills/ultra-init/SKILL.md +6 -5
  71. package/skills/ultra-init/references/brownfield-adoption.md +11 -8
  72. package/skills/ultra-plan/SKILL.md +10 -8
  73. package/skills/ultra-research/SKILL.md +12 -7
  74. package/skills/ultra-review/SKILL.md +9 -6
  75. package/skills/ultra-review/references/unified-schema.md +2 -0
  76. package/skills/ultra-review/scripts/review_wait.py +8 -0
  77. package/skills/ultra-status/SKILL.md +2 -2
  78. package/skills/ultra-test/SKILL.md +4 -2
  79. package/skills/ultra-think/SKILL.md +7 -7
  80. package/skills/ultra-think/references/interaction-boundary.md +56 -0
  81. package/spec/cli-protocol.md +80 -199
  82. package/spec/mcp-tools.yaml +37 -2804
  83. package/spec/schemas/state-db.sql +107 -4
  84. package/spec/schemas/{task-ledger.v1.schema.json → task-ledger.v2.schema.json} +70 -6
  85. package/ultra-tools/commands/status.cjs +369 -243
  86. package/mcp-server/lib/context-spine.cjs +0 -1208
  87. package/mcp-server/lib/decision-dialogue.cjs +0 -1128
  88. package/mcp-server/lib/project-breadcrumb.cjs +0 -173
  89. package/mcp-server/lib/spec-learning.cjs +0 -284
  90. package/mcp-server/lib/workflow-state.cjs +0 -3129
  91. package/skills/ultra-think/references/decision-dialogue.md +0 -155
package/hooks/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Ultra Builder Pro workflow hooks
2
2
 
3
- These hooks observe only Ultra-owned state. Session health/context recognizes an initialized
4
- `.ultra/.runtime/state.db` and active continuous changes. Direct writes to the MCP-owned Git
5
- checkpoint and checkout-local projections remain blocked; context, compact, stop, and subagent
6
- lifecycle behavior is recovery-oriented and does not turn advisory workflow conditions into
7
- refusal gates.
3
+ These Hooks observe only Ultra-owned state. Session health/context recognizes an
4
+ initialized `.ultra/.runtime/state.db` and reads the canonical Context Envelope.
5
+ Direct writes to the MCP-owned Git checkpoint and checkout-local projections remain
6
+ blocked; context, compact, stop, and agent lifecycle behavior is recovery-oriented and
7
+ does not turn semantic diagnostics into refusal gates.
8
8
 
9
9
  | Lifecycle | Hook | Purpose |
10
10
  |---|---|---|
11
11
  | Session start | `health_check.py` | Read-only integrity, incident, projection, session, and change-artifact checks |
12
- | Shared helper | `context_spine.py` | Read the latest role/gate/readiness snapshot from state.db and derive one compact breadcrumb |
13
- | Session start | `workflow_context.py` | Inject the DB-derived breadcrumb with bounded normalized accepted intent, never raw interaction or provider payloads |
12
+ | Shared helper | `context_envelope.py` | Read the same bounded Context Envelope used by MCP, Skills, Sessions, and Workers |
13
+ | Session start | `workflow_context.py` | Inject the compact envelope with accepted intent, never raw interaction or provider payloads |
14
14
  | Before edit | `active_task_context.py` | Protect `.ultra/tasks/tasks.json` (Git team checkpoint) and `.ultra/.runtime/projections/tasks.json` (local view), then restate the same DB-derived task breadcrumb |
15
15
  | Before compact | `workflow_checkpoint.py` | Validate and atomically save a minimal workflow checkpoint |
16
16
  | After compact/resume | `workflow_resume.py` | Prefer the DB breadcrumb; restore the minimal file checkpoint only when no active change owns recovery |
@@ -27,7 +27,7 @@ memory and graph content belongs to separately installed providers.
27
27
  `subagent_started` or `subagent_stopped` to `state.db`; it never stores transcript
28
28
  paths, messages, or a parallel JSONL lifecycle log.
29
29
 
30
- `context_spine.py` is an imported library, not an eighth lifecycle registration.
30
+ `context_envelope.py` is an imported library, not an additional lifecycle registration.
31
31
  Registering it separately would duplicate hook execution. The checkpoint is a recovery artifact, not a second authority. A valid newer
32
32
  live workflow wins; a newer valid checkpoint may restore a missing or older
33
33
  live file; corrupt, non-object, or terminal checkpoints are ignored.
@@ -37,3 +37,6 @@ Runtime wiring:
37
37
  - Claude Code: native `hooks/hooks.json` with `${CLAUDE_PLUGIN_ROOT}` paths.
38
38
  - Codex: native `hooks/hooks.json` through `hooks/adapters/codex.py` for wire normalization.
39
39
  - OpenCode: native JavaScript plugin events; the Python hook suite is not copied.
40
+ - Kimi Code: native lifecycle events through its wire adapter.
41
+ - Grok Build: camelCase lifecycle events; ignored stdout channels are used for
42
+ observation only, and every Skill rereads authoritative Context at entry.
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env python3
2
- """Protect Ultra managed task files and restate the active workflow boundary."""
2
+ """Protect managed task files and restate the live Ultra Context Envelope."""
3
3
 
4
4
  import json
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
- from context_spine import (
9
- ContextSpineError,
8
+ from context_envelope import (
9
+ ContextEnvelopeError,
10
10
  find_root,
11
- read_breadcrumb,
12
- render_breadcrumb,
11
+ read_context_envelope,
12
+ render_context_envelope,
13
13
  )
14
14
 
15
15
  TEAM_TASK_LEDGER = Path(".ultra/tasks/tasks.json")
@@ -86,7 +86,7 @@ def main() -> None:
86
86
  for target_root in managed_roots(data, start):
87
87
  try:
88
88
  target_authority = find_root(target_root)
89
- except ContextSpineError as exc:
89
+ except ContextEnvelopeError as exc:
90
90
  print(json.dumps(managed_file_denial(
91
91
  "Refusing a direct write to an MCP-managed Ultra task file while the "
92
92
  f"runtime authority is conflicted: {exc}. Resolve the "
@@ -100,13 +100,13 @@ def main() -> None:
100
100
  "Refusing a direct write to an MCP-managed Ultra task file. "
101
101
  ".ultra/tasks/tasks.json is the MCP-published team checkpoint and "
102
102
  ".ultra/.runtime/projections/tasks.json is the checkout-local DB view. "
103
- "Use the Ultra MCP task or task.ledger tools."
103
+ "Use ultra.record for task contracts/outcomes and ultra.sync for the team checkpoint."
104
104
  )))
105
105
  return
106
106
 
107
107
  try:
108
108
  root = find_root(start)
109
- except ContextSpineError as exc:
109
+ except ContextEnvelopeError as exc:
110
110
  print(
111
111
  f"[active_task_context] cannot resolve Ultra root: {exc}",
112
112
  file=sys.stderr,
@@ -119,7 +119,7 @@ def main() -> None:
119
119
  "Refusing a direct write to an MCP-managed Ultra task file while the "
120
120
  f"runtime authority is conflicted: {exc}. Resolve the "
121
121
  "state.db conflict with ultra-doctor before retrying the owning "
122
- "MCP operation."
122
+ "public Ultra operation."
123
123
  )))
124
124
  return
125
125
  root = None
@@ -131,18 +131,18 @@ def main() -> None:
131
131
  "Refusing a direct write to an MCP-managed Ultra task file. "
132
132
  ".ultra/tasks/tasks.json is the MCP-published team checkpoint and "
133
133
  ".ultra/.runtime/projections/tasks.json is the checkout-local DB view. "
134
- "Use the Ultra MCP task or task.ledger tools."
134
+ "Use ultra.record for task contracts/outcomes and ultra.sync for the team checkpoint."
135
135
  )))
136
136
  return
137
137
  try:
138
- breadcrumb = read_breadcrumb(root)
139
- except ContextSpineError as exc:
140
- print(f"[active_task_context] cannot inspect Context Spine: {exc}", file=sys.stderr)
141
- breadcrumb = None
142
- if breadcrumb and breadcrumb.get("workflow"):
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
143
  print(json.dumps({"hookSpecificOutput": {
144
144
  "hookEventName": "PreToolUse",
145
- "additionalContext": render_breadcrumb(root, breadcrumb),
145
+ "additionalContext": render_context_envelope(root, envelope),
146
146
  }}))
147
147
  return
148
148
  print(json.dumps({}))
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """Grok Build wire adapter for Ultra observational lifecycle hooks."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import copy
7
+ import json
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ HOOK_ROOT = Path(__file__).resolve().parent.parent
16
+ ALLOWED_FEATURES = {
17
+ "active_task_context.py",
18
+ "health_check.py",
19
+ "pre_stop_check.py",
20
+ "subagent_tracker.py",
21
+ "workflow_checkpoint.py",
22
+ }
23
+
24
+
25
+ def normalize_input(payload: dict[str, Any]) -> dict[str, Any]:
26
+ """Translate Grok's camelCase wire names into the shared hook contract."""
27
+ normalized = copy.deepcopy(payload)
28
+ aliases = {
29
+ "hookEventName": "hook_event_name",
30
+ "sessionId": "session_id",
31
+ "toolName": "tool_name",
32
+ "toolInput": "tool_input",
33
+ "toolResult": "tool_output",
34
+ "stopHookActive": "stop_hook_active",
35
+ "agentId": "agent_id",
36
+ "agentType": "agent_type",
37
+ "workspaceRoot": "workspace_root",
38
+ }
39
+ for source, target in aliases.items():
40
+ if source in normalized and target not in normalized:
41
+ normalized[target] = normalized[source]
42
+ return normalized
43
+
44
+
45
+ def _parse_output(stdout: str) -> dict[str, Any]:
46
+ text = stdout.strip()
47
+ if not text:
48
+ return {}
49
+ try:
50
+ value = json.loads(text)
51
+ except json.JSONDecodeError:
52
+ return {}
53
+ return value if isinstance(value, dict) else {}
54
+
55
+
56
+ def adapt_output(output: dict[str, Any], event: str, reason: str | None) -> dict[str, Any]:
57
+ """Return only output fields Grok actually consumes for this event."""
58
+ if event == "PreToolUse":
59
+ specific = output.get("hookSpecificOutput")
60
+ specific = specific if isinstance(specific, dict) else {}
61
+ if specific.get("permissionDecision") == "deny":
62
+ return {
63
+ "decision": "deny",
64
+ "reason": str(
65
+ specific.get("permissionDecisionReason")
66
+ or "Ultra refused a direct write to a managed authority file"
67
+ ),
68
+ }
69
+ return {"decision": "allow"}
70
+ # Ultra Stop is advisory. Grok's session-end Stop is observe-only and must
71
+ # never be confused with a genuine end_turn gate.
72
+ if event == "Stop" and reason != "end_turn":
73
+ return {}
74
+ return {}
75
+
76
+
77
+ def run_feature(feature: str, payload: dict[str, Any], feature_args: list[str]) -> dict[str, Any]:
78
+ if feature not in ALLOWED_FEATURES or Path(feature).name != feature:
79
+ return {}
80
+ script = HOOK_ROOT / feature
81
+ if not script.is_file():
82
+ return {}
83
+ normalized = normalize_input(payload)
84
+ env = os.environ.copy()
85
+ env["UBP_HOOK_RUNTIME"] = "grok"
86
+ plugin_data = env.get("GROK_PLUGIN_DATA") or env.get("CLAUDE_PLUGIN_DATA")
87
+ if plugin_data:
88
+ env["UBP_HOOK_DATA"] = plugin_data
89
+ cwd = normalized.get("cwd")
90
+ run_cwd = str(cwd) if isinstance(cwd, str) and Path(cwd).is_dir() else os.getcwd()
91
+ proc = subprocess.run(
92
+ [sys.executable, str(script), *feature_args],
93
+ input=json.dumps(normalized),
94
+ text=True,
95
+ capture_output=True,
96
+ cwd=run_cwd,
97
+ env=env,
98
+ timeout=30,
99
+ check=False,
100
+ )
101
+ if proc.stderr:
102
+ print(proc.stderr.rstrip(), file=sys.stderr)
103
+ if proc.returncode != 0:
104
+ return {}
105
+ return adapt_output(
106
+ _parse_output(proc.stdout),
107
+ str(payload.get("hookEventName") or ""),
108
+ str(payload.get("reason") or "") or None,
109
+ )
110
+
111
+
112
+ def main() -> int:
113
+ if len(sys.argv) < 2:
114
+ print(json.dumps({}))
115
+ return 0
116
+ feature = sys.argv[1]
117
+ try:
118
+ payload = json.loads(sys.stdin.read() or "{}")
119
+ if not isinstance(payload, dict):
120
+ payload = {}
121
+ except json.JSONDecodeError as exc:
122
+ print(f"[grok hook] invalid input: {exc}", file=sys.stderr)
123
+ payload = {}
124
+ try:
125
+ output = run_feature(feature, payload, sys.argv[2:])
126
+ except Exception as exc:
127
+ print(f"[grok hook] {feature} failed open: {exc}", file=sys.stderr)
128
+ output = {}
129
+ print(json.dumps(output, ensure_ascii=False))
130
+ return 0
131
+
132
+
133
+ if __name__ == "__main__":
134
+ raise SystemExit(main())
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Thin bridge to the single bundled JavaScript Context Spine reader."""
2
+ """Thin read-only bridge to the bundled Ultra Context Envelope reader."""
3
3
 
4
4
  import json
5
5
  import os
@@ -11,12 +11,12 @@ from pathlib import Path
11
11
  from runtime_paths import RuntimePathError, find_project_root
12
12
 
13
13
 
14
- class ContextSpineError(RuntimeError):
15
- """The canonical breadcrumb helper could not be executed or decoded."""
14
+ class ContextEnvelopeError(RuntimeError):
15
+ """The canonical Context Envelope helper could not be executed or decoded."""
16
16
 
17
17
 
18
- class Breadcrumb(dict):
19
- """Dictionary breadcrumb carrying its canonical rendered injection text."""
18
+ class ContextEnvelope(dict):
19
+ """Dictionary envelope carrying its canonical bounded injection text."""
20
20
 
21
21
  def __init__(self, value: dict, rendered: str):
22
22
  super().__init__(value)
@@ -27,27 +27,27 @@ def find_root(start: Path):
27
27
  try:
28
28
  return find_project_root(start)
29
29
  except RuntimePathError as exc:
30
- raise ContextSpineError(str(exc)) from exc
30
+ raise ContextEnvelopeError(str(exc)) from exc
31
31
 
32
32
 
33
33
  def find_root_for_hook(start: Path, hook_name: str):
34
34
  """Fail open at a hook boundary while preserving path-conflict evidence."""
35
35
  try:
36
36
  return find_root(start)
37
- except ContextSpineError as exc:
37
+ except ContextEnvelopeError as exc:
38
38
  print(f"[{hook_name}] cannot resolve Ultra root: {exc}", file=sys.stderr)
39
39
  return None
40
40
 
41
41
 
42
42
  def _helper_path() -> Path:
43
43
  hook_root = Path(__file__).resolve().parent
44
- installed = hook_root.parent / "runtime" / "breadcrumb.cjs"
44
+ installed = hook_root.parent / "runtime" / "hook-context.cjs"
45
45
  if installed.is_file():
46
46
  return installed
47
- source = hook_root.parent / "mcp-server" / "breadcrumb.cjs"
47
+ source = hook_root.parent / "mcp-server" / "hook-context.cjs"
48
48
  if source.is_file():
49
49
  return source
50
- raise ContextSpineError("bundled breadcrumb helper is missing")
50
+ raise ContextEnvelopeError("bundled Context Envelope helper is missing")
51
51
 
52
52
 
53
53
  def _node_binary() -> str:
@@ -57,10 +57,10 @@ def _node_binary() -> str:
57
57
  discovered = shutil.which("node")
58
58
  if discovered:
59
59
  return discovered
60
- raise ContextSpineError("node executable is unavailable")
60
+ raise ContextEnvelopeError("node executable is unavailable")
61
61
 
62
62
 
63
- def read_breadcrumb(root: Path):
63
+ def read_context_envelope(root: Path):
64
64
  try:
65
65
  result = subprocess.run(
66
66
  [_node_binary(), str(_helper_path()), str(root.resolve())],
@@ -70,27 +70,31 @@ def read_breadcrumb(root: Path):
70
70
  timeout=5,
71
71
  )
72
72
  except (OSError, subprocess.TimeoutExpired) as exc:
73
- raise ContextSpineError(str(exc)) from exc
73
+ raise ContextEnvelopeError(str(exc)) from exc
74
74
  if result.returncode != 0:
75
75
  detail = result.stderr.strip() or f"exit {result.returncode}"
76
- raise ContextSpineError(detail)
76
+ raise ContextEnvelopeError(detail)
77
77
  try:
78
78
  payload = json.loads(result.stdout)
79
79
  except json.JSONDecodeError as exc:
80
- raise ContextSpineError(f"invalid breadcrumb response: {exc}") from exc
80
+ raise ContextEnvelopeError(f"invalid Context Envelope response: {exc}") from exc
81
81
  if not isinstance(payload, dict):
82
- raise ContextSpineError("breadcrumb response must be an object")
83
- value = payload.get("breadcrumb")
82
+ raise ContextEnvelopeError("Context Envelope response must be an object")
83
+ value = payload.get("context")
84
84
  if value is None:
85
85
  return None
86
86
  rendered = payload.get("text")
87
87
  if not isinstance(value, dict) or not isinstance(rendered, str) or not rendered:
88
- raise ContextSpineError("breadcrumb response is missing canonical state or text")
89
- return Breadcrumb(value, rendered)
88
+ raise ContextEnvelopeError(
89
+ "Context Envelope response is missing canonical state or text"
90
+ )
91
+ return ContextEnvelope(value, rendered)
90
92
 
91
93
 
92
- def render_breadcrumb(_root: Path, breadcrumb: dict) -> str:
93
- rendered = getattr(breadcrumb, "rendered", None)
94
+ def render_context_envelope(_root: Path, context: dict) -> str:
95
+ rendered = getattr(context, "rendered", None)
94
96
  if not isinstance(rendered, str) or not rendered:
95
- raise ContextSpineError("breadcrumb was not produced by the canonical reader")
97
+ raise ContextEnvelopeError(
98
+ "Context Envelope was not produced by the canonical reader"
99
+ )
96
100
  return rendered
@@ -6,7 +6,11 @@ import sqlite3
6
6
  import sys
7
7
  from pathlib import Path
8
8
 
9
- from context_spine import ContextSpineError, find_root_for_hook, read_breadcrumb
9
+ from context_envelope import (
10
+ ContextEnvelopeError,
11
+ find_root_for_hook,
12
+ read_context_envelope,
13
+ )
10
14
  from runtime_paths import state_db_path
11
15
 
12
16
 
@@ -14,8 +18,9 @@ REQUIRED_TABLES = {
14
18
  "baselines",
15
19
  "tasks", "events", "sessions", "schema_version", "migration_history",
16
20
  "telemetry", "specs_refs", "circuit_breaker", "changes", "artifacts",
17
- "context_snapshots", "spec_learning_candidates", "trace_links", "incidents", "projection_jobs",
18
- "event_consumers", "workflow_runs", "workflow_steps",
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",
19
24
  }
20
25
 
21
26
 
@@ -28,22 +33,6 @@ def hook_input() -> dict:
28
33
  return {}
29
34
 
30
35
 
31
- def has_active_workflow(db_path: Path) -> bool:
32
- if not db_path.is_file():
33
- return False
34
- try:
35
- uri = f"file:{db_path}?mode=ro"
36
- with sqlite3.connect(uri, uri=True, timeout=1) as conn:
37
- row = conn.execute(
38
- """SELECT 1 FROM workflow_runs
39
- WHERE status IN ('active', 'blocked', 'ready')
40
- LIMIT 1"""
41
- ).fetchone()
42
- return row is not None
43
- except sqlite3.Error:
44
- return False
45
-
46
-
47
36
  def inspect(root: Path) -> dict:
48
37
  db_path = state_db_path(root)
49
38
  report = {"status": "healthy", "project": str(root), "checks": {}}
@@ -54,8 +43,8 @@ def inspect(root: Path) -> dict:
54
43
  return report
55
44
 
56
45
  try:
57
- breadcrumb = read_breadcrumb(root)
58
- except ContextSpineError as exc:
46
+ context = read_context_envelope(root)
47
+ except ContextEnvelopeError as exc:
59
48
  report["checks"]["authority"] = {"status": "fail", "reason": str(exc)}
60
49
  report["status"] = "degraded"
61
50
  return report
@@ -82,14 +71,18 @@ def inspect(root: Path) -> dict:
82
71
  report["status"] = "degraded"
83
72
  return report
84
73
 
74
+ envelope = context.get("envelope", {}) if context else {}
75
+ diagnostics = envelope.get("diagnostics", {})
85
76
  baseline_codes = [
86
- code for code in [
87
- *(breadcrumb.get("blockers", []) if breadcrumb else []),
88
- *(breadcrumb.get("warnings", []) if breadcrumb else []),
77
+ str(item.get("code"))
78
+ for item in [
79
+ *diagnostics.get("needs_attention", []),
80
+ *diagnostics.get("warnings", []),
81
+ *diagnostics.get("hard_conflicts", []),
89
82
  ]
90
- if str(code).startswith("BASELINE_")
83
+ if str(item.get("code", "")).startswith("BASELINE_")
91
84
  ]
92
- baseline = breadcrumb.get("baseline") if breadcrumb else None
85
+ baseline = envelope.get("baseline")
93
86
  baseline_is_ready = isinstance(baseline, dict) and baseline.get("status") == "ready"
94
87
  baseline_status = (
95
88
  "fail" if baseline_codes and baseline_is_ready
@@ -188,9 +181,6 @@ def main() -> None:
188
181
  if root is None:
189
182
  print(json.dumps({}))
190
183
  return
191
- if not has_active_workflow(state_db_path(root)):
192
- print(json.dumps({}))
193
- return
194
184
  report = inspect(root)
195
185
  if report["status"] != "healthy":
196
186
  print("[Ultra health] " + json.dumps(report, ensure_ascii=False, sort_keys=True), file=sys.stderr)
@@ -5,7 +5,11 @@ import json
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
- from context_spine import ContextSpineError, find_root_for_hook, read_breadcrumb
8
+ from context_envelope import (
9
+ ContextEnvelopeError,
10
+ find_root_for_hook,
11
+ read_context_envelope,
12
+ )
9
13
 
10
14
 
11
15
  def allow_stop() -> None:
@@ -32,21 +36,27 @@ def main() -> None:
32
36
  allow_stop()
33
37
  return
34
38
  try:
35
- breadcrumb = read_breadcrumb(root)
36
- except ContextSpineError as exc:
37
- print(f"[pre_stop_check] cannot inspect Context Spine: {exc}", file=sys.stderr)
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)
38
42
  allow_stop()
39
43
  return
40
- if breadcrumb and breadcrumb.get("workflow"):
41
- allowed = ", ".join(breadcrumb.get("allowed_transitions") or []) or "none"
42
- required = breadcrumb.get("required_transition") or "none"
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"
43
53
  print(
44
54
  "[Ultra stop advisory] Active Ultra change remains open: "
45
- f"change={breadcrumb['change_id']}, "
46
- f"task={breadcrumb.get('task_id') or 'none'}, "
47
- f"gate={breadcrumb.get('gate') or 'alignment'}, "
48
- f"allowed_transitions={allowed}, required_transition={required}. "
49
- "Stop is allowed; resume or explicitly archive the change later.",
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.",
50
60
  file=sys.stderr,
51
61
  )
52
62
  allow_stop()
@@ -13,7 +13,7 @@ import subprocess
13
13
  import sys
14
14
  from pathlib import Path
15
15
 
16
- from context_spine import find_root_for_hook
16
+ from context_envelope import find_root_for_hook
17
17
 
18
18
 
19
19
  def helper_path() -> Path:
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Write a recovery projection of the canonical DB breadcrumb before compaction."""
2
+ """Write an advisory recovery copy of the live Context Envelope before compaction."""
3
3
 
4
4
  import json
5
5
  import os
@@ -8,11 +8,11 @@ import tempfile
8
8
  from datetime import datetime, timezone
9
9
  from pathlib import Path
10
10
 
11
- from context_spine import (
12
- ContextSpineError,
11
+ from context_envelope import (
12
+ ContextEnvelopeError,
13
13
  find_root_for_hook,
14
- read_breadcrumb,
15
- render_breadcrumb,
14
+ read_context_envelope,
15
+ render_context_envelope,
16
16
  )
17
17
  from runtime_paths import RuntimePathError, validate_project_layout
18
18
 
@@ -32,12 +32,12 @@ def main() -> None:
32
32
  print(json.dumps({}))
33
33
  return
34
34
  try:
35
- breadcrumb = read_breadcrumb(root)
36
- except ContextSpineError as exc:
37
- print(f"[workflow_checkpoint] cannot inspect Context Spine: {exc}", file=sys.stderr)
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
38
  print(json.dumps({}))
39
39
  return
40
- if not breadcrumb or not breadcrumb.get("workflow"):
40
+ if not envelope:
41
41
  print(json.dumps({}))
42
42
  return
43
43
 
@@ -53,11 +53,12 @@ def main() -> None:
53
53
  runtime_dir = root / ".ultra" / ".runtime"
54
54
  runtime_dir.mkdir(parents=True, exist_ok=True)
55
55
  checkpoint = {
56
- "schema": 2,
56
+ "schema": 3,
57
57
  "captured_at": datetime.now(timezone.utc).isoformat(),
58
58
  "session_id": data.get("session_id", ""),
59
- "breadcrumb": dict(breadcrumb),
60
- "rendered": render_breadcrumb(root, breadcrumb),
59
+ "context_digest": envelope.get("digest"),
60
+ "context": dict(envelope),
61
+ "rendered": render_context_envelope(root, envelope),
61
62
  }
62
63
  fd, temp_name = tempfile.mkstemp(prefix="checkpoint.", suffix=".json", dir=runtime_dir)
63
64
  try:
@@ -68,7 +69,7 @@ def main() -> None:
68
69
  finally:
69
70
  if os.path.exists(temp_name):
70
71
  os.unlink(temp_name)
71
- print(json.dumps({"systemMessage": "Ultra workflow checkpoint saved."}))
72
+ print(json.dumps({"systemMessage": "Ultra Context Envelope checkpoint saved."}))
72
73
 
73
74
 
74
75
  if __name__ == "__main__":
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env python3
2
- """Inject one compact Ultra workflow-memory position without provider payloads."""
2
+ """Inject one compact, read-only Ultra Context Envelope."""
3
3
 
4
4
  import json
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
- from context_spine import (
9
- ContextSpineError,
8
+ from context_envelope import (
9
+ ContextEnvelopeError,
10
10
  find_root_for_hook,
11
- read_breadcrumb,
12
- render_breadcrumb,
11
+ read_context_envelope,
12
+ render_context_envelope,
13
13
  )
14
14
 
15
15
 
@@ -32,15 +32,14 @@ def main() -> None:
32
32
  print(json.dumps({}))
33
33
  return
34
34
  try:
35
- breadcrumb = read_breadcrumb(root)
36
- except ContextSpineError as exc:
37
- print(f"[workflow_context] cannot inspect Context Spine: {exc}", file=sys.stderr)
38
- breadcrumb = None
39
- if breadcrumb and breadcrumb.get("workflow"):
40
- context = render_breadcrumb(root, breadcrumb)
41
- else:
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:
42
40
  print(json.dumps({}))
43
41
  return
42
+ context = render_context_envelope(root, envelope)
44
43
  print(json.dumps({"hookSpecificOutput": {
45
44
  "hookEventName": "SessionStart",
46
45
  "additionalContext": context,