From c154ca908525033aa540680215aea2394644fc00 Mon Sep 17 00:00:00 2001 From: zizi Date: Fri, 18 Sep 2026 01:15:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=8E=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=EF=BC=9A=E8=BF=81=E7=A7=BB=20V0056=E2=80=93V0060=20?= =?UTF-8?q?=E4=B8=8E=E6=B5=81=E7=A8=8B=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 迁移:调用结算与配置验证(V0056)、来源当前授权(V0057)、成品补充验证回执(V0058)、检索索引代次(V0059)、 评测单元复用来源(V0060);新表按既有约定加只追加守卫与角色授权。 - 流程模板与载入口径同步(内联保护字段改为登记类型约束);旧库迁移工具链按新表结构对齐。 - 配置:提供方模板与运行配置同步角色策略版本;角色策略白名单新增 qwen3.8-flash(见收尾报告待裁决项: 该模型精确身份与独立性尚未核验,且策略版本号未随白名单升版)。 --- 数据库/旧库迁移/入口.py | 38 +++++++++------ 数据库/旧库迁移/切换检查.py | 4 +- 数据库/旧库迁移/导入新库.py | 3 +- 数据库/旧库迁移/建立映射.py | 21 +++++++++ 数据库/旧库迁移/核对结果.py | 3 +- 数据库/旧库迁移/知识记录分流.py | 57 +++++++++++++++-------- 数据库/旧库迁移/转换PG作品.py | 9 ++-- 数据库/迁移/V0056__调用结算与配置验证.sql | 55 ++++++++++++++++++++++ 数据库/迁移/V0057__来源当前授权.sql | 3 ++ 数据库/迁移/V0058__成品补充验证回执.sql | 17 +++++++ 数据库/迁移/V0059__检索索引代次.sql | 31 ++++++++++++ 数据库/迁移/V0060__评测单元复用来源.sql | 14 ++++++ 配置/提供方.example.toml | 3 +- 配置/流程模板/完整审校.yaml | 1 - 配置/流程模板/审校修订.yaml | 1 - 配置/流程模板/执行评测-检测.yaml | 1 - 配置/流程模板/执行评测-比较.yaml | 1 - 配置/流程模板/执行评测-生成.yaml | 1 - 配置/流程模板/生成正文.yaml | 1 - 配置/流程模板/生成规划.yaml | 1 - 配置/角色策略.yaml | 1 + 21 files changed, 217 insertions(+), 49 deletions(-) create mode 100644 数据库/迁移/V0056__调用结算与配置验证.sql create mode 100644 数据库/迁移/V0057__来源当前授权.sql create mode 100644 数据库/迁移/V0058__成品补充验证回执.sql create mode 100644 数据库/迁移/V0059__检索索引代次.sql create mode 100644 数据库/迁移/V0060__评测单元复用来源.sql diff --git a/数据库/旧库迁移/入口.py b/数据库/旧库迁移/入口.py index a2f628d..d061436 100644 --- a/数据库/旧库迁移/入口.py +++ b/数据库/旧库迁移/入口.py @@ -10,7 +10,7 @@ import tempfile from collections import Counter from pathlib import Path -from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 迁移错误 +from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 检查目标快照, 迁移错误 from 知识记录分流 import 分流批次 文件上限 = 16 * 1024 * 1024 @@ -159,6 +159,14 @@ def _目标命令(值) -> int: if not isinstance(输入, list) or not all(isinstance(r, dict) for r in 输入): raise 迁移错误("snapshot_invalid", "快照必须是记录数组") 快照 = [旧快照.从载荷(r) for r in 输入] + 检查目标快照(快照) + 当前 = None + if 值.command == "切换检查": + 当前输入 = _读文件(值.当前快照) + if not isinstance(当前输入, list) or not all(isinstance(r, dict) for r in 当前输入): + raise 迁移错误("snapshot_invalid", "当前快照必须是记录数组") + 当前 = [旧快照.从载荷(r) for r in 当前输入] + 检查目标快照(当前, 范围名称="当前") 映射 = 映射配置(_读文件(值.映射)) 配置 = 读取配置(值.配置) if 配置.HTTP is None: @@ -170,20 +178,20 @@ def _目标命令(值) -> int: "目标回执": 值.目标回执, "文件回执": 值.文件回执, } - if 值.command == "导入目标": - 结果 = 导入快照(装配, 身份, 快照, 映射, **参数) - 通过 = 结果["all_accounted"] and not 结果["states"].get("quarantined") - elif 值.command == "核对目标": - 请求 = _读文件(值.来源回放) if 值.来源回放 else None - 结果 = 核对迁移结果(装配, 身份, 快照, 映射, 来源回放=请求, **参数) - 通过 = 结果["data_checks_passed"] - else: - 当前输入 = _读文件(值.当前快照) - if not isinstance(当前输入, list) or not all(isinstance(r, dict) for r in 当前输入): - raise 迁移错误("snapshot_invalid", "当前快照必须是记录数组") - 结果 = 检查切换条件(装配, 身份, 快照, [旧快照.从载荷(r) for r in 当前输入], 映射, **参数) - 通过 = 结果["data_checks_passed"] - _保存文件(值.输出, 稳定JSON(结果) + "\n") + # 常驻数据库工厂只在生命周期内开池;目标命令借的还是同一个受控目标。 + with 装配.生命周期(): + if 值.command == "导入目标": + 结果 = 导入快照(装配, 身份, 快照, 映射, **参数) + 通过 = 结果["all_accounted"] and not 结果["states"].get("quarantined") + elif 值.command == "核对目标": + 请求 = _读文件(值.来源回放) if 值.来源回放 else None + 结果 = 核对迁移结果(装配, 身份, 快照, 映射, 来源回放=请求, **参数) + 通过 = 结果["data_checks_passed"] + else: + assert 当前 is not None + 结果 = 检查切换条件(装配, 身份, 快照, 当前, 映射, **参数) + 通过 = 结果["data_checks_passed"] + _保存文件(值.输出, 稳定JSON(结果) + "\n") print( 值.command + (":本范围数据检查通过" if 通过 else ":有隔离或未闭合项") diff --git a/数据库/旧库迁移/切换检查.py b/数据库/旧库迁移/切换检查.py index 89d9ab3..fcbfe58 100644 --- a/数据库/旧库迁移/切换检查.py +++ b/数据库/旧库迁移/切换检查.py @@ -2,7 +2,7 @@ from __future__ import annotations -from 建立映射 import 旧快照 +from 建立映射 import 旧快照, 检查目标快照 from 核对结果 import 核对迁移结果 @@ -22,6 +22,8 @@ def 核对快照变化(基线: list[旧快照], 当前: list[旧快照]) -> dict def 检查切换条件(装配, 身份, 基线: list[旧快照], 当前: list[旧快照], 映射, **目标参数) -> dict: + 检查目标快照(基线, 范围名称="基线") + 检查目标快照(当前, 范围名称="当前") 核对 = 核对迁移结果(装配, 身份, 基线, 映射, **目标参数) 增量 = 核对快照变化(基线, 当前) return { diff --git a/数据库/旧库迁移/导入新库.py b/数据库/旧库迁移/导入新库.py index 611386a..22f8aac 100644 --- a/数据库/旧库迁移/导入新库.py +++ b/数据库/旧库迁移/导入新库.py @@ -24,7 +24,7 @@ from muse.基础设施.数据库.连接 import 数据库工厂, 解析连接串 from muse.正式变更.接口 import 变更错误 from muse.配置 import 数据库引用 from 关联已确认 import 关联源指针, 冻结模式, 正式旧表 -from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 迁移错误 +from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 检查目标快照, 迁移错误 from 映射台账 import 迁移台账 from 知识记录分流 import 分流批次 from 转换业务对象 import 准备目标请求, 提交目标请求, 核对目标产物 @@ -334,6 +334,7 @@ def 导入快照( 文件回执: Path | None = None, ) -> dict: """重放真实S01命令补齐映射;终态台账不是业务对象存在的替代证明。""" + 检查目标快照(快照) 数据库 = 数据库工厂(装配.配置.数据库, 装配.配置.运行用途) 核对隔离目标(数据库, 允许实例=允许实例, 管理引用=管理引用, 目标回执=目标回执) if ( diff --git a/数据库/旧库迁移/建立映射.py b/数据库/旧库迁移/建立映射.py index 99802ac..1ab81ec 100644 --- a/数据库/旧库迁移/建立映射.py +++ b/数据库/旧库迁移/建立映射.py @@ -111,6 +111,27 @@ class 旧快照: return str(uuid5(NAMESPACE_URL, "muse:legacy:" + 稳定JSON(材料))) +def 检查目标快照(快照: list[旧快照], *, 范围名称: str = "输入") -> None: + """目标操作只接受具名非空记录范围;空数组不证明已扫描任何源表。""" + if not isinstance(快照, list) or not 快照: + raise 迁移错误("snapshot_empty", 范围名称 + "快照必须包含明确来源记录") + for 项 in 快照: + if not isinstance(项, 旧快照): + raise 迁移错误("snapshot_invalid", 范围名称 + "快照包含无来源身份的记录") + # 直接构造的dataclass也须有完整来源;不重编码整份内容或重复计算源哈希。 + if not isinstance(项.source, 源身份): + raise 迁移错误("snapshot_invalid", 范围名称 + "快照缺少来源身份") + try: + 原行 = 项.原行 + except (ValueError, TypeError) as 错: + raise 迁移错误("snapshot_invalid", 范围名称 + "快照原行不是有效JSON") from 错 + if not isinstance(原行, dict): + raise 迁移错误("snapshot_invalid", 范围名称 + "快照原行必须是对象") + for 键 in ("id", "revision"): + if 键 in 原行 and str(原行[键]) != getattr(项.source, 键): + raise 迁移错误("source_invalid", "原行与快照来源身份不一致:" + 键) + + def 数据集命名空间(标识: str, **范围) -> str: if ( not isinstance(标识, str) diff --git a/数据库/旧库迁移/核对结果.py b/数据库/旧库迁移/核对结果.py index c84fa70..e708fdf 100644 --- a/数据库/旧库迁移/核对结果.py +++ b/数据库/旧库迁移/核对结果.py @@ -13,7 +13,7 @@ from muse.基础设施.数据库.连接 import 数据库工厂 from muse.配置 import 数据库引用 from 关联已确认 import 冻结模式 from 导入新库 import 核对隔离目标 -from 建立映射 import 稳定JSON, 旧快照, 映射配置, 迁移错误 +from 建立映射 import 稳定JSON, 旧快照, 映射配置, 检查目标快照, 迁移错误 from 映射台账 import 迁移台账 from 来源回放 import 只读回放来源 from 读取旧文件 import 核对文件保全 @@ -32,6 +32,7 @@ def 核对迁移结果( 文件回执: Path | None = None, 来源回放: dict | None = None, ) -> dict: + 检查目标快照(快照) 库 = 数据库工厂(装配.配置.数据库, 装配.配置.运行用途) 核对隔离目标(库, 允许实例=允许实例, 管理引用=管理引用, 目标回执=目标回执) 正式 = 装配.要求知识方法().正式 diff --git a/数据库/旧库迁移/知识记录分流.py b/数据库/旧库迁移/知识记录分流.py index 612957a..039bad1 100644 --- a/数据库/旧库迁移/知识记录分流.py +++ b/数据库/旧库迁移/知识记录分流.py @@ -5,11 +5,32 @@ from __future__ import annotations from dataclasses import asdict, dataclass, field, replace from typing import Any +from PG作品映射 import PG内容表 from 关联已确认 import 生成关联建议, 请求历史关联 from 建立映射 import 读取JSON, 旧快照, 映射配置, 迁移错误 from 转换结构 import 取一致值, 转换动态字段 from 转换质量资产 import 质量案例ID, 例证表, 案例表, 规则历史表, 规则表 +历史运行表 = {"example_run", "example_run_receipt", "example_llm_call", "runs", "events"} +历史决定表 = {"reviews", "revisions", "example_user_decision", "example_candidate_cas"} +延后表 = { + "example_quality_result": "deferred_W22_23", + "example_candidate": "deferred_W25", + "example_lesson": "deferred_W26_27", + "lessons": "deferred_W26_27", +} +知识表 = { + "muse_knowledge_draft", + "muse_knowledge_entity", + "muse_knowledge_relation", + "cards", + "snapshots", + "example_knowledge_embedding", +} +质量表 = {规则表, 例证表, 案例表, 规则历史表} +已登记表 = PG内容表 | 历史运行表 | 历史决定表 | set(延后表) | 知识表 | 质量表 + + 方法五型 = {"craft", "combat", "emotion", "scene_pattern", "trope"} 实体六型 = {"character", "location", "item", "faction", "power_system", "event"} 已知类型 = 方法五型 | 实体六型 | {"character_relation", "style", "pacing"} @@ -214,8 +235,19 @@ def 分流知识记录( return 分流结果(源.记录键, 快照.源哈希, 归宿, 规则, 原因, 快照.导出(), **参数) try: + if 源.system != "file" and 表 not in 已登记表: + return 结果("隔离", "unknown_table") + if 表 in {"cards", "snapshots"}: + # 这两张SQLite镜像表没有当前授权列;快照内补列不能替代独立授权证据。 + if 源.system != "sqlite": + return 结果("隔离", "unknown_table") + 镜像列 = {"id", "kind", "payload_json", "created_at"} + if 表 == "cards": + 镜像列 |= {"title", "embedding", "work_id", "source_path", "content_hash"} + if set(行) - 镜像列: + return 结果("隔离", "unknown_fields") 质量绑定 = 映射.质量绑定(快照, 必须=False) - 是质量表 = 表 in {规则表, 例证表, 案例表, 规则历史表} + 是质量表 = 表 in 质量表 if 是质量表 or 质量绑定 is not None: 规则 = "Q01" if 表 == 规则历史表: @@ -325,26 +357,13 @@ def 分流知识记录( return 结果( "待转换", "linked_existing_confirmation", **生成关联建议(快照, 映射, 已知源 or {}) ) - if 表 in {"example_run", "example_run_receipt", "example_llm_call", "runs", "events"}: + if 表 in 历史运行表: return 结果("历史保留", "legacy_runtime", owner="S02", state=状态) - if 表 in {"reviews", "revisions", "example_user_decision", "example_candidate_cas"}: + if 表 in 历史决定表: return 结果("历史保留", "legacy_decision", owner="S01", state=状态) - 延后 = { - "example_quality_result": "deferred_W22_23", - "example_candidate": "deferred_W25", - "example_lesson": "deferred_W26_27", - "lessons": "deferred_W26_27", - } - if 表 in 延后: - return 结果("隔离", 延后[表], state=状态) - if 表 not in { - "muse_knowledge_draft", - "muse_knowledge_entity", - "muse_knowledge_relation", - "cards", - "snapshots", - "example_knowledge_embedding", - }: + if 表 in 延后表: + return 结果("隔离", 延后表[表], state=状态) + if 表 not in 知识表: return 结果("隔离", "unknown_table") 载荷 = _载荷(行, 表) work_id = 行.get("work_id") diff --git a/数据库/旧库迁移/转换PG作品.py b/数据库/旧库迁移/转换PG作品.py index 14376e2..d71236a 100644 --- a/数据库/旧库迁移/转换PG作品.py +++ b/数据库/旧库迁移/转换PG作品.py @@ -166,7 +166,8 @@ def 提交PG请求(装配, 身份, 冻结): 连, 身份.作者, TypeAdapter(导入请求).validate_python(p["request"]) ) # 重复位只是本次调用情况,不冒充另一个不可变导入回执。 - 版本 = 装配.要求资料().读取版本(连, 结果.source_id, 结果.revision) + # 参考导入零授权用途,原文须当前用途才能返回,迁移只回读回执与存储哈希。 + 版本 = 装配.要求资料().读取版本回执(连, 结果.source_id, 结果.revision) return { "source_id": 结果.source_id, "revision": 结果.revision, @@ -182,15 +183,15 @@ def 核对PG产物(装配, 身份, 冻结, 回执): raise 迁移错误("target_denied", "PG核对需要应用作者身份") if kind == "reference": with 装配.要求数据库().连接(只读=True) as 连: - v = 装配.要求资料().读取版本(连, 回执["source_id"], 回执["revision"]) - # B03公开版本带原文;与冻结请求逐码点核对,不仅比较自报哈希。 + v = 装配.要求资料().读取版本回执(连, 回执["source_id"], 回执["revision"]) + # 参考导入零授权用途,原文受当前用途门禁保护,迁移不自开用途; + # 核对冻结请求重算哈希、库内存储哈希与不可变回执三者一致。 import hashlib h = hashlib.sha256(p["request"]["content"].encode()).hexdigest() if ( v["content_hash"] != h or 回执["content_hash"] != h - or v["content"] != p["request"]["content"] or v["import_result"] != 回执["import_result"] or v["import_result"].get("导入者") != 身份.作者 ): diff --git a/数据库/迁移/V0056__调用结算与配置验证.sql b/数据库/迁移/V0056__调用结算与配置验证.sql new file mode 100644 index 0000000..ff2e364 --- /dev/null +++ b/数据库/迁移/V0056__调用结算与配置验证.sql @@ -0,0 +1,55 @@ +-- 结算不产生执行事件;账目变化由独立版本及只追加命令回执表达。 +ALTER TABLE public.muse_task ADD COLUMN accounting_revision bigint NOT NULL DEFAULT 0; +ALTER TABLE evaluation.muse_task ADD COLUMN accounting_revision bigint NOT NULL DEFAULT 0; +CREATE TABLE public.muse_call_settlement ( + task_id uuid NOT NULL REFERENCES public.muse_task(task_id), + command_id text NOT NULL CHECK (btrim(command_id) <> ''), + receipt_id uuid NOT NULL UNIQUE, + request_hash text NOT NULL CHECK (request_hash ~ '^[0-9a-f]{64}$'), + receipt jsonb NOT NULL, + created_at timestamptz NOT NULL DEFAULT clock_timestamp(), + PRIMARY KEY(task_id,command_id) +); +CREATE TABLE evaluation.muse_call_settlement (LIKE public.muse_call_settlement INCLUDING ALL); +ALTER TABLE evaluation.muse_call_settlement ADD FOREIGN KEY(task_id) REFERENCES evaluation.muse_task(task_id); +CREATE TRIGGER muse_call_settlement_immutable BEFORE UPDATE OR DELETE ON public.muse_call_settlement + FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); +CREATE TRIGGER muse_call_settlement_immutable BEFORE UPDATE OR DELETE ON evaluation.muse_call_settlement + FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); +GRANT SELECT,INSERT ON public.muse_call_settlement TO muse_app; +GRANT SELECT,INSERT ON evaluation.muse_call_settlement TO muse_eval; +REVOKE ALL ON public.muse_call_settlement FROM muse_eval; +-- 验证专用绑定不能被业务配置读取入口消费,不需要草案先启用。 +CREATE TABLE public.muse_config_probe_binding ( + task_id uuid PRIMARY KEY REFERENCES public.muse_task(task_id), + config_id text NOT NULL, + version text NOT NULL, + content_hash text NOT NULL CHECK (content_hash ~ '^[0-9a-f]{64}$'), + content jsonb NOT NULL, + created_at timestamptz NOT NULL DEFAULT clock_timestamp() +); +CREATE TABLE evaluation.muse_config_probe_binding (LIKE public.muse_config_probe_binding INCLUDING ALL); +ALTER TABLE evaluation.muse_config_probe_binding ADD FOREIGN KEY(task_id) REFERENCES evaluation.muse_task(task_id); +CREATE TRIGGER muse_config_probe_immutable BEFORE UPDATE OR DELETE ON public.muse_config_probe_binding + FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); +CREATE TRIGGER muse_config_probe_immutable BEFORE UPDATE OR DELETE ON evaluation.muse_config_probe_binding + FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); +GRANT SELECT,INSERT ON public.muse_config_probe_binding TO muse_app; +GRANT SELECT,INSERT ON evaluation.muse_config_probe_binding TO muse_eval; +REVOKE ALL ON public.muse_config_probe_binding FROM muse_eval; +CREATE TABLE public.muse_call_reconciliation ( + task_id uuid NOT NULL REFERENCES public.muse_task(task_id), + command_id text NOT NULL CHECK (btrim(command_id) <> ''), + request_hash text NOT NULL CHECK (request_hash ~ '^[0-9a-f]{64}$'), + receipt jsonb NOT NULL, + PRIMARY KEY(task_id,command_id) +); +CREATE TABLE evaluation.muse_call_reconciliation (LIKE public.muse_call_reconciliation INCLUDING ALL); +ALTER TABLE evaluation.muse_call_reconciliation ADD FOREIGN KEY(task_id) REFERENCES evaluation.muse_task(task_id); +CREATE TRIGGER muse_call_reconciliation_immutable BEFORE UPDATE OR DELETE ON public.muse_call_reconciliation + FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); +CREATE TRIGGER muse_call_reconciliation_immutable BEFORE UPDATE OR DELETE ON evaluation.muse_call_reconciliation + FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); +GRANT SELECT,INSERT ON public.muse_call_reconciliation TO muse_app; +GRANT SELECT,INSERT ON evaluation.muse_call_reconciliation TO muse_eval; +REVOKE ALL ON public.muse_call_reconciliation FROM muse_eval; diff --git a/数据库/迁移/V0057__来源当前授权.sql b/数据库/迁移/V0057__来源当前授权.sql new file mode 100644 index 0000000..7c39fe7 --- /dev/null +++ b/数据库/迁移/V0057__来源当前授权.sql @@ -0,0 +1,3 @@ +-- 当前消费授权与不可变内容版本独立;旧授权集合保持原值。 +ALTER TABLE muse_source ADD COLUMN authorization_revision bigint NOT NULL DEFAULT 1 + CHECK (authorization_revision >= 1); diff --git a/数据库/迁移/V0058__成品补充验证回执.sql b/数据库/迁移/V0058__成品补充验证回执.sql new file mode 100644 index 0000000..b4f9c85 --- /dev/null +++ b/数据库/迁移/V0058__成品补充验证回执.sql @@ -0,0 +1,17 @@ +-- B08显式完整审计的追加证明;既有成品字节及原始验证记录保持不可变。 +CREATE TABLE public.muse_delivery_artifact_validation ( + artifact_id uuid NOT NULL REFERENCES public.muse_delivery_artifact, + validation_version text NOT NULL CHECK(validation_version = 'delivery-validation-v2'), + author_id text NOT NULL, + verification jsonb NOT NULL, + audited_at timestamptz NOT NULL DEFAULT clock_timestamp(), + PRIMARY KEY(artifact_id,validation_version), + CHECK((verification->>'validation_version' = validation_version) IS TRUE), + CHECK((verification->'verified' = 'true'::jsonb) IS TRUE), + CHECK((jsonb_typeof(verification->'binding') = 'object') IS TRUE) +); +CREATE TRIGGER muse_delivery_artifact_validation_immutable BEFORE UPDATE OR DELETE + ON public.muse_delivery_artifact_validation FOR EACH ROW + EXECUTE FUNCTION public.muse_guard_literary_history(); +REVOKE ALL ON public.muse_delivery_artifact_validation FROM PUBLIC,muse_app,muse_eval; +GRANT SELECT,INSERT ON public.muse_delivery_artifact_validation TO muse_app; diff --git a/数据库/迁移/V0059__检索索引代次.sql b/数据库/迁移/V0059__检索索引代次.sql new file mode 100644 index 0000000..409b449 --- /dev/null +++ b/数据库/迁移/V0059__检索索引代次.sql @@ -0,0 +1,31 @@ +-- B09:索引现有条目和头的事务变更守卫;不产生第二套索引或业务权威。 +CREATE TABLE public.muse_method_index_guard ( + singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), + generation bigint NOT NULL DEFAULT 0 CHECK (generation >= 0) +); +INSERT INTO public.muse_method_index_guard (singleton, generation) VALUES (true, 0); + +CREATE FUNCTION public.muse_method_index_changed() RETURNS trigger +LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog AS $guard$ +BEGIN + UPDATE public.muse_method_index_guard + SET generation = generation + 1 + WHERE singleton; + IF NOT FOUND THEN + RAISE EXCEPTION 'method index mutation guard is missing'; + END IF; + RETURN NULL; +END +$guard$; + +-- statement 级别也覆盖批量改写和 TRUNCATE;事务失败时代次一并回滚。 +CREATE TRIGGER muse_method_index_changed +AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON public.muse_method_index +FOR EACH STATEMENT EXECUTE FUNCTION public.muse_method_index_changed(); +CREATE TRIGGER muse_method_index_head_changed +AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON public.muse_method_index_version +FOR EACH STATEMENT EXECUTE FUNCTION public.muse_method_index_changed(); + +REVOKE ALL ON public.muse_method_index_guard FROM PUBLIC, muse_app, muse_eval; +GRANT SELECT ON public.muse_method_index_guard TO muse_app, muse_eval; +REVOKE ALL ON FUNCTION public.muse_method_index_changed() FROM PUBLIC; diff --git a/数据库/迁移/V0060__评测单元复用来源.sql b/数据库/迁移/V0060__评测单元复用来源.sql new file mode 100644 index 0000000..b369d22 --- /dev/null +++ b/数据库/迁移/V0060__评测单元复用来源.sql @@ -0,0 +1,14 @@ +-- B10逐例复用只追加原来源,不复制任务、响应或费用账。 +CREATE TABLE evaluation.muse_evaluation_reuse ( + unit_id uuid PRIMARY KEY REFERENCES evaluation.muse_evaluation_unit(unit_id), + source_unit_id uuid NOT NULL REFERENCES evaluation.muse_evaluation_unit(unit_id), + source_experiment_id uuid NOT NULL REFERENCES evaluation.muse_experiment(experiment_id), + payload jsonb NOT NULL CHECK (jsonb_typeof(payload)='object'), + payload_hash text NOT NULL CHECK (payload_hash ~ '^[0-9a-f]{64}$'), + created_at timestamptz NOT NULL DEFAULT clock_timestamp(), + CHECK (unit_id <> source_unit_id) +); +CREATE INDEX evaluation_reuse_source ON evaluation.muse_evaluation_reuse(source_experiment_id); +CREATE TRIGGER evaluation_reuse_guard +BEFORE UPDATE OR DELETE ON evaluation.muse_evaluation_reuse +FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history(); diff --git a/配置/提供方.example.toml b/配置/提供方.example.toml index 886d1ed..1926130 100644 --- a/配置/提供方.example.toml +++ b/配置/提供方.example.toml @@ -1,7 +1,8 @@ # 本文件是运行配置草案,保存不等于验证或启用。不填写凭据值。 "宿主" = "direct" "宿主版本" = "1" -"角色策略版本" = "role-policy-r2-v1" +# 来源:配置/角色策略.yaml 的 version,生成后应与发布包一致。 +"角色策略版本" = "role-policy-r2-v2" "资源发布身份" = "<环境预检返回的resource_build_id>" "预算策略引用" = "<已登记且不可变的预算账户身份>" "计价版本" = "<后端已登记计价实现的版本>" diff --git a/配置/流程模板/完整审校.yaml b/配置/流程模板/完整审校.yaml index 5041845..b90057c 100644 --- a/配置/流程模板/完整审校.yaml +++ b/配置/流程模板/完整审校.yaml @@ -5,7 +5,6 @@ steps: - step_id: 冻结背景 processor: review.prepare processor_version: "1" - protection: 冻结上下文 - step_id: 审阅全文 processor: review.literary processor_version: "1" diff --git a/配置/流程模板/审校修订.yaml b/配置/流程模板/审校修订.yaml index 7a389f5..6a36a25 100644 --- a/配置/流程模板/审校修订.yaml +++ b/配置/流程模板/审校修订.yaml @@ -4,7 +4,6 @@ steps: - step_id: 冻结修订 processor: revision.freeze processor_version: "1" - protection: 冻结上下文 - step_id: 模型修订 processor: revision.generate processor_version: "1" diff --git a/配置/流程模板/执行评测-检测.yaml b/配置/流程模板/执行评测-检测.yaml index d1a6ac6..8cc7f06 100644 --- a/配置/流程模板/执行评测-检测.yaml +++ b/配置/流程模板/执行评测-检测.yaml @@ -5,7 +5,6 @@ steps: - step_id: 准备 processor: eval.prepare processor_version: "1" - protection: 冻结上下文 - step_id: 调用 processor: eval.call.detector processor_version: "1" diff --git a/配置/流程模板/执行评测-比较.yaml b/配置/流程模板/执行评测-比较.yaml index f39f3da..313e9c8 100644 --- a/配置/流程模板/执行评测-比较.yaml +++ b/配置/流程模板/执行评测-比较.yaml @@ -5,7 +5,6 @@ steps: - step_id: 准备 processor: eval.prepare processor_version: "1" - protection: 冻结上下文 - step_id: 调用 processor: eval.call.judge processor_version: "1" diff --git a/配置/流程模板/执行评测-生成.yaml b/配置/流程模板/执行评测-生成.yaml index ef194a3..68b5f5f 100644 --- a/配置/流程模板/执行评测-生成.yaml +++ b/配置/流程模板/执行评测-生成.yaml @@ -5,7 +5,6 @@ steps: - step_id: 准备 processor: eval.prepare processor_version: "1" - protection: 冻结上下文 - step_id: 调用 processor: eval.call.writer processor_version: "1" diff --git a/配置/流程模板/生成正文.yaml b/配置/流程模板/生成正文.yaml index ddeb975..e5fa090 100644 --- a/配置/流程模板/生成正文.yaml +++ b/配置/流程模板/生成正文.yaml @@ -16,7 +16,6 @@ steps: processor: ctx.freeze processor_version: "1" depends_on: [受限探索] - protection: 冻结上下文 - step_id: 无工具写作 processor: ctx.write processor_version: "1" diff --git a/配置/流程模板/生成规划.yaml b/配置/流程模板/生成规划.yaml index 74d6c1b..b6b5a20 100644 --- a/配置/流程模板/生成规划.yaml +++ b/配置/流程模板/生成规划.yaml @@ -5,7 +5,6 @@ steps: - step_id: 冻结规划 processor: plan.freeze processor_version: "1" - protection: 冻结上下文 - step_id: 模型规划 processor: plan.generate processor_version: "1" diff --git a/配置/角色策略.yaml b/配置/角色策略.yaml index 3315c24..dec5c65 100644 --- a/配置/角色策略.yaml +++ b/配置/角色策略.yaml @@ -15,6 +15,7 @@ models: - MiniMax-M2.7 - glm-5.2 - deepseek-v4-flash + - qwen3.8-flash actual_model_ids: claude-opus-4-8[1M]: ["claude-opus-4-8[1M]", "claude-opus-4-8"] claude-opus-4-8: ["claude-opus-4-8", "claude-opus-4-8[1M]"]