Initial commit: Add project files
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
create schema if not exists building_blocks;
|
||||
create schema if not exists signal_engine;
|
||||
|
||||
create table if not exists building_blocks.outbox_message (
|
||||
message_id uuid primary key,
|
||||
event_type text not null,
|
||||
schema_version integer not null check (schema_version > 0),
|
||||
payload_json jsonb not null,
|
||||
correlation_id text not null,
|
||||
occurred_at timestamptz not null,
|
||||
payload_hash text not null,
|
||||
published_at timestamptz null,
|
||||
attempt integer not null default 0
|
||||
);
|
||||
|
||||
create table if not exists building_blocks.inbox_message (
|
||||
consumer text not null,
|
||||
message_id uuid not null,
|
||||
received_at timestamptz not null,
|
||||
payload_hash text not null,
|
||||
primary key (consumer, message_id)
|
||||
);
|
||||
|
||||
create table if not exists building_blocks.job_run (
|
||||
job_run_id uuid primary key,
|
||||
job_type text not null,
|
||||
job_version integer not null,
|
||||
scope_key text not null,
|
||||
idempotency_key text not null unique,
|
||||
watermark text null,
|
||||
app_version text not null,
|
||||
model_version text null,
|
||||
config_version text null,
|
||||
data_version text null,
|
||||
contract_version text null,
|
||||
status text not null,
|
||||
input_hash text null,
|
||||
output_hash text null,
|
||||
started_at timestamptz null,
|
||||
heartbeat_at timestamptz null,
|
||||
finished_at timestamptz null,
|
||||
error_code text null,
|
||||
trace_id text null
|
||||
);
|
||||
@@ -0,0 +1,60 @@
|
||||
create table if not exists signal_engine.evidence_snapshot (
|
||||
evidence_id text primary key,
|
||||
as_of timestamptz not null,
|
||||
published_at_cutoff timestamptz not null,
|
||||
dataset_id text not null,
|
||||
data_hash text not null,
|
||||
model_version text not null,
|
||||
config_version text not null,
|
||||
payload jsonb not null,
|
||||
content_hash text not null unique,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create table if not exists signal_engine.signal_decision (
|
||||
decision_id uuid primary key,
|
||||
lot_id uuid not null,
|
||||
cycle_id uuid not null,
|
||||
evidence_id text not null references signal_engine.evidence_snapshot(evidence_id),
|
||||
action text not null,
|
||||
sell_fraction numeric(9,6) not null check (sell_fraction between 0 and 1),
|
||||
policy_id text not null,
|
||||
priority integer not null,
|
||||
reason_code text not null,
|
||||
reentry_eligible boolean not null,
|
||||
created_at timestamptz not null,
|
||||
decision_hash text not null unique
|
||||
);
|
||||
|
||||
create table if not exists signal_engine.reentry_watch (
|
||||
watch_id uuid primary key,
|
||||
sell_decision_id uuid not null unique references signal_engine.signal_decision(decision_id),
|
||||
prior_cycle_id uuid not null,
|
||||
state text not null,
|
||||
minimum_wait_sessions integer not null check (minimum_wait_sessions >= 0),
|
||||
minimum_spacing_sessions integer not null check (minimum_spacing_sessions >= 0),
|
||||
expires_after_sessions integer not null check (expires_after_sessions > 0),
|
||||
created_at timestamptz not null
|
||||
);
|
||||
|
||||
create table if not exists signal_engine.reentry_stage (
|
||||
watch_id uuid not null references signal_engine.reentry_watch(watch_id),
|
||||
stage_no integer not null,
|
||||
target_fraction numeric(9,6) not null check (target_fraction > 0 and target_fraction <= 1),
|
||||
status text not null,
|
||||
eligible_at timestamptz null,
|
||||
executed_cycle_id uuid null,
|
||||
primary key (watch_id, stage_no)
|
||||
);
|
||||
|
||||
create or replace function signal_engine.prevent_immutable_change()
|
||||
returns trigger language plpgsql as $$
|
||||
begin
|
||||
raise exception 'immutable evidence/decision rows cannot be updated or deleted';
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists evidence_snapshot_immutable on signal_engine.evidence_snapshot;
|
||||
create trigger evidence_snapshot_immutable
|
||||
before update or delete on signal_engine.evidence_snapshot
|
||||
for each row execute function signal_engine.prevent_immutable_change();
|
||||
@@ -0,0 +1,24 @@
|
||||
-- v11.1 hardening delta. Do not edit prior migrations.
|
||||
|
||||
create index if not exists ix_outbox_pending
|
||||
on building_blocks.outbox_message (occurred_at, message_id)
|
||||
where published_at is null;
|
||||
|
||||
create index if not exists ix_job_run_status_started
|
||||
on building_blocks.job_run (status, started_at);
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
add constraint ck_signal_decision_policy_id_not_blank
|
||||
check (length(trim(policy_id)) > 0) not valid;
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
validate constraint ck_signal_decision_policy_id_not_blank;
|
||||
|
||||
drop trigger if exists signal_decision_immutable on signal_engine.signal_decision;
|
||||
create trigger signal_decision_immutable
|
||||
before update or delete on signal_engine.signal_decision
|
||||
for each row execute function signal_engine.prevent_immutable_change();
|
||||
|
||||
create unique index if not exists ux_reentry_stage_executed_cycle
|
||||
on signal_engine.reentry_stage (executed_cycle_id)
|
||||
where executed_cycle_id is not null;
|
||||
@@ -0,0 +1,142 @@
|
||||
-- v12.0 integrated hardening delta. Prior migrations remain immutable.
|
||||
|
||||
-- Rename ambiguous columns while preserving existing data.
|
||||
do $$
|
||||
begin
|
||||
if exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'signal_engine'
|
||||
and table_name = 'signal_decision'
|
||||
and column_name = 'lot_id'
|
||||
) and not exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'signal_engine'
|
||||
and table_name = 'signal_decision'
|
||||
and column_name = 'position_lot_id'
|
||||
) then
|
||||
alter table signal_engine.signal_decision
|
||||
rename column lot_id to position_lot_id;
|
||||
end if;
|
||||
|
||||
if exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'signal_engine'
|
||||
and table_name = 'signal_decision'
|
||||
and column_name = 'sell_fraction'
|
||||
) and not exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'signal_engine'
|
||||
and table_name = 'signal_decision'
|
||||
and column_name = 'sell_ratio_of_lot'
|
||||
) then
|
||||
alter table signal_engine.signal_decision
|
||||
rename column sell_fraction to sell_ratio_of_lot;
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
add column if not exists target_portfolio_weight_after numeric(12,8),
|
||||
add column if not exists dataset_id text,
|
||||
add column if not exists model_version text,
|
||||
add column if not exists config_version text,
|
||||
add column if not exists code_sha text,
|
||||
add column if not exists idempotency_key text,
|
||||
add column if not exists correlation_id text;
|
||||
|
||||
update signal_engine.signal_decision d
|
||||
set dataset_id = coalesce(d.dataset_id, e.dataset_id),
|
||||
model_version = coalesce(d.model_version, e.model_version),
|
||||
config_version = coalesce(d.config_version, e.config_version),
|
||||
code_sha = coalesce(d.code_sha, 'LEGACY_UNKNOWN'),
|
||||
target_portfolio_weight_after = coalesce(d.target_portfolio_weight_after, 0),
|
||||
idempotency_key = coalesce(d.idempotency_key, 'legacy:' || d.decision_id::text),
|
||||
correlation_id = coalesce(d.correlation_id, 'legacy:' || d.decision_id::text)
|
||||
from signal_engine.evidence_snapshot e
|
||||
where e.evidence_id = d.evidence_id;
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
alter column target_portfolio_weight_after set not null,
|
||||
alter column dataset_id set not null,
|
||||
alter column model_version set not null,
|
||||
alter column config_version set not null,
|
||||
alter column code_sha set not null,
|
||||
alter column idempotency_key set not null,
|
||||
alter column correlation_id set not null;
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
drop constraint if exists ck_signal_decision_sell_ratio_of_lot,
|
||||
add constraint ck_signal_decision_sell_ratio_of_lot
|
||||
check (sell_ratio_of_lot between 0 and 1),
|
||||
drop constraint if exists ck_signal_decision_target_weight,
|
||||
add constraint ck_signal_decision_target_weight
|
||||
check (target_portfolio_weight_after between 0 and 1);
|
||||
|
||||
create unique index if not exists ux_signal_decision_idempotency
|
||||
on signal_engine.signal_decision (idempotency_key);
|
||||
|
||||
create index if not exists ix_signal_decision_lot_created
|
||||
on signal_engine.signal_decision (position_lot_id, created_at desc);
|
||||
|
||||
-- Rebuildable, versioned read model. It is populated only by approved upstream slices.
|
||||
create table if not exists signal_engine.sell_decision_context (
|
||||
context_id uuid primary key,
|
||||
position_lot_id uuid not null,
|
||||
cycle_id uuid not null,
|
||||
evidence_id text not null references signal_engine.evidence_snapshot(evidence_id),
|
||||
dataset_id text not null,
|
||||
model_version text not null,
|
||||
config_version text not null,
|
||||
code_sha text not null,
|
||||
as_of timestamptz not null,
|
||||
published_at_cutoff timestamptz not null,
|
||||
current_portfolio_weight numeric(12,8) not null
|
||||
check (current_portfolio_weight between 0 and 1),
|
||||
strategic_core_floor_weight numeric(12,8) not null
|
||||
check (strategic_core_floor_weight between 0 and current_portfolio_weight),
|
||||
hard_impairment_approved boolean not null,
|
||||
capital_floor_breached boolean not null,
|
||||
survival_sell_ratio_of_lot numeric(12,8) not null
|
||||
check (survival_sell_ratio_of_lot between 0 and 1),
|
||||
gap_below_floor_atr numeric(18,8) not null check (gap_below_floor_atr >= 0),
|
||||
consecutive_close_breaches integer not null check (consecutive_close_breaches >= 0),
|
||||
cooldown_satisfied boolean not null,
|
||||
concentration_sell_ratio_of_lot numeric(12,8) not null
|
||||
check (concentration_sell_ratio_of_lot between 0 and 1),
|
||||
opportunity_edge_lower_bound numeric(18,8) not null,
|
||||
opportunity_sell_ratio_of_lot numeric(12,8) not null
|
||||
check (opportunity_sell_ratio_of_lot between 0 and 1),
|
||||
quality_status text not null check (quality_status in ('PASS', 'WARN', 'QUARANTINED')),
|
||||
source_watermark text not null,
|
||||
projection_version integer not null,
|
||||
content_hash text not null unique,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (position_lot_id, as_of, projection_version)
|
||||
);
|
||||
|
||||
create index if not exists ix_sell_decision_context_lookup
|
||||
on signal_engine.sell_decision_context (position_lot_id, as_of desc)
|
||||
where quality_status = 'PASS';
|
||||
|
||||
drop trigger if exists sell_decision_context_immutable on signal_engine.sell_decision_context;
|
||||
create trigger sell_decision_context_immutable
|
||||
before update or delete on signal_engine.sell_decision_context
|
||||
for each row execute function signal_engine.prevent_immutable_change();
|
||||
|
||||
-- Audit rows are append-only. Corrections are represented as new events.
|
||||
create table if not exists signal_engine.decision_audit_event (
|
||||
audit_event_id uuid primary key,
|
||||
decision_id uuid not null references signal_engine.signal_decision(decision_id),
|
||||
event_type text not null,
|
||||
actor_id text not null,
|
||||
correlation_id text not null,
|
||||
payload_json jsonb not null,
|
||||
payload_hash text not null,
|
||||
occurred_at timestamptz not null,
|
||||
unique (decision_id, event_type, payload_hash)
|
||||
);
|
||||
|
||||
drop trigger if exists decision_audit_event_immutable on signal_engine.decision_audit_event;
|
||||
create trigger decision_audit_event_immutable
|
||||
before update or delete on signal_engine.decision_audit_event
|
||||
for each row execute function signal_engine.prevent_immutable_change();
|
||||
@@ -0,0 +1,68 @@
|
||||
-- v12.1 execution-readiness delta. Prior migrations remain immutable.
|
||||
|
||||
alter table building_blocks.outbox_message
|
||||
add column if not exists next_attempt_at timestamptz null,
|
||||
add column if not exists lease_owner text null,
|
||||
add column if not exists lease_until timestamptz null,
|
||||
add column if not exists dead_lettered_at timestamptz null,
|
||||
add column if not exists last_error_code text null;
|
||||
|
||||
create index if not exists ix_outbox_dispatchable
|
||||
on building_blocks.outbox_message (coalesce(next_attempt_at, occurred_at), occurred_at, message_id)
|
||||
where published_at is null and dead_lettered_at is null;
|
||||
|
||||
alter table building_blocks.inbox_message
|
||||
add column if not exists processed_at timestamptz null,
|
||||
add column if not exists result_hash text null;
|
||||
|
||||
create table if not exists building_blocks.projection_checkpoint (
|
||||
projection_name text not null,
|
||||
projection_version integer not null check (projection_version > 0),
|
||||
source_watermark text not null,
|
||||
last_event_id uuid null,
|
||||
content_hash text not null,
|
||||
built_at timestamptz not null,
|
||||
primary key (projection_name, projection_version)
|
||||
);
|
||||
|
||||
create table if not exists building_blocks.projection_rebuild_run (
|
||||
rebuild_id uuid primary key,
|
||||
projection_name text not null,
|
||||
target_version integer not null check (target_version > 0),
|
||||
through_watermark text not null,
|
||||
dry_run boolean not null,
|
||||
requested_by text not null,
|
||||
requested_at timestamptz not null,
|
||||
status text not null,
|
||||
source_count bigint null,
|
||||
projected_count bigint null,
|
||||
result_hash text null,
|
||||
finished_at timestamptz null,
|
||||
error_code text null
|
||||
);
|
||||
|
||||
create table if not exists building_blocks.audit_event (
|
||||
audit_event_id uuid primary key,
|
||||
entity_type text not null,
|
||||
entity_id text not null,
|
||||
event_type text not null,
|
||||
actor_id text not null,
|
||||
correlation_id text not null,
|
||||
payload_json jsonb not null,
|
||||
payload_hash text not null,
|
||||
corrects_audit_event_id uuid null references building_blocks.audit_event(audit_event_id),
|
||||
occurred_at timestamptz not null,
|
||||
unique (entity_type, entity_id, event_type, payload_hash)
|
||||
);
|
||||
|
||||
create or replace function building_blocks.prevent_append_only_change()
|
||||
returns trigger language plpgsql as $$
|
||||
begin
|
||||
raise exception 'append-only row cannot be updated or deleted';
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists audit_event_append_only on building_blocks.audit_event;
|
||||
create trigger audit_event_append_only
|
||||
before update or delete on building_blocks.audit_event
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
@@ -0,0 +1,61 @@
|
||||
-- v12.2 strategic data-correctness delta.
|
||||
-- Prior columns remain for compatibility; new code uses explicit security/lot weight semantics.
|
||||
|
||||
alter table signal_engine.sell_decision_context
|
||||
add column if not exists current_security_portfolio_weight numeric(12,8),
|
||||
add column if not exists current_lot_portfolio_weight numeric(12,8);
|
||||
|
||||
update signal_engine.sell_decision_context
|
||||
set current_security_portfolio_weight = coalesce(current_security_portfolio_weight, current_portfolio_weight),
|
||||
current_lot_portfolio_weight = coalesce(current_lot_portfolio_weight, current_portfolio_weight)
|
||||
where current_security_portfolio_weight is null
|
||||
or current_lot_portfolio_weight is null;
|
||||
|
||||
alter table signal_engine.sell_decision_context
|
||||
alter column current_security_portfolio_weight set not null,
|
||||
alter column current_lot_portfolio_weight set not null,
|
||||
drop constraint if exists ck_sell_context_security_weight,
|
||||
add constraint ck_sell_context_security_weight
|
||||
check (current_security_portfolio_weight between 0 and 1),
|
||||
drop constraint if exists ck_sell_context_lot_weight,
|
||||
add constraint ck_sell_context_lot_weight
|
||||
check (current_lot_portfolio_weight between 0 and current_security_portfolio_weight),
|
||||
drop constraint if exists ck_sell_context_published_cutoff,
|
||||
add constraint ck_sell_context_published_cutoff
|
||||
check (published_at_cutoff <= as_of);
|
||||
|
||||
comment on column signal_engine.sell_decision_context.current_portfolio_weight is
|
||||
'Legacy ambiguous field. Do not use in v12.2+ decisions.';
|
||||
comment on column signal_engine.sell_decision_context.current_security_portfolio_weight is
|
||||
'Absolute portfolio weight of the security, 0..1.';
|
||||
comment on column signal_engine.sell_decision_context.current_lot_portfolio_weight is
|
||||
'Absolute portfolio weight represented by the target PositionLot, 0..security weight.';
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
add column if not exists target_security_portfolio_weight_after numeric(12,8),
|
||||
add column if not exists policy_trace_json jsonb;
|
||||
|
||||
update signal_engine.signal_decision
|
||||
set target_security_portfolio_weight_after = coalesce(
|
||||
target_security_portfolio_weight_after,
|
||||
target_portfolio_weight_after,
|
||||
0),
|
||||
policy_trace_json = coalesce(policy_trace_json, '[]'::jsonb)
|
||||
where target_security_portfolio_weight_after is null
|
||||
or policy_trace_json is null;
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
alter column target_security_portfolio_weight_after set not null,
|
||||
alter column policy_trace_json set not null,
|
||||
drop constraint if exists ck_signal_decision_target_security_weight,
|
||||
add constraint ck_signal_decision_target_security_weight
|
||||
check (target_security_portfolio_weight_after between 0 and 1),
|
||||
drop constraint if exists ck_signal_decision_policy_trace_array,
|
||||
add constraint ck_signal_decision_policy_trace_array
|
||||
check (jsonb_typeof(policy_trace_json) = 'array');
|
||||
|
||||
comment on column signal_engine.signal_decision.target_portfolio_weight_after is
|
||||
'Legacy ambiguous target field. v12.2+ uses target_security_portfolio_weight_after.';
|
||||
|
||||
create index if not exists ix_signal_decision_policy_trace_gin
|
||||
on signal_engine.signal_decision using gin (policy_trace_json);
|
||||
@@ -0,0 +1,149 @@
|
||||
-- v12.3 semantic-versioning and contract-governance delta.
|
||||
-- Prior migrations remain immutable. Legacy ambiguous weight contexts are blocked, not guessed.
|
||||
|
||||
alter table signal_engine.sell_decision_context
|
||||
add column if not exists weight_semantics_version smallint;
|
||||
|
||||
-- Every row created before this migration is conservatively classified as legacy/ambiguous.
|
||||
update signal_engine.sell_decision_context
|
||||
set weight_semantics_version = 1
|
||||
where weight_semantics_version is null;
|
||||
|
||||
alter table signal_engine.sell_decision_context
|
||||
alter column weight_semantics_version set default 2,
|
||||
alter column weight_semantics_version set not null,
|
||||
drop constraint if exists ck_sell_context_weight_semantics_version,
|
||||
add constraint ck_sell_context_weight_semantics_version
|
||||
check (weight_semantics_version in (1, 2));
|
||||
|
||||
comment on column signal_engine.sell_decision_context.weight_semantics_version is
|
||||
'1=legacy ambiguous current_portfolio_weight backfill; 2=explicit security and lot weights from approved source. Decision reads require 2.';
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
add column if not exists decision_contract_version text,
|
||||
add column if not exists policy_trace_schema_version smallint;
|
||||
|
||||
update signal_engine.signal_decision
|
||||
set decision_contract_version = coalesce(decision_contract_version, 'sell-decision.v2'),
|
||||
policy_trace_schema_version = coalesce(policy_trace_schema_version, 2)
|
||||
where decision_contract_version is null
|
||||
or policy_trace_schema_version is null;
|
||||
|
||||
alter table signal_engine.signal_decision
|
||||
alter column decision_contract_version set default 'sell-decision.v2',
|
||||
alter column decision_contract_version set not null,
|
||||
alter column policy_trace_schema_version set default 2,
|
||||
alter column policy_trace_schema_version set not null,
|
||||
drop constraint if exists ck_signal_decision_contract_version,
|
||||
add constraint ck_signal_decision_contract_version
|
||||
check (decision_contract_version = 'sell-decision.v2'),
|
||||
drop constraint if exists ck_signal_decision_policy_trace_schema_version,
|
||||
add constraint ck_signal_decision_policy_trace_schema_version
|
||||
check (policy_trace_schema_version = 2);
|
||||
|
||||
create table if not exists signal_engine.policy_contract_definition (
|
||||
contract_version text primary key,
|
||||
content_hash text not null unique,
|
||||
policy_json jsonb not null,
|
||||
status text not null check (status in ('PROPOSED', 'APPROVED', 'RETIRED')),
|
||||
effective_from timestamptz null,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null default now(),
|
||||
check (jsonb_typeof(policy_json) = 'object')
|
||||
);
|
||||
|
||||
insert into signal_engine.policy_contract_definition
|
||||
(contract_version, content_hash, policy_json, status)
|
||||
values
|
||||
('sell-policy.v1', 'a269a0331b83c0f6ec108e7587de1d20c798036ff8d0c03d726cd854e73d8480', $policy$
|
||||
{
|
||||
"changeControl": "MODEL_CHANGE_AND_GOLDEN_OOS_REQUIRED",
|
||||
"contractVersion": "sell-policy.v1",
|
||||
"decisionContractVersion": "sell-decision.v2",
|
||||
"policies": [
|
||||
{
|
||||
"condition": "approved hard impairment",
|
||||
"documentedRange": [
|
||||
0.8,
|
||||
1.0
|
||||
],
|
||||
"mayCrossStrategicCore": true,
|
||||
"policyId": "ALG-SELL-001",
|
||||
"priority": 1000,
|
||||
"reentryEligible": false,
|
||||
"requestedSellRatioOfLot": 1.0,
|
||||
"terminal": true
|
||||
},
|
||||
{
|
||||
"condition": "capital floor breached",
|
||||
"decisionRequired": "Whether portfolio survival bypasses same-direction cooldown",
|
||||
"mayCrossStrategicCore": true,
|
||||
"policyId": "ALG-SELL-PORT-001",
|
||||
"priority": 900,
|
||||
"ratioSource": "server PIT risk context",
|
||||
"reentryEligible": true,
|
||||
"terminal": true
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"gapBelowFloorAtrGte": 1.5
|
||||
},
|
||||
"documentedRange": [
|
||||
0.3,
|
||||
0.5
|
||||
],
|
||||
"mayCrossStrategicCore": false,
|
||||
"policyId": "ALG-SELL-002",
|
||||
"priority": 800,
|
||||
"reentryEligible": true,
|
||||
"requestedSellRatioOfLot": 0.4,
|
||||
"terminal": false
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"consecutiveCloseBreachesGte": 2
|
||||
},
|
||||
"documentedRange": [
|
||||
0.15,
|
||||
0.25
|
||||
],
|
||||
"mayCrossStrategicCore": false,
|
||||
"policyId": "ALG-SELL-003",
|
||||
"priority": 700,
|
||||
"reentryEligible": true,
|
||||
"requestedSellRatioOfLot": 0.2,
|
||||
"terminal": false
|
||||
},
|
||||
{
|
||||
"condition": "approved concentration/liquidity excess ratio > 0",
|
||||
"mayCrossStrategicCore": false,
|
||||
"policyId": "ALG-SELL-004",
|
||||
"priority": 600,
|
||||
"ratioSource": "server PIT portfolio risk context",
|
||||
"reentryEligible": true,
|
||||
"terminal": false
|
||||
},
|
||||
{
|
||||
"condition": "opportunity edge lower bound > 0 and requested ratio > 0",
|
||||
"mayCrossStrategicCore": false,
|
||||
"policyId": "ALG-SELL-005",
|
||||
"priority": 500,
|
||||
"reentryEligible": true,
|
||||
"requestedRange": [
|
||||
0.1,
|
||||
0.25
|
||||
],
|
||||
"terminal": false
|
||||
}
|
||||
],
|
||||
"policyTraceSchemaVersion": 2,
|
||||
"status": "RESEARCH_CANDIDATE_NOT_PRODUCTION"
|
||||
}
|
||||
$policy$::jsonb, 'PROPOSED')
|
||||
on conflict (contract_version) do nothing;
|
||||
|
||||
drop trigger if exists policy_contract_definition_immutable on signal_engine.policy_contract_definition;
|
||||
create trigger policy_contract_definition_immutable
|
||||
before update or delete on signal_engine.policy_contract_definition
|
||||
for each row execute function signal_engine.prevent_immutable_change();
|
||||
@@ -0,0 +1,264 @@
|
||||
-- v12.4 continuous model operations delta.
|
||||
-- Evaluation and proposal automation only. This migration does not enable automatic model promotion,
|
||||
-- automatic order submission or KIS submission.
|
||||
|
||||
create schema if not exists evaluation;
|
||||
create schema if not exists governance;
|
||||
|
||||
create table if not exists evaluation.dataset_manifest (
|
||||
dataset_id text primary key,
|
||||
scope_key text not null,
|
||||
content_hash text not null unique,
|
||||
source_catalog_version text not null,
|
||||
lineage_hash text not null,
|
||||
status text not null check (status in ('PROPOSED', 'APPROVED', 'QUARANTINED', 'RETIRED')),
|
||||
frozen_at timestamptz not null,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create index if not exists ix_dataset_manifest_scope_frozen
|
||||
on evaluation.dataset_manifest (scope_key, frozen_at desc)
|
||||
where status = 'APPROVED';
|
||||
|
||||
create table if not exists governance.model_version_registry (
|
||||
model_version text not null,
|
||||
scope_key text not null,
|
||||
config_version text not null,
|
||||
code_sha text not null,
|
||||
contract_version text not null,
|
||||
lifecycle_state text not null check (lifecycle_state in
|
||||
('RESEARCH', 'CHALLENGER', 'SHADOW', 'CANDIDATE', 'APPROVED', 'RETIRED', 'ROLLED_BACK')),
|
||||
effective_at timestamptz not null,
|
||||
retired_at timestamptz null,
|
||||
model_card_hash text not null,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null default now(),
|
||||
primary key (model_version, scope_key, effective_at),
|
||||
check (lifecycle_state <> 'APPROVED' or (approved_by is not null and approved_at is not null))
|
||||
);
|
||||
|
||||
create index if not exists ix_model_version_registry_active
|
||||
on governance.model_version_registry (scope_key, effective_at desc)
|
||||
where lifecycle_state in ('RESEARCH', 'CHALLENGER', 'SHADOW', 'CANDIDATE', 'APPROVED');
|
||||
|
||||
create table if not exists evaluation.model_operation_schedule (
|
||||
schedule_id uuid primary key,
|
||||
operation_code text not null,
|
||||
operation_name text not null,
|
||||
scope_key text not null,
|
||||
cadence text not null check (cadence in ('DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY', 'EVENT_DRIVEN')),
|
||||
automation_mode text not null check (automation_mode in ('EVALUATION_ONLY', 'PROPOSAL_ONLY', 'DRILL_ONLY')),
|
||||
queue_name text not null,
|
||||
schedule_version integer not null check (schedule_version > 0),
|
||||
enabled boolean not null default true,
|
||||
next_due_at timestamptz not null,
|
||||
last_dispatched_at timestamptz null,
|
||||
last_background_job_id text null,
|
||||
max_lag interval not null,
|
||||
primary_owner text not null,
|
||||
secondary_owner text not null,
|
||||
lease_owner text null,
|
||||
lease_until timestamptz null,
|
||||
last_error_code text null,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
unique (operation_code, scope_key, schedule_version)
|
||||
);
|
||||
|
||||
create index if not exists ix_model_operation_schedule_due
|
||||
on evaluation.model_operation_schedule (next_due_at, operation_code, scope_key)
|
||||
where enabled = true;
|
||||
|
||||
create table if not exists evaluation.model_operation_request (
|
||||
request_id uuid primary key,
|
||||
schedule_id uuid not null references evaluation.model_operation_schedule(schedule_id),
|
||||
operation_code text not null,
|
||||
scope_key text not null,
|
||||
automation_mode text not null check (automation_mode in ('EVALUATION_ONLY', 'PROPOSAL_ONLY', 'DRILL_ONLY')),
|
||||
idempotency_key text not null unique,
|
||||
dataset_id text not null,
|
||||
data_hash text not null,
|
||||
model_version text not null,
|
||||
config_version text not null,
|
||||
code_sha text not null,
|
||||
contract_version text not null,
|
||||
lifecycle_state text not null,
|
||||
correlation_id text not null,
|
||||
status text not null check (status in ('REQUESTED', 'RUNNING', 'SUCCEEDED', 'BUSINESS_HOLD', 'FAILED', 'QUARANTINED')),
|
||||
output_hash text null,
|
||||
error_code text null,
|
||||
requested_at timestamptz not null,
|
||||
started_at timestamptz null,
|
||||
finished_at timestamptz null
|
||||
);
|
||||
|
||||
create index if not exists ix_model_operation_request_scope_time
|
||||
on evaluation.model_operation_request (scope_key, operation_code, requested_at desc);
|
||||
|
||||
-- Request is a lifecycle aggregate: status/timing fields are mutable under optimistic/transactional control.
|
||||
-- Every transition is additionally recorded as an immutable status event so operational history is never lost.
|
||||
create table if not exists evaluation.model_operation_status_event (
|
||||
event_id uuid primary key,
|
||||
request_id uuid not null references evaluation.model_operation_request(request_id),
|
||||
from_status text null,
|
||||
to_status text not null check (to_status in
|
||||
('REQUESTED', 'RUNNING', 'SUCCEEDED', 'BUSINESS_HOLD', 'FAILED', 'QUARANTINED')),
|
||||
reason_code text null,
|
||||
payload_hash text not null,
|
||||
actor_type text not null check (actor_type in ('SYSTEM', 'OPERATOR', 'REPLAY')),
|
||||
correlation_id text not null,
|
||||
occurred_at timestamptz not null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (request_id, to_status, occurred_at, payload_hash)
|
||||
);
|
||||
|
||||
create index if not exists ix_model_operation_status_event_request_time
|
||||
on evaluation.model_operation_status_event (request_id, occurred_at);
|
||||
|
||||
create table if not exists evaluation.model_metric_definition (
|
||||
metric_code text not null,
|
||||
definition_version integer not null check (definition_version > 0),
|
||||
numerator_definition text not null,
|
||||
denominator_definition text not null,
|
||||
evaluation_window text not null,
|
||||
aggregation_method text not null,
|
||||
threshold_json jsonb not null,
|
||||
status text not null check (status in ('PROPOSED', 'APPROVED', 'RETIRED')),
|
||||
content_hash text not null,
|
||||
effective_from timestamptz null,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null default now(),
|
||||
primary key (metric_code, definition_version),
|
||||
unique (content_hash)
|
||||
);
|
||||
|
||||
create table if not exists evaluation.model_metric_observation (
|
||||
observation_id uuid primary key,
|
||||
request_id uuid not null references evaluation.model_operation_request(request_id),
|
||||
metric_code text not null,
|
||||
definition_version integer not null,
|
||||
scope_key text not null,
|
||||
cohort_key text not null,
|
||||
value_numeric numeric(30, 12) null,
|
||||
value_text text null,
|
||||
sample_size bigint not null check (sample_size >= 0),
|
||||
dataset_id text not null,
|
||||
model_version text not null,
|
||||
as_of timestamptz not null,
|
||||
content_hash text not null,
|
||||
created_at timestamptz not null default now(),
|
||||
foreign key (metric_code, definition_version)
|
||||
references evaluation.model_metric_definition(metric_code, definition_version),
|
||||
check ((value_numeric is null) <> (value_text is null)),
|
||||
unique (request_id, metric_code, definition_version, cohort_key, content_hash)
|
||||
);
|
||||
|
||||
create table if not exists evaluation.model_evaluation_snapshot (
|
||||
snapshot_id uuid primary key,
|
||||
request_id uuid not null references evaluation.model_operation_request(request_id),
|
||||
scope_key text not null,
|
||||
model_version text not null,
|
||||
dataset_id text not null,
|
||||
gate_contract_version text not null,
|
||||
gate_decision text not null check (gate_decision in ('PASS', 'WARN', 'HOLD', 'FAIL')),
|
||||
blocking_reasons_json jsonb not null,
|
||||
warnings_json jsonb not null,
|
||||
evidence_hash text not null,
|
||||
as_of timestamptz not null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (request_id, evidence_hash)
|
||||
);
|
||||
|
||||
create table if not exists governance.model_improvement_proposal (
|
||||
proposal_id uuid primary key,
|
||||
source_request_id uuid not null references evaluation.model_operation_request(request_id),
|
||||
scope_key text not null,
|
||||
base_model_version text not null,
|
||||
proposal_type text not null check (proposal_type in
|
||||
('DATA_REMEDIATION', 'FEATURE_REVIEW', 'THRESHOLD_REVIEW', 'POLICY_REFACTORING',
|
||||
'CALIBRATION_REVIEW', 'RISK_LIMIT_REVIEW', 'TECH_DEBT_REPAYMENT')),
|
||||
problem_statement text not null,
|
||||
supporting_evidence_json jsonb not null,
|
||||
counter_evidence_json jsonb not null,
|
||||
expected_benefit text not null,
|
||||
risks text not null,
|
||||
required_tests_json jsonb not null,
|
||||
status text not null check (status in
|
||||
('DRAFT', 'REVIEW_REQUIRED', 'APPROVED_FOR_RESEARCH', 'REJECTED', 'EXPIRED')),
|
||||
created_by text not null,
|
||||
created_at timestamptz not null,
|
||||
reviewed_by text null,
|
||||
reviewed_at timestamptz null,
|
||||
expires_at timestamptz not null,
|
||||
content_hash text not null unique,
|
||||
check (status <> 'APPROVED_FOR_RESEARCH' or (reviewed_by is not null and reviewed_at is not null))
|
||||
);
|
||||
|
||||
create table if not exists governance.model_promotion_review (
|
||||
review_id uuid primary key,
|
||||
scope_key text not null,
|
||||
candidate_model_version text not null,
|
||||
evidence_snapshot_id uuid not null references evaluation.model_evaluation_snapshot(snapshot_id),
|
||||
independent_validation_hash text not null,
|
||||
compliance_review_hash text null,
|
||||
security_review_hash text null,
|
||||
decision text not null check (decision in ('PENDING', 'APPROVED_FOR_SHADOW', 'APPROVED_FOR_PILOT', 'REJECTED', 'EXPIRED')),
|
||||
maker_id text not null,
|
||||
checker_id text null,
|
||||
created_at timestamptz not null,
|
||||
decided_at timestamptz null,
|
||||
check (maker_id <> coalesce(checker_id, '')),
|
||||
check (decision = 'PENDING' or (checker_id is not null and decided_at is not null))
|
||||
);
|
||||
|
||||
create table if not exists governance.model_rollback_drill (
|
||||
drill_id uuid primary key,
|
||||
scope_key text not null,
|
||||
champion_model_version text not null,
|
||||
fallback_model_version text not null,
|
||||
runbook_version text not null,
|
||||
started_at timestamptz not null,
|
||||
finished_at timestamptz null,
|
||||
result text not null check (result in ('RUNNING', 'PASS', 'FAIL', 'BUSINESS_HOLD')),
|
||||
recovery_time_seconds integer null check (recovery_time_seconds is null or recovery_time_seconds >= 0),
|
||||
evidence_hash text null,
|
||||
owner text not null,
|
||||
secondary_owner text not null
|
||||
);
|
||||
|
||||
-- Append-only evidence. Request status is mutable, but status_event/observation/snapshot history is immutable.
|
||||
drop trigger if exists model_operation_status_event_append_only on evaluation.model_operation_status_event;
|
||||
create trigger model_operation_status_event_append_only
|
||||
before update or delete on evaluation.model_operation_status_event
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists model_metric_observation_append_only on evaluation.model_metric_observation;
|
||||
create trigger model_metric_observation_append_only
|
||||
before update or delete on evaluation.model_metric_observation
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists model_evaluation_snapshot_append_only on evaluation.model_evaluation_snapshot;
|
||||
create trigger model_evaluation_snapshot_append_only
|
||||
before update or delete on evaluation.model_evaluation_snapshot
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
-- Schedules intentionally automate evidence collection/proposal generation only.
|
||||
insert into evaluation.model_operation_schedule
|
||||
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
|
||||
schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner)
|
||||
values
|
||||
('e4100000-0000-4000-8000-000000000010', 'J10', 'OutcomeEvaluationRun', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-evaluation', 1, true, now() + interval '1 hour', interval '1 day', 'Quant/Ops', 'Data/QA'),
|
||||
('e4100000-0000-4000-8000-000000000011', 'J11', 'DailyScorecardBuild', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-evaluation', 1, true, now() + interval '2 hours', interval '1 day', 'Quant/Ops', 'Risk/SRE'),
|
||||
('e4100000-0000-4000-8000-000000000017', 'J17', 'DriftDetectionRun', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-evaluation', 1, true, now() + interval '3 hours', interval '1 day', 'Quant/Risk', 'Data/SRE'),
|
||||
('e4100000-0000-4000-8000-000000000018', 'J18', 'ChampionChallengerEvaluation', 'GLOBAL', 'WEEKLY', 'EVALUATION_ONLY', 'q-evaluation', 1, true, now() + interval '1 day', interval '7 days', 'Quant/Risk', 'QA/InvestmentCommittee'),
|
||||
('e4100000-0000-4000-8000-000000000019', 'J19', 'FrozenOosBacktest', 'GLOBAL', 'MONTHLY', 'EVALUATION_ONLY', 'q-research', 1, true, now() + interval '7 days', interval '31 days', 'Quant/Data', 'QA/Risk'),
|
||||
('e4100000-0000-4000-8000-000000000020', 'J20', 'RobustnessPboDsrRun', 'GLOBAL', 'QUARTERLY', 'EVALUATION_ONLY', 'q-research', 1, true, now() + interval '30 days', interval '95 days', 'Quant/Risk', 'IndependentValidation'),
|
||||
('e4100000-0000-4000-8000-000000000021', 'J21', 'ModelImprovementProposalBuild', 'GLOBAL', 'MONTHLY', 'PROPOSAL_ONLY', 'q-research', 1, true, now() + interval '14 days', interval '31 days', 'Quant Lead', 'Risk/Architect'),
|
||||
('e4100000-0000-4000-8000-000000000022', 'J22', 'PromotionEvidenceReviewBuild', 'GLOBAL', 'MONTHLY', 'PROPOSAL_ONLY', 'q-control', 1, true, now() + interval '21 days', interval '31 days', 'Risk/InvestmentCommittee', 'Compliance/QA'),
|
||||
('e4100000-0000-4000-8000-000000000023', 'J23', 'ModelRollbackDrill', 'GLOBAL', 'QUARTERLY', 'DRILL_ONLY', 'q-control', 1, true, now() + interval '45 days', interval '95 days', 'SRE/Risk', 'Module Owner/QA'),
|
||||
('e4100000-0000-4000-8000-000000000024', 'J24', 'DataRevisionRevalidation', 'GLOBAL', 'WEEKLY', 'EVALUATION_ONLY', 'q-backfill', 1, true, now() + interval '2 days', interval '7 days', 'Data/Quant', 'DBA/QA')
|
||||
on conflict (operation_code, scope_key, schedule_version) do nothing;
|
||||
@@ -0,0 +1,137 @@
|
||||
-- v12.5 execution assurance delta.
|
||||
-- This migration adds evidence and scheduling metadata only. It does not enable automatic model mutation,
|
||||
-- client publication, broker submission or model promotion.
|
||||
|
||||
-- Fail closed after upgrade. Each schedule must be enabled by an approved operational change.
|
||||
update evaluation.model_operation_schedule
|
||||
set enabled = false,
|
||||
updated_at = now(),
|
||||
last_error_code = 'V12_5_REAPPROVAL_REQUIRED'
|
||||
where schedule_version = 1
|
||||
and operation_code in ('J10','J11','J17','J18','J19','J20','J21','J22','J23','J24');
|
||||
|
||||
alter table evaluation.model_operation_schedule
|
||||
add column if not exists calendar_id text not null default 'GLOBAL_UTC',
|
||||
add column if not exists due_policy text not null default 'FIXED_CADENCE',
|
||||
add column if not exists dependency_json jsonb not null default '[]'::jsonb,
|
||||
add column if not exists catch_up_policy text not null default 'LATEST_ONLY',
|
||||
add column if not exists max_catch_up integer not null default 1 check (max_catch_up between 0 and 31),
|
||||
add column if not exists business_hold_code text null;
|
||||
|
||||
create table if not exists evaluation.model_operation_artifact (
|
||||
artifact_id uuid primary key,
|
||||
request_id uuid not null references evaluation.model_operation_request(request_id),
|
||||
artifact_type text not null,
|
||||
artifact_uri text not null,
|
||||
content_hash text not null,
|
||||
media_type text not null,
|
||||
evidence_class text not null check (evidence_class in ('SOURCE', 'RUNTIME', 'RESEARCH', 'RELEASE', 'AUDIT')),
|
||||
produced_by text not null,
|
||||
produced_at timestamptz not null,
|
||||
retention_until timestamptz null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (request_id, artifact_type, content_hash)
|
||||
);
|
||||
|
||||
create table if not exists evaluation.model_operation_dependency_result (
|
||||
dependency_result_id uuid primary key,
|
||||
request_id uuid not null references evaluation.model_operation_request(request_id),
|
||||
dependency_code text not null,
|
||||
result text not null check (result in ('PASS', 'WARN', 'HOLD', 'FAIL')),
|
||||
evidence_hash text not null,
|
||||
checked_at timestamptz not null,
|
||||
unique (request_id, dependency_code, evidence_hash)
|
||||
);
|
||||
|
||||
create table if not exists governance.release_evidence_bundle (
|
||||
bundle_id uuid primary key,
|
||||
release_version text not null,
|
||||
source_manifest_hash text not null,
|
||||
build_artifact_hash text null,
|
||||
test_artifact_hash text null,
|
||||
migration_artifact_hash text null,
|
||||
security_artifact_hash text null,
|
||||
rollback_artifact_hash text null,
|
||||
decision_log_hash text not null,
|
||||
status text not null check (status in ('DRAFT', 'REVIEW_REQUIRED', 'APPROVED', 'REJECTED', 'EXPIRED')),
|
||||
maker_id text not null,
|
||||
checker_id text null,
|
||||
created_at timestamptz not null,
|
||||
decided_at timestamptz null,
|
||||
content_hash text not null unique,
|
||||
check (status not in ('APPROVED', 'REJECTED') or (checker_id is not null and checker_id <> maker_id and decided_at is not null))
|
||||
);
|
||||
|
||||
create table if not exists governance.source_contract_snapshot (
|
||||
snapshot_id uuid primary key,
|
||||
source_code text not null,
|
||||
schema_version text not null,
|
||||
license_version text not null,
|
||||
sla_version text not null,
|
||||
timezone_contract text not null,
|
||||
unit_contract text not null,
|
||||
content_hash text not null unique,
|
||||
status text not null check (status in ('PROPOSED', 'APPROVED', 'RETIRED', 'QUARANTINED')),
|
||||
effective_from timestamptz null,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create table if not exists governance.execution_assurance_decision (
|
||||
decision_id uuid primary key,
|
||||
decision_code text not null,
|
||||
scope_key text not null,
|
||||
decision_version integer not null check (decision_version > 0),
|
||||
status text not null check (status in ('DECISION_REQUIRED', 'APPROVED', 'REJECTED', 'SUPERSEDED', 'EXPIRED')),
|
||||
statement text not null,
|
||||
basis_json jsonb not null,
|
||||
owner text not null,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
expires_at timestamptz null,
|
||||
content_hash text not null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (decision_code, scope_key, decision_version),
|
||||
unique (content_hash)
|
||||
);
|
||||
|
||||
-- Append-only operational evidence.
|
||||
drop trigger if exists model_operation_artifact_append_only on evaluation.model_operation_artifact;
|
||||
create trigger model_operation_artifact_append_only
|
||||
before update or delete on evaluation.model_operation_artifact
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists model_operation_dependency_result_append_only on evaluation.model_operation_dependency_result;
|
||||
create trigger model_operation_dependency_result_append_only
|
||||
before update or delete on evaluation.model_operation_dependency_result
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists release_evidence_bundle_append_only on governance.release_evidence_bundle;
|
||||
create trigger release_evidence_bundle_append_only
|
||||
before update or delete on governance.release_evidence_bundle
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists source_contract_snapshot_append_only on governance.source_contract_snapshot;
|
||||
create trigger source_contract_snapshot_append_only
|
||||
before update or delete on governance.source_contract_snapshot
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists execution_assurance_decision_append_only on governance.execution_assurance_decision;
|
||||
create trigger execution_assurance_decision_append_only
|
||||
before update or delete on governance.execution_assurance_decision
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
-- Additional checks are seeded disabled. They may be enabled only after their source/calendar/SLO contracts are approved.
|
||||
insert into evaluation.model_operation_schedule
|
||||
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
|
||||
schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner,
|
||||
calendar_id, due_policy, dependency_json, catch_up_policy, max_catch_up)
|
||||
values
|
||||
('e4100000-0000-4000-8000-000000000025', 'J25', 'SourceContractDriftCheck', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-evaluation', 1, false, now() + interval '1 day', interval '1 day', 'Data Governance', 'Adapter Owner/QA', 'GLOBAL_UTC', 'FIXED_CADENCE', '["SOURCE_CONTRACT_APPROVED"]', 'LATEST_ONLY', 1),
|
||||
('e4100000-0000-4000-8000-000000000026', 'J26', 'MarketCalendarCompletenessCheck', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-market-data', 1, false, now() + interval '1 day', interval '1 day', 'Data/Ops', 'Quant/QA', 'APPROVED_MARKET_CALENDAR', 'TRADING_SESSION', '["MARKET_CALENDAR_APPROVED","SOURCE_COMPLETE"]', 'LATEST_ONLY', 1),
|
||||
('e4100000-0000-4000-8000-000000000027', 'J27', 'EvidenceChainAudit', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-control', 1, false, now() + interval '1 day', interval '1 day', 'Compliance/QA', 'Data/BE', 'GLOBAL_UTC', 'FIXED_CADENCE', '["AUDIT_LEDGER_READY"]', 'LATEST_ONLY', 1),
|
||||
('e4100000-0000-4000-8000-000000000028', 'J28', 'ProjectionFreshnessCheck', 'GLOBAL', 'DAILY', 'EVALUATION_ONLY', 'q-evaluation', 1, false, now() + interval '1 day', interval '1 day', 'BE/Data', 'SRE/QA', 'GLOBAL_UTC', 'FIXED_CADENCE', '["PROJECTION_CONTRACT_APPROVED"]', 'LATEST_ONLY', 1),
|
||||
('e4100000-0000-4000-8000-000000000029', 'J29', 'CapacitySlaTrend', 'GLOBAL', 'WEEKLY', 'EVALUATION_ONLY', 'q-control', 1, false, now() + interval '7 days', interval '7 days', 'SRE/PM', 'DBA/Module Owner', 'GLOBAL_UTC', 'FIXED_CADENCE', '["SLO_APPROVED","VOLUME_ASSUMPTION_APPROVED"]', 'LATEST_ONLY', 1),
|
||||
('e4100000-0000-4000-8000-000000000030', 'J30', 'ReleaseEvidenceAssemble', 'GLOBAL', 'EVENT_DRIVEN', 'PROPOSAL_ONLY', 'q-control', 1, false, now() + interval '365 days', interval '365 days', 'QA/Release Manager', 'Compliance/SRE', 'GLOBAL_UTC', 'EVENT', '["BUILD_GREEN","TEST_GREEN","MIGRATION_GREEN","SECURITY_GREEN","ROLLBACK_READY"]', 'NONE', 0)
|
||||
on conflict (operation_code, scope_key, schedule_version) do nothing;
|
||||
@@ -0,0 +1,73 @@
|
||||
-- v13.0 continuous model feedback loop. All schedules are inserted disabled and are evidence/proposal only.
|
||||
create table if not exists evaluation.prediction_freeze (
|
||||
prediction_freeze_id uuid primary key,
|
||||
portfolio_scope text not null,
|
||||
market_session date not null,
|
||||
as_of timestamptz not null,
|
||||
dataset_id text not null,
|
||||
model_version text not null,
|
||||
config_version text not null,
|
||||
code_sha text not null,
|
||||
contract_version text not null,
|
||||
content_hash text not null unique,
|
||||
frozen_at timestamptz not null default now(),
|
||||
unique (portfolio_scope, market_session, model_version, content_hash)
|
||||
);
|
||||
create table if not exists evaluation.evaluation_window_maturity (
|
||||
maturity_id uuid primary key,
|
||||
decision_id uuid not null,
|
||||
window_sessions integer not null check (window_sessions in (1,5,20,63,126,252)),
|
||||
due_session date not null,
|
||||
maturity_revision integer not null check (maturity_revision > 0),
|
||||
status text not null check (status in ('PENDING','MATURED','HELD_DATA','EVALUATED','SUPERSEDED')),
|
||||
dataset_id text not null,
|
||||
supersedes_maturity_id uuid null references evaluation.evaluation_window_maturity(maturity_id),
|
||||
content_hash text not null unique,
|
||||
created_at timestamptz not null default now(),
|
||||
check (supersedes_maturity_id is null or supersedes_maturity_id <> maturity_id),
|
||||
unique (decision_id, window_sessions, dataset_id, maturity_revision)
|
||||
);
|
||||
create table if not exists governance.model_improvement_hypothesis (
|
||||
hypothesis_id uuid primary key,
|
||||
model_version text not null,
|
||||
hypothesis_version integer not null check (hypothesis_version > 0),
|
||||
problem_statement text not null,
|
||||
evidence_json jsonb not null,
|
||||
proposed_change text not null,
|
||||
expected_effect text not null,
|
||||
falsification_test text not null,
|
||||
non_goals text not null,
|
||||
status text not null check (status in ('DRAFT','REVIEW_REQUIRED','APPROVED_FOR_EXPERIMENT','REJECTED','SUPERSEDED','EXPIRED')),
|
||||
maker_id text not null,
|
||||
checker_id text null,
|
||||
supersedes_hypothesis_id uuid null references governance.model_improvement_hypothesis(hypothesis_id),
|
||||
expires_at timestamptz not null,
|
||||
content_hash text not null unique,
|
||||
created_at timestamptz not null default now(),
|
||||
decided_at timestamptz null,
|
||||
check (supersedes_hypothesis_id is null or supersedes_hypothesis_id <> hypothesis_id),
|
||||
check (status not in ('APPROVED_FOR_EXPERIMENT','REJECTED') or (checker_id is not null and checker_id <> maker_id and decided_at is not null)),
|
||||
unique (model_version, hypothesis_version)
|
||||
);
|
||||
|
||||
drop trigger if exists prediction_freeze_append_only on evaluation.prediction_freeze;
|
||||
create trigger prediction_freeze_append_only before update or delete on evaluation.prediction_freeze for each row execute function building_blocks.prevent_append_only_change();
|
||||
drop trigger if exists evaluation_window_maturity_append_only on evaluation.evaluation_window_maturity;
|
||||
create trigger evaluation_window_maturity_append_only before update or delete on evaluation.evaluation_window_maturity for each row execute function building_blocks.prevent_append_only_change();
|
||||
drop trigger if exists model_improvement_hypothesis_append_only on governance.model_improvement_hypothesis;
|
||||
create trigger model_improvement_hypothesis_append_only before update or delete on governance.model_improvement_hypothesis for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
insert into evaluation.model_operation_schedule
|
||||
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
|
||||
schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner,
|
||||
calendar_id, due_policy, dependency_json, catch_up_policy, max_catch_up)
|
||||
values
|
||||
('e4100000-0000-4000-8000-000000000031','J31','PredictionFreezeRun','GLOBAL','DAILY','EVALUATION_ONLY','q-evaluation',2,false,now()+interval '1 day',interval '1 day','Quant/Data','QA/Risk','APPROVED_MARKET_CALENDAR','TRADING_SESSION','["PIT_CONTEXT_APPROVED","MODEL_CONTEXT_APPROVED"]','LATEST_ONLY',1),
|
||||
('e4100000-0000-4000-8000-000000000032','J32','OutcomeMaturitySweep','GLOBAL','DAILY','EVALUATION_ONLY','q-evaluation',2,false,now()+interval '1 day',interval '1 day','Quant/Ops','Data/QA','APPROVED_MARKET_CALENDAR','TRADING_SESSION','["TOTAL_RETURN_COMPLETE","CALENDAR_APPROVED"]','BOUNDED',7),
|
||||
('e4100000-0000-4000-8000-000000000033','J33','SellReentryAttributionReview','GLOBAL','WEEKLY','EVALUATION_ONLY','q-evaluation',2,false,now()+interval '7 days',interval '7 days','Quant/Risk','Advisor/QA','GLOBAL_UTC','FIXED_CADENCE','["OUTCOME_MATURED","METRIC_CONTRACT_APPROVED"]','LATEST_ONLY',1),
|
||||
('e4100000-0000-4000-8000-000000000034','J34','CalibrationRegimeReview','GLOBAL','WEEKLY','EVALUATION_ONLY','q-evaluation',2,false,now()+interval '7 days',interval '7 days','Quant/Risk','Data/IndependentValidation','GLOBAL_UTC','FIXED_CADENCE','["COHORT_VERSION_APPROVED","REGIME_DEFINITION_APPROVED"]','LATEST_ONLY',1),
|
||||
('e4100000-0000-4000-8000-000000000035','J35','ImprovementHypothesisBuild','GLOBAL','MONTHLY','PROPOSAL_ONLY','q-research',2,false,now()+interval '30 days',interval '30 days','Quant Lead','Risk/Architect','GLOBAL_UTC','FIXED_CADENCE','["SCORECARD_COMPLETE","ATTRIBUTION_COMPLETE","DEBT_REGISTER_CURRENT"]','LATEST_ONLY',1),
|
||||
('e4100000-0000-4000-8000-000000000036','J36','ChallengerExperimentPlan','GLOBAL','MONTHLY','PROPOSAL_ONLY','q-research',2,false,now()+interval '30 days',interval '30 days','Quant Lead','IndependentValidation/QA','GLOBAL_UTC','FIXED_CADENCE','["HYPOTHESIS_APPROVED_FOR_EXPERIMENT","EXPERIMENT_REGISTRY_READY"]','LATEST_ONLY',1),
|
||||
('e4100000-0000-4000-8000-000000000037','J37','IndependentValidationPack','GLOBAL','QUARTERLY','PROPOSAL_ONLY','q-control',2,false,now()+interval '90 days',interval '90 days','IndependentValidation','Risk/Compliance','GLOBAL_UTC','FIXED_CADENCE','["OOS_COMPLETE","PBO_DSR_COMPLETE","REPRODUCIBILITY_COMPLETE"]','LATEST_ONLY',1),
|
||||
('e4100000-0000-4000-8000-000000000038','J38','ModelPolicyDebtReview','GLOBAL','MONTHLY','PROPOSAL_ONLY','q-control',2,false,now()+interval '30 days',interval '30 days','Risk/Architect','Quant/PM','GLOBAL_UTC','FIXED_CADENCE','["DEBT_REGISTER_CURRENT","INCIDENT_REVIEW_COMPLETE"]','LATEST_ONLY',1)
|
||||
on conflict (operation_code, scope_key, schedule_version) do nothing;
|
||||
@@ -0,0 +1,97 @@
|
||||
-- v14.0 governed feedback-cycle delta.
|
||||
-- This migration formalizes evidence-to-hypothesis-to-independent-validation history.
|
||||
-- It does not enable automatic model activation, client publication, automatic order submission or KIS submission.
|
||||
|
||||
create table if not exists evaluation.model_feedback_cycle (
|
||||
cycle_id uuid primary key,
|
||||
scope_key text not null,
|
||||
base_model_version text not null,
|
||||
state text not null check (state in (
|
||||
'PLANNED','PREDICTION_FROZEN','OUTCOMES_MATURING','EVALUATED','IMPROVEMENT_PROPOSED',
|
||||
'CHALLENGER_PLANNED','INDEPENDENTLY_VALIDATED','PROMOTION_REVIEW_PENDING','BUSINESS_HOLD','CLOSED')),
|
||||
revision integer not null check (revision > 0),
|
||||
started_at timestamptz not null,
|
||||
closed_at timestamptz null,
|
||||
last_evidence_hash text not null,
|
||||
updated_at timestamptz not null,
|
||||
check ((state = 'CLOSED') = (closed_at is not null))
|
||||
);
|
||||
|
||||
create index if not exists ix_model_feedback_cycle_scope_state
|
||||
on evaluation.model_feedback_cycle (scope_key, state, updated_at desc);
|
||||
|
||||
create table if not exists evaluation.model_feedback_transition (
|
||||
transition_id uuid primary key,
|
||||
cycle_id uuid not null references evaluation.model_feedback_cycle(cycle_id),
|
||||
sequence_no integer not null check (sequence_no > 0),
|
||||
from_state text not null,
|
||||
to_state text not null,
|
||||
reason_code text not null,
|
||||
evidence_hash text not null,
|
||||
actor_id text not null,
|
||||
actor_type text not null check (actor_type in ('SYSTEM','OPERATOR','INDEPENDENT_VALIDATOR','INVESTMENT_COMMITTEE')),
|
||||
correlation_id text not null,
|
||||
occurred_at timestamptz not null,
|
||||
content_hash text not null unique,
|
||||
unique (cycle_id, sequence_no)
|
||||
);
|
||||
|
||||
create table if not exists governance.model_hypothesis_evidence (
|
||||
hypothesis_evidence_id uuid primary key,
|
||||
hypothesis_id uuid not null references governance.model_improvement_hypothesis(hypothesis_id),
|
||||
side text not null check (side in ('SUPPORTING','COUNTER')),
|
||||
classification text not null check (classification in ('SOURCE','ASSUMPTION','UNKNOWN','DECISION_REQUIRED')),
|
||||
reference_code text not null,
|
||||
statement text not null,
|
||||
content_hash text not null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (hypothesis_id, side, reference_code, content_hash)
|
||||
);
|
||||
|
||||
create table if not exists governance.model_activation_decision (
|
||||
activation_decision_id uuid primary key,
|
||||
scope_key text not null,
|
||||
candidate_model_version text not null,
|
||||
promotion_review_id uuid not null references governance.model_promotion_review(review_id),
|
||||
decision text not null check (decision in ('KEEP_CURRENT_CHAMPION','ACTIVATE_FOR_SHADOW','ACTIVATE_FOR_PILOT','REJECT','EXPIRE')),
|
||||
effective_at timestamptz null,
|
||||
rollback_model_version text not null,
|
||||
maker_id text not null,
|
||||
checker_id text not null,
|
||||
actor_type text not null check (actor_type = 'HUMAN_CHANGE_APPROVAL'),
|
||||
change_ticket text not null,
|
||||
evidence_bundle_hash text not null,
|
||||
content_hash text not null unique,
|
||||
decided_at timestamptz not null,
|
||||
check (maker_id <> checker_id),
|
||||
check (decision not in ('ACTIVATE_FOR_SHADOW','ACTIVATE_FOR_PILOT') or effective_at is not null)
|
||||
);
|
||||
|
||||
-- Immutable evidence. The cycle aggregate itself remains mutable only through optimistic application logic;
|
||||
-- every transition is captured here and cannot be edited or deleted.
|
||||
drop trigger if exists model_feedback_transition_append_only on evaluation.model_feedback_transition;
|
||||
create trigger model_feedback_transition_append_only
|
||||
before update or delete on evaluation.model_feedback_transition
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists model_hypothesis_evidence_append_only on governance.model_hypothesis_evidence;
|
||||
create trigger model_hypothesis_evidence_append_only
|
||||
before update or delete on governance.model_hypothesis_evidence
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists model_activation_decision_append_only on governance.model_activation_decision;
|
||||
create trigger model_activation_decision_append_only
|
||||
before update or delete on governance.model_activation_decision
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
comment on table governance.model_activation_decision is
|
||||
'Human-only activation decision. Scheduler, Hangfire worker and proposal jobs must not insert into this table.';
|
||||
|
||||
-- Integrity audit is disabled until cycle aging, ownership and alert contracts are approved.
|
||||
insert into evaluation.model_operation_schedule
|
||||
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
|
||||
schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner,
|
||||
calendar_id, due_policy, dependency_json, catch_up_policy, max_catch_up)
|
||||
values
|
||||
('e4100000-0000-4000-8000-000000000039','J39','FeedbackCycleIntegrityAudit','GLOBAL','DAILY','EVALUATION_ONLY','q-control',1,false,now()+interval '1 day',interval '1 day','Compliance/QA','Quant/SRE','GLOBAL_UTC','FIXED_CADENCE','["FEEDBACK_CYCLE_CONTRACT_APPROVED","AGING_POLICY_APPROVED"]','LATEST_ONLY',1)
|
||||
on conflict (operation_code, scope_key, schedule_version) do nothing;
|
||||
@@ -0,0 +1,112 @@
|
||||
-- v15.0 execution-completeness delta.
|
||||
-- Evidence evaluation/proposal automation only. Automatic model activation, client publication,
|
||||
-- automatic order submission and KIS submission remain forbidden.
|
||||
|
||||
alter table evaluation.model_operation_schedule
|
||||
add column if not exists dispatch_revision integer not null default 0 check (dispatch_revision >= 0),
|
||||
add column if not exists anchor_at timestamptz null,
|
||||
add column if not exists timezone_id text not null default 'UTC',
|
||||
add column if not exists last_completed_at timestamptz null,
|
||||
add column if not exists last_hold_code text null;
|
||||
|
||||
update evaluation.model_operation_schedule
|
||||
set anchor_at = coalesce(anchor_at, next_due_at)
|
||||
where anchor_at is null;
|
||||
|
||||
alter table evaluation.model_operation_request
|
||||
add column if not exists execution_revision integer not null default 1 check (execution_revision > 0),
|
||||
add column if not exists last_heartbeat_at timestamptz null,
|
||||
add column if not exists hold_until timestamptz null,
|
||||
add column if not exists status_reason_code text null,
|
||||
add column if not exists scheduled_for timestamptz null;
|
||||
|
||||
create table if not exists evaluation.prediction_evaluation_window (
|
||||
window_request_id uuid primary key,
|
||||
prediction_evidence_id uuid not null,
|
||||
scope_key text not null,
|
||||
calendar_id text not null,
|
||||
prediction_session date not null,
|
||||
window_trading_days integer not null check (window_trading_days in (1,5,20,63,126,252)),
|
||||
due_session date not null,
|
||||
status text not null check (status in ('PLANNED','MATURED','EVALUATED','BUSINESS_HOLD','QUARANTINED')),
|
||||
dataset_id text not null,
|
||||
model_version text not null,
|
||||
metric_definition_set_hash text not null,
|
||||
content_hash text not null unique,
|
||||
created_at timestamptz not null,
|
||||
matured_at timestamptz null,
|
||||
evaluated_at timestamptz null,
|
||||
check (due_session >= prediction_session),
|
||||
unique (prediction_evidence_id, window_trading_days, metric_definition_set_hash)
|
||||
);
|
||||
|
||||
create index if not exists ix_prediction_evaluation_window_due
|
||||
on evaluation.prediction_evaluation_window (due_session, status, scope_key)
|
||||
where status in ('PLANNED','BUSINESS_HOLD');
|
||||
|
||||
create table if not exists evaluation.metric_cohort_definition (
|
||||
cohort_code text not null,
|
||||
definition_version integer not null check (definition_version > 0),
|
||||
scope_expression text not null,
|
||||
inclusion_expression text not null,
|
||||
exclusion_expression text not null,
|
||||
minimum_sample_size integer not null check (minimum_sample_size >= 0),
|
||||
effective_from timestamptz not null,
|
||||
effective_to timestamptz null,
|
||||
status text not null check (status in ('PROPOSED','APPROVED','RETIRED')),
|
||||
content_hash text not null unique,
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
primary key (cohort_code, definition_version),
|
||||
check (effective_to is null or effective_to > effective_from),
|
||||
check (status <> 'APPROVED' or (approved_by is not null and approved_at is not null))
|
||||
);
|
||||
|
||||
create table if not exists governance.ui_contract_release (
|
||||
ui_contract_release_id uuid primary key,
|
||||
contract_version text not null,
|
||||
provider_id text not null,
|
||||
provider_version text not null,
|
||||
capability_hash text not null,
|
||||
screen_template_hash text not null,
|
||||
accessibility_evidence_hash text not null,
|
||||
visual_regression_hash text null,
|
||||
performance_evidence_hash text null,
|
||||
status text not null check (status in ('PROPOSED','APPROVED','RETIRED')),
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null default now(),
|
||||
content_hash text not null unique,
|
||||
unique (contract_version, provider_id, provider_version),
|
||||
check (status <> 'APPROVED' or (approved_by is not null and approved_at is not null))
|
||||
);
|
||||
|
||||
-- Evaluation windows, cohort definitions and UI contract approvals are immutable evidence.
|
||||
drop trigger if exists prediction_evaluation_window_append_only on evaluation.prediction_evaluation_window;
|
||||
create trigger prediction_evaluation_window_append_only
|
||||
before update or delete on evaluation.prediction_evaluation_window
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists metric_cohort_definition_append_only on evaluation.metric_cohort_definition;
|
||||
create trigger metric_cohort_definition_append_only
|
||||
before update or delete on evaluation.metric_cohort_definition
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists ui_contract_release_append_only on governance.ui_contract_release;
|
||||
create trigger ui_contract_release_append_only
|
||||
before update or delete on governance.ui_contract_release
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
comment on table evaluation.prediction_evaluation_window is
|
||||
'Immutable 1/5/20/63/126/252 trading-session maturity plan. Revisions create new evidence rows.';
|
||||
comment on table governance.ui_contract_release is
|
||||
'Approved normalized UI contract/provider evidence. It does not authorize feature behavior or investment decisions.';
|
||||
|
||||
-- Integrity audit remains disabled until approved calendar/cohort definitions and alert runbook exist.
|
||||
insert into evaluation.model_operation_schedule
|
||||
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
|
||||
schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner,
|
||||
calendar_id, due_policy, dependency_json, catch_up_policy, max_catch_up, anchor_at, timezone_id)
|
||||
values
|
||||
('e4100000-0000-4000-8000-000000000040','J40','EvaluationWindowIntegrityAudit','GLOBAL','DAILY','EVALUATION_ONLY','q-control',1,false,now()+interval '1 day',interval '1 day','Quant/QA','Data/SRE','GLOBAL_UTC','FIXED_CADENCE','["TRADING_CALENDAR_APPROVED","METRIC_COHORT_APPROVED"]','LATEST_ONLY',1,now()+interval '1 day','UTC')
|
||||
on conflict (operation_code, scope_key, schedule_version) do nothing;
|
||||
@@ -0,0 +1,87 @@
|
||||
-- v16.0 reference implementation closure.
|
||||
-- Evaluation/proposal/audit only. Automatic model activation, customer publication,
|
||||
-- automatic order submission and KIS submission remain forbidden.
|
||||
|
||||
create table if not exists evaluation.model_operation_lease (
|
||||
operation_code text not null,
|
||||
scope_key text not null,
|
||||
fencing_token bigint not null check (fencing_token > 0),
|
||||
lease_owner text not null,
|
||||
acquired_at timestamptz not null,
|
||||
expires_at timestamptz not null,
|
||||
heartbeat_at timestamptz not null,
|
||||
dispatch_revision integer not null check (dispatch_revision >= 0),
|
||||
primary key (operation_code, scope_key),
|
||||
check (expires_at > acquired_at),
|
||||
check (heartbeat_at >= acquired_at)
|
||||
);
|
||||
|
||||
create table if not exists evaluation.model_evaluation_reconciliation (
|
||||
reconciliation_id uuid primary key,
|
||||
prediction_evidence_id uuid not null,
|
||||
window_trading_days integer not null check (window_trading_days in (1,5,20,63,126,252)),
|
||||
action text not null check (action in ('NONE','PLAN_MISSING','QUARANTINE_DUPLICATE','BUSINESS_HOLD_LATE','RECALCULATE_REVISION')),
|
||||
metric_definition_set_hash text not null,
|
||||
cohort_definition_hash text not null,
|
||||
dataset_id text not null,
|
||||
source_revision_hash text not null,
|
||||
reason_code text not null,
|
||||
content_hash text not null unique,
|
||||
created_at timestamptz not null,
|
||||
unique (prediction_evidence_id, window_trading_days, content_hash)
|
||||
);
|
||||
|
||||
create table if not exists building_blocks.projection_rebuild_request (
|
||||
rebuild_request_id uuid primary key,
|
||||
projection_name text not null,
|
||||
requested_version integer not null check (requested_version > 0),
|
||||
source_watermark text not null,
|
||||
requested_by text not null,
|
||||
reason_code text not null,
|
||||
status text not null check (status in ('REQUESTED','RUNNING','SUCCEEDED','FAILED','QUARANTINED')),
|
||||
before_hash text null,
|
||||
after_hash text null,
|
||||
requested_at timestamptz not null,
|
||||
completed_at timestamptz null,
|
||||
content_hash text not null unique,
|
||||
check (completed_at is null or completed_at >= requested_at)
|
||||
);
|
||||
|
||||
create table if not exists governance.ui_adapter_compatibility_evidence (
|
||||
compatibility_evidence_id uuid primary key,
|
||||
contract_version text not null,
|
||||
provider_id text not null,
|
||||
provider_version text not null,
|
||||
capability_hash text not null,
|
||||
contract_test_hash text not null,
|
||||
accessibility_test_hash text not null,
|
||||
visual_test_hash text null,
|
||||
performance_test_hash text null,
|
||||
rollback_test_hash text null,
|
||||
status text not null check (status in ('PROPOSED','APPROVED','REJECTED','RETIRED')),
|
||||
approved_by text null,
|
||||
approved_at timestamptz null,
|
||||
created_at timestamptz not null,
|
||||
content_hash text not null unique,
|
||||
check (status <> 'APPROVED' or (approved_by is not null and approved_at is not null))
|
||||
);
|
||||
|
||||
drop trigger if exists model_evaluation_reconciliation_append_only on evaluation.model_evaluation_reconciliation;
|
||||
create trigger model_evaluation_reconciliation_append_only before update or delete on evaluation.model_evaluation_reconciliation
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
drop trigger if exists ui_adapter_compatibility_evidence_append_only on governance.ui_adapter_compatibility_evidence;
|
||||
create trigger ui_adapter_compatibility_evidence_append_only before update or delete on governance.ui_adapter_compatibility_evidence
|
||||
for each row execute function building_blocks.prevent_append_only_change();
|
||||
|
||||
insert into evaluation.model_operation_schedule
|
||||
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
|
||||
schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner,
|
||||
calendar_id, due_policy, dependency_json, catch_up_policy, max_catch_up, anchor_at, timezone_id)
|
||||
values
|
||||
('e4100000-0000-4000-8000-000000000041','J41','SchedulerLeaseIntegrityAudit','GLOBAL','DAILY','EVALUATION_ONLY','q-control',1,false,now()+interval '1 day',interval '1 day','SRE/QA','BE/DBA','GLOBAL_UTC','FIXED_CADENCE','["LEASE_CONTRACT_APPROVED","ALERT_RUNBOOK_APPROVED"]','LATEST_ONLY',1,now()+interval '1 day','UTC'),
|
||||
('e4100000-0000-4000-8000-000000000042','J42','ModelEvaluationReconciliation','GLOBAL','DAILY','EVALUATION_ONLY','q-evaluation',1,false,now()+interval '1 day',interval '1 day','Quant/QA','Data/Risk','GLOBAL_UTC','FIXED_CADENCE','["TRADING_CALENDAR_APPROVED","METRIC_COHORT_APPROVED","SOURCE_REVISION_INDEX_READY"]','LATEST_ONLY',1,now()+interval '1 day','UTC')
|
||||
on conflict (operation_code, scope_key, schedule_version) do nothing;
|
||||
|
||||
comment on table evaluation.model_operation_lease is 'Lease fencing prevents stale workers from advancing schedules or duplicating model evaluation side effects.';
|
||||
comment on table evaluation.model_evaluation_reconciliation is 'Append-only evaluation repair plan. It never activates a model or submits an order.';
|
||||
Reference in New Issue
Block a user