muse-agent-example/db/ddl/106-example事实增量.sql
zizi b0bc7a8745 框架: 技能按动作-对象重组 + 先审后入创作闭环
一、技能重组(动作-对象命名)
- 旧目录 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)按"框架与创作分开"未入本提交。
2026-08-14 10:24:08 +08:00

98 lines
6.0 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 事实增量两表:example_fact_delta(增量提案,可变状态机)+ example_fact_ledger(已接受正典事实,append-only)。
-- 先审后入 + 结构化增量(05 §5、02/06 领域合同):
-- 模型只能提出有限类型化变更(DeltaProposal:人物位置/知情/关系/伏笔/时间线/设定),不能重写整份状态;
-- 提案必须带正文证据引文(确定性绑定到候选正文);抽取结果一律不自动升格;
-- 只有被批准的增量随正文在同一事务进账本(ChapterCommit),账本绑正文块 revision 与 command_id,
-- 正文被替换后按 source_block_revision 判 stale。
-- example_fact_delta 可变(proposed→accepted/rejected/superseded)带公共列六件套;
-- example_fact_ledger 是 append-only 账本,只带归属列,不带 updater/update_time/deleted。
-- 全部对象 IF NOT EXISTS / OR REPLACE,重复 apply 幂等。
-- ══ example_fact_delta:增量提案(可变状态机)══
CREATE TABLE IF NOT EXISTS example_fact_delta (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL, -- → muse_content_work.id(软引用)
target_chapter INTEGER NOT NULL, -- 提案所属章
run_id VARCHAR(64), -- → example_run.run_id(软引用)
candidate_sha256 CHAR(64) NOT NULL, -- 绑候选正文:提案必须出自该正文的审查
delta_id VARCHAR(64) NOT NULL, -- 本批提案内稳定 ID(delta-1…)
delta_type VARCHAR(40) NOT NULL, -- 类型闭集(见 chk)
subject_key VARCHAR(120), -- 变更主体(人物名/伏笔 ID/关系对),观察索引
payload JSONB NOT NULL, -- 类型化变更内容(reducer 逐型校验)
evidence_quote TEXT, -- 正文证据引文(必须出现在候选正文中)
status VARCHAR(20) NOT NULL DEFAULT 'proposed', -- proposed/accepted/rejected/superseded
decided_by VARCHAR(64), -- 批准/驳回人(升格必须人批准)
decision_rationale TEXT, -- 理据
source_revision INTEGER, -- 接受时正文块 revision(与账本登记同值)
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_example_fact_delta UNIQUE (tenant_id, candidate_sha256, delta_id),
CONSTRAINT chk_example_fact_delta_type
CHECK (delta_type IN ('character_location_changed','character_knowledge_added',
'relationship_changed','hook_advanced','timeline_event_added',
'setting_added')),
CONSTRAINT chk_example_fact_delta_status
CHECK (status IN ('proposed','accepted','rejected','superseded')),
CONSTRAINT chk_example_fact_delta_sha256
CHECK (candidate_sha256 ~ '^[0-9a-f]{64}$')
);
CREATE INDEX IF NOT EXISTS idx_example_fact_delta_work
ON example_fact_delta(tenant_id, work_id, target_chapter, status);
CREATE INDEX IF NOT EXISTS idx_example_fact_delta_subject
ON example_fact_delta(tenant_id, work_id, subject_key);
CREATE OR REPLACE TRIGGER trg_example_fact_delta_updated_at
BEFORE UPDATE ON example_fact_delta FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ══ example_fact_ledger:已接受正典事实账本(append-only)══
-- 一条 = 一个已随正文提交的事实增量;source_block_revision 是"这条事实由哪一版正文背书"的判据。
CREATE TABLE IF NOT EXISTS example_fact_ledger (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT NOT NULL, -- → muse_content_work.id(软引用)
target_chapter INTEGER NOT NULL, -- 事实成立的章
delta_ref BIGINT, -- → example_fact_delta.id(软引用)
delta_type VARCHAR(40) NOT NULL,
subject_key VARCHAR(120),
payload JSONB NOT NULL,
source_candidate_sha256 CHAR(64) NOT NULL, -- 产生该事实的候选正文哈希
source_block_revision INTEGER NOT NULL, -- 同事务写入的正文块 revision
command_id VARCHAR(120), -- 与正文提交同一幂等键
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT chk_example_fact_ledger_type
CHECK (delta_type IN ('character_location_changed','character_knowledge_added',
'relationship_changed','hook_advanced','timeline_event_added',
'setting_added')),
CONSTRAINT chk_example_fact_ledger_sha256
CHECK (source_candidate_sha256 ~ '^[0-9a-f]{64}$'),
CONSTRAINT chk_example_fact_ledger_revision
CHECK (source_block_revision >= 1)
);
CREATE INDEX IF NOT EXISTS idx_example_fact_ledger_work
ON example_fact_ledger(tenant_id, work_id, target_chapter);
CREATE INDEX IF NOT EXISTS idx_example_fact_ledger_subject
ON example_fact_ledger(tenant_id, work_id, subject_key);
CREATE INDEX IF NOT EXISTS idx_example_fact_ledger_revision
ON example_fact_ledger(tenant_id, work_id, source_block_revision);
CREATE OR REPLACE FUNCTION reject_example_fact_ledger_mutation()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION 'example_fact_ledger 是 append-only 表,禁止 UPDATE/DELETE';
END;
$$;
CREATE OR REPLACE TRIGGER trg_example_fact_ledger_append_only
BEFORE UPDATE OR DELETE ON example_fact_ledger
FOR EACH ROW EXECUTE FUNCTION reject_example_fact_ledger_mutation();
CREATE OR REPLACE TRIGGER trg_example_fact_ledger_no_truncate
BEFORE TRUNCATE ON example_fact_ledger
FOR EACH STATEMENT EXECUTE FUNCTION example_reject_truncate();