实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
387 lines
17 KiB
Python
387 lines
17 KiB
Python
"""离线原观察承接:真实重算诊断和保真,文件产物不能成为正式批准。"""
|
||
|
||
import json
|
||
from copy import deepcopy
|
||
from dataclasses import asdict, replace
|
||
|
||
import pytest
|
||
from pydantic import TypeAdapter, ValidationError
|
||
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.审校修订.接口 import 允许选区, 离线修订回放, 离线修订请求, 诊断文本, 读取内置规则
|
||
from muse.正式变更.接口 import 固定哈希
|
||
from muse.正文写作.接口 import 可见文本, 换行节点, 文本节点, 正文草稿, 段落, 段落哈希, 选段修改
|
||
|
||
|
||
def 请求副本():
|
||
doc = 正文草稿((段落("p1", (文本节点("值得注意的是,门外已经下起了雨。"),)),))
|
||
artifact = 诊断文本(
|
||
"值得注意的是,门外已经下起了雨。",
|
||
work_id="synthetic:demo",
|
||
规则库=读取内置规则(),
|
||
预览候选=True,
|
||
)
|
||
finding = next(f for f in artifact["findings"] if f["rule_id"] == "l002")
|
||
patch = 选段修改("p1", 段落哈希(doc.paragraphs[0]), 0, 7, "值得注意的是,", "", finding["id"])
|
||
return TypeAdapter(离线修订请求).validate_python(
|
||
{
|
||
"work_id": "synthetic:demo",
|
||
"document": doc,
|
||
"diagnosis": artifact,
|
||
"patches": (patch,),
|
||
"allowed_ranges": (允许选区("p1", 0, 7),),
|
||
"approved_findings": (finding["id"],),
|
||
"purpose": "删除作者确认无功能的元话语",
|
||
"author_approved": True,
|
||
"author_choice": "candidate",
|
||
"fact_snapshot": {
|
||
"payload": {"entities": []},
|
||
"content_hash": 固定哈希({"entities": []}),
|
||
},
|
||
}
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w23-239009",
|
||
environment="离线",
|
||
given="具名作者、固定来源、隔离测试配置与真实模块存储",
|
||
when="通过实际公开入口执行并核对拒绝、状态及持久结果",
|
||
then=["修订机械门回放保真"],
|
||
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md",
|
||
)
|
||
def test_修订机械门回放保真__239009():
|
||
"""只验证机械回放;独立成对选择由原观察集成测试另行核验。"""
|
||
req = 请求副本()
|
||
before = deepcopy(asdict(req))
|
||
report = 离线修订回放(req)
|
||
assert report["hard_gate"]["mechanical_pass"]
|
||
assert report["regression_gate"]["pass"]
|
||
assert report["candidate_text"] == "门外已经下起了雨。"
|
||
assert report["author_choice"] == "candidate" and report["final"]["action"] == "candidate"
|
||
assert report["summary"]["requested_changes"] == 1
|
||
assert not report["persisted"] and not report["model_verified"]
|
||
assert report["production_validity"] == "not_checked"
|
||
assert asdict(req) == before
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-7c28d3e5268e",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_artifact_header_missing_blocks_patch__7c28d3():
|
||
req = 请求副本()
|
||
for field in ("mode", "text_hash", "rule_library_version", "schema_version"):
|
||
artifact = deepcopy(req.diagnosis)
|
||
artifact.pop(field)
|
||
with pytest.raises(Muse错误):
|
||
离线修订回放(replace(req, diagnosis=artifact))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-2a3922291273",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_no_artifact_refuses_to_start__2a3922():
|
||
with pytest.raises(Muse错误, match="诊断"):
|
||
replace(请求副本(), diagnosis={})
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-c6ee33ed9ed2",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_no_patches_refuses_to_start__c6ee33():
|
||
with pytest.raises(Muse错误, match="修改"):
|
||
replace(请求副本(), patches=())
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-31336539614c",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["缺快照以明确audit_only结果返回并保留原文,不再依赖旧异常类名;没有产生提议稿。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_missing_snapshot_downgrades_to_audit__313365():
|
||
req = replace(请求副本(), fact_snapshot=None)
|
||
result = 离线修订回放(req)
|
||
assert result["final"]["action"] == "audit_only" and result["proposed_document"] is None
|
||
assert result["candidate_text"] == result["original_text"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-5ede83a386d7",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["显式缺快照授权只放开离线预览,事实来源仍missing且不能成为正式批准。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_snapshot_missing_with_explicit_authorization_proceeds__5ede83():
|
||
req = replace(请求副本(), fact_snapshot=None, no_snapshot_authorization=True)
|
||
result = 离线修订回放(req)
|
||
assert result["final"]["action"] == "candidate"
|
||
assert result["candidate_text"] == "门外已经下起了雨。"
|
||
assert result["fact_snapshot_verification"] == "missing" and not result["persisted"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-2fe0502fb00f",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["不信任传入确定性发现,重算诊断与规则副本后逐字段核对,伪rule_id被拒绝。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_forged_deterministic_finding_is_rejected__2fe050():
|
||
req = 请求副本()
|
||
artifact = deepcopy(req.diagnosis)
|
||
artifact["findings"][0]["rule_id"] = "l999"
|
||
with pytest.raises(Muse错误, match="确定性发现"):
|
||
离线修订回放(replace(req, diagnosis=artifact))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-86066255ada7",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["批准发现独立于不可变诊断保存,不篡改decision_proposal为repair;未点名批准不得修改。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_unarbitrated_finding_is_rejected__860662():
|
||
req = replace(请求副本(), approved_findings=())
|
||
with pytest.raises(Muse错误, match="作者批准"):
|
||
离线修订回放(req)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-1d7b172d5a50",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["不接受调用方自报硬门或human_score;输入合同拒绝预制检查,报告只由确定性检查生成。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_report_rejects_score_fields__1d7b17():
|
||
value = asdict(请求副本())
|
||
value["quality_score"] = 100
|
||
with pytest.raises(ValidationError):
|
||
TypeAdapter(离线修订请求).validate_python(value)
|
||
value.pop("quality_score")
|
||
value["hard_gate"] = {"pass": True, "checks": {}, "unverified": [], "human_score": 0.9}
|
||
with pytest.raises(ValidationError):
|
||
TypeAdapter(离线修订请求).validate_python(value)
|
||
artifact = deepcopy(value["diagnosis"])
|
||
artifact["human_probability"] = 1
|
||
with pytest.raises(Muse错误):
|
||
离线修订回放(replace(请求副本(), diagnosis=artifact))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-9cc771e0dac1",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=[
|
||
(
|
||
"从同一CLI main读取实际请求并输出JSON文件,禁止装配及读取DB配置,候选文本一致;输出明"
|
||
"确为离线副本而非落库。"
|
||
)
|
||
],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_cli_offline_patch_chain_writes_report_without_db__9cc771(tmp_path, monkeypatch, capsys):
|
||
import muse.接入.cli.入口 as cli
|
||
|
||
def 禁止构建(*args, **kwargs):
|
||
pytest.fail("显式离线回放不应装配或读取数据库配置")
|
||
|
||
monkeypatch.setattr(cli, "构建", 禁止构建)
|
||
monkeypatch.setattr(cli, "读取配置", 禁止构建)
|
||
request = tmp_path / "request.json"
|
||
request.write_text(json.dumps(asdict(请求副本()), ensure_ascii=False))
|
||
assert cli.main(["审校", "不存在的配置.toml", "修订回放", str(request), "--离线"]) == 0
|
||
text = capsys.readouterr().out
|
||
output = tmp_path / "report.json"
|
||
output.write_text(text)
|
||
report = json.loads(output.read_text())
|
||
assert report["candidate_text"] == "门外已经下起了雨。"
|
||
assert report["mode"] == "offline_preview" and not report["persisted"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-8b943d1cfafd",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["缺文件统一为新版CLI结构化业务失败及非零退出,不保留旧脚本专属退出码2。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_cli_requires_artifact_file__8b943d(tmp_path, capsys):
|
||
from muse.接入.cli.入口 import main
|
||
|
||
assert main(["审校", "-", "修订回放", str(tmp_path / "missing.json"), "--离线"]) != 0
|
||
error = json.loads(capsys.readouterr().err)
|
||
assert error["code"] and "请求文件" in error["message"]
|
||
|
||
|
||
def 以原文请求(doc, start, end, rule="l002"):
|
||
text = 可见文本(doc)
|
||
artifact = 诊断文本(text, work_id="synthetic:demo", 规则库=读取内置规则(), 预览候选=True)
|
||
finding = next(f for f in artifact["findings"] if f["rule_id"] == rule)
|
||
patch = 选段修改(
|
||
"p1", 段落哈希(doc.paragraphs[0]), start, end, text[start:end], "", finding["id"]
|
||
)
|
||
return replace(
|
||
请求副本(),
|
||
document=doc,
|
||
diagnosis=artifact,
|
||
patches=(patch,),
|
||
allowed_ranges=(允许选区("p1", start, end),),
|
||
approved_findings=(finding["id"],),
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-3d92e0528232",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_patch_without_finding_rejected__3d92e0():
|
||
req = 请求副本()
|
||
with pytest.raises(Muse错误, match="真实发现"):
|
||
离线修订回放(replace(req, patches=(replace(req.patches[0], finding_id="absent"),)))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-1ef3f9a6f139",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["重复文本不接受仅字面原串的旧patch形状;新协议要求段落、码点与哈希,不任取首次命中。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_patch_nonunique_match_rejected__1ef3f9():
|
||
text = "值得注意的是,值得注意的是,他来了。"
|
||
doc = 正文草稿((段落("p1", (文本节点(text),)),))
|
||
value = asdict(以原文请求(doc, 0, 7))
|
||
value["patches"] = [
|
||
{
|
||
"finding_id": value["approved_findings"][0],
|
||
"original": "值得注意的是,",
|
||
"replacement": "",
|
||
}
|
||
]
|
||
with pytest.raises(ValidationError):
|
||
TypeAdapter(离线修订请求).validate_python(value)
|
||
assert 可见文本(doc) == text
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-c0739c86f8d4",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_patch_not_covering_span_rejected__c0739c():
|
||
req = 请求副本()
|
||
patch = replace(req.patches[0], start=1, original="得注意的是,")
|
||
with pytest.raises(Muse错误, match="未覆盖"):
|
||
离线修订回放(replace(req, patches=(patch,)))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-96af2d395ccb",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=[
|
||
"作者明确替换协议不再接受skip伪装;额外action字段连同偷渡替换被类型合同拒绝。实际模型retain偷渡另由232002验证。"
|
||
],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_skip_patch_cannot_smuggle_replacement__96af2d():
|
||
value = asdict(请求副本())
|
||
patch = dict(value["patches"][0], action="skip", replacement="他突然笑了")
|
||
value["patches"] = [patch]
|
||
with pytest.raises(ValidationError):
|
||
TypeAdapter(离线修订请求).validate_python(value)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-2da14f616f72",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_patch_does_not_normalize_unrelated_blank_lines__2da14f():
|
||
doc = 正文草稿(
|
||
(
|
||
段落(
|
||
"p1",
|
||
(
|
||
文本节点("前文。"),
|
||
换行节点(),
|
||
换行节点(),
|
||
换行节点(),
|
||
文本节点("值得注意的是,他来了。"),
|
||
),
|
||
),
|
||
)
|
||
)
|
||
result = 离线修订回放(以原文请求(doc, 6, 13))
|
||
assert result["candidate_text"] == "前文。\n\n\n他来了。"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-4de18907353d",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_adjacent_punctuation_extension_allowed__4de189():
|
||
text = "他打量她,嘴角微微上扬。「姑娘」"
|
||
doc = 正文草稿((段落("p1", (文本节点(text),)),))
|
||
result = 离线修订回放(以原文请求(doc, 4, 11, rule="l003"))
|
||
assert result["candidate_text"] == "他打量她。「姑娘」"
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-35024a0ab5c7",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=["保留原例输入中的拒绝、保真或范围外不变性,以B05文档树与B06公开核验替代旧内部字典形状。"],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_patch_outside_author_scope_is_rejected__35024a():
|
||
req = replace(请求副本(), allowed_ranges=(允许选区("another-paragraph", 0, 7),))
|
||
with pytest.raises(Muse错误, match="允许范围"):
|
||
离线修订回放(req)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"TC-b9e7a876c2f7",
|
||
environment="默认离线",
|
||
when="执行具名公开保真、离线回放或实际CLI与隔离PG入口",
|
||
then=[
|
||
"旧测试把诊断patch扩大到整句。新版在批准发现范围内故意新增三百五十两验证同类数字硬门,同时由范围用例拒绝越界扩句。"
|
||
],
|
||
contract="docs/系统架构/新版设计/功能规格/P04-声音诊断与受控修订.md",
|
||
)
|
||
def test_fact_delta_blocks_new_number__b9e7a8():
|
||
doc = 正文草稿((段落("p1", (文本节点("值得注意的是,他付了三百两。"),)),))
|
||
req = 以原文请求(doc, 0, 7)
|
||
req = replace(req, patches=(replace(req.patches[0], replacement="他付了三百五十两,"),))
|
||
result = 离线修订回放(req)
|
||
assert not result["hard_gate"]["mechanical_pass"]
|
||
assert any("数字" in e for e in result["hard_gate"]["errors"])
|
||
assert result["candidate_text"] == result["original_text"]
|