godcrm/scripts/migrations/20260516-adr0061-p0-budgets-agent-runs.sql
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

113 lines
7.1 KiB
PL/PgSQL

-- ADR-0061 P0 — Runtime budgets + termination_reason + _agent_runs trace
-- Date: 2026-05-16
-- Author: @developer-ralph
-- Doc: 156716 (ADR-0061)
-- Ticket: T-156843
-- Companion rollback: scripts/rollback/adr-0061-p0-down.sql
--
-- Scope:
-- 1. Add `default_budget_json` (json) virtual column on agents (table_id=1784)
-- 2. Create `_agent_runs` virtual table (table_id=100001) with run-trace cols
--
-- Storage model: virtual columns (rows in table_columns), data in table_rows.data JSONB.
-- No physical postgres tables created — matches ADR-0030 P1 convention.
--
-- Idempotent: every INSERT is gated by WHERE NOT EXISTS, safe to re-run.
BEGIN;
-- =============================================================
-- Part 1 — default_budget_json on agents (table_id=1784)
-- Shape: {step_limit, time_limit_ms, tool_call_limit, token_limit?}
-- Nullable, no row-level default. NULL = harness defaults apply.
-- =============================================================
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 1784, 'default_budget_json', 'Default Budget', 'json',
'{"appearance":{"align":"left","indicator":{"type":"emoji","value":"🛡️"}},"cellFormat":{"mode":"markdown","textWrap":"wrap"}}',
200, 0, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=1784 AND column_name='default_budget_json');
-- =============================================================
-- Part 2 — _agent_runs virtual table (table_id=100001)
-- One row per agentLoop run. Holds the trace pulled into the
-- termination chip (run row → chip via send_widget_message).
-- Metadata lives in `universal_tables` (canonical store read by
-- the chip resolver in agent-tools/chat-tools.js).
-- =============================================================
INSERT INTO universal_tables (id, project_id, name, display_name, icon, is_system, show_in_nav, created_at, updated_at)
SELECT 100001, 1, '_agent_runs', 'Agent Runs', '🛡️', 1, 0, NOW(), NOW()
WHERE NOT EXISTS (SELECT 1 FROM universal_tables WHERE id=100001);
-- Keep sequence ahead of explicit id so future auto-assigns don't collide.
SELECT setval('universal_tables_id_seq', GREATEST((SELECT COALESCE(MAX(id),0) FROM universal_tables), 100001), true);
-- conversation_id: which conversation triggered the run (chip target)
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'conversation_id', 'Conversation', 'number', '{}', 0, 1, 1, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='conversation_id');
-- agent_id: agent row id (table 1784)
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'agent_id', 'Agent', 'number', '{}', 1, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='agent_id');
-- ticket_id: ticket row id (table 1708) — non-null for agent-worker dispatched runs
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'ticket_id', 'Ticket', 'number', '{}', 2, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='ticket_id');
-- started_at: ISO timestamp of loop entry
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'started_at', 'Started', 'datetime', '{}', 3, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='started_at');
-- ended_at: ISO timestamp of termination
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'ended_at', 'Ended', 'datetime', '{}', 4, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='ended_at');
-- termination_reason: enum (free-text after colon for out_of_budget:<field>)
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'termination_reason', 'Termination', 'select',
'{"appearance":{"align":"left","indicator":{"type":"emoji","value":"🛑"}},"options":[{"label":"goal_reached"},{"label":"human_stop"},{"label":"out_of_budget"},{"label":"out_of_budget:step_limit"},{"label":"out_of_budget:time_limit_ms"},{"label":"out_of_budget:tool_call_limit"},{"label":"out_of_budget:token_limit"},{"label":"tool_denied"},{"label":"error_unrecoverable"}]}',
5, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='termination_reason');
-- budget_json: snapshot of the effective merged budget for this run
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'budget_json', 'Budget', 'json',
'{"appearance":{"align":"left","indicator":{"type":"emoji","value":"🎯"}},"cellFormat":{"mode":"markdown"}}',
6, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='budget_json');
-- budget_consumed_json: final counter snapshot {steps, time_ms, tool_calls, tokens?}
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'budget_consumed_json', 'Consumed', 'json',
'{"appearance":{"align":"left","indicator":{"type":"emoji","value":"📊"}},"cellFormat":{"mode":"markdown"}}',
7, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='budget_consumed_json');
-- provider: which branch executed (anthropic/openai/claude-code/copilot)
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'provider', 'Provider', 'text', '{}', 8, 1, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='provider');
-- created_at / updated_at (audit)
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'created_at', 'Created', 'datetime', '{}', 9, 0, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='created_at');
INSERT INTO table_columns (table_id, column_name, display_name, type, config, order_index, is_visible, is_required, is_system)
SELECT 100001, 'updated_at', 'Updated', 'datetime', '{}', 10, 0, 0, 0
WHERE NOT EXISTS (SELECT 1 FROM table_columns WHERE table_id=100001 AND column_name='updated_at');
COMMIT;
-- Verification queries (run manually to confirm):
-- SELECT column_name FROM table_columns WHERE table_id=1784 AND column_name='default_budget_json'; -- 1 row
-- SELECT id, name FROM tables WHERE id=100001; -- 1 row
-- SELECT count(*) FROM table_columns WHERE table_id=100001; -- 11 rows