@rulemetric/local 0.14.2 → 0.15.0

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.
@@ -0,0 +1,99 @@
1
+ -- A parent only for the cross-case/job/result relationships not held by one eval.
2
+ CREATE TABLE performance_studies (
3
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
4
+ eval_target_id uuid NOT NULL REFERENCES eval_targets(id) ON DELETE CASCADE,
5
+ project_path text NOT NULL, protocol jsonb NOT NULL, protocol_hash text NOT NULL,
6
+ result jsonb, finalized_at timestamptz,
7
+ created_at timestamptz NOT NULL DEFAULT now(),
8
+ CONSTRAINT performance_study_version CHECK (protocol->>'version' = '1'),
9
+ CONSTRAINT performance_study_hash CHECK (protocol_hash ~ '^[a-f0-9]{64}$')
10
+ );
11
+ CREATE UNIQUE INDEX performance_studies_protocol_unique ON performance_studies(user_id,eval_target_id,protocol_hash);
12
+ ALTER TABLE performance_studies ENABLE ROW LEVEL SECURITY;
13
+ -- Writes go through the API validation boundary; REST clients cannot replace
14
+ -- or erase protocols to bypass frozen lineage and final-result guards.
15
+ CREATE POLICY performance_studies_owner ON performance_studies FOR SELECT USING (auth.uid()=user_id);
16
+ CREATE FUNCTION preserve_study_protocol() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp AS $$
17
+ BEGIN
18
+ IF (to_jsonb(NEW)-'result'-'finalized_at') IS DISTINCT FROM (to_jsonb(OLD)-'result'-'finalized_at') OR OLD.result IS NOT NULL THEN
19
+ RAISE EXCEPTION 'Frozen study protocols and final results are immutable; create a new revision';
20
+ END IF;
21
+ IF NEW.result IS NULL OR NEW.finalized_at IS NULL THEN RAISE EXCEPTION 'Final result and timestamp are required together'; END IF;
22
+ RETURN NEW;
23
+ END $$;
24
+ CREATE TRIGGER performance_study_immutable BEFORE UPDATE ON performance_studies FOR EACH ROW EXECUTE FUNCTION preserve_study_protocol();
25
+
26
+ -- Route guards give a friendly response; this also fences refinement/SQL writers.
27
+ CREATE FUNCTION preserve_frozen_study_case() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp AS $$
28
+ BEGIN
29
+ IF EXISTS (SELECT 1 FROM performance_studies s, jsonb_array_elements(s.protocol->'tasks') t
30
+ WHERE s.eval_target_id=OLD.eval_target_id AND t->>'evalId'=OLD.id::text
31
+ AND EXISTS (SELECT 1 FROM auth.users u WHERE u.id=s.user_id)) THEN
32
+ IF TG_OP='DELETE' OR NEW IS DISTINCT FROM OLD THEN
33
+ RAISE EXCEPTION 'Frozen study cases cannot be edited, retired or deleted';
34
+ END IF;
35
+ END IF;
36
+ IF TG_OP='DELETE' THEN RETURN OLD; END IF;
37
+ RETURN NEW;
38
+ END $$;
39
+ CREATE TRIGGER performance_study_case_immutable BEFORE UPDATE OR DELETE ON evals FOR EACH ROW EXECUTE FUNCTION preserve_frozen_study_case();
40
+ -- One execution budget per frozen revision. Retries resume the same job.
41
+ CREATE UNIQUE INDEX eval_jobs_study_unique ON eval_jobs(user_id,(payload->'study'->>'id')) WHERE payload->'study'->>'id' IS NOT NULL;
42
+ CREATE FUNCTION preserve_study_job_snapshot() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp AS $$
43
+ BEGIN
44
+ IF OLD.payload->'study' IS NOT NULL AND NEW.payload IS DISTINCT FROM OLD.payload THEN
45
+ RAISE EXCEPTION 'Queued study snapshot is immutable';
46
+ END IF;
47
+ RETURN NEW;
48
+ END $$;
49
+ CREATE TRIGGER study_job_snapshot_immutable BEFORE UPDATE ON eval_jobs FOR EACH ROW EXECUTE FUNCTION preserve_study_job_snapshot();
50
+
51
+ CREATE FUNCTION preserve_study_observation() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp AS $$
52
+ BEGIN
53
+ IF OLD.outputs->'study' IS NOT NULL AND (NEW.outputs IS DISTINCT FROM OLD.outputs OR NEW.eval_id IS DISTINCT FROM OLD.eval_id OR NEW.job_id IS DISTINCT FROM OLD.job_id OR NEW.configuration IS DISTINCT FROM OLD.configuration OR NEW.run_number IS DISTINCT FROM OLD.run_number OR NEW.iteration IS DISTINCT FROM OLD.iteration OR NEW.eval_target_id IS DISTINCT FROM OLD.eval_target_id OR NEW.eval_target_version IS DISTINCT FROM OLD.eval_target_version OR NEW.status IS DISTINCT FROM OLD.status) THEN
54
+ RAISE EXCEPTION 'Study observations are immutable; retain exclusions and append a new protocol revision';
55
+ END IF;
56
+ RETURN NEW;
57
+ END $$;
58
+ CREATE TRIGGER study_observation_immutable BEFORE UPDATE ON eval_runs FOR EACH ROW EXECUTE FUNCTION preserve_study_observation();
59
+
60
+ -- Reserve each actor invocation before spawning it. A lost acknowledgement or
61
+ -- crashed worker consumes the slot: repeating an unknown call would overspend
62
+ -- and selectively replace missing outcomes. Reservations survive job retries.
63
+ CREATE TABLE performance_study_calls (
64
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
65
+ study_id uuid NOT NULL REFERENCES performance_studies(id) ON DELETE CASCADE,
66
+ job_id uuid NOT NULL REFERENCES eval_jobs(id) ON DELETE CASCADE,
67
+ task_id text NOT NULL, arm text NOT NULL CHECK (arm IN ('baseline','treatment')),
68
+ repetition integer NOT NULL CHECK (repetition > 0),
69
+ reserved_at timestamptz NOT NULL DEFAULT now(),
70
+ UNIQUE(study_id,task_id,arm,repetition)
71
+ );
72
+ ALTER TABLE performance_study_calls ENABLE ROW LEVEL SECURITY;
73
+ CREATE POLICY performance_study_calls_owner ON performance_study_calls FOR SELECT USING (
74
+ EXISTS (SELECT 1 FROM performance_studies s WHERE s.id=study_id AND s.user_id=auth.uid())
75
+ );
76
+
77
+ -- Serialize result insertion with the existing job's terminal transition.
78
+ -- A GET sealing a terminal study cannot miss a concurrently accepted run.
79
+ CREATE FUNCTION guard_study_observation_insert() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp AS $$
80
+ DECLARE execution_status text;
81
+ BEGIN
82
+ IF NEW.outputs->'study' IS NOT NULL THEN
83
+ SELECT status INTO execution_status FROM eval_jobs WHERE id=NEW.job_id FOR UPDATE;
84
+ IF execution_status IS DISTINCT FROM 'running' THEN
85
+ RAISE EXCEPTION 'Study observations require an active execution';
86
+ END IF;
87
+ END IF;
88
+ RETURN NEW;
89
+ END $$;
90
+ CREATE TRIGGER study_observation_active BEFORE INSERT ON eval_runs FOR EACH ROW EXECUTE FUNCTION guard_study_observation_insert();
91
+
92
+ CREATE FUNCTION preserve_study_history() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp AS $$
93
+ BEGIN
94
+ IF EXISTS (SELECT 1 FROM auth.users WHERE id=OLD.user_id) THEN
95
+ RAISE EXCEPTION 'Frozen study history cannot be deleted while the account exists';
96
+ END IF;
97
+ RETURN OLD;
98
+ END $$;
99
+ CREATE TRIGGER performance_study_history BEFORE DELETE ON performance_studies FOR EACH ROW EXECUTE FUNCTION preserve_study_history();