import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md" TSL_INTRODUCTION = ROOT / "docs" / "tsl" / "syntax" / "01_introduction.md" TSL_QUICKSTART = ROOT / "docs" / "tsl" / "syntax" / "02_quickstart.md" TSL_CORE_MODEL = ROOT / "docs" / "tsl" / "syntax" / "03_core_model.md" TSL_VALUES = ROOT / "docs" / "tsl" / "syntax" / "04_values_and_literals.md" TSL_VARIABLES = ROOT / "docs" / "tsl" / "syntax" / "05_variables_and_constants.md" TSL_FUNCTIONS = ROOT / "docs" / "tsl" / "syntax" / "06_functions_and_calls.md" TSL_EXPRESSIONS = ROOT / "docs" / "tsl" / "syntax" / "07_expressions_and_operators.md" README = ROOT / "README.md" PLAYBOOK_EXAMPLE = ROOT / "playbook.toml.example" SKILLS_DOC = ROOT / "SKILLS.md" TEMPLATES_CI_README = ROOT / "templates" / "ci" / "README.md" TEMPLATES_README = ROOT / "templates" / "README.md" REMOVED_TSL_GUIDE = ROOT / "skills" / "tsl-guide" TSL_REFERENCE_INDEX = ROOT / "docs" / "tsl" / "reference" / "index.md" TSL_REFERENCE_CATALOG_INDEX = ROOT / "docs" / "tsl" / "reference" / "catalog" / "index.md" TSL_REFERENCE_VERIFIED_INDEX = ROOT / "docs" / "tsl" / "reference" / "verified" / "index.md" TSL_REFERENCE_VERIFIED_CORE = ROOT / "docs" / "tsl" / "reference" / "verified" / "core.md" TSL_REFERENCE_UNAVAILABLE = ROOT / "docs" / "tsl" / "reference" / "unavailable_methods.md" TSL_FINANCE_INDEX = ROOT / "docs" / "tsl" / "finance" / "index.md" TSL_MODULES_INDEX = ROOT / "docs" / "tsl" / "modules" / "index.md" TSL_REMAINING_SYNTAX_AGENT_SECTIONS = { "08_control_flow.md": "Agent 控制流判断流程", "09_objects_and_classes.md": "Agent 对象/类判断流程", "10_units_and_scope.md": "Agent unit/作用域判断流程", "11_runtime_context_and_with.md": "Agent 运行时上下文判断流程", "12_pitfalls.md": "Agent 常见误写判断流程", "13_matrix_and_collections.md": "Agent 矩阵/集合判断流程", "14_resultset_and_filters.md": "Agent 结果集/过滤判断流程", "15_ts_sql.md": "Agent TS-SQL 判断流程", "16_debug_and_profiler.md": "Agent 调试/Profiler 判断流程", "18_lexical_structure_and_compile_options.md": "Agent 词法/编译选项判断流程", "19_types_and_conversions.md": "Agent 类型/转换判断流程", "20_strings_and_text.md": "Agent 字符串/文本判断流程", "21_external_calls_and_threads.md": "Agent 外部调用/线程判断流程", "22_namespace_libpath_and_unit_runtime.md": "Agent 命名空间/Libpath 判断流程", "23_object_runtime_and_introspection.md": "Agent 对象运行时/反射判断流程", "24_builtin_runtime_objects.md": "Agent 内置运行时对象判断流程", "25_set_operations.md": "Agent 集合运算判断流程", "26_matrix_deep_dive.md": "Agent 矩阵深水判断流程", "27_fmarray.md": "Agent FMArray 判断流程", "28_ts_sql_core.md": "Agent TS-SQL Core 判断流程", "29_ts_sql_advanced.md": "Agent TS-SQL Advanced 判断流程", "30_runtime_services_and_global_cache.md": "Agent 运行时服务/全局缓存判断流程", "31_complex_and_weakref.md": "Agent 复数/弱引用判断流程", "32_object_overloads_and_iteration.md": "Agent 对象重载/迭代判断流程", } class TslEntrypointsConsistencyTests(unittest.TestCase): def test_ruleset_lists_canonical_tsl_layers(self): text = RULESET_TSL.read_text(encoding="utf-8") self.assertIn("docs/tsl/index.md", text) self.assertIn("docs/tsl/syntax/index.md", text) self.assertIn("docs/tsl/finance/index.md", text) self.assertIn("docs/tsl/modules/index.md", text) self.assertIn("docs/tsl/reference/index.md", text) def test_readme_only_lists_two_official_deployment_routes(self): text = README.read_text(encoding="utf-8") self.assertIn("方式一:git subtree", text) self.assertIn("方式二:外部 clone 后执行部署", text) self.assertIn("`project_root`:目标项目根目录", text) self.assertIn("`deploy_root`:相对于 `project_root` 的项目内目标目录", text) self.assertIn("不是外部 clone 出来的 Playbook 仓库路径", text) self.assertIn("外部 clone 场景下必须显式填写 `deploy_root`", text) self.assertNotIn("方式二:手动复制快照", text) self.assertNotIn("方式三:CLI 裁剪复制", text) self.assertNotIn("如果省略 `deploy_root`,默认仍部署到 `docs/standards/playbook`", text) def test_playbook_example_defines_deploy_root_as_target_path(self): text = PLAYBOOK_EXAMPLE.read_text(encoding="utf-8") self.assertIn('deploy_root = "docs/standards/playbook"', text) self.assertIn("相对于 project_root", text) self.assertIn("不是外部 clone 的 playbook 路径", text) self.assertIn("从外部 clone 执行时必填", text) self.assertNotIn("target_dir", text) def test_deployment_docs_do_not_reference_legacy_terms(self): self.assertNotIn("vendoring", README.read_text(encoding="utf-8")) self.assertNotIn("`.tmp`", README.read_text(encoding="utf-8")) self.assertNotIn("vendoring", SKILLS_DOC.read_text(encoding="utf-8")) self.assertNotIn("vendoring", TEMPLATES_CI_README.read_text(encoding="utf-8")) def test_repo_docs_use_platform_neutral_skills_source_dir(self): readme_text = README.read_text(encoding="utf-8") skills_text = SKILLS_DOC.read_text(encoding="utf-8") templates_text = TEMPLATES_README.read_text(encoding="utf-8") self.assertIn("`skills/`", readme_text) self.assertIn("`skills/`", skills_text) self.assertIn("├── skills/", templates_text) self.assertNotIn("`codex/skills/`", readme_text) self.assertNotIn("`codex/skills/`", skills_text) self.assertNotIn("├── codex/skills/", templates_text) def test_repo_no_longer_ships_tsl_guide_skill(self): self.assertFalse(REMOVED_TSL_GUIDE.exists()) self.assertNotIn("tsl-guide", README.read_text(encoding="utf-8")) self.assertNotIn("tsl-guide", SKILLS_DOC.read_text(encoding="utf-8")) self.assertNotIn("$tsl-guide", RULESET_TSL.read_text(encoding="utf-8")) def test_tsl_ruleset_describes_agent_first_file_model(self): text = RULESET_TSL.read_text(encoding="utf-8") self.assertIn("agent 判断流程", text) self.assertIn("用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据", text) self.assertIn("`.tsl` 是可执行脚本", text) self.assertIn("语句区按顺序执行", text) self.assertIn("函数/类声明区", text) self.assertIn("`.tsf` 是模块/函数扩展文件", text) self.assertIn("funcext", text) self.assertIn("仍不明确时向用户确认", text) self.assertNotIn("`.tsl` 仅 `function`", text) self.assertNotIn("脚本优先 `.tsl`,可复用扩展优先 `.tsf`", text) def test_tsl_syntax_docs_keep_script_then_declarations_model(self): quickstart = (ROOT / "docs/tsl/syntax/02_quickstart.md").read_text( encoding="utf-8" ) core_model = (ROOT / "docs/tsl/syntax/03_core_model.md").read_text( encoding="utf-8" ) functions = (ROOT / "docs/tsl/syntax/06_functions_and_calls.md").read_text( encoding="utf-8" ) pitfalls = (ROOT / "docs/tsl/syntax/12_pitfalls.md").read_text( encoding="utf-8" ) for text in (quickstart, core_model, functions, pitfalls): self.assertIn("语句区", text) self.assertIn("声明区", text) self.assertIn("test();", quickstart) self.assertIn("function test();", quickstart) self.assertIn("脚本语句后可以接函数声明", functions) self.assertIn("不要在声明区后面继续写脚本语句", pitfalls) self.assertNotIn("不要把顶层函数定义和松散语句混写", quickstart) self.assertNotIn("不要把顶层函数定义和松散语句混写", core_model) self.assertNotIn("不要把顶层函数定义和松散语句混在同一个文件模型里", functions) self.assertIn("后缀就是判断依据", quickstart) self.assertIn("未给后缀", quickstart) def test_tsl_agent_entrypoints_do_not_require_runtime_verification(self): entrypoints = [ RULESET_TSL, ROOT / "docs/tsl/index.md", ROOT / "docs/tsl/syntax/index.md", ] forbidden_phrases = [ "用当前解释器验证", "本地用 `tsl` 验证", "用 `tsl` 实测", "最小实测", "才写最小 `.tsl` / `.tsf` 例子", ] for path in entrypoints: text = path.read_text(encoding="utf-8") for phrase in forbidden_phrases: self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}") def test_tsl_index_requires_maintainer_verified_examples_before_publication(self): text = (ROOT / "docs/tsl/index.md").read_text(encoding="utf-8") self.assertIn("维护者入文档前验证", text) self.assertIn("代码库样例", text) self.assertIn("环境验证通过", text) self.assertIn("不能写成语法事实", text) self.assertIn("验证过程不写进语法页", text) self.assertIn("agent 不需要自行执行验证", text) def test_tsl_introduction_is_agent_first_decision_model(self): text = TSL_INTRODUCTION.read_text(encoding="utf-8") self.assertIn("agent", text) self.assertIn("后缀就是判断依据", text) self.assertIn("用户未给后缀", text) self.assertIn("可执行代码对应 `.tsl`", text) self.assertIn("通用模块对应 `.tsf`", text) self.assertIn("`.tsl` 可执行脚本第一印象", text) self.assertIn("`.tsf` 通用模块第一印象", text) self.assertIn("不要发明语法", text) self.assertIn("test();", text) self.assertIn("function test();", text) self.assertIn("function Test1();", text) self.assertNotIn("先看顶层主体", text) self.assertNotIn("顶层主体优先收敛成四类", text) def test_tsl_quickstart_is_agent_coding_gate(self): text = TSL_QUICKSTART.read_text(encoding="utf-8") self.assertIn("Agent 快速落代码流程", text) self.assertIn("先看用户有没有指定 `.tsl` / `.tsf` 后缀", text) self.assertIn("再根据交付目标判断 `.tsl` 或 `.tsf`", text) self.assertIn("只从 `代码块身份:已验证可执行示例` 的骨架起手", text) self.assertIn("不要发明语法", text) self.assertIn("不要在声明区后面继续追加脚本语句", text) def test_tsl_quickstart_labels_observable_outputs(self): text = TSL_QUICKSTART.read_text(encoding="utf-8") self.assertIn("代码块身份:已验证输出片段", text) self.assertIn("```text\ntest\n```", text) self.assertIn("```text\n5\n```", text) self.assertIn("```text\ntest1\n```", text) self.assertIn("```text\nhello\n```", text) def test_tsl_quickstart_omits_environment_verification_details(self): text = TSL_QUICKSTART.read_text(encoding="utf-8") for phrase in [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ]: self.assertNotIn(phrase, text) def test_tsl_core_model_is_agent_file_model_decision_page(self): text = TSL_CORE_MODEL.read_text(encoding="utf-8") self.assertIn("Agent 文件模型判断流程", text) self.assertIn("后缀是第一证据", text) self.assertIn("可执行交付", text) self.assertIn("可复用扩展交付", text) self.assertIn("不要把 `.tsl` 写成只有顶层函数的模块", text) self.assertIn("不要把 `.tsf` 写成会直接执行脚本语句的入口", text) self.assertIn("没有文档证据时不要发明文件模型", text) self.assertNotIn("误以为扩展名本身就完全决定能否写函数、类或 `unit`", text) def test_tsl_core_model_examples_have_verified_outputs(self): text = TSL_CORE_MODEL.read_text(encoding="utf-8") self.assertIn("代码块身份:已验证输出片段", text) self.assertIn("```text\ntest\n```", text) self.assertIn("```text\n5\n```", text) self.assertIn("```text\n1\n```", text) self.assertIn("```text\ninvalid statement\n```", text) def test_tsl_core_model_omits_environment_verification_details(self): text = TSL_CORE_MODEL.read_text(encoding="utf-8") for phrase in [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ]: self.assertNotIn(phrase, text) def test_tsl_values_page_is_agent_value_decision_page(self): text = TSL_VALUES.read_text(encoding="utf-8") self.assertIn("Agent 值写法判断流程", text) self.assertIn("普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手", text) self.assertIn("字符串默认用普通字符串", text) self.assertIn("`L\"\"` / `U\"\"` 只在编码或宽串场景", text) self.assertIn("先判断要顺序数组还是字符串键表", text) self.assertIn("顺序数组和二进制缓冲区下标从 `0` 开始", text) self.assertIn("字符串下标从 `1` 开始", text) self.assertIn("没有已验证代码块时不要发明值写法", text) def test_tsl_values_key_examples_have_verified_output_snippets(self): text = TSL_VALUES.read_text(encoding="utf-8") self.assertIn("代码块身份:已验证输出片段", text) self.assertIn("```text\n1\n12.5\nABC\n1\n0\n2\n```", text) self.assertIn("```text\n10\n20\n0001\nA\nB\nC\n```", text) self.assertIn("```text\nBCD\n```", text) self.assertIn("```text\nString index out of bounds\n```", text) def test_tsl_values_omits_environment_verification_details(self): text = TSL_VALUES.read_text(encoding="utf-8") for phrase in [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ]: self.assertNotIn(phrase, text) def test_tsl_variables_page_is_agent_binding_decision_page(self): text = TSL_VARIABLES.read_text(encoding="utf-8") self.assertIn("Agent 变量/常量判断流程", text) self.assertIn("普通变量默认直接用 `:=` 首次赋值", text) self.assertIn("只有用户要求显式声明或遇到 `{$Explicit+}` 时才优先写 `var`", text) self.assertIn("顶层脚本常量优先用 `const name := value;`", text) self.assertIn("`const Name = value;` 优先放在函数 `const` 段、`unit` 接口或类成员里", text) self.assertIn("单变量拆包必须写成 `[name, ] := array(...)`", text) self.assertIn("没有已验证代码块时不要发明变量/常量写法", text) def test_tsl_variables_key_examples_have_verified_output_snippets(self): text = TSL_VARIABLES.read_text(encoding="utf-8") self.assertIn("代码块身份:已验证输出片段", text) self.assertIn("```text\n1\n2\n```", text) self.assertIn("```text\n7\n```", text) self.assertIn("```text\n1\n3\n```", text) self.assertIn("```text\n1\n1\n```", text) self.assertIn("```text\nvariable not defined\n```", text) def test_tsl_variables_omits_environment_verification_details(self): text = TSL_VARIABLES.read_text(encoding="utf-8") for phrase in [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ]: self.assertNotIn(phrase, text) def test_tsl_functions_page_is_agent_function_decision_page(self): text = TSL_FUNCTIONS.read_text(encoding="utf-8") self.assertIn("Agent 函数/调用判断流程", text) self.assertIn("先判断当前文件是 `.tsl` 还是 `.tsf`", text) self.assertIn("`.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面", text) self.assertIn("需要返回值时用 `function`,不需要返回值时用 `procedure`", text) self.assertIn("命名参数只写 `name: value`", text) self.assertIn("不要把 `name = value` 当成命名参数", text) self.assertIn("没有已验证代码块时不要发明函数/调用写法", text) self.assertNotIn("在我于", text) def test_tsl_functions_key_examples_have_verified_output_snippets(self): text = TSL_FUNCTIONS.read_text(encoding="utf-8") self.assertIn("代码块身份:已验证输出片段", text) self.assertIn("```text\ntest\n```", text) self.assertIn("```text\n2\n```", text) self.assertIn("```text\n12\n```", text) self.assertIn("```text\ninvalid statement\n```", text) def test_tsl_functions_omits_environment_verification_details(self): text = TSL_FUNCTIONS.read_text(encoding="utf-8") for phrase in [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ]: self.assertNotIn(phrase, text) def test_tsl_expressions_page_is_agent_expression_decision_page(self): text = TSL_EXPRESSIONS.read_text(encoding="utf-8") self.assertIn("Agent 表达式/运算符判断流程", text) self.assertIn("先判断要写赋值、比较、条件求值、表达式对象、空安全访问还是链式比较", text) self.assertIn("赋值只能用 `:=`", text) self.assertIn("比较才用 `=`", text) self.assertIn("条件求值优先用 `flag ? true_value : false_value`", text) self.assertIn("`if condition then true_value else false_value` 必须带 `else`", text) self.assertIn("没有已验证代码块时不要发明表达式/运算符写法", text) self.assertNotIn("当前环境里", text) def test_tsl_expressions_key_examples_have_verified_output_snippets(self): text = TSL_EXPRESSIONS.read_text(encoding="utf-8") self.assertIn("代码块身份:已验证输出片段", text) self.assertIn("```text\n3\n```", text) self.assertIn("```text\nAB\n```", text) self.assertIn("```text\n222888\n1\n1\n1\n1\n```", text) self.assertIn("```text\n1\n0\n```", text) self.assertIn("```text\n2\n```", text) self.assertIn("```text\n6\n```", text) self.assertIn("```text\n1\n7\n1\n```", text) self.assertIn("```text\ninvalid statement\n```", text) def test_tsl_expressions_omits_environment_verification_details(self): text = TSL_EXPRESSIONS.read_text(encoding="utf-8") for phrase in [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ]: self.assertNotIn(phrase, text) def test_remaining_tsl_syntax_pages_are_agent_decision_pages(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" for filename, section in TSL_REMAINING_SYNTAX_AGENT_SECTIONS.items(): with self.subTest(filename=filename): text = (syntax_root / filename).read_text(encoding="utf-8") self.assertIn(section, text) self.assertIn("agent", text.lower()) self.assertIn("不要发明", text) def test_remaining_tsl_syntax_pages_have_verified_output_snippets(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS: with self.subTest(filename=filename): text = (syntax_root / filename).read_text(encoding="utf-8") if ( "代码块身份:已验证可执行示例" in text or "代码块身份:反例 / 不可照写" in text ): self.assertIn("代码块身份:已验证输出片段", text) def test_remaining_tsl_syntax_pages_omit_personal_and_environment_details(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" forbidden_phrases = [ "我在", "当前环境里", "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", ] for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS: with self.subTest(filename=filename): text = (syntax_root / filename).read_text(encoding="utf-8") for phrase in forbidden_phrases: self.assertNotIn(phrase, text) def test_tsl_reference_index_requires_verified_function_workflow(self): text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8") self.assertIn("Agent 函数使用规则", text) self.assertIn("只从 verified 函数页读取参数类型", text) self.assertIn("catalog 只是候选函数索引", text) self.assertIn("确认每个方法的参数类型", text) self.assertIn("verified/index.md", text) self.assertIn("verified/core.md", text) self.assertIn("unavailable_methods.md", text) self.assertIn("不能把 catalog 里的函数名直接当成可调用事实", text) self.assertNotIn("verification_failures", text) self.assertNotIn("函数入库验证流程", text) self.assertNotIn("验证过程", text) self.assertNotIn("代码块身份", text) self.assertNotIn("已验证输出片段", text) self.assertNotIn("验证失败", text) def test_tsl_reference_catalog_is_candidate_index_not_callable_fact(self): catalog_index = TSL_REFERENCE_CATALOG_INDEX.read_text(encoding="utf-8") self.assertIn("候选函数索引", catalog_index) self.assertIn("候选名没有进入 verified 函数页前不能当成可调用事实", catalog_index) self.assertIn("../verified/index.md", catalog_index) self.assertIn("../verified/core.md", catalog_index) self.assertIn("只从 verified 函数页读取参数类型", catalog_index) self.assertNotIn("错误参数组合", catalog_index) self.assertNotIn("参数验证", catalog_index) for path in (ROOT / "docs" / "tsl" / "reference" / "catalog").glob("*.md"): text = path.read_text(encoding="utf-8") self.assertIn("候选函数索引", text, msg=f"{path.name} missing candidate warning") self.assertIn("只从 verified 函数页读取参数类型", text) def test_tsl_reference_verified_index_routes_to_parameter_fact_pages(self): text = TSL_REFERENCE_VERIFIED_INDEX.read_text(encoding="utf-8") self.assertIn("文档类型:agent 参数事实索引", text) self.assertIn("core.md", text) self.assertIn("Abs", text) self.assertIn("ifInt", text) self.assertIn("DateToStr", text) self.assertIn("Length", text) self.assertIn("只从具体函数页读取接收类型", text) self.assertNotIn("验证过程", text) self.assertNotIn("代码块身份", text) def test_tsl_reference_verified_core_records_agent_parameter_facts(self): text = TSL_REFERENCE_VERIFIED_CORE.read_text(encoding="utf-8") self.assertIn("文档类型:agent 参数事实表", text) self.assertIn("接收类型", text) self.assertIn("返回", text) self.assertIn("`Abs(value)`", text) self.assertIn("`ifInt(value)`", text) self.assertIn("`DateToStr(value)`", text) self.assertIn("`Length(value)`", text) self.assertIn("整数", text) self.assertIn("实数", text) self.assertIn("字符串", text) self.assertIn("数组", text) self.assertIn("日期时间", text) self.assertNotIn("代码块身份", text) self.assertNotIn("已验证输出片段", text) self.assertNotIn("Function Abs execution error", text) self.assertNotIn("错误参数", text) def test_tsl_reference_all_markdown_avoid_verification_logs(self): for path in (ROOT / "docs" / "tsl" / "reference").rglob("*.md"): text = path.read_text(encoding="utf-8") with self.subTest(path=path.relative_to(ROOT)): self.assertNotIn("验证过程", text) self.assertNotIn("函数入库验证流程", text) self.assertNotIn("代码块身份", text) self.assertNotIn("已验证输出片段", text) self.assertNotIn("Function Abs execution error", text) def test_tsl_reference_unavailable_records_only_unavailable_methods(self): text = TSL_REFERENCE_UNAVAILABLE.read_text(encoding="utf-8") self.assertIn("文档类型:当前测试环境不支持的方法清单", text) self.assertIn("当前测试环境不支持的方法", text) self.assertIn("暂无已入档记录", text) self.assertNotIn("failure", text.lower()) self.assertNotIn("失败", text) self.assertNotIn("验证过程", text) self.assertNotIn("参数类型错误", text) self.assertNotIn("`Abs(\"-3\")`", text) self.assertNotIn("`Abs()`", text) self.assertNotIn("`DateToStr(\"2011-12-31\")`", text) self.assertNotIn("`DateToStr()`", text) self.assertNotIn("`Length(123)`", text) self.assertNotIn("Function Abs execution error", text) def test_tsl_reference_docs_do_not_name_valid_parameter_mismatches_as_failures(self): reference_root = ROOT / "docs" / "tsl" / "reference" for path in reference_root.rglob("*.md"): text = path.read_text(encoding="utf-8") with self.subTest(path=path.relative_to(ROOT)): self.assertNotIn("verification_failures", text) self.assertNotIn("错误参数", text) self.assertNotIn("验证失败", text) self.assertNotIn("Abs(\"-3\")", text) self.assertNotIn("DateToStr(\"2011-12-31\")", text) self.assertNotIn("Length(123)", text) def test_tsl_finance_docs_are_agent_business_decision_pages(self): finance_root = ROOT / "docs" / "tsl" / "finance" for path in finance_root.glob("*.md"): text = path.read_text(encoding="utf-8") with self.subTest(path=path.name): self.assertIn("Agent", text) self.assertIn("不要发明", text) self.assertIn("项目实际接口", text) self.assertNotIn("是否含已验证", text) self.assertNotIn("代码块身份", text) self.assertNotIn("验证过程", text) def test_tsl_finance_index_routes_all_finance_pages(self): text = TSL_FINANCE_INDEX.read_text(encoding="utf-8") self.assertIn("Agent Finance 路由规则", text) self.assertIn("entry_decision.md", text) self.assertIn("market_data_context.md", text) self.assertIn("series_and_indicator_model.md", text) self.assertIn("selection_and_signal_patterns.md", text) self.assertIn("backtest_and_trade_flow.md", text) def test_tsl_modules_docs_are_agent_integration_boundary_pages(self): modules_root = ROOT / "docs" / "tsl" / "modules" for path in modules_root.glob("*.md"): text = path.read_text(encoding="utf-8") with self.subTest(path=path.name): self.assertIn("Agent", text) self.assertIn("不要发明", text) self.assertNotIn("是否含已验证", text) self.assertNotIn("代码块身份", text) self.assertNotIn("验证过程", text) def test_tsl_modules_index_routes_all_module_pages(self): text = TSL_MODULES_INDEX.read_text(encoding="utf-8") self.assertIn("Agent Modules 路由规则", text) self.assertIn("tsbacktesting.md", text) self.assertIn("tsl_python_interop.md", text) self.assertIn("wechat_message.md", text) self.assertIn("pytsl_api.md", text) def test_tsl_finance_and_modules_omit_environment_verification_details(self): forbidden_phrases = [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", "当前环境里", "实测", ] for root_name in ("finance", "modules"): for path in (ROOT / "docs" / "tsl" / root_name).glob("*.md"): text = path.read_text(encoding="utf-8") for phrase in forbidden_phrases: self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}") if __name__ == "__main__": unittest.main()