muse-agent-example/db/ddl/108-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

68 lines
4.1 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_lesson(可变状态机)。
-- 质量与复利(06):运行中的教训(lesson)与成功(win)先登记为证据,绑定 run_id 与候选哈希;
-- 升格路径 proposed → reviewing → promoted/rejected,**只能人工批准**——脚本只负责登记与
-- 状态流转,任何自动升格(单章结果直接污染全局范式/技能)都被状态机触发器拒绝。
-- 升格目标三型:pattern(范式)/ skill(技能合同)/ tool(确定性工具),promoted 时必须指明。
-- 全部对象 IF NOT EXISTS / OR REPLACE,重复 apply 幂等。
CREATE TABLE IF NOT EXISTS example_lesson (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
work_id BIGINT, -- 可空:跨作品经验为空,单作品经验填作品
run_id VARCHAR(64), -- → example_run.run_id(证据:哪次运行学到的)
candidate_sha256 CHAR(64), -- 证据:绑定候选正文哈希(可空:非正文类经验)
kind VARCHAR(20) NOT NULL, -- lesson(教训)/ win(成功经验)
title VARCHAR(200) NOT NULL, -- 一句话经验
detail JSONB NOT NULL, -- 情境/行动/结果/证据指针(结构化)
status VARCHAR(20) NOT NULL DEFAULT 'proposed', -- proposed/reviewing/promoted/rejected
promotion_target_type VARCHAR(20), -- pattern/skill/tool(promoted 时必填)
promotion_target_ref VARCHAR(120), -- 升格落点(范式卡 ID / skill 名 / 工具路径)
decided_by VARCHAR(64), -- 批准/驳回人(升格必须人批准)
decision_rationale TEXT, -- 理据
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 chk_example_lesson_kind CHECK (kind IN ('lesson','win')),
CONSTRAINT chk_example_lesson_status
CHECK (status IN ('proposed','reviewing','promoted','rejected')),
CONSTRAINT chk_example_lesson_target_type
CHECK (promotion_target_type IS NULL OR promotion_target_type IN ('pattern','skill','tool')),
CONSTRAINT chk_example_lesson_sha256
CHECK (candidate_sha256 IS NULL OR candidate_sha256 ~ '^[0-9a-f]{64}$')
);
CREATE INDEX IF NOT EXISTS idx_example_lesson_work
ON example_lesson(tenant_id, work_id, status);
CREATE INDEX IF NOT EXISTS idx_example_lesson_status
ON example_lesson(tenant_id, status, kind);
CREATE OR REPLACE TRIGGER trg_example_lesson_updated_at
BEFORE UPDATE ON example_lesson FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 升格状态机:proposed→reviewing/rejected;reviewing→promoted/rejected;promoted/rejected 终态。
-- 跳过评审直接 promoted(自动升格)在 DB 级拒绝——升格必须经过人工 reviewing 环节。
CREATE OR REPLACE FUNCTION enforce_example_lesson_transition()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF OLD.status = 'proposed' AND NEW.status NOT IN ('reviewing','rejected') THEN
RAISE EXCEPTION 'proposed 经验只能进 reviewing 或被 rejected,不得直接 %(禁止自动升格)', NEW.status;
END IF;
IF OLD.status = 'reviewing' AND NEW.status NOT IN ('promoted','rejected') THEN
RAISE EXCEPTION 'reviewing 经验只能 promoted 或 rejected,得到 %', NEW.status;
END IF;
IF OLD.status IN ('promoted','rejected') THEN
RAISE EXCEPTION '% 是经验终态,不得再流转', OLD.status;
END IF;
IF NEW.status = 'promoted' AND (NEW.promotion_target_type IS NULL OR NEW.promotion_target_ref IS NULL) THEN
RAISE EXCEPTION 'promoted 必须指明升格目标类型与落点';
END IF;
RETURN NEW;
END;
$$;
CREATE OR REPLACE TRIGGER trg_example_lesson_transition
BEFORE UPDATE ON example_lesson
FOR EACH ROW EXECUTE FUNCTION enforce_example_lesson_transition();