-- 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;
