"""知识方法整链:提案确认、启用回执、固定绑定、三接缝消费、索引与来源回查。 验收口径:资料→分析→方法确认→固定绑定→生成消费可沿来源回查; 同哈希不同 owner、陈旧向量与不允许来源拒绝;参数上限来自版本策略。 """ import pytest from muse.作品规划.接口 import 档案保存, 节点目录保存 from muse.元数据.接口 import 元数据服务 from muse.共享.调用身份 import 内容用途, 用途, 调用身份 from muse.共享.错误 import Muse错误 from muse.启动 import 构建 from muse.正式变更.接口 import 作者动作 from muse.知识方法.模型 import ( 方法定位, 方法提案请求, 方法结构, 消费输入, 状态命令, 绑定命令, 证据引用, ) from muse.资料研究.接口 import 资料服务 from muse.资料研究.模型 import 导入请求 from muse.配置 import 应用配置 pytestmark = pytest.mark.数据库 @pytest.fixture def 方法环境(内置结构测试库): 应用测试库 = 内置结构测试库 装配 = 构建(应用配置(应用测试库[用途.生产].引用, "synthetic")) with 装配.生命周期(): 作者 = 调用身份("method-author", None, 用途.生产, 内容用途.规划) 服务 = 装配.要求知识方法() 作品 = 装配.要求作品() 结构 = 作品.新档案结构(作者, "method-work", "work_core", 1) 作品.保存档案(作者, "create-mw", 档案保存("method-work", 0, {"名称": "方法链作品"}, 结构)) 作品.添加章节(作者, "mw-1", "method-work", "mw-1", "第一章", 预期目录版本=0) with 应用测试库[用途.生产].连接() as 连: 资料 = 资料服务() 来源 = 资料.导入( 连, "method-author", 导入请求( kind="reference", title="参考书", origin="seed://method-source", content="第一章 埋下了枪。第二章 枪响了。", authorized_uses=("analysis",), ), ) 无权来源 = 资料.导入( 连, "method-author", 导入请求( kind="note", title="笔记", origin="seed://no-use", content="只有作者自己看的笔记。", authorized_uses=(), ), ) yield 装配, 作者, 服务, 来源, 无权来源, 应用测试库 def _方法身份(标签: str) -> str: from uuid import NAMESPACE_URL, uuid5 return str(uuid5(NAMESPACE_URL, "muse:B04:method:" + 标签)) def _提案(标签="craft-1", 标题="契诃夫之枪", source_id=None, revision=1, owner=None, 方法ID=None): return 方法提案请求( method_id=方法ID or _方法身份(标签), owner=owner or "method-author", type_id="craft", title=标题, schema=方法结构("craft", 1), content={ "装置形态": "结构装置", "原理": "当场景需要回收时引爆前文物件,因为读者会期待承诺兑现", "埋设手法": "挂在日常动线上", "回收点": "危机时刻顺手引爆", "记忆维持": "中点一次轻提", "间隔纪律": "至少隔两章", "滥用反例": "每章都埋新枪即廉价", "例证出处": "参考书 第1-2章 埋枪与响枪", }, 证据=( 证据引用(kind="source", source_id=source_id, revision=revision, 定位="第1-2章"), 证据引用(kind="counter", note="每章都埋即烂大街"), ), 参数上限={"单章上限": 1}, ) def _确认(服务, 作者, proposal_id): 提案 = 服务.读取方法提案(作者, proposal_id) 定位 = 方法定位( proposal_id, 提案["method_id"], 提案["revision"], 提案["proposal_hash"], 提案["current_version"], ) 审阅 = 服务.打开方法审阅(作者, 定位) return 服务.决定方法( 作者, f"decide-{proposal_id}", 定位, 作者动作.采纳, 审阅["review_id"], 审阅["review_hash"], ("method",), ) def 准备已确认消费方法(装配, 作者, work_id: str, *, 标题: str, 原理: str): """来源、提案、确认和绑定走真实服务;只用直接 SQL 准备历史启用状态。""" from dataclasses import replace from uuid import uuid4 服务 = 装配.要求知识方法() with 服务.数据库.连接() as 连: 来源 = 资料服务().导入( 连, 作者.作者, 导入请求( kind="reference", title="合成方法来源", origin="seed://model-method-evidence", content="用动作表现人物的迟疑;提前埋下的物件在危机中发挥作用。", authorized_uses=("analysis",), ), ) 请求 = _提案(str(uuid4()), 标题, str(来源.source_id), owner=作者.作者) 请求 = replace( 请求, content={ **请求.content, "原理": 原理, "例证出处": "方法字段仅供规划定位,不应发给写手。", }, ) 提案 = 服务.提出方法(作者, str(uuid4()), 请求) _确认(服务, 作者, 提案["target_ref"]) 版本 = 服务.读取方法详情(作者, 请求.method_id)["versions"][0] with 服务.数据库.连接() as 连: 连.execute("UPDATE muse_method SET state='enabled' WHERE method_id=%s", (请求.method_id,)) 服务.绑定方法( 作者, str(uuid4()), 绑定命令( work_id, "work", "work", 请求.method_id, str(版本["version_id"]), ), ) return 请求.method_id, str(版本["version_id"]) @pytest.mark.case_id( "NC-w20-20b01a", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=["提案→作者确认→版本台账与证据分组"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_提出确认与版本台账__20b01a(方法环境): 装配, 作者, 服务, 来源, _, 库 = 方法环境 回执 = 服务.提出方法(作者, "prop-1", _提案(source_id=str(来源.source_id))) 提案 = 服务.读取方法提案(作者, 回执["target_ref"]) assert 提案["state"] == "proposed" _确认(服务, 作者, 提案["proposal_id"]) 详情 = 服务.读取方法详情(作者, 提案["method_id"]) assert 详情["state"] == "confirmed" and len(详情["versions"]) == 1 assert 详情["evidence"]["sources"] and 详情["evidence"]["counters"] @pytest.mark.case_id( "NC-w20-20b02b", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=["同 owner 幂等、同哈希不同 owner 拒绝"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_同哈希去重与owner冲突__20b02b(方法环境): from dataclasses import replace 装配, 作者, 服务, 来源, _, 库 = 方法环境 请求 = _提案(source_id=str(来源.source_id)) 首 = 服务.提出方法(作者, "prop-2", 请求) 再 = 服务.提出方法(作者, "prop-2", 请求) assert 再 == 首 首提案 = 服务.读取方法提案(作者, 首["target_ref"]) assert 首提案["revision"] == 1 详情 = 服务.读取方法详情(作者, 请求.method_id) assert [str(条["proposal_id"]) for 条 in 详情["proposals"]] == [首["target_ref"]] assert {str(条["method_id"]) for 条 in 服务.列出方法(作者)} == {请求.method_id} # 不同命令可形成独立提案;它不应另立第二张同内容方法卡。 另 = 服务.提出方法(作者, "prop-2b", 请求) 另提案 = 服务.读取方法提案(作者, 另["target_ref"]) assert 另提案["method_id"] == 首提案["method_id"] assert 另提案["proposal_id"] != 首提案["proposal_id"] assert 另提案["revision"] == 2 assert {str(条["method_id"]) for 条 in 服务.列出方法(作者)} == {请求.method_id} 他人 = 调用身份("other-author", None, 用途.生产, 内容用途.规划) with pytest.raises(Muse错误) as 错: 服务.提出方法( 他人, "prop-2c", replace(请求, owner=他人.作者, method_id=_方法身份("other-owner")) ) assert 错.value.错误码 == "METHOD_OWNER_CONFLICT" @pytest.mark.case_id( "NC-w20-20b03c", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=["来源未授权 analysis 用途拒绝立卡"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_未授权来源拒绝__20b03c(方法环境): 装配, 作者, 服务, 来源, 无权, 库 = 方法环境 with pytest.raises(Muse错误, match="analysis 用途"): 服务.提出方法(作者, "prop-3", _提案(source_id=str(无权.source_id))) with pytest.raises(Muse错误, match="来源版本"): 服务.提出方法(作者, "prop-3-missing", _提案(source_id=str(来源.source_id), revision=999)) def _准备历史启用状态(库, 方法ID): """只为停用、绑定、索引回归准备历史数据,不作为正式启用的验收证据。""" with 库[用途.生产].连接() as 连: 连.execute("UPDATE muse_method SET state='enabled' WHERE method_id=%s", (方法ID,)) def _准备历史消费(库, 作者, 输入): """仅构造历史回执以测试拒绝启用和回查;不作为真实模型消费证明。""" from uuid import uuid4 from muse.知识方法.存储 import 方法存储 身份 = str(uuid4()) with 库[用途.生产].连接() as 连: 方法存储(连).写消费(身份, 作者.作者, 输入) return {"usage_id": 身份} @pytest.mark.case_id( "NC-w20-20b04d", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=[ ( "缺独立评测判据时即使有同版本消费也拒绝正式启用;HTTP 禁止自报消费;历史已启用方法停" "用后不再返回材料" ) ], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_启用需真实回执与停用不消费__20b04d(方法环境): 装配, 作者, 服务, 来源, _, 库 = 方法环境 服务.提出方法(作者, "prop-4", _提案(source_id=str(来源.source_id))) 提案 = 服务.读取方法提案(作者, _提案身份(服务, 作者, "prop-4")) _确认(服务, 作者, 提案["proposal_id"]) 方法ID = 提案["method_id"] with pytest.raises(Muse错误, match="回执"): 服务.变更方法状态( 作者, "st-1", 状态命令(方法ID, "enable", "not-a-usage", 提案["current_version"]) ) 版本 = 服务.读取方法详情(作者, 方法ID)["versions"][0] 自报 = 消费输入( method_version_id=str(版本["version_id"]), kind="generation", context_ref="snap-seed", fragment_hash="h1", ) with pytest.raises(Muse错误) as 错: 服务.记录消费(作者, 自报) assert 错.value.错误码 == "USAGE_EVIDENCE_REQUIRED" assert 服务.列出消费(作者, str(版本["version_id"])) == [] 用法 = _准备历史消费(库, 作者, 自报) 回读 = 服务.列出消费(作者, str(版本["version_id"])) assert [str(条["usage_id"]) for 条 in 回读] == [用法["usage_id"]] assert 回读[0]["kind"] == "generation" and 回读[0]["fragment_hash"] == "h1" # 即使同版本已有消费,缺少独立评测判据仍不能正式启用。 with pytest.raises(Muse错误) as 错: 服务.变更方法状态(作者, "st-2", 状态命令(方法ID, "enable", 用法["usage_id"], 1)) assert 错.value.错误码 == "METHOD_ACTIVATION_UNAVAILABLE" assert 服务.读取方法详情(作者, 方法ID)["state"] == "confirmed" from fastapi import FastAPI from fastapi.testclient import TestClient from muse.接入.http.路由.知识方法 import 路由 应用 = FastAPI() 应用.include_router(路由) with TestClient(应用) as 客户端: 响应 = 客户端.post( f"/api/v1/methods/{方法ID}/versions/{版本['version_id']}/usages", json={"kind": "generation", "context_ref": "fabricated"}, ) assert 响应.status_code == 405, "不得暴露自报模型消费的 HTTP 写入口" _准备历史启用状态(库, 方法ID) # 绑定后停用:材料不再返回,绑定保留可回查 服务.绑定方法( 作者, "bind-1", 绑定命令("method-work", "work", "work", 方法ID, str(版本["version_id"])), ) with 库[用途.生产].连接() as 连: from muse.知识方法.方法绑定 import 读取绑定材料 assert ( len( 读取绑定材料( 连, "method-work", 内容用途=作者.内容用途.value, 运行用途=作者.用途.value, ) ) == 1 ) 详情 = 服务.读取方法详情(作者, 方法ID) 服务.变更方法状态(作者, "st-3", 状态命令(方法ID, "disable", "", 详情["current_version"])) with 库[用途.生产].连接() as 连: from muse.知识方法.方法绑定 import 读取绑定材料 assert ( 读取绑定材料( 连, "method-work", 内容用途=作者.内容用途.value, 运行用途=作者.用途.value, ) == [] ) assert len(服务.列出绑定(作者, "method-work")) == 1 @pytest.mark.case_id( "NC-w20-20b05e", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=["绑定固定版本不追随修订;重绑影响只标记"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_绑定不追随修订与影响清单__20b05e(方法环境): 装配, 作者, 服务, 来源, _, 库 = 方法环境 服务.提出方法(作者, "prop-5", _提案(source_id=str(来源.source_id))) 提案 = 服务.读取方法提案(作者, _提案身份(服务, 作者, "prop-5")) _确认(服务, 作者, 提案["proposal_id"]) 方法ID = 提案["method_id"] v1 = 服务.读取方法详情(作者, 方法ID)["versions"][0] 用法 = _准备历史消费( 库, 作者, 消费输入( method_version_id=str(v1["version_id"]), kind="generation", context_ref="snap-a", fragment_hash="h1", ), ) _准备历史启用状态(库, 方法ID) 服务.绑定方法( 作者, "bind-2", 绑定命令("method-work", "work", "work", 方法ID, str(v1["version_id"])) ) from muse.上下文.接口 import 核对依赖 冻结范围 = 装配.要求上下文().授权任务范围(作者, "method-work", 0, 含事实=True, 含方法=True) assert 冻结范围["methods"][0]["version_id"] == str(v1["version_id"]) with 库[用途.生产].连接(只读=True) as 连: assert 核对依赖(连, 冻结范围).有效 # 修订:改内容再提案再确认 → v2 服务.提出方法( 作者, "prop-5b", 方法提案请求( method_id=方法ID, owner="method-author", type_id="craft", title="契诃夫之枪·修订", schema=方法结构("craft", 1), content={ "装置形态": "结构装置", "原理": "修订后的因果链:当冲突收紧时引爆前文承诺", "埋设手法": "挂在情绪节点上", "回收点": "高潮前置半拍引爆", "记忆维持": "四分之一处轻提", "间隔纪律": "至少隔三章", "滥用反例": "无埋设直接引爆", "例证出处": "参考书 第1-2章", }, 证据=( 证据引用(kind="source", source_id=str(来源.source_id), revision=1, 定位="第1-2章"), ), 参数上限={"单章上限": 1}, 修订自版本=1, ), ) 修订提案 = 服务.读取方法提案(作者, _提案身份(服务, 作者, "prop-5b")) assert 服务.读取方法详情(作者, 方法ID)["title"] == "契诃夫之枪", "提案不能改正式标题" assert 服务.读取版本(作者, str(v1["version_id"]))["title"] == "契诃夫之枪" _确认(服务, 作者, 修订提案["proposal_id"]) 详情 = 服务.读取方法详情(作者, 方法ID) assert len(详情["versions"]) == 2 # 同一方法的旧版消费不能证明新版本已经验证。 with pytest.raises(Muse错误, match="版本"): 服务.变更方法状态( 作者, "reject-old-receipt", 状态命令(方法ID, "enable", 用法["usage_id"], 2) ) assert 服务.读取方法详情(作者, 方法ID)["state"] == "confirmed" # 绑定仍指向 v1:不追随 绑定 = 服务.列出绑定(作者, "method-work")[0] assert str(绑定["version_id"]) == str(v1["version_id"]) and 绑定["version"] == 1 assert 绑定["title"] == "契诃夫之枪", "绑定标题也必须对应固定版本" assert 服务.读取方法详情(作者, 方法ID)["title"] == "契诃夫之枪·修订" 旧链 = 服务.读取来源回查链(作者, str(v1["version_id"])) 新链 = 服务.读取来源回查链(作者, str(详情["versions"][1]["version_id"])) assert len(旧链["证据"]) == 2 and len(新链["证据"]) == 1 assert 旧链["title"] == "契诃夫之枪" # 重绑影响评估:旧版本消费清单可见(只标记) 影响 = 服务.评估绑定变更(作者, "method-work", 方法ID, str(详情["versions"][1]["version_id"])) assert 影响["影响消费数"] >= 1 and any(u["kind"] == "generation" for u in 影响["影响清单"]) with 库[用途.生产].连接(只读=True) as 连: assert not 核对依赖(连, 冻结范围).有效, "确认新版本后未启用,旧任务不能继续使用" _准备历史启用状态(库, 方法ID) with 库[用途.生产].连接(只读=True) as 连: assert 核对依赖(连, 冻结范围).有效, "固定绑定不能自动追随当前版本" v2 = next(v for v in 详情["versions"] if v["version"] == 2) 重绑命令 = 绑定命令("method-work", "work", "work", 方法ID, str(v2["version_id"])) 首次 = 服务.绑定方法(作者, "bind-v2", 重绑命令) assert 服务.绑定方法(作者, "bind-v2", 重绑命令) == 首次, "命令重试不得再次释放或绑定" with 库[用途.生产].连接(只读=True) as 连: 失效 = 核对依赖(连, 冻结范围) assert not 失效.有效 and any("方法" in 项 for 项 in 失效.变化项) 新范围 = 装配.要求上下文().授权任务范围(作者, "method-work", 0, 含事实=True, 含方法=True) with 库[用途.生产].连接() as 连: 连.execute( "UPDATE muse_source SET authorized_uses='[]' WHERE source_id=%s", (来源.source_id,) ) with 库[用途.生产].连接(只读=True) as 连: 撤权 = 核对依赖(连, 新范围) assert not 撤权.有效 and any("授权" in 项 for 项 in 撤权.变化项) @pytest.mark.case_id( "NC-w20-20b06f", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=["保存目录不得自动登记方法消费;既有版本证据可回查来源。此用例不证明规划实际使用方法"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_规划接缝消费与回查链__20b06f(方法环境): 装配, 作者, 服务, 来源, _, 库 = 方法环境 服务.提出方法(作者, "prop-6", _提案(source_id=str(来源.source_id))) 提案 = 服务.读取方法提案(作者, _提案身份(服务, 作者, "prop-6")) _确认(服务, 作者, 提案["proposal_id"]) 方法ID = 提案["method_id"] v1 = 服务.读取方法详情(作者, 方法ID)["versions"][0] _准备历史启用状态(库, 方法ID) 服务.绑定方法( 作者, "bind-6", 绑定命令("method-work", "work", "work", 方法ID, str(v1["version_id"])) ) # 保存目录不是方法消费;不得凭绑定关系追加使用记录。 保存前消费 = 服务.列出消费(作者, str(v1["version_id"])) from muse.作品规划.模型 import 目录节点 目录 = 装配.要求作品().读取节点目录(作者, "method-work") 节点 = tuple( 目录节点(n["node_id"], n["kind"], n["title"], n.get("parent_id")) for n in 目录["nodes"] ) 装配.要求作品().保存节点目录( 作者, "plan-save-1", 节点目录保存("method-work", 目录["revision"], 节点), ) 消费 = 服务.列出消费(作者, str(v1["version_id"])) assert 消费 == 保存前消费 # 来源回查链:版本→提案→证据→来源版本 链 = 服务.读取来源回查链(作者, str(v1["version_id"])) assert 链["来源"] and 链["来源"][0]["source_id"] == str(来源.source_id) assert 链["来源"][0]["内容哈希"] @pytest.mark.case_id( "NC-w20-20b07a", environment="隔离数据库", given="隔离库与已装配知识方法服务", when="走公开表面执行整链", then=["重建索引可检索;污染即陈旧拒绝;重建不改方法本体"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_索引重建检索与陈旧拒绝__20b07a(方法环境): 装配, 作者, 服务, 来源, _, 库 = 方法环境 服务.提出方法(作者, "prop-7", _提案(source_id=str(来源.source_id))) 提案 = 服务.读取方法提案(作者, _提案身份(服务, 作者, "prop-7")) _确认(服务, 作者, 提案["proposal_id"]) 方法ID = 提案["method_id"] v1 = 服务.读取方法详情(作者, 方法ID)["versions"][0] _准备历史启用状态(库, 方法ID) 回执 = 服务.重建索引(作者) assert 回执["数量"] >= 1 命中 = 服务.检索方法(作者, "埋设 回收 枪", work_id="method-work") assert any(r["method_id"] == 方法ID for r in 命中["结果"]) assert 命中["结果"][0]["来源回读"] # 各自篡改实际文本、向量或嵌入器,不配合修改自报哈希;每项都必须识别陈旧。 前 = 服务.读取方法详情(作者, 方法ID) for 污染 in ("检索文本='污染'", "vector=ARRAY[0.1,0.2]", "embedder='obsolete'"): with 库[用途.生产].连接() as 连: 连.execute( f"UPDATE muse_method_index SET {污染} WHERE method_version_id=%s", (v1["version_id"],), ) assert not 服务.核对索引(作者)["新鲜"] with pytest.raises(Muse错误, match="陈旧"): 服务.检索方法(作者, "埋设 回收 枪") 服务.重建索引(作者) assert 服务.核对索引(作者)["新鲜"] 后 = 服务.读取方法详情(作者, 方法ID) assert 前["versions"] == 后["versions"] with 库[用途.生产].连接() as 连: 连.execute( "UPDATE muse_source SET authorized_uses='[]' WHERE source_id=%s", (来源.source_id,) ) assert not 服务.核对索引(作者)["新鲜"] # 查询必须真能命中该条目:命中后才进入逐项授权核对并整体拒绝。 with pytest.raises(Muse错误, match="METHOD_CANDIDATES_UNAVAILABLE"): 服务.检索方法(作者, "埋设 回收 枪") def _提案身份(服务, 作者, 命令ID: str) -> str: """提案身份是 uuid5(命令ID);经服务读取确认存在。""" from uuid import NAMESPACE_URL, uuid5 return str(uuid5(NAMESPACE_URL, "muse:B04:proposal:" + 命令ID)) @pytest.mark.case_id( "NC-w20-20b08b", environment="隔离PostgreSQL与真实S01服务;不调用模型", given="已确认的方法版本及索引,来源追加一个真实版本", when="仅修订证据并确认,再复用旧版本后新增修订", then=["提案不污染正式索引;证据修订产生独立版本;回查仅见对应版本证据;版本编号不复用"], contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_证据修订独立成版且回查不串版本__20b08b(方法环境): from dataclasses import replace _, 作者, 服务, 来源, _, 库 = 方法环境 原请求 = _提案(source_id=str(来源.source_id)) 服务.提出方法(作者, "evidence-v1", 原请求) _确认(服务, 作者, _提案身份(服务, 作者, "evidence-v1")) 服务.重建索引(作者) with 库[用途.生产].连接() as 连: 新来源 = 资料服务().导入( 连, 作者.作者, 导入请求( kind="reference", title="参考书", origin="seed://method-source", content="第一章留下一把钥匙,第三章用它打开密室。", authorized_uses=("analysis",), ), ) assert 新来源.revision == 2 新请求 = replace( 原请求, 修订自版本=1, 证据=( replace(原请求.证据[0], revision=2), 原请求.证据[1], ), ) 服务.提出方法(作者, "evidence-v2", 新请求) assert 服务.核对索引(作者)["新鲜"], "未确认的证据提案不能污染正式索引" _确认(服务, 作者, _提案身份(服务, 作者, "evidence-v2")) 版本 = 服务.读取方法详情(作者, 原请求.method_id)["versions"] assert [v["version"] for v in 版本] == [1, 2], "只改证据也必须形成独立版本" assert 服务.核对索引(作者)["新鲜"], "普通确认必须同事务更新派生索引" for v in 版本: 链 = 服务.读取来源回查链(作者, v["version_id"]) assert 链["来源"][0]["revision"] == v["version"] # 编号反例固定当前来源;不为测试回退而放宽 S01 的来源当前指针保护。 编号方法 = _方法身份("sequence") for 命令, 标题, 基线 in ( ("seq-a", "编号样本甲", None), ("seq-b", "编号样本乙", 1), ("seq-back", "编号样本甲", 2), ("seq-c", "编号样本丙", 1), ): 请求 = replace(新请求, method_id=编号方法, title=标题, 修订自版本=基线) 服务.提出方法(作者, 命令, 请求) _确认(服务, 作者, _提案身份(服务, 作者, 命令)) assert [v["version"] for v in 服务.读取方法详情(作者, 编号方法)["versions"]] == [1, 2, 3] @pytest.mark.case_id( "NC-w20-20b09c", environment="隔离PostgreSQL、S01/S04/B09真实服务;不调用模型", given="真实确认并绑定的craft方法;planning与generation字段用途不同", when="按不同任务用途取材,并经S04更新字段策略", then=[ "生成不读规划专用字段;S04策略变化使旧范围失效;重新授权不含禁止字段;缺少确认提案仅可回查" ], contract="docs/系统架构/新版设计/模块设计/B09-上下文.md", ) def test_方法投影遵循任务用途且策略收紧阻断旧范围__20b09c(方法环境): from dataclasses import asdict, replace from muse.上下文.接口 import 核对依赖, 核对范围方法 from muse.元数据.接口 import 启用命令, 字段限制, 定义哈希 装配, 作者, 服务, 来源, _, 库 = 方法环境 提案 = 服务.提出方法(作者, "projection-method", _提案(source_id=str(来源.source_id))) _确认(服务, 作者, 提案["target_ref"]) 方法ID = 服务.读取方法提案(作者, 提案["target_ref"])["method_id"] 版本ID = 服务.读取方法详情(作者, 方法ID)["versions"][0]["version_id"] _准备历史启用状态(库, 方法ID) 服务.绑定方法(作者, "projection-bind", 绑定命令("method-work", "work", "work", 方法ID, 版本ID)) 生成作者 = replace(作者, 内容用途=内容用途.生成) 上下文 = 装配.要求上下文() 生成范围 = 上下文.授权任务范围(生成作者, "method-work", 0, 含事实=True, 含方法=True) 规划范围 = 上下文.授权任务范围(作者, "method-work", 0, 含事实=True, 含方法=True) with 库[用途.生产].连接(只读=True) as 连: 写作材料 = 核对范围方法(连, 生成范围)[0] 规划材料 = 核对范围方法(连, 规划范围)[0] assert "例证出处:" not in 写作材料["材料"] assert "例证出处:" in 规划材料["材料"] assert 写作材料["projection_version"] != 规划材料["projection_version"] with 库[用途.维护].连接() as 连, 连.transaction(): 元数据 = 元数据服务(连) 字段 = next(项 for 项 in 元数据.读取有效结构("craft", 1).字段 if 项.key == "原理") 当前 = 元数据.当前策略("craft") 新 = replace( 当前, version=当前.version + 1, 限制=(*当前.限制, 字段限制(字段.field_id, ("aiContext:generation",))), ) 元数据.更新策略( 新, 启用命令( "restrict-method-context", 定义哈希(asdict(新)), 当前.version, 作者.作者, "type:craft", ), ) with 库[用途.生产].连接(只读=True) as 连: assert not 核对依赖(连, 生成范围).有效 新范围 = 上下文.授权任务范围(生成作者, "method-work", 0, 含事实=True, 含方法=True) with 库[用途.生产].连接(只读=True) as 连: assert "原理:" not in 核对范围方法(连, 新范围)[0]["材料"] # 历史资料可回查;缺少确认提案的版本不能重新进入模型材料。 with 库[用途.生产].连接() as 连: 连.execute("UPDATE muse_method_version SET proposal_id=NULL WHERE version_id=%s", (版本ID,)) assert 服务.读取版本(作者, 版本ID)["version"] == 1 with 库[用途.生产].连接(只读=True) as 连: assert not 核对依赖(连, 新范围).有效 # --------------------------------------------------------------------------- # 方法绑定/检索原观察承接:真实服务级等价边界(旧范式卡/双臂形状替代) # 每条只绑定一个 TC;数据库真实公开入口,不引用旧 SQL 或内部表示。 # --------------------------------------------------------------------------- def _绑定启用方法(装配, 作者, 服务, 来源, *, 标签="bind-material", work_id="method-work"): """提出→确认→历史启用→绑定,返回 (method_id, version_id)。""" from uuid import uuid4 请求 = _提案(uuid4().hex[:8], 标签, str(来源.source_id), revision=1, owner=作者.作者) 服务.提出方法(作者, f"prop-{标签}", 请求) 提案 = 服务.读取方法提案(作者, _提案身份(服务, 作者, f"prop-{标签}")) _确认(服务, 作者, 提案["proposal_id"]) 版本 = 服务.读取方法详情(作者, 请求.method_id)["versions"][0] with 服务.数据库.连接() as 连: 连.execute("UPDATE muse_method SET state='enabled' WHERE method_id=%s", (请求.method_id,)) 服务.绑定方法( 作者, f"bind-{标签}", 绑定命令(work_id, "work", "work", 请求.method_id, str(版本["version_id"])), ) return 请求.method_id, str(版本["version_id"]) @pytest.mark.case_id( "TC-c9aac0adb8a8", environment="离线,固定方法库与结构", when="选择或投影明确用途的固定方法版本。", contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_绑定材料向写手投影内容上限与来源出处__c9aac0(方法环境): """旧内容字段过合同且写手读到卡内容;新版等价:绑定材料含投影键值、参数上限与证据出处。""" from muse.知识方法.接口 import 读取绑定材料 装配, 作者, 服务, 来源, _, 库 = 方法环境 method_id, version_id = _绑定启用方法(装配, 作者, 服务, 来源, 标签="writer-content") with 库[用途.生产].连接(只读=True) as 连: 材料 = 读取绑定材料(连, "method-work", 内容用途="generation", 运行用途="production") 本条 = [m for m in 材料 if m["method_id"] == method_id] assert len(本条) == 1 assert 本条[0]["version_id"] == version_id and 本条[0]["version"] == 1 assert 本条[0]["参数上限"] == {"单章上限": 1} 文本 = 本条[0]["材料"] assert "writer-content" in 文本 assert "原理:" in 文本 and "埋设手法:" in 文本 assert "参数上限:" in 文本 出处行 = [行 for 行 in 文本.splitlines() if 行.startswith("出处:")] assert 出处行 == [f"出处:{来源.source_id}@r{来源.revision} 第1-2章"] 来源链 = 服务.读取来源回查链(作者, version_id)["来源"] assert [(条["source_id"], 条["revision"], 条["定位"]) for 条 in 来源链] == [ (str(来源.source_id), 来源.revision, "第1-2章") ] @pytest.mark.case_id( "TC-cb5645be12fa", environment="离线,固定方法库与结构", when="选择或投影明确用途的固定方法版本。", contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_绑定材料携带反例与上限且不夹带不可编辑字段__cb5645(方法环境): """旧每卡投影为来源指针+内容字段;新版等价:材料含反例定位与参数上限。""" from muse.知识方法.接口 import 读取绑定材料 装配, 作者, 服务, 来源, _, 库 = 方法环境 _绑定启用方法(装配, 作者, 服务, 来源, 标签="counter-and-cap") with 库[用途.生产].连接(只读=True) as 连: 材料 = 读取绑定材料(连, "method-work", 内容用途="generation", 运行用途="production") 文本 = 材料[0]["材料"] assert "反例:" in 文本 and "烂大街" in 文本 assert "单章上限:1" in 文本 or '"单章上限": 1' in 文本 assert "材料哈希" in 材料[0] and 材料[0]["材料哈希"] @pytest.mark.case_id( "TC-ccb4278b70c8", environment="离线,固定方法库与结构", when="选择或投影明确用途的固定方法版本。", contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_方法内容未知或越界字段在提出时拒绝__ccb427(方法环境): """旧合同失败关闭;新版等价:提案含不可编辑/未知字段被结构校验拒绝。""" from dataclasses import replace from muse.共享.错误 import Muse错误 _, 作者, 服务, 来源, _, _ = 方法环境 请求 = _提案(source_id=str(来源.source_id)) with pytest.raises(Muse错误): 服务.提出方法( 作者, "oversize-field", replace(请求, content={**请求.content, "未知字段": "不应进入"}), ) with pytest.raises(Muse错误): 服务.提出方法( 作者, "non-object-field", replace(请求, content={**请求.content, "原理": {"不是字符串": True}}), ) # 被拒绝的提案不落库:任何命令身份都查不到未被接受的提案。 for 命令 in ("oversize-field", "non-object-field"): with pytest.raises(Muse错误) as 错: 服务.读取方法提案(作者, _提案身份(服务, 作者, 命令)) assert 错.value.错误码 == "METHOD_PROPOSAL_NOT_FOUND" @pytest.mark.case_id( "TC-b9e8dac4520e", environment="离线,固定方法库与结构", when="选择或投影明确用途的固定方法版本。", contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_方法证据引用核对来源确切实体与版本__b9e8da(方法环境): """旧严格来源指针拒绝夹带;新版等价:证据引用必须命中已登记来源的真实版本。""" from dataclasses import replace from uuid import uuid4 from muse.共享.错误 import Muse错误 _, 作者, 服务, 来源, 无权, _ = 方法环境 请求 = _提案(uuid4().hex[:8], "strict-source", str(来源.source_id), revision=1, owner=作者.作者) 服务.提出方法(作者, "strict-source-ok", 请求) # 错误版本:证据 revision 指向不存在的版本 → 提出拒绝 with pytest.raises(Muse错误): 服务.提出方法( 作者, "strict-source-bad", replace( 请求, method_id=_方法身份("strict-bad"), 证据=( 证据引用( kind="source", source_id=str(来源.source_id), revision=999, 定位="第1-2章", ), 证据引用(kind="counter", note="每章都埋即烂大街"), ), ), ) # 未授权来源:analysis 授权缺失 → 提出拒绝 with pytest.raises(Muse错误): 服务.提出方法( 作者, "strict-source-unlicensed", replace( 请求, method_id=_方法身份("strict-unlicensed"), 证据=( 证据引用( kind="source", source_id=str(无权.source_id), revision=无权.revision, 定位="第1-2章", ), 证据引用(kind="counter", note="每章都埋即烂大街"), ), ), ) @pytest.mark.case_id( "TC-3a53564c2cf2", environment="离线,固定方法库与结构", when="选择或投影明确用途的固定方法版本。", contract="docs/系统架构/新版设计/模块设计/B04-知识方法.md", ) def test_绑定材料仅向启用且绑定版本投影__3a5356(方法环境): """旧无确认装配返回空;新版等价:未绑定或无启用状态的方法不返回绑定材料。""" from muse.知识方法.接口 import 读取绑定材料 装配, 作者, 服务, 来源, _, 库 = 方法环境 保留ID, 保留版本 = _绑定启用方法(装配, 作者, 服务, 来源, 标签="only-bound") with 库[用途.生产].连接(只读=True) as 连: assert ( len(读取绑定材料(连, "method-work", 内容用途="generation", 运行用途="production")) == 1 ) with 库[用途.生产].连接(只读=True) as 连: 未绑定 = 读取绑定材料(连, "no-such-work", 内容用途="generation", 运行用途="production") assert 未绑定 == [] # 停用的方法不返回材料:禁用一个已绑定方法后该方法的材料消失,其他启用绑定保留。 method_id, _ = _绑定启用方法(装配, 作者, 服务, 来源, 标签="disabled-material") with 库[用途.生产].连接(只读=True) as 连: 前 = 读取绑定材料(连, "method-work", 内容用途="generation", 运行用途="production") assert {m["method_id"] for m in 前} == {保留ID, method_id} with 服务.数据库.连接() as 连: 连.execute("UPDATE muse_method SET state='disabled' WHERE method_id=%s", (method_id,)) with 库[用途.生产].连接(只读=True) as 连: 材料 = 读取绑定材料(连, "method-work", 内容用途="generation", 运行用途="production") assert {(m["method_id"], m["version_id"]) for m in 材料} == {(保留ID, 保留版本)}