feat(governance): Governance Engine MVP implementation

- Backend:
  - Migration 032: agent_gov_level, status, incidents, permissions tables
  - Domain types for governance layer
  - Permission Engine with all governance checks
  - Governance Service (promote/demote/roles)
  - Revocation Service (revoke/suspend/reinstate)
  - Audit Service (events filtering and stats)
  - Incidents Service (create/assign/escalate/resolve)
  - REST API routes for governance, audit, incidents

- Frontend:
  - TypeScript types for governance
  - API clients for governance, audit, incidents
  - GovernanceLevelBadge component
  - CityGovernancePanel component
  - AuditDashboard component
  - IncidentsList component with detail modal

Based on: Agent_Governance_Protocol_v1.md
This commit is contained in:
Apple
2025-11-29 16:02:06 -08:00
parent 2627205663
commit e233d32ae7
20 changed files with 5837 additions and 0 deletions

View File

@@ -0,0 +1,257 @@
-- Migration 032: Governance Engine
-- Purpose: Implement Governance Layer for DAARION.city
-- Based on: docs/foundation/Agent_Governance_Protocol_v1.md
-- Date: 2025-11-29
-- Status: MVP Feature
-- ============================================================================
-- 0. ENUM TYPES
-- ============================================================================
-- Agent governance level (0-7 hierarchy)
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'agent_gov_level') THEN
CREATE TYPE agent_gov_level AS ENUM (
'guest', -- Level 0
'personal', -- Level 1
'member', -- Level 2
'worker', -- Level 3
'core_team', -- Level 4
'orchestrator', -- Level 5
'district_lead', -- Level 6
'city_governance' -- Level 7
);
END IF;
END $$;
-- Agent status
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'agent_status') THEN
CREATE TYPE agent_status AS ENUM ('active', 'suspended', 'revoked');
END IF;
END $$;
-- Revocation type
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'revocation_type') THEN
CREATE TYPE revocation_type AS ENUM ('soft', 'hard', 'shadow');
END IF;
END $$;
-- Incident status
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'incident_status') THEN
CREATE TYPE incident_status AS ENUM ('open', 'in_progress', 'resolved', 'closed');
END IF;
END $$;
-- Incident priority
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'incident_priority') THEN
CREATE TYPE incident_priority AS ENUM ('low', 'medium', 'high', 'critical');
END IF;
END $$;
-- Incident escalation level
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'escalation_level') THEN
CREATE TYPE escalation_level AS ENUM ('microdao', 'district', 'city');
END IF;
END $$;
-- Target scope type for incidents
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'target_scope_type') THEN
CREATE TYPE target_scope_type AS ENUM ('city', 'district', 'microdao', 'room', 'node', 'agent');
END IF;
END $$;
-- Permission action
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'permission_action') THEN
CREATE TYPE permission_action AS ENUM ('read', 'write', 'moderate', 'admin', 'superadmin');
END IF;
END $$;
-- ============================================================================
-- 1. AGENTS TABLE UPDATE - Governance Fields
-- ============================================================================
-- Add governance fields to agents
ALTER TABLE agents ADD COLUMN IF NOT EXISTS gov_level agent_gov_level DEFAULT 'personal';
ALTER TABLE agents ADD COLUMN IF NOT EXISTS status agent_status DEFAULT 'active';
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revoked_by TEXT;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revocation_reason TEXT;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS revocation_type revocation_type;
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_agents_gov_level ON agents(gov_level);
CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
CREATE INDEX IF NOT EXISTS idx_agents_revoked ON agents(revoked_at) WHERE revoked_at IS NOT NULL;
-- Migrate existing agent_role to gov_level
UPDATE agents
SET gov_level = 'orchestrator'::agent_gov_level
WHERE agent_role = 'orchestrator' AND gov_level = 'personal';
-- Set city governance agents
UPDATE agents
SET gov_level = 'city_governance'::agent_gov_level
WHERE id IN ('daarwizz', 'dario', 'daria');
-- ============================================================================
-- 2. INCIDENTS TABLE
-- ============================================================================
CREATE TABLE IF NOT EXISTS incidents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_by_dais_id TEXT NOT NULL,
target_scope_type target_scope_type NOT NULL,
target_scope_id TEXT NOT NULL,
status incident_status NOT NULL DEFAULT 'open',
priority incident_priority NOT NULL DEFAULT 'medium',
assigned_to_dais_id TEXT,
escalation_level escalation_level NOT NULL DEFAULT 'microdao',
title TEXT NOT NULL,
description TEXT,
resolution TEXT,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
resolved_at TIMESTAMPTZ,
closed_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_incidents_status ON incidents(status);
CREATE INDEX IF NOT EXISTS idx_incidents_priority ON incidents(priority);
CREATE INDEX IF NOT EXISTS idx_incidents_escalation ON incidents(escalation_level);
CREATE INDEX IF NOT EXISTS idx_incidents_created_by ON incidents(created_by_dais_id);
CREATE INDEX IF NOT EXISTS idx_incidents_assigned ON incidents(assigned_to_dais_id);
CREATE INDEX IF NOT EXISTS idx_incidents_target ON incidents(target_scope_type, target_scope_id);
CREATE INDEX IF NOT EXISTS idx_incidents_open ON incidents(status, priority) WHERE status IN ('open', 'in_progress');
-- ============================================================================
-- 3. INCIDENT HISTORY TABLE
-- ============================================================================
CREATE TABLE IF NOT EXISTS incident_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
incident_id UUID NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
action TEXT NOT NULL, -- created, assigned, escalated, resolved, closed, comment
actor_dais_id TEXT NOT NULL,
old_value JSONB,
new_value JSONB,
comment TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_incident_history_incident ON incident_history(incident_id);
CREATE INDEX IF NOT EXISTS idx_incident_history_actor ON incident_history(actor_dais_id);
-- ============================================================================
-- 4. PERMISSIONS TABLE
-- ============================================================================
CREATE TABLE IF NOT EXISTS permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
dais_id TEXT NOT NULL,
target_type TEXT NOT NULL, -- room, microdao, node, district, city
target_id TEXT NOT NULL,
action permission_action NOT NULL,
granted_by TEXT NOT NULL,
expires_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(dais_id, target_type, target_id, action)
);
CREATE INDEX IF NOT EXISTS idx_permissions_dais ON permissions(dais_id);
CREATE INDEX IF NOT EXISTS idx_permissions_target ON permissions(target_type, target_id);
CREATE INDEX IF NOT EXISTS idx_permissions_valid ON permissions(dais_id)
WHERE expires_at IS NULL OR expires_at > now();
-- ============================================================================
-- 5. REVOCATIONS TABLE (Audit trail for revocations)
-- ============================================================================
CREATE TABLE IF NOT EXISTS agent_revocations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
agent_id TEXT NOT NULL REFERENCES agents(id),
dais_id TEXT,
revoked_by TEXT NOT NULL,
revocation_type revocation_type NOT NULL,
reason TEXT NOT NULL,
scope TEXT NOT NULL, -- city, district:<id>, microdao:<id>
keys_invalidated BOOLEAN NOT NULL DEFAULT true,
wallet_disabled BOOLEAN NOT NULL DEFAULT true,
room_access_revoked BOOLEAN NOT NULL DEFAULT true,
node_privileges_removed BOOLEAN NOT NULL DEFAULT true,
assignments_terminated BOOLEAN NOT NULL DEFAULT true,
reversible BOOLEAN NOT NULL DEFAULT true,
reversed_at TIMESTAMPTZ,
reversed_by TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_revocations_agent ON agent_revocations(agent_id);
CREATE INDEX IF NOT EXISTS idx_revocations_dais ON agent_revocations(dais_id);
CREATE INDEX IF NOT EXISTS idx_revocations_type ON agent_revocations(revocation_type);
-- ============================================================================
-- 6. UPDATE EVENT_OUTBOX WITH NEW GOVERNANCE EVENTS
-- ============================================================================
-- Add actor_id column to event_outbox for audit
ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS actor_id TEXT;
ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS target_id TEXT;
ALTER TABLE event_outbox ADD COLUMN IF NOT EXISTS scope TEXT;
CREATE INDEX IF NOT EXISTS idx_outbox_actor ON event_outbox(actor_id);
CREATE INDEX IF NOT EXISTS idx_outbox_target ON event_outbox(target_id);
CREATE INDEX IF NOT EXISTS idx_outbox_scope ON event_outbox(scope);
-- ============================================================================
-- 7. DAIS KEYS - Add revoked flag
-- ============================================================================
ALTER TABLE dais_keys ADD COLUMN IF NOT EXISTS revoked BOOLEAN DEFAULT false;
ALTER TABLE dais_keys ADD COLUMN IF NOT EXISTS revoked_reason TEXT;
ALTER TABLE dais_keys ADD COLUMN IF NOT EXISTS revoked_by TEXT;
-- Migrate existing revoked_at to revoked
UPDATE dais_keys SET revoked = true WHERE revoked_at IS NOT NULL AND revoked = false;
-- ============================================================================
-- 8. COMMENTS
-- ============================================================================
COMMENT ON TABLE incidents IS 'Incident tracking for governance escalation';
COMMENT ON TABLE incident_history IS 'Audit trail for incident changes';
COMMENT ON TABLE permissions IS 'Explicit permissions for DAIS identities';
COMMENT ON TABLE agent_revocations IS 'Audit trail for agent revocations';
COMMENT ON COLUMN agents.gov_level IS 'Governance level: 0=guest to 7=city_governance';
COMMENT ON COLUMN agents.status IS 'Agent status: active, suspended, or revoked';
COMMENT ON COLUMN agents.revoked_at IS 'Timestamp when agent was revoked';
COMMENT ON COLUMN agents.revocation_type IS 'Type of revocation: soft, hard, or shadow';
COMMENT ON COLUMN incidents.escalation_level IS 'Current escalation: microdao → district → city';
COMMENT ON COLUMN incidents.target_scope_type IS 'What the incident is about: city, district, microdao, room, node, agent';
-- ============================================================================
-- 9. SEED DATA - City Governance Agents
-- ============================================================================
-- Ensure city governance agents have correct level
UPDATE agents
SET gov_level = 'city_governance'::agent_gov_level,
status = 'active'::agent_status
WHERE id IN ('daarwizz', 'dario', 'daria');
-- ============================================================================
-- DONE
-- ============================================================================
SELECT 'Migration 032 completed: Governance Engine' as result;