Files
.pouch/tests/test_ack_skill.py
T
laily f3cd56b78e feat: rename skills/skiff to pouch and move ACK state under .pouch
Use ~/.pouch, the pouch CLI, and .pouch.yaml as the SSOT container.
Keep the inner skills/ packages, and store ACK project state in
.pouch/ack instead of docs/ack.
2026-08-25 15:20:02 +08:00

164 lines
6.6 KiB
Python

from __future__ import annotations
import json
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
class AckSkillContentTests(unittest.TestCase):
def test_ack_skill_owns_resources_without_modifying_agent_instructions(self) -> None:
content = (REPO_ROOT / "skills" / "ack" / "SKILL.md").read_text(encoding="utf-8")
for expected in (
"pouch init ack --project <project-root>",
".pouch/ack/project.md",
".pouch/ack/tasks.yaml",
".pouch/ack/knowledge.yaml",
".pouch/ack/delivery.yaml",
"tasks: []",
"validate_tasks.py",
"validate_knowledge.py",
"validate_delivery.py",
"select_tasks.py",
"select_knowledge.py",
"references/kickoff.md",
"不要修改项目的 `AGENTS.md`",
"当前会话担任 Coordinator",
"intents.testEnvironment",
"运行测试环境",
"运行版本发布",
):
self.assertIn(expected, content)
def test_ack_skill_is_explicit_only(self) -> None:
metadata = (REPO_ROOT / "skills" / "ack" / "agents" / "openai.yaml").read_text(
encoding="utf-8"
)
self.assertIn('display_name: "ACK"', metadata)
self.assertIn("allow_implicit_invocation: false", metadata)
def test_model_routing_is_fingerprint_bound_and_does_not_trust_receipt_reuse(self) -> None:
content = (
REPO_ROOT / "skills" / "ack" / "references" / "model-routing.md"
).read_text(encoding="utf-8")
self.assertIn("--expected-launch-fingerprint", content)
self.assertIn("allowlist-v1", content)
self.assertIn("禁止根据持久化 receipt 自动复用", content)
self.assertIn("launcher 身份证明", content)
def test_worker_reuse_requires_idle_state_and_verified_history_reset(self) -> None:
skill = (REPO_ROOT / "skills" / "ack" / "SKILL.md").read_text(
encoding="utf-8"
)
adapter = (
REPO_ROOT / "skills" / "ack" / "references" / "orca-adapter.md"
).read_text(encoding="utf-8")
self.assertIn("空闲", skill)
self.assertIn("清理历史消息", skill)
self.assertIn("无法确认清理成功时创建 fresh worker", skill)
self.assertIn("角色、profile、worktree", adapter)
self.assertIn("不得复用仍在工作", adapter)
self.assertIn("或运行状态不明的 worker", adapter)
def test_coordinator_reclaims_only_verified_task_terminals_at_run_end(self) -> None:
skill = (REPO_ROOT / "skills" / "ack" / "SKILL.md").read_text(
encoding="utf-8"
)
kickoff = (
REPO_ROOT / "skills" / "ack" / "references" / "kickoff.md"
).read_text(encoding="utf-8")
self.assertIn("整轮任务完成", skill)
self.assertIn("回收所有只属于 `verified` 任务的 worker", skill)
self.assertIn("终端,并核对关闭回执", skill)
self.assertIn("不设置 TTL", skill)
self.assertIn("blocked", kickoff)
self.assertIn("failed_retest", kickoff)
self.assertIn("leftover", kickoff)
def test_environment_failures_are_reported_without_consuming_retest_rounds(self) -> None:
skill = (REPO_ROOT / "skills" / "ack" / "SKILL.md").read_text(
encoding="utf-8"
)
optimization = (
REPO_ROOT / "skills" / "ack" / "references" / "optimization-method.md"
).read_text(encoding="utf-8")
schema = json.loads(
(REPO_ROOT / "skills" / "ack" / "templates" / "tasks.schema.json")
.read_text(encoding="utf-8")
)
self.assertIn("环境失败不占复验轮次", skill)
self.assertIn("userAction", optimization)
self.assertIn("environmentIncidents", schema["definitions"]["task"]["properties"]["dispatch"]["properties"])
def test_validation_ready_hands_off_a_deployed_test_environment(self) -> None:
skill = (REPO_ROOT / "skills" / "ack" / "SKILL.md").read_text(
encoding="utf-8"
)
delivery = (
REPO_ROOT / "skills" / "ack" / "references" / "delivery.md"
).read_text(encoding="utf-8")
self.assertIn("不能停在", skill)
self.assertIn("`verified` 却声称整轮 ACK 已结束", skill)
self.assertIn("validation_ready", delivery)
self.assertIn("访问地址和用户下一步", delivery)
def test_ack_knowledge_resources_and_version_are_present(self) -> None:
ack_dir = REPO_ROOT / "skills" / "ack"
for relative_path in (
"templates/knowledge.template.yaml",
"templates/knowledge.schema.json",
"examples/knowledge.example.yaml",
"scripts/validate_knowledge.py",
"scripts/select_tasks.py",
"scripts/select_knowledge.py",
"scripts/run_verification.py",
"scripts/worker_profiles.py",
"scripts/launch_worker.py",
"templates/delivery.template.yaml",
"templates/delivery.schema.json",
"examples/delivery.example.yaml",
"references/delivery.md",
):
self.assertTrue((ack_dir / relative_path).is_file(), relative_path)
version = (ack_dir / "VERSION").read_text(encoding="utf-8").strip()
self.assertEqual(version, "0.19.0")
self.assertIn(
f'ackVersion: "{version}"',
(ack_dir / "examples" / "tasks.example.yaml").read_text(encoding="utf-8"),
)
self.assertIn(
f"ack v{version}",
(ack_dir / "examples" / "project.example.md").read_text(encoding="utf-8"),
)
self.assertIn(
'ackVersion: "<接入时的 ack skill 版本>"',
(ack_dir / "templates" / "tasks.template.yaml").read_text(
encoding="utf-8"
),
)
self.assertTrue((ack_dir / "references" / "feishu-bug-intake.md").is_file())
def test_task_template_derives_project_files_without_persisted_root_paths(self) -> None:
template = (
REPO_ROOT / "skills" / "ack" / "templates" / "tasks.template.yaml"
).read_text(encoding="utf-8")
# v0.19 起 allowedWorktrees 白名单废弃:模板只保留废弃说明注释,不再生成该字段
self.assertNotIn("allowedWorktrees:", template)
self.assertIn("allowedWorktrees", template)
self.assertIn('knowledgeFile: ".pouch/ack/knowledge.yaml"', template)
if __name__ == "__main__":
unittest.main()