Files
manual_slop/docs/superpowers/plans/2026-07-05-directive-preset-system.md

2169 lines
96 KiB
Markdown

# Directive Preset System Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace the single flat `current_baseline.md` preset with a layered preset system: a curated `baseline.md` (57 directives + 4 non-directive context files), 12 engagement presets that inherit baseline, 15 new op-specific directives, a deduplicated `.warm.md`, and an extended aggregate tool that resolves `## Inherits` and surfaces `## Non-directive context`.
**Architecture:** The aggregate tool (`scripts/aggregate_directives.py`) is extended to parse two new preset sections (`## Inherits` and `## Non-directive context`), recursively flatten the parent's directives + non-directive files into the child's list (dedup on path), read each directive's `v1.md` and each non-directive file's full body, and return one concatenated blob. Preset files are markdown (no YAML/TOML). Engagement presets list only their targeted additions; baseline provides the foundation.
**Tech Stack:** Python 3.11+, 1-space indent, type hints, `@dataclass(frozen=True, slots=True)` for any new data types, `re` for preset parsing, `pathlib.Path` for file I/O, `pytest` for tests.
**Spec:** `docs/superpowers/specs/2026-07-05-directive-preset-system-design.md`
---
## File Structure
**Files to create:**
- `conductor/directives/presets/baseline.md` — curated 57 BASELINE directives + 4 non-directive context files
- `conductor/directives/presets/audit.md` — inherits baseline, adds 3 audit directives
- `conductor/directives/presets/fix_tests.md` — inherits baseline, adds 23 testing + 2 regression-fix
- `conductor/directives/presets/implement_feature.md` — inherits baseline, adds testing + APP subsets
- `conductor/directives/presets/refactor.md` — inherits baseline, adds testing + 2 refactor
- `conductor/directives/presets/new_script_tool.md` — inherits baseline, adds 2 script directives
- `conductor/directives/presets/meta_tooling.md` — inherits baseline, adds 1 APP:mcp + 1 meta-tooling
- `conductor/directives/presets/directives_curation.md` — inherits baseline, adds META + documentation + 1 curation
- `conductor/directives/presets/documentation.md` — inherits baseline, adds 3 documentation + 1 doc-update
- `conductor/directives/presets/media_analysis.md` — inherits baseline, adds 2 media-analysis
- `conductor/directives/presets/ideation.md` — inherits baseline, adds 7 planning + 1 ideation
- `conductor/directives/presets/tier2_autonomous.md` — inherits baseline, adds tier2-sandbox + testing + delegation
- 15 new directive directories under `conductor/directives/<name>/` each with `v1.md` + `meta.md`
- `tests/test_aggregate_directives_presets.py` — tests for Inherits + Non-directive context
**Files to modify:**
- `scripts/aggregate_directives.py` — add `## Inherits` resolution + `## Non-directive context` section parsing + output
- `conductor/directives/tags.toml` — add 15 new directive entries
- `conductor/directives/presets/current_baseline.md` — mark deprecated in Notes
- `conductor/tier2/agents/tier2-autonomous.warm.md` — strip inline sections covered by directives, update preset path
- `scripts/tier2/setup_tier2_clone_directives.ps1` — change `-PresetPath` default to `tier2_autonomous.md`
---
## Phase 1: Extend aggregate_directives.py (Inherits + Non-directive context)
Focus: Add `## Inherits` recursive resolution and `## Non-directive context` file reading to the aggregate tool. TDD — write failing tests first, implement to pass.
### Task 1.1: Write failing tests for Inherits resolution
**Files:**
- Create: `tests/test_aggregate_directives_presets.py`
- Create: `tests/artifacts/_pytest_tmp/preset_child_test/` (temp preset fixtures)
- [ ] **Step 1: Write the failing test for Inherits**
Create `tests/test_aggregate_directives_presets.py`:
```python
"""Tests for aggregate_directives preset inheritance and non-directive context."""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
# Add scripts/ to sys.path so we can import aggregate_directives
SCRIPTS_DIR = Path(__file__).resolve().parent.parent / "scripts"
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
from aggregate_directives import parse_preset_sections, resolve_inheritance, aggregate_directives
class TestParsePresetSections:
"""Test the new section-based parser that extracts Inherits, Directives, Non-directive context."""
def test_parse_returns_three_sections(self, tmp_path: Path):
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- foo: conductor/directives/foo/v1.md
- bar: conductor/directives/bar/v1.md
## Non-directive context
Read each file below as full documents.
- project rules: AGENTS.md
## Notes
test
""", encoding="utf-8")
sections = parse_preset_sections(preset)
assert sections.inherits == "baseline"
assert len(sections.directives) == 2
assert sections.directives[0] == ("foo", Path("conductor/directives/foo/v1.md"))
assert sections.directives[1] == ("bar", Path("conductor/directives/bar/v1.md"))
assert len(sections.non_directive_context) == 1
assert sections.non_directive_context[0] == ("project rules", Path("AGENTS.md"))
def test_parse_preset_without_inherits(self, tmp_path: Path):
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- foo: conductor/directives/foo/v1.md
## Non-directive context
- doc: AGENTS.md
""", encoding="utf-8")
sections = parse_preset_sections(preset)
assert sections.inherits == ""
assert len(sections.directives) == 1
assert len(sections.non_directive_context) == 1
def test_parse_preset_without_non_directive_context(self, tmp_path: Path):
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- foo: conductor/directives/foo/v1.md
""", encoding="utf-8")
sections = parse_preset_sections(preset)
assert sections.inherits == ""
assert len(sections.directives) == 1
assert sections.non_directive_context == []
class TestResolveInheritance:
"""Test that resolve_inheritance merges parent directives into child, dedup on name."""
def test_child_directives_plus_parent_directives(self, tmp_path: Path):
parent = tmp_path / "parent.md"
parent.write_text("""# Preset: parent
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
- beta: conductor/directives/beta/v1.md
""", encoding="utf-8")
child = tmp_path / "child.md"
child.write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- beta: conductor/directives/beta/v1.md
- gamma: conductor/directives/gamma/v1.md
""", encoding="utf-8")
merged = resolve_inheritance(child, root=tmp_path)
names = [n for n, _ in merged.directives]
assert names == ["beta", "gamma", "alpha"]
assert len(merged.non_directive_context) == 0
def test_child_wins_on_directive_conflict(self, tmp_path: Path):
parent = tmp_path / "parent.md"
parent.write_text("""# Preset: parent
## Directives to warm
- shared: conductor/directives/shared_parent/v1.md
""", encoding="utf-8")
child = tmp_path / "child.md"
child.write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- shared: conductor/directives/shared_child/v1.md
""", encoding="utf-8")
merged = resolve_inheritance(child, root=tmp_path)
paths = [p for _, p in merged.directives]
assert Path("conductor/directives/shared_child/v1.md") in paths
assert Path("conductor/directives/shared_parent/v1.md") not in paths
def test_non_directive_context_merges_dedup_on_path(self, tmp_path: Path):
parent = tmp_path / "parent.md"
parent.write_text("""# Preset: parent
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
## Non-directive context
- rules: AGENTS.md
- product: conductor/product.md
""", encoding="utf-8")
child = tmp_path / "child.md"
child.write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- beta: conductor/directives/beta/v1.md
## Non-directive context
- rules: AGENTS.md
- tech: conductor/tech-stack.md
""", encoding="utf-8")
merged = resolve_inheritance(child, root=tmp_path)
paths = [p for _, p in merged.non_directive_context]
assert Path("AGENTS.md") in paths
assert Path("conductor/product.md") in paths
assert Path("conductor/tech-stack.md") in paths
assert paths.count(Path("AGENTS.md")) == 1
def test_three_level_inheritance(self, tmp_path: Path):
grandparent = tmp_path / "grandparent.md"
grandparent.write_text("""# Preset: grandparent
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
""", encoding="utf-8")
parent = tmp_path / "parent.md"
parent.write_text("""# Preset: parent
## Inherits
grandparent
## Directives to warm
- beta: conductor/directives/beta/v1.md
""", encoding="utf-8")
child = tmp_path / "child.md"
child.write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- gamma: conductor/directives/gamma/v1.md
""", encoding="utf-8")
merged = resolve_inheritance(child, root=tmp_path)
names = [n for n, _ in merged.directives]
assert "alpha" in names
assert "beta" in names
assert "gamma" in names
def test_inheritance_loop_detected(self, tmp_path: Path):
a = tmp_path / "a.md"
a.write_text("""# Preset: a
## Inherits
b
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
""", encoding="utf-8")
b = tmp_path / "b.md"
b.write_text("""# Preset: b
## Inherits
a
## Directives to warm
- beta: conductor/directives/beta/v1.md
""", encoding="utf-8")
with pytest.raises(ValueError, match="inheritance loop"):
resolve_inheritance(a, root=tmp_path)
```
- [ ] **Step 2: Run tests to verify they fail**
Run: `uv run pytest tests/test_aggregate_directives_presets.py -v`
Expected: FAIL with `ImportError: cannot import name 'parse_preset_sections' from 'aggregate_directives'`
---
### Task 1.2: Implement parse_preset_sections + resolve_inheritance + aggregate with non-directive context
**Files:**
- Modify: `scripts/aggregate_directives.py` (the full file — rewrite with new functions)
- [ ] **Step 1: Implement the new parsing + inheritance + non-directive context**
Replace the full content of `scripts/aggregate_directives.py` with:
```python
#!/usr/bin/env python3
"""Aggregate directive bodies from a preset markdown file.
Supports three preset sections:
## Inherits — name of parent preset (resolved recursively, 3-level max)
## Directives to warm — list of <name>: <path to v1.md>
## Non-directive context — list of <description>: <path to full markdown file>
Child directives + non-directive files override parent on name/path conflict.
"""
from __future__ import annotations
import argparse
import re
import sys
from dataclasses import dataclass, field
from pathlib import Path
if hasattr(sys.stdout, "reconfigure"):
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
if hasattr(sys.stderr, "reconfigure"):
try:
sys.stderr.reconfigure(encoding="utf-8")
except Exception:
pass
REPO_ROOT = Path(__file__).resolve().parent.parent
ENTRY_RE = re.compile(r"^\s*-\s+(?P<name>[^\s:]+)\s*:\s*(?P<path>\S+)\s*$")
EXPECTED_PATTERN_TEMPLATE = r"^conductor/directives/{name}/v\d+\.md$"
MAX_INHERIT_DEPTH = 3
@dataclass(frozen=True, slots=True)
class PresetSections:
inherits: str
directives: list[tuple[str, Path]]
non_directive_context: list[tuple[str, Path]]
def parse_preset_sections(preset_path: Path) -> PresetSections:
"""Parse a preset markdown file into Inherits, Directives, Non-directive context."""
if not preset_path.exists():
raise FileNotFoundError("preset file not found: " + str(preset_path))
text = preset_path.read_text(encoding="utf-8")
inherits: str = ""
directives: list[tuple[str, Path]] = []
non_directive: list[tuple[str, Path]] = []
current_section: str = ""
for line in text.splitlines():
if line.startswith("## Inherits"):
current_section = "inherits"
continue
if line.startswith("## Directives to warm"):
current_section = "directives"
continue
if line.startswith("## Non-directive context"):
current_section = "non_directive"
continue
if line.startswith("## ") or line.startswith("# "):
current_section = ""
continue
if current_section == "inherits" and line.strip():
inherits = line.strip()
elif current_section in ("directives", "non_directive"):
m = ENTRY_RE.match(line)
if m:
name = m.group("name")
rel_path = Path(m.group("path"))
if current_section == "directives":
directives.append((name, rel_path))
else:
non_directive.append((name, rel_path))
return PresetSections(inherits=inherits, directives=directives, non_directive_context=non_directive)
def resolve_inheritance(
preset_path: Path,
root: Path | None = None,
_visited: frozenset[str] | None = None,
_depth: int = 0,
) -> PresetSections:
"""Resolve a preset, recursively flattening parent directives + non-directive files."""
if root is None:
root = REPO_ROOT
p = preset_path if preset_path.is_absolute() else (root / preset_path).resolve()
if _visited is None:
_visited = frozenset()
key = str(p)
if key in _visited:
raise ValueError("inheritance loop detected at " + key)
if _depth > MAX_INHERIT_DEPTH:
raise ValueError("inheritance depth exceeds " + str(MAX_INHERIT_DEPTH) + " at " + key)
sections = parse_preset_sections(p)
if not sections.inherits:
return sections
parent_name = sections.inherits
parent_path = (root / "conductor" / "directives" / "presets" / (parent_name + ".md")).resolve()
if not parent_path.exists():
raise FileNotFoundError("parent preset not found: " + str(parent_path))
parent = resolve_inheritance(parent_path, root=root, _visited=_visited | {key}, _depth=_depth + 1)
merged_directives: list[tuple[str, Path]] = []
child_names: set[str] = {n for n, _ in sections.directives}
for name, path in parent.directives:
if name not in child_names:
merged_directives.append((name, path))
for name, path in sections.directives:
merged_directives.append((name, path))
merged_non_directive: list[tuple[str, Path]] = []
child_paths: set[Path] = {p for _, p in sections.non_directive_context}
for desc, path in parent.non_directive_context:
if path not in child_paths:
merged_non_directive.append((desc, path))
for desc, path in sections.non_directive_context:
merged_non_directive.append((desc, path))
return PresetSections(inherits="", directives=merged_directives, non_directive_context=merged_non_directive)
def aggregate_directives(
preset_path: str | Path,
*,
max_chars: int = 0,
project_root: str | Path | None = None,
) -> str:
"""
Read preset at preset_path, resolve inheritance, read each directive's v1.md
and each non-directive-context file, concatenate into a single text blob.
Preset format:
## Inherits <parent_name>
## Directives to warm (list of <name>: <relative path to v1.md>)
## Non-directive context (list of <description>: <relative path>)
Directives appear first (same ===== banner format as before), then a
NON-DIRECTIVE CONTEXT section appends each non-directive file's full body.
"""
root = Path(project_root).resolve() if project_root is not None else REPO_ROOT
p = Path(preset_path)
if not p.is_absolute():
p = (root / p).resolve()
if not p.exists():
raise FileNotFoundError("preset file not found: " + str(p))
sections = resolve_inheritance(p, root=root)
if not sections.directives and not sections.non_directive_context:
raise ValueError("no directive or non-directive entries found in " + str(p))
missing: list[tuple[str, Path]] = []
directive_bodies: list[tuple[str, str]] = []
for name, rel in sections.directives:
abs_path = (root / rel).resolve()
if not abs_path.exists():
missing.append((name, abs_path))
continue
try:
rel_str = abs_path.relative_to(root).as_posix()
except ValueError:
rel_str = str(abs_path)
pattern = EXPECTED_PATTERN_TEMPLATE.format(name=re.escape(name))
if not re.match(pattern, rel_str):
print("WARNING: " + name + ": path " + str(abs_path) + " does not match pattern conductor/directives/" + name + "/vN.md; including anyway", file=sys.stderr)
try:
body = abs_path.read_text(encoding="utf-8")
except UnicodeDecodeError as e:
raise ValueError(name + ": UTF-8 decode error in " + str(abs_path) + ": " + str(e)) from e
directive_bodies.append((name, body))
if missing:
names = ", ".join(n for n, _ in missing)
raise FileNotFoundError("missing v1.md files for directives: " + names)
non_directive_bodies: list[tuple[str, str]] = []
for desc, rel in sections.non_directive_context:
abs_path = (root / rel).resolve()
if not abs_path.exists():
print("WARNING: non-directive context file not found: " + str(abs_path), file=sys.stderr)
continue
try:
body = abs_path.read_text(encoding="utf-8")
except UnicodeDecodeError as e:
raise ValueError(desc + ": UTF-8 decode error in " + str(abs_path) + ": " + str(e)) from e
non_directive_bodies.append((desc, body))
out_lines: list[str] = []
for name, body in directive_bodies:
out_lines.append("=" * 40)
out_lines.append(name)
out_lines.append("=" * 40)
out_lines.append("")
out_lines.append(body.rstrip("\n"))
out_lines.append("")
out_lines.append("")
if non_directive_bodies:
out_lines.append("=" * 40)
out_lines.append("NON-DIRECTIVE CONTEXT")
out_lines.append("=" * 40)
out_lines.append("")
for desc, body in non_directive_bodies:
out_lines.append("--- " + desc + " ---")
out_lines.append("")
out_lines.append(body.rstrip("\n"))
out_lines.append("")
out_lines.append("")
rendered = "\n".join(out_lines)
if max_chars > 0 and len(rendered) > max_chars:
truncated = rendered[:max_chars]
suffix = "\n\n[truncated: showing " + str(max_chars) + " of " + str(len(rendered)) + " chars; omit max_chars for full text]"
rendered = truncated + suffix
return rendered
def aggregate(preset_path: Path, output_path: Path | None) -> int:
try:
rendered = aggregate_directives(preset_path)
except (FileNotFoundError, ValueError) as e:
print("ERROR: " + str(e), file=sys.stderr)
return 1
if output_path:
output_path.write_text(rendered, encoding="utf-8")
print("Wrote " + str(rendered.count("=" * 40) // 2) + " sections to " + str(output_path), file=sys.stderr)
else:
sys.stdout.write(rendered)
if not rendered.endswith("\n"):
sys.stdout.write("\n")
return 0
def main() -> int:
parser = argparse.ArgumentParser(description="Aggregate directive bodies from a preset markdown file.")
parser.add_argument("preset", type=Path, help="Path to the preset markdown file")
parser.add_argument("-o", "--output", type=Path, default=None, help="Write output to file instead of stdout")
args = parser.parse_args()
if not args.preset.exists():
print("ERROR: preset file not found: " + str(args.preset), file=sys.stderr)
return 1
output = args.output.resolve() if args.output else None
return aggregate(args.preset.resolve(), output)
if __name__ == "__main__":
sys.exit(main())
```
- [ ] **Step 2: Run tests to verify they pass**
Run: `uv run pytest tests/test_aggregate_directives_presets.py -v`
Expected: PASS (all 7 tests)
- [ ] **Step 3: Run existing aggregate to verify backward compat (no Inherits in current_baseline.md)**
Run: `uv run python scripts/aggregate_directives.py conductor/directives/presets/current_baseline.md -o tests/artifacts/_pytest_tmp/agg_compat_test.txt`
Expected: exit 0, output file contains 172 directive sections, no NON-DIRECTIVE CONTEXT section (current_baseline.md has no `## Non-directive context` section)
- [ ] **Step 4: Commit**
```bash
git add scripts/aggregate_directives.py tests/test_aggregate_directives_presets.py
git commit -m "feat(aggregate): add Inherits resolution + Non-directive context to preset parser"
```
---
### Task 1.3: Write integration test for non-directive context in the aggregate output
**Files:**
- Modify: `tests/test_aggregate_directives_presets.py`
- [ ] **Step 1: Add integration test for non-directive context in aggregate_directives output**
Append to `tests/test_aggregate_directives_presets.py`:
```python
class TestAggregateWithNonDirectiveContext:
"""Integration: aggregate_directives returns non-directive files in a separate section."""
def test_aggregate_includes_non_directive_section(self, tmp_path: Path):
directives_dir = tmp_path / "conductor" / "directives" / "alpha"
directives_dir.mkdir(parents=True)
(directives_dir / "v1.md").write_text("# Alpha rule\n\nDo the thing.\n", encoding="utf-8")
context_file = tmp_path / "AGENTS.md"
context_file.write_text("# Project Rules\n\nRule 1.\n", encoding="utf-8")
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
## Non-directive context
- project rules: AGENTS.md
""", encoding="utf-8")
rendered = aggregate_directives(str(preset), project_root=str(tmp_path))
assert "alpha" in rendered
assert "Alpha rule" in rendered
assert "NON-DIRECTIVE CONTEXT" in rendered
assert "Project Rules" in rendered
assert rendered.index("alpha") < rendered.index("NON-DIRECTIVE CONTEXT")
def test_aggregate_non_directive_section_after_directives(self, tmp_path: Path):
directives_dir = tmp_path / "conductor" / "directives" / "beta"
directives_dir.mkdir(parents=True)
(directives_dir / "v1.md").write_text("# Beta rule\n\nDo beta.\n", encoding="utf-8")
context_file = tmp_path / "conductor" / "product.md"
context_file.parent.mkdir(parents=True, exist_ok=True)
context_file.write_text("# Product\n\nVision.\n", encoding="utf-8")
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- beta: conductor/directives/beta/v1.md
## Non-directive context
- product: conductor/product.md
""", encoding="utf-8")
rendered = aggregate_directives(str(preset), project_root=str(tmp_path))
assert "Beta rule" in rendered
assert "Product" in rendered
assert rendered.index("Beta rule") < rendered.index("NON-DIRECTIVE CONTEXT")
assert "product" in rendered.lower() or "Product" in rendered
```
- [ ] **Step 2: Run tests to verify they pass**
Run: `uv run pytest tests/test_aggregate_directives_presets.py -v`
Expected: PASS (all 9 tests)
- [ ] **Step 3: Commit**
```bash
git add tests/test_aggregate_directives_presets.py
git commit -m "test(aggregate): add integration tests for non-directive context in aggregate output"
```
---
## Phase 2: Create baseline preset + verify
Focus: Write `baseline.md` with the 57 BASELINE directives + 4 non-directive context files. Verify the aggregate tool resolves it correctly against the real repo.
### Task 2.1: Create baseline.md
**Files:**
- Create: `conductor/directives/presets/baseline.md`
- [ ] **Step 1: Write baseline.md**
Create `conductor/directives/presets/baseline.md`:
```markdown
# Preset: baseline
The curated baseline — directives required to do anything in the codebase without creating entropy. Type promotion, edit discipline, git hard bans, research-first, error handling, state management, process anti-patterns. Excludes anything tier-specific, app-subsystem-specific, testing-specific, documentation-specific, or meta-directive. Engagement presets inherit this and add targeted subsets.
## Directives to warm
Read each file below before any action.
- atomic_per_task_commits: conductor/directives/atomic_per_task_commits/v1.md
- git_hard_bans: conductor/directives/git_hard_bans/v1.md
- timeline_is_immutable: conductor/directives/timeline_is_immutable/v1.md
- verbose_commit_message_ban: conductor/directives/verbose_commit_message_ban/v1.md
- verify_before_editing: conductor/directives/verify_before_editing/v1.md
- edit_small_incremental: conductor/directives/edit_small_incremental/v1.md
- mandatory_research_first: conductor/directives/mandatory_research_first/v1.md
- ast_parse_insufficient: conductor/directives/ast_parse_insufficient/v1.md
- ast_verify_class_methods_after_edit: conductor/directives/ast_verify_class_methods_after_edit/v1.md
- decorator_orphan_pitfall: conductor/directives/decorator_orphan_pitfall/v1.md
- preserve_line_endings: conductor/directives/preserve_line_endings/v1.md
- contract_change_audit: conductor/directives/contract_change_audit/v1.md
- search_all_call_sites_after_signature_change: conductor/directives/search_all_call_sites_after_signature_change/v1.md
- anti_entropy_state_audit_before_adding: conductor/directives/anti_entropy_state_audit_before_adding/v1.md
- no_diagnostic_noise: conductor/directives/no_diagnostic_noise/v1.md
- no_comments_in_body: conductor/directives/no_comments_in_body/v1.md
- one_space_indent: conductor/directives/one_space_indent/v1.md
- type_hints_required: conductor/directives/type_hints_required/v1.md
- no_new_src_files_without_permission: conductor/directives/no_new_src_files_without_permission/v1.md
- file_naming_convention: conductor/directives/file_naming_convention/v1.md
- large_files_are_fine: conductor/directives/large_files_are_fine/v1.md
- core_value_read_first: conductor/directives/core_value_read_first/v1.md
- ban_any_type: conductor/directives/ban_any_type/v1.md
- ban_dict_any: conductor/directives/ban_dict_any/v1.md
- ban_dict_get_on_known_fields: conductor/directives/ban_dict_get_on_known_fields/v1.md
- ban_getattr_dispatch: conductor/directives/ban_getattr_dispatch/v1.md
- ban_hasattr_dispatch: conductor/directives/ban_hasattr_dispatch/v1.md
- ban_optional_returns: conductor/directives/ban_optional_returns/v1.md
- ban_local_imports: conductor/directives/ban_local_imports/v1.md
- ban_prefix_aliasing: conductor/directives/ban_prefix_aliasing/v1.md
- ban_repeated_from_dict: conductor/directives/ban_repeated_from_dict/v1.md
- boundary_layer_exception: conductor/directives/boundary_layer_exception/v1.md
- metadata_boundary_type: conductor/directives/metadata_boundary_type/v1.md
- per_aggregate_dataclass_promotion: conductor/directives/per_aggregate_dataclass_promotion/v1.md
- typed_dataclass_fields: conductor/directives/typed_dataclass_fields/v1.md
- nil_sentinel_pattern: conductor/directives/nil_sentinel_pattern/v1.md
- result_error_pattern: conductor/directives/result_error_pattern/v1.md
- missing_data_renders_as_em_dash_not_crash: conductor/directives/missing_data_renders_as_em_dash_not_crash/v1.md
- comprehensive_logging: conductor/directives/comprehensive_logging/v1.md
- modular_controller_pattern: conductor/directives/modular_controller_pattern/v1.md
- strict_state_management: conductor/directives/strict_state_management/v1.md
- inherited_cruft_ask_first: conductor/directives/inherited_cruft_ask_first/v1.md
- deduction_loop_limit: conductor/directives/deduction_loop_limit/v1.md
- report_instead_of_fix_ban: conductor/directives/report_instead_of_fix_ban/v1.md
- scope_creep_track_doc_ban: conductor/directives/scope_creep_track_doc_ban/v1.md
- reproduction_before_fix: conductor/directives/reproduction_before_fix/v1.md
- single_hypothesis_minimal_test: conductor/directives/single_hypothesis_minimal_test/v1.md
- three_fix_failures_question_architecture: conductor/directives/three_fix_failures_question_architecture/v1.md
- cheap_fix_first_investigation_phases: conductor/directives/cheap_fix_first_investigation_phases/v1.md
- evidence_before_completion_claims: conductor/directives/evidence_before_completion_claims/v1.md
- verify_clean_baseline_before_starting: conductor/directives/verify_clean_baseline_before_starting/v1.md
- master_branch_default: conductor/directives/master_branch_default/v1.md
- pathlib_read_write_no_newline_kwarg: conductor/directives/pathlib_read_write_no_newline_kwarg/v1.md
- sdm_dependency_tags: conductor/directives/sdm_dependency_tags/v1.md
- convention_enforcement_4_mechanisms: conductor/directives/convention_enforcement_4_mechanisms/v1.md
- skill_check_before_clarifying: conductor/directives/skill_check_before_clarifying/v1.md
- no_content_duplication_across_agent_docs: conductor/directives/no_content_duplication_across_agent_docs/v1.md
## Non-directive context
Read each file below as full documents (NOT as directives — these carry product/tech/architecture context the directives reinforce but do not fully encode).
- project rules: AGENTS.md
- product vision: conductor/product.md
- product guidelines: conductor/product-guidelines.md
- tech stack: conductor/tech-stack.md
## Notes
The 57 BASELINE directives — rules required to do anything in the codebase without creating entropy. Classification based on the 2026-07-05 audit of all 172 directives (see docs/superpowers/specs/2026-07-05-directive-preset-system-design.md §2.4). The 4 non-directive context files are the product/tech docs agents tend to skip. Replaces current_baseline.md as the curated default; current_baseline.md is retained as the original control group.
```
- [ ] **Step 2: Verify the aggregate resolves baseline.md correctly**
Run: `uv run python scripts/aggregate_directives.py conductor/directives/presets/baseline.md -o tests/artifacts/_pytest_tmp/agg_baseline_test.txt`
Expected: exit 0, output file contains 57 directive sections + 1 NON-DIRECTIVE CONTEXT section with AGENTS.md, product.md, product-guidelines.md, tech-stack.md bodies.
- [ ] **Step 3: Verify the directive count is exactly 57**
Run: `uv run python -c "from pathlib import Path; import sys; sys.path.insert(0, 'scripts'); from aggregate_directives import resolve_inheritance; s = resolve_inheritance(Path('conductor/directives/presets/baseline.md')); print(len(s.directives)); print(len(s.non_directive_context))"`
Expected: `57` then `4`.
- [ ] **Step 4: Commit**
```bash
git add conductor/directives/presets/baseline.md
git commit -m "feat(directives): add curated baseline.md preset (57 BASELINE + 4 non-directive context)"
```
---
## Phase 3: Create 15 new op-specific directives
Focus: Create the 15 new directives that fill engagement-preset gaps. Each follows the existing `<name>/v1.md + meta.md` convention. These are new rules (no prior source to verbatim-lift); the `meta.md` records "new rule, no prior source."
### Task 3.1: Create the 3 audit directives
**Files:**
- Create: `conductor/directives/audit_script_as_gate/v1.md` + `meta.md`
- Create: `conductor/directives/audit_walks_filesystem_fresh/v1.md` + `meta.md`
- Create: `conductor/directives/baseline_capture_before_work/v1.md` + `meta.md`
- [ ] **Step 1: Write audit_script_as_gate**
Create `conductor/directives/audit_script_as_gate/v1.md`:
```markdown
# Audit scripts that classify or summarize must have a `--strict` quality gate that exits 1 on regression
## What it says
Every audit script in `scripts/audit_*.py` that produces classified output (weak types, exception handling, tier2 leaks, etc.) MUST implement a `--strict` mode that exits non-zero when the audit finds violations. The default mode (no `--strict`) is informational — it prints a human-readable report and exits 0. The `--strict` mode is the CI gate.
## Why
An audit without a gate is a suggestion. An audit with a `--strict` gate is a convention enforced before merge. The project's 4 existing audit scripts (`audit_weak_types.py`, `audit_exception_handling.py`, `audit_main_thread_imports.py`, `audit_no_models_config_io.py`) all follow this pattern. New audit scripts MUST follow it too.
## The pattern
```python
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--strict", action="store_true", help="Exit 1 on any violation")
parser.add_argument("--json", action="store_true", help="Machine-readable output")
args = parser.parse_args()
violations = run_audit()
if args.json:
print(json.dumps(violations))
else:
print_human_report(violations)
if args.strict and violations:
return 1
return 0
```
## See also
- `conductor/directives/convention_enforcement_4_mechanisms` — the 4-mechanism enforcement model (styleguide + checklist + audit script + CI gate)
- `conductor/directives/quality_gate_catches_broken_classifier_before_ship` — the quality gate for classifier scripts
```
Create `conductor/directives/audit_script_as_gate/meta.md`:
```markdown
# audit_script_as_gate
## v1
**Why this iteration:** New rule, no prior source. Encodes the pattern already followed by the 4 existing audit scripts (`audit_weak_types.py`, `audit_exception_handling.py`, `audit_main_thread_imports.py`, `audit_no_models_config_io.py`) so new audit scripts follow the same `--strict` gate convention.
**Source:** new rule (pattern inferred from existing `scripts/audit_*.py`)
---
**Lifted:** 2026-07-05
```
- [ ] **Step 2: Write audit_walks_filesystem_fresh**
Create `conductor/directives/audit_walks_filesystem_fresh/v1.md`:
```markdown
# Audit scripts walk the filesystem fresh on every run — never pin to a historical snapshot
## What it says
Audit scripts MUST walk the filesystem fresh on every invocation. They MUST NOT cache the file list, pin to a historical snapshot, or read from a stale index. Each run is a fresh walk of `src/`, `tests/`, `scripts/`, or whatever directory the audit targets.
## Why
A stale audit misses regressions. If the audit reads from a cached file list (e.g., a JSON index written last session), it will not see files added, renamed, or deleted since. The audit's value is "what does the codebase look like RIGHT NOW." A historical snapshot answers the wrong question.
## The pattern
```python
# WRONG: read from a cached index
files = json.loads(Path("tests/artifacts/file_index.json").read_text())
# RIGHT: walk fresh
files = list(Path("src").rglob("*.py"))
```
## See also
- `conductor/directives/generation_script_walks_filesystem_fresh_each_run` — the same rule for generation/index scripts
- `conductor/directives/audit_script_as_gate` — the `--strict` gate companion
```
Create `conductor/directives/audit_walks_filesystem_fresh/meta.md`:
```markdown
# audit_walks_filesystem_fresh
## v1
**Why this iteration:** New rule, no prior source. Companion to `generation_script_walks_filesystem_fresh_each_run` but scoped to audit scripts specifically.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 3: Write baseline_capture_before_work**
Create `conductor/directives/baseline_capture_before_work/v1.md`:
```markdown
# Before any track, capture the audit baseline numbers to a file so per-task deltas are auditable
## What it says
Before starting any track that will change the codebase, the agent MUST run the relevant audit scripts (`audit_weak_types.py`, `audit_exception_handling.py`, `audit_main_thread_imports.py`, `audit_no_models_config_io.py`) and save the baseline output to a file. The per-task commits then show the delta against this baseline.
## Why
Without a baseline, "did this task introduce a regression?" is unanswerable. The agent commits a change, the audit shows 47 violations, but the agent does not know if it was 47 before or 45 before. The baseline file is the "before" snapshot that makes the "after" meaningful.
## The pattern
```bash
# Before the first task
uv run python scripts/audit_weak_types.py --json > tests/artifacts/tier2_state/<track>/baseline_weak_types.json
uv run python scripts/audit_exception_handling.py --json > tests/artifacts/tier2_state/<track>/baseline_exception_handling.json
# After each task, compare
uv run python scripts/audit_weak_types.py --json > tests/artifacts/tier2_state/<track>/after_task_N_weak_types.json
```
## See also
- `conductor/directives/tier2_pre_flight_audit_gates` — the Tier 2 pre-flight audit protocol
- `conductor/directives/verify_clean_baseline_before_starting` — verify test baseline before starting
```
Create `conductor/directives/baseline_capture_before_work/meta.md`:
```markdown
# baseline_capture_before_work
## v1
**Why this iteration:** New rule, no prior source. Encodes the baseline-capture pattern already practiced in `conductor/directives/tier2_pre_flight_audit_gates` but generalized to any track (not just tier2-sandbox).
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 4: Commit**
```bash
git add conductor/directives/audit_script_as_gate/ conductor/directives/audit_walks_filesystem_fresh/ conductor/directives/baseline_capture_before_work/
git commit -m "feat(directives): add 3 audit-specific directives (gate, fresh-walk, baseline-capture)"
```
---
### Task 3.2: Create the 2 fix_tests directives
**Files:**
- Create: `conductor/directives/regression_bisect_to_clean_baseline/v1.md` + `meta.md`
- Create: `conductor/directives/adapt_test_not_skip_test/v1.md` + `meta.md`
- [ ] **Step 1: Write regression_bisect_to_clean_baseline**
Create `conductor/directives/regression_bisect_to_clean_baseline/v1.md`:
```markdown
# When fixing a regression, bisect to the last known-green commit before proposing a fix
## What it says
When a test regresses (was passing, now failing), the agent MUST `git bisect` (or manually bisect by running the test at recent commits) to identify the exact commit that introduced the regression BEFORE proposing a fix. The bisect result tells you what changed; the fix targets the cause, not the symptom.
## Why
Without a bisect, the agent is guessing at the cause. "The test fails because X is null" — but WHY is X null? The bisect points to the commit that made X null. The fix is then surgical: revert or repair the specific change. Without the bisect, the agent adds a null check (treating the symptom) and the real cause (a broken initialization in a different module) ships to production.
## The pattern
```bash
# Identify the last known-green commit
git log --oneline -20
# Run the failing test at recent commits until you find the green->red transition
git stash # if needed to preserve working tree
git switch -c bisect-tmp <older_commit>
uv run pytest tests/test_failing.py -v
# ... repeat until you find the transition commit
git switch - # back to the working branch
git branch -D bisect-tmp
```
## See also
- `conductor/directives/reproduction_before_fix` — reproduce the bug before fixing
- `conductor/directives/single_hypothesis_minimal_test` — test one hypothesis at a time
```
Create `conductor/directives/regression_bisect_to_clean_baseline/meta.md`:
```markdown
# regression_bisect_to_clean_baseline
## v1
**Why this iteration:** New rule, no prior source. The bisect-first pattern is implied by `reproduction_before_fix` but not explicitly stated for regressions.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 2: Write adapt_test_not_skip_test**
Create `conductor/directives/adapt_test_not_skip_test/v1.md`:
```markdown
# When a test fails due to a production API change, adapt the test to the new contract — never skip it
## What it says
When a test fails because production code changed its public API (function signature, return shape, callable-vs-value), the agent MUST adapt the test to the new contract. The agent MUST NOT add `@pytest.mark.skip` to make the test "pass." Skipping hides the regression; adapting fixes it.
## Why
A skipped test is a test that does not run. It provides zero coverage. The production API changed, the test is the only thing that would catch a consumer of the old API, and skipping it means the consumer breakage ships unnoticed. Adapting the test to the new contract keeps the coverage live.
## The pattern
```python
# WRONG: skip the test
@pytest.mark.skip(reason="API changed")
def test_old_behavior():
...
# RIGHT: adapt to the new contract
def test_new_behavior():
# Old: result = C_LBL (value)
# New: result = C_LBL() (callable)
result = C_LBL()
assert isinstance(result, ImVec4)
```
## See also
- `conductor/directives/adapt_test_mocks_to_production_api_change` — the broader rule for adapting test mocks
- `conductor/directives/no_skip_markers_as_avoidance` — skip markers are documentation, not avoidance
```
Create `conductor/directives/adapt_test_not_skip_test/meta.md`:
```markdown
# adapt_test_not_skip_test
## v1
**Why this iteration:** New rule, no prior source. Companion to `adapt_test_mocks_to_production_api_change` but scoped to the test itself (not the mock). Reinforces `no_skip_markers_as_avoidance` for the API-change case specifically.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 3: Commit**
```bash
git add conductor/directives/regression_bisect_to_clean_baseline/ conductor/directives/adapt_test_not_skip_test/
git commit -m "feat(directives): add 2 regression-fix directives (bisect-to-baseline, adapt-not-skip)"
```
---
### Task 3.3: Create the 2 refactor directives
**Files:**
- Create: `conductor/directives/refactor_preserves_behavior/v1.md` + `meta.md`
- Create: `conductor/directives/refactor_one_subsystem_per_phase/v1.md` + `meta.md`
- [ ] **Step 1: Write refactor_preserves_behavior**
Create `conductor/directives/refactor_preserves_behavior/v1.md`:
```markdown
# A refactor must preserve observable behavior — tests passing before must pass after
## What it says
A refactor is a change to internal structure that does NOT change observable behavior. The contract: every test that passed before the refactor MUST pass after. If a test fails after the refactor, either the refactor broke behavior (fix the refactor) or the test was testing an implementation detail (fix the test — but only if the implementation detail was not part of the public contract).
## Why
A refactor that breaks tests is not a refactor — it's a behavior change disguised as a refactor. The user reviews "refactor" commits differently than "feature" commits; a refactor is expected to be behavior-neutral. If the refactor breaks tests, the user's review assumption (this is safe) is violated.
## The pattern
```bash
# Before refactor: capture the test baseline
uv run python scripts/run_tests_batched.py --tier tier3 > tests/artifacts/tier2_state/<track>/before_refactor.log 2>&1
# After refactor: run the same tier
uv run python scripts/run_tests_batched.py --tier tier3 > tests/artifacts/tier2_state/<track>/after_refactor.log 2>&1
# Compare: every test that passed before must pass after
```
## See also
- `conductor/directives/contract_change_audit` — audit all callers when changing a public interface
- `conductor/directives/search_all_call_sites_after_signature_change` — search all call sites
```
Create `conductor/directives/refactor_preserves_behavior/meta.md`:
```markdown
# refactor_preserves_behavior
## v1
**Why this iteration:** New rule, no prior source. The behavior-preservation contract is implicit in the word "refactor" but never explicitly stated.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 2: Write refactor_one_subsystem_per_phase**
Create `conductor/directives/refactor_one_subsystem_per_phase/v1.md`:
```markdown
# Refactor one subsystem per phase — never touch two subsystems in the same atomic commit
## What it says
A refactor phase targets ONE subsystem (e.g., `src/ai_client.py`, `src/mcp_client.py`, `src/app_controller.py`). Two subsystems in the same phase means the diff is unreviewable: if one subsystem's refactor has a bug, the user cannot revert just that subsystem without also reverting the other.
## Why
The atomic-per-task commit principle extends to refactors. A phase that touches `ai_client.py` AND `mcp_client.py` in the same commit is a phase that cannot be bisected. If the post-refactor tests fail, the bisect points at the one commit that touched both — the agent cannot tell which subsystem caused the failure.
## The pattern
```
# WRONG: one phase, two subsystems
Phase 1: refactor ai_client.py type promotion + refactor mcp_client.py type promotion
# RIGHT: two phases, one subsystem each
Phase 1: refactor ai_client.py type promotion
Phase 2: refactor mcp_client.py type promotion
```
## See also
- `conductor/directives/atomic_per_task_commits` — one task = one commit
- `conductor/directives/scope_creep_track_doc_ban` — scope creep is banned
```
Create `conductor/directives/refactor_one_subsystem_per_phase/meta.md`:
```markdown
# refactor_one_subsystem_per_phase
## v1
**Why this iteration:** New rule, no prior source. Extends `atomic_per_task_commits` to the refactor phase granularity.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 3: Commit**
```bash
git add conductor/directives/refactor_preserves_behavior/ conductor/directives/refactor_one_subsystem_per_phase/
git commit -m "feat(directives): add 2 refactor directives (preserves-behavior, one-subsystem-per-phase)"
```
---
### Task 3.4: Create the 2 new_script_tool directives
**Files:**
- Create: `conductor/directives/scripts_namespace_isolated/v1.md` + `meta.md`
- Create: `conductor/directives/scripts_audit_pattern/v1.md` + `meta.md`
- [ ] **Step 1: Write scripts_namespace_isolated**
Create `conductor/directives/scripts_namespace_isolated/v1.md`:
```markdown
# Scripts are namespace-isolated by directory — no `from scripts.foo import bar` across script dirs
## What it says
Script directories (`scripts/`, `scripts/tier2/`, `scripts/audit/`, `scripts/tier2/artifacts/`) are namespace-isolated. A script in one directory MUST NOT import from a script in another directory. Scripts are not a package; they are standalone tools.
## Why
Scripts are throw-away or single-purpose tools. If `scripts/audit_weak_types.py` imports from `scripts/tier2/failcount.py`, then `audit_weak_types.py` depends on the tier2 sandbox's internal state. The dependency makes the audit script fragile (it breaks if tier2 moves) and the tier2 scripts less isolated (they're now part of a graph, not standalone).
## The pattern
```python
# WRONG: cross-directory import
from scripts.tier2.failcount import should_give_up
# RIGHT: copy the needed function into the script, or factor it into src/
```
## See also
- `conductor/directives/file_naming_convention` — the file naming rules
- `conductor/directives/large_files_are_fine` — large files are fine; don't split for modularity
```
Create `conductor/directives/scripts_namespace_isolated/meta.md`:
```markdown
# scripts_namespace_isolated
## v1
**Why this iteration:** New rule, no prior source. Encodes the existing project practice (scripts do not cross-import) as an explicit directive.
**Source:** new rule (pattern inferred from existing `scripts/` structure)
---
**Lifted:** 2026-07-05
```
- [ ] **Step 2: Write scripts_audit_pattern**
Create `conductor/directives/scripts_audit_pattern/v1.md`:
```markdown
# New audit scripts follow the `audit_<thing>.py` naming + `--json` + `--strict` pattern of the existing 4
## What it says
A new audit script MUST:
1. Be named `scripts/audit_<thing>.py` (not `check_<thing>.py`, not `lint_<thing>.py`).
2. Have a `--help` that explains what it checks and how to fix violations.
3. Have a `--json` mode for CI integration (machine-readable output).
4. Have a default informational mode (exits 0; prints human-readable report).
5. Have a `--strict` mode (exits 1 on any violation; the CI gate).
## Why
The 4 existing audit scripts (`audit_weak_types.py`, `audit_exception_handling.py`, `audit_main_thread_imports.py`, `audit_no_models_config_io.py`) all follow this pattern. A new audit script that breaks the pattern (e.g., named `check_foo.py` with no `--strict`) is inconsistent — the CI pipeline will not know how to gate on it, and the user will not know how to run it.
## See also
- `conductor/directives/audit_script_as_gate` — the `--strict` gate rule
- `conductor/directives/convention_enforcement_4_mechanisms` — the 4-mechanism model
```
Create `conductor/directives/scripts_audit_pattern/meta.md`:
```markdown
# scripts_audit_pattern
## v1
**Why this iteration:** New rule, no prior source. Encodes the existing audit-script conventions as a directive so new audit scripts follow the same shape.
**Source:** new rule (pattern inferred from existing `scripts/audit_*.py`)
---
**Lifted:** 2026-07-05
```
- [ ] **Step 3: Commit**
```bash
git add conductor/directives/scripts_namespace_isolated/ conductor/directives/scripts_audit_pattern/
git commit -m "feat(directives): add 2 script-authoring directives (namespace-isolated, audit-pattern)"
```
---
### Task 3.5: Create the remaining 6 directives (meta_tooling, directives_curation, documentation, media_analysis x2, ideation)
**Files:**
- Create: `conductor/directives/meta_tooling_no_app_imports/v1.md` + `meta.md`
- Create: `conductor/directives/directive_one_concept_per_file/v1.md` + `meta.md`
- Create: `conductor/directives/doc_update_after_track_ships/v1.md` + `meta.md`
- Create: `conductor/directives/media_source_cited_in_output/v1.md` + `meta.md`
- Create: `conductor/directives/media_analysis_neutral_voice/v1.md` + `meta.md`
- Create: `conductor/directives/ideation_leads_with_recommended/v1.md` + `meta.md`
- [ ] **Step 1: Write meta_tooling_no_app_imports**
Create `conductor/directives/meta_tooling_no_app_imports/v1.md`:
```markdown
# Meta-tooling scripts must not import `src/` application modules — the meta-tooling domain is distinct from the application domain
## What it says
Scripts in `scripts/` that are part of the META-TOOLING layer (the agent orchestration, the conductor, the directive system) MUST NOT import application modules from `src/` (e.g., `src/ai_client.py`, `src/app_controller.py`, `src/mcp_client.py`). The meta-tooling domain builds the tool; the application domain is the tool being built.
## Why
The meta-tooling/application boundary (see `docs/guide_meta_boundary.md`) is load-bearing. If a meta-tooling script imports `src/ai_client`, the meta-tooling domain now depends on the application's internal state. A refactor of `ai_client.py` breaks the meta-tooling script. The two domains evolve independently because they are different concerns.
## See also
- `conductor/directives/meta_tooling_app_boundary_check` — the broader boundary-check rule
- `docs/guide_meta_boundary.md` — the canonical boundary guide
```
Create `conductor/directives/meta_tooling_no_app_imports/meta.md`:
```markdown
# meta_tooling_no_app_imports
## v1
**Why this iteration:** New rule, no prior source. Reinforces `meta_tooling_app_boundary_check` at the import level.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 2: Write directive_one_concept_per_file**
Create `conductor/directives/directive_one_concept_per_file/v1.md`:
```markdown
# One directive per directory — do not combine two rules in one `v1.md`
## What it says
Each directive directory (`conductor/directives/<name>/`) contains exactly ONE rule in its `v1.md`. Do not combine two related-but-distinct rules into a single `v1.md`. If a rule has two parts that could be independently tagged, split into two directives.
## Why
Directives are independently tagged (the 6-dim `tags.toml`), independently composed into presets, and independently tested. A `v1.md` that combines "ban `dict[str, Any]`" and "ban `Optional[T]` returns" cannot be tagged with a single subject (data-structures vs error-handling) and cannot be independently included in a preset (one without the other). One concept per file keeps the directive atomic.
## See also
- `conductor/directives/verbatim_lift_not_rewrite` — the harvest rule
- `conductor/directives/warm_md_duplicates_not_in_place` — the `.warm.md` duplicate rule
```
Create `conductor/directives/directive_one_concept_per_file/meta.md`:
```markdown
# directive_one_concept_per_file
## v1
**Why this iteration:** New rule, no prior source. Encodes the existing practice (every directive is one concept) as an explicit rule for future harvesters.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 3: Write doc_update_after_track_ships**
Create `conductor/directives/doc_update_after_track_ships/v1.md`:
```markdown
# After a track ships, update the affected `docs/guide_*.md` to reflect the new module structure
## What it says
When a track changes the shape of a module (renames a file, moves a system, adds a new system file), the implementing agent MUST update the corresponding `docs/guide_*.md` to reflect the new structure. This is part of the track's final phase — not a follow-up track.
## Why
A track that only changes code (not docs) is incomplete. The next agent reads `docs/guide_*.md` to understand the module; if the guide is stale, the agent's mental model is wrong from the start. The documentation refresh is the track's handoff to the next agent.
## See also
- `conductor/directives/end_of_track_report_required` — the TRACK_COMPLETION report
- `conductor/directives/chronology_must_regenerate_after_every_track_ship` — the chronology regeneration
```
Create `conductor/directives/doc_update_after_track_ships/meta.md`:
```markdown
# doc_update_after_track_ships
## v1
**Why this iteration:** New rule, no prior source. Encodes the existing "Documentation Refresh Protocol" from `conductor/workflow.md` as a directive.
**Source:** new rule (pattern from `conductor/workflow.md` §"Documentation Refresh Protocol")
---
**Lifted:** 2026-07-05
```
- [ ] **Step 4: Write media_source_cited_in_output**
Create `conductor/directives/media_source_cited_in_output/v1.md`:
```markdown
# Media analysis output must cite the source URL/timestamp for every claim — no unsourced assertions
## What it says
When an agent analyzes content from a media source (YouTube transcript, substack article, tweet, user-provided text), every claim in the output MUST cite the source: the URL, the timestamp (for video/audio), or the paragraph number (for text). Unsourced assertions are banned.
## Why
Media analysis without citations is indistinguishable from hallucination. The user cannot verify a claim like "the author argues X" without knowing where in the source the author said X. The citation is the provenance; without it, the analysis is unverifiable.
## See also
- `conductor/directives/rag_six_rules` — the RAG provenance rule (same principle, different domain)
- `conductor/directives/comprehensive_logging` — comprehensive logging includes source provenance
```
Create `conductor/directives/media_source_cited_in_output/meta.md`:
```markdown
# media_source_cited_in_output
## v1
**Why this iteration:** New rule, no prior source. The citation requirement is the media-analysis equivalent of RAG's provenance rule.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 5: Write media_analysis_neutral_voice**
Create `conductor/directives/media_analysis_neutral_voice/v1.md`:
```markdown
# Media analysis output uses neutral voice — value judgments about the source content are the user's call, not the agent's
## What it says
When an agent analyzes media content, the output MUST use neutral voice. The agent describes what the source says, summarizes the arguments, extracts the claims — but does NOT evaluate whether the source is "good," "bad," "right," "wrong," "compelling," or "unconvincing." Value judgments are the user's domain.
## Why
The agent is a tool for understanding content, not a critic. If the agent says "this is a compelling argument," the user's own judgment is preempted. The user wants the content extracted and organized; the user decides whether it's compelling.
## See also
- `conductor/directives/neutral_language_for_doc_drift` — neutral language for documentation drift (same principle)
- `conductor/directives/no_performative_agreement_in_review` — no performative agreement (same principle)
```
Create `conductor/directives/media_analysis_neutral_voice/meta.md`:
```markdown
# media_analysis_neutral_voice
## v1
**Why this iteration:** New rule, no prior source. The neutral-voice principle from `neutral_language_for_doc_drift` applied to media analysis.
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 6: Write ideation_leads_with_recommended**
Create `conductor/directives/ideation_leads_with_recommended/v1.md`:
```markdown
# Ideation sessions lead with a recommended concept + the reasoning, not 3 unranked options
## What it says
When an agent helps the user ideate new concepts, tools, or UX, the output MUST lead with a single recommended concept and the reasoning for why it's the best fit. The agent MAY present 2-3 alternatives after the recommendation, but the recommendation comes first with the reasoning.
## Why
"Here are 3 options, pick one" offloads the synthesis to the user. The user asked for ideation help, not a multiple-choice quiz. The agent has the context; the agent should make the call. The user can reject the recommendation, but starting from a recommendation is faster than starting from a blank list.
## See also
- `conductor/directives/design_leads_with_recommendation` — the brainstorming equivalent
- `conductor/directives/exactly_four_completion_options` — completion options are different (they're structured choices, not ideation)
```
Create `conductor/directives/ideation_leads_with_recommended/meta.md`:
```markdown
# ideation_leads_with_recommended
## v1
**Why this iteration:** New rule, no prior source. Companion to `design_leads_with_recommendation` but scoped to ideation (concept generation) rather than design (architecture selection).
**Source:** new rule
---
**Lifted:** 2026-07-05
```
- [ ] **Step 7: Commit**
```bash
git add conductor/directives/meta_tooling_no_app_imports/ conductor/directives/directive_one_concept_per_file/ conductor/directives/doc_update_after_track_ships/ conductor/directives/media_source_cited_in_output/ conductor/directives/media_analysis_neutral_voice/ conductor/directives/ideation_leads_with_recommended/
git commit -m "feat(directives): add 6 engagement-specific directives (meta-tooling, curation, doc, media x2, ideation)"
```
---
### Task 3.6: Update tags.toml with the 15 new directives
**Files:**
- Modify: `conductor/directives/tags.toml`
- [ ] **Step 1: Add 15 new entries to tags.toml**
Append to `conductor/directives/tags.toml` (after the last existing entry, before EOF):
```toml
# 2026-07-05: 15 new engagement-specific directives (directive_preset_system track)
[directive.audit_script_as_gate]
tags = ["process", "verifying", "all-tiers", "conductor", "mandatory", "lifted-from-new-rule"]
[directive.audit_walks_filesystem_fresh]
tags = ["python", "writing", "all-tiers", "architecture", "mandatory", "lifted-from-new-rule"]
[directive.baseline_capture_before_work]
tags = ["process", "verifying", "all-tiers", "conductor", "mandatory", "lifted-from-new-rule"]
[directive.regression_bisect_to_clean_baseline]
tags = ["process", "debugging", "all-tiers", "conductor", "mandatory", "lifted-from-new-rule"]
[directive.adapt_test_not_skip_test]
tags = ["python", "testing", "all-tiers", "styleguide", "mandatory", "lifted-from-new-rule"]
[directive.refactor_preserves_behavior]
tags = ["python", "editing", "all-tiers", "architecture", "mandatory", "lifted-from-new-rule"]
[directive.refactor_one_subsystem_per_phase]
tags = ["process", "editing", "all-tiers", "conductor", "mandatory", "lifted-from-new-rule"]
[directive.scripts_namespace_isolated]
tags = ["python", "writing", "all-tiers", "imports", "mandatory", "lifted-from-new-rule"]
[directive.scripts_audit_pattern]
tags = ["process", "writing", "all-tiers", "conductor", "mandatory", "lifted-from-new-rule"]
[directive.meta_tooling_no_app_imports]
tags = ["python", "writing", "all-tiers", "mcp", "mandatory", "lifted-from-new-rule"]
[directive.directive_one_concept_per_file]
tags = ["process", "writing", "all-tiers", "documentation", "mandatory", "lifted-from-new-rule"]
[directive.doc_update_after_track_ships]
tags = ["process", "writing", "all-tiers", "documentation", "mandatory", "lifted-from-new-rule"]
[directive.media_source_cited_in_output]
tags = ["process", "writing", "all-tiers", "documentation", "mandatory", "lifted-from-new-rule"]
[directive.media_analysis_neutral_voice]
tags = ["process", "writing", "all-tiers", "documentation", "mandatory", "lifted-from-new-rule"]
[directive.ideation_leads_with_recommended]
tags = ["process", "writing", "all-tiers", "conductor", "mandatory", "lifted-from-new-rule"]
```
- [ ] **Step 2: Verify tags.toml parses**
Run: `uv run python -c "import tomllib; tomllib.load(open('conductor/directives/tags.toml','rb')); print('OK')"`
Expected: `OK`
- [ ] **Step 3: Commit**
```bash
git add conductor/directives/tags.toml
git commit -m "feat(directives): add 15 new directive entries to tags.toml"
```
---
## Phase 4: Create 12 engagement presets
Focus: Write the 12 engagement preset files that inherit baseline and add their targeted subsets. Each is a markdown file with `## Inherits`, `## Directives to warm`, `## Non-directive context`, and `## Notes`.
### Task 4.1: Create audit.md, fix_tests.md, new_script_tool.md, meta_tooling.md
**Files:**
- Create: `conductor/directives/presets/audit.md`
- Create: `conductor/directives/presets/fix_tests.md`
- Create: `conductor/directives/presets/new_script_tool.md`
- Create: `conductor/directives/presets/meta_tooling.md`
- [ ] **Step 1: Write audit.md**
Create `conductor/directives/presets/audit.md`:
```markdown
# Preset: audit
For agents auditing the codebase — running the audit scripts, capturing baselines, classifying violations, and producing audit reports. Adds the 3 audit-specific directives on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- audit_script_as_gate: conductor/directives/audit_script_as_gate/v1.md
- audit_walks_filesystem_fresh: conductor/directives/audit_walks_filesystem_fresh/v1.md
- baseline_capture_before_work: conductor/directives/baseline_capture_before_work/v1.md
## Notes
Engagement preset for the "auditing the codebase" op. Inherits baseline (57) + adds 3 audit-specific directives. Total: 60 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 2: Write fix_tests.md**
Create `conductor/directives/presets/fix_tests.md`:
```markdown
# Preset: fix_tests
For agents fixing test regressions — bisecting to the clean baseline, adapting tests to production API changes, running the batched test runner, and verifying in batch (not isolation). Adds the 23 TARGETED:testing directives + 2 regression-fix directives on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- tdd_red_green_required: conductor/directives/tdd_red_green_required/v1.md
- test_must_fail_for_believed_reason: conductor/directives/test_must_fail_for_believed_reason/v1.md
- test_passing_immediately_proves_nothing: conductor/directives/test_passing_immediately_proves_nothing/v1.md
- test_narrow_not_kitchen_sink: conductor/directives/test_narrow_not_kitchen_sink/v1.md
- batch_verification_not_isolation: conductor/directives/batch_verification_not_isolation/v1.md
- fragile_test_in_batch_is_failing_test: conductor/directives/fragile_test_in_batch_is_failing_test/v1.md
- no_skip_markers_as_avoidance: conductor/directives/no_skip_markers_as_avoidance/v1.md
- ban_arbitrary_core_mocking: conductor/directives/ban_arbitrary_core_mocking/v1.md
- test_instantiation_not_mock_away: conductor/directives/test_instantiation_not_mock_away/v1.md
- adapt_test_mocks_to_production_api_change: conductor/directives/adapt_test_mocks_to_production_api_change/v1.md
- live_gui_poll_not_sleep: conductor/directives/live_gui_poll_not_sleep/v1.md
- live_gui_session_scoped_no_restart: conductor/directives/live_gui_session_scoped_no_restart/v1.md
- no_real_io_during_tests: conductor/directives/no_real_io_during_tests/v1.md
- enforce_no_real_toml_in_tests: conductor/directives/enforce_no_real_toml_in_tests/v1.md
- test_sandbox: conductor/directives/test_sandbox/v1.md
- workspace_paths: conductor/directives/workspace_paths/v1.md
- opt_in_integration_test_via_env_var_marker: conductor/directives/opt_in_integration_test_via_env_var_marker/v1.md
- deterministic_signal_endpoint_pattern: conductor/directives/deterministic_signal_endpoint_pattern/v1.md
- failure_message_actionable_not_vague: conductor/directives/failure_message_actionable_not_vague/v1.md
- three_tier_test_strategy_for_fragile_subsystems: conductor/directives/three_tier_test_strategy_for_fragile_subsystems/v1.md
- test_classification_via_import_presence: conductor/directives/test_classification_via_import_presence/v1.md
- surface_dirty_state_in_test_runner: conductor/directives/surface_dirty_state_in_test_runner/v1.md
- imscope_tuple_return_per_scope_override: conductor/directives/imscope_tuple_return_per_scope_override/v1.md
- regression_bisect_to_clean_baseline: conductor/directives/regression_bisect_to_clean_baseline/v1.md
- adapt_test_not_skip_test: conductor/directives/adapt_test_not_skip_test/v1.md
## Notes
Engagement preset for the "fixing tests based on regressions" op. Inherits baseline (57) + adds 23 TARGETED:testing + 2 regression-fix. Total: 82 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 3: Write new_script_tool.md**
Create `conductor/directives/presets/new_script_tool.md`:
```markdown
# Preset: new_script_tool
For agents creating new script tools under `scripts/`. Adds the 2 script-authoring directives on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- scripts_namespace_isolated: conductor/directives/scripts_namespace_isolated/v1.md
- scripts_audit_pattern: conductor/directives/scripts_audit_pattern/v1.md
## Notes
Engagement preset for the "making new script tools" op. Inherits baseline (57) + adds 2 script-authoring directives. Total: 59 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 4: Write meta_tooling.md**
Create `conductor/directives/presets/meta_tooling.md`:
```markdown
# Preset: meta_tooling
For agents adjusting the meta-tooling layer — the conductor system, the directive harness, the agent prompts, the MCP server. Adds the meta-tooling boundary directive on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- meta_tooling_app_boundary_check: conductor/directives/meta_tooling_app_boundary_check/v1.md
- meta_tooling_no_app_imports: conductor/directives/meta_tooling_no_app_imports/v1.md
## Non-directive context
Read each file below as full documents.
- meta boundary guide: docs/guide_meta_boundary.md
## Notes
Engagement preset for the "adjusting meta-tooling" op. Inherits baseline (57) + adds 1 APP:mcp + 1 new meta-tooling directive + the meta-boundary guide. Total: 59 directives + 5 non-directive context files.
```
- [ ] **Step 5: Commit**
```bash
git add conductor/directives/presets/audit.md conductor/directives/presets/fix_tests.md conductor/directives/presets/new_script_tool.md conductor/directives/presets/meta_tooling.md
git commit -m "feat(directives): add 4 engagement presets (audit, fix_tests, new_script_tool, meta_tooling)"
```
---
### Task 4.2: Create implement_feature.md, refactor.md, directives_curation.md, documentation.md
**Files:**
- Create: `conductor/directives/presets/implement_feature.md`
- Create: `conductor/directives/presets/refactor.md`
- Create: `conductor/directives/presets/directives_curation.md`
- Create: `conductor/directives/presets/documentation.md`
- [ ] **Step 1: Write implement_feature.md**
Create `conductor/directives/presets/implement_feature.md`:
```markdown
# Preset: implement_feature
For agents implementing a new feature into `./src`, `./simulation`, or `./tests`. Adds the full testing set + the APP-subsystem directives (imgui, app-controller, architecture, rag-memory, mcp) on top of baseline. The agent loads the union; if the feature touches only one subsystem, the irrelevant APP directives are still useful as context.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- tdd_red_green_required: conductor/directives/tdd_red_green_required/v1.md
- test_must_fail_for_believed_reason: conductor/directives/test_must_fail_for_believed_reason/v1.md
- test_passing_immediately_proves_nothing: conductor/directives/test_passing_immediately_proves_nothing/v1.md
- test_narrow_not_kitchen_sink: conductor/directives/test_narrow_not_kitchen_sink/v1.md
- batch_verification_not_isolation: conductor/directives/batch_verification_not_isolation/v1.md
- fragile_test_in_batch_is_failing_test: conductor/directives/fragile_test_in_batch_is_failing_test/v1.md
- no_skip_markers_as_avoidance: conductor/directives/no_skip_markers_as_avoidance/v1.md
- ban_arbitrary_core_mocking: conductor/directives/ban_arbitrary_core_mocking/v1.md
- test_instantiation_not_mock_away: conductor/directives/test_instantiation_not_mock_away/v1.md
- adapt_test_mocks_to_production_api_change: conductor/directives/adapt_test_mocks_to_production_api_change/v1.md
- live_gui_poll_not_sleep: conductor/directives/live_gui_poll_not_sleep/v1.md
- live_gui_session_scoped_no_restart: conductor/directives/live_gui_session_scoped_no_restart/v1.md
- no_real_io_during_tests: conductor/directives/no_real_io_during_tests/v1.md
- enforce_no_real_toml_in_tests: conductor/directives/enforce_no_real_toml_in_tests/v1.md
- test_sandbox: conductor/directives/test_sandbox/v1.md
- workspace_paths: conductor/directives/workspace_paths/v1.md
- opt_in_integration_test_via_env_var_marker: conductor/directives/opt_in_integration_test_via_env_var_marker/v1.md
- deterministic_signal_endpoint_pattern: conductor/directives/deterministic_signal_endpoint_pattern/v1.md
- failure_message_actionable_not_vague: conductor/directives/failure_message_actionable_not_vague/v1.md
- three_tier_test_strategy_for_fragile_subsystems: conductor/directives/three_tier_test_strategy_for_fragile_subsystems/v1.md
- test_classification_via_import_presence: conductor/directives/test_classification_via_import_presence/v1.md
- surface_dirty_state_in_test_runner: conductor/directives/surface_dirty_state_in_test_runner/v1.md
- imscope_tuple_return_per_scope_override: conductor/directives/imscope_tuple_return_per_scope_override/v1.md
- imgui_scope_verification: conductor/directives/imgui_scope_verification/v1.md
- imgui_scope_entered_flag_for_no_op_return: conductor/directives/imgui_scope_entered_flag_for_no_op_return/v1.md
- ui_delegation_for_hot_reload: conductor/directives/ui_delegation_for_hot_reload/v1.md
- modal_explicit_opened_list_for_lifecycle: conductor/directives/modal_explicit_opened_list_for_lifecycle/v1.md
- view_composes_does_not_leak_into_theme_get_color: conductor/directives/view_composes_does_not_leak_into_theme_get_color/v1.md
- float_only_math_for_visual_transforms: conductor/directives/float_only_math_for_visual_transforms/v1.md
- controller_property_delegation_no_dual_state: conductor/directives/controller_property_delegation_no_dual_state/v1.md
- config_state_owner: conductor/directives/config_state_owner/v1.md
- reset_session_preserves_project_path: conductor/directives/reset_session_preserves_project_path/v1.md
- defer_heavy_sdk_imports_to_subprocess: conductor/directives/defer_heavy_sdk_imports_to_subprocess/v1.md
- defer_not_catch_for_native_crashes: conductor/directives/defer_not_catch_for_native_crashes/v1.md
- graceful_optional_dependency_degradation: conductor/directives/graceful_optional_dependency_degradation/v1.md
- submit_io_lazy_pool_recreation: conductor/directives/submit_io_lazy_pool_recreation/v1.md
- log_pruner_backoff_for_locked_files: conductor/directives/log_pruner_backoff_for_locked_files/v1.md
- undo_redo_100_snapshot_capacity: conductor/directives/undo_redo_100_snapshot_capacity/v1.md
- feature_flag_delete_to_turn_off: conductor/directives/feature_flag_delete_to_turn_off/v1.md
- runtime_config_flag_vs_test_env_var_gate: conductor/directives/runtime_config_flag_vs_test_env_var_gate/v1.md
- interceptor_activates_only_on_matching_shape: conductor/directives/interceptor_activates_only_on_matching_shape/v1.md
- quarantine_flag_the_engine_not_shared_types: conductor/directives/quarantine_flag_the_engine_not_shared_types/v1.md
- profile_first_optimize_second: conductor/directives/profile_first_optimize_second/v1.md
- state_visible_at_the_right_layer: conductor/directives/state_visible_at_the_right_layer/v1.md
- per_dimension_pick_dim_not_tool: conductor/directives/per_dimension_pick_dim_not_tool/v1.md
- cache_stable_to_volatile: conductor/directives/cache_stable_to_volatile/v1.md
- rag_six_rules: conductor/directives/rag_six_rules/v1.md
- knowledge_harvest_pattern: conductor/directives/knowledge_harvest_pattern/v1.md
- chroma_cache_path: conductor/directives/chroma_cache_path/v1.md
- per_conversation_scratch_dir: conductor/directives/per_conversation_scratch_dir/v1.md
- file_id_stable_across_rename: conductor/directives/file_id_stable_across_rename/v1.md
- meta_tooling_app_boundary_check: conductor/directives/meta_tooling_app_boundary_check/v1.md
## Notes
Engagement preset for the "implementing a new feature into ./src, ./simulation, or ./tests" op. Inherits baseline (57) + adds 23 TARGETED:testing + 14 APP:architecture + 6 APP:imgui + 3 APP:app-controller + 5 APP:rag-memory + 1 APP:mcp. Total: 109 directives + 4 non-directive context files (inherited). If the blob is too large for the context window, pass a smaller op-specific subset via `max_chars`.
```
- [ ] **Step 2: Write refactor.md**
Create `conductor/directives/presets/refactor.md`:
```markdown
# Preset: refactor
For agents refactoring or overhauling the codebase. Adds the testing set + 2 refactor-specific directives on top of baseline. The refactor must preserve behavior (tests pass before and after); one subsystem per phase.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- tdd_red_green_required: conductor/directives/tdd_red_green_required/v1.md
- test_must_fail_for_believed_reason: conductor/directives/test_must_fail_for_believed_reason/v1.md
- test_passing_immediately_proves_nothing: conductor/directives/test_passing_immediately_proves_nothing/v1.md
- test_narrow_not_kitchen_sink: conductor/directives/test_narrow_not_kitchen_sink/v1.md
- batch_verification_not_isolation: conductor/directives/batch_verification_not_isolation/v1.md
- fragile_test_in_batch_is_failing_test: conductor/directives/fragile_test_in_batch_is_failing_test/v1.md
- no_skip_markers_as_avoidance: conductor/directives/no_skip_markers_as_avoidance/v1.md
- ban_arbitrary_core_mocking: conductor/directives/ban_arbitrary_core_mocking/v1.md
- test_instantiation_not_mock_away: conductor/directives/test_instantiation_not_mock_away/v1.md
- adapt_test_mocks_to_production_api_change: conductor/directives/adapt_test_mocks_to_production_api_change/v1.md
- live_gui_poll_not_sleep: conductor/directives/live_gui_poll_not_sleep/v1.md
- live_gui_session_scoped_no_restart: conductor/directives/live_gui_session_scoped_no_restart/v1.md
- no_real_io_during_tests: conductor/directives/no_real_io_during_tests/v1.md
- enforce_no_real_toml_in_tests: conductor/directives/enforce_no_real_toml_in_tests/v1.md
- test_sandbox: conductor/directives/test_sandbox/v1.md
- workspace_paths: conductor/directives/workspace_paths/v1.md
- opt_in_integration_test_via_env_var_marker: conductor/directives/opt_in_integration_test_via_env_var_marker/v1.md
- deterministic_signal_endpoint_pattern: conductor/directives/deterministic_signal_endpoint_pattern/v1.md
- failure_message_actionable_not_vague: conductor/directives/failure_message_actionable_not_vague/v1.md
- three_tier_test_strategy_for_fragile_subsystems: conductor/directives/three_tier_test_strategy_for_fragile_subsystems/v1.md
- test_classification_via_import_presence: conductor/directives/test_classification_via_import_presence/v1.md
- surface_dirty_state_in_test_runner: conductor/directives/surface_dirty_state_in_test_runner/v1.md
- imscope_tuple_return_per_scope_override: conductor/directives/imscope_tuple_return_per_scope_override/v1.md
- refactor_preserves_behavior: conductor/directives/refactor_preserves_behavior/v1.md
- refactor_one_subsystem_per_phase: conductor/directives/refactor_one_subsystem_per_phase/v1.md
## Notes
Engagement preset for the "refactoring or overhauling the codebase" op. Inherits baseline (57) + adds 23 TARGETED:testing + 2 refactor. Total: 82 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 3: Write directives_curation.md**
Create `conductor/directives/presets/directives_curation.md`:
```markdown
# Preset: directives_curation
For agents improving, curating, or adding workflow directives or context. Adds the 3 META directives + 3 TARGETED:documentation directives + 1 new curation directive on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- verbatim_lift_not_rewrite: conductor/directives/verbatim_lift_not_rewrite/v1.md
- warm_md_duplicates_not_in_place: conductor/directives/warm_md_duplicates_not_in_place/v1.md
- generation_script_walks_filesystem_fresh_each_run: conductor/directives/generation_script_walks_filesystem_fresh_each_run/v1.md
- docs_philosophy_then_boundaries_then_logic_then_verify: conductor/directives/docs_philosophy_then_boundaries_then_logic_then_verify/v1.md
- neutral_language_for_doc_drift: conductor/directives/neutral_language_for_doc_drift/v1.md
- no_conductor_yaml_for_artifacts: conductor/directives/no_conductor_yaml_for_artifacts/v1.md
- directive_one_concept_per_file: conductor/directives/directive_one_concept_per_file/v1.md
## Notes
Engagement preset for the "improving, curating, or adding workflow directives or context" op. Inherits baseline (57) + adds 3 META + 3 TARGETED:documentation + 1 curation. Total: 64 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 4: Write documentation.md**
Create `conductor/directives/presets/documentation.md`:
```markdown
# Preset: documentation
For agents writing or updating codebase documentation (`docs/guide_*.md`, `docs/Readme.md`, `docs/AGENTS.md`). Adds the 3 TARGETED:documentation directives + 1 new doc-update directive on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- docs_philosophy_then_boundaries_then_logic_then_verify: conductor/directives/docs_philosophy_then_boundaries_then_logic_then_verify/v1.md
- neutral_language_for_doc_drift: conductor/directives/neutral_language_for_doc_drift/v1.md
- no_conductor_yaml_for_artifacts: conductor/directives/no_conductor_yaml_for_artifacts/v1.md
- doc_update_after_track_ships: conductor/directives/doc_update_after_track_ships/v1.md
## Non-directive context
Read each file below as full documents.
- docs index: docs/Readme.md
- agent docs mirror: docs/AGENTS.md
## Notes
Engagement preset for the "writing or updating codebase documentation" op. Inherits baseline (57) + adds 3 TARGETED:documentation + 1 doc-update. Total: 61 directives + 6 non-directive context files (4 inherited + 2 added).
```
- [ ] **Step 5: Commit**
```bash
git add conductor/directives/presets/implement_feature.md conductor/directives/presets/refactor.md conductor/directives/presets/directives_curation.md conductor/directives/presets/documentation.md
git commit -m "feat(directives): add 4 engagement presets (implement_feature, refactor, directives_curation, documentation)"
```
---
### Task 4.3: Create media_analysis.md, ideation.md, tier2_autonomous.md
**Files:**
- Create: `conductor/directives/presets/media_analysis.md`
- Create: `conductor/directives/presets/ideation.md`
- Create: `conductor/directives/presets/tier2_autonomous.md`
- [ ] **Step 1: Write media_analysis.md**
Create `conductor/directives/presets/media_analysis.md`:
```markdown
# Preset: media_analysis
For agents doing analysis on content from various media sources (YouTube, substack, twitter, user-provided text/markdown). Adds 2 media-analysis directives on top of baseline. No testing directives — media analysis does not touch the test suite.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- media_source_cited_in_output: conductor/directives/media_source_cited_in_output/v1.md
- media_analysis_neutral_voice: conductor/directives/media_analysis_neutral_voice/v1.md
## Notes
Engagement preset for the "doing analysis on content from various media sources" op. Inherits baseline (57) + adds 2 media-analysis. Total: 59 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 2: Write ideation.md**
Create `conductor/directives/presets/ideation.md`:
```markdown
# Preset: ideation
For agents ideating new concepts, tools, or UX with the user. Adds the 7 PROCESS:planning directives + 1 new ideation directive on top of baseline.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- brainstorm_even_for_simple_projects: conductor/directives/brainstorm_even_for_simple_projects/v1.md
- design_leads_with_recommendation: conductor/directives/design_leads_with_recommendation/v1.md
- spec_self_review_four_checks: conductor/directives/spec_self_review_four_checks/v1.md
- plan_steps_2_to_5_minutes_each: conductor/directives/plan_steps_2_to_5_minutes_each/v1.md
- plans_no_placeholders_or_tbds: conductor/directives/plans_no_placeholders_or_tbds/v1.md
- review_plan_critically_before_executing: conductor/directives/review_plan_critically_before_executing/v1.md
- surface_upstream_api_limits_honestly_in_spec: conductor/directives/surface_upstream_api_limits_honestly_in_spec/v1.md
- ideation_leads_with_recommended: conductor/directives/ideation_leads_with_recommended/v1.md
## Notes
Engagement preset for the "ideating new concepts or tools or ux with the user" op. Inherits baseline (57) + adds 7 PROCESS:planning + 1 ideation. Total: 65 directives + 4 non-directive context files (inherited).
```
- [ ] **Step 3: Write tier2_autonomous.md**
Create `conductor/directives/presets/tier2_autonomous.md`:
```markdown
# Preset: tier2_autonomous
The compound preset for Tier-2 autonomous sandbox execution. Inherits baseline + adds the 20 TARGETED:tier2-sandbox + 23 TARGETED:testing + 8 PROCESS:delegation directives. This is the preset the `tier2-autonomous.warm.md` agent prompt warms with.
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- ban_appdata_paths: conductor/directives/ban_appdata_paths/v1.md
- use_batched_test_runner: conductor/directives/use_batched_test_runner/v1.md
- no_output_filtering: conductor/directives/no_output_filtering/v1.md
- prefer_targeted_tier_runs: conductor/directives/prefer_targeted_tier_runs/v1.md
- throwaway_scripts_isolated_subdir: conductor/directives/throwaway_scripts_isolated_subdir/v1.md
- acknowledgment_in_first_commit: conductor/directives/acknowledgment_in_first_commit/v1.md
- end_of_track_report_required: conductor/directives/end_of_track_report_required/v1.md
- tier2_pre_commit_deletion_and_diff_check: conductor/directives/tier2_pre_commit_deletion_and_diff_check/v1.md
- tier2_pre_flight_audit_gates: conductor/directives/tier2_pre_flight_audit_gates/v1.md
- tier2_post_track_ruff_mypy_audit: conductor/directives/tier2_post_track_ruff_mypy_audit/v1.md
- per_phase_metric_regression_fix: conductor/directives/per_phase_metric_regression_fix/v1.md
- run_full_tier_after_phase_refactor: conductor/directives/run_full_tier_after_phase_refactor/v1.md
- user_corrections_log_in_state_toml: conductor/directives/user_corrections_log_in_state_toml/v1.md
- surface_gaps_at_discovery_not_checkpoint: conductor/directives/surface_gaps_at_discovery_not_checkpoint/v1.md
- chronology_must_regenerate_after_every_track_ship: conductor/directives/chronology_must_regenerate_after_every_track_ship/v1.md
- use_git_history_as_classification_source_of_truth: conductor/directives/use_git_history_as_classification_source_of_truth/v1.md
- classifier_must_emit_per_row_evidence: conductor/directives/classifier_must_emit_per_row_evidence/v1.md
- quality_gate_catches_broken_classifier_before_ship: conductor/directives/quality_gate_catches_broken_classifier_before_ship/v1.md
- manual_compaction_only_no_auto_summarize: conductor/directives/manual_compaction_only_no_auto_summarize/v1.md
- preserve_before_compact_archive: conductor/directives/preserve_before_compact_archive/v1.md
- tdd_red_green_required: conductor/directives/tdd_red_green_required/v1.md
- test_must_fail_for_believed_reason: conductor/directives/test_must_fail_for_believed_reason/v1.md
- test_passing_immediately_proves_nothing: conductor/directives/test_passing_immediately_proves_nothing/v1.md
- test_narrow_not_kitchen_sink: conductor/directives/test_narrow_not_kitchen_sink/v1.md
- batch_verification_not_isolation: conductor/directives/batch_verification_not_isolation/v1.md
- fragile_test_in_batch_is_failing_test: conductor/directives/fragile_test_in_batch_is_failing_test/v1.md
- no_skip_markers_as_avoidance: conductor/directives/no_skip_markers_as_avoidance/v1.md
- ban_arbitrary_core_mocking: conductor/directives/ban_arbitrary_core_mocking/v1.md
- test_instantiation_not_mock_away: conductor/directives/test_instantiation_not_mock_away/v1.md
- adapt_test_mocks_to_production_api_change: conductor/directives/adapt_test_mocks_to_production_api_change/v1.md
- live_gui_poll_not_sleep: conductor/directives/live_gui_poll_not_sleep/v1.md
- live_gui_session_scoped_no_restart: conductor/directives/live_gui_session_scoped_no_restart/v1.md
- no_real_io_during_tests: conductor/directives/no_real_io_during_tests/v1.md
- enforce_no_real_toml_in_tests: conductor/directives/enforce_no_real_toml_in_tests/v1.md
- test_sandbox: conductor/directives/test_sandbox/v1.md
- workspace_paths: conductor/directives/workspace_paths/v1.md
- opt_in_integration_test_via_env_var_marker: conductor/directives/opt_in_integration_test_via_env_var_marker/v1.md
- deterministic_signal_endpoint_pattern: conductor/directives/deterministic_signal_endpoint_pattern/v1.md
- failure_message_actionable_not_vague: conductor/directives/failure_message_actionable_not_vague/v1.md
- three_tier_test_strategy_for_fragile_subsystems: conductor/directives/three_tier_test_strategy_for_fragile_subsystems/v1.md
- test_classification_via_import_presence: conductor/directives/test_classification_via_import_presence/v1.md
- surface_dirty_state_in_test_runner: conductor/directives/surface_dirty_state_in_test_runner/v1.md
- imscope_tuple_return_per_scope_override: conductor/directives/imscope_tuple_return_per_scope_override/v1.md
- tier3_worker_amnesia: conductor/directives/tier3_worker_amnesia/v1.md
- tier4_qa_compressed_fix: conductor/directives/tier4_qa_compressed_fix/v1.md
- token_firewall_prevents_bloat: conductor/directives/token_firewall_prevents_bloat/v1.md
- never_inherit_session_history_to_subagent: conductor/directives/never_inherit_session_history_to_subagent/v1.md
- subagent_returns_artifact_not_transcript: conductor/directives/subagent_returns_artifact_not_transcript/v1.md
- agent_prompt_one_independent_domain: conductor/directives/agent_prompt_one_independent_domain/v1.md
- decompose_or_isolate_never_offload: conductor/directives/decompose_or_isolate_never_offload/v1.md
- worker_three_point_abort_check: conductor/directives/worker_three_point_abort_check/v1.md
## Non-directive context
Read each file below as full documents.
- tier2 autonomous guide: docs/guide_tier2_autonomous.md
## Notes
Compound preset for Tier-2 autonomous sandbox execution. Inherits baseline (57) + adds 20 TARGETED:tier2-sandbox + 23 TARGETED:testing + 8 PROCESS:delegation. Total: 108 directives + 5 non-directive context files (4 inherited + 1 added). This is the preset `tier2-autonomous.warm.md` warms with.
```
- [ ] **Step 4: Verify each engagement preset resolves correctly via aggregate**
Run: `uv run python -c "from pathlib import Path; import sys; sys.path.insert(0, 'scripts'); from aggregate_directives import resolve_inheritance; [print(f'{n}: {len(resolve_inheritance(Path(f\"conductor/directives/presets/{n}.md\")).directives)} directives') for n in ['baseline','audit','fix_tests','implement_feature','refactor','new_script_tool','meta_tooling','directives_curation','documentation','media_analysis','ideation','tier2_autonomous']]"`
Expected: baseline=57, audit=60, fix_tests=82, implement_feature=109, refactor=82, new_script_tool=59, meta_tooling=59, directives_curation=64, documentation=61, media_analysis=59, ideation=65, tier2_autonomous=108.
- [ ] **Step 5: Commit**
```bash
git add conductor/directives/presets/media_analysis.md conductor/directives/presets/ideation.md conductor/directives/presets/tier2_autonomous.md
git commit -m "feat(directives): add 3 engagement presets (media_analysis, ideation, tier2_autonomous)"
```
---
### Task 4.4: Mark current_baseline.md deprecated
**Files:**
- Modify: `conductor/directives/presets/current_baseline.md` (Notes section only)
- [ ] **Step 1: Update the Notes section of current_baseline.md**
In `conductor/directives/presets/current_baseline.md`, replace the `## Notes` section's first line. Find the line starting with `All v1 (verbatim lifts from current production docs;` and prepend a deprecation notice above it:
Insert at the start of the Notes section (after `## Notes`):
```markdown
**DEPRECATED (2026-07-05):** This preset is the original control group from the `directive_hotswap_harness_20260627` track. It lists all 172 directives indiscriminately. The curated replacement is `baseline.md` (57 BASELINE directives + 4 non-directive context files). Engagement presets (`audit.md`, `fix_tests.md`, etc.) inherit `baseline.md` and add targeted subsets. This file is RETAINED as the original control group for future A/B encoding experiments — do not delete. New agents should warm with `baseline.md` or an engagement preset, not this file.
```
- [ ] **Step 2: Commit**
```bash
git add conductor/directives/presets/current_baseline.md
git commit -m "docs(directives): mark current_baseline.md deprecated (retained as control group)"
```
---
## Phase 5: Dedup .warm.md + update bootstrap default
Focus: Strip the inline bulk-markdown sections from `tier2-autonomous.warm.md` that are now covered by directives. Update the preset path to `tier2_autonomous.md`. Update the bootstrap script's default.
### Task 5.1: Dedup tier2-autonomous.warm.md
**Files:**
- Modify: `conductor/tier2/agents/tier2-autonomous.warm.md`
- [ ] **Step 1: Update the warm-with preset path**
In `conductor/tier2/agents/tier2-autonomous.warm.md`, find the `manual-slop_aggregate_directives(preset_path="conductor/directives/presets/current_baseline.md")` line and change it to:
```
manual-slop_aggregate_directives(preset_path="conductor/directives/presets/tier2_autonomous.md")
```
- [ ] **Step 2: Strip the "Hard Bans" section**
Find the `## Hard Bans (cannot run, enforced at 3 layers)` section and its entire body (including the "THE TIMELINE-IS-IMMUTABLE PRINCIPLE" subsection) and remove it. This content is covered by the `git_hard_bans`, `ban_appdata_paths`, and `timeline_is_immutable` directives.
- [ ] **Step 3: Strip the "Conventions" section**
Find the `## Conventions (MUST follow - added 2026-06-17; updated 2026-06-27)` section and remove its bullet points about: test runner, never filter test output, prefer targeted tier runs, default branch, line endings, throw-away scripts, end-of-track report, run-time expectation, temp files. These are covered by `use_batched_test_runner`, `no_output_filtering`, `prefer_targeted_tier_runs`, `master_branch_default`, `preserve_line_endings`, `throwaway_scripts_isolated_subdir`, `end_of_track_report_required` directives. Replace the entire section with a one-line pointer:
```markdown
## Conventions
The tier2_autonomous.md preset (warmed above) includes the TARGETED:tier2-sandbox directives that encode all sandbox conventions (batched test runner, no output filtering, targeted tier runs, throw-away script paths, end-of-track report, AppData ban, etc.). Follow them.
```
- [ ] **Step 4: Strip the "Sub-Agent Delegation" section**
Find the `## Sub-Agent Delegation (replaces legacy mma_exec.py — updated 2026-06-27)` section and remove it. Covered by `tier3_worker_amnesia`, `tier4_qa_compressed_fix`, `agent_prompt_one_independent_domain`, `decompose_or_isolate_never_offload` directives.
- [ ] **Step 5: Strip the "TDD Protocol" section**
Find the `## TDD Protocol` section and remove it. Covered by `tdd_red_green_required` and `per_phase_metric_regression_fix`.
- [ ] **Step 6: Strip the "Per-Task Commit Protocol" section**
Find the `## Per-Task Commit Protocol` section and remove it. Covered by `atomic_per_task_commits` and `verbose_commit_message_ban`.
- [ ] **Step 7: Keep "Failcount Contract", "Pre-Delegation Checkpoint", and "Limitations" inline**
Do NOT strip these sections. They contain tier-2-specific operational detail (failcount thresholds, `git add .` safety net, scope limitations) that no directive covers.
- [ ] **Step 8: Verify the file is syntactically valid markdown + reduced in size**
Run: `uv run python -c "from pathlib import Path; content = Path('conductor/tier2/agents/tier2-autonomous.warm.md').read_text(encoding='utf-8'); print(f'Size: {len(content)} chars, {content.count(chr(10))} lines')"`
Expected: size significantly reduced from ~17940 chars (target ~6-8KB).
- [ ] **Step 9: Commit**
```bash
git add conductor/tier2/agents/tier2-autonomous.warm.md
git commit -m "refactor(tier2): dedup tier2-autonomous.warm.md — strip inline sections covered by directives"
```
---
### Task 5.2: Update setup_tier2_clone_directives.ps1 default preset
**Files:**
- Modify: `scripts/tier2/setup_tier2_clone_directives.ps1`
- [ ] **Step 1: Change the -PresetPath default**
In `scripts/tier2/setup_tier2_clone_directives.ps1`, find the param block:
```powershell
param(
[string]$MainRepoPath = "C:\projects\manual_slop",
[string]$Tier2ClonePath = "C:\projects\manual_slop_tier2",
[string]$PresetPath = "conductor/directives/presets/current_baseline.md"
)
```
Change the `$PresetPath` default to:
```powershell
[string]$PresetPath = "conductor/directives/presets/tier2_autonomous.md"
```
- [ ] **Step 2: Run the script to verify it works with the new default**
Run: `& "C:\projects\manual_slop\scripts\tier2\setup_tier2_clone_directives.ps1"`
Expected: bootstrap completes, `[tier2-bootstrap-directives] preset resolved at C:\projects\manual_slop_tier2\conductor\directives\presets\tier2_autonomous.md`.
- [ ] **Step 3: Commit**
```bash
git add scripts/tier2/setup_tier2_clone_directives.ps1
git commit -m "feat(tier2): update setup_tier2_clone_directives.ps1 default preset to tier2_autonomous.md"
```
---
## Phase 6: Final verification
Focus: Run the full test suite for the new test file, verify all presets resolve, verify the aggregate tool produces correct output for the new presets.
### Task 6.1: Run all tests + verify all presets resolve
- [ ] **Step 1: Run the aggregate_directives preset tests**
Run: `uv run pytest tests/test_aggregate_directives_presets.py -v`
Expected: all 9 tests PASS.
- [ ] **Step 2: Verify all 12 presets resolve via the aggregate tool**
Run: `uv run python -c "from pathlib import Path; import sys; sys.path.insert(0, 'scripts'); from aggregate_directives import aggregate_directives; [aggregate_directives(f'conductor/directives/presets/{n}.md') or print(f'{n}: OK') for n in ['baseline','audit','fix_tests','implement_feature','refactor','new_script_tool','meta_tooling','directives_curation','documentation','media_analysis','ideation','tier2_autonomous']]"`
Expected: 12 lines of `<name>: OK`.
- [ ] **Step 3: Verify baseline.md returns exactly 57 directives + 4 non-directive context**
Run: `uv run python -c "from pathlib import Path; import sys; sys.path.insert(0, 'scripts'); from aggregate_directives import resolve_inheritance; s = resolve_inheritance(Path('conductor/directives/presets/baseline.md')); assert len(s.directives) == 57, f'expected 57, got {len(s.directives)}'; assert len(s.non_directive_context) == 4, f'expected 4, got {len(s.non_directive_context)}'; print('baseline: 57 directives + 4 non-directive context OK')"`
Expected: `baseline: 57 directives + 4 non-directive context OK`.
- [ ] **Step 4: Verify tier2_autonomous.md returns the expected count**
Run: `uv run python -c "from pathlib import Path; import sys; sys.path.insert(0, 'scripts'); from aggregate_directives import resolve_inheritance; s = resolve_inheritance(Path('conductor/directives/presets/tier2_autonomous.md')); assert len(s.directives) == 108, f'expected 108, got {len(s.directives)}'; assert len(s.non_directive_context) == 5, f'expected 5, got {len(s.non_directive_context)}'; print('tier2_autonomous: 108 directives + 5 non-directive context OK')"`
Expected: `tier2_autonomous: 108 directives + 5 non-directive context OK`.
- [ ] **Step 5: Verify the .warm.md file is reduced**
Run: `uv run python -c "from pathlib import Path; content = Path('conductor/tier2/agents/tier2-autonomous.warm.md').read_text(encoding='utf-8'); size = len(content); assert size < 10000, f'expected < 10KB, got {size}'; assert 'tier2_autonomous.md' in content, 'preset path not updated'; assert '## Hard Bans' not in content, 'Hard Bans section not stripped'; assert '## Sub-Agent Delegation' not in content, 'Sub-Agent Delegation not stripped'; print(f'.warm.md: {size} chars OK')"`
Expected: `.warm.md: <size> chars OK`.
- [ ] **Step 6: Commit the final verification state (if any uncommitted test artifacts)**
If there are uncommitted test artifacts in `tests/artifacts/_pytest_tmp/`, clean them up:
```bash
Remove-Item -Recurse -Force tests/artifacts/_pytest_tmp/ -ErrorAction SilentlyContinue
```
No commit needed if the working tree is clean.
---
## Self-Review Notes
**Spec coverage check:**
- G1 (baseline preset) → Task 2.1 ✓
- G2 (12 engagement presets) → Tasks 4.1-4.3 ✓
- G3 (preset format extension: Inherits + Non-directive context) → Task 1.2 ✓
- G4 (aggregate tool extended) → Task 1.2 ✓
- G5 (15 new directives) → Tasks 3.1-3.5 ✓
- G6 (.warm.md dedup) → Task 5.1 ✓
- G7 (current_baseline.md retained, marked deprecated) → Task 4.4 ✓
- G8 (setup script default updated) → Task 5.2 ✓
- NFR5 (tags.toml updated) → Task 3.6 ✓
- V1-V5 (verification) → Task 6.1 ✓
**Type consistency check:** `PresetSections` dataclass is used consistently in `parse_preset_sections`, `resolve_inheritance`, and the tests. Field names match: `inherits`, `directives`, `non_directive_context`.
**No placeholders:** All steps contain complete code. No "TBD", "implement later", "similar to Task N".