88 lines
4.1 KiB
MySQL
88 lines
4.1 KiB
MySQL
|
|
-- IT Glue asset audit + write-back tables.
|
||
|
|
-- See docs/wulf-pulse-ticket-analyzer-build-notes.md → Phase 4.
|
||
|
|
--
|
||
|
|
-- Two tables:
|
||
|
|
-- 1. itglue_asset_audits — one row per audit run (LLM gap analysis).
|
||
|
|
-- 2. itglue_writes — one row per write attempt (one field, one moment),
|
||
|
|
-- with provenance back to the audit that prompted it.
|
||
|
|
--
|
||
|
|
-- A separate generic audit_log row is also written by the API layer
|
||
|
|
-- (lib/services/audit.ts) so /admin/audit-log surfaces every IT Glue change
|
||
|
|
-- alongside other admin actions. The two domain-specific tables here exist
|
||
|
|
-- because:
|
||
|
|
-- - audits accumulate full LLM context snapshots (heavy, infrequent),
|
||
|
|
-- - writes are atomic per-field decisions with before/after diffs that the
|
||
|
|
-- generic audit_log's free-form JSONB doesn't capture cleanly.
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS itglue_asset_audits (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
asset_type TEXT NOT NULL CHECK (asset_type IN ('flexible_asset')),
|
||
|
|
asset_id BIGINT NOT NULL,
|
||
|
|
asset_type_id BIGINT,
|
||
|
|
organization_id BIGINT,
|
||
|
|
generated_by_user_id TEXT REFERENCES "user"(id) ON DELETE SET NULL,
|
||
|
|
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
provider TEXT NOT NULL DEFAULT 'anthropic'
|
||
|
|
CHECK (provider IN ('anthropic','openrouter')),
|
||
|
|
model_used TEXT,
|
||
|
|
asset_snapshot JSONB NOT NULL,
|
||
|
|
ticket_count INT NOT NULL,
|
||
|
|
field_gaps JSONB NOT NULL,
|
||
|
|
notes_promotions JSONB NOT NULL,
|
||
|
|
contradictions JSONB NOT NULL,
|
||
|
|
overall_score NUMERIC(3,2),
|
||
|
|
estimated_cost_usd NUMERIC(10,4),
|
||
|
|
total_input_tokens INT,
|
||
|
|
total_output_tokens INT,
|
||
|
|
status TEXT NOT NULL DEFAULT 'complete'
|
||
|
|
CHECK (status IN ('pending','running','complete','failed')),
|
||
|
|
error_message TEXT
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS ix_itglue_asset_audits_asset
|
||
|
|
ON itglue_asset_audits (asset_type, asset_id, generated_at DESC);
|
||
|
|
CREATE INDEX IF NOT EXISTS ix_itglue_asset_audits_org
|
||
|
|
ON itglue_asset_audits (organization_id, generated_at DESC);
|
||
|
|
|
||
|
|
COMMENT ON TABLE itglue_asset_audits IS
|
||
|
|
'One row per LLM-driven audit of an IT Glue asset against ticket history. '
|
||
|
|
'Captures the full input snapshot (asset traits at the time, redacted), the '
|
||
|
|
'extracted gaps/promotions/contradictions, and cost. Forever-retained.';
|
||
|
|
|
||
|
|
COMMENT ON COLUMN itglue_asset_audits.field_gaps IS
|
||
|
|
'JSON array: [{ field_name, why_missing_matters, suggested_value, '
|
||
|
|
'evidence_ticket_numbers, confidence }]';
|
||
|
|
COMMENT ON COLUMN itglue_asset_audits.notes_promotions IS
|
||
|
|
'JSON array: [{ quoted_note_text, target_field, suggested_value, confidence }]';
|
||
|
|
COMMENT ON COLUMN itglue_asset_audits.contradictions IS
|
||
|
|
'JSON array: [{ description, evidence }]';
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS itglue_writes (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
audit_id UUID REFERENCES itglue_asset_audits(id) ON DELETE SET NULL,
|
||
|
|
asset_type TEXT NOT NULL CHECK (asset_type IN ('flexible_asset')),
|
||
|
|
asset_id BIGINT NOT NULL,
|
||
|
|
field_name TEXT NOT NULL,
|
||
|
|
before_value JSONB,
|
||
|
|
after_value JSONB NOT NULL,
|
||
|
|
performed_by_user_id TEXT REFERENCES "user"(id) ON DELETE SET NULL,
|
||
|
|
performed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
status TEXT NOT NULL
|
||
|
|
CHECK (status IN ('pending','committed','failed','reverted')),
|
||
|
|
itglue_response JSONB,
|
||
|
|
error_message TEXT,
|
||
|
|
source_evidence JSONB
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS ix_itglue_writes_asset
|
||
|
|
ON itglue_writes (asset_type, asset_id, performed_at DESC);
|
||
|
|
CREATE INDEX IF NOT EXISTS ix_itglue_writes_user
|
||
|
|
ON itglue_writes (performed_by_user_id, performed_at DESC);
|
||
|
|
CREATE INDEX IF NOT EXISTS ix_itglue_writes_audit
|
||
|
|
ON itglue_writes (audit_id);
|
||
|
|
|
||
|
|
COMMENT ON TABLE itglue_writes IS
|
||
|
|
'One row per write attempt to IT Glue. Pending → committed | failed | reverted. '
|
||
|
|
'Reverts produce a new row whose before_value/after_value swap the original; '
|
||
|
|
'the original row gets status=reverted. Forever-retained.';
|