- 场景检索.py:切片语料两路等权RRF(键带slice:前缀),授权join过滤、代次陈旧拒绝、结果带书名预览双路得分;RRF抽公共融合入基础设施 - 参考材料双载体:references节判别式(方法/片段),片段冻结身份带来源内容哈希哨兵,现读核当前版本一致否则SOURCE_STALE - 选段授权slice_references→范围授权→校验→依赖复检→装配→快照绑定全链贯通;片段不登记方法消费 - 范围校验source_ids基集、服务包装、依赖锁定键分派同步;B03接口增读取来源当前指纹(架构门禁禁私表) - 集成a10032-a10036:检索知情候选/未建拒绝/撤回空/片段进快照/来源漂移失效
341 lines
14 KiB
Python
341 lines
14 KiB
Python
"""冻结来源的授权、撤回与版本变化核对;恢复重验以真实检查阻断旧任务。
|
||
|
||
合同依据:模块设计/B09-上下文.md、接口契约/任务工具与事件.md「恢复与停机」。
|
||
|
||
- 输入冻结范围(或快照)加事务连接:核对授权仍在(作者/作品/用途)、来源未撤回
|
||
(目录版本、文稿版本与哈希、细纲身份三元组、事实系统版本)、结构与投影版本未变。
|
||
- 输出当前有效性及锁定依据:有效附各来源当前版本绑定,无效附具体变化项;
|
||
只判断有效性,不修改任何模块内容。
|
||
- 恢复重验检查供 S02 流程登记挂接:漂移即抛错阻断任务重验,不自报已重验。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import Callable
|
||
|
||
from psycopg import Connection
|
||
|
||
from muse.上下文.任务范围 import 上下文服务
|
||
from muse.上下文.偏好材料 import 投影版本 as 偏好投影
|
||
from muse.上下文.偏好材料 import 核对范围偏好
|
||
from muse.上下文.偏好材料 import 结构哈希 as 偏好结构
|
||
from muse.上下文.声音材料 import 核对范围声音
|
||
from muse.上下文.来源读取 import 投影版本 as 可见投影版本
|
||
from muse.上下文.来源读取 import 读取作品范围
|
||
from muse.上下文.模型 import (
|
||
上下文错误,
|
||
依赖状态,
|
||
依赖锁定,
|
||
允许内容用途,
|
||
允许执行用途,
|
||
来源绑定,
|
||
)
|
||
from muse.任务运行.接口 import 任务快照
|
||
from muse.作品规划.接口 import 作品错误, 读取细纲投影
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.故事世界.接口 import 事实错误, 读取当前事实系统版本
|
||
from muse.正式变更.接口 import 固定哈希
|
||
from muse.正文写作.接口 import 正文错误, 读取正文元数据
|
||
|
||
|
||
def _核对文稿(连, 作者, 冻结范围, 目录, 基准, 投影) -> tuple[list[依赖锁定], list[str]]:
|
||
锁定: list[依赖锁定] = []
|
||
变化项: list[str] = []
|
||
for 目标 in 冻结范围.get("documents", ()):
|
||
章节 = next((c for c in 目录["chapters"] if c["chapter_id"] == 目标["chapter_id"]), None)
|
||
if 章节 is None or 章节["position"] != 目标["position"]:
|
||
变化项.append(f"章节 {目标['chapter_id']} 的目录位置已漂移或已被移除;来源锁定失效")
|
||
continue
|
||
try:
|
||
依据 = 读取正文元数据(
|
||
连, 作者, 目标["chapter_id"], 目标["revision"], 分支=目标["branch_id"]
|
||
)
|
||
except 正文错误 as 错误:
|
||
变化项.append(
|
||
f"文稿 {目标['chapter_id']} 版本 {目标['revision']} 已不可读取:{错误.说明}"
|
||
)
|
||
continue
|
||
if 依据["current_revision"] != 目标["revision"]:
|
||
变化项.append(
|
||
f"文稿 {目标['chapter_id']} 已从版本 {目标['revision']} 前进到 "
|
||
f"{依据['current_revision']};来源锁定失效"
|
||
)
|
||
continue
|
||
if 依据["document_hash"] != 目标["document_hash"]:
|
||
变化项.append(f"文稿 {目标['chapter_id']} 的结构哈希与冻结绑定不一致;来源锁定失效")
|
||
continue
|
||
基 = 基准.get(依据["document_id"])
|
||
if 基 is not None and (基.数据版本, 基.结构哈希, 基.投影版本) != (
|
||
str(目标["revision"]),
|
||
目标["document_hash"],
|
||
投影,
|
||
):
|
||
变化项.append(
|
||
f"来源 {依据['document_id']} 与冻结消费绑定的版本、结构或投影版本不一致;"
|
||
"来源锁定失效"
|
||
)
|
||
continue
|
||
锁定.append(
|
||
依赖锁定(
|
||
依据["document_id"],
|
||
"document",
|
||
str(目标["revision"]),
|
||
依据["document_hash"],
|
||
投影,
|
||
)
|
||
)
|
||
return 锁定, 变化项
|
||
|
||
|
||
def _核对细纲(
|
||
连, 作者, 作品, 冻结范围, 内容, 运行, 引用解析, 基准
|
||
) -> tuple[list[依赖锁定], list[str]]:
|
||
锁定: list[依赖锁定] = []
|
||
变化项: list[str] = []
|
||
目标章 = 冻结范围.get("target_plan")
|
||
if isinstance(目标章, dict):
|
||
try:
|
||
记录 = 读取细纲投影(
|
||
连,
|
||
作者,
|
||
作品,
|
||
目标章["chapter_id"],
|
||
内容用途=内容,
|
||
运行用途=运行,
|
||
引用解析=引用解析,
|
||
)
|
||
except (作品错误, 事实错误) as 错误:
|
||
# 细纲不可用、已撤回或其本书引用失去当前依据都算锁定失效,不向外泄漏
|
||
变化项.append(f"目标章 {目标章['chapter_id']} 细纲已不可用或引用失效:{错误.说明}")
|
||
else:
|
||
if (
|
||
记录["plan_id"],
|
||
记录["revision"],
|
||
记录["content_hash"],
|
||
) != (
|
||
目标章["plan_id"],
|
||
目标章["revision"],
|
||
目标章["content_hash"],
|
||
):
|
||
变化项.append(
|
||
f"目标章 {目标章['chapter_id']} 细纲已从版本 {目标章['revision']} 漂移到 "
|
||
f"{记录['revision']};来源锁定失效"
|
||
)
|
||
else:
|
||
基 = 基准.get(记录["plan_id"])
|
||
if 基 is not None and (
|
||
基.数据版本,
|
||
基.结构哈希,
|
||
基.投影版本,
|
||
) != (
|
||
str(记录["revision"]),
|
||
记录["content_hash"],
|
||
记录["projection_version"],
|
||
):
|
||
变化项.append(
|
||
f"来源 {记录['plan_id']} 与冻结消费绑定的版本、结构或投影版本不一致;"
|
||
"来源锁定失效"
|
||
)
|
||
else:
|
||
锁定.append(
|
||
依赖锁定(
|
||
记录["plan_id"],
|
||
"plan",
|
||
str(记录["revision"]),
|
||
记录["content_hash"],
|
||
记录["projection_version"],
|
||
)
|
||
)
|
||
return 锁定, 变化项
|
||
|
||
|
||
def _核对事实(连, 作者, 作品, 冻结范围, 投影) -> tuple[list[依赖锁定], list[str]]:
|
||
锁定: list[依赖锁定] = []
|
||
变化项: list[str] = []
|
||
事实节 = 冻结范围.get("facts")
|
||
if isinstance(事实节, dict):
|
||
try:
|
||
当前版本 = 读取当前事实系统版本(连, 作者, 作品)
|
||
except 事实错误 as 错误:
|
||
变化项.append(f"事实系统已不可读取:{错误.说明}")
|
||
else:
|
||
if 当前版本 != 事实节["system_revision"]:
|
||
变化项.append(
|
||
f"事实系统版本已从 {事实节['system_revision']} 前进到 {当前版本};来源锁定失效"
|
||
)
|
||
else:
|
||
锁定.append(依赖锁定(f"{作品}:事实系统", "facts", str(当前版本), "", 投影))
|
||
return 锁定, 变化项
|
||
|
||
|
||
def 核对依赖(
|
||
连: Connection,
|
||
冻结范围: dict,
|
||
引用解析=None,
|
||
基准绑定: tuple[来源绑定, ...] = (),
|
||
) -> 依赖状态:
|
||
"""在调用方事务内重查冻结来源;返回当前有效性与逐来源锁定依据。"""
|
||
if not isinstance(冻结范围, dict):
|
||
raise 上下文错误("CONTEXT_SCOPE_INVALID", "依赖核对需要任务冻结范围")
|
||
基准: dict[str, 来源绑定] = {}
|
||
for 绑 in 基准绑定:
|
||
if not isinstance(绑, 来源绑定) or 绑.来源ID in 基准:
|
||
raise 上下文错误("CONTEXT_INPUT_INVALID", "基准来源绑定必须是未重复的来源绑定对象")
|
||
基准[绑.来源ID] = 绑
|
||
作者 = 冻结范围.get("authorized_by")
|
||
作品 = 冻结范围.get("work_id")
|
||
if not isinstance(作者, str) or not 作者 or not isinstance(作品, str) or not 作品:
|
||
raise 上下文错误("CONTEXT_SCOPE_INVALID", "依赖核对需要冻结范围的作者与作品绑定")
|
||
变化项: list[str] = []
|
||
锁定: list[依赖锁定] = []
|
||
try:
|
||
目录 = 读取作品范围(连, 作者, 作品)
|
||
except Muse错误 as 错误:
|
||
return 依赖状态(False, (), (f"作品或授权已不可用:{错误.说明}",))
|
||
运行 = 冻结范围.get("run_purpose")
|
||
内容 = 冻结范围.get("content_purpose")
|
||
用途有效 = (
|
||
isinstance(运行, str)
|
||
and 运行 in 允许执行用途
|
||
and isinstance(内容, str)
|
||
and 内容 in 允许内容用途
|
||
)
|
||
if not 用途有效:
|
||
变化项.append(f"冻结范围的执行用途 {运行!r} 或内容用途 {内容!r} 已不被接受;来源锁定失效")
|
||
运行 = 运行 if isinstance(运行, str) else ""
|
||
内容 = 内容 if isinstance(内容, str) else ""
|
||
投影 = 可见投影版本(运行, 内容) if 用途有效 else ""
|
||
if 目录["revision"] != 冻结范围["directory_revision"]:
|
||
变化项.append(
|
||
f"作品目录已从版本 {冻结范围['directory_revision']} 前进到 {目录['revision']};"
|
||
"来源锁定失效"
|
||
)
|
||
else:
|
||
目录源 = 冻结范围.get("directory_source")
|
||
哈希 = 固定哈希(目录) if 目录源 else ""
|
||
if 目录源 and 目录源["content_hash"] != 哈希:
|
||
变化项.append("目录内容与冻结哈希不一致;来源锁定失效")
|
||
else:
|
||
锁定.append(
|
||
依赖锁定(
|
||
f"{作品}:目录",
|
||
"directory",
|
||
str(目录["revision"]),
|
||
哈希,
|
||
"work-directory-v1" if 目录源 else "",
|
||
)
|
||
)
|
||
当前锁定, 当前变化 = _核对文稿(连, 作者, 冻结范围, 目录, 基准, 投影)
|
||
锁定.extend(当前锁定)
|
||
变化项.extend(当前变化)
|
||
当前锁定, 当前变化 = _核对细纲(连, 作者, 作品, 冻结范围, 内容, 运行, 引用解析, 基准)
|
||
锁定.extend(当前锁定)
|
||
变化项.extend(当前变化)
|
||
当前锁定, 当前变化 = _核对事实(连, 作者, 作品, 冻结范围, 投影)
|
||
锁定.extend(当前锁定)
|
||
变化项.extend(当前变化)
|
||
|
||
try:
|
||
声音 = 核对范围声音(连, 冻结范围)
|
||
except 上下文错误 as 错误:
|
||
变化项.append(错误.说明)
|
||
else:
|
||
if 声音 is not None:
|
||
锁定.append(
|
||
依赖锁定(
|
||
声音["version_id"],
|
||
"voice",
|
||
str(声音["revision"]),
|
||
声音["schema_hash"],
|
||
声音["projection_version"],
|
||
)
|
||
)
|
||
绑 = 基准.get(声音["version_id"])
|
||
if 绑 is not None and (绑.数据版本, 绑.结构哈希, 绑.投影版本) != (
|
||
str(声音["revision"]),
|
||
声音["schema_hash"],
|
||
声音["projection_version"],
|
||
):
|
||
变化项.append("声音来源绑定与当前投影不一致")
|
||
from muse.上下文.任务范围 import 核对范围方法
|
||
from muse.知识方法.接口 import 方法错误
|
||
|
||
try:
|
||
方法 = 核对范围方法(连, 冻结范围)
|
||
except 方法错误 as 错误:
|
||
变化项.append(f"方法授权或绑定已失效:{错误.说明}")
|
||
else:
|
||
for 条 in 方法:
|
||
锁定.append(
|
||
依赖锁定(
|
||
条["binding_id"],
|
||
"method",
|
||
str(条["version"]),
|
||
条["schema_hash"],
|
||
条["projection_version"],
|
||
)
|
||
)
|
||
|
||
try:
|
||
偏好 = 核对范围偏好(连, 冻结范围)
|
||
except 上下文错误 as 错误:
|
||
变化项.append(错误.说明)
|
||
else:
|
||
for 条 in 偏好:
|
||
锁定.append(
|
||
依赖锁定(条["preference_id"], "preference", str(条["revision"]), 偏好结构, 偏好投影)
|
||
)
|
||
|
||
from muse.上下文.参考材料 import 核对范围参考
|
||
|
||
try:
|
||
参考 = 核对范围参考(连, 冻结范围)
|
||
except 上下文错误 as 错误:
|
||
变化项.append(错误.说明)
|
||
else:
|
||
for 条 in 参考:
|
||
if 条.get("载体") == "slice":
|
||
锁定.append(
|
||
依赖锁定(
|
||
条["slice_id"],
|
||
"reference-slice",
|
||
str(条["revision"]),
|
||
条["内容哈希"],
|
||
"reference-slice-v1",
|
||
)
|
||
)
|
||
else:
|
||
锁定.append(
|
||
依赖锁定(
|
||
条["version_id"],
|
||
"reference",
|
||
str(条["version"]),
|
||
条["schema_hash"],
|
||
条["projection_version"],
|
||
)
|
||
)
|
||
return 依赖状态(有效=not 变化项, 锁定依据=tuple(锁定), 变化项=tuple(变化项))
|
||
|
||
|
||
def 快照漂移说明(状态: 依赖状态) -> str | None:
|
||
"""快照对外失效说明:变化项只描述差异,还要给出下一步,漂移范围必须重新授权。"""
|
||
if 状态.有效:
|
||
return None
|
||
return ";".join(状态.变化项) + ";需重新授权"
|
||
|
||
|
||
def 依赖重验检查(服务: 上下文服务) -> Callable[[任务快照], None]:
|
||
"""S02 恢复检查的真实实现:授权、来源、结构与版本重验;漂移即阻断。"""
|
||
|
||
def 检查(任务: 任务快照) -> None:
|
||
范围 = 服务.恢复冻结范围(任务)
|
||
with 服务.数据库.连接(只读=True) as 连:
|
||
状态 = 核对依赖(连, 范围, 服务.规划引用解析)
|
||
if not 状态.有效:
|
||
raise 上下文错误("SOURCE_STALE", "恢复重验未通过:" + ";".join(状态.变化项))
|
||
|
||
return 检查
|
||
|
||
|
||
__all__ = ["依赖重验检查", "核对依赖", "快照漂移说明"]
|