zizi cf0f4fb985 W20–W24:保存知识方法、迁移框架、审校修订与案例行为的集成成果
接续 88dd570,保存 W20–W24 已实现的共享接口、业务入口、迁移、工作台、测试与文档。
W20/W22/W23 保持 in_progress,W21/W24 保持 verified;此提交不宣称方法或规则正式启用、多轮返修、真实角色评测完成。

W25 新增实验、标定、逐调用交付与角色执行及其迁移/测试/索引留在实施工作树,原有私人和旧实现保留项不纳入。

验证:离线 571、前端 33 通过;PG 469 项通过、2 项浏览器未启用,2 项误带入的 W25 用例已移出本提交;最终任务与交付边界 37 项通过。make 检查、最终类型、84 项资源及 diff 检查通过。未重跑浏览器或 Pi 宿主,不以合成调用认证外部模型效果。
独立整体审查四维通过;证据保存在 R2-20260909/提交W20-W24。
2026-09-13 16:24:42 +08:00

468 lines
19 KiB
Python
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.

"""明确旧源与既存确认的关联;纯解释和公开只读核验,不导入分流/台账或写业务。"""
from __future__ import annotations
import re
from typing import Any
from muse.元数据.接口 import 元数据服务, 校验值
from muse.共享.错误 import Muse错误
from muse.正式变更.接口 import 固定哈希
from 建立映射 import 稳定JSON, 旧快照, 映射配置, 迁移错误
from 转换结构 import 转换动态字段, 转换目标字段
正式表 = {"muse_knowledge_entity": "entity", "muse_knowledge_relation": "relation"}
实体型 = {"character", "location", "item", "faction", "power_system", "event"}
def 冻结模式(冻结: dict) -> str:
if (
not isinstance(冻结, dict)
or not isinstance(冻结.get("mode", "create"), str)
or 冻结.get("mode", "create") not in {"create", "link_confirmed"}
):
raise 迁移错误("target_request_invalid", "未知冻结模式")
模式 = 冻结.get("mode", "create")
if 模式 == "link_confirmed" and 冻结.get("owner") != "B02":
raise 迁移错误("target_request_invalid", "历史关联仅支持B02")
return 模式
def _字符串(值: Any) -> bool:
return isinstance(值, str) and bool(值.strip())
def _正整(值: Any) -> bool:
return type(值) is int and 值 > 0
def _哈希(值: Any) -> bool:
return isinstance(值, str) and re.fullmatch(r"[a-f0-9]{64}", 值) is not None
def _指针(值: Any) -> bool:
return (
isinstance(值, dict)
and set(值) == {"source_key", "source_hash"}
and _字符串(值["source_key"])
and _哈希(值["source_hash"])
)
def _确认指针(值: Any) -> bool:
return (
isinstance(值, dict)
and set(值) == {"command_id", "receipt_id", "revision"}
and all(_字符串(值[k]) for k in ("command_id", "receipt_id"))
and _正整(值["revision"])
)
def 正式旧表(快照: 旧快照) -> bool:
return 快照.source.table.rsplit(".", 1)[-1] in 正式表
def 请求历史关联(快照: 旧快照, 映射: 映射配置) -> bool:
return any(
r.get("source_key") == 快照.source.记录键
and ("existing_confirmation" in r or "target_source" in r)
for r in 映射.条目("confirmations", 快照.source)
)
def 关联源指针(快照: 旧快照, 映射: 映射配置) -> dict | None:
"""只供导入层按显式key读取己台账;非法形状由纯解释保全拒绝。"""
项 = [
r
for r in 映射.条目("confirmations", 快照.source)
if r.get("source_key") == 快照.source.记录键
]
if len(项) == 1 and _指针(项[0].get("target_source")):
return 项[0]["target_source"]
return None
def _原行资格(快照: 旧快照) -> str:
行 = 快照.原行
表 = 快照.source.table.rsplit(".", 1)[-1]
if (
快照.source.system != "pg"
or 行.get("source_status") not in ("active", "authorized")
or 行.get("source_action_policy") != "allowed"
):
raise 迁移错误("legacy_link_conflict", "旧来源不具备历史关联资格")
if 行.get("deleted", False) not in (False, 0, "false", "0"):
raise 迁移错误("legacy_link_conflict", "删除或未知删除标记不关联")
if 表 in 正式表:
if 表 == "muse_knowledge_entity" and 行.get("status") != "active":
raise 迁移错误("legacy_link_conflict", "正式实体非active")
return 正式表[表]
if (
表 != "muse_knowledge_draft"
or 行.get("status") != "confirmed"
or 行.get("draft_type") not in ("entity", "relation")
):
raise 迁移错误("legacy_link_conflict", "只有正式旧源与已确认草稿可关联")
return 行["draft_type"]
def _确认条目(快照, 映射, 作品, 家族):
项 = [
r
for r in 映射.条目("confirmations", 快照.source)
if r.get("source_key") == 快照.source.记录键
]
必需 = {
"system",
"database",
"source_key",
"source_hash",
"target_ref",
"decision_ref",
"existing_confirmation",
}
if not 正式旧表(快照):
必需.add("target_source")
if (
len(项) != 1
or set(项[0]) != 必需
or 项[0]["source_hash"] != 快照.源哈希
or not all(_字符串(项[0].get(k)) for k in ("target_ref", "decision_ref"))
):
raise 迁移错误("legacy_link_conflict", "确认关联字段、身份或源哈希不一致")
声明 = 项[0]
if not _确认指针(声明["existing_confirmation"]):
raise 迁移错误("existing_confirmation_invalid", "必须明确既存真实确认指针")
行 = 快照.原行
id_ = (
快照.source.id
if 正式旧表(快照)
else 行.get("entity_id" if 家族 == "entity" else "target_object_id")
)
if type(id_) not in (str, int) or not str(id_).strip():
raise 迁移错误("legacy_link_conflict", "旧目标强关联缺失")
if (
not 正式旧表(快照)
and 行.get("target_object_id" if 家族 == "entity" else "entity_id") is not None
):
raise 迁移错误("legacy_link_conflict", "草稿目标族存在矛盾指针")
if 映射.确认(快照, [家族 + ":" + str(id_)], 作品=作品) is None:
raise 迁移错误("legacy_link_conflict", "旧目标与明确对象映射不一致")
return 声明, str(id_)
def 生成关联建议(快照: 旧快照, 映射: 映射配置, 已知源: dict[str, 旧快照]) -> dict:
家族 = _原行资格(快照)
作品 = 映射.作品(快照.source, 快照.原行.get("work_id"), 映射.选择用途(快照))
if 作品["use"] != "author":
raise 迁移错误("target_adapter_unavailable", "其他用途的确认不进入B02关联")
声明, 旧目标ID = _确认条目(快照, 映射, 作品, 家族)
正式源 = 快照
if not 正式旧表(快照):
指针 = 声明.get("target_source")
if not _指针(指针):
raise 迁移错误("legacy_link_conflict", "正式源指针格式不正确")
正式源 = 已知源.get(指针["source_key"])
if 正式源 is None:
raise 迁移错误("legacy_target_source_missing", "未提供或留存明确正式旧源")
if (
正式源.源哈希 != 指针["source_hash"]
or not 正式旧表(正式源)
or (正式源.source.system, 正式源.source.database)
!= (快照.source.system, 快照.source.database)
or 正式源.source.id != 旧目标ID
or str(正式源.原行.get("work_id")) != str(快照.原行.get("work_id"))
or _原行资格(正式源) != 家族
):
raise 迁移错误("legacy_link_conflict", "正式源与旧草稿身份、主键或族不一致")
正式声明, _ = _确认条目(正式源, 映射, 作品, 家族)
if any(
稳定JSON(正式声明[k]) != 稳定JSON(声明[k])
for k in ("target_ref", "existing_confirmation")
):
raise 迁移错误("legacy_link_conflict", "两旧源未指向同一新目标与确认")
行 = 正式源.原行
字段 = {} if 行.get("attributes") is None else 行["attributes"]
类型 = "character_relation" if 家族 == "relation" else 行.get("entity_type")
if not isinstance(类型, str):
raise 迁移错误("unknown_type", "正式旧源缺明确类型")
别名 = 映射.值.get("type_aliases", {})
if not isinstance(别名, dict):
raise 迁移错误("unknown_type", "类型映射必须是对象")
类型 = 别名.get(类型, 类型)
if (
not isinstance(类型, str)
or 类型 not in 实体型 | {"character_relation"}
or (类型 == "character_relation") != (家族 == "relation")
):
raise 迁移错误("unknown_type", "正式旧源类型与族不一致")
内容 = {
"name": 行.get("normalized_name", ""),
"brief": 行.get("description") or "",
"fields": 转换动态字段(字段, 类型, 映射.值.get("schemas", {})),
}
if (家族 == "entity" and not _字符串(内容["name"])) or (
行.get("description") is not None and not isinstance(行["description"], str)
):
raise 迁移错误("field_type_mismatch", "正式旧源名称或描述类型不正确")
if 家族 == "relation":
if not _字符串(行.get("relation_type")):
raise 迁移错误("field_type_mismatch", "关系类型缺失")
for 侧 in ("source", "target"):
id_ = 行.get(侧 + "_entity_id")
if type(id_) not in (str, int) or not str(id_).strip():
raise 迁移错误("legacy_link_conflict", "关系旧端点必须明确")
内容[侧] = 映射.端点(正式源.source, 行["work_id"], ["entity:" + str(id_)], 作品=作品)
内容["relation_type"] = 行["relation_type"]
结构 = 映射.值.get("target_schemas", {}).get("B02:" + 类型)
if (
not isinstance(结构, dict)
or not _字符串(结构.get("schema_id"))
or not _正整(结构.get("base_version"))
):
raise 迁移错误("target_schema_missing", "关联需要明确目标历史结构")
扩展 = 结构.get("extension_version")
if 扩展 is not None and not _正整(扩展):
raise 迁移错误("target_schema_missing", "扩展版本不正确")
if 家族 == "relation" and any(
结构.get("fields", {}).get(k) != v
for k, v in (("甲方", "source"), ("乙方", "target"), ("关系类型", "relation_type"))
):
raise 迁移错误("target_relation_mapping", "关系必须映射核对后的两端与类型")
规范前值 = 转换目标字段(内容, 结构.get("fields"))
spec = {
"target_source": {"source_key": 正式源.source.记录键, "source_hash": 正式源.源哈希},
"decision_ref": 声明["decision_ref"],
"existing_confirmation": 声明["existing_confirmation"],
"type_id": 类型,
"instance_family": 家族,
"content": 规范前值,
"legacy_draft_id": None if 正式旧表(快照) else 快照.source.id,
"legacy_target_id": 正式源.source.id,
"s04": {
"schema_id": 结构["schema_id"],
"base_version": 结构["base_version"],
"extension_version": 扩展,
},
}
return {
"owner": "B02",
"scope": 作品["target_ref"],
"target_ref": 声明["target_ref"],
"type_id": 类型,
"state": "confirmed_history",
"content": {"link": spec},
}
def 检查关联冻结(行: dict, 冻结: dict) -> None:
if 冻结模式(冻结) != "link_confirmed":
if 行["route"].get("reason_code") == "linked_existing_confirmation":
raise 迁移错误("target_request_invalid", "历史关联不能降为候选创建模式")
return
if set(冻结) != {"mode", "owner", "scope", "subject_ref", "payload"}:
raise 迁移错误("target_request_invalid", "关联冻结外层字段不完整")
原 = 旧快照.从载荷(行["original"])
家族 = _原行资格(原)
路由 = 行["route"]
spec = 路由.get("content", {}).get("link")
p = 冻结.get("payload")
if (
not isinstance(spec, dict)
or not isinstance(p, dict)
or set(p)
!= {
"source_key",
"source_hash",
"mapping_hash",
"target_source",
"decision_ref",
"existing_confirmation",
"target",
"s04",
}
):
raise 迁移错误("target_request_invalid", "关联冻结载荷字段不正确")
if (
路由.get("rule_id") != "K06"
or 路由.get("state") != "confirmed_history"
or any(
冻结[k] != 路由[r]
for k, r in (("owner", "owner"), ("scope", "scope"), ("subject_ref", "target_ref"))
)
or any(p[k] != 行[k] for k in ("source_key", "source_hash", "mapping_hash"))
):
raise 迁移错误("target_request_invalid", "原来源、路由与冻结不一致")
if (
any(
稳定JSON(p[k]) != 稳定JSON(spec[k])
for k in ("target_source", "decision_ref", "existing_confirmation")
)
or not _指针(p["target_source"])
or not _确认指针(p["existing_confirmation"])
):
raise 迁移错误("target_request_invalid", "冻结确认与正式源指针不一致")
自身 = p["target_source"] == {"source_key": 行["source_key"], "source_hash": 行["source_hash"]}
if 自身 != 正式旧表(原) or (
not 自身
and str(原.原行.get("entity_id" if 家族 == "entity" else "target_object_id"))
!= spec["legacy_target_id"]
):
raise 迁移错误("target_request_invalid", "根源或草稿强关联不一致")
t, s = p["target"], p["s04"]
if (
not isinstance(t, dict)
or set(t) != {"instance_family", "type_id", "revision", "system_revision", "content_hash"}
or t["instance_family"] != 家族
or t["type_id"] != spec["type_id"]
or not all(_正整(t[k]) for k in ("revision", "system_revision"))
or not _哈希(t["content_hash"])
):
raise 迁移错误("target_request_invalid", "冻结世界版本不正确")
if (
not isinstance(s, dict)
or set(s)
!= {
"schema_id",
"base_version",
"extension_version",
"effective_schema_hash",
"projection_version",
}
or any(s[k] != spec["s04"][k] for k in spec["s04"])
or not _哈希(s["effective_schema_hash"])
or not _字符串(s["projection_version"])
):
raise 迁移错误("target_request_invalid", "冻结历史结构不正确")
def _世界回执(回执: dict, target: str, 声明: dict) -> dict:
b = 回执.get("basis", {})
行 = 回执.get("results")
if (
回执.get("command_id") != 声明["command_id"]
or 回执.get("receipt_id") != 声明["receipt_id"]
or 回执.get("action") != "adopt"
or not _字符串(回执.get("author_review_id"))
or 回执.get("target_ref") != "world:" + target
or not isinstance(b, dict)
or b.get("目标") != "world:" + target
or b.get("变更项") != ["object"]
or not _字符串(b.get("候选ID"))
or not _正整(b.get("候选版本"))
or not _哈希(b.get("候选哈希"))
or not _哈希(b.get("有效结构哈希"))
or not _字符串(b.get("投影版本"))
or not _哈希(b.get("来源摘要哈希"))
or type(b.get("数据版本")) is not int
or b["数据版本"] < 0
or not isinstance(行, list)
or len(行) != 1
):
raise 迁移错误("existing_confirmation_invalid", "必须是明确的独立世界采纳回执")
r = 行[0]
if (
not isinstance(r, dict)
or r.get("object_id") != target
or any(k in r for k in ("proposal_id", "candidate_id"))
or not all(_正整(r.get(k)) for k in ("revision", "system_revision"))
or not _哈希(r.get("content_hash"))
or r["revision"] != 声明["revision"]
or r["revision"] != b["数据版本"] + 1
):
raise 迁移错误("existing_confirmation_invalid", "回执没有唯一正确世界版本")
return r
def 关联回执产物(冻结: dict, 回执: dict) -> dict:
r = _世界回执(回执, 冻结["subject_ref"], 冻结["payload"]["existing_confirmation"])
if any(
r[k] != 冻结["payload"]["target"][k]
for k in ("revision", "system_revision", "content_hash")
):
raise 迁移错误("existing_confirmation_invalid", "真实确认与冻结版本不同")
return {
"id": r["object_id"],
"revision": r["revision"],
"kind": "world_object",
"owner": "B02",
"subject_ref": 冻结["subject_ref"],
"scope": 冻结["scope"],
}
def 核对既存目标于(连, 身份, 正式, 世界, route: dict) -> tuple[dict, dict, dict]:
spec = route["content"]["link"]
声明 = spec["existing_confirmation"]
try:
回 = 正式.读取回执于(连, 身份, 声明["command_id"])
except Muse错误 as 错:
if 错.可重试:
raise
raise 迁移错误("existing_confirmation_invalid", "无法读取本作者既存确认") from None
r = _世界回执(回, route["target_ref"], 声明)
try:
历史 = 世界.读取实体轨迹于(连, 身份, route["scope"], route["target_ref"])
命中 = [v for v in 历史["版本"] if v["revision"] == r["revision"]]
if (
历史["instance_family"] != spec["instance_family"]
or 历史["type_id"] != spec["type_id"]
or len(命中) != 1
or any(命中[0][k] != r[k] for k in ("system_revision", "content_hash"))
):
raise 迁移错误("existing_target_mismatch", "世界原版本、类型或哈希不符")
s = spec["s04"]
结构 = 元数据服务(连).读取有效结构(
s["schema_id"],
s["base_version"],
work_id=route["scope"],
extension_version=s["extension_version"],
)
if 结构.基础.type_id != spec["type_id"]:
raise 迁移错误("target_type_mismatch", "目标结构类型与旧正式源不一致")
规范值 = 校验值(结构, spec["content"]).值
if (
结构.effective_schema_hash != 回["basis"]["有效结构哈希"]
or 固定哈希(规范值) != r["content_hash"]
):
raise 迁移错误("existing_target_mismatch", "原正式源规范值或历史结构不符")
except 迁移错误:
raise
except (Muse错误, ValueError) as 错:
if getattr(错, "可重试", False):
raise
raise 迁移错误("existing_target_mismatch", "公开历史或结构核验失败") from None
目标 = {
"instance_family": spec["instance_family"],
"type_id": spec["type_id"],
**{k: r[k] for k in ("revision", "system_revision", "content_hash")},
}
结构快照 = {
**s,
"effective_schema_hash": 结构.effective_schema_hash,
"projection_version": 回["basis"]["投影版本"],
}
return 回, 目标, 结构快照
def 构建关联冻结(数据库, 身份, 正式, 世界, 快照, route: dict, mapping_hash: str) -> dict:
if 正式.数据库 != 数据库 or 世界.数据库 != 数据库:
raise 迁移错误("target_denied", "关联读取器必须属于同一工厂")
with 数据库.连接(只读=True) as 连:
_, 目标, 结构 = 核对既存目标于(连, 身份, 正式, 世界, route)
spec = route["content"]["link"]
return {
"mode": "link_confirmed",
"owner": "B02",
"scope": route["scope"],
"subject_ref": route["target_ref"],
"payload": {
"source_key": 快照.source.记录键,
"source_hash": 快照.源哈希,
"mapping_hash": mapping_hash,
**{k: spec[k] for k in ("target_source", "decision_ref", "existing_confirmation")},
"target": 目标,
"s04": 结构,
},
}