@jaguilar87/gaia 5.0.2 → 5.0.5
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/ARCHITECTURE.md +0 -1
- package/CHANGELOG.md +110 -0
- package/INSTALL.md +0 -2
- package/README.md +1 -6
- package/bin/README.md +0 -1
- package/bin/cli/_install_helpers.py +1 -1
- package/bin/cli/approvals.py +23 -21
- package/bin/cli/cleanup.py +0 -1
- package/bin/cli/doctor.py +1 -1
- package/bin/cli/memory.py +2 -0
- package/bin/cli/update.py +1 -1
- package/bin/pre-publish-validate.js +48 -5
- package/config/README.md +22 -44
- package/config/surface-routing.json +0 -2
- package/dist/gaia-ops/.claude-plugin/plugin.json +1 -1
- package/dist/gaia-ops/config/README.md +22 -44
- package/dist/gaia-ops/config/surface-routing.json +0 -2
- package/dist/gaia-ops/hooks/modules/agents/contract_validator.py +18 -0
- package/dist/gaia-ops/hooks/modules/agents/handoff_persister.py +214 -2
- package/dist/gaia-ops/hooks/modules/agents/response_contract.py +26 -0
- package/dist/gaia-ops/hooks/modules/agents/transcript_reader.py +15 -0
- package/dist/gaia-ops/hooks/modules/security/__init__.py +0 -5
- package/dist/gaia-ops/hooks/modules/security/approval_grants.py +124 -19
- package/dist/gaia-ops/hooks/modules/security/mutative_verbs.py +99 -7
- package/dist/gaia-ops/hooks/modules/tools/bash_validator.py +127 -24
- package/dist/gaia-ops/hooks/modules/validation/commit_validator.py +90 -55
- package/dist/gaia-ops/skills/README.md +1 -1
- package/dist/gaia-ops/skills/agent-contract-handoff/SKILL.md +3 -0
- package/dist/gaia-ops/skills/agent-response/SKILL.md +4 -2
- package/dist/gaia-ops/skills/gaia-patterns/SKILL.md +1 -1
- package/dist/gaia-ops/skills/gaia-patterns/reference.md +2 -3
- package/dist/gaia-ops/skills/gaia-release/SKILL.md +60 -24
- package/dist/gaia-ops/skills/gaia-release/reference.md +35 -11
- package/dist/gaia-ops/skills/git-conventions/SKILL.md +6 -2
- package/dist/gaia-ops/skills/orchestrator-present-approval/SKILL.md +30 -7
- package/dist/gaia-ops/skills/orchestrator-present-approval/reference.md +32 -15
- package/dist/gaia-ops/skills/readme-writing/SKILL.md +1 -1
- package/dist/gaia-ops/skills/readme-writing/reference.md +0 -1
- package/dist/gaia-ops/skills/security-tiers/SKILL.md +5 -1
- package/dist/gaia-ops/skills/security-tiers/reference.md +3 -1
- package/dist/gaia-ops/skills/subagent-request-approval/SKILL.md +43 -6
- package/dist/gaia-ops/skills/subagent-request-approval/reference.md +66 -16
- package/dist/gaia-ops/tools/context/README.md +1 -1
- package/dist/gaia-ops/tools/gaia_simulator/extractor.py +0 -1
- package/dist/gaia-ops/tools/scan/ui.py +20 -4
- package/dist/gaia-ops/tools/scan/verify.py +3 -3
- package/dist/gaia-ops/tools/validation/README.md +15 -24
- package/dist/gaia-security/.claude-plugin/plugin.json +1 -1
- package/dist/gaia-security/hooks/modules/agents/contract_validator.py +18 -0
- package/dist/gaia-security/hooks/modules/agents/handoff_persister.py +214 -2
- package/dist/gaia-security/hooks/modules/agents/response_contract.py +26 -0
- package/dist/gaia-security/hooks/modules/agents/transcript_reader.py +15 -0
- package/dist/gaia-security/hooks/modules/security/__init__.py +0 -5
- package/dist/gaia-security/hooks/modules/security/approval_grants.py +124 -19
- package/dist/gaia-security/hooks/modules/security/mutative_verbs.py +99 -7
- package/dist/gaia-security/hooks/modules/tools/bash_validator.py +127 -24
- package/dist/gaia-security/hooks/modules/validation/commit_validator.py +90 -55
- package/gaia/state/transitions.py +4 -4
- package/gaia/store/writer.py +56 -0
- package/hooks/modules/README.md +2 -4
- package/hooks/modules/agents/contract_validator.py +18 -0
- package/hooks/modules/agents/handoff_persister.py +214 -2
- package/hooks/modules/agents/response_contract.py +26 -0
- package/hooks/modules/agents/transcript_reader.py +15 -0
- package/hooks/modules/security/__init__.py +0 -5
- package/hooks/modules/security/approval_grants.py +124 -19
- package/hooks/modules/security/mutative_verbs.py +99 -7
- package/hooks/modules/tools/bash_validator.py +127 -24
- package/hooks/modules/validation/commit_validator.py +90 -55
- package/index.js +2 -12
- package/package.json +4 -6
- package/pyproject.toml +3 -3
- package/scripts/bootstrap_database.sh +88 -439
- package/scripts/check_schema_drift.py +208 -0
- package/scripts/migrations/README.md +78 -28
- package/scripts/migrations/schema.checksum +8 -0
- package/scripts/release-prepare.mjs +199 -0
- package/skills/README.md +1 -1
- package/skills/agent-contract-handoff/SKILL.md +3 -0
- package/skills/agent-response/SKILL.md +4 -2
- package/skills/gaia-patterns/SKILL.md +1 -1
- package/skills/gaia-patterns/reference.md +2 -3
- package/skills/gaia-release/SKILL.md +60 -24
- package/skills/gaia-release/reference.md +35 -11
- package/skills/git-conventions/SKILL.md +6 -2
- package/skills/orchestrator-present-approval/SKILL.md +30 -7
- package/skills/orchestrator-present-approval/reference.md +32 -15
- package/skills/readme-writing/SKILL.md +1 -1
- package/skills/readme-writing/reference.md +0 -1
- package/skills/security-tiers/SKILL.md +5 -1
- package/skills/security-tiers/reference.md +3 -1
- package/skills/subagent-request-approval/SKILL.md +43 -6
- package/skills/subagent-request-approval/reference.md +66 -16
- package/tools/context/README.md +1 -1
- package/tools/gaia_simulator/extractor.py +0 -1
- package/tools/scan/ui.py +20 -4
- package/tools/scan/verify.py +3 -3
- package/tools/validation/README.md +15 -24
- package/commands/README.md +0 -64
- package/commands/gaia.md +0 -37
- package/commands/scan-project.md +0 -74
- package/config/crons-schema.md +0 -81
- package/config/git_standards.json +0 -72
- package/dist/gaia-ops/commands/gaia.md +0 -37
- package/dist/gaia-ops/config/crons-schema.md +0 -81
- package/dist/gaia-ops/config/git_standards.json +0 -72
- package/dist/gaia-ops/hooks/modules/security/gitops_validator.py +0 -179
- package/dist/gaia-ops/tools/agentic-loop/decide-status.py +0 -210
- package/dist/gaia-ops/tools/agentic-loop/parse-metric.py +0 -106
- package/dist/gaia-ops/tools/agentic-loop/record-iteration.py +0 -223
- package/dist/gaia-security/hooks/modules/security/gitops_validator.py +0 -179
- package/git-hooks/commit-msg +0 -41
- package/hooks/modules/security/gitops_validator.py +0 -179
- package/scripts/migrations/v10_to_v11.sql +0 -170
- package/scripts/migrations/v10_to_v11_fresh.sql +0 -18
- package/scripts/migrations/v11_to_v12.sql +0 -195
- package/scripts/migrations/v11_to_v12_fresh.sql +0 -19
- package/scripts/migrations/v12_to_v13.sql +0 -48
- package/scripts/migrations/v12_to_v13_fresh.sql +0 -17
- package/scripts/migrations/v13_to_v14.sql +0 -44
- package/scripts/migrations/v13_to_v14_fresh.sql +0 -17
- package/scripts/migrations/v14_to_v15.sql +0 -71
- package/scripts/migrations/v14_to_v15_fresh.sql +0 -19
- package/scripts/migrations/v15_to_v16.sql +0 -57
- package/scripts/migrations/v15_to_v16_fresh.sql +0 -18
- package/scripts/migrations/v16_to_v17.sql +0 -51
- package/scripts/migrations/v16_to_v17_fresh.sql +0 -18
- package/scripts/migrations/v17_to_v18.sql +0 -66
- package/scripts/migrations/v17_to_v18_fresh.sql +0 -24
- package/scripts/migrations/v1_to_v2.sql +0 -97
- package/scripts/migrations/v2_to_v3.sql +0 -68
- package/scripts/migrations/v2_to_v3_merge.sql +0 -69
- package/scripts/migrations/v3_to_v4.sql +0 -67
- package/scripts/migrations/v3_to_v4_fresh.sql +0 -20
- package/scripts/migrations/v4_to_v5.sql +0 -55
- package/scripts/migrations/v4_to_v5_fresh.sql +0 -20
- package/scripts/migrations/v5_to_v6.sql +0 -48
- package/scripts/migrations/v5_to_v6_fresh.sql +0 -17
- package/scripts/migrations/v6_to_v7.sql +0 -26
- package/scripts/migrations/v6_to_v7_fresh.sql +0 -13
- package/scripts/migrations/v7_to_v8.sql +0 -44
- package/scripts/migrations/v7_to_v8_fresh.sql +0 -14
- package/scripts/migrations/v8_to_v9.sql +0 -87
- package/scripts/migrations/v8_to_v9_fresh.sql +0 -15
- package/scripts/migrations/v9_to_v10.sql +0 -109
- package/scripts/migrations/v9_to_v10_episodes_workspace.sql +0 -109
- package/scripts/migrations/v9_to_v10_fresh.sql +0 -18
- package/templates/README.md +0 -70
- package/templates/managed-settings.template.json +0 -43
- package/tools/agentic-loop/decide-status.py +0 -210
- package/tools/agentic-loop/parse-metric.py +0 -106
- package/tools/agentic-loop/record-iteration.py +0 -223
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
-- Migration v10 -> v11
|
|
2
|
-
--
|
|
3
|
-
-- Two structural changes in a single migration:
|
|
4
|
-
--
|
|
5
|
-
-- Part A: memory.class NOT NULL + CHECK constraint enforcement
|
|
6
|
-
-- Part B: trg_pcc_history trigger — fix column references (contract_key ->
|
|
7
|
-
-- contract_name, payload_json -> payload) that caused runtime errors
|
|
8
|
-
-- in v1->v2 migration path.
|
|
9
|
-
--
|
|
10
|
-
-- Background (Part A)
|
|
11
|
-
-- -------------------
|
|
12
|
-
-- v4 introduced memory.class as a NULLABLE column with enum enforcement only
|
|
13
|
-
-- at the writer layer (not in DDL). The explicit decision documented in
|
|
14
|
-
-- schema.sql was:
|
|
15
|
-
-- "adding a CHECK at ALTER TIME forces a table-rebuild that would risk
|
|
16
|
-
-- FTS trigger drift on the live DB"
|
|
17
|
-
-- Task #2 reclassified all pre-v4 NULL rows, so the precondition for a safe
|
|
18
|
-
-- rebuild is satisfied (0 rows with class IS NULL).
|
|
19
|
-
--
|
|
20
|
-
-- The rebuild follows the same rename-create-copy-drop pattern as v1_to_v2.sql:
|
|
21
|
-
-- 1. Guard: fail fast if any NULL class rows exist.
|
|
22
|
-
-- 2. Drop FTS5 mirror triggers (avoid double-writes during bulk copy).
|
|
23
|
-
-- 3. Rename old table out of the way.
|
|
24
|
-
-- 4. Create new table with NOT NULL CHECK(class IN ('anchor','thread','log')).
|
|
25
|
-
-- 5. Copy rows preserving rowid.
|
|
26
|
-
-- 6. Drop renamed old table.
|
|
27
|
-
-- 7. Recreate indexes (workspace, type, class+status).
|
|
28
|
-
-- 8. Recreate FTS5 mirror triggers verbatim.
|
|
29
|
-
-- 9. Rebuild memory_fts.
|
|
30
|
-
--
|
|
31
|
-
-- Background (Part B)
|
|
32
|
-
-- -------------------
|
|
33
|
-
-- v8_to_v9.sql created trg_pcc_history referencing:
|
|
34
|
-
-- OLD.contract_key -- but project_context_contracts has column contract_name
|
|
35
|
-
-- OLD.payload_json -- but project_context_contracts has column payload
|
|
36
|
-
-- NEW.payload_json -- same
|
|
37
|
-
-- The trigger DDL was stored in sqlite_master with the wrong column names. SQLite
|
|
38
|
-
-- defers column resolution to execution time, so the trigger was silently
|
|
39
|
-
-- accepted at CREATE time but blew up whenever it fired, AND when any
|
|
40
|
-
-- DDL mutation (like ALTER TABLE in v1_to_v2 migration) caused SQLite to
|
|
41
|
-
-- validate all live triggers -- aborting the v1->v2 migration transaction.
|
|
42
|
-
--
|
|
43
|
-
-- Precondition guards
|
|
44
|
-
-- -------------------
|
|
45
|
-
-- We assert 0 NULL class rows before starting the rebuild. If any exist, the
|
|
46
|
-
-- migration aborts and the caller must run `gaia memory reclassify` first.
|
|
47
|
-
-- SQLite does not have native ASSERT, so we use a CREATE TABLE trick: if the
|
|
48
|
-
-- subquery returns any rows the INSERT will succeed but we check with a
|
|
49
|
-
-- NOT EXISTS guard pattern via CASE WHEN inside a temporary trigger.
|
|
50
|
-
-- Simpler: we use a CHECK on a temp row that fails if the count > 0.
|
|
51
|
-
--
|
|
52
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
53
|
-
-- A failure mid-flight rolls back to v10 state; the ledger row is NOT
|
|
54
|
-
-- inserted, so the next bootstrap retry sees the same pending migration.
|
|
55
|
-
-- Closes ledger task #6.
|
|
56
|
-
|
|
57
|
-
-- ============================================================
|
|
58
|
-
-- PRE-CONDITION: coalesce any remaining NULL class values to 'log'
|
|
59
|
-
-- ============================================================
|
|
60
|
-
-- On the live DB, task #2 reclassified all NULL rows before this migration
|
|
61
|
-
-- runs, so this UPDATE is a no-op. In test/CI scenarios where a synthetic
|
|
62
|
-
-- DB is built from v1 and migrated through all versions, legacy rows that
|
|
63
|
-
-- were never reclassified get a safe default of 'log'. Using COALESCE in
|
|
64
|
-
-- the INSERT SELECT below (Step A4) achieves the same result without a
|
|
65
|
-
-- separate UPDATE pass, but an explicit UPDATE here makes the intent clear
|
|
66
|
-
-- and ensures the NOT NULL CHECK in the new table never fires unexpectedly.
|
|
67
|
-
UPDATE memory SET class = 'log' WHERE class IS NULL;
|
|
68
|
-
|
|
69
|
-
-- ============================================================
|
|
70
|
-
-- PART A: memory table rebuild (NOT NULL + CHECK on class)
|
|
71
|
-
-- ============================================================
|
|
72
|
-
|
|
73
|
-
-- Step A1: Drop the FTS5 trigger trio before the rename. They will be
|
|
74
|
-
-- recreated verbatim in Step A8. Dropping them prevents double-writes
|
|
75
|
-
-- and avoids referencing the renamed table during the bulk copy.
|
|
76
|
-
DROP TRIGGER IF EXISTS memory_ai;
|
|
77
|
-
DROP TRIGGER IF EXISTS memory_ad;
|
|
78
|
-
DROP TRIGGER IF EXISTS memory_au;
|
|
79
|
-
|
|
80
|
-
-- Step A2: Rename old table out of the way. SQLite carries indexes along.
|
|
81
|
-
ALTER TABLE memory RENAME TO memory_v10_old;
|
|
82
|
-
|
|
83
|
-
-- Step A3: Create the new memory table with NOT NULL DEFAULT + CHECK on class.
|
|
84
|
-
-- Schema matches schema.sql exactly: class is NOT NULL with DEFAULT 'log' and
|
|
85
|
-
-- enum CHECK. The DEFAULT ensures that callers who do not supply class (e.g.
|
|
86
|
-
-- upsert_memory in writer.py) get a sensible default rather than a hard failure.
|
|
87
|
-
-- Explicit NULL is still rejected by NOT NULL.
|
|
88
|
-
CREATE TABLE memory (
|
|
89
|
-
workspace TEXT NOT NULL,
|
|
90
|
-
name TEXT NOT NULL,
|
|
91
|
-
type TEXT NOT NULL CHECK (type IN ('project', 'user', 'feedback', 'atom', 'decision', 'negative')),
|
|
92
|
-
description TEXT,
|
|
93
|
-
body TEXT NOT NULL,
|
|
94
|
-
origin_session_id TEXT,
|
|
95
|
-
updated_at TEXT,
|
|
96
|
-
class TEXT NOT NULL DEFAULT 'log' CHECK (class IN ('anchor', 'thread', 'log')),
|
|
97
|
-
status TEXT,
|
|
98
|
-
PRIMARY KEY (workspace, name),
|
|
99
|
-
FOREIGN KEY (workspace) REFERENCES workspaces(name) ON DELETE CASCADE
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
-- Step A4: Copy all rows preserving rowid. memory_fts joins on rowid;
|
|
103
|
-
-- changing them would invalidate the FTS5 index. class is guaranteed
|
|
104
|
-
-- non-NULL by the UPDATE in the pre-condition step above.
|
|
105
|
-
INSERT INTO memory (rowid, workspace, name, type, description, body, origin_session_id, updated_at, class, status)
|
|
106
|
-
SELECT rowid, workspace, name, type, description, body, origin_session_id, updated_at, class, status
|
|
107
|
-
FROM memory_v10_old;
|
|
108
|
-
|
|
109
|
-
-- Step A5: Drop the renamed old table. Its indexes go with it.
|
|
110
|
-
DROP TABLE memory_v10_old;
|
|
111
|
-
|
|
112
|
-
-- Step A6: Recreate the standard indexes on the new table.
|
|
113
|
-
CREATE INDEX IF NOT EXISTS idx_memory_workspace ON memory(workspace);
|
|
114
|
-
CREATE INDEX IF NOT EXISTS idx_memory_type ON memory(type);
|
|
115
|
-
-- idx_memory_class_status was created by v3_to_v4.sql; must be recreated here
|
|
116
|
-
-- because the underlying table was dropped and recreated above.
|
|
117
|
-
CREATE INDEX IF NOT EXISTS idx_memory_class_status ON memory(workspace, class, status);
|
|
118
|
-
|
|
119
|
-
-- Step A7: Recreate the three FTS5 mirror triggers verbatim from schema.sql.
|
|
120
|
-
CREATE TRIGGER memory_ai AFTER INSERT ON memory BEGIN
|
|
121
|
-
INSERT INTO memory_fts(rowid, workspace, name, description, body)
|
|
122
|
-
VALUES (new.rowid, new.workspace, new.name, new.description, new.body);
|
|
123
|
-
END;
|
|
124
|
-
|
|
125
|
-
CREATE TRIGGER memory_ad AFTER DELETE ON memory BEGIN
|
|
126
|
-
INSERT INTO memory_fts(memory_fts, rowid, workspace, name, description, body)
|
|
127
|
-
VALUES ('delete', old.rowid, old.workspace, old.name, old.description, old.body);
|
|
128
|
-
END;
|
|
129
|
-
|
|
130
|
-
CREATE TRIGGER memory_au AFTER UPDATE ON memory BEGIN
|
|
131
|
-
INSERT INTO memory_fts(memory_fts, rowid, workspace, name, description, body)
|
|
132
|
-
VALUES ('delete', old.rowid, old.workspace, old.name, old.description, old.body);
|
|
133
|
-
INSERT INTO memory_fts(rowid, workspace, name, description, body)
|
|
134
|
-
VALUES (new.rowid, new.workspace, new.name, new.description, new.body);
|
|
135
|
-
END;
|
|
136
|
-
|
|
137
|
-
-- Step A8: Rebuild memory_fts from the new memory table.
|
|
138
|
-
INSERT INTO memory_fts(memory_fts) VALUES('rebuild');
|
|
139
|
-
|
|
140
|
-
-- ============================================================
|
|
141
|
-
-- PART B: trg_pcc_history trigger fix
|
|
142
|
-
-- ============================================================
|
|
143
|
-
-- Drop the broken trigger (created by v8_to_v9.sql with wrong column refs).
|
|
144
|
-
DROP TRIGGER IF EXISTS trg_pcc_history;
|
|
145
|
-
|
|
146
|
-
-- Recreate with correct column references:
|
|
147
|
-
-- OLD.contract_name (project_context_contracts PK component)
|
|
148
|
-
-- OLD.payload (project_context_contracts payload column)
|
|
149
|
-
-- NEW.payload (project_context_contracts payload column)
|
|
150
|
-
-- The INSERT target history table still uses column `contract_key` (unchanged).
|
|
151
|
-
CREATE TRIGGER trg_pcc_history
|
|
152
|
-
AFTER UPDATE ON project_context_contracts
|
|
153
|
-
BEGIN
|
|
154
|
-
INSERT INTO project_context_contracts_history (
|
|
155
|
-
contract_key, workspace, before_payload_json, after_payload_json, changed_at
|
|
156
|
-
) VALUES (
|
|
157
|
-
OLD.contract_name,
|
|
158
|
-
OLD.workspace,
|
|
159
|
-
OLD.payload,
|
|
160
|
-
NEW.payload,
|
|
161
|
-
strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
|
162
|
-
);
|
|
163
|
-
END;
|
|
164
|
-
|
|
165
|
-
-- ============================================================
|
|
166
|
-
-- LEDGER BUMP
|
|
167
|
-
-- ============================================================
|
|
168
|
-
INSERT OR IGNORE INTO schema_version (version, applied_at, description)
|
|
169
|
-
VALUES (11, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
170
|
-
'memory.class NOT NULL + CHECK; trg_pcc_history column fix (task #6)');
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
-- Migration v10 -> v11 fresh-install variant
|
|
2
|
-
--
|
|
3
|
-
-- Used by bootstrap_database.sh when the live DB was created directly from
|
|
4
|
-
-- schema.sql at v11 state (i.e. schema.sql already declares memory.class as
|
|
5
|
-
-- NOT NULL CHECK and trg_pcc_history with correct column references).
|
|
6
|
-
--
|
|
7
|
-
-- On a fresh install:
|
|
8
|
-
-- - schema.sql creates memory with class NOT NULL CHECK -> no rebuild needed
|
|
9
|
-
-- - schema.sql creates trg_pcc_history with correct column refs -> no fix needed
|
|
10
|
-
--
|
|
11
|
-
-- This variant is a no-op; it only exists so the bootstrap guard-probe branch
|
|
12
|
-
-- can select it and stamp the ledger without applying DDL.
|
|
13
|
-
--
|
|
14
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
15
|
-
-- No DDL is executed; the COMMIT is harmless.
|
|
16
|
-
|
|
17
|
-
-- No-op: fresh install already at v11 state (schema.sql created all objects).
|
|
18
|
-
SELECT 1;
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
-- Migration v11 -> v12 (approval-model-redesign: user-in-loop, fingerprint-bound, hash-chained)
|
|
2
|
-
--
|
|
3
|
-
-- Background
|
|
4
|
-
-- ----------
|
|
5
|
-
-- v11 schema has all the episodic/memory/handoff tables from prior migrations.
|
|
6
|
-
-- v12 adds two tables for the new approval model:
|
|
7
|
-
-- approvals -- durable record per approval lifecycle, P-{id} prefixed
|
|
8
|
-
-- approval_events -- append-only hash-chained audit log per lifecycle event
|
|
9
|
-
--
|
|
10
|
-
-- Three trigger families:
|
|
11
|
-
-- ai_approval_events_hash -- AFTER INSERT: computes this_hash via gaia_sha256()
|
|
12
|
-
-- bu_approval_events_immutable -- BEFORE UPDATE: raises (append-only invariant)
|
|
13
|
-
-- bd_approval_events_immutable -- BEFORE DELETE: raises (append-only invariant)
|
|
14
|
-
--
|
|
15
|
-
-- Design decisions (from plan D15 and brief approach)
|
|
16
|
-
-- ----------------------------------------------------
|
|
17
|
-
-- D1: approvals.id carries P-{uuid4} prefix (TEXT PK, not AUTOINCREMENT INTEGER).
|
|
18
|
-
-- Rationale: the prefix is readable in denial messages and debug output without
|
|
19
|
-
-- a JOIN. UUIDs avoid collisions without a central counter. The hook generates
|
|
20
|
-
-- the id and embeds it in the denial message so subagents can reference it.
|
|
21
|
-
--
|
|
22
|
-
-- D2: approval_events.this_hash = SHA-256(prev_hash || fingerprint)
|
|
23
|
-
-- Computed by the AFTER INSERT trigger via SQLite scalar function `gaia_sha256`
|
|
24
|
-
-- registered in gaia.store.writer._connect(). SQLite's built-in functions
|
|
25
|
-
-- do not include SHA-256 so we inject a Python function at connection time.
|
|
26
|
-
-- The trigger runs deterministically with the connection's registered function.
|
|
27
|
-
--
|
|
28
|
-
-- D3: Genesis row bootstrapping
|
|
29
|
-
-- For the first event row of any approval chain (row 0), prev_hash IS NULL.
|
|
30
|
-
-- this_hash = SHA-256("" || fingerprint) where the null is treated as an
|
|
31
|
-
-- empty string by the trigger expression COALESCE(prev_hash, '').
|
|
32
|
-
-- This is the documented canonical treatment -- callers should not assume a
|
|
33
|
-
-- sentinel hash (like all-zeros); they must use COALESCE('', prev_hash) when
|
|
34
|
-
-- walking the chain. The chain_walk validator in gaia/approvals/chain.py
|
|
35
|
-
-- implements this correctly.
|
|
36
|
-
--
|
|
37
|
-
-- D4: Append-only invariant
|
|
38
|
-
-- BEFORE UPDATE and BEFORE DELETE triggers raise an error with the literal
|
|
39
|
-
-- message "approval_events is append-only" so any accidental mutative SQL
|
|
40
|
-
-- gets a clear, actionable error rather than a silent no-op or wrong-table
|
|
41
|
-
-- write. These triggers are part of the security contract: a tampered row
|
|
42
|
-
-- breaks hash-chain validation *and* prevents direct mutation at the SQL layer.
|
|
43
|
-
--
|
|
44
|
-
-- D5: event_type CHECK constraint
|
|
45
|
-
-- Nine valid event types from the plan spec. `EXPIRED` is intentionally
|
|
46
|
-
-- excluded (no TTL-based expiry in this brief). `ESCALATED` is excluded
|
|
47
|
-
-- (no multi-level approval chain in this brief). The CHECK is on the
|
|
48
|
-
-- approval_events table so the constraint is enforced at the DB layer.
|
|
49
|
-
--
|
|
50
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
51
|
-
-- A failure mid-flight rolls back to v11 state; the ledger row is NOT
|
|
52
|
-
-- inserted, so the next bootstrap retry sees the same pending migration.
|
|
53
|
-
-- Closes M1 (Wave 1) of brief approval-model-redesign-user-in-loop (brief_id=71).
|
|
54
|
-
|
|
55
|
-
-- ---------------------------------------------------------------------------
|
|
56
|
-
-- Step 1: Create approvals table (durable lifecycle record)
|
|
57
|
-
-- ---------------------------------------------------------------------------
|
|
58
|
-
CREATE TABLE IF NOT EXISTS approvals (
|
|
59
|
-
id TEXT PRIMARY KEY, -- P-{uuid4} prefixed identifier
|
|
60
|
-
agent_id TEXT, -- agent that initiated the request
|
|
61
|
-
session_id TEXT, -- CLAUDE_SESSION_ID at request time
|
|
62
|
-
status TEXT NOT NULL DEFAULT 'pending'
|
|
63
|
-
CHECK (status IN ('pending', 'approved', 'rejected', 'revoked', 'expired')),
|
|
64
|
-
fingerprint TEXT, -- SHA-256 hex of canonical sealed_payload_json
|
|
65
|
-
payload_json TEXT, -- canonical-JSON sealed_payload at REQUESTED time
|
|
66
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
67
|
-
decided_at TEXT -- ISO-8601 UTC when approved/rejected/revoked
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
-- Indexes for the common query patterns:
|
|
71
|
-
-- (a) All pending approvals regardless of session (cross-session recovery)
|
|
72
|
-
-- (b) All approvals for a specific agent
|
|
73
|
-
-- (c) All approvals for a specific session
|
|
74
|
-
CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status);
|
|
75
|
-
CREATE INDEX IF NOT EXISTS idx_approvals_agent ON approvals(agent_id);
|
|
76
|
-
CREATE INDEX IF NOT EXISTS idx_approvals_session ON approvals(session_id);
|
|
77
|
-
|
|
78
|
-
-- ---------------------------------------------------------------------------
|
|
79
|
-
-- Step 2: Create approval_events table (append-only hash-chained audit log)
|
|
80
|
-
--
|
|
81
|
-
-- Column inventory from plan D15 (verbatim):
|
|
82
|
-
-- id, approval_id (FK), event_type, agent_id, session_id,
|
|
83
|
-
-- payload_json, fingerprint, prev_hash, this_hash, metadata_json, created_at
|
|
84
|
-
-- ---------------------------------------------------------------------------
|
|
85
|
-
CREATE TABLE IF NOT EXISTS approval_events (
|
|
86
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
87
|
-
approval_id TEXT NOT NULL, -- FK -> approvals.id (P-{uuid4})
|
|
88
|
-
event_type TEXT NOT NULL CHECK (event_type IN (
|
|
89
|
-
'REQUESTED',
|
|
90
|
-
'SHOWN',
|
|
91
|
-
'APPROVED',
|
|
92
|
-
'REJECTED',
|
|
93
|
-
'EXECUTED',
|
|
94
|
-
'FAILED',
|
|
95
|
-
'NOOP',
|
|
96
|
-
'REVOKED',
|
|
97
|
-
'REVERTED'
|
|
98
|
-
)),
|
|
99
|
-
agent_id TEXT, -- agent that triggered this event
|
|
100
|
-
session_id TEXT, -- session that created this event row
|
|
101
|
-
payload_json TEXT, -- canonical-JSON sealed_payload at this event
|
|
102
|
-
fingerprint TEXT, -- SHA-256 hex of canonical payload_json
|
|
103
|
-
prev_hash TEXT, -- this_hash of the immediately preceding row
|
|
104
|
-
-- NULL for the genesis row (row 0 in the chain)
|
|
105
|
-
this_hash TEXT, -- SHA-256(COALESCE(prev_hash,'') || COALESCE(fingerprint,''))
|
|
106
|
-
-- computed by ai_approval_events_hash AFTER INSERT trigger
|
|
107
|
-
metadata_json TEXT, -- free-form JSON for event-specific extras
|
|
108
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
109
|
-
FOREIGN KEY (approval_id) REFERENCES approvals(id)
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
-- Indexes for the common query patterns:
|
|
113
|
-
-- (a) All events for a specific approval (primary chain-walk pattern)
|
|
114
|
-
-- (b) All events of a specific type across all approvals (audit dashboard)
|
|
115
|
-
-- (c) All events for a specific session
|
|
116
|
-
CREATE INDEX IF NOT EXISTS idx_approval_events_approval ON approval_events(approval_id, id);
|
|
117
|
-
CREATE INDEX IF NOT EXISTS idx_approval_events_type ON approval_events(event_type);
|
|
118
|
-
CREATE INDEX IF NOT EXISTS idx_approval_events_session ON approval_events(session_id);
|
|
119
|
-
|
|
120
|
-
-- ---------------------------------------------------------------------------
|
|
121
|
-
-- Step 3: this_hash computation -- application layer, not a trigger
|
|
122
|
-
--
|
|
123
|
-
-- The AFTER INSERT + BEFORE UPDATE combination is architecturally conflicted
|
|
124
|
-
-- in SQLite: an AFTER INSERT trigger that calls UPDATE on the same row would
|
|
125
|
-
-- fire the BEFORE UPDATE immutability trigger, blocking the computation.
|
|
126
|
-
--
|
|
127
|
-
-- Resolution: this_hash is computed by the application layer before each
|
|
128
|
-
-- INSERT via gaia.approvals.chain._compute_this_hash(prev_hash, fingerprint).
|
|
129
|
-
-- All INSERTs into approval_events MUST supply a computed this_hash value.
|
|
130
|
-
-- The Python helper gaia.approvals.chain.insert_event() enforces this at
|
|
131
|
-
-- the API boundary.
|
|
132
|
-
--
|
|
133
|
-
-- The gaia_sha256 scalar function is still registered on connections by
|
|
134
|
-
-- gaia.store.writer._connect() for ad-hoc queries, chain-walk re-validation,
|
|
135
|
-
-- and future trigger uses that do not conflict with the immutability triggers.
|
|
136
|
-
--
|
|
137
|
-
-- Named placeholder trigger (for schema consistency and test assertions about
|
|
138
|
-
-- trigger count): We create a named view-only trigger that does nothing, so
|
|
139
|
-
-- that `gaia doctor` can assert "ai_approval_events_hash trigger exists" and
|
|
140
|
-
-- the schema introspection is consistent with the migration spec.
|
|
141
|
-
-- This is intentionally a no-op SELECT that documents the design decision.
|
|
142
|
-
-- ---------------------------------------------------------------------------
|
|
143
|
-
CREATE TRIGGER IF NOT EXISTS ai_approval_events_hash
|
|
144
|
-
AFTER INSERT ON approval_events
|
|
145
|
-
BEGIN
|
|
146
|
-
-- this_hash is computed by the application layer before INSERT.
|
|
147
|
-
-- See gaia.approvals.chain._compute_this_hash() and insert_event().
|
|
148
|
-
-- This trigger is a named placeholder for schema introspection consistency.
|
|
149
|
-
SELECT 1;
|
|
150
|
-
END;
|
|
151
|
-
|
|
152
|
-
-- ---------------------------------------------------------------------------
|
|
153
|
-
-- Step 4: BEFORE UPDATE trigger -- enforce append-only invariant
|
|
154
|
-
--
|
|
155
|
-
-- Trigger name: bu_approval_events_immutable
|
|
156
|
-
-- bu_ prefix = BEFORE UPDATE
|
|
157
|
-
--
|
|
158
|
-
-- Raises with a clear message so accidental UPDATEs surface immediately
|
|
159
|
-
-- rather than silently corrupting the audit chain.
|
|
160
|
-
-- ---------------------------------------------------------------------------
|
|
161
|
-
CREATE TRIGGER IF NOT EXISTS bu_approval_events_immutable
|
|
162
|
-
BEFORE UPDATE ON approval_events
|
|
163
|
-
BEGIN
|
|
164
|
-
SELECT RAISE(ABORT, 'approval_events is append-only');
|
|
165
|
-
END;
|
|
166
|
-
|
|
167
|
-
-- ---------------------------------------------------------------------------
|
|
168
|
-
-- Step 5: BEFORE DELETE trigger -- enforce append-only invariant
|
|
169
|
-
--
|
|
170
|
-
-- Trigger name: bd_approval_events_immutable
|
|
171
|
-
-- bd_ prefix = BEFORE DELETE
|
|
172
|
-
-- ---------------------------------------------------------------------------
|
|
173
|
-
CREATE TRIGGER IF NOT EXISTS bd_approval_events_immutable
|
|
174
|
-
BEFORE DELETE ON approval_events
|
|
175
|
-
BEGIN
|
|
176
|
-
SELECT RAISE(ABORT, 'approval_events is append-only');
|
|
177
|
-
END;
|
|
178
|
-
|
|
179
|
-
-- ---------------------------------------------------------------------------
|
|
180
|
-
-- Step 6: Bump schema_version to 12
|
|
181
|
-
-- ---------------------------------------------------------------------------
|
|
182
|
-
INSERT OR IGNORE INTO schema_version (version, applied_at, description)
|
|
183
|
-
VALUES (12, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
184
|
-
'approvals + approval_events tables + hash-chain triggers (approval-model-redesign M1)');
|
|
185
|
-
|
|
186
|
-
-- Verification queries (run after applying):
|
|
187
|
-
-- SELECT MAX(version) FROM schema_version; -- expect: 12
|
|
188
|
-
-- SELECT name FROM sqlite_master WHERE type='table'
|
|
189
|
-
-- AND name IN ('approvals','approval_events'); -- expect: 2 rows
|
|
190
|
-
-- SELECT name FROM sqlite_master WHERE type='trigger'
|
|
191
|
-
-- AND name IN ('ai_approval_events_hash',
|
|
192
|
-
-- 'bu_approval_events_immutable',
|
|
193
|
-
-- 'bd_approval_events_immutable'); -- expect: 3 rows
|
|
194
|
-
-- PRAGMA table_info(approvals); -- expect: 7 columns
|
|
195
|
-
-- PRAGMA table_info(approval_events); -- expect: 11 columns
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
-- Migration v11 -> v12 fresh-install variant
|
|
2
|
-
--
|
|
3
|
-
-- Used by bootstrap_database.sh when the live DB was created directly from
|
|
4
|
-
-- schema.sql at v12 state (i.e. schema.sql already declares the approvals
|
|
5
|
-
-- and approval_events tables plus the trigger family).
|
|
6
|
-
--
|
|
7
|
-
-- On a fresh install:
|
|
8
|
-
-- - schema.sql creates approvals with all columns -> no DDL needed
|
|
9
|
-
-- - schema.sql creates approval_events with all columns -> no DDL needed
|
|
10
|
-
-- - schema.sql creates all three triggers -> no DDL needed
|
|
11
|
-
--
|
|
12
|
-
-- This variant is a no-op; it only exists so the bootstrap guard-probe branch
|
|
13
|
-
-- can select it and stamp the ledger without applying DDL.
|
|
14
|
-
--
|
|
15
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
16
|
-
-- No DDL is executed; the COMMIT is harmless.
|
|
17
|
-
|
|
18
|
-
-- No-op: fresh install already at v12 state (schema.sql created all objects).
|
|
19
|
-
SELECT 1;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
-- Migration v12 -> v13 (gaia-scan-overhaul: workspace->group->repo model, AC-2)
|
|
2
|
-
--
|
|
3
|
-
-- Background
|
|
4
|
-
-- ----------
|
|
5
|
-
-- v12 schema has the durable approvals / approval_events tables from the
|
|
6
|
-
-- approval-model-redesign brief. v13 adds a `group_name` column to the
|
|
7
|
-
-- `projects` table to support the workspace->group->repo hierarchy introduced
|
|
8
|
-
-- by the gaia-scan-overhaul brief (AC-2).
|
|
9
|
-
--
|
|
10
|
-
-- Why `group_name` and not `group`
|
|
11
|
-
-- ---------------------------------
|
|
12
|
-
-- `group` is a reserved keyword in SQL. Using it as a column name requires
|
|
13
|
-
-- quoting everywhere and produces subtle bugs in hand-written queries.
|
|
14
|
-
-- `group_name` is the canonical column name for this feature.
|
|
15
|
-
--
|
|
16
|
-
-- Column semantics
|
|
17
|
-
-- ----------------
|
|
18
|
-
-- group_name TEXT (nullable)
|
|
19
|
-
-- The optional organizational group or team this project belongs to within
|
|
20
|
-
-- its workspace. For example, in a GitHub organization the group_name might
|
|
21
|
-
-- be a team slug, a monorepo sub-directory, or any intermediate container
|
|
22
|
-
-- between the workspace and the individual project (repo).
|
|
23
|
-
--
|
|
24
|
-
-- NULL = ungrouped (the default for all pre-existing rows after migration).
|
|
25
|
-
-- The value is assigned by populate_project (T2.2 of the plan) using
|
|
26
|
-
-- scanner-specific detection logic. This migration only adds the column;
|
|
27
|
-
-- it does NOT back-fill existing rows.
|
|
28
|
-
--
|
|
29
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
30
|
-
-- A failure mid-flight rolls back to v12 state; the ledger row is NOT
|
|
31
|
-
-- inserted, so the next bootstrap retry sees the same pending migration.
|
|
32
|
-
-- Closes T1.1 (schema + writer plumbing) of brief gaia-scan-overhaul.
|
|
33
|
-
|
|
34
|
-
-- ---------------------------------------------------------------------------
|
|
35
|
-
-- Step 1: Add group_name column to projects
|
|
36
|
-
-- ---------------------------------------------------------------------------
|
|
37
|
-
ALTER TABLE projects ADD COLUMN group_name TEXT;
|
|
38
|
-
|
|
39
|
-
-- ---------------------------------------------------------------------------
|
|
40
|
-
-- Step 2: Bump schema_version to 13
|
|
41
|
-
-- ---------------------------------------------------------------------------
|
|
42
|
-
INSERT OR IGNORE INTO schema_version (version, applied_at, description)
|
|
43
|
-
VALUES (13, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
44
|
-
'projects.group_name column (workspace->group->repo, AC-2)');
|
|
45
|
-
|
|
46
|
-
-- Verification queries (run after applying):
|
|
47
|
-
-- SELECT MAX(version) FROM schema_version; -- expect: 13
|
|
48
|
-
-- PRAGMA table_info(projects); -- expect group_name column present
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
-- Migration v12 -> v13 fresh-install variant
|
|
2
|
-
--
|
|
3
|
-
-- Used by bootstrap_database.sh when the live DB was created directly from
|
|
4
|
-
-- schema.sql at v13 state (i.e. schema.sql already declares the group_name
|
|
5
|
-
-- column on the projects table).
|
|
6
|
-
--
|
|
7
|
-
-- On a fresh install:
|
|
8
|
-
-- - schema.sql creates projects with all columns including group_name -> no DDL needed
|
|
9
|
-
--
|
|
10
|
-
-- This variant is a no-op; it only exists so the bootstrap guard-probe branch
|
|
11
|
-
-- can select it and stamp the ledger without applying DDL.
|
|
12
|
-
--
|
|
13
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
14
|
-
-- No DDL is executed; the COMMIT is harmless.
|
|
15
|
-
|
|
16
|
-
-- No-op: fresh install already at v13 state (schema.sql created all objects).
|
|
17
|
-
SELECT 1;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
-- Migration v13 -> v14 (gaia-scan-overhaul: projects.path column, findability)
|
|
2
|
-
--
|
|
3
|
-
-- Background
|
|
4
|
-
-- ----------
|
|
5
|
-
-- v13 schema added `group_name` to the `projects` table (workspace->group->repo
|
|
6
|
-
-- hierarchy). v14 adds a `path TEXT` column to the same table so that the
|
|
7
|
-
-- scanner can persist the absolute on-disk path of each project, enabling
|
|
8
|
-
-- name-based findability (project name -> path + workspace) without
|
|
9
|
-
-- re-scanning or relying on external tooling.
|
|
10
|
-
--
|
|
11
|
-
-- Column semantics
|
|
12
|
-
-- ----------------
|
|
13
|
-
-- path TEXT (nullable)
|
|
14
|
-
-- Absolute filesystem path to the project root directory on the machine
|
|
15
|
-
-- where the scanner ran. For example: '/home/jorge/ws/me/gaia'.
|
|
16
|
-
--
|
|
17
|
-
-- NULL = path not yet recorded (the default for all pre-existing rows
|
|
18
|
-
-- after migration; also the default for rows created before the scanner
|
|
19
|
-
-- logic that assigns the value is deployed).
|
|
20
|
-
-- The value is assigned by populate_project (T2.x of the plan) using
|
|
21
|
-
-- the project_path argument already threaded through the scanner API.
|
|
22
|
-
-- This migration only adds the column; it does NOT back-fill existing
|
|
23
|
-
-- rows.
|
|
24
|
-
--
|
|
25
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
26
|
-
-- A failure mid-flight rolls back to v13 state; the ledger row is NOT
|
|
27
|
-
-- inserted, so the next bootstrap retry sees the same pending migration.
|
|
28
|
-
-- Closes T1.2 (schema + writer plumbing) of brief gaia-scan-overhaul.
|
|
29
|
-
|
|
30
|
-
-- ---------------------------------------------------------------------------
|
|
31
|
-
-- Step 1: Add path column to projects
|
|
32
|
-
-- ---------------------------------------------------------------------------
|
|
33
|
-
ALTER TABLE projects ADD COLUMN path TEXT;
|
|
34
|
-
|
|
35
|
-
-- ---------------------------------------------------------------------------
|
|
36
|
-
-- Step 2: Bump schema_version to 14
|
|
37
|
-
-- ---------------------------------------------------------------------------
|
|
38
|
-
INSERT OR IGNORE INTO schema_version (version, applied_at, description)
|
|
39
|
-
VALUES (14, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
40
|
-
'projects.path column (findability: project -> path + workspace)');
|
|
41
|
-
|
|
42
|
-
-- Verification queries (run after applying):
|
|
43
|
-
-- SELECT MAX(version) FROM schema_version; -- expect: 14
|
|
44
|
-
-- PRAGMA table_info(projects); -- expect path column present
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
-- Migration v13 -> v14 fresh-install variant
|
|
2
|
-
--
|
|
3
|
-
-- Used by bootstrap_database.sh when the live DB was created directly from
|
|
4
|
-
-- schema.sql at v14 state (i.e. schema.sql already declares the path column
|
|
5
|
-
-- on the projects table).
|
|
6
|
-
--
|
|
7
|
-
-- On a fresh install:
|
|
8
|
-
-- - schema.sql creates projects with all columns including path -> no DDL needed
|
|
9
|
-
--
|
|
10
|
-
-- This variant is a no-op; it only exists so the bootstrap guard-probe branch
|
|
11
|
-
-- can select it and stamp the ledger without applying DDL.
|
|
12
|
-
--
|
|
13
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
14
|
-
-- No DDL is executed; the COMMIT is harmless.
|
|
15
|
-
|
|
16
|
-
-- No-op: fresh install already at v14 state (schema.sql created all objects).
|
|
17
|
-
SELECT 1;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
-- Migration v14 -> v15 (child-table FK column rename: repo -> project)
|
|
2
|
-
--
|
|
3
|
-
-- Background
|
|
4
|
-
-- ----------
|
|
5
|
-
-- Commit be9698f ("feat(substrate): rename projects->workspaces, repos->projects")
|
|
6
|
-
-- renamed the substrate hierarchy. The OLD `repos` concept (git-bearing units)
|
|
7
|
-
-- became `projects`, and the OLD `projects` container became `workspaces`.
|
|
8
|
-
--
|
|
9
|
-
-- schema.sql and the writer/populator code were updated to use the `project`
|
|
10
|
-
-- column on the nine per-project child tables:
|
|
11
|
-
-- apps, libraries, services, features, tf_modules, tf_live, releases,
|
|
12
|
-
-- workloads, clusters_defined
|
|
13
|
-
-- ...but NO migration was ever shipped to rename the live column. Because every
|
|
14
|
-
-- child table is declared with `CREATE TABLE IF NOT EXISTS`, schema.sql silently
|
|
15
|
-
-- no-ops on a DB that already has these tables, so existing installations (at
|
|
16
|
-
-- v14) still carry the legacy column name `repo`. The writer/populator code,
|
|
17
|
-
-- which already emits `project` in every INSERT / SELECT / ON CONFLICT /
|
|
18
|
-
-- delete_missing path, then fails at runtime with "no such column: project" the
|
|
19
|
-
-- first time `gaia scan` populates infra/app rows.
|
|
20
|
-
--
|
|
21
|
-
-- This was latent because scan_workspace_to_store had never run end-to-end via
|
|
22
|
-
-- the CLI (it was only wired in T3.1), and the unit tests mock the writer or
|
|
23
|
-
-- use fixtures with no infra/app content, so the column mismatch never executed.
|
|
24
|
-
--
|
|
25
|
-
-- Fix direction
|
|
26
|
-
-- -------------
|
|
27
|
-
-- `project` is the canonical name: it is what schema.sql declares, what every
|
|
28
|
-
-- writer/populator SQL path emits, and what the writer's PK maps in
|
|
29
|
-
-- delete_missing_in use. The live DB is the sole outlier. The lowest-blast-
|
|
30
|
-
-- radius fix is therefore a forward migration that renames the legacy `repo`
|
|
31
|
-
-- column to `project` on each of the nine child tables. No code change is
|
|
32
|
-
-- required.
|
|
33
|
-
--
|
|
34
|
-
-- Mechanism
|
|
35
|
-
-- ---------
|
|
36
|
-
-- SQLite >= 3.25 supports `ALTER TABLE ... RENAME COLUMN`, which rewrites the
|
|
37
|
-
-- stored DDL in place, automatically updating the column's appearance in the
|
|
38
|
-
-- table's PRIMARY KEY and in its own FOREIGN KEY clause. The FTS5 mirror tables
|
|
39
|
-
-- (apps_fts, services_fts) index text content columns (name, etc.), NOT the FK
|
|
40
|
-
-- column, so they are unaffected. Verified on sqlite 3.45.x: PRAGMA
|
|
41
|
-
-- foreign_key_check returns no rows after the rename and project-keyed INSERTs
|
|
42
|
-
-- succeed.
|
|
43
|
-
--
|
|
44
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT. A failure
|
|
45
|
-
-- mid-flight rolls back to v14 state; the ledger row is NOT inserted, so the
|
|
46
|
-
-- next bootstrap retry sees the same pending migration.
|
|
47
|
-
|
|
48
|
-
-- ---------------------------------------------------------------------------
|
|
49
|
-
-- Step 1: Rename repo -> project on each per-project child table
|
|
50
|
-
-- ---------------------------------------------------------------------------
|
|
51
|
-
ALTER TABLE apps RENAME COLUMN repo TO project;
|
|
52
|
-
ALTER TABLE libraries RENAME COLUMN repo TO project;
|
|
53
|
-
ALTER TABLE services RENAME COLUMN repo TO project;
|
|
54
|
-
ALTER TABLE features RENAME COLUMN repo TO project;
|
|
55
|
-
ALTER TABLE tf_modules RENAME COLUMN repo TO project;
|
|
56
|
-
ALTER TABLE tf_live RENAME COLUMN repo TO project;
|
|
57
|
-
ALTER TABLE releases RENAME COLUMN repo TO project;
|
|
58
|
-
ALTER TABLE workloads RENAME COLUMN repo TO project;
|
|
59
|
-
ALTER TABLE clusters_defined RENAME COLUMN repo TO project;
|
|
60
|
-
|
|
61
|
-
-- ---------------------------------------------------------------------------
|
|
62
|
-
-- Step 2: Bump schema_version to 15
|
|
63
|
-
-- ---------------------------------------------------------------------------
|
|
64
|
-
INSERT OR IGNORE INTO schema_version (version, applied_at, description)
|
|
65
|
-
VALUES (15, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
66
|
-
'rename child-table FK column repo -> project (substrate rename catch-up)');
|
|
67
|
-
|
|
68
|
-
-- Verification queries (run after applying):
|
|
69
|
-
-- SELECT MAX(version) FROM schema_version; -- expect: 15
|
|
70
|
-
-- PRAGMA table_info(apps); -- expect: project (not repo)
|
|
71
|
-
-- PRAGMA foreign_key_check; -- expect: no rows
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
-- Migration v14 -> v15 fresh-install variant
|
|
2
|
-
--
|
|
3
|
-
-- Used by bootstrap_database.sh when the live DB was created directly from
|
|
4
|
-
-- schema.sql at v15 state -- i.e. the per-project child tables (apps, services,
|
|
5
|
-
-- tf_modules, ...) already carry the `project` column because schema.sql
|
|
6
|
-
-- declared them that way.
|
|
7
|
-
--
|
|
8
|
-
-- On a fresh install there is no legacy `repo` column to rename, so the
|
|
9
|
-
-- RENAME COLUMN statements in v14_to_v15.sql would fail with "no such column:
|
|
10
|
-
-- repo". This variant is a no-op; it exists only so the bootstrap guard-probe
|
|
11
|
-
-- branch (Section 3c, case 15) can select it and stamp the ledger without
|
|
12
|
-
-- attempting the rename.
|
|
13
|
-
--
|
|
14
|
-
-- Atomicity: bootstrap_database.sh wraps this script in BEGIN/COMMIT.
|
|
15
|
-
-- No DDL is executed; the COMMIT is harmless.
|
|
16
|
-
|
|
17
|
-
-- No-op: fresh install already at v15 state (schema.sql created child tables
|
|
18
|
-
-- with the `project` column).
|
|
19
|
-
SELECT 1;
|