"""CodeStable skills 的直接语义守卫与 routing fixture 回归。""" from __future__ import annotations from pathlib import Path import yaml ROOT = Path(__file__).resolve().parents[1] SKILLS = ROOT / "plugins/codestable/skills" # 工具 skill(authoring/eval,不随插件交付)也必须使用标准 frontmatter。 LOCAL_SKILLS = ROOT / ".claude/skills" TASK_SKILLS = ("cs-feat", "cs-issue", "cs-refactor", "cs-epic") ORDINARY_TASK_SKILLS = ("cs-feat", "cs-issue", "cs-refactor") LEGACY_KNOWLEDGE_DIRS = { ".codestable/roadmap/", ".codestable/features/", ".codestable/issues/", ".codestable/refactors/", ".codestable/goals/", ".codestable/compound/", ".codestable/audits/", ".codestable/brainstorms/", ".codestable/feedback/", } LESSON_READ_REPAIR_CONTRACT = { "经验命中:{path}({status});核验:{fact};影响:{plan_or_check}", "旧 lesson 缺 `status` 按 `observed` 读取", "`retired` 不应用", "`observed` / `validated` 先核实再用", "只做一次有界、最低成本的定向核实", "不得仅为核实 lesson 运行大范围测试或反复复现", "仍不足时跳过该 lesson,不阻塞正常任务", "只是相关但没有改变行为时不制造复用证据", "明确排除一个具体且合理的错误路径", "当前事实明确反证时立即停止应用", "证据不足时不猜", } CRYSTALLIZATION_SIGNAL_CONTRACT = { "任务内只在内存保留最多 3 条候选", "不暂停或询问", "owner 纠正实际改变方案/代码/术语/验证", "可复现证据推翻根因", "同一路径失败两次后更换假设", "blocking/important finding 暴露未编码不变量", "新 red -> green 捕获可复发失败", "lesson 真实改变本次行为或被反证", "重复 workaround", "方法显著降低重试、成本或风险", "可追溯证据", "能写成未来动作", "本次精确 diff 之外", "没有现成 canonical owner", "网络波动、拼写、泛化口号、活动记录", "已被机械 owner 完整覆盖", } NARROW_LESSON_MAINTENANCE_CONTRACT = { "仅对已有且有效命中的 lesson", "`observed -> validated`", "独立后续任务确实采用并验证成功", "只补一次代表性证据", "必须记录 lesson 实际改变的计划或验证", "本次通过的验收证据", "`observed|validated -> retired`", "当前仓库事实直接反证", "发现已有 canonical owner", "只写原因与替代/反证指针", "不新建事实", "不改规则", "不扩 scope", "不新增 gate", "稳定 validated 命中不写文件", "需要改写结论或证据不足时只给候选", "新结论不得通过复活 retired 条目", "最终报告列出文件变化", } THIN_SKILL_SAFETY_INVARIANTS = { "cs": ( "同轮直转", "只推荐入口", "导览与推荐本身不写任何文件", "讨论只存在于当前会话", "不创建 `.codestable/work/discussion-*`", ), "cs-review": ( "只读", "叶子执行器", "禁止创建、委派或唤醒任何子 agent", "不得再次调用 `cs-review`", "不保存复审轮次", "blocking 未解决", ), "cs-code-review": ( "兼容别名", "canonical entry `cs-review`", "不得读取 sibling skill 文件", ), "cs-epic": ( "拆解方案必须经用户确认", "由当前主流程创建一个 fresh reviewer", "本轮失败且不计轮次", "不得盲目重发", "不代替用户做整体验收", ), "cs-feat": ( "不得代替用户确认设计", "由当前主流程创建一个 fresh reviewer", "本轮失败且不计轮次", "不得盲目重发", "与声明相称的可核验证据", ), "cs-issue": ( "能明确变红的验证", "变红的验证必须变绿", "由当前主流程创建一个 fresh reviewer", "本轮失败且不计轮次", "不得盲目重发", ), "cs-keep": ("没有可追溯证据不写", "`.codestable/compound/` 是只读历史知识源"), "cs-onboard": ("存量文件一律不动", "不复制"), "cs-refactor": ( "行为等价", "先有能自证等价的验证", "由当前主流程创建一个 fresh reviewer", "本轮失败且不计轮次", "不得盲目重发", ), } THIN_SKILL_FORBIDDEN_TEXT = { "cs": ("L0-L4",), "cs-review": ( "git push", "read all references", "用独立 subagent reviewer", "派发 reviewer 时", "探测全部已配置的审查 agent/model", "累计最多 3 轮", ), "cs-code-review": ("按 `cs-review` 的 SKILL.md 执行",), "cs-epic": ("ConfirmGoalCommitAuthorization", "git push", "read all references"), "cs-feat": ("git push", "read all references"), "cs-issue": ("git push", "read all references"), "cs-refactor": ("git push", "read all references"), } ISSUE_DIAGNOSIS_CONTRACT = ( "用户只要求定位、分析、解释或排查时,执行只诊断", "诊断结论不扩大为修复授权", "不改产品代码、测试或配置,不创建 commit", "diagnose 不新增审查阶段", "`已证实根因` / `可证伪假设` / `证据不足`", "至少实际运行一次", "可再次执行的命令及红色输出", "手工复现步骤及失败观察", "diagnose 下“优先失败测试”指优先运行既有失败或只读检查", "同一失败信号", "那条变红的验证必须变绿", "无法建立时列明已尝试", "日志、trace、环境访问", "临时 harness、日志或埋点", "先停下说明目标、路径、影响与清理方式", "同会话直接授权修复不算诊断终止", "核对工作树与仓库外临时路径", "残留文件与保留理由", "单独 change candidate", ) ISSUE_DEBUG_REFERENCE_CONTRACT = ( "覆盖用户实际症状", "出现任一才升级,不默认升级", "3-5 个排序的可证伪假设", "一次只动一个变量", "自动化二分", "提高并报告复现率", "性能问题先建立可比较的性能基线", "正确接缝", "前缀或等价标签", "定向搜索证明已移除", "最小场景", "原始完整复现", ) def _read_skill(path: Path) -> tuple[dict[str, object], str]: text = path.read_text(encoding="utf-8") assert text.startswith("---\n"), path _, frontmatter, body = text.split("---\n", 2) return yaml.safe_load(frontmatter), body def _contains_contract(text: str, anchor: str) -> bool: return "".join(anchor.split()) in "".join(text.split()) def test_active_skills_do_not_use_legacy_frontmatter_contracts() -> None: for root in (SKILLS, LOCAL_SKILLS): for path in sorted(root.glob("*/SKILL.md")): frontmatter, _ = _read_skill(path) assert "contracts" not in frontmatter, path def test_thin_skills_keep_explicit_safety_invariants() -> None: active_skills = {path.parent.name for path in SKILLS.glob("*/SKILL.md")} assert set(THIN_SKILL_SAFETY_INVARIANTS) == active_skills for skill_name, invariants in THIN_SKILL_SAFETY_INVARIANTS.items(): _, body = _read_skill(SKILLS / skill_name / "SKILL.md") for invariant in invariants: assert invariant in body, f"{skill_name}: missing {invariant!r}" for skill_name, forbidden_texts in THIN_SKILL_FORBIDDEN_TEXT.items(): _, body = _read_skill(SKILLS / skill_name / "SKILL.md") for forbidden in forbidden_texts: assert forbidden not in body, f"{skill_name}: forbidden {forbidden!r}" review_openai = yaml.safe_load( (SKILLS / "cs-review/agents/openai.yaml").read_text(encoding="utf-8") ) review_frontmatter, _ = _read_skill(SKILLS / "cs-review/SKILL.md") review_prompt = review_openai["interface"]["default_prompt"] assert str(review_frontmatter["description"]).startswith("只读审查叶子执行器") assert "$cs-review" in review_prompt assert "不创建任何子 agent" in review_prompt assert "独立" not in review_openai["interface"]["short_description"] assert "独立" not in review_prompt assert "派发独立 subagent reviewer" not in review_prompt def test_cs_issue_owns_diagnosis_and_authorized_fix_modes() -> None: frontmatter, issue = _read_skill(SKILLS / "cs-issue/SKILL.md") debug = (SKILLS / "cs-issue/references/debug.md").read_text(encoding="utf-8") _, cs = _read_skill(SKILLS / "cs/SKILL.md") assert str(frontmatter["description"]).startswith("诊断或修复 bug、报错、性能回退") for anchor in ISSUE_DIAGNOSIS_CONTRACT: assert _contains_contract(issue, anchor), f"cs-issue: missing {anchor!r}" for anchor in ISSUE_DEBUG_REFERENCE_CONTRACT: assert _contains_contract(debug, anchor), f"debug reference: missing {anchor!r}" shipped_issue = issue + debug assert "diagnosing-bugs" not in shipped_issue assert "排查这个报错为什么发生" in cs assert "排查、诊断、bug、报错、性能回退、行为异常" in cs refactor_frontmatter, refactor = _read_skill(SKILLS / "cs-refactor/SKILL.md") assert "行为等价" in refactor assert "性能优化" in str(refactor_frontmatter["description"]) def test_cs_discusses_in_session_then_hands_off_same_turn() -> None: _, cs = _read_skill(SKILLS / "cs/SKILL.md") for anchor in ( "用户显式要求先讨论", "默认优先级最高", "产品决策会实质改变建档或改代码路径", "讨论只存在于当前会话", "仓库可核实的事实由 agent 自行调查", "一次只问一个真正需要 owner 决定的问题", "目标入口、原始诉求、目标或期望行为、范围、非目标、验收口径", "已核实仓库事实及来源", "owner 已确认的术语与决策", "已有执行授权时同轮移交", "不再询问“是否继续”", "讨论过程本身不产生授权", "不创建 `.codestable/work/discussion-*`", "未收敛讨论不跨会话恢复", "三个已确认出口", "不附带 handoff 的不重复确认契约", "原始问答、未决讨论和候选分支不落盘", "canonical 术语归宿", "难逆转、缺少上下文会令人意外且源于真实取舍", "永久 Epic 文档", "`attention.md`", "`lessons/`", ): assert anchor in cs work = ROOT / ".codestable/work" assert not any(path.name.startswith("discussion-") for path in work.glob("*.md")) assert not (SKILLS / "cs-align").exists() def test_confirmed_cs_handoff_avoids_duplicate_intake_without_expanding_authority() -> None: handoff_owners = ("cs-feat", "cs-issue", "cs-epic") for skill_name in handoff_owners: _, owner = _read_skill(SKILLS / skill_name / "SKILL.md") for anchor in ( "同一会话由 `cs` 交入且带已确认 handoff", "packet 精确范围内已确认的事项不重复询问", "owner 已确认的术语与决策", "canonical 资产指针或资产候选", "仓库事实冲突", "会改变结果的新风险", "缺少会改变方向的事实", "超出已确认边界", "不扩大实现、commit、发布或写入授权", "不替代本 skill 的 review、验证与确认门槛", ): assert anchor in owner, skill_name _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") assert "handoff 只用于起草 proposed 永久 Epic 文档" in epic assert "不替代 fresh design review、批准 hash 或第一道 owner gate" in epic _, refactor = _read_skill(SKILLS / "cs-refactor/SKILL.md") assert "同一会话由 `cs` 交入且带已确认 handoff" not in refactor def test_task_skills_retrieve_legacy_knowledge_read_only() -> None: for skill_name in TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") kickoff = body.split("## 开工", 1)[1].split("\n## ", 1)[0] found = { path for path in LEGACY_KNOWLEDGE_DIRS if f"`{path}`" in kickoff } assert found == LEGACY_KNOWLEDGE_DIRS, skill_name assert "关键词" in kickoff, skill_name assert "检索" in kickoff or "grep" in kickoff, skill_name assert "只读" in kickoff, skill_name assert "命中要报告来源路径" in kickoff, skill_name assert "不得继续生成" in kickoff, skill_name assert "原地改写" in kickoff, skill_name assert "批量迁移" in kickoff, skill_name def test_task_skills_share_the_complete_lesson_read_repair_contract() -> None: signatures = {} for skill_name in TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") signatures[skill_name] = { anchor for anchor in LESSON_READ_REPAIR_CONTRACT if _contains_contract(body, anchor) } assert len({frozenset(signature) for signature in signatures.values()}) == 1 for skill_name, signature in signatures.items(): assert signature == LESSON_READ_REPAIR_CONTRACT, skill_name def test_task_skills_share_the_bounded_strong_signal_contract() -> None: signatures = {} for skill_name in TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") signatures[skill_name] = { anchor for anchor in CRYSTALLIZATION_SIGNAL_CONTRACT if _contains_contract(body, anchor) } assert len({frozenset(signature) for signature in signatures.values()}) == 1 for skill_name, signature in signatures.items(): assert signature == CRYSTALLIZATION_SIGNAL_CONTRACT, skill_name def test_crystallization_closing_is_quiet_and_prefers_mechanical_guards() -> None: for skill_name in TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") for anchor in ( "当前任务范围内能直接落成 red -> green 测试/checker", "优先机械化", "不另写重复 lesson", "会扩大范围时只给候选", ): assert _contains_contract(body, anchor), skill_name for skill_name in ORDINARY_TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") for anchor in ( "普通任务只在强信号成立时展示最高价值一条", "`晶化候选:{rule}`", "证据、范围和建议归宿", "无强信号完全不显示模板", "没有记忆写入授权时不落盘", ): assert _contains_contract(body, anchor), skill_name for skill_name in TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") for anchor in ( "用户已明确说“记住 / 更新 / 退役”", "同轮按 `cs-keep` 处理", "不重复确认", ): assert _contains_contract(body, anchor), skill_name _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") for anchor in ( "Epic 子项不展示、不询问", "每个子项至多把一条去重候选写入既有游标证据区", "最终毕业清单一次处理", "复用最终 owner gate", "不得询问“是否继续下一项”", "不得把普通子项完成当作终态返回", ): assert _contains_contract(epic, anchor) def test_task_skills_allow_only_two_narrow_lesson_maintenance_paths() -> None: signatures = {} for skill_name in TASK_SKILLS: _, body = _read_skill(SKILLS / skill_name / "SKILL.md") signatures[skill_name] = { anchor for anchor in NARROW_LESSON_MAINTENANCE_CONTRACT if _contains_contract(body, anchor) } assert _contains_contract(body, "创建、改写规则/scope、晋升、删除与跨项目反馈仍须") assert _contains_contract(body, "随当次代码、证据和游标进入同一语义原子 milestone") assert len({frozenset(signature) for signature in signatures.values()}) == 1 for skill_name, signature in signatures.items(): assert signature == NARROW_LESSON_MAINTENANCE_CONTRACT, skill_name def test_keep_never_writes_v1_compound() -> None: _, keep = _read_skill(SKILLS / "cs-keep/SKILL.md") assert "`.codestable/compound/` 是只读历史知识源" in keep assert "有增量时" in keep assert "`.codestable/lessons/`" in keep assert "能合并就更新旧文件" not in keep def test_keep_owns_the_three_state_lesson_lifecycle() -> None: frontmatter, keep = _read_skill(SKILLS / "cs-keep/SKILL.md") assert str(frontmatter["description"]).startswith( "管理有证据的项目事实、lesson 生命周期与 canonical 归宿" ) for anchor in ( "status: observed", "scope: 模块 / 命令 / 场景关键词", "适用 / 不适用:边界与停止应用信号", "证据:最多三个代表性路径、测试、diff 或任务指针", "候选归宿:test | checker | attention | project-doc | adr | codestable-eval", "`observed`", "尚未在独立后续任务验证", "`validated`", "非创建该 lesson 的任务和 agent invocation 中有效命中", "真实改善行为并验证成功", "明确排除一个具体且合理的错误路径", "必须记录 lesson 实际改变的计划或验证", "本次通过的验收证据", "`retired`", "不得应用且不再复活原结论", "旧 lesson 缺 `status` 按 `observed` 读取", "不批量迁移", "不得保存原始对话、逐次命中日志或无限 evidence history", "Epic 最终毕业 gate 批准候选", ): assert _contains_contract(keep, anchor) def test_keep_promotes_or_retires_without_creating_parallel_truth() -> None: _, keep = _read_skill(SKILLS / "cs-keep/SKILL.md") for anchor in ( "机械 guard 优先", "测试、checker、lint、类型或 deterministic helper", "高频必读事实进入 `attention.md`", "全文保持 ≤25 条", "难回退、缺少上下文会令人意外、源于真实取舍", "其他稳定方法进入项目既有文档", "目标不存在时请 owner 选择", "不发明目录", "先验证新 owner", "同一更新中删除重复 lesson", "约 50 条预算", "`codestable-eval` 只标记未来上游候选", "不导出、不上传、不改 skill", ): assert _contains_contract(keep, anchor) for anchor in ( "新建、改写规则/scope、晋升、删除或跨项目分享仍需用户显式授权", "不能把创建该 lesson 的同一任务自证为 validated", "retired 条目上的新结论必须另建 observed lesson", "不因命中次数或模型自评晋级", ): assert _contains_contract(keep, anchor) def test_onboard_keeps_the_base_skeleton_minimal() -> None: _, onboard = _read_skill(SKILLS / "cs-onboard/SKILL.md") skeleton = onboard.split("```text", 1)[1].split("```", 1)[0] assert "epics/" not in skeleton assert "requirements/" not in skeleton assert "首次 Epic" in onboard assert "`.codestable/epics/` 不属于基础骨架" in onboard assert "按需创建" in onboard for path in LEGACY_KNOWLEDGE_DIRS: assert f"`{path}`" in onboard def test_requirements_only_follow_an_explicit_canonical_owner() -> None: for skill_name in ("cs-feat", "cs-epic"): _, body = _read_skill(SKILLS / skill_name / "SKILL.md") assert "`.codestable/attention.md` 明确记录" in body, skill_name assert "canonical requirement" in body, skill_name assert "不存在时不新建 `.codestable/requirements/`" in body, skill_name def test_review_delegation_discovers_subagent_management_then_selects() -> None: for skill_name in ("cs-feat", "cs-issue", "cs-refactor", "cs-epic"): _, caller = _read_skill(SKILLS / skill_name / "SKILL.md") assert "当前主流程创建 reviewer 前" in caller, skill_name assert "当前会话可调用的 subagent 创建与管理能力" in caller, skill_name assert "项目上下文有显式创建方式/model 约束时先遵守" in caller, skill_name assert "达到审查质量基线后" in caller, skill_name assert "优先选择与实现者异构的 agent" in caller, skill_name assert "受管理的结构化委派能力" in caller, skill_name assert "宿主 subagent" in caller, skill_name assert "本机有界 agent CLI 回退" in caller, skill_name assert "不得只扫 PATH" in caller, skill_name assert "显式指定最强稳定 `model`" in caller, skill_name assert "禁止依赖默认模型" in caller, skill_name assert "最终创建方式、agent/model 与回退原因写入 task packet" in caller, skill_name assert "通道" not in caller, skill_name anchors = ( "当前主流程创建 reviewer 前", "当前会话可调用的 subagent 创建与管理能力", "项目上下文有显式创建方式/model 约束时先遵守", "达到审查质量基线后", "优先选择与实现者异构的 agent", "受管理的结构化委派能力", "宿主 subagent", "本机有界 agent CLI 回退", "最终创建方式、agent/model 与回退原因写入 task packet", ) positions = [caller.index(anchor) for anchor in anchors] assert positions == sorted(positions), skill_name def test_review_delegation_keeps_a_healthy_run_bound() -> None: for skill_name in ("cs-feat", "cs-issue", "cs-refactor", "cs-epic"): _, caller = _read_skill(SKILLS / skill_name / "SKILL.md") assert "reviewer 创建后绑定该运行" in caller, skill_name assert "状态健康时等待终态报告" in caller, skill_name assert "`Awaiting` 携带可查询的同一 run identity" in caller, skill_name assert "后来发现更优创建方式" in caller, skill_name assert "取消、重复创建或并行补发" in caller, skill_name assert "能力不满足或目标失效" in caller, skill_name def test_review_reuses_one_reviewer_lineage_for_finding_driven_repairs() -> None: lineage_contract = ( "每个独立审查阶段的首轮必须由当前主流程创建一个 fresh reviewer", "一个独立审查阶段由单一审查目的界定", "design review、change review、contract review 与 Epic final acceptance 是不同阶段", "只有为本阶段 findings 所作修复的复审", "先修复并重跑验证,再冻结新的完整审查目标", "同一 reviewer 的同一 session", "完整当前候选与本轮修复增量", "`resolved` / `unresolved` / `new findings`", "不得只核对旧 finding 或机械打勾", "累计最多 3 个有终态报告的轮次", "更换 reviewer 不重置计数", "只有原 run/session 失败或不可恢复、能力不满足、目标、范围、设计或核心路径发生重大变化", "reviewer 声明无法继续独立判断", "owner 要求第二意见", "独立于实现者,不要求对自身上一轮审查失忆", ) for skill_name in ("cs-feat", "cs-issue", "cs-refactor", "cs-epic"): _, caller = _read_skill(SKILLS / skill_name / "SKILL.md") for anchor in lineage_contract: assert _contains_contract(caller, anchor), f"{skill_name}: missing {anchor!r}" assert "处理后重跑验证、重新冻结审查目标并创建 fresh reviewer" not in caller assert "需要复审时重新创建 reviewer" not in caller assert "按需重新创建 reviewer" not in caller assert "需要时重新创建 reviewer" not in caller assert "按需重新发起" not in caller trigger_contract = { "cs-feat": "改动完成后默认进入 change review 审查阶段", "cs-issue": "修复完成后默认进入 change review 审查阶段", "cs-refactor": "完成后进入 change review 审查阶段", "cs-epic": "交确认前进入 design review 审查阶段", } for skill_name, anchor in trigger_contract.items(): _, caller = _read_skill(SKILLS / skill_name / "SKILL.md") assert _contains_contract(caller, anchor), skill_name _, review = _read_skill(SKILLS / "cs-review/SKILL.md") for anchor in ( "首轮 task packet 传入单一审查目的", "design review、change review、contract review 与 Epic final acceptance 使用不同阶段", "同一 reviewer 的同一 session", "完整当前候选与本轮修复增量", "`resolved` / `unresolved` / `new findings`", "独立于实现者,不要求对自身上一轮审查失忆", ): assert anchor in review assert "不修复、不自行发起复审" in review assert "可承接来源流程发给同一 session 的 follow-up" in review assert "目标变化则本轮失效,由调用方重新冻结后创建 fresh reviewer" not in review assert "在进入本 skill 前完成 reviewer 创建方式与 agent/model 选择并创建 fresh reviewer" not in review def test_epic_final_acceptance_starts_a_separate_fresh_reviewer_lineage() -> None: _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") assert "final acceptance 是独立审查阶段" in epic assert "另建 fresh reviewer" in epic assert "不得沿用子项、此前 design review 或 contract review 的 reviewer lineage" in epic assert "主流程按同一 lineage 处理该阶段 findings" in epic assert "契约变化形成新的 contract review 审查阶段" in epic assert "永久文档已批准后的执行中" in epic def test_shipped_skills_do_not_bind_review_backend_products() -> None: forbidden = ( "cs-agent-mcp", "cs_agent", "paseo", "claude", "codex", "anthropic", "openai", "gpt-", ) paths = [path for path in sorted(SKILLS.rglob("*")) if path.is_file()] assert paths # Host registration filenames are packaging surfaces, not runtime backend selection. allowed_product_named_paths = {Path("cs-review/agents/openai.yaml")} product_named_paths = { path.relative_to(SKILLS) for path in paths if any(name in path.relative_to(SKILLS).as_posix().lower() for name in forbidden) } assert product_named_paths == allowed_product_named_paths for path in paths: text = path.read_text(encoding="utf-8").lower() for product_name in forbidden: assert product_name not in text, f"{path}: bound to {product_name!r}" def test_review_snapshots_and_milestone_commits_are_not_conflated() -> None: _, review = _read_skill(SKILLS / "cs-review/SKILL.md") assert "冻结一个明确的审查目标" in review assert "审查目标标识" in review assert "reviewer 返回前不得移动该目标" in review assert "diff review 优先 staged diff" in review assert "design review 冻结对应文档版本" in review assert "audit 冻结 commit + 范围标识" in review for skill_name in ("cs-feat", "cs-issue", "cs-refactor", "cs-epic"): _, caller = _read_skill(SKILLS / skill_name / "SKILL.md") assert "diff review 优先 staged diff" in caller, skill_name assert "design review 冻结对应文档版本" in caller, skill_name assert "audit 冻结 commit + 范围标识" in caller, skill_name assert "目标标识写入 task packet" in caller, skill_name assert "重新冻结完整审查目标" in caller, skill_name assert "有 blocking 或未被用户明确接受的 important 时不提交当前候选" in caller, skill_name assert "正式里程碑 commit" in caller, skill_name assert "WIP/checkpoint commit" in caller, skill_name assert "不代表 review 通过" in caller, skill_name _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") assert "同一时间只允许一个 `current_item`" in epic assert "不把多个子项堆进同一 diff" in epic assert "milestone_commit" in epic def test_epic_continues_without_a_per_item_owner_gate() -> None: _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") for anchor in ( "串行约束,不是每个子项的人工 gate", "永久文档顺序中第一个依赖已满足的未完成子项", "在同一受托主流程中继续执行", "不得询问“是否继续下一项”", "不得把普通子项完成当作终态返回", "拆解确认本身不等于版本控制授权", "按已记录的逐项 checkpoint 策略暂停", "需要 owner 明确接受的 important findings", "子项 owning skill 自身的确认门槛", "进入 executing 前不得保留 `pending` 或非法组合", "恢复后沿用游标策略,不重新询问是否继续", "旧游标缺字段或组合非法时暂停一次补记/修正", "每个语义原子 commit 后按项目、宿主或 owner 已确定的 branch/remote 策略发布", "在集成验证与 final acceptance review 通过后、请求 owner 最终接受前发布一次", "agent 不执行远端发布", "发布失败时写入 `blocked_by` 并暂停", "Epic 内的已有 commit 授权只指 `milestone_commit: authorized`", ): assert anchor in epic for field in ("item_progression", "milestone_commit", "remote_publish"): assert field in epic assert "`milestone_commit: manual` 只能搭配 `item_progression: per-item`" in epic assert "`milestone_commit: manual` 只能搭配 `remote_publish: manual`" in epic assert "`remote_publish: each-milestone` 只能搭配 `milestone_commit: authorized`" in epic assert "`authorized + per-item` 是合法的显式逐项暂停策略" in epic assert "每次只推进一个已确认子项" not in epic def test_epic_separates_durable_record_from_execution_cursor() -> None: _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") assert "项目已有明确 Epic、RFC 或 initiative 归宿时沿用" in epic assert "`.codestable/epics/{slug}.md`" in epic assert "永久 Epic 文档" in epic assert "`.codestable/work/epic-{slug}.md`" in epic assert "执行游标" in epic permanent = epic.split("永久文档最小结构", 1)[1].split( "work 游标最小结构", 1 )[0] for field in ( "起点", "目标", "范围", "非目标", "验收标准", "子项契约", "关键决策", "最终交付索引", "整体验收", "遗留风险", ): assert field in permanent cursor_owner = epic.split("- **执行游标**", 1)[1].split( "永久文档最小结构", 1 )[0] for field in ( "永久文档指针", "approved_revision", "phase", "当前子项", "下一步", "blocked_by", "item_progression", "milestone_commit", "remote_publish", "临时决策", "证据", "commit 指针", ): assert field in cursor_owner assert "不得复制目标、验收、子项定义或最终结论" in epic assert "shasum -a 256 " in epic assert "确认前保持 `pending`" in epic assert "active 期间永久文档冻结" in epic assert "终态 `accepted` / `superseded` / `cancelled`" in epic assert "永久 Epic 文档不得删除" in epic def test_epic_internalizes_goal_semantics_without_v1_runtime() -> None: _, epic = _read_skill(SKILLS / "cs-epic/SKILL.md") assert "拆解方案必须经用户确认" in epic assert "目标、边界、验收、子项契约" in epic assert "重新 review 并征得同意" in epic assert "最新 owner 已批准的验收标准" in epic assert "audit/acceptance review" in epic assert "owner 最终接受" in epic assert "不恢复 `cs-goal` 入口" in epic assert "`state.yaml`" in epic assert "逐轮 iteration 报告" in epic assert not (SKILLS / "cs-goal").exists() assert not any( path.name in {"state.yaml", "goal-state.yaml", "goal-plan.md"} for path in (SKILLS / "cs-epic").rglob("*") ) assert not any( "iterations" in path.parts for path in (SKILLS / "cs-epic").rglob("*") ) def test_build_cs_skill_requires_semantic_and_host_safe_validation() -> None: build_root = LOCAL_SKILLS / "build-cs-skill" build = (build_root / "SKILL.md").read_text(encoding="utf-8") openai = yaml.safe_load( (build_root / "agents/openai.yaml").read_text(encoding="utf-8") ) spec = (build_root / "references/cs-skill-spec-standard.md").read_text( encoding="utf-8" ) gates = (build_root / "references/cs-skill-quality-gates.md").read_text( encoding="utf-8" ) for path in build_root.rglob("*.md"): assert len(path.read_text(encoding="utf-8").splitlines()) <= 300, path assert "$build-cs-skill" in openai["interface"]["default_prompt"] assert "thin harness" in build assert "thick context" in build assert "data SkillShape" in build assert "ThinOperator" in build assert "ContextualWorkflow" in build assert "ToolBackedWorkflow" in build assert "data RulePlacement" in build assert "placeRule :: Rule -> RulePlacement" in build assert "not (independentSkillNeeded kind source) = NoActiveSkill" in build assert "CompatibilityShim -> ShimSkill" in build assert "ContextPlan" in build assert "buildContextPlan" in build assert "discover subagent creation and management capabilities callable in the current session" in build assert "Reviewer creation methods prefer" in build assert "Do not substitute a `PATH` executable scan for capability discovery" in build assert "A healthy running delegation stays bound" in build assert "idle without a return payload and no recoverable run identity" in " ".join( build.split() ) assert "subagent 创建与管理能力契约" in spec assert "健康运行的 delegation" in gates assert "无可恢复 run identity" in gates assert "能力不匹配" in gates assert "target 失效" in gates assert "Haskell Contract Gate" in build assert "CompatibilityShim -> ShimRoute" in build assert "lifecycleDriven source -> LifecycleProtocol" in build assert "algorithmic source -> AlgorithmProtocol" in build placement = build[build.index("placeRule r"):build.index("buildContextPlan ::")] placement_guards = ( "mechanizable r", "nonMechanizableSafety r", "projectSpecific r", "stageSpecific r", "everyInvocationNeeds r", "otherwise", ) positions = [placement.index(guard) for guard in placement_guards] assert positions == sorted(positions) assert "data RulePlacement" in spec assert "NoActiveSkill" in spec assert "ShimSkill" in spec assert "ReferenceSkill" in spec assert "placeRule :: Rule ->" not in spec assert "Responsibility Contract" in spec assert "Context Contract" in spec assert "Collaboration Contract" in spec assert "contractDecision :: Input -> State -> Outcome" in spec assert "contractDecision _ _ = Blocked InvalidTransition" in spec assert "`HumanCheckpoint` only for an actual owner decision" in spec assert "`Awaiting` for already-started external work" in spec assert "Every `HumanCheckpoint` must have an explicit resume input" in spec assert "cross-skill handoff must retain its target and complete context" in spec assert "## Haskell Contract Semantics Gate" in gates assert "## Thin Harness Gate" in gates assert "## Context Plan Gate" in gates assert "## Evolution Compression Gate" in gates assert "## Regression Ladder" in gates assert "## Live Host Safety Gate" in gates assert "## Family Audit Coverage Gate" in gates assert "compare it with the reviewed/classified set by equality" in gates assert "target the canonical main entry without selecting its internal stage/lane" in gates assert "process start identity" in gates assert "CI or a quiet disposable host" in gates fixtures = (build_root / "references/cs-skill-fixture-patterns.md").read_text( encoding="utf-8" ) normalized_build = " ".join(build.split()) assert "data DelegationRole = Orchestrator | LeafExecutor" in build assert "A `LeafExecutor` must not dispatch another agent" in build assert "idle without a return payload" in normalized_build assert "The calling orchestrator owns integration" in normalized_build assert "outermost orchestrator" not in build assert "`LeafExecutor`" in spec assert "不得创建、委派、唤醒或跟进子 agent" in spec assert "调用该 `LeafExecutor` 的 `Orchestrator`" in spec assert "最外层 `Orchestrator`" not in spec assert "idle/silence without return payload" in gates assert "calling `Orchestrator`" in gates assert "最外层 agent" not in gates assert "review-leaf-does-not-delegate" in fixtures assert "spawn_subagent" in fixtures assert "wake_subagent" in fixtures assert "follow_up_child" in fixtures assert "invoke_cs_code_review" in fixtures assert "return_idle_without_report" in fixtures assert "review-prefers-managed-subagent-creation" in fixtures assert "review-cli-is-bounded-fallback" in fixtures assert "healthy-review-run-stays-bound" in fixtures assert fixtures.count("step: selectReviewCreationMethod") >= 2 assert fixtures.count("step: monitorReviewRun") >= 2 assert "path_only_discovery" in fixtures assert "selected_creation_method" in fixtures assert "selected_channel" not in fixtures assert "cancel_reviewer" in fixtures assert "spawn_duplicate_reviewer" in fixtures assert "当前 v2 活动 skill 不因历史 goal driver" not in fixtures assert "read `.codestable/attention.md` just to author" not in build