ultra-builder-pro-cli 0.19.1 → 0.21.1

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 (103) hide show
  1. package/.ultra-template/docs/research/README.md +26 -5
  2. package/.ultra-template/specs/discovery.md +1 -1
  3. package/.ultra-template/tasks/tasks.json +1 -1
  4. package/CHANGELOG.md +90 -1
  5. package/README.md +62 -29
  6. package/adapters/_shared/codex-assets.cjs +69 -11
  7. package/adapters/_shared/interaction-contract.cjs +60 -5
  8. package/adapters/_shared/runtime-assets.cjs +41 -1
  9. package/adapters/claude.js +7 -5
  10. package/adapters/codex.js +1 -1
  11. package/adapters/kimi.js +2 -8
  12. package/adapters/opencode.js +30 -28
  13. package/bin/orchestrator.js +64 -24
  14. package/commands/ultra-research.md +1 -1
  15. package/docs/AGENT-CONTEXT.md +79 -45
  16. package/docs/ARCHITECTURE.md +75 -38
  17. package/docs/ARTIFACT-AUTHORITY.md +234 -0
  18. package/docs/DECISIONS.md +20 -10
  19. package/docs/LEGACY-CLI-CROSSWALK.md +15 -11
  20. package/docs/PLUGIN-ISOLATION-CONTRACT.md +4 -3
  21. package/docs/ROADMAP.md +21 -6
  22. package/docs/RUNTIME-COMPAT-MATRIX.md +30 -13
  23. package/docs/STATE-DB-ACCESS-POLICY.md +11 -9
  24. package/docs/WORKFLOW-LIFECYCLE.md +105 -54
  25. package/hooks/README.md +6 -5
  26. package/hooks/active_task_context.py +97 -12
  27. package/hooks/context_spine.py +16 -4
  28. package/hooks/health_check.py +5 -4
  29. package/hooks/pre_stop_check.py +5 -2
  30. package/hooks/runtime_paths.py +396 -0
  31. package/hooks/subagent_tracker.py +5 -2
  32. package/hooks/workflow_checkpoint.py +16 -3
  33. package/hooks/workflow_context.py +11 -3
  34. package/hooks/workflow_resume.py +7 -4
  35. package/mcp-server/breadcrumb.cjs +20 -2
  36. package/mcp-server/lib/archive-journal.cjs +1026 -111
  37. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  38. package/mcp-server/lib/artifact-registry.cjs +1126 -0
  39. package/mcp-server/lib/baseline-workflow.cjs +9 -4
  40. package/mcp-server/lib/change-packet.cjs +818 -0
  41. package/mcp-server/lib/change-workflow.cjs +932 -176
  42. package/mcp-server/lib/context-paths.cjs +111 -0
  43. package/mcp-server/lib/context-spine.cjs +511 -45
  44. package/mcp-server/lib/decision-dialogue.cjs +427 -12
  45. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  46. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  47. package/mcp-server/lib/doctor.cjs +375 -12
  48. package/mcp-server/lib/git-bootstrap.cjs +459 -47
  49. package/mcp-server/lib/init-project.cjs +190 -95
  50. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  51. package/mcp-server/lib/plan-store.cjs +700 -5
  52. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  53. package/mcp-server/lib/projector.cjs +174 -40
  54. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  55. package/mcp-server/lib/safe-project-file.cjs +385 -0
  56. package/mcp-server/lib/spec-learning.cjs +28 -12
  57. package/mcp-server/lib/state-db.cjs +397 -13
  58. package/mcp-server/lib/state-ops.cjs +248 -49
  59. package/mcp-server/lib/telemetry.cjs +12 -3
  60. package/mcp-server/lib/workflow-state.cjs +614 -62
  61. package/mcp-server/server.cjs +282 -33
  62. package/orchestrator/daemon.cjs +2 -10
  63. package/orchestrator/parallel-orchestrator.cjs +314 -50
  64. package/orchestrator/planner/plan-builder.cjs +1 -1
  65. package/orchestrator/recovery.cjs +125 -7
  66. package/orchestrator/session-close-journal-worker.cjs +5 -0
  67. package/orchestrator/session-close-journal.cjs +1014 -0
  68. package/orchestrator/session-runner.cjs +679 -69
  69. package/orchestrator/worktree-manager.cjs +186 -35
  70. package/package.json +2 -3
  71. package/skills/cc-collab/SKILL.md +4 -1
  72. package/skills/codex-collab/SKILL.md +5 -2
  73. package/skills/ultra-change/SKILL.md +38 -13
  74. package/skills/ultra-deliver/SKILL.md +23 -11
  75. package/skills/ultra-dev/SKILL.md +33 -6
  76. package/skills/ultra-doctor/SKILL.md +6 -2
  77. package/skills/ultra-init/SKILL.md +4 -2
  78. package/skills/ultra-plan/SKILL.md +68 -18
  79. package/skills/ultra-research/SKILL.md +45 -32
  80. package/skills/ultra-review/SKILL.md +9 -5
  81. package/skills/ultra-status/SKILL.md +9 -4
  82. package/skills/ultra-test/SKILL.md +15 -7
  83. package/skills/ultra-think/SKILL.md +6 -0
  84. package/skills/ultra-think/references/decision-dialogue.md +61 -14
  85. package/skills/ultra-verify/SKILL.md +4 -1
  86. package/spec/cli-protocol.md +19 -7
  87. package/spec/mcp-tools.yaml +531 -46
  88. package/spec/schemas/change-delta.v1.schema.json +153 -0
  89. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  90. package/spec/schemas/mcp-tools.schema.json +1 -1
  91. package/spec/schemas/state-db.sql +57 -17
  92. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  93. package/ultra-tools/cli.cjs +3 -2
  94. package/ultra-tools/commands/db.cjs +76 -25
  95. package/ultra-tools/commands/migrate.cjs +995 -80
  96. package/ultra-tools/commands/session.cjs +34 -8
  97. package/ultra-tools/commands/status.cjs +34 -7
  98. package/ultra-tools/commands/system.cjs +129 -22
  99. package/commands/learn.md +0 -10
  100. package/orchestrator/skill-miner.cjs +0 -133
  101. package/output-styles/coding-vibes.md +0 -67
  102. package/output-styles/structural-thinking.md +0 -61
  103. package/skills/learn/SKILL.md +0 -58
@@ -0,0 +1,153 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://ultra-builder-pro.dev/schemas/change-delta.v1.schema.json",
4
+ "title": "Ultra Change delta v1",
5
+ "type": "object",
6
+ "required": [
7
+ "$schema",
8
+ "change_id",
9
+ "baseline_anchor",
10
+ "decisions",
11
+ "non_goals",
12
+ "acceptance",
13
+ "documentation_impact",
14
+ "unknowns",
15
+ "mutations"
16
+ ],
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "$schema": { "const": "ultra-change-delta-v1" },
20
+ "change_id": { "type": "string", "minLength": 1 },
21
+ "baseline_anchor": {
22
+ "type": "object",
23
+ "required": ["baseline_id", "repository_revision", "specs"],
24
+ "additionalProperties": false,
25
+ "properties": {
26
+ "baseline_id": { "type": "string", "minLength": 1 },
27
+ "repository_revision": { "type": ["string", "null"] },
28
+ "specs": {
29
+ "type": "array",
30
+ "uniqueItems": true,
31
+ "items": {
32
+ "type": "object",
33
+ "required": ["path", "digest"],
34
+ "additionalProperties": false,
35
+ "properties": {
36
+ "path": { "type": "string", "minLength": 1 },
37
+ "digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ },
43
+ "decisions": {
44
+ "type": "array",
45
+ "items": {
46
+ "type": "object",
47
+ "required": ["id", "summary"],
48
+ "additionalProperties": false,
49
+ "properties": {
50
+ "id": { "type": "string", "pattern": "^[a-zA-Z0-9_\\-]+$" },
51
+ "summary": { "type": "string", "minLength": 1 },
52
+ "rationale": { "type": "string", "minLength": 1 }
53
+ }
54
+ }
55
+ },
56
+ "non_goals": {
57
+ "type": "array",
58
+ "uniqueItems": true,
59
+ "items": { "type": "string", "minLength": 1 }
60
+ },
61
+ "acceptance": {
62
+ "type": "array",
63
+ "minItems": 1,
64
+ "items": {
65
+ "type": "object",
66
+ "required": ["id", "criterion", "verification"],
67
+ "additionalProperties": false,
68
+ "properties": {
69
+ "id": { "type": "string", "pattern": "^[a-zA-Z0-9_\\-]+$" },
70
+ "criterion": { "type": "string", "minLength": 1 },
71
+ "verification": { "type": "string", "minLength": 1 }
72
+ }
73
+ }
74
+ },
75
+ "documentation_impact": {
76
+ "type": "object",
77
+ "required": ["status", "files", "rationale"],
78
+ "additionalProperties": false,
79
+ "properties": {
80
+ "status": { "enum": ["none", "required"] },
81
+ "files": {
82
+ "type": "array",
83
+ "uniqueItems": true,
84
+ "items": { "type": "string", "minLength": 1 }
85
+ },
86
+ "rationale": { "type": "string", "minLength": 3 }
87
+ }
88
+ },
89
+ "unknowns": {
90
+ "type": "array",
91
+ "items": {
92
+ "type": "object",
93
+ "required": ["id", "summary", "blocking"],
94
+ "additionalProperties": false,
95
+ "properties": {
96
+ "id": { "type": "string", "pattern": "^[a-zA-Z0-9_\\-]+$" },
97
+ "summary": { "type": "string", "minLength": 1 },
98
+ "blocking": { "type": "boolean" }
99
+ }
100
+ }
101
+ },
102
+ "no_semantic_change_reason": { "type": "string", "minLength": 3 },
103
+ "mutations": {
104
+ "type": "array",
105
+ "items": {
106
+ "type": "object",
107
+ "required": [
108
+ "id",
109
+ "action",
110
+ "target_path",
111
+ "overlay_path",
112
+ "before_digest",
113
+ "after_digest",
114
+ "acceptance_refs",
115
+ "documentation_refs"
116
+ ],
117
+ "additionalProperties": false,
118
+ "properties": {
119
+ "id": { "type": "string", "pattern": "^[a-zA-Z0-9_\\-]+$" },
120
+ "action": { "enum": ["add", "update"] },
121
+ "target_path": { "type": "string", "minLength": 1 },
122
+ "overlay_path": { "type": "string", "minLength": 1 },
123
+ "before_digest": {
124
+ "type": ["string", "null"],
125
+ "pattern": "^[0-9a-f]{64}$"
126
+ },
127
+ "after_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
128
+ "acceptance_refs": {
129
+ "type": "array",
130
+ "minItems": 1,
131
+ "uniqueItems": true,
132
+ "items": { "type": "string", "minLength": 1 }
133
+ },
134
+ "documentation_refs": {
135
+ "type": "array",
136
+ "uniqueItems": true,
137
+ "items": { "type": "string", "minLength": 1 }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ },
143
+ "allOf": [
144
+ {
145
+ "if": {
146
+ "properties": {
147
+ "mutations": { "maxItems": 0 }
148
+ }
149
+ },
150
+ "then": { "required": ["no_semantic_change_reason"] }
151
+ }
152
+ ]
153
+ }
@@ -0,0 +1,106 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://ultra-builder-pro.dev/schemas/documentation-reconciliation.v1.schema.json",
4
+ "title": "Ultra documentation reconciliation v1",
5
+ "type": "object",
6
+ "required": [
7
+ "$schema",
8
+ "change_id",
9
+ "delta_artifact_id",
10
+ "delta_digest",
11
+ "documents"
12
+ ],
13
+ "additionalProperties": false,
14
+ "properties": {
15
+ "$schema": { "const": "ultra-documentation-reconciliation-v1" },
16
+ "change_id": { "type": "string", "minLength": 1 },
17
+ "delta_artifact_id": { "type": "string", "minLength": 1 },
18
+ "delta_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
19
+ "no_change_reason": { "type": "string", "minLength": 3 },
20
+ "documents": {
21
+ "type": "array",
22
+ "items": {
23
+ "type": "object",
24
+ "required": [
25
+ "path",
26
+ "action",
27
+ "overlay_path",
28
+ "before_digest",
29
+ "after_digest",
30
+ "delta_refs",
31
+ "acceptance_refs",
32
+ "verification",
33
+ "consumers"
34
+ ],
35
+ "additionalProperties": false,
36
+ "properties": {
37
+ "path": { "type": "string", "minLength": 1 },
38
+ "action": { "enum": ["add", "update"] },
39
+ "overlay_path": { "type": "string", "minLength": 1 },
40
+ "before_digest": {
41
+ "type": ["string", "null"],
42
+ "pattern": "^[0-9a-f]{64}$"
43
+ },
44
+ "after_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
45
+ "delta_refs": {
46
+ "type": "array",
47
+ "minItems": 1,
48
+ "uniqueItems": true,
49
+ "items": { "type": "string", "minLength": 1 }
50
+ },
51
+ "acceptance_refs": {
52
+ "type": "array",
53
+ "minItems": 1,
54
+ "uniqueItems": true,
55
+ "items": { "type": "string", "minLength": 1 }
56
+ },
57
+ "verification": {
58
+ "type": "array",
59
+ "minItems": 1,
60
+ "items": {
61
+ "type": "object",
62
+ "required": ["command", "status", "evidence_refs"],
63
+ "additionalProperties": false,
64
+ "properties": {
65
+ "command": { "type": "string", "minLength": 1 },
66
+ "status": { "const": "pass" },
67
+ "evidence_refs": {
68
+ "type": "array",
69
+ "minItems": 1,
70
+ "uniqueItems": true,
71
+ "items": { "type": "string", "minLength": 1 }
72
+ }
73
+ }
74
+ }
75
+ },
76
+ "consumers": {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "object",
80
+ "required": ["type", "id", "relation"],
81
+ "additionalProperties": false,
82
+ "properties": {
83
+ "type": {
84
+ "enum": ["artifact", "project", "baseline", "change", "task", "workflow", "external"]
85
+ },
86
+ "id": { "type": "string", "minLength": 1 },
87
+ "relation": { "type": "string", "minLength": 1 }
88
+ }
89
+ }
90
+ },
91
+ "no_consumer_reason": { "type": "string", "minLength": 3 }
92
+ }
93
+ }
94
+ }
95
+ },
96
+ "allOf": [
97
+ {
98
+ "if": {
99
+ "properties": {
100
+ "documents": { "maxItems": 0 }
101
+ }
102
+ },
103
+ "then": { "required": ["no_change_reason"] }
104
+ }
105
+ ]
106
+ }
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "family": {
62
62
  "type": "string",
63
- "enum": ["task", "memory", "review", "impact", "skill", "session", "ask", "plan", "baseline", "change", "decision", "workflow", "system"]
63
+ "enum": ["task", "memory", "review", "impact", "skill", "session", "ask", "plan", "baseline", "change", "decision", "workflow", "artifact", "system"]
64
64
  },
65
65
  "description": { "type": "string", "minLength": 10 },
66
66
  "phase": {
@@ -1,5 +1,5 @@
1
1
  -- Ultra Builder Pro — authoritative state schema
2
- -- This file builds .ultra/state.db, the source of truth for all
2
+ -- This file builds .ultra/.runtime/state.db, the source of truth for all
3
3
  -- baseline / change / task / session / event / telemetry data. tasks.json and context md status
4
4
  -- headers are projections.
5
5
  --
@@ -15,7 +15,7 @@
15
15
  -- ──────────────────────────── project baseline ─────────────────────────────
16
16
  -- The baseline is the approved, repository-scoped snapshot that every change
17
17
  -- is measured against. It stores digests and evidence references, never source,
18
- -- prompt, transcript, memory, or code-graph payloads.
18
+ -- prompt, transcript, general memory-provider, or code-graph payloads.
19
19
  CREATE TABLE IF NOT EXISTS baselines (
20
20
  id TEXT PRIMARY KEY,
21
21
  project_name TEXT NOT NULL,
@@ -159,6 +159,8 @@ CREATE TABLE IF NOT EXISTS sessions (
159
159
 
160
160
  CREATE INDEX IF NOT EXISTS sessions_active ON sessions(status, task_id);
161
161
  CREATE INDEX IF NOT EXISTS sessions_lease ON sessions(lease_expires_at) WHERE status = 'running';
162
+ CREATE UNIQUE INDEX IF NOT EXISTS sessions_one_active_task
163
+ ON sessions(task_id) WHERE status = 'running';
162
164
 
163
165
  -- ──────────────────────────── schema_version ──────────────────────────────
164
166
  -- Cross-version misread guard (D30, R27). Single row per applied version.
@@ -225,20 +227,51 @@ CREATE INDEX IF NOT EXISTS circuit_breaker_tripped ON circuit_breaker(tripped_at
225
227
 
226
228
  -- ──────────────────────────── change artifacts ────────────────────────────
227
229
  CREATE TABLE IF NOT EXISTS artifacts (
228
- id TEXT PRIMARY KEY,
229
- change_id TEXT NOT NULL REFERENCES changes(id) ON DELETE CASCADE,
230
- task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
231
- kind TEXT NOT NULL,
232
- path TEXT NOT NULL,
233
- content_hash TEXT,
234
- metadata_json TEXT,
235
- status TEXT NOT NULL DEFAULT 'current' CHECK (status IN ('current', 'archived')),
236
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
237
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
238
- UNIQUE(change_id, kind, path)
230
+ id TEXT PRIMARY KEY,
231
+ owner_type TEXT NOT NULL
232
+ CHECK (owner_type IN ('project', 'baseline', 'change', 'task', 'workflow')),
233
+ owner_id TEXT NOT NULL,
234
+ change_id TEXT REFERENCES changes(id) ON DELETE SET NULL,
235
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
236
+ kind TEXT NOT NULL,
237
+ path TEXT NOT NULL,
238
+ digest TEXT,
239
+ content_hash TEXT,
240
+ before_digest TEXT,
241
+ after_digest TEXT,
242
+ provenance_json TEXT NOT NULL DEFAULT '{}',
243
+ metadata_json TEXT,
244
+ managed INTEGER NOT NULL DEFAULT 0 CHECK (managed IN (0, 1)),
245
+ status TEXT NOT NULL DEFAULT 'current'
246
+ CHECK (status IN ('current', 'stale', 'terminal', 'archived')),
247
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
248
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
249
+ UNIQUE(owner_type, owner_id, kind, path)
239
250
  );
240
251
 
241
252
  CREATE INDEX IF NOT EXISTS artifacts_change ON artifacts(change_id, kind);
253
+ CREATE INDEX IF NOT EXISTS artifacts_owner ON artifacts(owner_type, owner_id, status);
254
+ CREATE INDEX IF NOT EXISTS artifacts_path ON artifacts(path, status);
255
+ CREATE UNIQUE INDEX IF NOT EXISTS artifacts_one_active_path
256
+ ON artifacts(path) WHERE status <> 'archived';
257
+
258
+ CREATE TABLE IF NOT EXISTS artifact_edges (
259
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
260
+ source_type TEXT NOT NULL
261
+ CHECK (source_type IN ('artifact', 'project', 'baseline', 'change', 'task', 'workflow', 'external')),
262
+ source_id TEXT NOT NULL,
263
+ target_type TEXT NOT NULL
264
+ CHECK (target_type IN ('artifact', 'project', 'baseline', 'change', 'task', 'workflow', 'external')),
265
+ target_id TEXT NOT NULL,
266
+ relation TEXT NOT NULL,
267
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
268
+ UNIQUE(source_type, source_id, target_type, target_id, relation)
269
+ );
270
+
271
+ CREATE INDEX IF NOT EXISTS artifact_edges_source
272
+ ON artifact_edges(source_type, source_id);
273
+ CREATE INDEX IF NOT EXISTS artifact_edges_target
274
+ ON artifact_edges(target_type, target_id);
242
275
 
243
276
  CREATE TABLE IF NOT EXISTS context_snapshots (
244
277
  id TEXT PRIMARY KEY,
@@ -265,6 +298,8 @@ CREATE TABLE IF NOT EXISTS context_snapshots (
265
298
  );
266
299
 
267
300
  CREATE INDEX IF NOT EXISTS context_snapshots_change ON context_snapshots(change_id, created_at);
301
+ CREATE INDEX IF NOT EXISTS context_snapshots_binding
302
+ ON context_snapshots(change_id, task_id, role, gate, created_at);
268
303
 
269
304
  CREATE TABLE IF NOT EXISTS spec_learning_candidates (
270
305
  id TEXT PRIMARY KEY,
@@ -407,16 +442,16 @@ CREATE INDEX IF NOT EXISTS workflow_steps_status
407
442
  ON workflow_steps(run_id, status, position);
408
443
 
409
444
  -- ──────────────────────────── decision dialogue ─────────────────────────
410
- -- The host owns reasoning and normalized decision content. MCP owns one-at-a-
411
- -- time presentation, durable authority, checkpoints, and recovery. Raw prompts
412
- -- and transcripts never belong in these tables.
445
+ -- The host owns reasoning, presentation, and normalized decision content. MCP
446
+ -- owns the one-open-item invariant, pending recovery state, durable authority,
447
+ -- checkpoints, and recovery. Raw prompts and transcripts never belong here.
413
448
  CREATE TABLE IF NOT EXISTS decision_threads (
414
449
  id TEXT PRIMARY KEY,
415
450
  purpose TEXT NOT NULL,
416
451
  mode TEXT NOT NULL DEFAULT 'guided'
417
452
  CHECK (mode IN ('guided', 'fast', 'autonomous', 'diagnostic')),
418
453
  status TEXT NOT NULL DEFAULT 'active'
419
- CHECK (status IN ('active', 'checkpoint_ready', 'confirmed', 'cancelled')),
454
+ CHECK (status IN ('active', 'checkpoint_ready', 'completed', 'confirmed', 'cancelled')),
420
455
  baseline_id TEXT REFERENCES baselines(id) ON DELETE SET NULL,
421
456
  change_id TEXT REFERENCES changes(id) ON DELETE CASCADE,
422
457
  workflow_run_id TEXT REFERENCES workflow_runs(id) ON DELETE CASCADE,
@@ -424,6 +459,7 @@ CREATE TABLE IF NOT EXISTS decision_threads (
424
459
  checkpoint_json TEXT NOT NULL DEFAULT '{}',
425
460
  started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
426
461
  updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
462
+ completed_at TEXT,
427
463
  confirmed_at TEXT,
428
464
  CHECK (baseline_id IS NOT NULL OR change_id IS NOT NULL OR workflow_run_id IS NOT NULL)
429
465
  );
@@ -492,3 +528,7 @@ INSERT OR IGNORE INTO schema_version (version, description)
492
528
  VALUES ('17.0', 'Explicit unborn Git authority and owner-authorized baseline checkpoints');
493
529
  INSERT OR IGNORE INTO schema_version (version, description)
494
530
  VALUES ('18.0', 'Adaptive capability transitions, independent initialization, and recoverable workflow migration');
531
+ INSERT OR IGNORE INTO schema_version (version, description)
532
+ VALUES ('19.0', 'Non-ceremonial decision completion and host-neutral intent persistence');
533
+ INSERT OR IGNORE INTO schema_version (version, description)
534
+ VALUES ('20.0', 'Typed artifact registry, normalized dependency edges, and orphan diagnostics');
@@ -9,7 +9,7 @@
9
9
  "properties": {
10
10
  "schema_version": { "const": "4.5" },
11
11
  "generated_at": { "type": "string", "format": "date-time" },
12
- "source": { "const": ".ultra/state.db", "description": "authoritative source (D32)" },
12
+ "source": { "const": ".ultra/.runtime/state.db", "description": "authoritative source (D32)" },
13
13
  "tasks": {
14
14
  "type": "array",
15
15
  "items": { "$ref": "#/$defs/task" }
@@ -17,6 +17,7 @@
17
17
 
18
18
  const fs = require('node:fs');
19
19
  const path = require('node:path');
20
+ const runtimePaths = require('../mcp-server/lib/runtime-paths.cjs');
20
21
 
21
22
  const VERSION = (() => {
22
23
  try {
@@ -38,7 +39,7 @@ SUBCOMMANDS:
38
39
  system doctor [--repair] | restore | rebaseline
39
40
  status [--cost] [--since <duration>] [--json]
40
41
  db init | checkpoint | vacuum | integrity | backup (Phase 2)
41
- migrate --from=<4.4|4.5> --to=<4.5|18.0> [--dry|--rollback]
42
+ migrate --from=<4.4|4.5> --to=<4.5|20.0> [--dry|--rollback]
42
43
  legacy-memory inspect | archive | prune --confirm DELETE_ULTRA_LEGACY_MEMORY
43
44
 
44
45
  --help / -h show this message
@@ -75,7 +76,7 @@ const TELEMETRY_SUBCOMMANDS = new Set(['session']);
75
76
 
76
77
  function emitCliTelemetry(sub, rest) {
77
78
  try {
78
- const dbPath = path.resolve('.ultra', 'state.db');
79
+ const dbPath = runtimePaths.locateStateDb(process.cwd());
79
80
  if (!fs.existsSync(dbPath)) return;
80
81
  const Database = require('better-sqlite3');
81
82
  const db = new Database(dbPath);
@@ -2,11 +2,14 @@
2
2
 
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
+ const { randomUUID } = require('node:crypto');
6
+ const Database = require('better-sqlite3');
5
7
 
6
8
  const { initStateDb, openStateDb, closeStateDb } = require('../../mcp-server/lib/state-db.cjs');
9
+ const runtimePaths = require('../../mcp-server/lib/runtime-paths.cjs');
7
10
 
8
- const DEFAULT_DB_PATH = path.join('.ultra', 'state.db');
9
- const DEFAULT_BACKUP_DIR = path.join('.ultra', 'backups');
11
+ const DEFAULT_DB_PATH = runtimePaths.STATE_DB_RELATIVE_PATH;
12
+ const DEFAULT_BACKUP_DIR = path.join(runtimePaths.RUNTIME_RELATIVE_DIR, 'backups');
10
13
 
11
14
  function parseFlags(args) {
12
15
  const flags = { _: [] };
@@ -50,9 +53,19 @@ function dispatch(args) {
50
53
  }
51
54
  }
52
55
 
56
+ function resolveAuthorityPath(flags, { migrateLegacy = true } = {}) {
57
+ const env = flags.path
58
+ ? { UBP_DB_PATH: path.resolve(flags.path) }
59
+ : process.env;
60
+ return runtimePaths.resolveConfiguredStateDb(process.cwd(), {
61
+ env,
62
+ migrateLegacy,
63
+ }).stateDbPath;
64
+ }
65
+
53
66
  function cmdInit(flags) {
54
- const dbPath = path.resolve(flags.path || DEFAULT_DB_PATH);
55
67
  try {
68
+ const dbPath = resolveAuthorityPath(flags, { migrateLegacy: true });
56
69
  const { db, schema_version, created, tables, path: actualPath } = initStateDb(dbPath);
57
70
  closeStateDb(db);
58
71
  emit({
@@ -80,13 +93,13 @@ function cmdInit(flags) {
80
93
  }
81
94
 
82
95
  function withOpenDb(flags, fn, errorCode) {
83
- const dbPath = path.resolve(flags.path || DEFAULT_DB_PATH);
84
- if (!fs.existsSync(dbPath)) {
85
- emit({ ok: false, error: { code: 'DB_NOT_FOUND', message: `state.db missing at ${dbPath}; run 'db init' first` } });
86
- return 2;
87
- }
88
96
  let db;
89
97
  try {
98
+ const dbPath = resolveAuthorityPath(flags, { migrateLegacy: true });
99
+ if (!fs.existsSync(dbPath)) {
100
+ emit({ ok: false, error: { code: 'DB_NOT_FOUND', message: `state.db missing at ${dbPath}; run 'db init' first` } });
101
+ return 2;
102
+ }
90
103
  db = openStateDb(dbPath);
91
104
  const data = fn(db, dbPath);
92
105
  emit({ ok: true, data: { path: dbPath, ...data } });
@@ -131,24 +144,54 @@ function defaultBackupPath() {
131
144
  return path.join(DEFAULT_BACKUP_DIR, `state-db-${ts}.db`);
132
145
  }
133
146
 
134
- function cmdBackup(flags) {
135
- const dbPath = path.resolve(flags.path || DEFAULT_DB_PATH);
136
- const targetPath = path.resolve(flags.to || defaultBackupPath());
137
- if (!fs.existsSync(dbPath)) {
138
- emit({ ok: false, error: { code: 'DB_NOT_FOUND', message: `state.db missing at ${dbPath}` } });
139
- return 2;
147
+ function quoteSqlString(value) {
148
+ return `'${String(value).replaceAll("'", "''")}'`;
149
+ }
150
+
151
+ function createOnlineBackup(db, targetPath, {
152
+ beforeSnapshot = null,
153
+ } = {}) {
154
+ const stage = path.join(
155
+ path.dirname(targetPath),
156
+ `.${path.basename(targetPath)}.snapshot-${process.pid}-${randomUUID()}`,
157
+ );
158
+ try {
159
+ if (beforeSnapshot) beforeSnapshot();
160
+ // VACUUM INTO is SQLite's synchronous, transactionally consistent
161
+ // snapshot primitive. It reads committed pages through SQLite rather than
162
+ // copying a main file that may lag committed WAL frames.
163
+ db.exec(`VACUUM INTO ${quoteSqlString(stage)}`);
164
+ const snapshot = new Database(stage, { readonly: true, fileMustExist: true });
165
+ try {
166
+ const integrity = snapshot.pragma('integrity_check');
167
+ if (integrity.length !== 1 || integrity[0].integrity_check !== 'ok') {
168
+ throw new Error('SQLite backup snapshot failed integrity_check');
169
+ }
170
+ } finally {
171
+ snapshot.close();
172
+ }
173
+ fs.renameSync(stage, targetPath);
174
+ return fs.statSync(targetPath).size;
175
+ } finally {
176
+ fs.rmSync(stage, { force: true });
140
177
  }
141
- fs.mkdirSync(path.dirname(targetPath), { recursive: true });
178
+ }
179
+
180
+ function cmdBackup(flags) {
142
181
  let db;
143
182
  try {
144
- // Flush WAL into the main file so a plain copy captures all committed
145
- // writes; fall back to copy-only on a fresh file with no WAL.
183
+ const dbPath = resolveAuthorityPath(flags, { migrateLegacy: true });
184
+ const targetPath = runtimePaths.assertManagedBackupDestination(
185
+ process.cwd(),
186
+ flags.to || defaultBackupPath(),
187
+ ).targetPath;
188
+ if (!fs.existsSync(dbPath)) {
189
+ emit({ ok: false, error: { code: 'DB_NOT_FOUND', message: `state.db missing at ${dbPath}` } });
190
+ return 2;
191
+ }
192
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
146
193
  db = openStateDb(dbPath);
147
- db.pragma('wal_checkpoint(TRUNCATE)');
148
- closeStateDb(db);
149
- db = null;
150
- fs.copyFileSync(dbPath, targetPath);
151
- const size = fs.statSync(targetPath).size;
194
+ const size = createOnlineBackup(db, targetPath);
152
195
  emit({ ok: true, data: { source: dbPath, target: targetPath, size_bytes: size } });
153
196
  return 0;
154
197
  } catch (err) {
@@ -168,8 +211,16 @@ Verbs (Phase 2):
168
211
  integrity [--path <db>] run PRAGMA integrity_check
169
212
  backup [--path <db>] [--to <out>] online .backup() to a file
170
213
 
171
- Default db: .ultra/state.db
172
- Default backup: .ultra/backups/state-db-{iso}.db
214
+ Default db: .ultra/.runtime/state.db
215
+ Default backup: .ultra/.runtime/backups/state-db-{iso}.db
173
216
  `;
174
217
 
175
- module.exports = { dispatch, USAGE, DEFAULT_DB_PATH, DEFAULT_BACKUP_DIR };
218
+ module.exports = {
219
+ dispatch,
220
+ USAGE,
221
+ DEFAULT_DB_PATH,
222
+ DEFAULT_BACKUP_DIR,
223
+ _internal: {
224
+ createOnlineBackup,
225
+ },
226
+ };