- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具) - 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺 - 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口 - 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影) - 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威) - R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
501 lines
22 KiB
Python
501 lines
22 KiB
Python
"""旧规则版本与质量案例转换;只经 B06 公开用例,不触碰 owner 私表。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import hashlib
|
||
from dataclasses import asdict
|
||
from typing import Any
|
||
from uuid import NAMESPACE_URL, uuid5
|
||
|
||
import yaml
|
||
from pydantic import TypeAdapter
|
||
|
||
from muse.审校修订.接口 import 核对规则库, 案例来源, 案例采集请求
|
||
from muse.正式变更.接口 import 固定哈希
|
||
from muse.正文写作.接口 import 可见文本, 正文结构哈希, 正文草稿
|
||
from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 迁移错误
|
||
from 读取旧文件 import 解码文件
|
||
|
||
规则表 = "example_ai_flavor_rule"
|
||
例证表 = "example_ai_flavor_sample"
|
||
案例表 = "example_ai_flavor_case"
|
||
规则历史表 = "example_ai_flavor_rule_event"
|
||
允许保全许可 = {"owned", "licensed", "public_domain", "synthetic"}
|
||
# 旧DDL 111的精确默认;仅用于缺省键的投影比较,不补写原载荷或授权。
|
||
缺省投影默认 = {"case_card_ids": [], "source_license": "synthetic", "source_ref": ""}
|
||
|
||
|
||
def 质量案例ID(作者: str, source_key: str) -> str:
|
||
"""与迁移命令及B06案例公开用例相同的确定性身份。"""
|
||
键 = hashlib.sha256(稳定JSON([作者, source_key]).encode()).hexdigest()
|
||
命令 = str(uuid5(NAMESPACE_URL, "muse:legacy-import:" + 键))
|
||
return str(uuid5(NAMESPACE_URL, f"quality-case:{作者}:{命令}"))
|
||
|
||
|
||
def _表(快照: 旧快照) -> str:
|
||
return 快照.source.table.rsplit(".", 1)[-1]
|
||
|
||
|
||
def _对象(值: Any, 错误码: str, 说明: str) -> dict:
|
||
if isinstance(值, str):
|
||
try:
|
||
值 = 读取JSON(值)
|
||
except (ValueError, RecursionError) as 错:
|
||
raise 迁移错误(错误码, 说明) from 错
|
||
if not isinstance(值, dict):
|
||
raise 迁移错误(错误码, 说明)
|
||
return 值
|
||
|
||
|
||
def _清单(值: Any, 错误码: str, 说明: str) -> list:
|
||
if isinstance(值, str):
|
||
try:
|
||
值 = 读取JSON(值)
|
||
except (ValueError, RecursionError) as 错:
|
||
raise 迁移错误(错误码, 说明) from 错
|
||
if not isinstance(值, list):
|
||
raise 迁移错误(错误码, 说明)
|
||
return 值
|
||
|
||
|
||
def _内容哈希(值: dict) -> str:
|
||
return hashlib.sha256(稳定JSON(值).encode()).hexdigest()
|
||
|
||
|
||
def _文件YAML(快照: 旧快照) -> Any:
|
||
try:
|
||
return yaml.safe_load(解码文件(快照).decode("utf-8"))
|
||
except (UnicodeDecodeError, yaml.YAMLError) as 错:
|
||
raise 迁移错误("quality_source_invalid", "旧质量文件不是可解释的UTF-8 YAML") from 错
|
||
|
||
|
||
def _核对投影(行: dict, 载荷: dict, 字段: dict[str, str]) -> None:
|
||
for 列, 键 in 字段.items():
|
||
值 = 行.get(列)
|
||
if 列 in {"trigger_json"}:
|
||
值 = _对象(值, "quality_source_invalid", "旧规则JSON投影不是对象")
|
||
elif 列 in {"carve_out", "function_check", "case_card_ids", "rules"}:
|
||
值 = _清单(值, "quality_source_invalid", "旧质量JSON投影不是清单")
|
||
elif 列 == "sample_refs":
|
||
值 = _对象(值, "quality_source_invalid", "旧规则例证引用不是对象")
|
||
载荷值 = 载荷.get(键)
|
||
if 列 == 键 and 列 in 缺省投影默认 and 键 not in 载荷:
|
||
载荷值 = 缺省投影默认[列]
|
||
if 稳定JSON(值) != 稳定JSON(载荷值):
|
||
raise 迁移错误("quality_projection_mismatch", "旧运行投影与原载荷不一致:" + 列)
|
||
|
||
|
||
def _规则(快照: 旧快照) -> tuple[dict, str]:
|
||
if 快照.source.system == "file":
|
||
值 = _文件YAML(快照)
|
||
if not isinstance(值, dict):
|
||
raise 迁移错误("quality_source_invalid", "旧规则文件必须只含一个规则对象")
|
||
return 值, str(值.get("status", ""))
|
||
if _表(快照) != 规则表:
|
||
raise 迁移错误("quality_source_invalid", "规则绑定未指向旧规则表或文件")
|
||
行 = 快照.原行
|
||
载荷 = _对象(行.get("payload"), "quality_source_invalid", "旧规则缺完整payload")
|
||
if 行.get("content_sha256") != _内容哈希(载荷):
|
||
raise 迁移错误("quality_content_hash_mismatch", "旧规则内容哈希不符")
|
||
_核对投影(
|
||
行,
|
||
载荷,
|
||
{
|
||
"rule_id": "id",
|
||
"name": "name",
|
||
"layer": "layer",
|
||
"carrier_scope": "carrier_scope",
|
||
"default_disposition": "default_disposition",
|
||
"status": "status",
|
||
"version": "version",
|
||
"trigger_json": "trigger",
|
||
"carve_out": "carve_out",
|
||
"function_check": "function_check",
|
||
"sample_refs": "samples",
|
||
"case_card_ids": "case_card_ids",
|
||
"fix_hint": "fix_hint",
|
||
"evidence": "evidence",
|
||
},
|
||
)
|
||
return 载荷, str(行.get("status", ""))
|
||
|
||
|
||
def _例证集(快照: 旧快照) -> dict[str, dict]:
|
||
if 快照.source.system == "file":
|
||
值 = _文件YAML(快照)
|
||
if (
|
||
not isinstance(值, dict)
|
||
or set(值) != {"samples"}
|
||
or not isinstance(值["samples"], list)
|
||
):
|
||
raise 迁移错误("quality_source_invalid", "旧例证文件必须是单一samples清单")
|
||
行集 = 值["samples"]
|
||
else:
|
||
if _表(快照) != 例证表:
|
||
raise 迁移错误("quality_source_invalid", "例证绑定未指向旧例证表或文件")
|
||
行 = 快照.原行
|
||
载荷 = _对象(行.get("payload"), "quality_source_invalid", "旧例证缺完整payload")
|
||
if 行.get("content_sha256") != _内容哈希(载荷):
|
||
raise 迁移错误("quality_content_hash_mismatch", "旧例证内容哈希不符")
|
||
_核对投影(
|
||
行,
|
||
载荷,
|
||
{
|
||
"sample_id": "id",
|
||
"sample_type": "type",
|
||
"carrier": "carrier",
|
||
"source": "source",
|
||
"source_license": "source_license",
|
||
"rules": "rules",
|
||
"text": "text",
|
||
"note": "note",
|
||
"case_card_id": "case_card_id",
|
||
"source_ref": "source_ref",
|
||
},
|
||
)
|
||
行集 = [载荷]
|
||
结果 = {}
|
||
for 行 in 行集:
|
||
if not isinstance(行, dict) or not isinstance(行.get("id"), str) or 行["id"] in 结果:
|
||
raise 迁移错误("quality_source_invalid", "旧例证身份缺失或重复")
|
||
结果[行["id"]] = 行
|
||
return 结果
|
||
|
||
|
||
def 转换规则库(快照: 旧快照, 绑定: dict, 可用源: dict[str, 旧快照]) -> tuple[dict, dict]:
|
||
规则, 旧状态 = _规则(快照)
|
||
参数 = 绑定["parameters"]
|
||
if set(参数) != {"rule_id", "version"} or (规则.get("id"), 规则.get("version")) != (
|
||
参数.get("rule_id"),
|
||
参数.get("version"),
|
||
):
|
||
raise 迁移错误("quality_parameter_mismatch", "规则身份/版本与冻结参数不一致")
|
||
需要 = (
|
||
{x for v in 规则.get("samples", {}).values() for x in v}
|
||
if isinstance(规则.get("samples"), dict)
|
||
else set()
|
||
)
|
||
已选, 例证 = set(), {}
|
||
for 源 in 绑定["sources"]:
|
||
if set(源) != {"source_key", "source_hash", "sample_ids", "license", "source_ref"}:
|
||
raise 迁移错误("quality_binding_invalid", "规则例证来源未冻结身份、许可或样例集")
|
||
原 = 可用源.get(源["source_key"])
|
||
if 原 is None:
|
||
raise 迁移错误(
|
||
"quality_dependency_missing", "规则例证快照缺失:" + str(源["source_key"])
|
||
)
|
||
if 原.源哈希 != 源["source_hash"]:
|
||
raise 迁移错误("quality_source_drift", "规则例证快照哈希改变")
|
||
if (
|
||
not isinstance(源["sample_ids"], list)
|
||
or not 源["sample_ids"]
|
||
or not isinstance(源["license"], str)
|
||
or 源["license"] not in 允许保全许可
|
||
or not isinstance(源["source_ref"], str)
|
||
or not 源["source_ref"]
|
||
):
|
||
raise 迁移错误("quality_binding_invalid", "规则例证来源或许可声明不完整")
|
||
候选 = _例证集(原)
|
||
for id_ in 源["sample_ids"]:
|
||
if id_ in 已选 or id_ not in 候选:
|
||
raise 迁移错误("quality_example_mismatch", "例证身份重复或不属于冻结来源")
|
||
样例 = dict(候选[id_])
|
||
if 样例.get("source_license") not in (None, 源["license"]):
|
||
raise 迁移错误("quality_license_mismatch", "例证的旧许可与冻结许可不一致")
|
||
if 样例.get("source_ref") not in (None, "", 源["source_ref"]):
|
||
raise 迁移错误("quality_source_mismatch", "例证的旧来源与冻结来源不一致")
|
||
样例["source_license"], 样例["source_ref"] = 源["license"], 源["source_ref"]
|
||
例证[id_] = 样例
|
||
已选.add(id_)
|
||
if 已选 != 需要:
|
||
raise 迁移错误(
|
||
"quality_examples_incomplete", "规则的sf/snf/boundary/regression例证与冻结集合不一致"
|
||
)
|
||
候选规则 = {**规则, "status": "candidate"}
|
||
库 = 核对规则库([候选规则], list(例证.values()))
|
||
return 库, {"status": 旧状态, "source_key": 快照.source.记录键, "source_hash": 快照.源哈希}
|
||
|
||
|
||
def _旧案例(快照: 旧快照, card_id: str) -> dict:
|
||
if 快照.source.system == "file":
|
||
值 = _文件YAML(快照)
|
||
if not isinstance(值, dict) or not isinstance(值.get("cards"), list):
|
||
raise 迁移错误("quality_source_invalid", "旧案例文件必须含cards清单")
|
||
项 = [r for r in 值["cards"] if isinstance(r, dict) and r.get("id") == card_id]
|
||
if len(项) != 1:
|
||
raise 迁移错误("quality_case_ambiguous", "旧案例文件未唯一命中冻结卡片")
|
||
卡 = 项[0]
|
||
源 = _对象(卡.get("source"), "quality_source_invalid", "旧案例缺来源对象")
|
||
return {
|
||
"card_id": card_id,
|
||
"state": 卡.get("state"),
|
||
"label": 卡.get("label", "unclassified"),
|
||
"excerpt": 卡.get("excerpt", ""),
|
||
"context": 卡.get("context", ""),
|
||
"source_ref": 源.get("source_ref", ""),
|
||
"work_ref": 源.get("work_ref"),
|
||
"source_sha256": 源.get("source_sha256"),
|
||
"excerpt_sha256": 源.get("excerpt_sha256"),
|
||
"source_location": 源.get("location"),
|
||
"source_license": 源.get("license"),
|
||
"capture_mode": 卡.get("capture_mode"),
|
||
"original": 卡,
|
||
}
|
||
if _表(快照) != 案例表 or 快照.原行.get("card_id") != card_id:
|
||
raise 迁移错误("quality_source_invalid", "案例绑定未指向旧案例表或指定卡片")
|
||
行 = 快照.原行
|
||
return {
|
||
**{
|
||
k: 行.get(k)
|
||
for k in (
|
||
"card_id",
|
||
"state",
|
||
"label",
|
||
"excerpt",
|
||
"context",
|
||
"source_ref",
|
||
"work_ref",
|
||
"source_sha256",
|
||
"excerpt_sha256",
|
||
"source_location",
|
||
"source_license",
|
||
"capture_mode",
|
||
)
|
||
},
|
||
"original": 行,
|
||
}
|
||
|
||
|
||
def _哈希旧片段(案例: dict) -> None:
|
||
哈希 = 案例.get("excerpt_sha256")
|
||
if not isinstance(哈希, str) or len(哈希) != 64:
|
||
raise 迁移错误("quality_excerpt_hash_missing", "旧案例缺少片段哈希")
|
||
原文 = 案例.get("excerpt")
|
||
if 原文:
|
||
if not isinstance(原文, str) or hashlib.sha256(原文.encode()).hexdigest() != 哈希:
|
||
raise 迁移错误("quality_excerpt_hash_mismatch", "旧案例片段字节与旧哈希不一致")
|
||
|
||
|
||
def _案例请求(装配, 身份, 快照: 旧快照, 绑定: dict) -> tuple[案例采集请求, dict, str]:
|
||
参数 = 绑定["parameters"]
|
||
if set(参数) != {"card_id", "preserve_text"} or type(参数.get("preserve_text")) is not bool:
|
||
raise 迁移错误("quality_parameter_mismatch", "案例参数只能冻结card_id与preserve_text")
|
||
案例 = _旧案例(快照, 参数.get("card_id"))
|
||
_哈希旧片段(案例)
|
||
if len(绑定["sources"]) != 1:
|
||
raise 迁移错误("quality_binding_invalid", "旧案例必须唯一绑定一个新版before来源")
|
||
来源 = 绑定["sources"][0]
|
||
if (
|
||
set(来源) != {"role", "license", "license_ref", "legacy", "target"}
|
||
or 来源.get("role") != "before"
|
||
):
|
||
raise 迁移错误("quality_binding_invalid", "案例来源必须冻结许可、旧引用和新版before目标")
|
||
旧引用, 目标 = 来源["legacy"], 来源["target"]
|
||
if not isinstance(旧引用, dict) or set(旧引用) != {
|
||
"source_ref",
|
||
"work_ref",
|
||
"source_sha256",
|
||
"excerpt_sha256",
|
||
"location",
|
||
}:
|
||
raise 迁移错误("quality_binding_invalid", "旧案例引用身份不完整")
|
||
实际旧引用 = {
|
||
"source_ref": 案例.get("source_ref"),
|
||
"work_ref": 案例.get("work_ref"),
|
||
"source_sha256": 案例.get("source_sha256"),
|
||
"excerpt_sha256": 案例.get("excerpt_sha256"),
|
||
"location": 案例.get("source_location"),
|
||
}
|
||
if 稳定JSON(旧引用) != 稳定JSON(实际旧引用):
|
||
raise 迁移错误("quality_source_mismatch", "冻结旧来源与原案例引用不一致")
|
||
if (
|
||
来源.get("license") != 案例.get("source_license")
|
||
or not isinstance(来源.get("license_ref"), str)
|
||
or not 来源["license_ref"]
|
||
):
|
||
raise 迁移错误("quality_license_mismatch", "旧案例许可没有唯一证明或与原记录不同")
|
||
if 参数["preserve_text"] and 来源["license"] not in 允许保全许可:
|
||
raise 迁移错误("quality_license_denied", "旧来源许可不允许保全原文")
|
||
if not isinstance(目标, dict) or set(目标) != {
|
||
"kind",
|
||
"ref_id",
|
||
"revision",
|
||
"source_hash",
|
||
"start",
|
||
"end",
|
||
"excerpt_hash",
|
||
"branch_id",
|
||
"work_ref",
|
||
}:
|
||
raise 迁移错误("quality_binding_invalid", "案例新版来源缺少确切版本、哈希或范围")
|
||
if 目标.get("excerpt_hash") != 案例["excerpt_sha256"]:
|
||
raise 迁移错误("quality_excerpt_hash_mismatch", "旧片段哈希与新版来源范围不同")
|
||
目标定位 = {
|
||
k: 目标[k]
|
||
for k in (
|
||
"kind",
|
||
"ref_id",
|
||
"revision",
|
||
"source_hash",
|
||
"start",
|
||
"end",
|
||
"excerpt_hash",
|
||
"branch_id",
|
||
)
|
||
}
|
||
try:
|
||
定位 = 案例来源(**目标定位)
|
||
except (TypeError, ValueError) as 错:
|
||
raise 迁移错误("quality_binding_invalid", "案例新版来源定位不符B06合同") from 错
|
||
_核对新版案例来源(装配, 身份, 定位, 目标["work_ref"], 参数["preserve_text"])
|
||
标签 = 案例.get("label")
|
||
if 标签 not in {"unclassified", "sf", "snf", "boundary", "regression"}:
|
||
raise 迁移错误("quality_label_invalid", "旧案例标签无法保存,不自动重分类")
|
||
请求 = 案例采集请求(
|
||
定位, label=标签, disposition="ask", mode="backfill", preserve_text=参数["preserve_text"]
|
||
)
|
||
return 请求, 案例, 目标["work_ref"]
|
||
|
||
|
||
def _核对新版案例来源(装配, 身份, 定位: 案例来源, work_ref: str, 保全: bool) -> None:
|
||
if 定位.kind == "source":
|
||
with 装配.要求数据库().连接(只读=True) as 连:
|
||
原 = 装配.要求资料().读取案例证据(
|
||
连, 身份.作者, 定位.ref_id, 定位.revision, 定位.start, 定位.end
|
||
)
|
||
实际作品, 实际源哈希, 实际片段 = 原["work_group"], 原["source_hash"], 原["excerpt_hash"]
|
||
if 保全 and not 原["text_allowed"]:
|
||
raise 迁移错误("quality_license_denied", "新版资料来源未授权作案例原文")
|
||
else:
|
||
正文 = 装配.要求正文()
|
||
if 定位.kind == "document":
|
||
原 = 正文.读取正文(身份, 定位.ref_id, 分支=定位.branch_id, 版本=定位.revision)
|
||
else:
|
||
原 = 正文.读取候选(身份, 定位.ref_id, 版本=定位.revision)
|
||
if 原["branch_id"] != 定位.branch_id:
|
||
raise 迁移错误("quality_source_mismatch", "新版候选分支与冻结定位不同")
|
||
文档 = TypeAdapter(正文草稿).validate_python(原["document"])
|
||
文本 = 可见文本(文档)
|
||
实际作品, 实际源哈希 = "work:" + 原["work_id"], 正文结构哈希(文档)
|
||
实际片段 = hashlib.sha256(文本[定位.start : 定位.end].encode()).hexdigest()
|
||
if (实际作品, 实际源哈希, 实际片段) != (work_ref, 定位.source_hash, 定位.excerpt_hash):
|
||
raise 迁移错误("quality_source_mismatch", "新版来源作品、版本内容或片段与冻结绑定不同")
|
||
|
||
|
||
def 准备质量请求(装配, 身份, 快照: 旧快照, 映射: 映射配置, 可用源: dict[str, 旧快照]) -> dict:
|
||
绑定 = 映射.质量绑定(快照)
|
||
if 绑定 is None:
|
||
raise 迁移错误("quality_binding_missing", "质量资产缺少显式来源绑定")
|
||
if 绑定["author_id"] != 身份.作者:
|
||
raise 迁移错误("target_denied", "质量资产绑定不属于当前认证作者")
|
||
if 绑定["route"] == "rule_version":
|
||
库, 旧源 = 转换规则库(快照, 绑定, 可用源)
|
||
if not callable(getattr(装配.要求审校(), "读取规则版本", None)):
|
||
raise 迁移错误(
|
||
"target_owner_read_unavailable",
|
||
"B06缺少按rule_id+version精确读取规则版本的公开接口",
|
||
)
|
||
return {
|
||
"owner": "B06",
|
||
"scope": "rule-library:" + 身份.作者,
|
||
"subject_ref": 绑定["parameters"]["rule_id"],
|
||
"quality_kind": "rule_version",
|
||
"legacy_source": 旧源,
|
||
"source_bindings": 绑定["sources"],
|
||
"payload": 库,
|
||
}
|
||
if 绑定["route"] != "quality_case":
|
||
raise 迁移错误("target_adapter_unavailable", "规则例证依赖不单独创建质量对象")
|
||
请求, 旧案例, 作品组 = _案例请求(装配, 身份, 快照, 绑定)
|
||
case_id = 质量案例ID(身份.作者, 快照.source.记录键)
|
||
return {
|
||
"owner": "B06",
|
||
"scope": 作品组,
|
||
"subject_ref": case_id,
|
||
"quality_kind": "quality_case",
|
||
"legacy_source": {
|
||
"source_key": 快照.source.记录键,
|
||
"source_hash": 快照.源哈希,
|
||
"card_id": 旧案例["card_id"],
|
||
"state": 旧案例["state"],
|
||
"capture_mode": 旧案例["capture_mode"],
|
||
},
|
||
"source_bindings": 绑定["sources"],
|
||
"payload": asdict(请求),
|
||
}
|
||
|
||
|
||
def 提交质量请求(装配, 身份, 命令ID: str, 冻结: dict) -> dict:
|
||
审校 = 装配.要求审校()
|
||
if 冻结["quality_kind"] == "rule_version":
|
||
return 审校.导入规则候选(身份, 规则库=冻结["payload"])
|
||
请求 = TypeAdapter(案例采集请求).validate_python(冻结["payload"])
|
||
return 审校.采集案例(身份, 命令ID, 请求)
|
||
|
||
|
||
def 核对规则owner产物(审校, 身份, 冻结: dict, 回执: dict) -> list[dict]:
|
||
if 冻结.get("quality_kind") != "rule_version":
|
||
raise 迁移错误("target_request_invalid", "owner规则回执校验只接受规则版本")
|
||
规则 = next(iter(冻结["payload"]["rules"].values()))
|
||
读取 = getattr(审校, "读取规则版本", None)
|
||
if not callable(读取):
|
||
raise 迁移错误("target_owner_read_unavailable", "B06缺少精确规则版本读回")
|
||
实际 = 读取(身份, 规则["id"], 规则["version"])
|
||
if (
|
||
not isinstance(回执, dict)
|
||
or 回执.get("persisted") is not True
|
||
or 回执.get("fingerprint") != 冻结["payload"]["fingerprint"]
|
||
or not isinstance(回执.get("results"), list)
|
||
or len(回执["results"]) != 1
|
||
or not isinstance(实际, dict)
|
||
or 实际.get("rule_id") != 规则["id"]
|
||
or 实际.get("revision") != 规则["version"]
|
||
or 实际.get("status") != "candidate"
|
||
or 实际.get("activation_recorded") is not False
|
||
or 稳定JSON(实际.get("payload"))
|
||
!= 稳定JSON({"rule": 规则, "samples": 冻结["payload"]["samples"]})
|
||
or 实际.get("payload_hash") != 固定哈希(实际.get("payload"))
|
||
or 实际.get("version_id") != 回执["results"][0].get("version_id")
|
||
):
|
||
raise 迁移错误("target_mismatch", "B06精确规则版本、候选状态或owner回执不符")
|
||
return [
|
||
{
|
||
"id": 实际["version_id"],
|
||
"revision": 规则["version"],
|
||
"kind": "rule_version",
|
||
"owner": "B06",
|
||
"subject_ref": 规则["id"],
|
||
"scope": 冻结["scope"],
|
||
"payload_hash": 实际.get("payload_hash"),
|
||
}
|
||
]
|
||
|
||
|
||
def 核对质量产物(装配, 身份, 冻结: dict, 回执: dict) -> list[dict]:
|
||
审校 = 装配.要求审校()
|
||
if 冻结["quality_kind"] == "rule_version":
|
||
return 核对规则owner产物(审校, 身份, 冻结, 回执)
|
||
结果 = 回执.get("results", []) if isinstance(回执, dict) else []
|
||
命中 = [r for r in 结果 if isinstance(r, dict) and r.get("case_id") == 冻结["subject_ref"]]
|
||
if 回执.get("action") != "manual_save" or len(命中) != 1 or 命中[0].get("state") != "shadow":
|
||
raise 迁移错误("target_receipt_invalid", "案例迁移只接受B06未决采集回执")
|
||
实际 = 审校.读取案例(身份, 冻结["subject_ref"], revision=命中[0].get("revision"))
|
||
if (
|
||
实际.get("state") != "shadow"
|
||
or 实际.get("sampleable")
|
||
or 实际.get("case_hash") != 命中[0].get("case_hash")
|
||
or 稳定JSON(实际.get("payload", {}).get("request")) != 稳定JSON(冻结["payload"])
|
||
):
|
||
raise 迁移错误("target_mismatch", "B06案例精确版本与冻结回填不符")
|
||
return [
|
||
{
|
||
"id": 实际["case_id"],
|
||
"revision": 实际["revision"],
|
||
"kind": "quality_case",
|
||
"owner": "B06",
|
||
"subject_ref": 冻结["subject_ref"],
|
||
"scope": 冻结["scope"],
|
||
"case_hash": 实际["case_hash"],
|
||
}
|
||
]
|