muse-agent-example/tests/架构/test_技能与文档索引.py
zizi d909d1bd1b 后端实现与用例身份:19 包集成落地并修复收尾缺陷
实现侧:
- 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。
- 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。
- 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。
- 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。
- 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。
- 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。
- 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。
- 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。

用例侧:
- 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存;
- 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
2026-09-18 01:15:00 +08:00

750 lines
32 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.

"""技能与文档索引行为验证。
保护:技能目录合同(SKILL.md 完整、名称唯一且全中文、frontmatter 一致)
与活跃文档索引(目录.md 标准索引、链接可达)。
测试针对现行能力与索引合同,不维护历史过程状态。
"""
from __future__ import annotations
import re
import sys
import unicodedata
from pathlib import Path
import pytest
仓库根 = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(仓库根 / "工具"))
from 维护索引 import 检查技能目录, 检查技能索引, 检查目录链接 # noqa: E402
def _写能力样本(根: Path, 相对: str, 身份: str, 名称: str) -> Path:
import yaml
文件 = 根 / 相对
文件.parent.mkdir(parents=True, exist_ok=True)
声明 = {
"id": 身份,
"name": 名称,
"category": "method",
"contract_version": 1,
"description": "合成方法",
}
文件.write_text(
"---\n" + yaml.safe_dump(声明, allow_unicode=True) + "---\n\n只提出有依据的规划方法。\n"
)
return 文件
@pytest.mark.case_id(
"TC-a189e40d3896",
environment="离线,临时资源目录",
given="不依赖当前仓的临时项目,仅登记一份alpha能力文件",
when="从其他cwd编译显式资源根",
then=["只得到一份具名能力和原字节,无异常"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_passing_project_is_independent_of_current_repository__a189e4(tmp_path, monkeypatch):
from 构建资源包 import 编译能力资源
根 = tmp_path / "独立项目"
文件 = _写能力样本(根, ".agent/skills/alpha/SKILL.md", "alpha", "合成方法")
monkeypatch.chdir(tmp_path)
目录, 资源 = 编译能力资源(根, [文件.relative_to(根).as_posix()], set())
assert set(目录["method"]) == {"alpha"}
assert len(资源) == 1 and 资源["能力/method/alpha.md"] == 文件.read_bytes()
@pytest.mark.case_id(
"TC-c9cb853e4a71",
environment="离线,临时资源目录",
given="显式登记.agent/skills/planning/alpha/SKILL.md",
when="从嵌套物理路径编译能力",
then=["稳定包路径和原文件字节一致"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_nested_skill_path_is_accepted_from_manifest__c9cb85(tmp_path):
from 构建资源包 import 编译能力资源
相对 = ".agent/skills/planning/alpha/SKILL.md"
文件 = _写能力样本(tmp_path, 相对, "alpha", "合成方法")
目录, 资源 = 编译能力资源(tmp_path, [相对], set())
assert 目录["method"]["alpha"]["resource_path"] == "能力/method/alpha.md"
assert 资源["能力/method/alpha.md"] == 文件.read_bytes()
@pytest.mark.case_id(
"TC-c089ceceda65",
environment="离线,临时资源目录",
given="能力name为原输入alpha",
when="构建中文能力目录",
then=["以名称错误拒绝编译"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_chinese_only_policy_rejects_english_manifest_name__c089ce(tmp_path):
from 构建资源包 import 编译能力资源
_写能力样本(tmp_path, "SKILL.md", "alpha", "alpha")
with pytest.raises(ValueError, match="中文名称"):
编译能力资源(tmp_path, ["SKILL.md"], set())
@pytest.mark.case_id(
"TC-b72bcc397057",
environment="离线,临时资源目录",
given="规划作品和新增技能两个中文方法",
when="编译两份原名称资源",
then=["两份能力与字节资源均被接纳"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_chinese_only_policy_allows_new_native_chinese_skill__b72bcc(tmp_path):
from 构建资源包 import 编译能力资源
a = _写能力样本(tmp_path, "规划/规划作品/SKILL.md", "plan-work", "规划作品")
b = _写能力样本(tmp_path, "写作/新增技能/SKILL.md", "new-method", "新增技能")
目录, 资源 = 编译能力资源(tmp_path, [p.relative_to(tmp_path).as_posix() for p in (a, b)], set())
assert {v["name"] for v in 目录["method"].values()} == {"规划作品", "新增技能"}
assert len(资源) == 2
@pytest.mark.case_id(
"TC-5b50cc5fa9e9",
environment="离线,临时资源目录",
given="原输入e加组合重音-name作为展示名",
when="解析并编译能力声明",
then=["名称无效时拒绝,不对调用方原输入静默改写"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_non_normalized_skill_name_is_rejected__5b50cc(tmp_path):
from 构建资源包 import 编译能力资源
_写能力样本(tmp_path, "SKILL.md", "alpha", "e\u0301-name")
with pytest.raises(ValueError, match="中文名称"):
编译能力资源(tmp_path, ["SKILL.md"], set())
@pytest.mark.case_id(
"TC-984f56f78feb",
environment="离线,临时资源目录",
given="原输入故事结构!作为展示名",
when="解析并编译能力声明",
then=["以名称错误拒绝"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_skill_name_with_decorative_punctuation_is_rejected__984f56(tmp_path):
from 构建资源包 import 编译能力资源
_写能力样本(tmp_path, "SKILL.md", "story", "故事结构!")
with pytest.raises(ValueError, match="中文名称"):
编译能力资源(tmp_path, ["SKILL.md"], set())
@pytest.mark.case_id(
"NC-capability-stable-identity",
environment="离线资源编译",
given="显式角色frontmatter及正文",
when="编译并移动物理文件",
then=["稳定身份、包内路径及正文哈希不因源路径移动改变"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_资源按稳定身份编译且移动不改变身份__a66001(tmp_path):
from 构建资源包 import 编译能力资源
文件 = tmp_path / "初始.md"
文件.write_text(
"---\nid: writer\nname: 写手\ncategory: role\ncontract_version: 1\n"
"description: 合成职责\n---\n\n只执行固定任务。\n"
)
目录, 资源 = 编译能力资源(tmp_path, ["初始.md"], set())
assert 目录["role"]["writer"]["name"] == "写手"
assert 资源[目录["role"]["writer"]["resource_path"]] == 文件.read_bytes()
文件.rename(tmp_path / "迁移.md")
新目录, _ = 编译能力资源(tmp_path, ["迁移.md"], set())
assert 新目录["role"]["writer"]["sha256"] == 目录["role"]["writer"]["sha256"]
@pytest.mark.case_id(
"NC-capability-command-registry",
environment="离线资源编译",
given="操作技能引用具名命令",
when="核对公开操作及重复身份",
then=["未实现命令和重复能力身份拒绝;有效引用可进入目录"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_技能只声明已实现命令且重复身份拒绝__a66002(tmp_path):
import pytest
from 构建资源包 import 编译能力资源
文件 = tmp_path / "SKILL.md"
文件.write_text(
"---\nid: inspect-task\nname: 查看任务\ncategory: operation\ncontract_version: 1\n"
"description: 合成操作\ncommands: [read_task]\n---\n\n按授权读取任务。\n"
)
with pytest.raises(ValueError, match="命令"):
编译能力资源(tmp_path, ["SKILL.md"], set())
目录, _ = 编译能力资源(tmp_path, ["SKILL.md"], {"read_task"})
assert 目录["operation"]["inspect-task"]["commands"] == ["read_task"]
(tmp_path / "副本.md").write_bytes(文件.read_bytes())
with pytest.raises(ValueError, match="重复"):
编译能力资源(tmp_path, ["SKILL.md", "副本.md"], {"read_task"})
@pytest.mark.case_id(
"NC-capability-runtime-resource",
environment="离线实际包资源",
given="实际资源包中的五角色与两操作",
when="经公开资源读取及角色策略接口加载",
then=["按稳定身份和冻结哈希取正文,不将frontmatter写入提示,未知哈希拒绝"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_运行按发布目录读取角色正文和命令__a66003():
import pytest
from muse.任务运行.接口 import 角色策略目录
from muse.共享.错误 import 资源缺失错误
from muse.资源加载 import 读取能力, 读取能力目录
目录 = 读取能力目录()
assert set(目录["role"]) == {"writer", "planner", "extractor", "detector", "judge"}
角色 = 读取能力("role", "writer", 预期哈希=目录["role"]["writer"]["sha256"])
assert 角色["name"] == "写手" and "生成阶段" in 角色["正文"]
assert not 角色["正文"].startswith("---")
策略 = 角色策略目录.从发布包()
assert 策略.读取角色提示("writer") == 角色["正文"]
assert len(策略.资源发布身份) == 64
操作 = 读取能力("operation", "inspect-task")
assert "control_task" in 操作["commands"]
with pytest.raises(资源缺失错误):
读取能力("role", "writer", 预期哈希="0" * 64)
def _建技能(
根: Path, 分组: str, 名称: str, *, front名: str | None = None, 正文: str | None = None
) -> Path:
目录 = 根 / ".agent" / "skills" / 分组 / 名称
目录.mkdir(parents=True, exist_ok=True)
if 正文 is None:
有效名 = front名 if front名 is not None else 名称
正文 = f"---\nname: {有效名}\ndescription: 合成技能说明\n---\n\n# {名称}\n\n方法正文。\n"
(目录 / "SKILL.md").write_text(正文, encoding="utf-8")
return 目录
@pytest.mark.case_id(
"TC-b35be1711a49",
environment="离线,临时资源目录",
given=".agent 下所有一级内容目录与根索引",
when="核对索引存在、固定五列表头和根索引发现路径",
then=["三种检查全部通过,缺失或不可发现时独立失败"],
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_agent_content_directories_have_standard_indexes__b35be1() -> None:
"""一级内容目录可从根索引发现,且使用固定五列表头。"""
根 = 仓库根 / ".agent"
根索引 = (根 / "目录.md").read_text()
表头 = "| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |"
for 目录 in sorted(p for p in 根.iterdir() if p.is_dir()):
索引 = 目录 / "目录.md"
assert 索引.is_file(), f"缺少一级目录索引:{目录.name}"
正文 = re.sub(r"\A(?:<!-- 导航元信息: .*? -->\n)+", "", 索引.read_text())
assert 正文.startswith(表头), f"未使用固定五列表头:{索引}"
assert f"{目录.name}/目录.md" in 根索引, f"一级目录不可发现:{目录.name}"
@pytest.mark.case_id(
"TC-3734a6672473",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_active_markdown_links_resolve__3734a6() -> None:
"""活跃执行知识(.agent 树)的 Markdown 相对链接可达。"""
问题: list[str] = []
import re
链接 = re.compile(r"\[[^\]]*\]\(([^)#\s]+)[)#]")
for 文件 in (仓库根 / ".agent").rglob("*.md"):
if "__pycache__" in 文件.parts:
continue
base = 文件.parent
for 序, 行 in enumerate(文件.read_text(encoding="utf-8").splitlines(), 1):
if 行.strip().startswith("|"):
continue # 目录表行由 检查目录链接 覆盖
for 地址 in 链接.findall(行):
if 地址.startswith(("http://", "https://", "mailto:")):
continue
if not (base / 地址).exists():
问题.append(f"{文件.relative_to(仓库根)}:{序} 链接不存在:{地址}")
assert 问题 == [], "\n".join(问题[:10])
@pytest.mark.case_id(
"TC-e8450b6e7c65",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_skill_directory_without_skill_file_fails__e8450b(tmp_path: Path) -> None:
"""技能目录缺少 SKILL.md 时被报告。"""
目录 = tmp_path / ".agent" / "skills" / "写作" / "无文件技能"
目录.mkdir(parents=True)
(目录 / "references.md").write_text("只有资料", encoding="utf-8")
问题 = 检查技能目录(tmp_path)
assert any("缺少 SKILL.md" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-1d2b76028e03",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_empty_skill_file_is_reported__1d2b76(tmp_path: Path) -> None:
"""空 SKILL.md 被报告,不静默放行。"""
_建技能(tmp_path, "写作", "空技能", 正文="---\n")
问题 = 检查技能目录(tmp_path)
assert any("为空" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-703c7b148785",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_frontmatter_name_must_match_skill_directory__703c7b(tmp_path: Path) -> None:
"""frontmatter name 必须与技能目录名一致。"""
_建技能(tmp_path, "写作", "真名技能", front名="别名技能")
问题 = 检查技能目录(tmp_path)
assert any("name 与目录不一致" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-eb80b52b2bdc",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_duplicate_frontmatter_names_are_reported__eb80b5(tmp_path: Path) -> None:
"""同名技能(跨分组)被报告。"""
_建技能(tmp_path, "写作", "重复技能")
_建技能(tmp_path, "规划", "重复技能")
问题 = 检查技能目录(tmp_path)
assert any("重复" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-de3efec07d8b",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_chinese_skill_name_is_accepted__de3efe(tmp_path: Path) -> None:
"""全中文技能名通过全部目录合同。"""
_建技能(tmp_path, "写作", "中文技能")
assert 检查技能目录(tmp_path) == []
@pytest.mark.case_id(
"TC-d5c460039ec2",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_chinese_only_policy_rejects_english_skill_name__d5c460(tmp_path: Path) -> None:
"""英文技能名被中文唯一政策拒绝。"""
_建技能(tmp_path, "写作", "english-skill")
问题 = 检查技能目录(tmp_path)
assert any("全中文" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-a87a412a67d0",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_non_normalized_skill_name_is_rejected__a87a41(tmp_path: Path) -> None:
"""非 NFC 规范形式的技能名被拒绝。"""
非规范 = "组合e\u0301音技能" # e + 组合变音符:NFC 会合成 é,与原名不同
assert unicodedata.normalize("NFC", 非规范) != 非规范
_建技能(tmp_path, "写作", 非规范)
问题 = 检查技能目录(tmp_path)
assert any("NFC" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-b68ab9a51c08",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_skill_name_with_whitespace_is_rejected__b68ab9(tmp_path: Path) -> None:
"""含空白的技能名被拒绝。"""
_建技能(tmp_path, "写作", "带 空白技能")
问题 = 检查技能目录(tmp_path)
assert any("含空白" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-5acb683e6f5c",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_missing_skills_directory_fails_closed__5acb68(tmp_path: Path) -> None:
"""技能根目录缺失时明确失败,不静默跳过。"""
问题 = 检查技能目录(tmp_path)
assert any("不存在" in p for p in 问题), 问题
@pytest.mark.case_id(
"TC-000974f474ac",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_index_matches_disk_and_manifest__000974(tmp_path: Path) -> None:
"""每个分组的 目录.md 索引与磁盘上的技能目录一一对应(不多不少)。"""
assert 检查技能索引(仓库根) == []
from 维护索引 import 写入目录
技能 = _建技能(tmp_path, "写作", "新技能")
assert 检查技能索引(tmp_path) # 源新增后,双向检查不能遗漏新技能。
写入目录(tmp_path)
assert 检查技能索引(tmp_path) == []
assert 写入目录(tmp_path) == []
原索引 = (技能.parent / "目录.md").read_text()
assert "合成技能说明" in 原索引
assert "| | |" in 原索引 # 缺场景或要求时保持未知,不生成作者意图。
(技能.parent / "目录.md").write_text(原索引 + 原索引.splitlines()[-1] + "\n")
assert any("重复" in 问题 for 问题 in 检查技能索引(tmp_path))
写入目录(tmp_path)
assert 检查技能索引(tmp_path) == []
(技能 / "SKILL.md").unlink()
写入目录(tmp_path)
assert "新技能/SKILL.md" not in (技能.parent / "目录.md").read_text()
@pytest.mark.case_id(
"NC-index-links-current-tree",
environment="离线",
given="当前仓库全部 目录.md 索引",
when="运行目录链接检查",
then=["所有表格相对链接可达,无悬空索引"],
contract="docs/系统架构/新版设计/文件设计/工程配置与部署.md#file-87efcaf91ed97cf4",
)
def test_仓库当前树通过目录链接检查__b7c8d9() -> None:
"""当前仓库 目录.md 索引链接全部可达。"""
assert 检查目录链接(仓库根) == []
# ---------------------------------------------------------------------------
# 能力目录与角色合同(原 SkillCatalogTest)
# ---------------------------------------------------------------------------
def _角色字节(身份: str, 名称: str, 额外: str = "") -> bytes:
return (
f"---\nid: {身份}\nname: {名称}\ncategory: role\n"
f"contract_version: 1\ndescription: 合成角色\n{额外}---\n\n你是{名称},按合同执行。\n"
).encode()
@pytest.mark.case_id(
"TC-569f09681a64",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_current_role_catalog_is_exact_and_host_neutral__569f09() -> None:
"""已发布角色目录恰好五类且宿主中立:frontmatter 不携带任何模型绑定。"""
from muse.资源加载 import 解析能力声明, 读取能力目录
目录 = 读取能力目录()["role"]
assert set(目录) == {"writer", "planner", "extractor", "detector", "judge"}
from muse.资源加载 import 打开资源
for 身份, 条目 in 目录.items():
声明, 正文 = 解析能力声明(打开资源(条目["resource_path"]))
assert 声明["id"] == 身份 and 声明["category"] == "role"
assert "model" not in 声明 and "provider" not in 声明, f"{身份} 携带静态模型绑定"
assert 正文.strip()
@pytest.mark.case_id(
"TC-4da9120a63cb",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_role_catalog_ignores_directory_index__4da912(tmp_path: Path) -> None:
"""能力目录只收登记文件;目录索引不是能力,不进入发布包。"""
import sys as _sys
_sys.path.insert(0, str(仓库根 / "工具"))
from 构建资源包 import 编译能力资源
角色 = ("writer", "planner", "extractor", "detector", "judge")
名称 = {
"writer": "写手",
"planner": "规划员",
"extractor": "知识抽取员",
"detector": "检测员",
"judge": "评委",
}
for r in 角色:
(tmp_path / "角色").mkdir(exist_ok=True)
(tmp_path / "角色" / f"{名称[r]}.md").write_bytes(_角色字节(r, 名称[r]))
(tmp_path / "角色" / "目录.md").write_text(
"| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |\n"
"|------|----------|----------|----------|----------|\n",
encoding="utf-8",
)
目录, 资源 = 编译能力资源(tmp_path, [f"角色/{名称[r]}.md" for r in 角色], set())
assert set(目录["role"]) == set(角色)
assert len(资源) == 5 # 索引文件未被扫描进包
@pytest.mark.case_id(
"TC-a74ee65b8606",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_role_contract_is_central_and_complete__a74ee6() -> None:
"""角色合同与策略是唯一中心:五角色、固定模型策略与正文提示都齐备。"""
import yaml
from muse.任务运行.角色策略 import 角色策略目录
from muse.资源加载 import 读取能力目录
定义 = yaml.safe_load((仓库根 / "配置" / "角色策略.yaml").read_text(encoding="utf-8"))
策略 = 角色策略目录(定义, 角色资源=读取能力目录()["role"])
合同 = (仓库根 / ".agent" / "角色" / "角色合同.md").read_text(encoding="utf-8")
for 角色, 中文名 in {
"writer": "网文写手",
"detector": "检测员",
"judge": "质量评委",
"extractor": "知识抽取员",
}.items():
assert 角色 in 合同 and 中文名 in 合同
for 身份 in ("writer", "planner"):
assert 定义["roles"][身份]["model_policy"] == "fixed"
assert 定义["roles"]["judge"]["model_policy"] == "judge-fixed"
for 别名, 正名 in 定义.get("aliases", {}).items():
assert 策略.读取角色提示(别名) == 策略.读取角色提示(正名)
@pytest.mark.case_id(
"TC-7d0984bb6019",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_role_frontmatter_rejects_static_model_binding__7d0984() -> None:
"""角色 frontmatter 出现 model 等未登记字段直接拒绝。"""
from muse.资源加载 import 解析能力声明
with pytest.raises(ValueError, match="未登记字段"):
解析能力声明(_角色字节("writer", "写手", 额外="model: claude-opus\n"))
with pytest.raises(ValueError, match="未登记字段"):
解析能力声明(_角色字节("planner", "规划员", 额外="provider: primary\n"))
@pytest.mark.case_id(
"TC-0af776e73f52",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_current_catalog_is_valid__0af776() -> None:
"""按批准资源列表和源码frontmatter独立核验实际读取结果。"""
import hashlib
import json
import yaml
from muse.资源加载 import 打开资源, 读取能力, 读取能力目录
登记 = json.loads((仓库根 / "工具/资源登记.json").read_text())["能力文件"]
期望 = {}
for 相对 in 登记:
原字节 = (仓库根 / 相对).read_bytes()
_, 头部, 正文 = 原字节.decode().split("---", 2)
声明 = yaml.safe_load(头部)
# 首部导航注记是索引工装,只服务目录派生;进模型上下文的能力正文不含它。
期望[声明["category"], 声明["id"]] = (
声明,
re.sub(r"\A\s*(?:<!--\s*导航元信息:.*?-->\s*)+", "", 正文, flags=re.S).strip(),
原字节,
)
目录 = 读取能力目录()
assert {(分类, 身份) for 分类, 组 in 目录.items() for 身份 in 组} == set(期望)
for (分类, 身份), (声明, 正文, 原字节) in 期望.items():
哈希 = hashlib.sha256(原字节).hexdigest()
条目 = 目录[分类][身份]
资源 = 读取能力(分类, 身份, 预期哈希=哈希)
assert 条目["sha256"] == 哈希
assert 打开资源(条目["resource_path"]) == 原字节
assert 资源["name"] == 声明["name"]
assert 资源["description"] == 声明["description"]
assert 资源["正文"] == 正文 and 正文
@pytest.mark.case_id(
"TC-29d9f3aef372",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_directory_and_name_must_match__29d9f3(tmp_path: Path) -> None:
"""能力身份由声明而非文件位置决定:入包路径从身份派生,声明与目录一致。"""
import sys as _sys
_sys.path.insert(0, str(仓库根 / "工具"))
from muse.资源加载 import 解析能力声明
from 构建资源包 import 编译能力资源
# 源文件名与中文名无关;入包后由身份定位,不再允许目录名充当身份。
(tmp_path / "旧位置").mkdir()
源 = tmp_path / "旧位置" / "legacy-plan.md"
源.write_bytes(_角色字节("writer", "写手"))
目录, 资源 = 编译能力资源(tmp_path, ["旧位置/legacy-plan.md"], set())
assert "能力/role/writer.md" in 资源
声明, _ = 解析能力声明(资源["能力/role/writer.md"])
assert (声明["id"], 声明["name"]) == ("writer", "写手")
assert 目录["role"]["writer"]["resource_path"] == "能力/role/writer.md"
@pytest.mark.case_id(
"TC-16a67a06447a",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_chinese_skill_name_is_accepted__16a67a() -> None:
"""中文名称的能力声明被接受并原样保留。"""
from muse.资源加载 import 解析能力声明
声明, 正文 = 解析能力声明(_角色字节("writer", "写手"))
assert 声明["name"] == "写手"
assert "按合同执行" in 正文
@pytest.mark.case_id(
"TC-432390f6c7ef",
environment="离线,临时资源目录",
when="读取或生成登记资源及导航。",
contract="docs/系统架构/新版设计/编排与能力资源.md",
)
def test_unknown_frontmatter_key_is_rejected__432390() -> None:
"""未知 frontmatter 键被拒绝;合法键集内仍通过。"""
from muse.资源加载 import 解析能力声明
with pytest.raises(ValueError, match="未登记字段"):
解析能力声明(_角色字节("judge", "评委", 额外="temperature: 0.7\n"))
声明, _ = 解析能力声明(_角色字节("judge", "评委"))
assert set(声明) == {"id", "name", "category", "contract_version", "description"}
@pytest.mark.case_id(
"TC-fc9b819e3c0d",
environment="离线;scripted和资源指纹不证明真实模型",
when="运行新版公共场景裁决、实际CLI或安装包能力查询",
then=[
"旧Skill内CLI改为共享审校CLI;安装包建立规则技能独占propose_rule_from_cases,解析动作和稳定命令身份真实存在。"
],
contract="docs/系统架构/新版设计/验证设计/诊断行为场景.md",
)
def test_规则建立使用共享CLI且有独立命令归属__fc9b81():
from muse.接入.cli.入口 import _解析参数
from muse.资源加载 import 读取能力
args = _解析参数(["审校", "synthetic.toml", "案例规则", "request.json"])
assert args.动作 == "案例规则"
skill = 读取能力("operation", "operation.propose-review-rule")
assert skill["name"] == "建立机器味规则"
assert "propose_rule_from_cases" in skill["commands"]
assert "capture_quality_case" not in skill["commands"]
@pytest.mark.case_id(
"TC-d04e74393b21",
environment="离线;scripted和资源指纹不证明真实模型",
when="运行新版公共场景裁决、实际CLI或安装包能力查询",
then=["记录案例技能只暴露采集、审阅、决定、撤回与读取,不暴露规则生成命令。"],
contract="docs/系统架构/新版设计/验证设计/诊断行为场景.md",
)
def test_记录案例技能不暴露规则生成命令__d04e74():
from muse.资源加载 import 读取能力
skill = 读取能力("operation", "operation.capture-quality-case")
assert "capture_quality_case" in skill["commands"]
assert "propose_rule_from_cases" not in skill["commands"]
assert {"open_quality_case_review", "decide_quality_case", "withdraw_quality_case"} <= set(
skill["commands"]
)
@pytest.mark.case_id(
"TC-2242b7ff5b75",
environment="离线;scripted和资源指纹不证明真实模型",
when="运行新版公共场景裁决、实际CLI或安装包能力查询",
then=[
"20个研究项目与15项能力、六个技能归属在新版权威矩阵逐项校验;路径存在只为导航,pending/partial与限制保留。"
],
contract="docs/系统架构/新版设计/验证设计/诊断行为场景.md",
)
def test_研究能力映射保留20项来源与六技能归属__2242b7():
import json
from pathlib import Path
root = Path(__file__).parents[2]
path = root / "docs/系统架构/新版设计/研究依据/质量能力承接.json"
data = json.loads(path.read_text())
assert data["schema_version"] == "quality-research-capabilities-v2"
capabilities = data["capabilities"]
assert len(capabilities) >= 12
cap_ids = {c["id"] for c in capabilities}
assert len(data["projects"]) == 20
assert len({p["id"] for p in data["projects"]}) == 20
for p in data["projects"]:
assert p["mapped_capabilities"] and set(p["mapped_capabilities"]) <= cap_ids
assert {c["owner_skill"] for c in capabilities} == {
"建立作品声音档案",
"预防机器味",
"诊断机器味",
"修正正文机器味",
"记录机器味案例",
"建立机器味规则",
}
for c in capabilities:
assert (root / c["implementation"]).is_file()
assert c["status"] in {"implemented", "partial", "pending"}
assert c["verification_limit"]
@pytest.mark.case_id(
"NC-o04-capability-frontmatter-independent",
environment="离线,当前源资源与损坏的派生目录",
given="派生目录和读取能力同时带有相同的错误名称或描述",
when="对照批准资源登记和源frontmatter独立核验",
then=["不能因读取能力原样回传目录元信息而错误放行"],
)
@pytest.mark.parametrize("字段", ["name", "description"], ids=["name", "description"])
def test_能力元信息同源错误不掩盖真实声明(monkeypatch, 字段):
from copy import deepcopy
from muse import 资源加载
目录 = deepcopy(资源加载.读取能力目录())
组 = next(组 for 组 in 目录.values() if 组)
next(iter(组.values()))[字段] = "合成错误声明"
monkeypatch.setattr(资源加载, "读取能力目录", lambda: 目录)
with pytest.raises(AssertionError):
test_current_catalog_is_valid__0af776()