feat(workflow): add superpowers planning and execution state tracking

This commit is contained in:
csh
2026-05-18 16:28:06 +08:00
parent c1702a667a
commit 234b335663
11 changed files with 1287 additions and 310 deletions
+59
View File
@@ -55,6 +55,65 @@ class PlaybookCliTests(unittest.TestCase):
self.assertEqual(result.returncode, 0)
self.assertIn("Usage:", result.stdout + result.stderr)
def test_record_spec_updates_progress_workflow_state(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text("# 当前进展\n", encoding="utf-8")
result = run_cli(
"-record-spec",
"docs/superpowers/specs/2026-05-18-demo-design.md",
"-progress",
str(progress),
)
self.assertEqual(result.returncode, 0, msg=result.stdout + result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("phase: planning", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
def test_record_plan_updates_progress_workflow_state(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# 当前进展",
"",
"## Workflow State",
"",
"<!-- workflow-state:start -->",
"phase: planning",
"spec: docs/superpowers/specs/2026-05-18-demo-design.md",
"<!-- workflow-state:end -->",
]
)
+ "\n",
encoding="utf-8",
)
result = run_cli(
"-record-plan",
"docs/superpowers/plans/2026-05-18-demo.md",
"-progress",
str(progress),
)
self.assertEqual(result.returncode, 0, msg=result.stdout + result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("phase: planning", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
self.assertIn("plan: docs/superpowers/plans/2026-05-18-demo.md", text)
self.assertIn("executor: executing-plans", text)
self.assertIn(
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
text,
)
def test_missing_config_is_error(self):
result = run_cli()
self.assertNotEqual(result.returncode, 0)
@@ -79,7 +79,7 @@ echo ""
echo "🔍 验证 memory-bank 模板"
MEMORY_BANK_DIR="$TEMPLATES_DIR/memory-bank"
for name in project-brief tech-stack architecture progress decisions; do
for name in project-brief tech-context system-patterns active-context progress decisions; do
validate_file_exists "$MEMORY_BANK_DIR/$name.template.md" "memory-bank/$name.template.md"
done
@@ -90,9 +90,10 @@ PROMPTS_DIR="$TEMPLATES_DIR/prompts"
validate_file_exists "$PROMPTS_DIR/README.md" "prompts/README.md"
validate_file_exists "$PROMPTS_DIR/system/agent-behavior.template.md" "prompts/system/agent-behavior.template.md"
validate_file_exists "$PROMPTS_DIR/coding/clarify.template.md" "prompts/coding/clarify.template.md"
validate_file_exists "$PROMPTS_DIR/coding/review.template.md" "prompts/coding/review.template.md"
validate_file_exists "$PROMPTS_DIR/coding/verify-change.template.md" "prompts/coding/verify-change.template.md"
validate_file_exists "$PROMPTS_DIR/coding/close-task.template.md" "prompts/coding/close-task.template.md"
validate_file_exists "$PROMPTS_DIR/coding/update-memory.template.md" "prompts/coding/update-memory.template.md"
validate_file_exists "$PROMPTS_DIR/coding/code-review.template.md" "prompts/coding/code-review.template.md"
validate_file_exists "$PROMPTS_DIR/meta/prompt-generator.template.md" "prompts/meta/prompt-generator.template.md"
echo ""
+417 -11
View File
@@ -1,13 +1,22 @@
import importlib.util
import os
import platform
import subprocess
import sys
import tempfile
import threading
import time
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "main_loop.py"
_SPEC = importlib.util.spec_from_file_location("playbook_main_loop", SCRIPT)
assert _SPEC and _SPEC.loader
MAIN_LOOP = importlib.util.module_from_spec(_SPEC)
_SPEC.loader.exec_module(MAIN_LOOP)
def run_cli(*args, cwd=None):
return subprocess.run(
@@ -29,7 +38,7 @@ class MainLoopCliTests(unittest.TestCase):
def test_claim_seeds_progress_and_marks_first_plan_in_progress(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "plans"
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-old.md").write_text("old", encoding="utf-8")
(plans_dir / "2026-01-02-new.md").write_text("new", encoding="utf-8")
@@ -37,26 +46,78 @@ class MainLoopCliTests(unittest.TestCase):
result = run_cli(
"claim",
"-plans",
"docs/plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
self.assertEqual(result.stdout.strip(), "PLAN=docs/plans/2026-01-01-old.md")
self.assertEqual(
result.stdout.strip(),
"PLAN=docs/superpowers/plans/2026-01-01-old.md",
)
progress = root / "memory-bank" / "progress.md"
text = progress.read_text(encoding="utf-8")
self.assertIn("<!-- workflow-state:start -->", text)
self.assertIn("<!-- workflow-state:end -->", text)
self.assertIn("phase: executing", text)
self.assertIn("plan: docs/superpowers/plans/2026-01-01-old.md", text)
self.assertIn("<!-- plan-status:start -->", text)
self.assertIn("<!-- plan-status:end -->", text)
self.assertIn("`2026-01-01-old.md` in-progress", text)
self.assertIn("`2026-01-02-new.md` pending", text)
def test_claim_preserves_human_progress_sections_when_plan_block_missing(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-demo.md").write_text("demo", encoding="utf-8")
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# 当前进展",
"",
"## Current Focus",
"",
"- keep-this-focus",
"",
"## Recent Changes",
"",
"- keep-this-change",
"",
]
)
+ "\n",
encoding="utf-8",
)
result = run_cli(
"claim",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("- keep-this-focus", text)
self.assertIn("- keep-this-change", text)
self.assertIn("<!-- workflow-state:start -->", text)
self.assertIn("<!-- plan-status:start -->", text)
self.assertIn("`2026-01-01-demo.md` in-progress", text)
def test_claim_returns_existing_in_progress_before_pending(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "plans"
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-a.md").write_text("a", encoding="utf-8")
(plans_dir / "2026-01-02-b.md").write_text("b", encoding="utf-8")
@@ -81,19 +142,65 @@ class MainLoopCliTests(unittest.TestCase):
result = run_cli(
"claim",
"-plans",
"docs/plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
self.assertEqual(result.stdout.strip(), "PLAN=docs/plans/2026-01-01-a.md")
self.assertEqual(
result.stdout.strip(),
"PLAN=docs/superpowers/plans/2026-01-01-a.md",
)
def test_claim_skips_stale_progress_entries_for_deleted_plans(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-02-live.md").write_text("live", encoding="utf-8")
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# Plan 状态",
"",
"<!-- workflow-state:start -->",
"phase: planning",
"<!-- workflow-state:end -->",
"",
"<!-- plan-status:start -->",
"- [ ] `2026-01-01-deleted.md` pending",
"- [ ] `2026-01-02-live.md` pending",
"<!-- plan-status:end -->",
"",
]
),
encoding="utf-8",
)
result = run_cli(
"claim",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
self.assertEqual(
result.stdout.strip(),
"PLAN=docs/superpowers/plans/2026-01-02-live.md",
)
def test_claim_resumes_env_blocked_plan_and_preserves_note(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "plans"
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-05-env.md").write_text("env", encoding="utf-8")
@@ -118,7 +225,7 @@ class MainLoopCliTests(unittest.TestCase):
result = run_cli(
"claim",
"-plans",
"docs/plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
@@ -129,7 +236,7 @@ class MainLoopCliTests(unittest.TestCase):
result.stdout.strip(),
"\n".join(
[
"PLAN=docs/plans/2026-01-05-env.md",
"PLAN=docs/superpowers/plans/2026-01-05-env.md",
f"NOTE={note}",
]
),
@@ -160,7 +267,7 @@ class MainLoopCliTests(unittest.TestCase):
result = run_cli(
"finish",
"-plan",
"docs/plans/2026-01-03-demo.md",
"docs/superpowers/plans/2026-01-03-demo.md",
"-status",
"done",
"-progress",
@@ -171,7 +278,306 @@ class MainLoopCliTests(unittest.TestCase):
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("- [x] `2026-01-03-demo.md` done", text)
self.assertEqual(text.count("2026-01-03-demo.md"), 1)
self.assertEqual(
text.count("- [x] `2026-01-03-demo.md` done"),
1,
)
def test_finish_updates_workflow_phase_and_preserves_metadata(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# 当前进展",
"",
"## Workflow State",
"",
"<!-- workflow-state:start -->",
"phase: executing",
"spec: docs/superpowers/specs/2026-05-18-demo-design.md",
"plan: docs/superpowers/plans/2026-05-18-demo.md",
"executor: executing-plans",
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
"<!-- workflow-state:end -->",
"",
"## Plan Status",
"",
"<!-- plan-status:start -->",
"- [ ] `2026-05-18-demo.md` in-progress",
"<!-- plan-status:end -->",
"",
]
)
+ "\n",
encoding="utf-8",
)
result = run_cli(
"finish",
"-plan",
"docs/superpowers/plans/2026-05-18-demo.md",
"-status",
"done",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("phase: done", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
self.assertIn("executor: executing-plans", text)
self.assertIn(
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
text,
)
def test_record_updates_workflow_state_block(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# 当前进展",
"",
"## Plan Status",
"",
"<!-- plan-status:start -->",
"<!-- plan-status:end -->",
"",
]
)
+ "\n",
encoding="utf-8",
)
result = run_cli(
"record",
"-progress",
"memory-bank/progress.md",
"-phase",
"planning",
"-spec",
"docs/superpowers/specs/2026-05-18-demo-design.md",
"-plan",
"docs/superpowers/plans/2026-05-18-demo.md",
"-executor",
"executing-plans",
"-constraints",
"karpathy-guidelines,.agents,AGENT_RULES",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("<!-- workflow-state:start -->", text)
self.assertIn("phase: planning", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
self.assertIn("plan: docs/superpowers/plans/2026-05-18-demo.md", text)
self.assertIn("executor: executing-plans", text)
self.assertIn(
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
text,
)
def test_record_claim_finish_workflow_chain(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-05-18-demo.md").write_text("demo", encoding="utf-8")
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
result = run_cli(
"record",
"-progress",
"memory-bank/progress.md",
"-phase",
"planning",
"-spec",
"docs/superpowers/specs/2026-05-18-demo-design.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
result = run_cli(
"record",
"-progress",
"memory-bank/progress.md",
"-phase",
"planning",
"-spec",
"docs/superpowers/specs/2026-05-18-demo-design.md",
"-plan",
"docs/superpowers/plans/2026-05-18-demo.md",
"-executor",
"executing-plans",
"-constraints",
"karpathy-guidelines,.agents,AGENT_RULES",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
result = run_cli(
"claim",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
result = run_cli(
"finish",
"-plan",
"docs/superpowers/plans/2026-05-18-demo.md",
"-status",
"done",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("phase: done", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
self.assertIn("plan: docs/superpowers/plans/2026-05-18-demo.md", text)
self.assertIn("executor: executing-plans", text)
self.assertIn(
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
text,
)
self.assertIn("- [x] `2026-05-18-demo.md` done", text)
def test_concurrent_record_preserves_spec_and_plan_metadata(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
original_load = MAIN_LOOP.load_progress_lines
first_load = {"seen": False}
gate = threading.Lock()
def delayed_load(progress_path):
lines = original_load(progress_path)
with gate:
if not first_load["seen"]:
first_load["seen"] = True
threading.Event().wait(0.2)
return lines
MAIN_LOOP.load_progress_lines = delayed_load
try:
threads = [
threading.Thread(
target=MAIN_LOOP.record_workflow_state,
args=(
progress,
"planning",
"docs/superpowers/specs/2026-05-18-demo-design.md",
None,
None,
None,
),
),
threading.Thread(
target=MAIN_LOOP.record_workflow_state,
args=(
progress,
"planning",
None,
"docs/superpowers/plans/2026-05-18-demo.md",
"executing-plans",
"karpathy-guidelines,.agents,AGENT_RULES",
),
),
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
finally:
MAIN_LOOP.load_progress_lines = original_load
text = progress.read_text(encoding="utf-8")
self.assertIn("phase: planning", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
self.assertIn("plan: docs/superpowers/plans/2026-05-18-demo.md", text)
self.assertIn("executor: executing-plans", text)
self.assertIn(
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
text,
)
def test_cross_process_record_lock_preserves_spec_and_plan_metadata(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
slow_env = dict(os.environ)
slow_env["PLAYBOOK_MAIN_LOOP_HOLD_LOCK_MS"] = "300"
proc = subprocess.Popen(
[
sys.executable,
str(SCRIPT),
"record",
"-progress",
"memory-bank/progress.md",
"-phase",
"planning",
"-spec",
"docs/superpowers/specs/2026-05-18-demo-design.md",
],
cwd=root,
env=slow_env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
time.sleep(0.05)
result = run_cli(
"record",
"-progress",
"memory-bank/progress.md",
"-phase",
"planning",
"-plan",
"docs/superpowers/plans/2026-05-18-demo.md",
"-executor",
"executing-plans",
"-constraints",
"karpathy-guidelines,.agents,AGENT_RULES",
cwd=root,
)
stdout, stderr = proc.communicate(timeout=5)
self.assertEqual(proc.returncode, 0, msg=stderr or stdout)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn("phase: planning", text)
self.assertIn("spec: docs/superpowers/specs/2026-05-18-demo-design.md", text)
self.assertIn("plan: docs/superpowers/plans/2026-05-18-demo.md", text)
self.assertIn("executor: executing-plans", text)
self.assertIn(
"constraints: karpathy-guidelines,.agents,AGENT_RULES",
text,
)
if __name__ == "__main__":
+99 -13
View File
@@ -42,12 +42,42 @@ class SyncTemplatesPlaceholdersTests(unittest.TestCase):
)
self.assertNotIn("{{MAIN_LANGUAGE}}", agents_template)
tech_stack_template = (
ROOT / "templates" / "memory-bank" / "tech-stack.template.md"
tech_context_template = (
ROOT / "templates" / "memory-bank" / "tech-context.template.md"
).read_text(encoding="utf-8")
self.assertNotIn("{{MAIN_LANGUAGE}}", tech_stack_template)
self.assertNotIn("{{LANGUAGE_1}}", tech_stack_template)
self.assertNotIn("**主要语言**", tech_stack_template)
self.assertNotIn("{{MAIN_LANGUAGE}}", tech_context_template)
self.assertNotIn("{{LANGUAGE_1}}", tech_context_template)
self.assertNotIn("**主要语言**", tech_context_template)
update_memory_template = (
ROOT / "templates" / "prompts" / "coding" / "update-memory.template.md"
).read_text(encoding="utf-8")
self.assertIn("workflow-state", update_memory_template)
self.assertIn("plan-status", 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("workflow-state.phase", close_task_template)
verify_change_template = (
ROOT / "templates" / "prompts" / "coding" / "verify-change.template.md"
).read_text(encoding="utf-8")
self.assertIn("workflow-state.phase", verify_change_template)
self.assertIn("plan-status", verify_change_template)
prompts_readme = (
ROOT / "templates" / "prompts" / "README.md"
).read_text(encoding="utf-8")
self.assertIn("playbook.py -record-spec", prompts_readme)
self.assertIn("playbook.py -record-plan", prompts_readme)
agent_behavior_template = (
ROOT / "templates" / "prompts" / "system" / "agent-behavior.template.md"
).read_text(encoding="utf-8")
self.assertIn("playbook.py -record-spec", agent_behavior_template)
self.assertIn("playbook.py -record-plan", agent_behavior_template)
def test_sync_templates_replaces_playbook_scripts_without_main_language_support(self):
with tempfile.TemporaryDirectory() as tmp_dir:
@@ -74,18 +104,28 @@ langs = [\"cpp\", \"tsl\"]
self.assertIn(".agents/cpp/index.md", text)
self.assertNotIn("{{MAIN_LANGUAGE}}", text)
tech_stack = Path(tmp_dir) / "memory-bank" / "tech-stack.md"
tech_stack_text = tech_stack.read_text(encoding="utf-8")
self.assertNotIn("{{LANGUAGE_1}}", tech_stack_text)
self.assertNotIn("{{MAIN_LANGUAGE}}", tech_stack_text)
self.assertNotIn("**主要语言**", tech_stack_text)
tech_context = Path(tmp_dir) / "memory-bank" / "tech-context.md"
tech_context_text = tech_context.read_text(encoding="utf-8")
self.assertNotIn("{{LANGUAGE_1}}", tech_context_text)
self.assertNotIn("{{MAIN_LANGUAGE}}", tech_context_text)
self.assertNotIn("**主要语言**", tech_context_text)
rules_md = Path(tmp_dir) / "AGENT_RULES.md"
rules_text = rules_md.read_text(encoding="utf-8")
self.assertIn("docs/standards/playbook/scripts/main_loop.py claim", rules_text)
self.assertIn(
"docs/standards/playbook/scripts/main_loop.py claim",
rules_text,
)
self.assertIn("docs/superpowers/plans", rules_text)
self.assertNotIn("plan_progress.py", rules_text)
self.assertIn("不得直接使用 `$executing-plans`", rules_text)
self.assertIn("不得直接使用 `$subagent-driven-development`", rules_text)
self.assertIn("记录 `phase=planning` 与 `spec=<path>`", rules_text)
self.assertIn(
"记录 `plan=<path>`、`executor=executing-plans`、",
rules_text,
)
self.assertIn("未领取 Plan 前,不得直接进入 `$executing-plans`", rules_text)
self.assertIn("默认执行使用 `$executing-plans`", rules_text)
self.assertIn("不是默认执行器", rules_text)
self.assertNotIn("{{PLAYBOOK_SCRIPTS}}", rules_text)
self.assertFalse(rules_text.endswith("\n\n"))
@@ -134,6 +174,52 @@ langs = ["typescript"]
self.assertIn("`docs/standards/playbook/docs/typescript/", text)
self.assertNotIn("`docs/typescript/", text)
def test_sync_memory_bank_includes_active_context_and_human_readable_progress(self):
with tempfile.TemporaryDirectory() as tmp_dir:
config_body = f"""
[playbook]
project_root = "{tmp_dir}"
deploy_root = "{DEFAULT_DEPLOY_ROOT}"
[sync_rules]
[sync_memory_bank]
project_name = "MyProject"
"""
config_path = Path(tmp_dir) / "playbook.toml"
config_path.write_text(config_body, encoding="utf-8")
result = run_cli("-config", str(config_path))
self.assertEqual(result.returncode, 0, msg=result.stderr)
active_context = Path(tmp_dir) / "memory-bank" / "active-context.md"
self.assertTrue(active_context.is_file())
progress = Path(tmp_dir) / "memory-bank" / "progress.md"
progress_text = progress.read_text(encoding="utf-8")
self.assertIn("## Current Focus", progress_text)
self.assertIn("## 状态块示例", progress_text)
self.assertIn("phase: planning", progress_text)
self.assertIn("executor: executing-plans", progress_text)
self.assertIn("<!-- workflow-state:start -->", progress_text)
self.assertIn("<!-- workflow-state:end -->", progress_text)
self.assertIn("## Plan Status", progress_text)
self.assertIn("<!-- plan-status:start -->", progress_text)
self.assertIn("<!-- plan-status:end -->", progress_text)
system_patterns = Path(tmp_dir) / "memory-bank" / "system-patterns.md"
system_patterns_text = system_patterns.read_text(encoding="utf-8")
self.assertIn("# 系统模式与约束", system_patterns_text)
self.assertIn("## 核心不变量", system_patterns_text)
agents_md = Path(tmp_dir) / "AGENTS.md"
agents_text = agents_md.read_text(encoding="utf-8")
self.assertIn("memory-bank/active-context.md", agents_text)
rules_md = Path(tmp_dir) / "AGENT_RULES.md"
rules_text = rules_md.read_text(encoding="utf-8")
self.assertIn("memory-bank/active-context.md", rules_text)
if __name__ == "__main__":
unittest.main()