✨ feat(workflow): adopt Matt Pocock ticket workflow
Replace the Superpowers plan pipeline with grill-with-docs, specs, local tickets, and ticket-native execution. BREAKING CHANGE: Remove the legacy Plan CLI, prompt templates, and Superpowers skills.
This commit is contained in:
+267
-156
@@ -1,31 +1,69 @@
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
TEMPLATES = ROOT / "templates"
|
||||
LEGACY_FLOW_TERMS = (
|
||||
"using-superpowers",
|
||||
"brainstorming",
|
||||
"writing-plans",
|
||||
"executing-plans",
|
||||
"docs/superpowers/",
|
||||
"plan-status",
|
||||
"旧开发流程目录",
|
||||
"## 禁止旧流程",
|
||||
"旧开发队列",
|
||||
"单文件开发计划主链",
|
||||
"active-context.md",
|
||||
"progress.md",
|
||||
"decisions.md",
|
||||
)
|
||||
|
||||
|
||||
class TemplateContractsTests(unittest.TestCase):
|
||||
def test_project_templates_drop_legacy_language_placeholders(self):
|
||||
templates_readme = (ROOT / "templates" / "README.md").read_text(
|
||||
encoding="utf-8"
|
||||
def test_templates_define_only_the_matt_ticket_workflow(self):
|
||||
combined = "\n".join(
|
||||
path.read_text(encoding="utf-8")
|
||||
for path in sorted(TEMPLATES.rglob("*.md"))
|
||||
)
|
||||
|
||||
for required in (
|
||||
"setup-matt-pocock-skills",
|
||||
"grill-with-docs",
|
||||
"to-spec",
|
||||
"to-tickets",
|
||||
".scratch/",
|
||||
"main_loop.py claim",
|
||||
"main_loop.py integrate",
|
||||
):
|
||||
self.assertIn(required, combined)
|
||||
for legacy in LEGACY_FLOW_TERMS:
|
||||
self.assertNotIn(legacy, combined)
|
||||
|
||||
def test_project_templates_drop_legacy_language_placeholders(self):
|
||||
templates_readme = (TEMPLATES / "README.md").read_text(encoding="utf-8")
|
||||
self.assertNotIn("{{MAIN_LANGUAGE}}", templates_readme)
|
||||
self.assertNotIn("{{LANGUAGE_1}}", templates_readme)
|
||||
self.assertNotIn("docs/workflows/", templates_readme)
|
||||
self.assertNotIn("templates/workflows/", templates_readme)
|
||||
self.assertIn("docs/superpowers/", templates_readme)
|
||||
self.assertIn("docs/prompts/README.md", templates_readme)
|
||||
self.assertIn("完整主链只在 `AGENT_RULES.template.md` 定义", templates_readme)
|
||||
self.assertNotIn("docs/superpowers/", templates_readme)
|
||||
self.assertIn(".scratch/<feature>/spec.md", templates_readme)
|
||||
self.assertNotIn("docs/prompts/", templates_readme)
|
||||
|
||||
agents_template = (ROOT / "templates" / "AGENTS.template.md").read_text(
|
||||
agents_template = (TEMPLATES / "AGENTS.template.md").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertNotIn("{{MAIN_LANGUAGE}}", agents_template)
|
||||
self.assertIn("AGENT_RULES.md", agents_template)
|
||||
self.assertIn("docs/prompts/README.md", agents_template)
|
||||
self.assertIn("memory-bank/project-brief.md", agents_template)
|
||||
self.assertIn("memory-bank/tech-context.md", agents_template)
|
||||
self.assertIn("memory-bank/system-patterns.md", agents_template)
|
||||
self.assertIn(".scratch/queue.md", agents_template)
|
||||
self.assertNotIn("docs/prompts/", agents_template)
|
||||
|
||||
tech_context_template = (
|
||||
ROOT / "templates" / "memory-bank" / "tech-context.template.md"
|
||||
TEMPLATES / "memory-bank" / "tech-context.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertNotIn("{{MAIN_LANGUAGE}}", tech_context_template)
|
||||
self.assertNotIn("{{LANGUAGE_1}}", tech_context_template)
|
||||
@@ -33,166 +71,239 @@ class TemplateContractsTests(unittest.TestCase):
|
||||
self.assertIn("## 不可假设项", tech_context_template)
|
||||
|
||||
project_brief_template = (
|
||||
ROOT / "templates" / "memory-bank" / "project-brief.template.md"
|
||||
TEMPLATES / "memory-bank" / "project-brief.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("## 成功定义", project_brief_template)
|
||||
|
||||
def test_memory_bank_templates_capture_short_lived_context_contracts(self):
|
||||
progress_template = (
|
||||
ROOT / "templates" / "memory-bank" / "progress.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("短期状态快照,不是 changelog", progress_template)
|
||||
self.assertIn("`Recent Changes` 只保留最近 3-5 条", progress_template)
|
||||
self.assertIn("整理/替换旧摘要,不做无限追加", progress_template)
|
||||
|
||||
active_context_template = (
|
||||
ROOT / "templates" / "memory-bank" / "active-context.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("短期上下文快照,不是长期日志", active_context_template)
|
||||
self.assertIn("`Recent Changes` 只保留最近 3-5 条", active_context_template)
|
||||
self.assertIn(
|
||||
"`Touched Files` 只保留当前 Plan / 下一轮仍相关的文件",
|
||||
active_context_template,
|
||||
)
|
||||
self.assertIn("整理/替换旧上下文,不做无限追加", active_context_template)
|
||||
|
||||
def test_prompt_templates_point_to_current_superpowers_flow(self):
|
||||
update_memory_template = (
|
||||
ROOT / "templates" / "prompts" / "coding" / "update-memory.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertNotIn("workflow-state", update_memory_template)
|
||||
self.assertIn("plan-status", update_memory_template)
|
||||
self.assertIn("## Updated Files", update_memory_template)
|
||||
self.assertIn("## New Context", update_memory_template)
|
||||
self.assertIn("## Outstanding Risks", update_memory_template)
|
||||
self.assertIn("AGENT_RULES.local.md", update_memory_template)
|
||||
self.assertIn("短期状态快照", update_memory_template)
|
||||
self.assertIn(
|
||||
"不要把 `Recent Changes` 当作无限追加日志", update_memory_template
|
||||
)
|
||||
self.assertIn("短期上下文快照", update_memory_template)
|
||||
self.assertIn(
|
||||
"`Touched Files` 只保留当前 Plan / 下一轮仍相关的文件",
|
||||
update_memory_template,
|
||||
)
|
||||
|
||||
close_task_template = (
|
||||
ROOT / "templates" / "prompts" / "coding" / "close-task.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("main_loop.py finish", close_task_template)
|
||||
self.assertIn("如本轮来自 `main_loop.py claim`", close_task_template)
|
||||
self.assertNotIn("workflow-state", close_task_template)
|
||||
self.assertIn("未归档不得声明 Plan 完成", close_task_template)
|
||||
self.assertIn("按项目归档机制只归档", close_task_template)
|
||||
self.assertIn("状态已写回,交付未完成", close_task_template)
|
||||
self.assertIn("## Completed", close_task_template)
|
||||
self.assertIn("## Not Completed", close_task_template)
|
||||
self.assertIn("## Verification", close_task_template)
|
||||
self.assertIn("## Risks", close_task_template)
|
||||
self.assertIn("## Next Steps", close_task_template)
|
||||
|
||||
verify_change_template = (
|
||||
ROOT / "templates" / "prompts" / "coding" / "verify-change.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("如本轮来自 `main_loop.py claim`", verify_change_template)
|
||||
self.assertNotIn("workflow-state", verify_change_template)
|
||||
self.assertIn("plan-status", verify_change_template)
|
||||
self.assertIn("验证通过不等于 Plan 完成", verify_change_template)
|
||||
self.assertIn(
|
||||
"当前 Plan 相关差异未归档/提交时,不得声明 Plan 完成",
|
||||
verify_change_template,
|
||||
)
|
||||
self.assertIn("## Validated", verify_change_template)
|
||||
self.assertIn("## Evidence", verify_change_template)
|
||||
self.assertIn("## Not Validated", verify_change_template)
|
||||
self.assertIn("## Risks", verify_change_template)
|
||||
|
||||
clarify_template = (
|
||||
ROOT / "templates" / "prompts" / "coding" / "clarify.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertNotIn("Vibe-coding", clarify_template)
|
||||
self.assertIn("## Current Understanding", clarify_template)
|
||||
self.assertIn("## Open Question", clarify_template)
|
||||
self.assertIn("## Recommended Default", clarify_template)
|
||||
|
||||
code_review_template = (
|
||||
ROOT / "templates" / "prompts" / "coding" / "code-review.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("## Findings", code_review_template)
|
||||
self.assertIn("## Open Questions", code_review_template)
|
||||
self.assertIn("## Residual Risk", code_review_template)
|
||||
|
||||
prompts_readme = (ROOT / "templates" / "prompts" / "README.md").read_text(
|
||||
def test_agents_template_nests_agents_block_inside_framework_block(self):
|
||||
agents_template = (TEMPLATES / "AGENTS.template.md").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertIn("AGENT_RULES.md", prompts_readme)
|
||||
self.assertIn("docs/superpowers/", prompts_readme)
|
||||
self.assertIn("不是流程权威", prompts_readme)
|
||||
self.assertNotIn("playbook.py -record-spec", prompts_readme)
|
||||
self.assertNotIn("playbook.py -record-plan", prompts_readme)
|
||||
self.assertNotIn("using-superpowers", prompts_readme)
|
||||
self.assertNotIn("main_loop.py claim", prompts_readme)
|
||||
self.assertNotIn("docs/workflows/", prompts_readme)
|
||||
self.assertIn("设计与计划产物", prompts_readme)
|
||||
lines = [line.strip() for line in agents_template.splitlines()]
|
||||
for marker in (
|
||||
"<!-- playbook:framework:start -->",
|
||||
"<!-- playbook:framework:end -->",
|
||||
"<!-- playbook:agents:start -->",
|
||||
"<!-- playbook:agents:end -->",
|
||||
):
|
||||
self.assertIn(marker, lines, msg=f"missing marker: {marker}")
|
||||
|
||||
agent_behavior_template = (
|
||||
ROOT / "templates" / "prompts" / "system" / "agent-behavior.template.md"
|
||||
).read_text(encoding="utf-8")
|
||||
self.assertIn("AGENT_RULES.md", agent_behavior_template)
|
||||
self.assertIn("AGENT_RULES.local.md", agent_behavior_template)
|
||||
self.assertIn("docs/superpowers/specs/", agent_behavior_template)
|
||||
self.assertIn("docs/superpowers/plans/", agent_behavior_template)
|
||||
self.assertNotIn("using-superpowers", agent_behavior_template)
|
||||
self.assertNotIn("main_loop.py claim", agent_behavior_template)
|
||||
self.assertNotIn("playbook.py -record-spec", agent_behavior_template)
|
||||
self.assertNotIn("playbook.py -record-plan", agent_behavior_template)
|
||||
self.assertIn(
|
||||
"项目上下文与执行状态写入 `memory-bank/`", agent_behavior_template
|
||||
framework_start = lines.index("<!-- playbook:framework:start -->")
|
||||
framework_end = lines.index("<!-- playbook:framework:end -->")
|
||||
agents_start = lines.index("<!-- playbook:agents:start -->")
|
||||
agents_end = lines.index("<!-- playbook:agents:end -->")
|
||||
nesting_reason = (
|
||||
"sync_agents_template replaces the whole framework block; the agents "
|
||||
"sub-block must stay inside it so preserve_agents_subblock can carry "
|
||||
"the deployed language rules across the replacement"
|
||||
)
|
||||
self.assertLess(framework_start, agents_start, msg=nesting_reason)
|
||||
self.assertLess(agents_start, agents_end, msg=nesting_reason)
|
||||
self.assertLess(agents_end, framework_end, msg=nesting_reason)
|
||||
|
||||
def test_templates_readme_separates_ownership_from_deployment_config(self):
|
||||
templates_readme = (TEMPLATES / "README.md").read_text(encoding="utf-8")
|
||||
classification = templates_readme.split("## 模板分类", 1)[1].split(
|
||||
"## 模板说明", 1
|
||||
)[0]
|
||||
|
||||
self.assertIn("### 1. 入口导航", classification)
|
||||
self.assertIn("### 2. 初始化后由项目维护", classification)
|
||||
self.assertIn("### 3. 参考模板", classification)
|
||||
self.assertIn("项目新增的 `memory-bank/*`", classification)
|
||||
self.assertNotIn("[sync_", classification)
|
||||
self.assertNotIn("force", classification)
|
||||
self.assertNotIn("no_backup", classification)
|
||||
|
||||
def test_memory_bank_contains_only_stable_project_knowledge(self):
|
||||
memory_templates = {
|
||||
path.name for path in (TEMPLATES / "memory-bank").glob("*.template.md")
|
||||
}
|
||||
self.assertEqual(
|
||||
memory_templates,
|
||||
{
|
||||
"project-brief.template.md",
|
||||
"tech-context.template.md",
|
||||
"system-patterns.template.md",
|
||||
},
|
||||
)
|
||||
|
||||
def test_agent_rules_template_defines_plan_and_archival_contracts(self):
|
||||
rules_template = (ROOT / "templates" / "AGENT_RULES.template.md").read_text(
|
||||
encoding="utf-8"
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
stable_paths = (
|
||||
"memory-bank/project-brief.md",
|
||||
"memory-bank/tech-context.md",
|
||||
"memory-bank/system-patterns.md",
|
||||
)
|
||||
self.assertIn("框架流程约束基线", rules_template)
|
||||
self.assertIn("{{PLAYBOOK_ROOT}}", rules_template)
|
||||
self.assertIn("Playbook 模板/供应商目录", rules_template)
|
||||
self.assertIn("唯一设计与计划产物中心", rules_template)
|
||||
self.assertIn("memory-bank/progress.md", rules_template)
|
||||
self.assertIn("优先恢复 `in-progress`", rules_template)
|
||||
self.assertIn("选择第一个可执行 Plan", rules_template)
|
||||
for path in stable_paths:
|
||||
self.assertIn(path, rules)
|
||||
self.assertIn("进入 `grill-with-docs` 或本地 ticket 执行协议前", rules)
|
||||
normalized_rules = " ".join(rules.split())
|
||||
for required in (
|
||||
"只记录下一 session 仍需要的稳定知识",
|
||||
"写入 `tech-context.md` 的命令和环境事实必须已经验证",
|
||||
"关键取舍及理由写入 `docs/adr/`",
|
||||
"不把聊天流水、未验证猜测或短期进度写入 `CONTEXT.md`",
|
||||
"没有长期价值的信息时不更新这些文件",
|
||||
):
|
||||
self.assertIn(required, normalized_rules)
|
||||
|
||||
def test_prompt_templates_are_not_part_of_the_workflow(self):
|
||||
self.assertFalse(TEMPLATES.joinpath("prompts").exists())
|
||||
|
||||
def test_agent_rules_template_defines_ticket_and_integration_contracts(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
self.assertIn("Matt Pocock 工程流程与 ticket 执行约束", rules)
|
||||
self.assertIn("{{PLAYBOOK_ROOT}}", rules)
|
||||
self.assertIn("Playbook 模板/供应商目录", rules)
|
||||
self.assertIn("setup-matt-pocock-skills", rules)
|
||||
self.assertIn("grill-with-docs", rules)
|
||||
self.assertIn("to-spec", rules)
|
||||
self.assertIn("to-tickets", rules)
|
||||
self.assertIn("一次可以先生成多个 feature", rules)
|
||||
self.assertIn("`.scratch/queue.md`", rules)
|
||||
self.assertIn("多个 frontier tickets 可在 worktree 模式并发执行", rules)
|
||||
self.assertIn("不创建额外 worktree", rules)
|
||||
self.assertIn("共享同一文件系统", rules)
|
||||
self.assertIn("跨机器或独立 clone", " ".join(rules.split()))
|
||||
self.assertIn("只有 `reclaim` 可以接管", rules)
|
||||
self.assertIn("先提交实现", rules)
|
||||
self.assertIn("Standards/Spec 双轴审查", rules)
|
||||
self.assertIn("三个独立门禁,不能互相替代", rules)
|
||||
for legacy in LEGACY_FLOW_TERMS:
|
||||
self.assertNotIn(legacy, rules)
|
||||
|
||||
def test_agent_rules_routes_each_matt_on_ramp_to_its_real_destination(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
on_ramps = rules.split("## On-ramps 与 detours", 1)[1].split(
|
||||
"## Phase boundaries", 1
|
||||
)[0]
|
||||
|
||||
self.assertIn("`wayfinder`", on_ramps)
|
||||
self.assertIn("`to-spec -> to-tickets`", on_ramps)
|
||||
self.assertIn("`research`", on_ramps)
|
||||
self.assertIn("先进入 `grill-with-docs`", on_ramps)
|
||||
self.assertIn("`handoff` 到独立目录运行", on_ramps)
|
||||
self.assertIn("`prototype`", on_ramps)
|
||||
self.assertIn("再 `handoff` 结论回原设计会话", on_ramps)
|
||||
|
||||
bug_route = rules.split("### 已明确预期行为的 bug", 1)[1].split(
|
||||
"## 正式工程主链", 1
|
||||
)[0]
|
||||
self.assertIn("`diagnosing-bugs` 完整执行 Phase 1-6", bug_route)
|
||||
self.assertIn("不再进入 `implement` 或重复 `tdd`", bug_route)
|
||||
self.assertNotIn("to-spec", bug_route)
|
||||
|
||||
def test_agent_rules_defines_a_local_ticket_execution_adapter(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
main_flow = rules.split("## 正式工程主链", 1)[1].split(
|
||||
"## On-ramps 与 detours", 1
|
||||
)[0]
|
||||
adapter = rules.split("## 本地 Ticket 执行协议", 1)[1].split(
|
||||
"## 文档职责", 1
|
||||
)[0]
|
||||
normalized_adapter = " ".join(adapter.split())
|
||||
|
||||
self.assertIn("本地 ticket 执行协议", main_flow)
|
||||
self.assertNotIn("implement + tdd", main_flow)
|
||||
self.assertIn("不直接调用上游 `implement`", normalized_adapter)
|
||||
ordered_steps = (
|
||||
"读取已领取 ticket 的 spec",
|
||||
"按 `tdd`",
|
||||
"提交全部实现",
|
||||
"运行 `code-review`",
|
||||
"调用 `main_loop.py finish`",
|
||||
)
|
||||
positions = [adapter.index(step) for step in ordered_steps]
|
||||
self.assertEqual(positions, sorted(positions))
|
||||
|
||||
def test_agent_rules_defines_mechanical_review_inputs_and_pass_mapping(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
review_contract = rules.split("### Review 适配契约", 1)[1].split(
|
||||
"### Ticket 完成或状态转换", 1
|
||||
)[0]
|
||||
normalized_review_contract = " ".join(review_contract.split())
|
||||
|
||||
for required in (
|
||||
"fixed point",
|
||||
"`.scratch/<feature>/spec.md`",
|
||||
"`.scratch/<feature>/issues/<ticket>-*.md`",
|
||||
"零个未解决的硬 finding",
|
||||
"不得据此填写 `standards=pass` 或 `spec=pass`",
|
||||
):
|
||||
self.assertIn(required, normalized_review_contract)
|
||||
|
||||
def test_agent_rules_reads_claimed_context_after_claim_and_defines_lease_policy(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
startup = rules.split("## 会话启动", 1)[1].split("## 任务入口", 1)[0]
|
||||
claim = rules.split("### 领取", 1)[1].split("### 心跳和接管", 1)[0]
|
||||
lease = rules.split("### 心跳和接管", 1)[1].split(
|
||||
"### Review 适配契约", 1
|
||||
)[0]
|
||||
|
||||
self.assertNotIn("当前 `.scratch/<feature>/spec.md`", startup)
|
||||
self.assertNotIn("当前 `.scratch/<feature>/issues/<ticket>.md`", startup)
|
||||
self.assertIn("领取成功后立即读取", claim)
|
||||
self.assertIn("全局唯一", claim)
|
||||
self.assertIn("每 10 分钟", lease)
|
||||
self.assertIn("固定为 30 分钟", lease)
|
||||
|
||||
def test_agent_rules_preserves_to_spec_seam_and_phase_boundary_contracts(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
main_flow = rules.split("## 正式工程主链", 1)[1].split(
|
||||
"## On-ramps 与 detours", 1
|
||||
)[0]
|
||||
self.assertIn("不重新进行已经完成的需求采访", main_flow)
|
||||
self.assertIn("完成 seam confirmation", main_flow)
|
||||
self.assertIn("`tdd` 不得在未经确认的 seam 上开始", main_flow)
|
||||
|
||||
phase_boundaries = rules.split("## Phase boundaries", 1)[1].split(
|
||||
"## 文档职责", 1
|
||||
)[0]
|
||||
ordered_options = (
|
||||
"继续当前 session",
|
||||
"使用 `clear`",
|
||||
"使用 `handoff`",
|
||||
"交给 subagent",
|
||||
"使用 `compact`",
|
||||
)
|
||||
positions = [phase_boundaries.index(option) for option in ordered_options]
|
||||
self.assertEqual(positions, sorted(positions))
|
||||
self.assertIn("上下文过长不等于必须 `handoff`", phase_boundaries)
|
||||
|
||||
def test_agent_rules_binds_state_and_evidence_to_claimed_git_context(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
execution = rules.split("### 领取", 1)[1]
|
||||
after_claim_output = execution.split("stdout 返回", 1)[1]
|
||||
|
||||
self.assertIn("绝对 `STATE_ROOT`", execution)
|
||||
self.assertIn("`CONTROL_ROOT` 是共享 Git control checkout", execution)
|
||||
self.assertIn("`WORKSPACE` 是当前 ticket 的代码工作区", execution)
|
||||
self.assertNotIn("--state-root .scratch", after_claim_output)
|
||||
self.assertIn("--review-base <同一feature HEAD>", execution)
|
||||
self.assertIn(
|
||||
"`main_loop.py finish -status done` 只负责写回机器状态",
|
||||
rules_template,
|
||||
'commit=<HEAD>; base=<review-base>; standards=pass; spec=pass',
|
||||
execution,
|
||||
)
|
||||
self.assertIn("把该 `FEATURE_HEAD` 合入 ticket branch", execution)
|
||||
self.assertIn(
|
||||
"`--feature-head`、`--review-base` 和 review evidence 的 `base`",
|
||||
execution,
|
||||
)
|
||||
self.assertIn(
|
||||
"Plan `done` 后必须完成当前 Plan 变更的归档/提交", rules_template
|
||||
"base=<最新main HEAD>; standards=pass; spec=pass",
|
||||
execution,
|
||||
)
|
||||
self.assertIn(
|
||||
"spec/plan 产出阶段不单独提交或归档", rules_template
|
||||
)
|
||||
self.assertIn(
|
||||
"如外部 skill", rules_template
|
||||
)
|
||||
self.assertIn("Plan 范围是归档/提交边界", rules_template)
|
||||
self.assertIn(
|
||||
"不自动提交或归档",
|
||||
rules_template,
|
||||
)
|
||||
self.assertIn("当前 Plan 文件", rules_template)
|
||||
self.assertIn("`memory-bank/progress.md`", rules_template)
|
||||
self.assertIn("必要 memory 更新", rules_template)
|
||||
self.assertIn("允许其未归档共存", rules_template)
|
||||
self.assertIn("当前 Plan 无遗留差异", rules_template)
|
||||
self.assertIn("状态已写回,交付未完成", rules_template)
|
||||
self.assertNotIn("如项目使用 Git", rules_template)
|
||||
self.assertNotIn("Git 提交", rules_template)
|
||||
self.assertNotIn("git status", rules_template)
|
||||
self.assertNotIn("git commit", rules_template)
|
||||
self.assertNotIn("git worktree", rules_template)
|
||||
self.assertNotIn(".gitattributes", rules_template)
|
||||
self.assertIn("`progress.md` 上半部分是短期状态快照", rules_template)
|
||||
self.assertIn("`active-context.md` 是短期上下文快照", rules_template)
|
||||
|
||||
def test_agent_rules_limits_main_loop_to_shared_local_markdown_state(self):
|
||||
rules = (TEMPLATES / "AGENT_RULES.template.md").read_text(encoding="utf-8")
|
||||
isolation = rules.split("## 执行隔离", 1)[1].split(
|
||||
"## 主循环命令", 1
|
||||
)[0]
|
||||
|
||||
self.assertIn("`main_loop.py` 只支持 local Markdown tracker", isolation)
|
||||
self.assertIn("没有远程 tracker adapter", isolation)
|
||||
self.assertIn("跨机器或独立\nclone 的并发不受支持", isolation)
|
||||
self.assertNotIn("必须改用具备远程", isolation)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user