Initial commit: Add project files
This commit is contained in:
@@ -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();
|
||||
Reference in New Issue
Block a user