|
1 | | -"""Anti-drift: thinking lenses, engineering/agent/context/domain prompts, and workflow skills cross-link.""" |
| 1 | +"""Anti-drift: thinking lenses, engineering/agent/context/domain/repo prompts, and workflow skills cross-link.""" |
2 | 2 |
|
3 | 3 | from pathlib import Path |
4 | 4 |
|
|
10 | 10 | AGENT_DIR = LIBRARY_ROOT / "04-agent" |
11 | 11 | CONTEXT_DIR = LIBRARY_ROOT / "03-context" |
12 | 12 | DOMAIN_DIR = LIBRARY_ROOT / "05-domain" |
| 13 | +REPO_DIR = LIBRARY_ROOT / "06-repo" |
13 | 14 | SKILLS_DIR = LIBRARY_ROOT / "skills" |
14 | 15 |
|
15 | 16 | ENGINEERING_THINKING_LINKS: dict[str, tuple[str, ...]] = { |
|
183 | 184 |
|
184 | 185 | DOMAIN_PROMPTS = tuple(sorted(DOMAIN_DIR.glob("*.md"))) |
185 | 186 |
|
| 187 | +REPO_THINKING_LINKS: dict[str, tuple[str, ...]] = { |
| 188 | + "AGENTS.md": ( |
| 189 | + "01-thinking/why-what-how-done.md", |
| 190 | + "01-thinking/critical-thinking-check.md", |
| 191 | + "01-thinking/socratic-reviewer.md", |
| 192 | + ), |
| 193 | + "cursor-rules.md": ( |
| 194 | + "01-thinking/critical-thinking-check.md", |
| 195 | + "01-thinking/falsifiability.md", |
| 196 | + ), |
| 197 | + "codex-opencode.md": ( |
| 198 | + "01-thinking/why-what-how-done.md", |
| 199 | + "01-thinking/falsifiability.md", |
| 200 | + ), |
| 201 | + "PROJECT_KNOWLEDGE.template.md": ( |
| 202 | + "01-thinking/socratic-reviewer.md", |
| 203 | + "01-thinking/falsifiability.md", |
| 204 | + ), |
| 205 | +} |
| 206 | + |
| 207 | +REPO_SKILL_LINKS: dict[str, tuple[str, ...]] = { |
| 208 | + "AGENTS.md": ( |
| 209 | + "reflective-dispatch", |
| 210 | + "reflective-implement", |
| 211 | + "reflective-spec-plan", |
| 212 | + ), |
| 213 | + "cursor-rules.md": ("reflective-implement", "reflective-review"), |
| 214 | + "codex-opencode.md": ("reflective-implement", "reflective-dispatch"), |
| 215 | + "PROJECT_KNOWLEDGE.template.md": ("reflective-handoff-retro", "reflective-brief"), |
| 216 | +} |
| 217 | + |
| 218 | +REPO_PROMPTS = tuple(sorted(REPO_DIR.glob("*.md"))) |
| 219 | + |
186 | 220 | CONTEXT_PROMPTS = tuple(sorted(CONTEXT_DIR.glob("*.md"))) |
187 | 221 |
|
188 | 222 |
|
@@ -300,3 +334,26 @@ def test_thinking_lens_files_exist_for_domain_links(): |
300 | 334 | for ref in linked: |
301 | 335 | assert (LIBRARY_ROOT / ref).is_file(), f"missing thinking lens file {ref}" |
302 | 336 |
|
| 337 | +@pytest.mark.parametrize("prompt_name,thinking_refs", REPO_THINKING_LINKS.items()) |
| 338 | +def test_repo_prompt_links_thinking_lens(prompt_name: str, thinking_refs: tuple[str, ...]): |
| 339 | + path = REPO_DIR / prompt_name |
| 340 | + preamble = _preamble(path) |
| 341 | + for ref in thinking_refs: |
| 342 | + assert ref in preamble, f"{prompt_name} preamble should reference {ref}" |
| 343 | + |
| 344 | + |
| 345 | +def test_all_repo_prompts_have_thinking_cross_link(): |
| 346 | + assert set(REPO_THINKING_LINKS) == {p.name for p in REPO_PROMPTS} |
| 347 | + |
| 348 | + |
| 349 | +@pytest.mark.parametrize("prompt_name,skill_refs", REPO_SKILL_LINKS.items()) |
| 350 | +def test_repo_prompt_maps_workflow_skill(prompt_name: str, skill_refs: tuple[str, ...]): |
| 351 | + preamble = _preamble(REPO_DIR / prompt_name) |
| 352 | + for skill in skill_refs: |
| 353 | + assert skill in preamble, f"{prompt_name} preamble should reference {skill}" |
| 354 | + |
| 355 | + |
| 356 | +def test_thinking_lens_files_exist_for_repo_links(): |
| 357 | + linked = {ref for refs in REPO_THINKING_LINKS.values() for ref in refs} |
| 358 | + for ref in linked: |
| 359 | + assert (LIBRARY_ROOT / ref).is_file(), f"missing thinking lens file {ref}" |
0 commit comments