-- AI 味案例卡、来源重验证批次与逐卡回执。 -- agent-example 的 muse-example 是此资产的正式权威;离线 JSON 只是可复核的导入/恢复证据。 -- 案例卡是当前投影(可按评审更新);批次与回执是 append-only,保留每次检查历史。 -- 研究限定/未授权来源只允许 hash + 位置,数据库级约束禁止写入原文或升格。 CREATE TABLE IF NOT EXISTS example_ai_flavor_case ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, card_id VARCHAR(64) NOT NULL, schema_version VARCHAR(64) NOT NULL, card_type VARCHAR(64) NOT NULL, state VARCHAR(20) NOT NULL, label VARCHAR(20) NOT NULL, layer VARCHAR(20) NOT NULL, carrier VARCHAR(32) NOT NULL, capture_mode VARCHAR(32) NOT NULL, source_kind VARCHAR(32) NOT NULL, source_license VARCHAR(32) NOT NULL, source_sha256 VARCHAR(64) NOT NULL, excerpt_sha256 VARCHAR(64) NOT NULL, source_ref VARCHAR(512) NOT NULL DEFAULT '', work_ref VARCHAR(256), source_location JSONB NOT NULL, surface_location JSONB, excerpt TEXT NOT NULL DEFAULT '', context TEXT NOT NULL DEFAULT '', observation JSONB NOT NULL, feedback JSONB, review JSONB, rule_candidate_ids JSONB NOT NULL DEFAULT '[]'::jsonb, inventory_ref VARCHAR(512), inventory_sha256 VARCHAR(64), creator VARCHAR(64) NOT NULL DEFAULT '1', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updater VARCHAR(64) NOT NULL DEFAULT '1', update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted BOOLEAN NOT NULL DEFAULT FALSE, tenant_id BIGINT NOT NULL DEFAULT 1, CONSTRAINT uk_example_ai_flavor_case UNIQUE (tenant_id, card_id), CONSTRAINT chk_example_ai_flavor_case_schema CHECK (schema_version = 'ai-flavor-case-v1'), CONSTRAINT chk_example_ai_flavor_case_type CHECK (card_type = 'ai_flavor_case'), CONSTRAINT chk_example_ai_flavor_case_state CHECK (state IN ('shadow','canonical','rejected','archived')), CONSTRAINT chk_example_ai_flavor_case_label CHECK (label IN ('unclassified','sf','snf','boundary','regression')), CONSTRAINT chk_example_ai_flavor_case_layer CHECK (layer IN ('mechanical','lexical','structural','density','semantic','unknown')), CONSTRAINT chk_example_ai_flavor_case_carrier CHECK (carrier IN ('narration','dialogue','monologue','in_text_carrier','mixed','unknown')), CONSTRAINT chk_example_ai_flavor_case_capture CHECK (capture_mode IN ('backfill','live_feedback','synthetic','review_import')), CONSTRAINT chk_example_ai_flavor_case_source_kind CHECK (source_kind IN ('existing_work','creation_feedback','synthetic','public_domain')), CONSTRAINT chk_example_ai_flavor_case_license CHECK (source_license IN ('owned','licensed','public_domain','research_only','unauthorized','synthetic')), CONSTRAINT chk_example_ai_flavor_case_id CHECK (card_id ~ '^case-[a-z0-9]{20}$'), CONSTRAINT chk_example_ai_flavor_case_source_sha CHECK (source_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_case_excerpt_sha CHECK (excerpt_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_case_inventory_sha CHECK (inventory_sha256 IS NULL OR inventory_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_case_json_arrays CHECK (jsonb_typeof(rule_candidate_ids) = 'array'), CONSTRAINT chk_example_ai_flavor_case_research_only CHECK ( source_license NOT IN ('research_only','unauthorized') OR (excerpt = '' AND context = '' AND state = 'shadow') ) ); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_case_work_state ON example_ai_flavor_case(tenant_id, work_ref, state) WHERE deleted = FALSE; CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_case_pattern ON example_ai_flavor_case(tenant_id, ((observation->>'pattern_key'))) WHERE deleted = FALSE; CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_case_source_sha ON example_ai_flavor_case(tenant_id, source_sha256); CREATE OR REPLACE TRIGGER trg_example_ai_flavor_case_updated_at BEFORE UPDATE ON example_ai_flavor_case FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TABLE IF NOT EXISTS example_ai_flavor_revalidation_batch ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, batch_ref VARCHAR(512) NOT NULL, report_sha256 VARCHAR(64) NOT NULL, schema_version VARCHAR(64) NOT NULL, checked_on DATE NOT NULL, source_root_ref VARCHAR(512), usable BOOLEAN NOT NULL, totals JSONB NOT NULL, works JSONB NOT NULL DEFAULT '[]'::jsonb, report_path VARCHAR(512), creator VARCHAR(64) NOT NULL DEFAULT '1', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, tenant_id BIGINT NOT NULL DEFAULT 1, CONSTRAINT uk_example_ai_flavor_revalidation_batch UNIQUE (tenant_id, report_sha256), CONSTRAINT chk_example_ai_flavor_revalidation_batch_sha CHECK (report_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_revalidation_batch_schema CHECK (schema_version = 'ai-flavor-revalidation-v1'), CONSTRAINT chk_example_ai_flavor_revalidation_batch_json CHECK ( jsonb_typeof(totals) = 'object' AND jsonb_typeof(works) = 'array' ) ); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_revalidation_batch_date ON example_ai_flavor_revalidation_batch(tenant_id, checked_on DESC, id DESC); CREATE TABLE IF NOT EXISTS example_ai_flavor_revalidation ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, batch_id BIGINT NOT NULL, card_id VARCHAR(64) NOT NULL, work_ref VARCHAR(256), source_ref VARCHAR(512) NOT NULL DEFAULT '', expected_source_sha256 VARCHAR(64) NOT NULL, actual_source_sha256 VARCHAR(64), status VARCHAR(20) NOT NULL, reason VARCHAR(64) NOT NULL DEFAULT '', checked_on DATE NOT NULL, creator VARCHAR(64) NOT NULL DEFAULT '1', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, tenant_id BIGINT NOT NULL DEFAULT 1, CONSTRAINT uk_example_ai_flavor_revalidation UNIQUE (tenant_id, batch_id, card_id), CONSTRAINT chk_example_ai_flavor_revalidation_card CHECK (card_id ~ '^case-[a-z0-9]{20}$'), CONSTRAINT chk_example_ai_flavor_revalidation_expected_sha CHECK (expected_source_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_revalidation_actual_sha CHECK (actual_source_sha256 IS NULL OR actual_source_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_revalidation_status CHECK (status IN ('verified','stale','unavailable','card_mismatch')) ); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_revalidation_card ON example_ai_flavor_revalidation(tenant_id, card_id, checked_on DESC); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_revalidation_status ON example_ai_flavor_revalidation(tenant_id, status, checked_on DESC); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_revalidation_batch ON example_ai_flavor_revalidation(tenant_id, batch_id); CREATE OR REPLACE FUNCTION reject_example_ai_flavor_revalidation_mutation() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION '% 是 append-only 表,禁止 UPDATE/DELETE', TG_TABLE_NAME; END; $$; CREATE OR REPLACE TRIGGER trg_example_ai_flavor_revalidation_batch_append_only BEFORE UPDATE OR DELETE ON example_ai_flavor_revalidation_batch FOR EACH ROW EXECUTE FUNCTION reject_example_ai_flavor_revalidation_mutation(); CREATE OR REPLACE TRIGGER trg_example_ai_flavor_revalidation_append_only BEFORE UPDATE OR DELETE ON example_ai_flavor_revalidation FOR EACH ROW EXECUTE FUNCTION reject_example_ai_flavor_revalidation_mutation(); CREATE OR REPLACE TRIGGER trg_example_ai_flavor_revalidation_batch_no_truncate BEFORE TRUNCATE ON example_ai_flavor_revalidation_batch FOR EACH STATEMENT EXECUTE FUNCTION example_reject_truncate(); CREATE OR REPLACE TRIGGER trg_example_ai_flavor_revalidation_no_truncate BEFORE TRUNCATE ON example_ai_flavor_revalidation FOR EACH STATEMENT EXECUTE FUNCTION example_reject_truncate(); GRANT SELECT ON example_ai_flavor_case TO muse_read; GRANT SELECT ON example_ai_flavor_revalidation_batch TO muse_read; GRANT SELECT ON example_ai_flavor_revalidation TO muse_read;