@plazmodium/odin 0.3.2-beta
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/README.md +306 -0
- package/dist/adapters/archive/supabase.d.ts +19 -0
- package/dist/adapters/archive/supabase.d.ts.map +1 -0
- package/dist/adapters/archive/supabase.js +121 -0
- package/dist/adapters/archive/supabase.js.map +1 -0
- package/dist/adapters/archive/types.d.ts +26 -0
- package/dist/adapters/archive/types.d.ts.map +1 -0
- package/dist/adapters/archive/types.js +6 -0
- package/dist/adapters/archive/types.js.map +1 -0
- package/dist/adapters/formal-verification/tla-precheck.d.ts +22 -0
- package/dist/adapters/formal-verification/tla-precheck.d.ts.map +1 -0
- package/dist/adapters/formal-verification/tla-precheck.js +270 -0
- package/dist/adapters/formal-verification/tla-precheck.js.map +1 -0
- package/dist/adapters/formal-verification/types.d.ts +37 -0
- package/dist/adapters/formal-verification/types.d.ts.map +1 -0
- package/dist/adapters/formal-verification/types.js +6 -0
- package/dist/adapters/formal-verification/types.js.map +1 -0
- package/dist/adapters/review/semgrep.d.ts +12 -0
- package/dist/adapters/review/semgrep.d.ts.map +1 -0
- package/dist/adapters/review/semgrep.js +175 -0
- package/dist/adapters/review/semgrep.js.map +1 -0
- package/dist/adapters/review/types.d.ts +14 -0
- package/dist/adapters/review/types.d.ts.map +1 -0
- package/dist/adapters/review/types.js +6 -0
- package/dist/adapters/review/types.js.map +1 -0
- package/dist/adapters/skills/filesystem.d.ts +18 -0
- package/dist/adapters/skills/filesystem.d.ts.map +1 -0
- package/dist/adapters/skills/filesystem.js +398 -0
- package/dist/adapters/skills/filesystem.js.map +1 -0
- package/dist/adapters/skills/types.d.ts +19 -0
- package/dist/adapters/skills/types.d.ts.map +1 -0
- package/dist/adapters/skills/types.js +6 -0
- package/dist/adapters/skills/types.js.map +1 -0
- package/dist/adapters/sql-executor/direct-postgres.d.ts +15 -0
- package/dist/adapters/sql-executor/direct-postgres.d.ts.map +1 -0
- package/dist/adapters/sql-executor/direct-postgres.js +33 -0
- package/dist/adapters/sql-executor/direct-postgres.js.map +1 -0
- package/dist/adapters/sql-executor/supabase-management-api.d.ts +17 -0
- package/dist/adapters/sql-executor/supabase-management-api.d.ts.map +1 -0
- package/dist/adapters/sql-executor/supabase-management-api.js +40 -0
- package/dist/adapters/sql-executor/supabase-management-api.js.map +1 -0
- package/dist/adapters/sql-executor/types.d.ts +15 -0
- package/dist/adapters/sql-executor/types.d.ts.map +1 -0
- package/dist/adapters/sql-executor/types.js +6 -0
- package/dist/adapters/sql-executor/types.js.map +1 -0
- package/dist/adapters/workflow-state/in-memory.d.ts +69 -0
- package/dist/adapters/workflow-state/in-memory.d.ts.map +1 -0
- package/dist/adapters/workflow-state/in-memory.js +444 -0
- package/dist/adapters/workflow-state/in-memory.js.map +1 -0
- package/dist/adapters/workflow-state/supabase.d.ts +55 -0
- package/dist/adapters/workflow-state/supabase.d.ts.map +1 -0
- package/dist/adapters/workflow-state/supabase.js +823 -0
- package/dist/adapters/workflow-state/supabase.js.map +1 -0
- package/dist/adapters/workflow-state/types.d.ts +55 -0
- package/dist/adapters/workflow-state/types.d.ts.map +1 -0
- package/dist/adapters/workflow-state/types.js +6 -0
- package/dist/adapters/workflow-state/types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +115 -0
- package/dist/config.js.map +1 -0
- package/dist/domain/actors.d.ts +10 -0
- package/dist/domain/actors.d.ts.map +1 -0
- package/dist/domain/actors.js +60 -0
- package/dist/domain/actors.js.map +1 -0
- package/dist/domain/development-evals.d.ts +9 -0
- package/dist/domain/development-evals.d.ts.map +1 -0
- package/dist/domain/development-evals.js +164 -0
- package/dist/domain/development-evals.js.map +1 -0
- package/dist/domain/matching.d.ts +8 -0
- package/dist/domain/matching.d.ts.map +1 -0
- package/dist/domain/matching.js +24 -0
- package/dist/domain/matching.js.map +1 -0
- package/dist/domain/phases.d.ts +10 -0
- package/dist/domain/phases.d.ts.map +1 -0
- package/dist/domain/phases.js +165 -0
- package/dist/domain/phases.js.map +1 -0
- package/dist/domain/quality-gates.d.ts +7 -0
- package/dist/domain/quality-gates.d.ts.map +1 -0
- package/dist/domain/quality-gates.js +8 -0
- package/dist/domain/quality-gates.js.map +1 -0
- package/dist/domain/resonance.d.ts +33 -0
- package/dist/domain/resonance.d.ts.map +1 -0
- package/dist/domain/resonance.js +100 -0
- package/dist/domain/resonance.js.map +1 -0
- package/dist/domain/tasks.d.ts +9 -0
- package/dist/domain/tasks.d.ts.map +1 -0
- package/dist/domain/tasks.js +57 -0
- package/dist/domain/tasks.js.map +1 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +387 -0
- package/dist/init.js.map +1 -0
- package/dist/schemas.d.ts +366 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +184 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +243 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/apply-migrations.d.ts +21 -0
- package/dist/tools/apply-migrations.d.ts.map +1 -0
- package/dist/tools/apply-migrations.js +286 -0
- package/dist/tools/apply-migrations.js.map +1 -0
- package/dist/tools/archive-feature-release.d.ts +13 -0
- package/dist/tools/archive-feature-release.d.ts.map +1 -0
- package/dist/tools/archive-feature-release.js +182 -0
- package/dist/tools/archive-feature-release.js.map +1 -0
- package/dist/tools/capture-learning.d.ts +9 -0
- package/dist/tools/capture-learning.d.ts.map +1 -0
- package/dist/tools/capture-learning.js +53 -0
- package/dist/tools/capture-learning.js.map +1 -0
- package/dist/tools/explore-knowledge.d.ts +9 -0
- package/dist/tools/explore-knowledge.d.ts.map +1 -0
- package/dist/tools/explore-knowledge.js +142 -0
- package/dist/tools/explore-knowledge.js.map +1 -0
- package/dist/tools/get-claims-needing-review.d.ts +8 -0
- package/dist/tools/get-claims-needing-review.d.ts.map +1 -0
- package/dist/tools/get-claims-needing-review.js +21 -0
- package/dist/tools/get-claims-needing-review.js.map +1 -0
- package/dist/tools/get-development-eval-status.d.ts +8 -0
- package/dist/tools/get-development-eval-status.d.ts.map +1 -0
- package/dist/tools/get-development-eval-status.js +49 -0
- package/dist/tools/get-development-eval-status.js.map +1 -0
- package/dist/tools/get-feature-status.d.ts +8 -0
- package/dist/tools/get-feature-status.d.ts.map +1 -0
- package/dist/tools/get-feature-status.js +68 -0
- package/dist/tools/get-feature-status.js.map +1 -0
- package/dist/tools/get-next-phase.d.ts +8 -0
- package/dist/tools/get-next-phase.d.ts.map +1 -0
- package/dist/tools/get-next-phase.js +26 -0
- package/dist/tools/get-next-phase.js.map +1 -0
- package/dist/tools/prepare-phase-context.d.ts +9 -0
- package/dist/tools/prepare-phase-context.d.ts.map +1 -0
- package/dist/tools/prepare-phase-context.js +151 -0
- package/dist/tools/prepare-phase-context.js.map +1 -0
- package/dist/tools/record-commit.d.ts +8 -0
- package/dist/tools/record-commit.d.ts.map +1 -0
- package/dist/tools/record-commit.js +28 -0
- package/dist/tools/record-commit.js.map +1 -0
- package/dist/tools/record-eval-plan.d.ts +8 -0
- package/dist/tools/record-eval-plan.d.ts.map +1 -0
- package/dist/tools/record-eval-plan.js +40 -0
- package/dist/tools/record-eval-plan.js.map +1 -0
- package/dist/tools/record-eval-run.d.ts +8 -0
- package/dist/tools/record-eval-run.d.ts.map +1 -0
- package/dist/tools/record-eval-run.js +42 -0
- package/dist/tools/record-eval-run.js.map +1 -0
- package/dist/tools/record-merge.d.ts +8 -0
- package/dist/tools/record-merge.d.ts.map +1 -0
- package/dist/tools/record-merge.js +16 -0
- package/dist/tools/record-merge.js.map +1 -0
- package/dist/tools/record-phase-artifact.d.ts +8 -0
- package/dist/tools/record-phase-artifact.d.ts.map +1 -0
- package/dist/tools/record-phase-artifact.js +26 -0
- package/dist/tools/record-phase-artifact.js.map +1 -0
- package/dist/tools/record-phase-result.d.ts +9 -0
- package/dist/tools/record-phase-result.d.ts.map +1 -0
- package/dist/tools/record-phase-result.js +122 -0
- package/dist/tools/record-phase-result.js.map +1 -0
- package/dist/tools/record-pull-request.d.ts +8 -0
- package/dist/tools/record-pull-request.d.ts.map +1 -0
- package/dist/tools/record-pull-request.js +16 -0
- package/dist/tools/record-pull-request.js.map +1 -0
- package/dist/tools/record-quality-gate.d.ts +8 -0
- package/dist/tools/record-quality-gate.d.ts.map +1 -0
- package/dist/tools/record-quality-gate.js +26 -0
- package/dist/tools/record-quality-gate.js.map +1 -0
- package/dist/tools/record-watcher-review.d.ts +8 -0
- package/dist/tools/record-watcher-review.d.ts.map +1 -0
- package/dist/tools/record-watcher-review.js +18 -0
- package/dist/tools/record-watcher-review.js.map +1 -0
- package/dist/tools/run-policy-checks.d.ts +8 -0
- package/dist/tools/run-policy-checks.d.ts.map +1 -0
- package/dist/tools/run-policy-checks.js +38 -0
- package/dist/tools/run-policy-checks.js.map +1 -0
- package/dist/tools/run-review-checks.d.ts +9 -0
- package/dist/tools/run-review-checks.d.ts.map +1 -0
- package/dist/tools/run-review-checks.js +45 -0
- package/dist/tools/run-review-checks.js.map +1 -0
- package/dist/tools/start-feature.d.ts +8 -0
- package/dist/tools/start-feature.d.ts.map +1 -0
- package/dist/tools/start-feature.js +33 -0
- package/dist/tools/start-feature.js.map +1 -0
- package/dist/tools/submit-claim.d.ts +8 -0
- package/dist/tools/submit-claim.d.ts.map +1 -0
- package/dist/tools/submit-claim.js +45 -0
- package/dist/tools/submit-claim.js.map +1 -0
- package/dist/tools/verify-claims.d.ts +8 -0
- package/dist/tools/verify-claims.d.ts.map +1 -0
- package/dist/tools/verify-claims.js +39 -0
- package/dist/tools/verify-claims.js.map +1 -0
- package/dist/tools/verify-design.d.ts +8 -0
- package/dist/tools/verify-design.d.ts.map +1 -0
- package/dist/tools/verify-design.js +31 -0
- package/dist/tools/verify-design.js.map +1 -0
- package/dist/types.d.ts +333 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +52 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +50 -0
- package/dist/utils.js.map +1 -0
- package/migrations/001_schema.sql +795 -0
- package/migrations/002_functions.sql +2126 -0
- package/migrations/003_views.sql +599 -0
- package/migrations/004_seed.sql +106 -0
- package/migrations/005_odin_v2_schema.sql +217 -0
- package/migrations/006_odin_v2_functions.sql +671 -0
- package/migrations/007_odin_v2_phase_alignment.sql +554 -0
- package/migrations/008_related_learnings.sql +80 -0
- package/migrations/README.md +23 -0
- package/package.json +63 -0
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Odin v2 Functions
|
|
3
|
+
-- Version: 2.0.0
|
|
4
|
+
-- Created: 2026-03-05
|
|
5
|
+
-- Description: Functions for Odin v2 features:
|
|
6
|
+
-- - Agent claims submission and verification
|
|
7
|
+
-- - Policy engine (deterministic checks)
|
|
8
|
+
-- - Watcher reviews (LLM escalation)
|
|
9
|
+
-- - Security findings management
|
|
10
|
+
--
|
|
11
|
+
-- Dependencies: Requires 005_odin_v2_schema.sql to be run first
|
|
12
|
+
--
|
|
13
|
+
-- IMPORTANT: DO NOT RUN ON SUPABASE UNTIL READY FOR DEPLOYMENT
|
|
14
|
+
-- ============================================================================
|
|
15
|
+
|
|
16
|
+
-- ============================================================================
|
|
17
|
+
-- AGENT CLAIMS FUNCTIONS
|
|
18
|
+
-- ============================================================================
|
|
19
|
+
|
|
20
|
+
-- Submit a claim from an agent
|
|
21
|
+
-- Used by Builder, Integrator, Release agents to assert work performed
|
|
22
|
+
CREATE OR REPLACE FUNCTION submit_claim(
|
|
23
|
+
p_feature_id TEXT,
|
|
24
|
+
p_phase phase,
|
|
25
|
+
p_agent_name TEXT,
|
|
26
|
+
p_claim_type claim_type,
|
|
27
|
+
p_description TEXT,
|
|
28
|
+
p_evidence_refs JSONB DEFAULT '{}'::jsonb,
|
|
29
|
+
p_risk_level TEXT DEFAULT 'LOW',
|
|
30
|
+
p_invocation_id UUID DEFAULT NULL
|
|
31
|
+
)
|
|
32
|
+
RETURNS TABLE (
|
|
33
|
+
claim_id UUID,
|
|
34
|
+
feature_id TEXT,
|
|
35
|
+
phase phase,
|
|
36
|
+
claim_type claim_type,
|
|
37
|
+
risk_level TEXT,
|
|
38
|
+
created_at TIMESTAMPTZ
|
|
39
|
+
)
|
|
40
|
+
LANGUAGE plpgsql
|
|
41
|
+
SET search_path = public
|
|
42
|
+
AS $$
|
|
43
|
+
DECLARE
|
|
44
|
+
v_claim_id UUID;
|
|
45
|
+
v_created_at TIMESTAMPTZ;
|
|
46
|
+
BEGIN
|
|
47
|
+
-- Validate risk level
|
|
48
|
+
IF p_risk_level NOT IN ('LOW', 'MEDIUM', 'HIGH') THEN
|
|
49
|
+
RAISE EXCEPTION 'Invalid risk_level: %. Must be LOW, MEDIUM, or HIGH.', p_risk_level;
|
|
50
|
+
END IF;
|
|
51
|
+
|
|
52
|
+
-- Insert claim
|
|
53
|
+
INSERT INTO agent_claims (
|
|
54
|
+
feature_id, phase, agent_name, claim_type,
|
|
55
|
+
claim_description, evidence_refs, risk_level, invocation_id
|
|
56
|
+
) VALUES (
|
|
57
|
+
p_feature_id, p_phase, p_agent_name, p_claim_type,
|
|
58
|
+
p_description, p_evidence_refs, p_risk_level, p_invocation_id
|
|
59
|
+
)
|
|
60
|
+
RETURNING id, agent_claims.created_at INTO v_claim_id, v_created_at;
|
|
61
|
+
|
|
62
|
+
-- Log to audit
|
|
63
|
+
INSERT INTO audit_log (feature_id, operation, agent_name, details)
|
|
64
|
+
VALUES (p_feature_id, 'CLAIM_SUBMITTED', p_agent_name, jsonb_build_object(
|
|
65
|
+
'table_name', 'agent_claims',
|
|
66
|
+
'record_id', v_claim_id::text,
|
|
67
|
+
'phase', p_phase,
|
|
68
|
+
'claim_type', p_claim_type,
|
|
69
|
+
'risk_level', p_risk_level
|
|
70
|
+
));
|
|
71
|
+
|
|
72
|
+
RETURN QUERY SELECT v_claim_id, p_feature_id, p_phase, p_claim_type, p_risk_level, v_created_at;
|
|
73
|
+
END;
|
|
74
|
+
$$;
|
|
75
|
+
|
|
76
|
+
COMMENT ON FUNCTION submit_claim IS 'Submit a structured claim from an agent. Returns the created claim record.';
|
|
77
|
+
|
|
78
|
+
-- Get all claims for a feature
|
|
79
|
+
CREATE OR REPLACE FUNCTION get_feature_claims(p_feature_id TEXT)
|
|
80
|
+
RETURNS TABLE (
|
|
81
|
+
claim_id UUID,
|
|
82
|
+
phase phase,
|
|
83
|
+
agent_name TEXT,
|
|
84
|
+
claim_type claim_type,
|
|
85
|
+
claim_description TEXT,
|
|
86
|
+
evidence_refs JSONB,
|
|
87
|
+
risk_level TEXT,
|
|
88
|
+
policy_verdict verification_status,
|
|
89
|
+
watcher_verdict verification_status,
|
|
90
|
+
final_status TEXT,
|
|
91
|
+
created_at TIMESTAMPTZ
|
|
92
|
+
)
|
|
93
|
+
LANGUAGE plpgsql
|
|
94
|
+
SET search_path = public
|
|
95
|
+
AS $$
|
|
96
|
+
BEGIN
|
|
97
|
+
RETURN QUERY
|
|
98
|
+
SELECT
|
|
99
|
+
ac.id AS claim_id,
|
|
100
|
+
ac.phase,
|
|
101
|
+
ac.agent_name,
|
|
102
|
+
ac.claim_type,
|
|
103
|
+
ac.claim_description,
|
|
104
|
+
ac.evidence_refs,
|
|
105
|
+
ac.risk_level,
|
|
106
|
+
pv.verdict AS policy_verdict,
|
|
107
|
+
wr.verdict AS watcher_verdict,
|
|
108
|
+
CASE
|
|
109
|
+
WHEN wr.verdict IS NOT NULL THEN wr.verdict::text
|
|
110
|
+
WHEN pv.verdict IS NOT NULL THEN pv.verdict::text
|
|
111
|
+
ELSE 'PENDING'
|
|
112
|
+
END AS final_status,
|
|
113
|
+
ac.created_at
|
|
114
|
+
FROM agent_claims ac
|
|
115
|
+
LEFT JOIN policy_verdicts pv ON ac.id = pv.claim_id
|
|
116
|
+
LEFT JOIN watcher_reviews wr ON ac.id = wr.claim_id
|
|
117
|
+
WHERE ac.feature_id = p_feature_id
|
|
118
|
+
ORDER BY ac.created_at;
|
|
119
|
+
END;
|
|
120
|
+
$$;
|
|
121
|
+
|
|
122
|
+
COMMENT ON FUNCTION get_feature_claims IS 'Get all claims for a feature with their verification status';
|
|
123
|
+
|
|
124
|
+
-- ============================================================================
|
|
125
|
+
-- POLICY ENGINE FUNCTIONS
|
|
126
|
+
-- ============================================================================
|
|
127
|
+
|
|
128
|
+
-- Record a policy verdict
|
|
129
|
+
CREATE OR REPLACE FUNCTION record_policy_verdict(
|
|
130
|
+
p_claim_id UUID,
|
|
131
|
+
p_verdict verification_status,
|
|
132
|
+
p_rule_name TEXT,
|
|
133
|
+
p_reason TEXT DEFAULT NULL,
|
|
134
|
+
p_evidence_checked JSONB DEFAULT '{}'::jsonb
|
|
135
|
+
)
|
|
136
|
+
RETURNS TABLE (
|
|
137
|
+
verdict_id UUID,
|
|
138
|
+
claim_id UUID,
|
|
139
|
+
verdict verification_status,
|
|
140
|
+
rule_name TEXT
|
|
141
|
+
)
|
|
142
|
+
LANGUAGE plpgsql
|
|
143
|
+
SET search_path = public
|
|
144
|
+
AS $$
|
|
145
|
+
DECLARE
|
|
146
|
+
v_verdict_id UUID;
|
|
147
|
+
v_feature_id TEXT;
|
|
148
|
+
v_agent_name TEXT;
|
|
149
|
+
BEGIN
|
|
150
|
+
-- Get claim info for audit
|
|
151
|
+
SELECT ac.feature_id, ac.agent_name INTO v_feature_id, v_agent_name
|
|
152
|
+
FROM agent_claims ac WHERE ac.id = p_claim_id;
|
|
153
|
+
|
|
154
|
+
IF v_feature_id IS NULL THEN
|
|
155
|
+
RAISE EXCEPTION 'Claim not found: %', p_claim_id;
|
|
156
|
+
END IF;
|
|
157
|
+
|
|
158
|
+
-- Insert verdict
|
|
159
|
+
INSERT INTO policy_verdicts (
|
|
160
|
+
claim_id, verdict, rule_name, reason, evidence_checked
|
|
161
|
+
) VALUES (
|
|
162
|
+
p_claim_id, p_verdict, p_rule_name, p_reason, p_evidence_checked
|
|
163
|
+
)
|
|
164
|
+
RETURNING id INTO v_verdict_id;
|
|
165
|
+
|
|
166
|
+
-- Log to audit
|
|
167
|
+
INSERT INTO audit_log (feature_id, operation, agent_name, details)
|
|
168
|
+
VALUES (v_feature_id, 'POLICY_VERDICT', 'policy-engine', jsonb_build_object(
|
|
169
|
+
'table_name', 'policy_verdicts',
|
|
170
|
+
'record_id', v_verdict_id::text,
|
|
171
|
+
'claim_id', p_claim_id,
|
|
172
|
+
'verdict', p_verdict,
|
|
173
|
+
'rule_name', p_rule_name
|
|
174
|
+
));
|
|
175
|
+
|
|
176
|
+
RETURN QUERY SELECT v_verdict_id, p_claim_id, p_verdict, p_rule_name;
|
|
177
|
+
END;
|
|
178
|
+
$$;
|
|
179
|
+
|
|
180
|
+
COMMENT ON FUNCTION record_policy_verdict IS 'Record the result of a deterministic policy check on a claim';
|
|
181
|
+
|
|
182
|
+
-- Policy engine: verify claim has evidence
|
|
183
|
+
-- Returns PASS if evidence present and not high-risk
|
|
184
|
+
-- Returns NEEDS_REVIEW if evidence missing or high-risk
|
|
185
|
+
CREATE OR REPLACE FUNCTION policy_verify_evidence(p_claim_id UUID)
|
|
186
|
+
RETURNS verification_status
|
|
187
|
+
LANGUAGE plpgsql
|
|
188
|
+
SET search_path = public
|
|
189
|
+
AS $$
|
|
190
|
+
DECLARE
|
|
191
|
+
v_claim agent_claims%ROWTYPE;
|
|
192
|
+
v_verdict verification_status;
|
|
193
|
+
v_reason TEXT;
|
|
194
|
+
BEGIN
|
|
195
|
+
SELECT * INTO v_claim FROM agent_claims WHERE id = p_claim_id;
|
|
196
|
+
|
|
197
|
+
IF v_claim IS NULL THEN
|
|
198
|
+
RAISE EXCEPTION 'Claim not found: %', p_claim_id;
|
|
199
|
+
END IF;
|
|
200
|
+
|
|
201
|
+
-- Determine verdict
|
|
202
|
+
IF v_claim.evidence_refs IS NULL OR v_claim.evidence_refs = '{}'::jsonb THEN
|
|
203
|
+
v_verdict := 'NEEDS_REVIEW';
|
|
204
|
+
v_reason := 'Missing evidence references - escalate to watcher';
|
|
205
|
+
ELSIF v_claim.risk_level = 'HIGH' THEN
|
|
206
|
+
v_verdict := 'NEEDS_REVIEW';
|
|
207
|
+
v_reason := 'High risk claim - requires watcher review';
|
|
208
|
+
ELSE
|
|
209
|
+
v_verdict := 'PASS';
|
|
210
|
+
v_reason := 'Evidence references present';
|
|
211
|
+
END IF;
|
|
212
|
+
|
|
213
|
+
-- Record the verdict
|
|
214
|
+
PERFORM record_policy_verdict(
|
|
215
|
+
p_claim_id,
|
|
216
|
+
v_verdict,
|
|
217
|
+
'evidence_check',
|
|
218
|
+
v_reason,
|
|
219
|
+
v_claim.evidence_refs
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
RETURN v_verdict;
|
|
223
|
+
END;
|
|
224
|
+
$$;
|
|
225
|
+
|
|
226
|
+
COMMENT ON FUNCTION policy_verify_evidence IS 'Deterministic check: verify claim has evidence refs. Escalates to watcher if missing or high-risk.';
|
|
227
|
+
|
|
228
|
+
-- Run all policy checks on pending claims for a feature
|
|
229
|
+
CREATE OR REPLACE FUNCTION run_policy_checks(p_feature_id TEXT)
|
|
230
|
+
RETURNS TABLE (
|
|
231
|
+
claim_id UUID,
|
|
232
|
+
claim_type claim_type,
|
|
233
|
+
verdict verification_status,
|
|
234
|
+
needs_watcher BOOLEAN
|
|
235
|
+
)
|
|
236
|
+
LANGUAGE plpgsql
|
|
237
|
+
SET search_path = public
|
|
238
|
+
AS $$
|
|
239
|
+
DECLARE
|
|
240
|
+
v_claim RECORD;
|
|
241
|
+
v_verdict verification_status;
|
|
242
|
+
BEGIN
|
|
243
|
+
FOR v_claim IN
|
|
244
|
+
SELECT ac.id, ac.claim_type
|
|
245
|
+
FROM agent_claims ac
|
|
246
|
+
LEFT JOIN policy_verdicts pv ON ac.id = pv.claim_id
|
|
247
|
+
WHERE ac.feature_id = p_feature_id
|
|
248
|
+
AND pv.id IS NULL -- No verdict yet
|
|
249
|
+
ORDER BY ac.created_at
|
|
250
|
+
LOOP
|
|
251
|
+
-- Run evidence check
|
|
252
|
+
v_verdict := policy_verify_evidence(v_claim.id);
|
|
253
|
+
|
|
254
|
+
claim_id := v_claim.id;
|
|
255
|
+
claim_type := v_claim.claim_type;
|
|
256
|
+
verdict := v_verdict;
|
|
257
|
+
needs_watcher := (v_verdict = 'NEEDS_REVIEW');
|
|
258
|
+
RETURN NEXT;
|
|
259
|
+
END LOOP;
|
|
260
|
+
END;
|
|
261
|
+
$$;
|
|
262
|
+
|
|
263
|
+
COMMENT ON FUNCTION run_policy_checks IS 'Run all pending policy checks for a feature. Returns claims needing watcher review.';
|
|
264
|
+
|
|
265
|
+
-- ============================================================================
|
|
266
|
+
-- WATCHER FUNCTIONS
|
|
267
|
+
-- ============================================================================
|
|
268
|
+
|
|
269
|
+
-- Record a watcher review result
|
|
270
|
+
CREATE OR REPLACE FUNCTION record_watcher_review(
|
|
271
|
+
p_claim_id UUID,
|
|
272
|
+
p_verdict verification_status,
|
|
273
|
+
p_reasoning TEXT,
|
|
274
|
+
p_watcher_agent TEXT,
|
|
275
|
+
p_confidence DECIMAL DEFAULT 0.80
|
|
276
|
+
)
|
|
277
|
+
RETURNS TABLE (
|
|
278
|
+
review_id UUID,
|
|
279
|
+
claim_id UUID,
|
|
280
|
+
verdict verification_status,
|
|
281
|
+
confidence DECIMAL
|
|
282
|
+
)
|
|
283
|
+
LANGUAGE plpgsql
|
|
284
|
+
SET search_path = public
|
|
285
|
+
AS $$
|
|
286
|
+
DECLARE
|
|
287
|
+
v_review_id UUID;
|
|
288
|
+
v_feature_id TEXT;
|
|
289
|
+
BEGIN
|
|
290
|
+
-- Get claim info for audit
|
|
291
|
+
SELECT ac.feature_id INTO v_feature_id
|
|
292
|
+
FROM agent_claims ac WHERE ac.id = p_claim_id;
|
|
293
|
+
|
|
294
|
+
IF v_feature_id IS NULL THEN
|
|
295
|
+
RAISE EXCEPTION 'Claim not found: %', p_claim_id;
|
|
296
|
+
END IF;
|
|
297
|
+
|
|
298
|
+
-- Insert review
|
|
299
|
+
INSERT INTO watcher_reviews (
|
|
300
|
+
claim_id, verdict, confidence, reasoning, watcher_agent
|
|
301
|
+
) VALUES (
|
|
302
|
+
p_claim_id, p_verdict, p_confidence, p_reasoning, p_watcher_agent
|
|
303
|
+
)
|
|
304
|
+
RETURNING id INTO v_review_id;
|
|
305
|
+
|
|
306
|
+
-- Log to audit
|
|
307
|
+
INSERT INTO audit_log (feature_id, operation, agent_name, details)
|
|
308
|
+
VALUES (v_feature_id, 'WATCHER_REVIEW', p_watcher_agent, jsonb_build_object(
|
|
309
|
+
'table_name', 'watcher_reviews',
|
|
310
|
+
'record_id', v_review_id::text,
|
|
311
|
+
'claim_id', p_claim_id,
|
|
312
|
+
'verdict', p_verdict,
|
|
313
|
+
'confidence', p_confidence
|
|
314
|
+
));
|
|
315
|
+
|
|
316
|
+
RETURN QUERY SELECT v_review_id, p_claim_id, p_verdict, p_confidence;
|
|
317
|
+
END;
|
|
318
|
+
$$;
|
|
319
|
+
|
|
320
|
+
COMMENT ON FUNCTION record_watcher_review IS 'Record the result of an LLM watcher review on an escalated claim';
|
|
321
|
+
|
|
322
|
+
-- Get claims needing watcher review
|
|
323
|
+
CREATE OR REPLACE FUNCTION get_claims_needing_review(p_feature_id TEXT DEFAULT NULL)
|
|
324
|
+
RETURNS TABLE (
|
|
325
|
+
claim_id UUID,
|
|
326
|
+
feature_id TEXT,
|
|
327
|
+
phase phase,
|
|
328
|
+
agent_name TEXT,
|
|
329
|
+
claim_type claim_type,
|
|
330
|
+
claim_description TEXT,
|
|
331
|
+
evidence_refs JSONB,
|
|
332
|
+
risk_level TEXT,
|
|
333
|
+
policy_verdict verification_status,
|
|
334
|
+
policy_reason TEXT,
|
|
335
|
+
created_at TIMESTAMPTZ
|
|
336
|
+
)
|
|
337
|
+
LANGUAGE plpgsql
|
|
338
|
+
SET search_path = public
|
|
339
|
+
AS $$
|
|
340
|
+
BEGIN
|
|
341
|
+
RETURN QUERY
|
|
342
|
+
SELECT
|
|
343
|
+
ac.id AS claim_id,
|
|
344
|
+
ac.feature_id,
|
|
345
|
+
ac.phase,
|
|
346
|
+
ac.agent_name,
|
|
347
|
+
ac.claim_type,
|
|
348
|
+
ac.claim_description,
|
|
349
|
+
ac.evidence_refs,
|
|
350
|
+
ac.risk_level,
|
|
351
|
+
pv.verdict AS policy_verdict,
|
|
352
|
+
pv.reason AS policy_reason,
|
|
353
|
+
ac.created_at
|
|
354
|
+
FROM agent_claims ac
|
|
355
|
+
LEFT JOIN policy_verdicts pv ON ac.id = pv.claim_id
|
|
356
|
+
LEFT JOIN watcher_reviews wr ON ac.id = wr.claim_id
|
|
357
|
+
WHERE (pv.verdict = 'NEEDS_REVIEW' OR ac.risk_level = 'HIGH')
|
|
358
|
+
AND wr.id IS NULL -- No watcher review yet
|
|
359
|
+
AND (p_feature_id IS NULL OR ac.feature_id = p_feature_id)
|
|
360
|
+
ORDER BY
|
|
361
|
+
CASE WHEN ac.risk_level = 'HIGH' THEN 0 ELSE 1 END,
|
|
362
|
+
ac.created_at;
|
|
363
|
+
END;
|
|
364
|
+
$$;
|
|
365
|
+
|
|
366
|
+
COMMENT ON FUNCTION get_claims_needing_review IS 'Get claims that need LLM watcher review (escalated or high-risk)';
|
|
367
|
+
|
|
368
|
+
-- Get verification summary for a feature
|
|
369
|
+
CREATE OR REPLACE FUNCTION get_verification_summary(p_feature_id TEXT)
|
|
370
|
+
RETURNS TABLE (
|
|
371
|
+
total_claims INTEGER,
|
|
372
|
+
policy_pass INTEGER,
|
|
373
|
+
policy_fail INTEGER,
|
|
374
|
+
policy_needs_review INTEGER,
|
|
375
|
+
watcher_pass INTEGER,
|
|
376
|
+
watcher_fail INTEGER,
|
|
377
|
+
pending_review INTEGER,
|
|
378
|
+
all_verified BOOLEAN
|
|
379
|
+
)
|
|
380
|
+
LANGUAGE plpgsql
|
|
381
|
+
SET search_path = public
|
|
382
|
+
AS $$
|
|
383
|
+
BEGIN
|
|
384
|
+
RETURN QUERY
|
|
385
|
+
WITH claim_stats AS (
|
|
386
|
+
SELECT
|
|
387
|
+
COUNT(*) AS total,
|
|
388
|
+
COUNT(*) FILTER (WHERE pv.verdict = 'PASS') AS p_pass,
|
|
389
|
+
COUNT(*) FILTER (WHERE pv.verdict = 'FAIL') AS p_fail,
|
|
390
|
+
COUNT(*) FILTER (WHERE pv.verdict = 'NEEDS_REVIEW') AS p_review,
|
|
391
|
+
COUNT(*) FILTER (WHERE wr.verdict = 'PASS') AS w_pass,
|
|
392
|
+
COUNT(*) FILTER (WHERE wr.verdict = 'FAIL') AS w_fail,
|
|
393
|
+
COUNT(*) FILTER (WHERE pv.verdict = 'NEEDS_REVIEW' AND wr.id IS NULL) AS pending
|
|
394
|
+
FROM agent_claims ac
|
|
395
|
+
LEFT JOIN policy_verdicts pv ON ac.id = pv.claim_id
|
|
396
|
+
LEFT JOIN watcher_reviews wr ON ac.id = wr.claim_id
|
|
397
|
+
WHERE ac.feature_id = p_feature_id
|
|
398
|
+
)
|
|
399
|
+
SELECT
|
|
400
|
+
cs.total::INTEGER,
|
|
401
|
+
cs.p_pass::INTEGER,
|
|
402
|
+
cs.p_fail::INTEGER,
|
|
403
|
+
cs.p_review::INTEGER,
|
|
404
|
+
cs.w_pass::INTEGER,
|
|
405
|
+
cs.w_fail::INTEGER,
|
|
406
|
+
cs.pending::INTEGER,
|
|
407
|
+
(cs.p_fail = 0 AND cs.w_fail = 0 AND cs.pending = 0)
|
|
408
|
+
FROM claim_stats cs;
|
|
409
|
+
END;
|
|
410
|
+
$$;
|
|
411
|
+
|
|
412
|
+
COMMENT ON FUNCTION get_verification_summary IS 'Get summary of claim verification status for a feature';
|
|
413
|
+
|
|
414
|
+
-- ============================================================================
|
|
415
|
+
-- SECURITY FINDINGS FUNCTIONS
|
|
416
|
+
-- ============================================================================
|
|
417
|
+
|
|
418
|
+
-- Record a security finding from SAST tool
|
|
419
|
+
CREATE OR REPLACE FUNCTION record_security_finding(
|
|
420
|
+
p_feature_id TEXT,
|
|
421
|
+
p_tool TEXT,
|
|
422
|
+
p_severity finding_severity,
|
|
423
|
+
p_message TEXT,
|
|
424
|
+
p_file_path TEXT DEFAULT NULL,
|
|
425
|
+
p_line_number INTEGER DEFAULT NULL,
|
|
426
|
+
p_rule_id TEXT DEFAULT NULL,
|
|
427
|
+
p_snippet TEXT DEFAULT NULL,
|
|
428
|
+
p_fix_suggestion TEXT DEFAULT NULL,
|
|
429
|
+
p_column_number INTEGER DEFAULT NULL,
|
|
430
|
+
p_end_line INTEGER DEFAULT NULL,
|
|
431
|
+
p_end_column INTEGER DEFAULT NULL
|
|
432
|
+
)
|
|
433
|
+
RETURNS TABLE (
|
|
434
|
+
finding_id UUID,
|
|
435
|
+
feature_id TEXT,
|
|
436
|
+
severity finding_severity,
|
|
437
|
+
is_blocking BOOLEAN
|
|
438
|
+
)
|
|
439
|
+
LANGUAGE plpgsql
|
|
440
|
+
SET search_path = public
|
|
441
|
+
AS $$
|
|
442
|
+
DECLARE
|
|
443
|
+
v_finding_id UUID;
|
|
444
|
+
BEGIN
|
|
445
|
+
INSERT INTO security_findings (
|
|
446
|
+
feature_id, tool, severity, message, file_path, line_number,
|
|
447
|
+
rule_id, snippet, fix_suggestion, column_number, end_line, end_column
|
|
448
|
+
) VALUES (
|
|
449
|
+
p_feature_id, p_tool, p_severity, p_message, p_file_path, p_line_number,
|
|
450
|
+
p_rule_id, p_snippet, p_fix_suggestion, p_column_number, p_end_line, p_end_column
|
|
451
|
+
)
|
|
452
|
+
RETURNING id INTO v_finding_id;
|
|
453
|
+
|
|
454
|
+
-- Log to audit
|
|
455
|
+
INSERT INTO audit_log (feature_id, operation, agent_name, details)
|
|
456
|
+
VALUES (p_feature_id, 'SECURITY_FINDING', 'reviewer-agent', jsonb_build_object(
|
|
457
|
+
'table_name', 'security_findings',
|
|
458
|
+
'record_id', v_finding_id::text,
|
|
459
|
+
'tool', p_tool,
|
|
460
|
+
'severity', p_severity,
|
|
461
|
+
'rule_id', p_rule_id,
|
|
462
|
+
'file_path', p_file_path
|
|
463
|
+
));
|
|
464
|
+
|
|
465
|
+
RETURN QUERY SELECT
|
|
466
|
+
v_finding_id,
|
|
467
|
+
p_feature_id,
|
|
468
|
+
p_severity,
|
|
469
|
+
(p_severity IN ('HIGH', 'CRITICAL'));
|
|
470
|
+
END;
|
|
471
|
+
$$;
|
|
472
|
+
|
|
473
|
+
COMMENT ON FUNCTION record_security_finding IS 'Record a security finding from a SAST tool. Returns whether the finding blocks progression.';
|
|
474
|
+
|
|
475
|
+
-- Resolve a security finding
|
|
476
|
+
CREATE OR REPLACE FUNCTION resolve_security_finding(
|
|
477
|
+
p_finding_id UUID,
|
|
478
|
+
p_resolved_by TEXT,
|
|
479
|
+
p_resolution_note TEXT DEFAULT NULL
|
|
480
|
+
)
|
|
481
|
+
RETURNS TABLE (
|
|
482
|
+
finding_id UUID,
|
|
483
|
+
resolved BOOLEAN,
|
|
484
|
+
resolved_at TIMESTAMPTZ
|
|
485
|
+
)
|
|
486
|
+
LANGUAGE plpgsql
|
|
487
|
+
SET search_path = public
|
|
488
|
+
AS $$
|
|
489
|
+
DECLARE
|
|
490
|
+
v_feature_id TEXT;
|
|
491
|
+
v_resolved_at TIMESTAMPTZ;
|
|
492
|
+
BEGIN
|
|
493
|
+
-- Get feature_id for audit
|
|
494
|
+
SELECT sf.feature_id INTO v_feature_id
|
|
495
|
+
FROM security_findings sf WHERE sf.id = p_finding_id;
|
|
496
|
+
|
|
497
|
+
IF v_feature_id IS NULL THEN
|
|
498
|
+
RAISE EXCEPTION 'Finding not found: %', p_finding_id;
|
|
499
|
+
END IF;
|
|
500
|
+
|
|
501
|
+
-- Update finding
|
|
502
|
+
UPDATE security_findings sf
|
|
503
|
+
SET resolved = true,
|
|
504
|
+
resolved_by = p_resolved_by,
|
|
505
|
+
resolved_at = now(),
|
|
506
|
+
resolution_note = p_resolution_note
|
|
507
|
+
WHERE sf.id = p_finding_id
|
|
508
|
+
RETURNING sf.resolved_at INTO v_resolved_at;
|
|
509
|
+
|
|
510
|
+
-- Log to audit
|
|
511
|
+
INSERT INTO audit_log (feature_id, operation, agent_name, details)
|
|
512
|
+
VALUES (v_feature_id, 'FINDING_RESOLVED', p_resolved_by, jsonb_build_object(
|
|
513
|
+
'table_name', 'security_findings',
|
|
514
|
+
'record_id', p_finding_id::text,
|
|
515
|
+
'resolution_note', p_resolution_note
|
|
516
|
+
));
|
|
517
|
+
|
|
518
|
+
RETURN QUERY SELECT p_finding_id, true, v_resolved_at;
|
|
519
|
+
END;
|
|
520
|
+
$$;
|
|
521
|
+
|
|
522
|
+
COMMENT ON FUNCTION resolve_security_finding IS 'Mark a security finding as resolved with optional note';
|
|
523
|
+
|
|
524
|
+
-- Get unresolved security findings for a feature
|
|
525
|
+
CREATE OR REPLACE FUNCTION get_unresolved_findings(p_feature_id TEXT)
|
|
526
|
+
RETURNS TABLE (
|
|
527
|
+
id UUID,
|
|
528
|
+
tool TEXT,
|
|
529
|
+
severity finding_severity,
|
|
530
|
+
rule_id TEXT,
|
|
531
|
+
file_path TEXT,
|
|
532
|
+
line_number INTEGER,
|
|
533
|
+
message TEXT,
|
|
534
|
+
snippet TEXT,
|
|
535
|
+
fix_suggestion TEXT,
|
|
536
|
+
is_blocking BOOLEAN,
|
|
537
|
+
created_at TIMESTAMPTZ
|
|
538
|
+
)
|
|
539
|
+
LANGUAGE plpgsql
|
|
540
|
+
SET search_path = public
|
|
541
|
+
AS $$
|
|
542
|
+
BEGIN
|
|
543
|
+
RETURN QUERY
|
|
544
|
+
SELECT
|
|
545
|
+
sf.id, sf.tool, sf.severity, sf.rule_id,
|
|
546
|
+
sf.file_path, sf.line_number, sf.message, sf.snippet, sf.fix_suggestion,
|
|
547
|
+
(sf.severity IN ('HIGH', 'CRITICAL')) AS is_blocking,
|
|
548
|
+
sf.created_at
|
|
549
|
+
FROM security_findings sf
|
|
550
|
+
WHERE sf.feature_id = p_feature_id
|
|
551
|
+
AND sf.resolved = false
|
|
552
|
+
ORDER BY
|
|
553
|
+
CASE sf.severity
|
|
554
|
+
WHEN 'CRITICAL' THEN 0
|
|
555
|
+
WHEN 'HIGH' THEN 1
|
|
556
|
+
WHEN 'MEDIUM' THEN 2
|
|
557
|
+
WHEN 'LOW' THEN 3
|
|
558
|
+
ELSE 4
|
|
559
|
+
END,
|
|
560
|
+
sf.created_at;
|
|
561
|
+
END;
|
|
562
|
+
$$;
|
|
563
|
+
|
|
564
|
+
COMMENT ON FUNCTION get_unresolved_findings IS 'Get all unresolved security findings for a feature, ordered by severity';
|
|
565
|
+
|
|
566
|
+
-- Check if feature can proceed past Reviewer phase
|
|
567
|
+
CREATE OR REPLACE FUNCTION can_proceed_past_reviewer(p_feature_id TEXT)
|
|
568
|
+
RETURNS TABLE (
|
|
569
|
+
can_proceed BOOLEAN,
|
|
570
|
+
blocking_count INTEGER,
|
|
571
|
+
total_findings INTEGER,
|
|
572
|
+
reason TEXT
|
|
573
|
+
)
|
|
574
|
+
LANGUAGE plpgsql
|
|
575
|
+
SET search_path = public
|
|
576
|
+
AS $$
|
|
577
|
+
DECLARE
|
|
578
|
+
v_blocking_count INTEGER;
|
|
579
|
+
v_total_count INTEGER;
|
|
580
|
+
BEGIN
|
|
581
|
+
SELECT
|
|
582
|
+
COUNT(*) FILTER (WHERE severity IN ('HIGH', 'CRITICAL') AND resolved = false),
|
|
583
|
+
COUNT(*) FILTER (WHERE resolved = false)
|
|
584
|
+
INTO v_blocking_count, v_total_count
|
|
585
|
+
FROM security_findings
|
|
586
|
+
WHERE feature_id = p_feature_id;
|
|
587
|
+
|
|
588
|
+
RETURN QUERY
|
|
589
|
+
SELECT
|
|
590
|
+
v_blocking_count = 0,
|
|
591
|
+
v_blocking_count,
|
|
592
|
+
v_total_count,
|
|
593
|
+
CASE
|
|
594
|
+
WHEN v_blocking_count = 0 AND v_total_count = 0 THEN 'No security findings'
|
|
595
|
+
WHEN v_blocking_count = 0 THEN v_total_count || ' non-blocking findings (can proceed)'
|
|
596
|
+
ELSE v_blocking_count || ' unresolved HIGH/CRITICAL findings (must resolve before proceeding)'
|
|
597
|
+
END;
|
|
598
|
+
END;
|
|
599
|
+
$$;
|
|
600
|
+
|
|
601
|
+
COMMENT ON FUNCTION can_proceed_past_reviewer IS 'Check if a feature can proceed past Reviewer phase (no unresolved HIGH/CRITICAL findings)';
|
|
602
|
+
|
|
603
|
+
-- Get security findings summary for a feature
|
|
604
|
+
CREATE OR REPLACE FUNCTION get_security_summary(p_feature_id TEXT)
|
|
605
|
+
RETURNS TABLE (
|
|
606
|
+
total_findings INTEGER,
|
|
607
|
+
critical_count INTEGER,
|
|
608
|
+
high_count INTEGER,
|
|
609
|
+
medium_count INTEGER,
|
|
610
|
+
low_count INTEGER,
|
|
611
|
+
info_count INTEGER,
|
|
612
|
+
resolved_count INTEGER,
|
|
613
|
+
unresolved_blocking INTEGER,
|
|
614
|
+
can_proceed BOOLEAN
|
|
615
|
+
)
|
|
616
|
+
LANGUAGE plpgsql
|
|
617
|
+
SET search_path = public
|
|
618
|
+
AS $$
|
|
619
|
+
BEGIN
|
|
620
|
+
RETURN QUERY
|
|
621
|
+
SELECT
|
|
622
|
+
COUNT(*)::INTEGER AS total_findings,
|
|
623
|
+
COUNT(*) FILTER (WHERE severity = 'CRITICAL')::INTEGER AS critical_count,
|
|
624
|
+
COUNT(*) FILTER (WHERE severity = 'HIGH')::INTEGER AS high_count,
|
|
625
|
+
COUNT(*) FILTER (WHERE severity = 'MEDIUM')::INTEGER AS medium_count,
|
|
626
|
+
COUNT(*) FILTER (WHERE severity = 'LOW')::INTEGER AS low_count,
|
|
627
|
+
COUNT(*) FILTER (WHERE severity = 'INFO')::INTEGER AS info_count,
|
|
628
|
+
COUNT(*) FILTER (WHERE resolved = true)::INTEGER AS resolved_count,
|
|
629
|
+
COUNT(*) FILTER (WHERE resolved = false AND severity IN ('HIGH', 'CRITICAL'))::INTEGER AS unresolved_blocking,
|
|
630
|
+
(COUNT(*) FILTER (WHERE resolved = false AND severity IN ('HIGH', 'CRITICAL')) = 0) AS can_proceed
|
|
631
|
+
FROM security_findings
|
|
632
|
+
WHERE feature_id = p_feature_id;
|
|
633
|
+
END;
|
|
634
|
+
$$;
|
|
635
|
+
|
|
636
|
+
COMMENT ON FUNCTION get_security_summary IS 'Get summary of security findings for a feature by severity';
|
|
637
|
+
|
|
638
|
+
-- ============================================================================
|
|
639
|
+
-- PHASE NAME HELPER (UPDATED FOR V2)
|
|
640
|
+
-- ============================================================================
|
|
641
|
+
|
|
642
|
+
-- Get phase name for v2 workflow
|
|
643
|
+
CREATE OR REPLACE FUNCTION get_phase_name_v2(p_phase phase)
|
|
644
|
+
RETURNS TEXT
|
|
645
|
+
LANGUAGE plpgsql
|
|
646
|
+
IMMUTABLE
|
|
647
|
+
SET search_path = public
|
|
648
|
+
AS $$
|
|
649
|
+
BEGIN
|
|
650
|
+
RETURN CASE p_phase
|
|
651
|
+
WHEN '0' THEN 'Planning'
|
|
652
|
+
WHEN '1' THEN 'Product'
|
|
653
|
+
WHEN '2' THEN 'Discovery'
|
|
654
|
+
WHEN '3' THEN 'Architect'
|
|
655
|
+
WHEN '4' THEN 'Guardian'
|
|
656
|
+
WHEN '5' THEN 'Builder'
|
|
657
|
+
WHEN '6' THEN 'Reviewer'
|
|
658
|
+
WHEN '7' THEN 'Integrator'
|
|
659
|
+
WHEN '8' THEN 'Documenter'
|
|
660
|
+
WHEN '9' THEN 'Release'
|
|
661
|
+
WHEN '10' THEN 'Complete'
|
|
662
|
+
ELSE 'Unknown'
|
|
663
|
+
END;
|
|
664
|
+
END;
|
|
665
|
+
$$;
|
|
666
|
+
|
|
667
|
+
COMMENT ON FUNCTION get_phase_name_v2 IS 'Get human-readable name for v2 workflow phase (11 phases)';
|
|
668
|
+
|
|
669
|
+
-- ============================================================================
|
|
670
|
+
-- END OF FUNCTIONS
|
|
671
|
+
-- ============================================================================
|