#!/usr/bin/env python3 """以真实离线CLI消费首批法器清单;保留输入、输出和JUnit,不执行被审工具。""" import argparse import configparser import copy from collections import Counter import hashlib import json import os import resource from pathlib import Path import subprocess import sys import tempfile import unittest import xml.etree.ElementTree as ET from markdown_it import MarkdownIt import source_archive ROOT = Path(__file__).resolve().parents[1] ARTIFACTS = None TEMPLATE = ROOT / ".github/tool-explanation-template-v1.md" def v1_field_names(): names = [] for line in TEMPLATE.read_text(encoding="utf-8").splitlines(): if line.startswith("| ") and line.split("|")[1].strip().isdigit(): names.append(line.split("|")[2].strip()) return names def v1_payload(subject_names): labels = v1_field_names() return { "template_sha256": hashlib.sha256(TEMPLATE.read_bytes()).hexdigest(), "items": [ { "object_key": "faqi:" + subject_id, "fields": [ {"name": label, "content": name if index == 0 else "隔离夹具:" + label} for index, label in enumerate(labels) ], } for subject_id, name in subject_names.items() ], } class FaqiCatalogTest(unittest.TestCase): def invoke(self, label, *args, memory_limit=None): command = [sys.executable, str(ROOT / ".github/sync-faqi-catalog.py"), *map(str, args)] def bound_memory(): resource.setrlimit(resource.RLIMIT_AS, (memory_limit, memory_limit)) result = subprocess.run(command, cwd=ROOT, capture_output=True, timeout=45, preexec_fn=bound_memory if memory_limit else None) (ARTIFACTS / (label + ".json")).write_text(json.dumps({ "command": command, "exit_code": result.returncode, "stdout": result.stdout.decode(), "stderr": result.stderr.decode(), }, ensure_ascii=False, indent=2), encoding="utf-8") return result def input_copy(self, label, data): path = ARTIFACTS / (label + "-input.json") path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") return path def test_skill_rename_and_cloud_retirement_keep_fixed_archival_bindings(self): self.assertTrue((ROOT / ".github/skills/skill/SKILL.md").is_file()) self.assertFalse((ROOT / ".github/skills/auto-skill").exists()) archive = json.loads((ROOT / ".github/context/faqi/data.json").read_text(encoding="utf-8")) self.assertEqual(".github/skills/skill", archive["locations"][".github/skills/auto-skill"]) self.assertEqual(".github/skills/skill/README.md", archive["locations"][".github/skills/auto-skill/README.md"]) live_skill = archive["live_sources"][".github/skills/auto-skill/README.md"] self.assertEqual(".github/skills/skill/README.md", live_skill["path"]) self.assertEqual(hashlib.sha256((ROOT / live_skill["path"]).read_bytes()).hexdigest(), live_skill["sha256"]) self.assertIn(".github/skills/auto-skill/README.md", archive["documents"]) current_skill_associations = { item["object_key"]: next(field["content"] for field in item["fields"] if field["name"] == "关联信息") for item in archive["explanations_v1"]["items"] if item["object_key"] in { "faqi:faqi-auto-skill-create", "faqi:faqi-auto-skill-validate", "faqi:faqi-skill-seekers-adapter", } } self.assertEqual(3, len(current_skill_associations)) # auto-skill路径在documents中保留为固定历史身份;当前位置由上面的映射断言约束。 for object_key, association in current_skill_associations.items(): self.assertIn("相关Skill说明=.github/skills/skill", association, object_key) self.assertNotIn("相关Skill说明=.github/skills/auto-skill", association, object_key) self.assertNotIn(".github/skills/auto-tmux", archive["locations"]) self.assertFalse(any(path.startswith(".github/skills/auto-tmux/") for path in archive["live_sources"])) self.assertFalse(any(item["path"].startswith(".github/skills/auto-tmux/") for item in archive["projections"])) self.assertNotIn(".github/context/external-resources", archive["locations"].values()) metadata = json.loads(source_archive.read_text(ROOT / ".github/metadata/faqi.json")) for source in metadata["external_sources"]: document = archive["documents"][source["path"]] self.assertEqual(source["sha256"], document["sha256"], source["path"]) self.assertNotIn(source["path"], archive["locations"]) explanations = {item["object_key"]: item["fields"] for item in archive["explanations_v1"]["items"]} auto_tmux = next(field["content"] for field in explanations["faqi:faqi-auto-tmux"] if field["name"] == "关联信息") self.assertIn("已从GitHub当前树移除", auto_tmux) self.assertTrue(TEMPLATE.is_file()) self.assertEqual(archive["explanations_v1"]["template_sha256"], hashlib.sha256(TEMPLATE.read_bytes()).hexdigest()) def test_current_source_review_is_consumable(self): result = self.invoke("current", "--check") self.assertEqual(0, result.returncode, result.stderr.decode()) report = json.loads(result.stdout) self.assertEqual((10, 13, 9, 158, 12, 8), ( report["objects"], report["local_entries"], report["local_exclusions"], report["external_resources"], report["dependency_objects"], report["dependency_categories"])) self.assertFalse(report["independent_review"]) self.assertFalse(report["runtime_verified"]) self.assertTrue(report["grades_assigned"]) data = json.loads(source_archive.read_text(ROOT / ".github/metadata/faqi.json")) self.assertEqual("faqi-master-classification-register", data["kind"]) dependency_objects = {o["id"]: o for o in data["dependency_graph"]["objects"]} self.assertNotIn("dep-claude-code", dependency_objects) self.assertEqual("1.1.0", dependency_objects["dep-pi-cli"]["version"]) self.assertEqual("software-artifact", dependency_objects["dep-pi-cli"]["type_id"]) self.assertIn("8类", data["dependency_graph"]["user_confirmation"]["statement"]) self.assertEqual(8, len(data["dependency_graph"]["dependency_categories"])) self.assertTrue(all(item["disposition"] == "user_confirmed_in_scope" for item in data["dependency_graph"]["dependency_categories"])) self.assertTrue(all(o["grade_id"] is None and o["grade_status"] == "pending" for o in dependency_objects.values())) assignments = data["provisional_grade_assignments"] expected_subjects = ({o["id"] for o in data["objects"]} | set(dependency_objects) | {node["id"] for node in data["dependency_graph"]["dependency_categories"]}) self.assertEqual(expected_subjects, {item["subject_id"] for item in assignments}) self.assertTrue(all(item["assignment_status"] == "user_directed_provisional" for item in assignments)) self.assertEqual({ "faqi-tian-advanced": 2, "faqi-tian-intermediate": 2, "faqi-di-advanced": 3, "faqi-di-intermediate": 5, "faqi-di-initial": 4, "faqi-xuan-advanced": 2, "faqi-xuan-intermediate": 4, "faqi-xuan-initial": 3, "faqi-huang-advanced": 3, "faqi-huang-intermediate": 1, "faqi-huang-initial": 1, }, Counter(item["grade_id"] for item in assignments)) self.assertEqual(30, len(assignments)) self.assertTrue(all(item["basis"] for item in assignments)) self.assertTrue(all(link["object_id"] in {o["id"] for o in data["objects"]} for link in data["dependency_graph"]["local_links"])) directory = ROOT / ".github/context/faqi" catalog = (directory / "README.md").read_text(encoding="utf-8") self.assertEqual({p.name for p in directory.iterdir()}, {'data.json', 'README.md', 'tian.md', 'di.md', 'xuan.md', 'huang.md'}) pages = {tier: (directory / f"{tier}.md").read_text(encoding="utf-8") for tier in ("tian", "di", "xuan", "huang")} all_views = catalog + "\n" + "\n".join(pages.values()) active_archive = json.loads((directory / "data.json").read_text(encoding="utf-8")) skill_seekers = next(item for item in active_archive["explanations_v1"]["items"] if item["object_key"] == "faqi:faqi-skill-seekers") association = next(field["content"] for field in skill_seekers["fields"] if field["name"] == "关联信息") self.assertIn("LICENSE文本标为MIT,但版权行仍为[Your Name/Username]占位符", association) self.assertIn("同修订pyproject.toml的authors字段列为Yusuf Karaaslan", association) self.assertIn("作者元数据不证明版权归属或授权", association) self.assertIn("本项目仍未批准该许可证", association) self.assertIn("本地入口=无", association) self.assertIn("已从当前树移除", association) retired_submodules = { ".github/tools/external/Skill_Seekers-development", ".github/tools/external/claude-official-skills", } self.assertEqual(retired_submodules, set(active_archive["retired_submodules"])) former_paths = { ".github/extensions/Skill_Seekers-development", ".github/extensions/claude-official-skills", } for path in retired_submodules: self.assertFalse(any(item == path or item.startswith(path + "/") for item in active_archive["locations"])) self.assertFalse(any(item == path or item.startswith(path + "/") for item in active_archive["live_sources"])) for path in former_paths: self.assertFalse(any(item == path or item.startswith(path + "/") for item in active_archive["locations"].values())) self.assertFalse(any(binding["path"] == path or binding["path"].startswith(path + "/") for binding in active_archive["live_sources"].values())) self.assertFalse((ROOT / path).exists(), path) self.assertFalse((ROOT / path).is_symlink(), path) self.assertFalse(any(item["path"] == ".github/init_skill.py" for item in active_archive["projections"])) self.assertEqual( "8c02660dacd304f51fe8c4a9685d3f6d5324a7eef7779b6baa7325827bf01985", active_archive["documents"][".github/tools/external/Skill_Seekers-development/src/skill_seekers/cli/main.py"]["sha256"], ) claude_source_hashes = { ".github/tools/external/claude-official-skills/README.md": "d7c5c2f9b248c7c0b31f093cf22b9a7407f5b093c8ca9ebe75a5b6845b02b76e", ".github/tools/external/claude-official-skills/skills/skill-creator/LICENSE.txt": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd", ".github/tools/external/claude-official-skills/skills/skill-creator/SKILL.md": "d57b6e3a44535b9c0ef454dad5c86f5df78185fc80a65c7092885b4b5e10f81b", ".github/tools/external/claude-official-skills/skills/skill-creator/scripts/init_skill.py": "cf12790f1d4958799281920d599f56aea38fb5c26e2812a61dd740bf3d4b6b99", } for path, digest in claude_source_hashes.items(): self.assertEqual(digest, active_archive["documents"][path]["sha256"], path) claude_fields = next(item["fields"] for item in active_archive["explanations_v1"]["items"] if item["object_key"] == "faqi:faqi-claude-skill-init") claude_sources = next(field["content"] for field in claude_fields if field["name"] == "出处") self.assertIn("1ed29a03dc852d30fa6ef2ca53a67dc2c2c2c563", claude_sources) self.assertTrue(all(path in claude_sources for path in claude_source_hashes)) module_config = configparser.RawConfigParser() module_config.read_string((ROOT / ".gitmodules").read_text(encoding="utf-8")) self.assertEqual({".github/extensions/oh-my-tmux", ".github/extensions/tmux"}, {module_config[section]["path"] for section in module_config.sections()}) self.assertFalse((ROOT / ".github/init_skill.py").exists()) self.assertFalse((ROOT / ".github/init_skill.py").is_symlink()) current_paths = { "faqi:faqi-oh-my-tmux": ".github/extensions/oh-my-tmux", "faqi:faqi-tmux": ".github/extensions/tmux", } associations = { item["object_key"]: next(field["content"] for field in item["fields"] if field["name"] == "关联信息") for item in active_archive["explanations_v1"]["items"] } claude_association = associations["faqi:faqi-claude-skill-init"] self.assertIn("本地入口=无", claude_association) self.assertIn("已从当前树移除", claude_association) self.assertNotIn("本地入口=.github/extensions/claude-official-skills", claude_association) for object_key, path in current_paths.items(): self.assertIn(f"本地入口={path}", associations[object_key], object_key) self.assertNotIn(".github/tools/prompts-library", active_archive["locations"]) self.assertFalse(any(path.startswith(".github/tools/prompts-library/") for path in active_archive["live_sources"])) for archived_path in ( ".github/tools/prompts-library/README.md", ".github/tools/prompts-library/main.py", ): self.assertIn(archived_path, active_archive["documents"]) self.assertNotIn(archived_path, active_archive["locations"]) for object_key in ( "faqi:faqi-prompt-converter", "faqi:faqi-codex-config-installer", "faqi:faqi-auto-skill-create", "faqi:faqi-auto-skill-validate", "faqi:faqi-skill-seekers-adapter", "faqi:faqi-auto-tmux", ): self.assertIn("本地入口=无", associations[object_key], object_key) self.assertIn("已退役", associations[object_key], object_key) converter_association = associations["faqi:faqi-prompt-converter"] self.assertNotIn(".github/extensions/prompts-library", converter_association) for path in ( ".github/skills/auto-skill/scripts/create-skill.sh", ".github/skills/auto-skill/scripts/validate-skill.sh", ".github/skills/auto-skill/scripts/skill-seekers.sh", ".github/skills/auto-tmux/scripts/auto-tmux.sh", ): self.assertNotIn(path, active_archive["locations"], path) fields_by_name = {field["name"]: field["content"] for field in skill_seekers["fields"]} implementation = fields_by_name["内部如何实现?"] cost = fields_by_name["付出了什么代价?"] applicability = fields_by_name["明确这些规律成立的前提与适用条件。"] conclusion = fields_by_name["结论"] warrant = fields_by_name["连接规则"] evidence = fields_by_name["依据"] self.assertIn("默认绑定127.0.0.1", implementation) self.assertIn("可通过--host指定其他地址", implementation) self.assertIn('allow_origins=["*"]', implementation) self.assertIn("所检server路径未配置应用层认证", implementation) self.assertIn("api_key参数最终加入子进程argv(--api-key)", cost) self.assertIn("形成进程参数暴露面", cost) self.assertIn("可被不可信调用者访问", cost) self.assertIn("仅固定修订26638b248279a3b001b651475e0f28975f5ff069的S67–S73选区", applicability) self.assertIn("未运行服务、导入或执行上游代码", applicability) self.assertIn("静态审阅结果:WARN", conclusion) self.assertIn("不要向不可信网络暴露HTTP MCP", conclusion) self.assertIn("来源S67/S68显示", warrant) self.assertIn("S67, S68, S69, S70, S71, S72, S73", evidence) self.assertIn("静态审阅结果:WARN", pages["xuan"]) self.assertIn("固定修订许可观察", pages["xuan"]) self.assertNotIn("dep-claude-code", all_views) self.assertNotIn("Claude Code(依赖图替代项)", all_views) self.assertIn("Vibe Coding 重要性暂定", catalog) self.assertIn("部分软件工件已退役但保留历史重要性记录", catalog) self.assertIn("不表示当前可运行", catalog) self.assertIn("Codex CLI", catalog) self.assertIn("", catalog) self.assertIn("## 法器总表", catalog) self.assertNotIn("| 序号 | 说明项 | 内容 |", catalog) self.assertIn("天阶高级", pages["tian"]) self.assertIn("法器对象ID=faqi-prompt-converter", all_views) self.assertIn("依赖图对象ID=dep-pi-cli", all_views) self.assertNotIn("依赖图位置", all_views) field_names = v1_field_names() self.assertEqual(18, len(field_names)) import tool_explanations table_count = 0 tier_counts = Counter(item["grade_id"].split("-")[1] for item in assignments) for tier, page in pages.items(): tokens = MarkdownIt("default").parse(page) table_indexes = [index for index, token in enumerate(tokens) if token.type == "table_open"] self.assertEqual(tier_counts[tier], len(table_indexes), f"{tier}文档只包含对应阶级的法器对象") table_count += len(table_indexes) for table_index in table_indexes: table_end = next(index for index in range(table_index + 1, len(tokens)) if tokens[index].type == "table_close") table_tokens = tokens[table_index:table_end + 1] self.assertEqual(19, sum(token.type == "tr_open" for token in table_tokens), "一行表头加18项说明") inline = [token.content for token in table_tokens if token.type == "inline"] self.assertEqual(["序号", "说明项", "内容"], inline[:3]) self.assertEqual(field_names, [inline[4 + index * 3] for index in range(18)]) self.assertEqual(57, sum(token.type in {"th_open", "td_open"} for token in table_tokens), "每张表严格三列") self.assertEqual(len(assignments), table_count, "每个法器对象恰好出现在一个阶级文档中") for item in assignments: tier = item["grade_id"].split("-")[1] anchor = tool_explanations.anchor_for("faqi:" + item["subject_id"]) self.assertIn(f"{tier}.md#{anchor}", catalog) self.assertIn(f'', catalog, "旧README对象锚点应保留为跳转入口") self.assertIn(assignments[0]["scope"], all_views) self.assertIn(assignments[0]["basis"], all_views) # 首批父仓库来源必须在已发布主线可达,不能依赖仅本地存在的提交。 published = subprocess.run(["git", "merge-base", "--is-ancestor", data["source_revision"], "refs/remotes/origin/main"], cwd=ROOT, capture_output=True, timeout=20) self.assertEqual(0, published.returncode, "父仓库来源不在已发布主线历史可达") objects = {o["id"]: o for o in data["objects"]} purposes = {s["id"]: s.get("purpose") for s in data["sources"]} # 用途来自实际读过的实现/说明选区,而非由对象引用反推。 self.assertEqual("support", purposes["S01"]) for sid in ("S02", "S06", "S08", "S09", "S10", "S12", "S16", "S18", "S21", "S23"): self.assertEqual("implementation", purposes[sid]) expected_exclusion_sources = {"S04", "S25", "S26", "S27", "S28", "S29", "S32", "S34", "S36"} for sid in expected_exclusion_sources: self.assertEqual("exclusion_evidence", purposes[sid]) exclusions = {(item["path"], item["name"]): item for item in data["local_exclusions"]} expected_excluded = { (".github/tools/chat-vault", "Chat Vault同步与查询程序"): "私人对话数据", (".github/tools/external/MCPlayerTransfer", "Minecraft基岩版角色迁移程序"): "LevelDB", (".github/tools/external/XHS-image-to-PDF-conversion", "小红书图片ZIP转PDF程序"): "原ZIP", (".github/tools/external/html-tools-main", "EPUB CSS清理网页程序"): "按用户指定", (".github/tools/external/html-tools-main", "Markdown预览与文本导出网页程序"): "按用户指定", (".github/tools/external/html-tools-main", "任务卡片PNG导出网页程序"): "按用户指定", (".github/tools/external/html-tools-main", "小红书内容卡片PNG导出网页程序"): "按用户指定", (".github/tools/external/html-tools-main", "Markdown同步预览与PNG导出网页程序"): "按用户指定", (".github/tools/external/my-nvim", "my-nvim的LazyVim引导配置程序"): "按用户指定", } self.assertEqual(set(expected_excluded), set(exclusions)) self.assertEqual(expected_exclusion_sources, {item["source"] for item in data["local_exclusions"]}) for key, evidence in expected_excluded.items(): self.assertIn(evidence, exclusions[key]["rationale"]) empty_paths = {entry["path"] for entry in data["local_entries"] if not entry["objects"]} self.assertEqual({key[0] for key in expected_excluded}, empty_paths) for excluded_id in ( "faqi-chat-vault", "faqi-mc-player-transfer", "faqi-xhs-zip-pdf", "faqi-html-epub-css", "faqi-html-markdown-text", "faqi-html-task-card", "faqi-html-xhs-card", "faqi-html-markdown-sync-png", "faqi-my-nvim-lua", ): self.assertNotIn(excluded_id, objects) # 期望来自实现选区与本体:可执行配置不是CLI本体,技能说明不是脚本。 for name in ("faqi-tmux", "faqi-oh-my-tmux", "faqi-codex-config-installer", "faqi-auto-tmux"): self.assertEqual("software-artifact", objects[name]["type_id"]) self.assertNotIn("faqi-codex-cli", objects) decisions = {rid: r["decision"] for r in data["external_reviews"] for rid in r["resource_ids"]} self.assertEqual("excluded_model", decisions["tools-platforms-claude-opus-4-6-f751380c"]) self.assertEqual("excluded_content", decisions["tools-platforms-prompt-jsonl-8e9b2066"]) self.assertEqual("pending_identity", decisions["tools-platforms-zsh-d6d24951"]) self.assertEqual("restricted_service_pending", decisions["network-payment-services-bybit-81d32c91"]) self.assertEqual("related_local_source", decisions["repo-rules-scaffolds-tmux-9f36981a"]) aliases = {a["path"]: a for a in data["references"]} self.assertEqual(("symlink", "../../../tools/external/tmux", "faqi-tmux"), ( aliases[".github/skills/auto-tmux/assets/tmux-src"]["state"], aliases[".github/skills/auto-tmux/assets/tmux-src"]["target"], aliases[".github/skills/auto-tmux/assets/tmux-src"]["related_object_id"], )) self.assertEqual(("symlink", "../../../tools/external/.tmux", "faqi-oh-my-tmux"), ( aliases[".github/skills/auto-tmux/assets/oh-my-tmux"]["state"], aliases[".github/skills/auto-tmux/assets/oh-my-tmux"]["target"], aliases[".github/skills/auto-tmux/assets/oh-my-tmux"]["related_object_id"], )) def test_license_evidence_sources_are_support_only(self): data = json.loads(source_archive.read_text(ROOT / ".github/metadata/faqi.json")) implementation = next(source for source in data["sources"] if source["id"] == "S18") relative_path = Path(".github/tools/external/.tmux/LICENSE.MIT") body = source_archive.read_bytes(ROOT / relative_path) line_count = len(body.decode("utf-8").splitlines(keepends=True)) license_source_id = "S_TEST_LICENSE" self.assertNotIn(license_source_id, {source["id"] for source in data["sources"]}) license_source = { "id": license_source_id, "purpose": "support", "path": relative_path.as_posix(), "repository": implementation["repository"], "revision": implementation["revision"], "sha256": hashlib.sha256(body).hexdigest(), "lines": [1, line_count + 1], "selection_sha256": hashlib.sha256(body).hexdigest(), } data["sources"].append(license_source) skill_object = next( item for item in data["objects"] if item["id"] == "faqi-oh-my-tmux" ) skill_object["support"].append(license_source_id) local_entry = next( item for item in data["local_entries"] if item["path"] == ".github/tools/external/.tmux" ) local_entry["sources"].append(license_source_id) result = self.invoke("license-document-as-support", "--input", self.input_copy("license-support", data), "--check") self.assertEqual(0, result.returncode, result.stderr.decode()) self.assertFalse(data["limits"]["license_approved"], "记录许可证原文不等于项目批准") license_source["purpose"] = "implementation" result = self.invoke("license-document-as-implementation", "--input", self.input_copy("license-implementation", data), "--check") self.assertNotEqual(0, result.returncode) self.assertIn("来源不是公开代码、配置或说明", result.stderr.decode()) support_only_sources = ("S57", "S58", "S61", "S62", "S63", "S64") for source_id in ("S57", "S58", "S59", "S60", "S61", "S62", "S63", "S64"): data = json.loads(source_archive.read_text(ROOT / ".github/metadata/faqi.json")) source = next(item for item in data["sources"] if item["id"] == source_id) self.assertEqual("support", source["purpose"]) if source_id not in support_only_sources: continue source["purpose"] = "implementation" result = self.invoke( f"{source_id.lower()}-notice-as-implementation", "--input", self.input_copy(f"{source_id.lower()}-implementation", data), "--check", ) self.assertNotEqual(0, result.returncode) self.assertIn("来源不是公开代码、配置或说明", result.stderr.decode()) data = json.loads(source_archive.read_text(ROOT / ".github/metadata/faqi.json")) unsupported_image = next(item for item in data["sources"] if item["id"] == "S64") unsupported_image["path"] = ".github/tools/external/tmux/logo/icons/128x128/tmux.png" result = self.invoke( "unsupported-image-as-notice-source", "--input", self.input_copy("unsupported-image", data), "--check", ) self.assertNotEqual(0, result.returncode) self.assertIn("来源不是公开代码、配置或说明", result.stderr.decode()) def test_rejects_unsupported_identity_and_source_claims_without_writing(self): original = json.loads(source_archive.read_text(ROOT / ".github/metadata/faqi.json")) changed = copy.deepcopy(original) changed["objects"].append({**changed["objects"][0], "id": "faqi-duplicate-cli-mcp"}) cases = [("same-content-two-identities", changed)] changed = copy.deepcopy(original); changed["objects"][0]["type_id"] = "model-artifact" cases.append(("model-as-tool", changed)) changed = copy.deepcopy(original); changed["limits"]["runtime_verified"] = True cases.append(("invented-runtime", changed)) changed = copy.deepcopy(original); changed["sources"][0]["sha256"] = "0" * 64 cases.append(("source-drift", changed)) changed = copy.deepcopy(original); changed["sources"][0]["lines"] = [1, 999999] cases.append(("selection-overrun", changed)) changed = copy.deepcopy(original); changed["sources"][0]["path"] = "../../outside.py" cases.append(("path-escape", changed)) changed = copy.deepcopy(original); changed["sources"][0]["path"] = ".github/tools/config/.codex/auth.json" cases.append(("credential-source", changed)) changed = copy.deepcopy(original); changed["external_reviews"][0]["resource_ids"].pop() cases.append(("missing-resource", changed)) changed = copy.deepcopy(original); changed["objects"][0]["grade"] = "天阶高级" cases.append(("grade-injection", changed)) changed = copy.deepcopy(original); changed["provisional_grade_assignments"][0]["grade_id"] = "invented-grade" cases.append(("unsupported-provisional-grade", changed)) changed = copy.deepcopy(original); changed["provisional_grade_assignments"].pop() cases.append(("missing-provisional-grade", changed)) changed = copy.deepcopy(original); changed["dependency_graph"]["objects"][0]["type_id"] = "invented-platform" cases.append(("unknown-dependency-type", changed)) changed = copy.deepcopy(original); changed["dependency_graph"]["source_sha256"] = "0" * 64 cases.append(("dependency-graph-source-drift", changed)) changed = copy.deepcopy(original); changed["objects"][0]["implementation"] = changed["sources"][0]["id"] cases.append(("description-as-implementation", changed)) changed = copy.deepcopy(original) changed["local_entries"][0]["objects"], changed["local_entries"][1]["objects"] = changed["local_entries"][1]["objects"], changed["local_entries"][0]["objects"] cases.append(("wrong-entry-identity", changed)) changed = copy.deepcopy(original); changed["sources"][1]["purpose"] = "support" cases.append(("unsupported-source-purpose", changed)) changed = copy.deepcopy(original); changed["local_exclusions"] = [] cases.append(("unrecorded-empty-entry", changed)) changed = copy.deepcopy(original) changed["local_exclusions"].append(copy.deepcopy(next(item for item in changed["local_exclusions"] if item["source"] == "S04"))) cases.append(("duplicate-exclusion-record", changed)) for source_id, path, name, support in ( ("S04", ".github/tools/chat-vault", "Chat Vault同步与查询程序", ["S03"]), ("S25", ".github/tools/external/html-tools-main", "EPUB CSS清理网页程序", ["S24"]), ("S32", ".github/tools/external/my-nvim", "my-nvim的LazyVim引导配置程序", ["S30", "S31"]), ): changed = copy.deepcopy(original) excluded_source = next(i for i, source in enumerate(changed["sources"]) if source["id"] == source_id) changed["sources"][excluded_source]["purpose"] = "implementation" resurrected = {"id": f"faqi-{source_id.lower()}-reintroduced", "name": name, "type_id": "software-artifact", "implementation": source_id, "support": support, "boundary": "反事实复入测试,不作新批准", "declared_version": None} changed["objects"].append(resurrected) entry = next(item for item in changed["local_entries"] if item["path"] == path) entry["objects"].append(resurrected["id"]) cases.append((f"excluded-source-{source_id}-reclassified-as-implementation", changed)) changed = copy.deepcopy(original); changed["sources"][1]["selection_sha256"] = "0" * 64 cases.append(("selection-drift", changed)) changed = copy.deepcopy(original); changed["objects"][0]["name"] = "程序\n```\n ``` [link](javascript:void(0)) |' safe_table = tool_explanations.render_table(hostile_fields) safe_markup = parser.render(safe_table) self.assertNotIn("