zizi 9e6f1c4481 R2 改造交付:新版模块化单体全量成果
- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具)
- 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺
- 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口
- 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影)
- 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威)
- R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
2026-09-15 12:47:42 +08:00

340 lines
15 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.

"""PG逐源台账的冻结与共享回执核对;不写业务owner表。"""
from PG作品映射 import 指针
from 建立映射 import 稳定JSON, 旧快照, 迁移错误
from 转换PG作品 import PG命令ID, 准备PG请求, 提交PG请求, 核对PG产物
from 转换PG正文 import 转换PG正文
def 检查PG冻结(行, 请求):
kind = 请求.get("pg_kind")
if set(请求) != {
"owner",
"scope",
"subject_ref",
"pg_kind",
"legacy_source",
"command_source",
"dependencies",
"mapping_hash",
"payload",
} or kind not in {"profile", "directory", "body", "reference"}:
raise 迁移错误("target_request_invalid", "PG冻结字段或目标种类不合法")
if (
请求["legacy_source"] != {"source_key": 行["source_key"], "source_hash": 行["source_hash"]}
or 请求["mapping_hash"] != 行["mapping_hash"]
or any(
请求[k] != 行["route"][v]
for k, v in (("owner", "owner"), ("scope", "scope"), ("subject_ref", "target_ref"))
)
or 行["route"]["type_id"] != "pg_" + kind
):
raise 迁移错误("target_request_invalid", "PG冻结与原台账身份/映射不同")
p = 请求["payload"]
owner = {"profile": "B01", "directory": "B01", "body": "B05", "reference": "B03"}[kind]
if 请求["owner"] != owner or not isinstance(p, dict):
raise 迁移错误("target_request_invalid", "PG目标种类与owner或载荷不同")
if kind == "profile" and (
p.get("work_id") != 请求["scope"]
or p.get("work_id") != 请求["subject_ref"]
or p.get("expected_revision") != 0
or p.get("schema", {}).get("schema_id") != "work_core"
or p.get("schema", {}).get("base_version") != 1
):
raise 迁移错误("target_request_invalid", "作品冻结必须是指定作品的初始档案")
if kind == "directory" and (
p.get("work_id") != 请求["scope"]
or p.get("chapter_id") != 请求["subject_ref"]
or not isinstance(p.get("chapter_ids"), list)
or not p["chapter_ids"]
or p["chapter_ids"][-1] != 请求["subject_ref"]
or p.get("title") != 行["original"]["record"].get("title")
):
raise 迁移错误("target_request_invalid", "目录冻结不能改变原章、标题或作品")
if kind == "body" and (
p.get("chapter_id") != 请求["subject_ref"]
or p.get("branch_id") != "main"
or type(p.get("base_revision")) is not int
or p["base_revision"] != 0
):
raise 迁移错误("target_request_invalid", "PG正文冻结只允许指定章的main/0候选")
if (
kind == "reference"
and "member_of" not in p
and (
p.get("request", {}).get("kind") != "reference"
or p.get("request", {}).get("authorized_uses") not in ([], ())
or p.get("request", {}).get("reference_work_ref") is not None
)
):
raise 迁移错误("target_request_invalid", "参考导入不能扩大用途或伪造作品归属")
deps = 请求["dependencies"]
if (
not isinstance(deps, list)
or not deps
or any(not isinstance(p, dict) or set(p) != {"source_key", "source_hash"} for p in deps)
or 请求["legacy_source"] not in deps
or 请求["command_source"] not in deps
):
raise 迁移错误("target_request_invalid", "PG冻结缺少自身或共享命令原来源")
def 核对PG台账(台账, 行, 回执):
冻结 = 行["frozen_request"]
检查PG冻结(行, 冻结)
if 台账.装配 is None:
raise 迁移错误("target_owner_read_unavailable", "PG台账需要真实owner公开读回能力")
if 冻结["pg_kind"] == "reference" and "member_of" in 冻结["payload"]:
root_pointer = 冻结["payload"]["member_of"]
if (
root_pointer != 冻结["command_source"]
or set(冻结["payload"]) != {"member_of"}
or 冻结["dependencies"] != [root_pointer, 冻结["legacy_source"]]
):
raise 迁移错误("target_request_invalid", "参考成员只能引用明确原根与自身")
key = root_pointer["source_key"]
cached = 台账.PG参考根缓存.get(key)
if cached is None:
root = 台账.读取(key)
if (
root is None
or root["state"] != "mapped"
or root["frozen_request"] is None
or "member_of" in root["frozen_request"]["payload"]
):
raise 迁移错误("pg_dependency_pending", "参考原文根尚无真实版本")
targets = 核对PG台账(台账, root, root["receipt"])
cached = (
root,
targets,
{p["source_key"]: p for p in root["frozen_request"]["dependencies"]},
)
台账.PG参考根缓存[key] = cached
root, targets, members = cached
if (
root["source_hash"] != root_pointer["source_hash"]
or root["mapping_hash"] != 行["mapping_hash"]
or members.get(行["source_key"]) != 冻结["legacy_source"]
or 稳定JSON(root["receipt"]) != 稳定JSON(回执)
or any(
冻结[k] != root["frozen_request"][k]
for k in ("owner", "scope", "subject_ref", "command_source")
)
):
raise 迁移错误("target_request_invalid", "参考成员来源/回执与已核原根不一致")
return targets
sources = []
for p in 冻结["dependencies"]:
dep = 台账.读取(p["source_key"])
if (
dep is None
or dep["source_hash"] != p["source_hash"]
or dep["mapping_hash"] != 行["mapping_hash"]
):
raise 迁移错误("pg_dependency_missing", "PG依赖尚未逐源留存或哈希/映射漂移")
sources.append(旧快照.从载荷(dep["original"]))
if (
冻结["pg_kind"] in {"body", "directory"}
and dep["source_key"] != 行["source_key"]
and dep["original"]["source"]["table"].split(".")[-1]
in {"muse_content_work", "muse_content_chapter"}
and dep["state"] != "mapped"
):
raise 迁移错误("pg_dependency_pending", "作品/前序章节依赖尚无真实成功回执")
根 = 台账.读取(冻结["command_source"]["source_key"])
if 根 is None:
raise 迁移错误("pg_dependency_missing", "共享命令源缺失")
if 根["source_key"] != 行["source_key"]:
if 根["state"] != "mapped" or 根["frozen_request"] is None:
raise 迁移错误("pg_dependency_pending", "共享产物根尚未成功映射")
def operation(f):
return {k: v for k, v in f.items() if k != "legacy_source"}
if 稳定JSON(operation(根["frozen_request"])) != 稳定JSON(operation(冻结)) or 稳定JSON(
根["receipt"]
) != 稳定JSON(回执):
raise 迁移错误("target_request_invalid", "共享产物与原根操作/真实回执不同")
if 冻结["pg_kind"] == "body":
blocks = [s for s in sources if s.source.table.split(".")[-1] == "muse_content_block"]
if 稳定JSON(转换PG正文(blocks)) != 稳定JSON(冻结["payload"]["draft"]):
raise 迁移错误("target_request_invalid", "逐block原内容/段映射与冻结正文不同")
if 冻结["pg_kind"] == "reference":
from 转换PG作品 import 组装PG参考
chapters = []
for source in sources[1:]:
if source.source.table.split(".")[-1] == "muse_content_chapter":
chapters.append((source, []))
elif (
source.source.table.split(".")[-1] == "muse_content_block"
and chapters
and source.原行["chapter_id"] == chapters[-1][0].原行["id"]
):
chapters[-1][1].append(source)
else:
raise 迁移错误("target_request_invalid", "参考冻结依赖不是原章/块顺序")
text, chapter_map = 组装PG参考(chapters)
p = 冻结["payload"]
if (
text != p["request"]["content"]
or 稳定JSON(chapter_map) != 稳定JSON(p["chapter_map"])
or p["request"]["authorized_uses"] not in ([], ())
or p["request"]["title"] != sources[0].原行["title"]
):
raise 迁移错误("target_request_invalid", "参考原文、逐章/块对照或用途与原行不同")
if 冻结["pg_kind"] == "profile" and 冻结["payload"]["content"] != {
"名称": sources[0].原行["title"]
}:
raise 迁移错误("target_request_invalid", "作品名称与原行不同")
return 核对PG产物(台账.装配, 台账.身份, 冻结, 回执)
def 执行PG行(装配, 身份, 台账, 源, 判定, 映射, 可用源, 行):
"""根先提交,子源只关联原根冻结;恢复不重新生成候选或改变基线。"""
from pydantic import ValidationError
from muse.共享.错误 import Muse错误
from muse.正式变更.接口 import 变更错误
key = 源.source.记录键
if 行["state"] == "mapped":
targets = 核对PG台账(台账, 行, 行["receipt"])
if 稳定JSON(targets) != 稳定JSON(行["targets"]):
raise 迁移错误("target_mismatch", "PG已有映射与真实owner不同")
return
try:
frozen = 行["frozen_request"]
if frozen is None:
# 共享成员复用根已经核验的冻结操作,不以当前状态重造初始基线。
from PG作品映射 import PG绑定
group, chapter = PG绑定(映射, 源)
root = (
group["work"]
if group["use"] == "reference"
else (chapter["blocks"][0] if 判定.type_id == "pg_body" else 指针(源))
)
if root != 指针(源):
cached = (
台账.PG参考根缓存.get(root["source_key"])
if group["use"] == "reference"
else None
)
parent = cached[0] if cached else 台账.读取(root["source_key"])
if parent is not None and parent["state"] in {"historical", "quarantined"}:
raise 迁移错误("pg_dependency_failed", "共享根已隔离或历史保留,不能映射成员")
if (
parent is None
or parent["state"] != "mapped"
or parent["source_hash"] != root["source_hash"]
or parent["mapping_hash"] != 行["mapping_hash"]
):
raise 迁移错误("pg_dependency_pending", "共享根尚未成功,保留reserved供恢复")
frozen = {**parent["frozen_request"], "legacy_source": 指针(源)}
if group["use"] == "reference":
frozen = {
**frozen,
"dependencies": [root, 指针(源)],
"payload": {"member_of": root},
}
else:
if 判定.type_id != "pg_reference":
try:
装配.要求作品().正式.读取回执(身份, 行["command_id"])
except 变更错误 as exc:
if exc.错误码 != "RECEIPT_NOT_FOUND":
raise
else:
raise 迁移错误("command_collision", "PG未冻结请求但命令已有回执")
frozen = 准备PG请求(装配, 身份, 源, 判定, 映射, 可用源)
frozen = 台账.冻结请求(key, frozen)
receipt = None
if frozen["command_source"] != 指针(源):
cached = 台账.PG参考根缓存.get(frozen["command_source"]["source_key"])
receipt = (
cached[0]["receipt"]
if cached
else 台账.读取(frozen["command_source"]["source_key"])["receipt"]
)
elif frozen["pg_kind"] != "reference":
try:
receipt = 装配.要求作品().正式.读取回执(身份, PG命令ID(身份, frozen))
except 变更错误 as exc:
if exc.错误码 != "RECEIPT_NOT_FOUND":
raise
if receipt is None:
receipt = 提交PG请求(装配, 身份, frozen)
except (迁移错误, Muse错误, ValidationError) as exc:
code = (
exc.code
if isinstance(exc, 迁移错误)
else getattr(exc, "错误码", "target_request_invalid")
)
if code in {"pg_dependency_pending", "pg_dependency_missing"}:
return # 固定映射下等待后续快照分片,不将缺少输入写成不可逆隔离。
if (
code in {"target_owner_read_unavailable", "command_collision"}
or isinstance(exc, Muse错误)
and exc.可重试
):
raise
# 已冻结的操作可能已在owner成功;不把保存回执中断伪判为未写。
if 台账.读取(key)["frozen_request"] is not None:
raise
台账.保存终态(key, "quarantined", 原因=code)
return
行 = 台账.读取(key)
targets = 核对PG台账(台账, 行, receipt)
updated = 台账.保存终态(key, "mapped", 目标=targets, 回执=receipt)
if frozen["pg_kind"] == "reference" and "member_of" not in frozen["payload"]:
台账.PG参考根缓存[key] = (
updated,
targets,
{p["source_key"]: p for p in frozen["dependencies"]},
)
def PG分片等待(台账, 映射, 可用源, 待处理):
"""只把实缺原源的未冻结reserved归为等待;不把异常中断/隔离放行。"""
from collections import Counter
from PG作品映射 import PG依赖指针, 是PG内容
状态, 缺源, 已解释 = Counter(), set(), 0
for 源 in {s.source.记录键: s for s in 待处理}.values():
行 = 台账.读取(源.source.记录键)
if 行 is None:
状态["missing_ledger"] += 1
continue
状态[行["state"]] += 1
if 行["state"] != "reserved" or 行["frozen_request"] is not None or not 是PG内容(源):
continue
try:
依赖 = PG依赖指针(映射, 源)
if any(
p["source_key"] in 可用源 and 可用源[p["source_key"]].源哈希 != p["source_hash"]
for p in 依赖
):
continue
本缺 = {p["source_key"] for p in 依赖 if p["source_key"] not in 可用源}
if 本缺:
已解释 += 1
缺源.update(本缺)
except 迁移错误:
continue
允许继续 = (
bool(缺源)
and 已解释 == 状态["reserved"]
and not 状态["quarantined"]
and not 状态["missing_ledger"]
)
return {
"can_continue": 允许继续,
"reason_code": "pg_dependency_missing" if 允许继续 else "not_fragment_wait",
"pending_record_count": 状态["reserved"],
"classified_pending_count": 已解释,
"missing_source_count": len(缺源),
"affected_states": dict(状态),
}