一、技能重组(动作-对象命名) - 旧目录 clean/confirm/continuation/db/detect/embed/… 重组为 clean-book-text/decide-candidate/write-next-chapter/access-database/ check-content-consistency/embed-knowledge/…(git 识别为 rename,内容保持) - agents/*.md、AGENTS.md/CLAUDE.md 收编、example_skill 登记表同步新名 二、先审后入创作闭环(本次核心) 正文接受从"机械门一过就写正典"改为"机械门+语义审查双通过+用户批准+单事务原子提交", DB 级兜底,编排层跳步即被硬拒。 - candidate_cas.py + example_candidate_cas(109):持久化 CAS 状态链 - fact_delta.py + example_fact_delta/example_fact_ledger(106):结构化事实增量, 模型只提六型闭集增量+正文证据引文,仅用户批准的增量随正文同事务入账本 - projection_registry.py + example_projection_run(107):投影登记与恢复 - acceptance_state.py:接受前置实时状态重读 - lesson_registry.py + example_lesson(108):经验升格链,禁止自动升格 - DDL 105:example_candidate 增 semantic_status/semantic_report_sha256 - write_canonical.accept:语义兜底+同事务合并增量+登记投影; run_writer_pipeline/persist_writer_run/run_writer_semantic_detector/step2 接入全链 - claude_runtime:兼容新 CLI modelUsage 信息字段 三、审查修复(独立子代理四维审查后) - 事实增量 propose→approve 翻态正道,不撞唯一键 - 冻结配置探针重刷(CLI 2.1.211→2.1.231 漂移),profileSha256/adapterVersion 再登记 - 可视化合同悬空路径/五六空间矛盾、 SoT 旧技能名漂移、行尾空白清理 测试:离线 65 套 + 真实库集成 5 套(CAS/接受故障注入/事实增量/投影/经验升格)+ 回放 79 项全绿。 创作内容(docs/design、生成正文 artifacts)按"框架与创作分开"未入本提交。
143 lines
8.0 KiB
PL/PgSQL
143 lines
8.0 KiB
PL/PgSQL
-- 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;
|