a2e742c78d
- 3 API endpoints: POST /approvals, GET /approvals, POST /approvals/{id}/approve
- State machine: DRAFT → PROPOSED → APPROVED → ACTIVE
- RBAC enforcement: Maker ≠ Checker separation of duties
- Evidence linkage: PBO/DSR/OOS artifact URLs stored
- Schema: Append-only events with correlation_id
- Tests: 5+ unit/integration scenarios
- Documentation: Full API contracts + compliance procedures
- AGENTS.md v16.0 13/13 compliance ✅
Closes workstream H (Phase 2 implementation).
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
66 lines
3.2 KiB
SQL
66 lines
3.2 KiB
SQL
-- Migration 0036: Approval workflow schema (VS-03)
|
|
-- Creates tables for model activation approval gates with maker-checker separation
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'approval_proposals' AND table_schema = 'model_operations')
|
|
BEGIN
|
|
CREATE TABLE model_operations.approval_proposals (
|
|
id UUID PRIMARY KEY,
|
|
model_id UUID NOT NULL REFERENCES model_operations.models(id),
|
|
status VARCHAR(50) NOT NULL,
|
|
created_by VARCHAR(255) NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
justification TEXT NOT NULL,
|
|
effective_at DATE NOT NULL,
|
|
proposed_at TIMESTAMPTZ,
|
|
approved_by VARCHAR(255),
|
|
approved_at TIMESTAMPTZ,
|
|
approval_notes TEXT,
|
|
activated_by VARCHAR(255),
|
|
activated_at TIMESTAMPTZ,
|
|
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
revision INT NOT NULL DEFAULT 1,
|
|
correlation_id UUID NOT NULL
|
|
);
|
|
|
|
CREATE INDEX ix_approval_proposals_model_id ON model_operations.approval_proposals(model_id);
|
|
CREATE INDEX ix_approval_proposals_status ON model_operations.approval_proposals(status);
|
|
CREATE INDEX ix_approval_proposals_created_by ON model_operations.approval_proposals(created_by);
|
|
CREATE INDEX ix_approval_proposals_approved_by ON model_operations.approval_proposals(approved_by);
|
|
CREATE INDEX ix_approval_proposals_correlation_id ON model_operations.approval_proposals(correlation_id);
|
|
END;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'approval_evidence' AND table_schema = 'model_operations')
|
|
BEGIN
|
|
CREATE TABLE model_operations.approval_evidence (
|
|
id UUID PRIMARY KEY,
|
|
approval_proposal_id UUID NOT NULL REFERENCES model_operations.approval_proposals(id),
|
|
evidence_type VARCHAR(50) NOT NULL,
|
|
evidence_url TEXT NOT NULL,
|
|
reviewer_comment TEXT,
|
|
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
correlation_id UUID NOT NULL
|
|
);
|
|
|
|
CREATE INDEX ix_approval_evidence_proposal_id ON model_operations.approval_evidence(approval_proposal_id);
|
|
CREATE INDEX ix_approval_evidence_type ON model_operations.approval_evidence(evidence_type);
|
|
CREATE INDEX ix_approval_evidence_correlation_id ON model_operations.approval_evidence(correlation_id);
|
|
END;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'approval_events' AND table_schema = 'model_operations')
|
|
BEGIN
|
|
CREATE TABLE model_operations.approval_events (
|
|
id UUID PRIMARY KEY,
|
|
approval_proposal_id UUID NOT NULL REFERENCES model_operations.approval_proposals(id),
|
|
event_type VARCHAR(50) NOT NULL,
|
|
actor_email VARCHAR(255) NOT NULL,
|
|
event_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
details JSONB,
|
|
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
correlation_id UUID NOT NULL
|
|
);
|
|
|
|
CREATE INDEX ix_approval_events_proposal_id ON model_operations.approval_events(approval_proposal_id);
|
|
CREATE INDEX ix_approval_events_type ON model_operations.approval_events(event_type);
|
|
CREATE INDEX ix_approval_events_correlation_id ON model_operations.approval_events(correlation_id);
|
|
END;
|