ultra-builder-pro-cli 0.20.0 → 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.
- package/.ultra-template/docs/research/README.md +26 -5
- package/.ultra-template/specs/discovery.md +1 -1
- package/.ultra-template/tasks/tasks.json +1 -1
- package/CHANGELOG.md +66 -0
- package/README.md +54 -24
- package/adapters/_shared/codex-assets.cjs +69 -11
- package/adapters/_shared/interaction-contract.cjs +36 -3
- package/adapters/_shared/runtime-assets.cjs +41 -1
- package/adapters/claude.js +7 -5
- package/adapters/codex.js +1 -1
- package/adapters/kimi.js +2 -8
- package/adapters/opencode.js +30 -28
- package/bin/orchestrator.js +64 -24
- package/docs/AGENT-CONTEXT.md +74 -43
- package/docs/ARCHITECTURE.md +63 -31
- package/docs/ARTIFACT-AUTHORITY.md +234 -0
- package/docs/DECISIONS.md +10 -4
- package/docs/LEGACY-CLI-CROSSWALK.md +6 -4
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +4 -3
- package/docs/ROADMAP.md +19 -5
- package/docs/RUNTIME-COMPAT-MATRIX.md +24 -13
- package/docs/STATE-DB-ACCESS-POLICY.md +11 -9
- package/docs/WORKFLOW-LIFECYCLE.md +70 -39
- package/hooks/README.md +6 -5
- package/hooks/active_task_context.py +97 -12
- package/hooks/context_spine.py +16 -4
- package/hooks/health_check.py +5 -4
- package/hooks/pre_stop_check.py +5 -2
- package/hooks/runtime_paths.py +396 -0
- package/hooks/subagent_tracker.py +5 -2
- package/hooks/workflow_checkpoint.py +16 -3
- package/hooks/workflow_context.py +11 -3
- package/hooks/workflow_resume.py +7 -4
- package/mcp-server/breadcrumb.cjs +20 -2
- package/mcp-server/lib/archive-journal.cjs +1026 -111
- package/mcp-server/lib/archive-mutation-worker.py +365 -0
- package/mcp-server/lib/artifact-registry.cjs +1126 -0
- package/mcp-server/lib/baseline-workflow.cjs +9 -4
- package/mcp-server/lib/change-packet.cjs +818 -0
- package/mcp-server/lib/change-workflow.cjs +932 -176
- package/mcp-server/lib/context-paths.cjs +111 -0
- package/mcp-server/lib/context-spine.cjs +511 -45
- package/mcp-server/lib/decision-dialogue.cjs +345 -3
- package/mcp-server/lib/delivery-transaction.cjs +471 -0
- package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
- package/mcp-server/lib/doctor.cjs +373 -11
- package/mcp-server/lib/git-bootstrap.cjs +459 -47
- package/mcp-server/lib/init-project.cjs +190 -95
- package/mcp-server/lib/plan-publication-worker.cjs +830 -0
- package/mcp-server/lib/plan-store.cjs +700 -5
- package/mcp-server/lib/project-breadcrumb.cjs +23 -4
- package/mcp-server/lib/projector.cjs +174 -40
- package/mcp-server/lib/runtime-paths.cjs +1687 -0
- package/mcp-server/lib/safe-project-file.cjs +385 -0
- package/mcp-server/lib/spec-learning.cjs +28 -12
- package/mcp-server/lib/state-db.cjs +263 -9
- package/mcp-server/lib/state-ops.cjs +248 -49
- package/mcp-server/lib/telemetry.cjs +12 -3
- package/mcp-server/lib/workflow-state.cjs +610 -58
- package/mcp-server/server.cjs +278 -33
- package/orchestrator/daemon.cjs +2 -10
- package/orchestrator/parallel-orchestrator.cjs +314 -50
- package/orchestrator/planner/plan-builder.cjs +1 -1
- package/orchestrator/recovery.cjs +125 -7
- package/orchestrator/session-close-journal-worker.cjs +5 -0
- package/orchestrator/session-close-journal.cjs +1014 -0
- package/orchestrator/session-runner.cjs +679 -69
- package/orchestrator/worktree-manager.cjs +186 -35
- package/package.json +2 -3
- package/skills/cc-collab/SKILL.md +4 -1
- package/skills/codex-collab/SKILL.md +5 -2
- package/skills/ultra-change/SKILL.md +26 -8
- package/skills/ultra-deliver/SKILL.md +23 -11
- package/skills/ultra-dev/SKILL.md +33 -6
- package/skills/ultra-doctor/SKILL.md +6 -2
- package/skills/ultra-init/SKILL.md +4 -2
- package/skills/ultra-plan/SKILL.md +49 -16
- package/skills/ultra-research/SKILL.md +30 -27
- package/skills/ultra-review/SKILL.md +9 -5
- package/skills/ultra-status/SKILL.md +9 -4
- package/skills/ultra-test/SKILL.md +10 -5
- package/skills/ultra-think/SKILL.md +6 -2
- package/skills/ultra-think/references/decision-dialogue.md +17 -6
- package/skills/ultra-verify/SKILL.md +4 -1
- package/spec/cli-protocol.md +18 -7
- package/spec/mcp-tools.yaml +502 -44
- package/spec/schemas/change-delta.v1.schema.json +153 -0
- package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +50 -13
- package/spec/schemas/tasks.v4.5.schema.json +1 -1
- package/ultra-tools/cli.cjs +3 -2
- package/ultra-tools/commands/db.cjs +76 -25
- package/ultra-tools/commands/migrate.cjs +995 -80
- package/ultra-tools/commands/session.cjs +34 -8
- package/ultra-tools/commands/status.cjs +32 -6
- package/ultra-tools/commands/system.cjs +129 -22
- package/commands/learn.md +0 -10
- package/orchestrator/skill-miner.cjs +0 -133
- package/output-styles/coding-vibes.md +0 -67
- package/output-styles/structural-thinking.md +0 -61
- 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
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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,
|
|
@@ -495,3 +530,5 @@ INSERT OR IGNORE INTO schema_version (version, description)
|
|
|
495
530
|
VALUES ('18.0', 'Adaptive capability transitions, independent initialization, and recoverable workflow migration');
|
|
496
531
|
INSERT OR IGNORE INTO schema_version (version, description)
|
|
497
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" }
|
package/ultra-tools/cli.cjs
CHANGED
|
@@ -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|
|
|
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 =
|
|
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 =
|
|
9
|
-
const DEFAULT_BACKUP_DIR = path.join(
|
|
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
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function cmdBackup(flags) {
|
|
142
181
|
let db;
|
|
143
182
|
try {
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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 = {
|
|
218
|
+
module.exports = {
|
|
219
|
+
dispatch,
|
|
220
|
+
USAGE,
|
|
221
|
+
DEFAULT_DB_PATH,
|
|
222
|
+
DEFAULT_BACKUP_DIR,
|
|
223
|
+
_internal: {
|
|
224
|
+
createOnlineBackup,
|
|
225
|
+
},
|
|
226
|
+
};
|