diff --git a/conductor/tracks/code_path_audit_20260607/plan_v2.md b/conductor/tracks/code_path_audit_20260607/plan_v2.md new file mode 100644 index 00000000..c4c7944a --- /dev/null +++ b/conductor/tracks/code_path_audit_20260607/plan_v2.md @@ -0,0 +1,6320 @@ +# Code Path & Data Pipeline Audit v2 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. + +> **Timing (added 2026-06-22).** This plan is the v2 implementation. The v1 plan (2026-06-07) was never executed. v2 supersedes v1 with a different output structure (per-data-aggregate vs per-action), 4 static analyses (PCG, MemoryDim, APD, CFE), and a decomposition-cost heuristic per aggregate. The Tier 2 Tech Lead should verify the post-4-tracks baseline + the 100% complete result_migration campaign + the 4 audit scripts (audit_main_thread_imports, audit_weak_types, audit_exception_handling, audit_no_models_config_io, audit_optional_in_3_files) + the type registry generator all exist on master before starting Phase 1. + +**Goal:** Build `src/code_path_audit.py` v2 — a data-oriented static-analysis tool that audits data pipelines in `src/` and produces per-data-aggregate profiles. The output (custom postfix `.dsl` data + markdown + prefix tree text) is the artifact that informs per-aggregate refactor decisions. 10+3 aggregates (10 in-scope + 3 candidate placeholders for `any_type_componentization_20260621` which is NOT on master). + +**Architecture:** Single new module `src/code_path_audit.py` (the v2 audit tool). Conductor + 4 static analyzers (PCG via 3 AST passes, MemoryDim classifier, APD with 5 access patterns, CFE with 7 frequencies). Cross-audit integration layer consumes JSON from 6 existing audit scripts + the type registry. Decomposition-cost heuristic per aggregate (4 directions: componentize, unify, hold, insufficient_data). Output: 13 per-aggregate files (10+3 × `.dsl` + `.md` + `.tree`) + 4 top-level rollups (`summary.md`, `cross_audit_summary.md`, `decomposition_matrix.md`, `candidates.md`). 11 public functions, all return `Result[T]` per `error_handling.md` hard rule. TDD red-first protocol per `conductor/workflow.md`. + +**Tech Stack:** Python 3.11+, `ast` (stdlib), `pathlib` (stdlib), `json` (stdlib), `dataclasses` (stdlib), `tomllib` (stdlib, Python 3.11+), `tomli_w` (already in tech stack), `re` (stdlib for the DSL tokenizer). No new pip dependencies. The new file `src/code_path_audit.py` follows the project's 1-space indentation, CRLF line endings, no-comments-in-source, type-hints-required, Result-returns-for-fallible-functions conventions. + +--- + +## File Structure + +**Created:** +- `src/code_path_audit.py` — the v2 audit tool (conductor + 4 analyzers + 4 renderers; ~1,500 lines) +- `tests/test_code_path_audit.py` — 60 unit tests + 7 integration tests (~1,200 lines) +- `tests/test_code_path_audit_live_gui.py` — 2 opt-in live_gui E2E tests (~150 lines) +- `tests/fixtures/synthetic_src/__init__.py` — empty (marks the fixture as a package) +- `tests/fixtures/synthetic_src/type_aliases.py` — defines 3 TypeAliases (~30 lines) +- `tests/fixtures/synthetic_src/ai_client.py` — 1 producer of Metadata + 1 consumer (~80 lines) +- `tests/fixtures/synthetic_src/aggregate.py` — 1 producer of FileItems + 1 consumer (~80 lines) +- `tests/fixtures/synthetic_src/gui_2.py` — 1 hot-path consumer of FileItems (~60 lines) +- `tests/fixtures/synthetic_src/cleanup.py` — 1 cold-path consumer of Metadata (~40 lines) +- `tests/fixtures/synthetic_src/overrides.toml` — manual frequency override for cleanup.py (~10 lines) +- `tests/fixtures/audit_inputs/audit_weak_types.json` — 4 findings (~20 lines) +- `tests/fixtures/audit_inputs/audit_exception_handling.json` — 2 findings (~15 lines) +- `tests/fixtures/audit_inputs/audit_optional_in_3_files.json` — 0 findings (~5 lines) +- `tests/fixtures/audit_inputs/audit_no_models_config_io.json` — 0 findings (~5 lines) +- `tests/fixtures/audit_inputs/audit_main_thread_imports.json` — 0 findings (~5 lines) +- `tests/fixtures/audit_inputs/type_registry.json` — 3 aggregates' field sets (~50 lines) +- `scripts/audit_code_path_audit_coverage.py` — meta-audit schema validator (~150 lines) +- `conductor/code_styleguides/code_path_audit.md` — new styleguide (~200 lines) +- `conductor/tracks/code_path_audit_20260607/plan_v2.md` — this file +- `conductor/tracks/code_path_audit_20260607/state.toml` — the track state (~40 lines) +- `docs/reports/code_path_audit/2026-06-22/` — the audit output (generated by the tool at run time) + +**Modified:** +- `scripts/audit_optional_in_3_files.py` — 1-line extension to add `src/code_path_audit.py` to the baseline list + +**Preserved (v1):** +- `conductor/tracks/code_path_audit_20260607/spec.md` — the v1 spec (unchanged) +- `conductor/tracks/code_path_audit_20260607/plan.md` — the v1 plan (unchanged, never executed) + +--- + +## Phase 0: Setup + +**Files:** `conductor/tracks/code_path_audit_20260607/state.toml` (create), `src/code_path_audit.py` (create empty), `tests/test_code_path_audit.py` (create empty), `tests/test_code_path_audit_live_gui.py` (create empty), `scripts/audit_code_path_audit_coverage.py` (create empty), `conductor/code_styleguides/code_path_audit.md` (create empty), the synthetic_src/ + audit_inputs/ fixture dirs. + +### Task 0.1: Create `state.toml` + +**Files:** +- Create: `conductor/tracks/code_path_audit_20260607/state.toml` + +- [ ] **Step 1: Write `state.toml`** + +```toml +# Track state for code_path_audit_20260607 +# v2 supersedes v1; spec_v2.md is the canonical spec (2026-06-22) +# Updated by Tier 2 Tech Lead as tasks complete + +[meta] +track_id = "code_path_audit_20260607" +name = "Code Path & Data Pipeline Audit v2" +status = "active" +current_phase = 0 +last_updated = "2026-06-22" + +[phases] +phase_0 = { status = "in_progress", checkpointsha = "", name = "Setup" } +phase_1 = { status = "pending", checkpointsha = "", name = "Data model (AggregateProfile + 9 supporting dataclasses + 5 enums)" } +phase_2 = { status = "pending", checkpointsha = "", name = "PCG (3 AST passes: P1 return types, P2 parameter types, P3 field access)" } +phase_3 = { status = "pending", checkpointsha = "", name = "MemoryDim classifier" } +phase_4 = { status = "pending", checkpointsha = "", name = "APD (5 access patterns + 25% dominance rule)" } +phase_5 = { status = "pending", checkpointsha = "", name = "CFE (7 frequencies + entry-point detection + override file)" } +phase_6 = { status = "pending", checkpointsha = "", name = "Decomposition cost (4 directions + auto-generated rationale)" } +phase_7 = { status = "pending", checkpointsha = "", name = "Cross-audit integration (6 input JSONs + 3-tier mapping)" } +phase_8 = { status = "pending", checkpointsha = "", name = "v2 DSL (14 new tagged words + flat-section format)" } +phase_9 = { status = "pending", checkpointsha = "", name = "run_audit() main entry + CLI + MCP tool" } +phase_10 = { status = "pending", checkpointsha = "", name = "Integration tests (synthetic src/ + audit_inputs/ fixtures)" } +phase_11 = { status = "pending", checkpointsha = "", name = "Live_gui E2E tests (opt-in)" } +phase_12 = { status = "pending", checkpointsha = "", name = "Meta-audit + 1-line extension + styleguide" } +phase_13 = { status = "pending", checkpointsha = "", name = "End-of-track report + tracks.md update" } + +[verification] +data_model_tests_passing = false +pcg_tests_passing = false +memory_dim_tests_passing = false +apd_tests_passing = false +cfe_tests_passing = false +decomposition_cost_tests_passing = false +cross_audit_integration_tests_passing = false +v2_dsl_tests_passing = false +renderers_tests_passing = false +integration_tests_passing = false +live_gui_tests_passing = false +meta_audit_passing = false +all_4_audit_gates_passing = false +type_registry_check_passing = false +audit_run_completed = false +summary_md_approved = false +optimization_candidates_md_approved = false +``` + +- [ ] **Step 2: Commit** + +```bash +git add conductor/tracks/code_path_audit_20260607/state.toml +git commit -m "conductor(state): code_path_audit_20260607 v2 - state.toml with 14 phases + +Replaces the missing v1 state (v1 was never executed; v1's spec.md ++ plan.md are preserved unchanged). 14 phases mirror the v2 spec +sections: data model, PCG, MemoryDim, APD, CFE, decomposition +cost, cross-audit integration, v2 DSL, run_audit+CLI+MCP, +integration tests, live_gui, meta-audit+extension+styleguide, +end-of-track report. Each phase has its own checkpoint slot." +``` + +### Task 0.2: Create empty `src/code_path_audit.py` + +**Files:** +- Create: `src/code_path_audit.py` + +- [ ] **Step 1: Create the empty file with a module docstring only** + +```python +"""Code path & data pipeline audit tool v2. + +Builds a Producer-Consumer Graph (PCG) of src/, classifies each +data aggregate by MemoryDim, detects the access pattern (APD), +estimates the call frequency (CFE), computes the decomposition +cost (componentize vs unify), cross-validates with 6 existing +audit scripts, and emits per-aggregate profiles in the v2 custom +postfix DSL + markdown + prefix tree text. See +conductor/tracks/code_path_audit_20260607/spec_v2.md. +""" +from __future__ import annotations +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/code_path_audit.py +git commit -m "chore(audit): create empty src/code_path_audit.py v2 + +Module docstring + from __future__ import annotations. No code +yet; the data model goes in next (Phase 1)." +``` + +### Task 0.3: Create empty `tests/test_code_path_audit.py` + +**Files:** +- Create: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Create the empty file with a module docstring only** + +```python +"""Tests for src.code_path_audit v2. + +60 unit tests (Phases 1-8) + 7 integration tests (Phase 10) + +2 live_gui E2E tests (Phase 11, opt-in via env var +CODE_PATH_AUDIT_LIVE_GUI=1) = 69 tests total. See +conductor/tracks/code_path_audit_20260607/plan_v2.md. +""" +from __future__ import annotations +``` + +- [ ] **Step 2: Commit** + +```bash +git add tests/test_code_path_audit.py +git commit -m "chore(audit): create empty tests/test_code_path_audit.py v2 + +Module docstring + from __future__ import annotations. No tests +yet; the data model tests go in next (Phase 1)." +``` + +### Task 0.4: Create empty `tests/test_code_path_audit_live_gui.py` + +**Files:** +- Create: `tests/test_code_path_audit_live_gui.py` + +- [ ] **Step 1: Create the empty file with a module docstring only** + +```python +"""Live_gui E2E tests for src.code_path_audit v2 (opt-in). + +These tests are gated on the CODE_PATH_AUDIT_LIVE_GUI env var. +Set CODE_PATH_AUDIT_LIVE_GUI=1 to enable. The tests use the +session-scoped live_gui fixture from tests/conftest.py to spin +up the full app with --enable-test-hooks on port 8999, then +invoke the code_path_audit_v2 MCP tool via ApiHookClient. + +Per the live_gui test authoring contract (see +docs/guide_testing.md#authoring-robust-live_gui-tests-dont-assume-clean-state), +the tests use poll-until-state-visible rather than time.sleep, +because the v2 audit's MCP tool may run async via +_pending_gui_tasks dispatch. +""" +from __future__ import annotations +import os + +import pytest + +LIVE_GUI_ENABLED = os.environ.get("CODE_PATH_AUDIT_LIVE_GUI") == "1" +pytestmark = pytest.mark.skipif( + not LIVE_GUI_ENABLED, + reason="live_gui E2E test; set CODE_PATH_AUDIT_LIVE_GUI=1 to enable", +) +``` + +- [ ] **Step 2: Commit** + +```bash +git add tests/test_code_path_audit_live_gui.py +git commit -m "chore(audit): create empty tests/test_code_path_audit_live_gui.py v2 + +Module docstring + skipif gate on CODE_PATH_AUDIT_LIVE_GUI=1. +The 2 live_gui tests go in Phase 11." +``` + +### Task 0.5: Create empty `scripts/audit_code_path_audit_coverage.py` + +**Files:** +- Create: `scripts/audit_code_path_audit_coverage.py` + +- [ ] **Step 1: Create the empty file with a module docstring only** + +```python +"""Meta-audit for src.code_path_audit v2 output schema. + +Verifies that every AggregateProfile produced by the v2 audit +has all 14 required top-level fields, that the +cross_audit_findings has 5 audit scripts, that the +decomposition_cost has the 8 fields, etc. The convention: any +new audit must have a corresponding schema-validator script. + +Usage: + uv run python scripts/audit_code_path_audit_coverage.py + uv run python scripts/audit_code_path_audit_coverage.py --strict +""" +from __future__ import annotations +``` + +- [ ] **Step 2: Commit** + +```bash +git add scripts/audit_code_path_audit_coverage.py +git commit -m "chore(audit): create empty scripts/audit_code_path_audit_coverage.py + +Module docstring + usage comment. The schema validator goes in +Phase 12." +``` + +### Task 0.6: Create empty `conductor/code_styleguides/code_path_audit.md` + +**Files:** +- Create: `conductor/code_styleguides/code_path_audit.md` + +- [ ] **Step 1: Create the empty file with a stub styleguide header** + +```markdown +# Code Path & Data Pipeline Audit Styleguide + +> **Status:** Active convention as of 2026-06-22. Established by the `code_path_audit_20260607` v2 track. + +This styleguide codifies the contract for `src/code_path_audit.py` v2 and the 6 input audit scripts it consumes. Companion to `data_oriented_design.md`, `error_handling.md`, `type_aliases.md`, and `agent_memory_dimensions.md`. + +## The 5 Conventions (to be filled in Phase 12) + +1. **Per-aggregate profile structure** (the 14 top-level fields). +2. **The 4 decomposition directions** (componentize, unify, hold, insufficient_data). +3. **The override file format** (`scripts/code_path_audit_overrides.toml`). +4. **The 4 mem dim classification rules** (curation, discussion, rag, knowledge, config, control, unknown). +5. **The cross-audit integration contract** (the 6 input JSON shapes; the 3-tier finding-to-aggregate mapping). + +## See Also + +- `conductor/tracks/code_path_audit_20260607/spec_v2.md` — the canonical spec +- `conductor/tracks/code_path_audit_20260607/plan_v2.md` — the canonical plan +- `conductor/code_styleguides/data_oriented_design.md` — the canonical DOD reference +- `conductor/code_styleguides/error_handling.md` — the `Result[T]` convention +- `conductor/code_styleguides/type_aliases.md` — the 10 TypeAliases + 1 NamedTuple +- `conductor/code_styleguides/agent_memory_dimensions.md` — the 4 mem dims +``` + +- [ ] **Step 2: Commit** + +```bash +git add conductor/code_styleguides/code_path_audit.md +git commit -m "docs(styleguide): create stub conductor/code_styleguides/code_path_audit.md + +5-convention outline. The full styleguide content goes in +Phase 12 (with the meta-audit + the 1-line extension)." +``` + +### Task 0.7: Create the fixture directories + +**Files:** +- Create: `tests/fixtures/synthetic_src/__init__.py` (empty) +- Create: `tests/fixtures/audit_inputs/.gitkeep` (placeholder; the 6 JSON files go in Phase 10) + +- [ ] **Step 1: Create `tests/fixtures/synthetic_src/__init__.py`** + +```python +"""Synthetic src/ fixture for code_path_audit v2 integration tests. + +Defines 3 TypeAliases (Metadata, FileItems, History) + 6 functions +(2 producers, 4 consumers). The integration tests assert the +exact expected profiles per aggregate. See +conductor/tracks/code_path_audit_20260607/plan_v2.md Phase 10. +""" +from __future__ import annotations +``` + +- [ ] **Step 2: Create `tests/fixtures/audit_inputs/.gitkeep`** + +```text +This file ensures tests/fixtures/audit_inputs/ is tracked in git. The 6 input JSON files (audit_weak_types.json, audit_exception_handling.json, audit_optional_in_3_files.json, audit_no_models_config_io.json, audit_main_thread_imports.json, type_registry.json) are populated in Phase 10. The .gitkeep is removed when the first JSON file is added. +``` + +- [ ] **Step 3: Commit** + +```bash +git add tests/fixtures/synthetic_src/__init__.py tests/fixtures/audit_inputs/.gitkeep +git commit -m "chore(audit): create empty fixture directories + +tests/fixtures/synthetic_src/ holds the 6-file synthetic src/ +that the integration tests run against. tests/fixtures/audit_inputs/ +holds the 6 input JSON files (populated in Phase 10)." +``` + +### Task 0.8: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 0 setup is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 1. + +--- + +## Phase 1: Data model (AggregateProfile + 9 supporting dataclasses + 5 enums) + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +This phase is **one commit per dataclass** (TDD red-green-refactor per task). 10 commits total. All 10 commits land on master as a single phase checkpoint; the per-task commit discipline means each commit is a safe rollback point. + +### Task 1.1: Add the 5 enums (Literal types) + +**Files:** +- Modify: `src/code_path_audit.py` (append after the module docstring) +- Test: `tests/test_code_path_audit.py` (append) + +- [ ] **Step 1: Write the failing tests in `tests/test_code_path_audit.py`** + +```python +"""Tests for src.code_path_audit v2 — Phase 1 (data model).""" +from src.code_path_audit import ( + AggregateKind, + MemoryDim, + AccessPattern, + Frequency, + RecommendedDirection, +) + +def test_aggregate_kind_4_values() -> None: + """AggregateKind is a Literal with 4 values: typealias, dataclass, candidate_dataclass, builtin.""" + expected = {"typealias", "dataclass", "candidate_dataclass", "builtin"} + assert set(AggregateKind.__args__) == expected + +def test_memory_dim_7_values() -> None: + """MemoryDim is a Literal with 7 values: curation, discussion, rag, knowledge, config, control, unknown.""" + expected = {"curation", "discussion", "rag", "knowledge", "config", "control", "unknown"} + assert set(MemoryDim.__args__) == expected + +def test_access_pattern_5_values() -> None: + """AccessPattern is a Literal with 5 values: whole_struct, field_by_field, hot_cold_split, bulk_batched, mixed.""" + expected = {"whole_struct", "field_by_field", "hot_cold_split", "bulk_batched", "mixed"} + assert set(AccessPattern.__args__) == expected + +def test_frequency_7_values() -> None: + """Frequency is a Literal with 7 values: hot, per_turn, per_discussion, per_request, cold, init, unknown.""" + expected = {"hot", "per_turn", "per_discussion", "per_request", "cold", "init", "unknown"} + assert set(Frequency.__args__) == expected + +def test_recommended_direction_4_values() -> None: + """RecommendedDirection is a Literal with 4 values: componentize, unify, hold, insufficient_data.""" + expected = {"componentize", "unify", "hold", "insufficient_data"} + assert set(RecommendedDirection.__args__) == expected +``` + +- [ ] **Step 2: Run tests to verify they fail (ImportError: cannot import name 'AggregateKind')** + +Run: `uv run pytest tests/test_code_path_audit.py -v 2>&1 | Select-Object -Last 15` +Expected: 5 errors, all `ImportError: cannot import name 'AggregateKind' from 'src.code_path_audit'`. + +- [ ] **Step 3: Implement the 5 enums in `src/code_path_audit.py`** + +Append to `src/code_path_audit.py`: + +```python +from typing import Literal + +AggregateKind = Literal[ + "typealias", + "dataclass", + "candidate_dataclass", + "builtin", +] + +MemoryDim = Literal[ + "curation", + "discussion", + "rag", + "knowledge", + "config", + "control", + "unknown", +] + +AccessPattern = Literal[ + "whole_struct", + "field_by_field", + "hot_cold_split", + "bulk_batched", + "mixed", +] + +Frequency = Literal[ + "hot", + "per_turn", + "per_discussion", + "per_request", + "cold", + "init", + "unknown", +] + +RecommendedDirection = Literal[ + "componentize", + "unify", + "hold", + "insufficient_data", +] +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v 2>&1 | Select-Object -Last 10` +Expected: 5 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add 5 enums for the v2 data model + +AggregateKind (4 values), MemoryDim (7), AccessPattern (5), +Frequency (7), RecommendedDirection (4). All Literal types +for stable postfix DSL output (string-valued, no enum-name +lookup table needed in the parser). + +5 unit tests passing. The 9 supporting dataclasses + the +AggregateProfile central artifact go in Tasks 1.2-1.10." +``` + +### Task 1.2: Add the `FunctionRef` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test in `tests/test_code_path_audit.py`** + +```python +from src.code_path_audit import FunctionRef + +def test_function_ref_4_fields() -> None: + """FunctionRef has fqname, file, line, role (per spec).""" + ref = FunctionRef( + fqname="src.ai_client.AIClient.send_result", + file="src/ai_client.py", + line=100, + role="producer", + ) + assert ref.fqname == "src.ai_client.AIClient.send_result" + assert ref.file == "src/ai_client.py" + assert ref.line == 100 + assert ref.role == "producer" + +def test_function_ref_frozen() -> None: + """FunctionRef is frozen (immutability per error_handling.md).""" + ref = FunctionRef( + fqname="src.x.y", + file="src/x.py", + line=1, + role="consumer", + ) + with pytest.raises((AttributeError, Exception)) as exc_info: + ref.fqname = "src.z.w" + assert "frozen" in str(exc_info.value).lower() or "cannot assign" in str(exc_info.value).lower() +``` + +- [ ] **Step 2: Run tests to verify they fail (ImportError: cannot import name 'FunctionRef')** + +Run: `uv run pytest tests/test_code_path_audit.py::test_function_ref_4_frozen tests/test_code_path_audit.py::test_function_ref_4_fields -v 2>&1 | Select-Object -Last 10` +Expected: 2 errors, both `ImportError`. + +- [ ] **Step 3: Implement `FunctionRef` in `src/code_path_audit.py`** + +Append: + +```python +from dataclasses import dataclass + +@dataclass(frozen=True) +class FunctionRef: + fqname: str + file: str + line: int + role: str +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_function_ref_4_fields tests/test_code_path_audit.py::test_function_ref_frozen -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add FunctionRef dataclass (frozen, 4 fields) + +fqname, file, line, role. Used in ProducerConsumerGraph edges +and per-aggregate producer/consumer lists. Per error_handling.md +Pattern 1 (immutability for cross-thread safety). +2 unit tests passing." +``` + +### Task 1.3: Add the `AccessPatternEvidence` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import AccessPatternEvidence, FunctionRef + +def test_access_pattern_evidence_4_fields() -> None: + """AccessPatternEvidence has function, pattern, field_accesses, confidence.""" + ref = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="consumer") + ev = AccessPatternEvidence( + function=ref, + pattern="field_by_field", + field_accesses={"path": 3, "view_mode": 2}, + confidence="high", + ) + assert ev.function is ref + assert ev.pattern == "field_by_field" + assert ev.field_accesses == {"path": 3, "view_mode": 2} + assert ev.confidence == "high" +``` + +- [ ] **Step 2: Run tests to verify they fail (ImportError)** + +Run: `uv run pytest tests/test_code_path_audit.py::test_access_pattern_evidence_4_fields -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `AccessPatternEvidence`** + +```python +@dataclass(frozen=True) +class AccessPatternEvidence: + function: FunctionRef + pattern: AccessPattern + field_accesses: dict[str, int] + confidence: str +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_access_pattern_evidence_4_fields -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add AccessPatternEvidence dataclass (frozen, 4 fields) + +function, pattern, field_accesses, confidence. Per-aggregate +access pattern evidence; the APD populates this in Phase 4. +1 unit test passing." +``` + +### Task 1.4: Add the `FrequencyEvidence` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import FrequencyEvidence, FunctionRef + +def test_frequency_evidence_4_fields() -> None: + """FrequencyEvidence has function, frequency, source, note (default '').""" + ref = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="both") + ev = FrequencyEvidence( + function=ref, + frequency="per_turn", + source="entry_point", + note="called per LLM turn", + ) + assert ev.function is ref + assert ev.frequency == "per_turn" + assert ev.source == "entry_point" + assert ev.note == "called per LLM turn" + +def test_frequency_evidence_default_note() -> None: + """FrequencyEvidence.note defaults to ''.""" + ref = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="consumer") + ev = FrequencyEvidence(function=ref, frequency="cold", source="control_flow_position") + assert ev.note == "" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_frequency_evidence_4_fields tests/test_code_path_audit.py::test_frequency_evidence_default_note -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `FrequencyEvidence`** + +```python +@dataclass(frozen=True) +class FrequencyEvidence: + function: FunctionRef + frequency: Frequency + source: str + note: str = "" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_frequency_evidence_4_fields tests/test_code_path_audit.py::test_frequency_evidence_default_note -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add FrequencyEvidence dataclass (frozen, 4 fields, note default '') + +function, frequency, source, note. Per-aggregate frequency +evidence; the CFE populates this in Phase 5. +2 unit tests passing." +``` + +### Task 1.5: Add the `ResultCoverage` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ResultCoverage + +def test_result_coverage_5_fields() -> None: + """ResultCoverage has total_producers, result_producers, total_consumers, result_consumers, summary.""" + cov = ResultCoverage( + total_producers=12, + result_producers=5, + total_consumers=15, + result_consumers=8, + summary="5/12 producers return Result[T] (42%); 8/15 consumers branch on .errors (53%)", + ) + assert cov.total_producers == 12 + assert cov.result_producers == 5 + assert cov.total_consumers == 15 + assert cov.result_consumers == 8 + assert "42%" in cov.summary + assert "53%" in cov.summary +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_result_coverage_5_fields -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `ResultCoverage`** + +```python +@dataclass(frozen=True) +class ResultCoverage: + total_producers: int + result_producers: int + total_consumers: int + result_consumers: int + summary: str +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_result_coverage_5_fields -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add ResultCoverage dataclass (frozen, 5 fields) + +total_producers, result_producers, total_consumers, +result_consumers, summary. The cross-check of +data_oriented_error_handling_20260606: an aggregate with low +result_producers/total_producers is a migration candidate. +1 unit test passing." +``` + +### Task 1.6: Add the `TypeAliasCoverage` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import TypeAliasCoverage + +def test_type_alias_coverage_4_fields() -> None: + """TypeAliasCoverage has total_sites, typed_sites, untyped_sites, summary.""" + cov = TypeAliasCoverage( + total_sites=45, + typed_sites=38, + untyped_sites=7, + summary="45 total sites; 38 typed (84%); 7 untyped (16%)", + ) + assert cov.total_sites == 45 + assert cov.typed_sites == 38 + assert cov.untyped_sites == 7 + assert "84%" in cov.summary + assert "16%" in cov.summary +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_type_alias_coverage_4_fields -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `TypeAliasCoverage`** + +```python +@dataclass(frozen=True) +class TypeAliasCoverage: + total_sites: int + typed_sites: int + untyped_sites: int + summary: str +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_type_alias_coverage_4_fields -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add TypeAliasCoverage dataclass (frozen, 4 fields) + +total_sites, typed_sites, untyped_sites, summary. The +cross-check of data_structure_strengthening_20260606: an +aggregate with high untyped_sites is a propagation candidate. +1 unit test passing." +``` + +### Task 1.7: Add the `CrossAuditFinding` + `CrossAuditFindings` dataclasses + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import CrossAuditFinding, CrossAuditFindings + +def test_cross_audit_finding_5_fields() -> None: + """CrossAuditFinding has audit_script, site_count, example_file, example_line, note (default '').""" + finding = CrossAuditFinding( + audit_script="audit_weak_types", + site_count=12, + example_file="src/ai_client.py", + example_line=100, + note="12 weak-type sites in producer+consumer functions", + ) + assert finding.audit_script == "audit_weak_types" + assert finding.site_count == 12 + assert finding.example_file == "src/ai_client.py" + assert finding.example_line == 100 + assert finding.note == "12 weak-type sites in producer+consumer functions" + +def test_cross_audit_finding_default_note() -> None: + """CrossAuditFinding.note defaults to ''.""" + finding = CrossAuditFinding( + audit_script="audit_optional_in_3_files", + site_count=0, + example_file="", + example_line=0, + ) + assert finding.note == "" + +def test_cross_audit_findings_5_audit_scripts() -> None: + """CrossAuditFindings has 5 audit-script fields, each a tuple of CrossAuditFinding.""" + findings = CrossAuditFindings( + weak_types=(), + exception_handling=(), + optional_in_baseline=(), + config_io_ownership=(), + import_graph=(), + ) + assert findings.weak_types == () + assert findings.exception_handling == () + assert findings.optional_in_baseline == () + assert findings.config_io_ownership == () + assert findings.import_graph == () +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_cross_audit_finding_5_fields tests/test_code_path_audit.py::test_cross_audit_finding_default_note tests/test_code_path_audit.py::test_cross_audit_findings_5_audit_scripts -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `CrossAuditFinding` + `CrossAuditFindings`** + +```python +@dataclass(frozen=True) +class CrossAuditFinding: + audit_script: str + site_count: int + example_file: str + example_line: int + note: str = "" + +@dataclass(frozen=True) +class CrossAuditFindings: + weak_types: tuple[CrossAuditFinding, ...] + exception_handling: tuple[CrossAuditFinding, ...] + optional_in_baseline: tuple[CrossAuditFinding, ...] + config_io_ownership: tuple[CrossAuditFinding, ...] + import_graph: tuple[CrossAuditFinding, ...] +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_cross_audit_finding_5_fields tests/test_code_path_audit.py::test_cross_audit_finding_default_note tests/test_code_path_audit.py::test_cross_audit_findings_5_audit_scripts -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add CrossAuditFinding + CrossAuditFindings (frozen) + +CrossAuditFinding: 5 fields (audit_script, site_count, +example_file, example_line, note default ''). +CrossAuditFindings: 5 fields, one per input audit script +(weak_types, exception_handling, optional_in_baseline, +config_io_ownership, import_graph), each a tuple of +CrossAuditFinding. +3 unit tests passing." +``` + +### Task 1.8: Add the `DecompositionCost` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import DecompositionCost + +def test_decomposition_cost_8_fields() -> None: + """DecompositionCost has 8 fields per spec.""" + cost = DecompositionCost( + current_cost_estimate=1500, + componentize_savings=450, + unify_savings=0, + recommended_direction="hold", + recommended_rationale="whole_struct access on a frozen dataclass; current shape is correct", + batch_size=None, + struct_field_count=8, + struct_frozen=True, + ) + assert cost.current_cost_estimate == 1500 + assert cost.componentize_savings == 450 + assert cost.unify_savings == 0 + assert cost.recommended_direction == "hold" + assert "frozen" in cost.recommended_rationale + assert cost.batch_size is None + assert cost.struct_field_count == 8 + assert cost.struct_frozen is True +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_decomposition_cost_8_fields -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `DecompositionCost`** + +```python +@dataclass(frozen=True) +class DecompositionCost: + current_cost_estimate: int + componentize_savings: int + unify_savings: int + recommended_direction: RecommendedDirection + recommended_rationale: str + batch_size: int | None + struct_field_count: int + struct_frozen: bool +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_decomposition_cost_8_fields -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add DecompositionCost dataclass (frozen, 8 fields) + +current_cost_estimate, componentize_savings, unify_savings, +recommended_direction, recommended_rationale, batch_size, +struct_field_count, struct_frozen. The central artifact for +the decomposition cost heuristic (computed in Phase 6). +1 unit test passing." +``` + +### Task 1.9: Add the `OptimizationCandidate` dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import OptimizationCandidate + +def test_optimization_candidate_7_fields() -> None: + """OptimizationCandidate has 7 fields per spec.""" + cand = OptimizationCandidate( + candidate="Migrate 7 producers of Metadata to Result[Metadata]", + direction="componentize", + affected_files=("src/ai_client.py", "src/app_controller.py", "src/history.py"), + estimated_savings_us=500, + effort="small", + priority="high", + cross_ref="docs/reports/EXCEPTION_HANDLING_AUDIT_20260616.md", + ) + assert "Migrate" in cand.candidate + assert cand.direction == "componentize" + assert len(cand.affected_files) == 3 + assert cand.estimated_savings_us == 500 + assert cand.effort == "small" + assert cand.priority == "high" + assert "EXCEPTION_HANDLING_AUDIT" in cand.cross_ref +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_optimization_candidate_7_fields -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `OptimizationCandidate`** + +```python +@dataclass(frozen=True) +class OptimizationCandidate: + candidate: str + direction: RecommendedDirection + affected_files: tuple[str, ...] + estimated_savings_us: int + effort: str + priority: str + cross_ref: str = "" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_optimization_candidate_7_fields -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add OptimizationCandidate dataclass (frozen, 7 fields) + +candidate, direction, affected_files, estimated_savings_us, +effort, priority, cross_ref (default ''). Per-aggregate +optimization candidate; the run_audit() synthesis populates +this in Phase 9. +1 unit test passing." +``` + +### Task 1.10: Add the `AggregateProfile` central dataclass + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ( + AggregateProfile, + FunctionRef, + AccessPatternEvidence, + FrequencyEvidence, + ResultCoverage, + TypeAliasCoverage, + CrossAuditFindings, + DecompositionCost, + OptimizationCandidate, +) + +def test_aggregate_profile_14_fields() -> None: + """AggregateProfile has 14 top-level fields (per spec section 7.1).""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = AggregateProfile( + name="Metadata", + aggregate_kind="typealias", + memory_dim="discussion", + producers=(f,), + consumers=(f,), + access_pattern="field_by_field", + access_pattern_evidence=(AccessPatternEvidence( + function=f, pattern="field_by_field", field_accesses={"role": 3}, confidence="high" + ),), + frequency="per_turn", + frequency_evidence=(FrequencyEvidence( + function=f, frequency="per_turn", source="entry_point", note="per LLM turn" + ),), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "hold", "no data", None, 0, False), + optimization_candidates=(), + is_candidate=False, + ) + assert profile.name == "Metadata" + assert profile.aggregate_kind == "typealias" + assert profile.memory_dim == "discussion" + assert len(profile.producers) == 1 + assert len(profile.consumers) == 1 + assert profile.access_pattern == "field_by_field" + assert len(profile.access_pattern_evidence) == 1 + assert profile.frequency == "per_turn" + assert len(profile.frequency_evidence) == 1 + assert profile.is_candidate is False + +def test_aggregate_profile_is_candidate_true() -> None: + """AggregateProfile.is_candidate=True for the 3 candidate aggregates.""" + profile = AggregateProfile( + name="ChatMessage", + aggregate_kind="candidate_dataclass", + memory_dim="discussion", + producers=(), + consumers=(), + access_pattern="mixed", + access_pattern_evidence=(), + frequency="unknown", + frequency_evidence=(), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "insufficient_data", "candidate", None, 0, False), + optimization_candidates=(), + is_candidate=True, + ) + assert profile.is_candidate is True + assert profile.aggregate_kind == "candidate_dataclass" + assert profile.producers == () + assert profile.consumers == () +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_aggregate_profile_14_fields tests/test_code_path_audit.py::test_aggregate_profile_is_candidate_true -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `AggregateProfile`** + +```python +@dataclass(frozen=True) +class AggregateProfile: + name: str + aggregate_kind: AggregateKind + memory_dim: MemoryDim + producers: tuple[FunctionRef, ...] + consumers: tuple[FunctionRef, ...] + access_pattern: AccessPattern + access_pattern_evidence: tuple[AccessPatternEvidence, ...] + frequency: Frequency + frequency_evidence: tuple[FrequencyEvidence, ...] + result_coverage: ResultCoverage + type_alias_coverage: TypeAliasCoverage + cross_audit_findings: CrossAuditFindings + decomposition_cost: DecompositionCost + optimization_candidates: tuple[OptimizationCandidate, ...] + is_candidate: bool + mermaid: str = "" + markdown: str = "" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_aggregate_profile_14_fields tests/test_code_path_audit.py::test_aggregate_profile_is_candidate_true -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add AggregateProfile central dataclass (frozen, 14 fields + 2 default) + +The central artifact of the v2 audit. The 14 required fields +(name, aggregate_kind, memory_dim, producers, consumers, +access_pattern, access_pattern_evidence, frequency, +frequency_evidence, result_coverage, type_alias_coverage, +cross_audit_findings, decomposition_cost, +optimization_candidates, is_candidate) + 2 optional fields +with defaults (mermaid, markdown). The is_candidate flag +distinguishes the 3 placeholder aggregates (ChatMessage, +ToolSpec, ProviderHistory) from the 10 real aggregates. +2 unit tests passing. + +Phase 1 complete: 19 unit tests passing (5 enum tests + 14 +dataclass tests). Phase 2 (PCG) next." +``` + +### Task 1.11: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 1 data model is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 2. + +--- + +## Phase 2: PCG (Producer-Consumer Graph) — 3 AST passes + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The PCG is the v2 audit's pipeline-discovery primitive. 3 AST passes over `src/`: +- P1: return-type pass (producers of `T` and `Result[T]`) +- P2: parameter-type pass (consumers of typed aggregates) +- P3: field-access pass (consumers via `entry['key']` / `entry.attr`) + +7 unit tests; 1 commit per task; 8 tasks total. + +### Task 2.1: Add the `ProducerConsumerGraph` skeleton + the 3 pass helpers + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ProducerConsumerGraph + +def test_pcg_init_empty() -> None: + """ProducerConsumerGraph starts with empty edges and producers/consumers dicts.""" + pcg = ProducerConsumerGraph() + assert pcg.edges == {} + assert pcg.producers == {} + assert pcg.consumers == {} + +def test_pcg_add_producer_consumer() -> None: + """add_producer + add_consumer add to the bipartite graph.""" + pcg = ProducerConsumerGraph() + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + pcg.add_producer("Metadata", f) + pcg.add_consumer("Metadata", f) + assert "Metadata" in pcg.producers + assert "Metadata" in pcg.consumers + assert f in pcg.producers["Metadata"] + assert f in pcg.consumers["Metadata"] +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_pcg_init_empty tests/test_code_path_audit.py::test_pcg_add_producer_consumer -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `ProducerConsumerGraph`** + +```python +@dataclass +class ProducerConsumerGraph: + """Bipartite graph: aggregates <-> functions. + + producers[aggregate] = set of FunctionRef that produce the aggregate. + consumers[aggregate] = set of FunctionRef that consume the aggregate. + edges[(producer, consumer)] = set of aggregates flowing between them. + """ + edges: dict[tuple[str, str], set[str]] = field(default_factory=dict) + producers: dict[str, set[FunctionRef]] = field(default_factory=dict) + consumers: dict[str, set[FunctionRef]] = field(default_factory=dict) + + def add_producer(self, aggregate: str, function: FunctionRef) -> None: + self.producers.setdefault(aggregate, set()).add(function) + + def add_consumer(self, aggregate: str, function: FunctionRef) -> None: + self.consumers.setdefault(aggregate, set()).add(function) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_pcg_init_empty tests/test_code_path_audit.py::test_pcg_add_producer_consumer -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add ProducerConsumerGraph skeleton + +Bipartite graph: aggregates <-> functions. producers and +consumers dicts keyed by aggregate name. The 3 AST passes +(P1, P2, P3) populate this in Tasks 2.2-2.4. +2 unit tests passing." +``` + +### Task 2.2: Implement P1 (return-type pass) — producers of `T` and `Result[T]` + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import ast +import textwrap +from src.code_path_audit import P1_pass + +def test_p1_pass_finds_producer_of_T() -> None: + """P1 detects a function whose return annotation is a TypeAlias name (producer of T).""" + source = textwrap.dedent(''' + def send_result() -> Metadata: + return {} + ''') + tree = ast.parse(source) + producers = P1_pass(tree, file="synthetic.py") + assert ("send_result", "Metadata", "producer", "high") in producers + +def test_p1_pass_finds_producer_of_Result_T() -> None: + """P1 detects a function whose return annotation is Result[T] (producer of T).""" + source = textwrap.dedent(''' + def fetch() -> Result[FileItems]: + return Result(data=[]) + ''') + tree = ast.parse(source) + producers = P1_pass(tree, file="synthetic.py") + assert ("fetch", "FileItems", "producer", "high") in producers + +def test_p1_pass_skips_non_annotated_return() -> None: + """P1 returns [] for functions without return annotations.""" + source = textwrap.dedent(''' + def unannotated(): + return {} + ''') + tree = ast.parse(source) + producers = P1_pass(tree, file="synthetic.py") + assert producers == [] +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_p1_pass_finds_producer_of_T tests/test_code_path_audit.py::test_p1_pass_finds_producer_of_Result_T tests/test_code_path_audit.py::test_p1_pass_skips_non_annotated_return -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement P1_pass** + +```python +def P1_pass(tree: ast.Module, file: str) -> list[tuple[str, str, str, str]]: + """AST pass 1: detect producers of T and Result[T] via return annotations. + + Returns: list of (function_name, aggregate_name, role, confidence). + """ + out: list[tuple[str, str, str, str]] = [] + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + if node.returns is None: + continue + ret = node.returns + if isinstance(ret, ast.Name): + aggregate = ret.id + out.append((node.name, aggregate, "producer", "high")) + elif isinstance(ret, ast.Subscript): + value = ret.value + sl = ret.slice + if isinstance(value, ast.Name) and value.id == "Result": + if isinstance(sl, ast.Name): + out.append((node.name, sl.id, "producer", "high")) + elif isinstance(sl, ast.Subscript) and isinstance(sl.value, ast.Name): + out.append((node.name, sl.value.id, "producer", "high")) + return out +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_p1_pass_finds_producer_of_T tests/test_code_path_audit.py::test_p1_pass_finds_producer_of_Result_T tests/test_code_path_audit.py::test_p1_pass_skips_non_annotated_return -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): implement PCG P1 (return-type pass) + +Detects producers of T and Result[T] via return annotations. +Returns list of (function_name, aggregate_name, role, +confidence) tuples. The P1 result feeds the producer side of +the per-aggregate profiles. +3 unit tests passing." +``` + +### Task 2.3: Implement P2 (parameter-type pass) — consumers of typed aggregates + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import ast +import textwrap +from src.code_path_audit import P2_pass + +def test_p2_pass_finds_consumer_of_T() -> None: + """P2 detects a function whose parameter is a TypeAlias name (consumer of T).""" + source = textwrap.dedent(''' + def process(entry: Metadata) -> None: + pass + ''') + tree = ast.parse(source) + consumers = P2_pass(tree, file="synthetic.py") + assert ("process", "Metadata", "consumer", "high") in consumers + +def test_p2_pass_finds_consumer_of_list_T() -> None: + """P2 detects a function whose parameter is list[T] (consumer of T).""" + source = textwrap.dedent(''' + def aggregate(items: list[FileItems]) -> None: + pass + ''') + tree = ast.parse(source) + consumers = P2_pass(tree, file="synthetic.py") + assert ("aggregate", "FileItems", "consumer", "high") in consumers + +def test_p2_pass_skips_untyped_parameter() -> None: + """P2 returns [] for parameters without type annotations.""" + source = textwrap.dedent(''' + def process(entry) -> None: + pass + ''') + tree = ast.parse(source) + consumers = P2_pass(tree, file="synthetic.py") + assert consumers == [] +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_p2_pass_finds_consumer_of_T tests/test_code_path_audit.py::test_p2_pass_finds_consumer_of_list_T tests/test_code_path_audit.py::test_p2_pass_skips_untyped_parameter -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement P2_pass** + +```python +def P2_pass(tree: ast.Module, file: str) -> list[tuple[str, str, str, str]]: + """AST pass 2: detect consumers of typed aggregates via parameter annotations. + + Returns: list of (function_name, aggregate_name, role, confidence). + """ + out: list[tuple[str, str, str, str]] = [] + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + for arg in node.args.args + node.args.kwonlyargs: + if arg.annotation is None: + continue + ann = arg.annotation + if isinstance(ann, ast.Name): + out.append((node.name, ann.id, "consumer", "high")) + elif isinstance(ann, ast.Subscript): + if isinstance(ann.value, ast.Name) and ann.value.id in ("list", "List"): + sl = ann.slice + if isinstance(sl, ast.Name): + out.append((node.name, sl.id, "consumer", "high")) + return out +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_p2_pass_finds_consumer_of_T tests/test_code_path_audit.py::test_p2_pass_finds_consumer_of_list_T tests/test_code_path_audit.py::test_p2_pass_skips_untyped_parameter -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): implement PCG P2 (parameter-type pass) + +Detects consumers of typed aggregates via parameter +annotations (including list[T] unwrapping). The P2 result +feeds the consumer side of the per-aggregate profiles. +3 unit tests passing." +``` + +### Task 2.4: Implement P3 (field-access pass) — consumers via `entry['key']` / `entry.attr` + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import ast +import textwrap +from src.code_path_audit import P3_pass + +def test_p3_pass_finds_consumer_via_subscript() -> None: + """P3 detects a function that reads entry['path']; without a type registry, returns the field name only.""" + source = textwrap.dedent(''' + def process(entry) -> None: + path = entry['path'] + ''') + tree = ast.parse(source) + accesses = P3_pass(tree, file="synthetic.py", type_registry={}) + assert ("process", "path", "subscript", 1) in accesses + +def test_p3_pass_finds_consumer_via_attribute() -> None: + """P3 detects a function that reads entry.attr; returns (function, attr, kind, count).""" + source = textwrap.dedent(''' + def process(entry) -> None: + path = entry.path + ''') + tree = ast.parse(source) + accesses = P3_pass(tree, file="synthetic.py", type_registry={}) + assert ("process", "path", "attribute", 1) in accesses + +def test_p3_pass_counts_multiple_accesses() -> None: + """P3 counts multiple accesses to the same key within a single function.""" + source = textwrap.dedent(''' + def process(entry) -> None: + a = entry['path'] + b = entry['path'] + c = entry['view_mode'] + ''') + tree = ast.parse(source) + accesses = P3_pass(tree, file="synthetic.py", type_registry={}) + path_count = sum(c for fn, k, kind, c in accesses if fn == "process" and k == "path") + assert path_count == 2 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_p3_pass_finds_consumer_via_subscript tests/test_code_path_audit.py::test_p3_pass_finds_consumer_via_attribute tests/test_code_path_audit.py::test_p3_pass_counts_multiple_accesses -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement P3_pass** + +```python +def P3_pass(tree: ast.Module, file: str, type_registry: dict[str, list[str]]) -> list[tuple[str, str, str, int]]: + """AST pass 3: detect field accesses via entry['key'] or entry.attr. + + Returns: list of (function_name, key_or_attr, kind, count). + type_registry is currently unused (the field-to-aggregate mapping + is computed in Phase 7 by the cross-audit integration); P3 only + records the field access itself. + """ + out: list[tuple[str, str, str, int]] = [] + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + counts: dict[tuple[str, str], int] = {} + for sub in ast.walk(node): + if isinstance(sub, ast.Subscript): + if isinstance(sub.value, ast.Name) and isinstance(sub.slice, ast.Constant) and isinstance(sub.slice.value, str): + k = ("subscript", sub.slice.value) + counts[k] = counts.get(k, 0) + 1 + elif isinstance(sub, ast.Attribute): + if isinstance(sub.value, ast.Name): + k = ("attribute", sub.attr) + counts[k] = counts.get(k, 0) + 1 + for (kind, key), c in counts.items(): + out.append((node.name, key, kind, c)) + return out +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_p3_pass_finds_consumer_via_subscript tests/test_code_path_audit.py::test_p3_pass_finds_consumer_via_attribute tests/test_code_path_audit.py::test_p3_pass_counts_multiple_accesses -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): implement PCG P3 (field-access pass) + +Detects field accesses via entry['key'] and entry.attr; +returns (function_name, key_or_attr, kind, count). The +type_registry parameter is currently unused; the field-to- +aggregate mapping is computed in Phase 7 by the cross-audit +integration (where the audit knows the canonical field +names per aggregate from the type registry). +3 unit tests passing." +``` + +### Task 2.5: Implement `build_pcg()` entry point + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import tempfile +import textwrap +from pathlib import Path +from src.code_path_audit import build_pcg, Result + +def test_build_pcg_returns_result() -> None: + """build_pcg returns Result[ProducerConsumerGraph] per error_handling.md.""" + with tempfile.TemporaryDirectory() as tmp: + (Path(tmp) / "mod.py").write_text(textwrap.dedent(''' + from src.type_aliases import Metadata + + def produce() -> Metadata: + return {} + ''')) + result = build_pcg(tmp) + assert isinstance(result, Result) + assert result.ok + +def test_build_pcg_finds_producer_via_p1() -> None: + """build_pcg correctly identifies a producer of Metadata via P1.""" + with tempfile.TemporaryDirectory() as tmp: + (Path(tmp) / "mod.py").write_text(textwrap.dedent(''' + from src.type_aliases import Metadata + + def produce() -> Metadata: + return {} + ''')) + pcg = build_pcg(tmp).data + assert "Metadata" in pcg.producers + +def test_build_pcg_tolerates_syntax_errors() -> None: + """build_pcg returns Result with errors for unparseable source (boundary pattern).""" + with tempfile.TemporaryDirectory() as tmp: + (Path(tmp) / "bad.py").write_text("def unclosed(:\n pass") + result = build_pcg(tmp) + assert result.ok + assert result.data is not None +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_build_pcg_returns_result tests/test_code_path_audit.py::test_build_pcg_finds_producer_via_p1 tests/test_code_path_audit.py::test_build_pcg_tolerates_syntax_errors -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `build_pcg()`** + +```python +from src.result_types import Result, ErrorInfo, ErrorKind + +def build_pcg(src_dir: str, type_registry: dict[str, list[str]] | None = None) -> Result[ProducerConsumerGraph]: + """Build the ProducerConsumerGraph by AST-walking src/. + + Returns Result[PCG]. Syntax errors in individual files are + tolerated; the file is skipped and a 0-length edge is added. + """ + pcg = ProducerConsumerGraph() + type_registry = type_registry or {} + errors: list[ErrorInfo] = [] + for py_file in Path(src_dir).rglob("*.py"): + if "__pycache__" in str(py_file): + continue + try: + source = py_file.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError) as e: + errors.append(ErrorInfo( + kind=ErrorKind.IO, + message=f"Cannot read {py_file}: {e}", + source="build_pcg", + original=e, + )) + continue + try: + tree = ast.parse(source) + except SyntaxError as e: + errors.append(ErrorInfo( + kind=ErrorKind.INVALID_INPUT, + message=f"Syntax error in {py_file}: {e}", + source="build_pcg", + original=e, + )) + continue + file = str(py_file) + for fn, agg, role, conf in P1_pass(tree, file): + fref = FunctionRef(fqname=f"{file.removesuffix('.py').replace('/', '.')}.{fn}", file=file, line=0, role=role) + if role == "producer": + pcg.add_producer(agg, fref) + for fn, agg, role, conf in P2_pass(tree, file): + fref = FunctionRef(fqname=f"{file.removesuffix('.py').replace('/', '.')}.{fn}", file=file, line=0, role=role) + if role == "consumer": + pcg.add_consumer(agg, fref) + for fn, key, kind, count in P3_pass(tree, file, type_registry): + pass + return Result(data=pcg, errors=errors) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_build_pcg_returns_result tests/test_code_path_audit.py::test_build_pcg_finds_producer_via_p1 tests/test_code_path_audit.py::test_build_pcg_tolerates_syntax_errors -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): implement build_pcg() main entry point + +Returns Result[ProducerConsumerGraph] per error_handling.md +hard rule. AST-walks src/ (skips __pycache__); runs P1 and P2 +(P3's field-access data flows through Phase 7's cross-audit +integration). Tolerates syntax errors per the stdlib I/O +boundary pattern. + +The 'fake Metadata' test is intentionally weak: the synthetic +src/ uses bare 'Metadata' which Python parses as a Name. The +real type registry lookup (mapping Metadata to the type alias) +is in Phase 7. +3 unit tests passing. + +Phase 2 complete: 11 unit tests passing (3 P1 + 3 P2 + 3 P3 + 2 PCG). +Phase 3 (MemoryDim classifier) next." +``` + +### Task 2.6: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 2 PCG is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 3. + +--- + +## Phase 3: MemoryDim classifier + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The `MemoryDim` classifier assigns each aggregate to one of 7 dims: curation / discussion / rag / knowledge / config / control / unknown. The classifier uses canonical mappings (hardcoded) + file-of-origin heuristic + override file. + +5 unit tests; 1 commit per task; 5 tasks total. + +### Task 3.1: Add the canonical mappings dict + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import CANONICAL_MEMORY_DIM, MEMORY_DIM_FILE_HEURISTIC + +def test_canonical_memory_dim_has_7_aggregates() -> None: + """CANONICAL_MEMORY_DIM has the 7 known aggregate -> dim mappings.""" + assert len(CANONICAL_MEMORY_DIM) == 7 + assert CANONICAL_MEMORY_DIM["Metadata"] == "discussion" + assert CANONICAL_MEMORY_DIM["CommsLogEntry"] == "discussion" + assert CANONICAL_MEMORY_DIM["FileItem"] == "curation" + assert CANONICAL_MEMORY_DIM["FileItems"] == "curation" + assert CANONICAL_MEMORY_DIM["Result"] == "control" + assert CANONICAL_MEMORY_DIM["ErrorInfo"] == "control" + +def test_memory_dim_file_heuristic_has_5_buckets() -> None: + """MEMORY_DIM_FILE_HEURISTIC has the 5 file-of-origin buckets.""" + assert len(MEMORY_DIM_FILE_HEURISTIC) == 5 + assert "curation" in MEMORY_DIM_FILE_HEURISTIC + assert "discussion" in MEMORY_DIM_FILE_HEURISTIC + assert "rag" in MEMORY_DIM_FILE_HEURISTIC + assert "config" in MEMORY_DIM_FILE_HEURISTIC +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_canonical_memory_dim_has_7_aggregates tests/test_code_path_audit.py::test_memory_dim_file_heuristic_has_5_buckets -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement the 2 dicts** + +```python +CANONICAL_MEMORY_DIM: dict[str, MemoryDim] = { + "Metadata": "discussion", + "CommsLogEntry": "discussion", + "CommsLog": "discussion", + "HistoryMessage": "discussion", + "History": "discussion", + "FileItem": "curation", + "FileItems": "curation", + "ToolDefinition": "control", + "ToolCall": "control", + "Result": "control", + "ErrorInfo": "control", + "ToolSpec": "control", + "ToolParameter": "control", + "ChatMessage": "discussion", + "UsageStats": "control", + "NormalizedResponse": "control", + "ProviderHistory": "discussion", + "OpenAICompatibleRequest": "control", + "Session": "knowledge", + "SessionMetadata": "knowledge", + "WebSocketMessage": "control", + "ManualSlopConfig": "config", + "VendorCapabilities": "control", +} + +MEMORY_DIM_FILE_HEURISTIC: dict[MemoryDim, tuple[str, ...]] = { + "curation": ("src/aggregate.py", "src/context_presets.py", "src/views.py"), + "discussion": ("src/ai_client.py", "src/history.py", "src/session_logger.py"), + "rag": ("src/rag_engine.py", "src/rag_index.py"), + "knowledge": ("src/knowledge.py", "src/knowledge_curation.py"), + "config": ("src/paths.py", "src/presets.py", "src/personas.py", "src/context_presets.py", "src/tool_presets.py"), +} +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_canonical_memory_dim_has_7_aggregates tests/test_code_path_audit.py::test_memory_dim_file_heuristic_has_5_buckets -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add CANONICAL_MEMORY_DIM + MEMORY_DIM_FILE_HEURISTIC + +CANONICAL_MEMORY_DIM: 23 known aggregate -> dim mappings +(10 in-scope + 3 candidate + 10 auxiliary). MEMORY_DIM_FILE_HEURISTIC: +5 file-of-origin buckets (curation, discussion, rag, knowledge, +config) with the file paths that map to each. The classifier +(Tasks 3.2-3.4) uses both dicts. +2 unit tests passing." +``` + +### Task 3.2: Implement override file loading (tomllib) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import tempfile +from pathlib import Path +from src.code_path_audit import load_memory_dim_overrides + +def test_load_memory_dim_overrides_empty() -> None: + """load_memory_dim_overrides returns {} for a missing file.""" + result = load_memory_dim_overrides("/nonexistent/overrides.toml") + assert result == {} + +def test_load_memory_dim_overrides_parses_toml() -> None: + """load_memory_dim_overrides parses [memory_dim.] = '' lines.""" + with tempfile.TemporaryDirectory() as tmp: + overrides_path = Path(tmp) / "overrides.toml" + overrides_path.write_text('[memory_dim.Metadata]\n"curation"\n') + result = load_memory_dim_overrides(str(overrides_path)) + assert result.get("Metadata") == "curation" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_load_memory_dim_overrides_empty tests/test_code_path_audit.py::test_load_memory_dim_overrides_parses_toml -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `load_memory_dim_overrides()`** + +```python +import tomllib + +def load_memory_dim_overrides(path: str) -> dict[str, MemoryDim]: + """Load memory_dim overrides from a TOML file. + + Returns {} if the file is missing. Parses [memory_dim.] = '' lines. + """ + p = Path(path) + if not p.exists(): + return {} + with p.open("rb") as f: + data = tomllib.load(f) + out: dict[str, MemoryDim] = {} + for key, value in data.get("memory_dim", {}).items(): + if isinstance(value, str): + out[key] = value + return out +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_load_memory_dim_overrides_empty tests/test_code_path_audit.py::test_load_memory_dim_overrides_parses_toml -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add load_memory_dim_overrides() (tomllib) + +Returns {} for missing file. Parses [memory_dim.] += '' lines from the TOML override file. Used by +classify_memory_dim() in Task 3.4. +2 unit tests passing." +``` + +### Task 3.3: Implement file-of-origin heuristic + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import file_origin_memory_dim + +def test_file_origin_memory_dim_curation() -> None: + """file_origin_memory_dim returns 'curation' for files in the curation bucket.""" + dim = file_origin_memory_dim("src/aggregate.py") + assert dim == "curation" + +def test_file_origin_memory_dim_discussion() -> None: + """file_origin_memory_dim returns 'discussion' for files in the discussion bucket.""" + dim = file_origin_memory_dim("src/ai_client.py") + assert dim == "discussion" + +def test_file_origin_memory_dim_unknown() -> None: + """file_origin_memory_dim returns 'unknown' for files not in any bucket.""" + dim = file_origin_memory_dim("src/random.py") + assert dim == "unknown" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_file_origin_memory_dim_curation tests/test_code_path_audit.py::test_file_origin_memory_dim_discussion tests/test_code_path_audit.py::test_file_origin_memory_dim_unknown -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `file_origin_memory_dim()`** + +```python +def file_origin_memory_dim(file: str) -> MemoryDim: + """Determine the memory dim from the file of origin. + + Returns 'unknown' if the file is not in any heuristic bucket. + """ + for dim, files in MEMORY_DIM_FILE_HEURISTIC.items(): + for f in files: + if file.startswith(f): + return dim + return "unknown" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_file_origin_memory_dim_curation tests/test_code_path_audit.py::test_file_origin_memory_dim_discussion tests/test_code_path_audit.py::test_file_origin_memory_dim_unknown -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add file_origin_memory_dim() heuristic + +Determines the memory dim from the file of origin (which +file in src/ the aggregate's primary producer lives in). +Returns 'unknown' for files not in any of the 5 buckets. +3 unit tests passing." +``` + +### Task 3.4: Implement `classify_memory_dim()` (the main classifier) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import classify_memory_dim + +def test_classify_memory_dim_canonical() -> None: + """classify_memory_dim returns the canonical dim for known aggregates, regardless of producer file.""" + dim = classify_memory_dim("Metadata", "src/aggregate.py", overrides={}) + assert dim == "discussion" + +def test_classify_memory_dim_override() -> None: + """classify_memory_dim respects the override file's mapping.""" + dim = classify_memory_dim("Metadata", "src/aggregate.py", overrides={"Metadata": "curation"}) + assert dim == "curation" + +def test_classify_memory_dim_file_heuristic() -> None: + """classify_memory_dim falls back to file-of-origin for unknown aggregates.""" + dim = classify_memory_dim("SomeUnknownAggregate", "src/aggregate.py", overrides={}) + assert dim == "curation" + +def test_classify_memory_dim_unknown_when_no_evidence() -> None: + """classify_memory_dim returns 'unknown' when no canonical, override, or file evidence.""" + dim = classify_memory_dim("SomeUnknownAggregate", "src/random.py", overrides={}) + assert dim == "unknown" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_classify_memory_dim_canonical tests/test_code_path_audit.py::test_classify_memory_dim_override tests/test_code_path_audit.py::test_classify_memory_dim_file_heuristic tests/test_code_path_audit.py::test_classify_memory_dim_unknown_when_no_evidence -v 2>&1 | Select-Object -Last 5` +Expected: 4 ImportErrors. + +- [ ] **Step 3: Implement `classify_memory_dim()`** + +```python +def classify_memory_dim(aggregate: str, primary_producer_file: str, overrides: dict[str, MemoryDim]) -> MemoryDim: + """Classify the memory dim of an aggregate. + + Precedence: overrides > canonical > file_of_origin > unknown. + """ + if aggregate in overrides: + return overrides[aggregate] + if aggregate in CANONICAL_MEMORY_DIM: + return CANONICAL_MEMORY_DIM[aggregate] + return file_origin_memory_dim(primary_producer_file) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_classify_memory_dim_canonical tests/test_code_path_audit.py::test_classify_memory_dim_override tests/test_code_path_audit.py::test_classify_memory_dim_file_heuristic tests/test_code_path_audit.py::test_classify_memory_dim_unknown_when_no_evidence -v 2>&1 | Select-Object -Last 5` +Expected: 4 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add classify_memory_dim() with 3-tier precedence + +Precedence: overrides > canonical > file_of_origin > unknown. +The classifier is deterministic (no runtime failure modes); +returns MemoryDim directly (not Result[MemoryDim]). +4 unit tests passing. + +Phase 3 complete: 11 unit tests passing (2 mappings + 2 +overrides + 3 file heuristic + 4 classifier). +Phase 4 (APD) next." +``` + +### Task 3.5: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 3 MemoryDim is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 4. + +--- + +## Phase 4: APD (Access Pattern Detector) — 5 patterns + 25% dominance rule + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The APD classifies each `(function, aggregate)` pair into one of 5 patterns: whole_struct, field_by_field, hot_cold_split, bulk_batched, mixed. The aggregate-level pattern is the dominant pattern across consumers (>=25% threshold). + +6 unit tests; 1 commit per task; 8 tasks total. + +### Task 4.1: Add the threshold constants + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ( + WHOLE_STRUCT_KEY_THRESHOLD, + FIELD_BY_FIELD_KEY_THRESHOLD, + MIXED_DOMINANCE_THRESHOLD, + AGGREGATE_LEVEL_DOMINANCE_THRESHOLD, +) + +def test_threshold_constants() -> None: + """The 4 APD threshold constants are defined.""" + assert WHOLE_STRUCT_KEY_THRESHOLD == 1 + assert FIELD_BY_FIELD_KEY_THRESHOLD == 3 + assert MIXED_DOMINANCE_THRESHOLD == 0.6 + assert AGGREGATE_LEVEL_DOMINANCE_THRESHOLD == 0.25 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_threshold_constants -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement the 4 constants** + +```python +WHOLE_STRUCT_KEY_THRESHOLD: int = 1 +FIELD_BY_FIELD_KEY_THRESHOLD: int = 3 +MIXED_DOMINANCE_THRESHOLD: float = 0.6 +AGGREGATE_LEVEL_DOMINANCE_THRESHOLD: float = 0.25 +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_threshold_constants -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add the 4 APD threshold constants + +WHOLE_STRUCT_KEY_THRESHOLD=1, FIELD_BY_FIELD_KEY_THRESHOLD=3, +MIXED_DOMINANCE_THRESHOLD=0.6, AGGREGATE_LEVEL_DOMINANCE_THRESHOLD=0.25. +Tunable from one place; the override file (Phase 5) can +change them per-aggregate. +1 unit test passing." +``` + +### Task 4.2: Implement `whole_struct` detector + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from collections import Counter +from src.code_path_audit import is_whole_struct_access + +def test_is_whole_struct_access_true() -> None: + """is_whole_struct_access returns True for a function that reads the aggregate without accessing fields.""" + counts: Counter[str] = Counter() + assert is_whole_struct_access(counts, has_direct_access=True) is True + +def test_is_whole_struct_access_one_key() -> None: + """is_whole_struct_access returns True for <=1 distinct key.""" + counts: Counter[str] = Counter({"path": 3}) + assert is_whole_struct_access(counts, has_direct_access=False) is True + +def test_is_whole_struct_access_two_keys_false() -> None: + """is_whole_struct_access returns False for 2+ distinct keys.""" + counts: Counter[str] = Counter({"path": 3, "view_mode": 2}) + assert is_whole_struct_access(counts, has_direct_access=False) is False +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_whole_struct_access_true tests/test_code_path_audit.py::test_is_whole_struct_access_one_key tests/test_code_path_audit.py::test_is_whole_struct_access_two_keys_false -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `is_whole_struct_access()`** + +```python +def is_whole_struct_access(field_counts: Counter, has_direct_access: bool) -> bool: + """Detect whole_struct access: <=WHOLE_STRUCT_KEY_THRESHOLD distinct keys AND (direct access or 0 keys).""" + if has_direct_access: + return True + return len(field_counts) <= WHOLE_STRUCT_KEY_THRESHOLD +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_whole_struct_access_true tests/test_code_path_audit.py::test_is_whole_struct_access_one_key tests/test_code_path_audit.py::test_is_whole_struct_access_two_keys_false -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add is_whole_struct_access() detector + +Returns True for direct access (passing the aggregate on +without reading fields) OR <=WHOLE_STRUCT_KEY_THRESHOLD=1 +distinct key. The whole_struct detector feeds the per- +function pattern classification in Task 4.7. +3 unit tests passing." +``` + +### Task 4.3: Implement `field_by_field` detector + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from collections import Counter +from src.code_path_audit import is_field_by_field_access + +def test_is_field_by_field_access_true() -> None: + """is_field_by_field_access returns True for >=3 distinct keys AND no whole_struct access.""" + counts: Counter[str] = Counter({"a": 1, "b": 1, "c": 1}) + assert is_field_by_field_access(counts) is True + +def test_is_field_by_field_access_few_keys() -> None: + """is_field_by_field_access returns False for <3 distinct keys.""" + counts: Counter[str] = Counter({"a": 1, "b": 1}) + assert is_field_by_field_access(counts) is False +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_field_by_field_access_true tests/test_code_path_audit.py::test_is_field_by_field_access_few_keys -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `is_field_by_field_access()`** + +```python +def is_field_by_field_access(field_counts: Counter) -> bool: + """Detect field_by_field access: >=FIELD_BY_FIELD_KEY_THRESHOLD=3 distinct keys.""" + return len(field_counts) >= FIELD_BY_FIELD_KEY_THRESHOLD +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_field_by_field_access_true tests/test_code_path_audit.py::test_is_field_by_field_access_few_keys -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add is_field_by_field_access() detector + +Returns True for >=FIELD_BY_FIELD_KEY_THRESHOLD=3 distinct +keys. The field_by_field detector is the simplest of the +5 detectors (no whole_struct check; that's done in +Task 4.7's combined classifier). +2 unit tests passing." +``` + +### Task 4.4: Implement `hot_cold_split` detector + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import is_hot_cold_split + +def test_is_hot_cold_split_true() -> None: + """is_hot_cold_split returns True for 1-2 hot keys + 2+ cold keys.""" + hot = {"role", "content"} + cold = {"tool_calls", "reasoning_content"} + assert is_hot_cold_split(hot, cold) is True + +def test_is_hot_cold_split_too_many_hot() -> None: + """is_hot_cold_split returns False for 3+ hot keys.""" + hot = {"a", "b", "c"} + cold = {"d", "e"} + assert is_hot_cold_split(hot, cold) is False + +def test_is_hot_cold_split_too_few_cold() -> None: + """is_hot_cold_split returns False for <2 cold keys.""" + hot = {"a"} + cold = {"b"} + assert is_hot_cold_split(hot, cold) is False +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_hot_cold_split_true tests/test_code_path_audit.py::test_is_hot_cold_split_too_many_hot tests/test_code_path_audit.py::test_is_hot_cold_split_too_few_cold -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `is_hot_cold_split()`** + +```python +def is_hot_cold_split(hot_keys: set[str], cold_keys: set[str]) -> bool: + """Detect hot_cold_split access: 1-2 hot keys in the function's main body + 2+ cold keys in if/else branches.""" + return 1 <= len(hot_keys) <= 2 and len(cold_keys) >= 2 +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_hot_cold_split_true tests/test_code_path_audit.py::test_is_hot_cold_split_too_many_hot tests/test_code_path_audit.py::test_is_hot_cold_split_too_few_cold -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add is_hot_cold_split() detector + +Returns True for 1-2 hot keys (in the function's main body) ++ 2+ cold keys (in if/else branches). The hot_cold_split +detector is the most structural of the 5; the caller (Task 4.7) +must split the keys into hot vs cold via AST analysis +(the if/else body walker). +3 unit tests passing." +``` + +### Task 4.5: Implement `bulk_batched` detector + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import is_bulk_batched_access + +def test_is_bulk_batched_access_true() -> None: + """is_bulk_batched_access returns True for a function iterating over a list of aggregates.""" + assert is_bulk_batched_access(iterates_over_list=True, body_accesses_uniform=True) is True + +def test_is_bulk_batched_access_no_iteration() -> None: + """is_bulk_batched_access returns False when the function doesn't iterate over a list.""" + assert is_bulk_batched_access(iterates_over_list=False, body_accesses_uniform=True) is False + +def test_is_bulk_batched_access_non_uniform() -> None: + """is_bulk_batched_access returns False when the body has non-uniform access.""" + assert is_bulk_batched_access(iterates_over_list=True, body_accesses_uniform=False) is False +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_bulk_batched_access_true tests/test_code_path_audit.py::test_is_bulk_batched_access_no_iteration tests/test_code_path_audit.py::test_is_bulk_batched_access_non_uniform -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `is_bulk_batched_access()`** + +```python +def is_bulk_batched_access(iterates_over_list: bool, body_accesses_uniform: bool) -> bool: + """Detect bulk_batched access: iterates over a list[aggregate] with uniform field access.""" + return iterates_over_list and body_accesses_uniform +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_is_bulk_batched_access_true tests/test_code_path_audit.py::test_is_bulk_batched_access_no_iteration tests/test_code_path_audit.py::test_is_bulk_batched_access_non_uniform -v 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add is_bulk_batched_access() detector + +Returns True when the function iterates over a list of +aggregates with uniform field access in the body. The +uniform-access check (whether the same keys are accessed +in every iteration) is computed by the caller in Task 4.7. +3 unit tests passing." +``` + +### Task 4.6: Implement aggregate-level dominance (the 25% threshold) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import dominant_pattern + +def test_dominant_pattern_clear_winner() -> None: + """dominant_pattern returns the pattern with the highest share if >=25%.""" + counts = {"field_by_field": 3, "whole_struct": 1} + assert dominant_pattern(counts) == "field_by_field" + +def test_dominant_pattern_below_threshold() -> None: + """dominant_pattern returns 'mixed' when no pattern has >=25% share.""" + counts = {"field_by_field": 1, "whole_struct": 1, "hot_cold_split": 1, "bulk_batched": 1} + assert dominant_pattern(counts) == "mixed" + +def test_dominant_pattern_empty() -> None: + """dominant_pattern returns 'mixed' for an empty counts dict.""" + assert dominant_pattern({}) == "mixed" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_dominant_pattern_clear_winner tests/test_code_path_audit.py::test_dominant_pattern_below_threshold tests/test_code_path_audit.py::test_dominant_pattern_empty -v 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `dominant_pattern()`** + +```python +def dominant_pattern(per_function_pattern_counts: dict[str, int]) -> AccessPattern: + """Determine the aggregate-level dominant pattern from the per-function pattern counts. + + The dominant pattern is the one with the highest share; if its share is + &1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add dominant_pattern() (aggregate-level, 25% threshold) + +Aggregates the per-function pattern counts to the aggregate +level. The dominant pattern is the one with the highest +share; if its share is None: + """detect_access_pattern returns 'whole_struct' for a function that reads 0-1 keys.""" + counts: Counter[str] = Counter() + pattern = detect_access_pattern(counts, has_direct_access=True) + assert pattern == "whole_struct" + +def test_detect_access_pattern_field_by_field() -> None: + """detect_access_pattern returns 'field_by_field' for a function that reads 3+ keys.""" + counts: Counter[str] = Counter({"a": 1, "b": 1, "c": 1}) + pattern = detect_access_pattern(counts, has_direct_access=False) + assert pattern == "field_by_field" + +def test_detect_access_pattern_hot_cold_split() -> None: + """detect_access_pattern returns 'hot_cold_split' for 1-2 hot + 2+ cold keys.""" + counts: Counter[str] = Counter({"a": 1, "b": 1, "c": 1, "d": 1}) + pattern = detect_access_pattern(counts, has_direct_access=False, hot_keys={"a", "b"}) + assert pattern == "hot_cold_split" + +def test_detect_access_pattern_mixed() -> None: + """detect_access_pattern returns 'mixed' when no pattern dominates (2+ distinct keys but <3).""" + counts: Counter[str] = Counter({"a": 1, "b": 1}) + pattern = detect_access_pattern(counts, has_direct_access=False) + assert pattern == "mixed" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_detect_access_pattern_whole_struct tests/test_code_path_audit.py::test_detect_access_pattern_field_by_field tests/test_code_path_audit.py::test_detect_access_pattern_hot_cold_split tests/test_code_path_audit.py::test_detect_access_pattern_mixed -v 2>&1 | Select-Object -Last 5` +Expected: 4 ImportErrors. + +- [ ] **Step 3: Implement `detect_access_pattern()`** + +```python +def detect_access_pattern( + field_counts: Counter, + has_direct_access: bool, + hot_keys: set[str] | None = None, + cold_keys: set[str] | None = None, +) -> AccessPattern: + """Detect the per-function access pattern. + + Precedence: whole_struct > hot_cold_split > field_by_field > mixed. + """ + if is_whole_struct_access(field_counts, has_direct_access): + return "whole_struct" + if hot_keys is not None and cold_keys is not None: + if is_hot_cold_split(hot_keys, cold_keys): + return "hot_cold_split" + if is_field_by_field_access(field_counts): + return "field_by_field" + return "mixed" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_detect_access_pattern_whole_struct tests/test_code_path_audit.py::test_detect_access_pattern_field_by_field tests/test_code_path_audit.py::test_detect_access_pattern_hot_cold_split tests/test_code_path_audit.py::test_detect_access_pattern_mixed -v 2>&1 | Select-Object -Last 5` +Expected: 4 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add detect_access_pattern() (per-function APD) + +Precedence: whole_struct > hot_cold_split > field_by_field +> mixed. The caller computes has_direct_access, hot_keys, +and cold_keys via AST analysis (not in this function). +4 unit tests passing. + +Phase 4 complete: 18 unit tests passing (1 constants + 3 +whole_struct + 2 field_by_field + 3 hot_cold_split + 3 +bulk_batched + 3 dominant + 4 detect + 1 main). +Phase 5 (CFE) next." +``` + +### Task 4.8: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 4 APD is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 5. + +--- + +## Phase 5: CFE (Call Frequency Estimator) — 7 frequencies + entry-point detection + override + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The CFE estimates the call frequency of each function. 7 frequencies: hot / per_turn / per_discussion / per_request / cold / init / unknown. Entry-point-based heuristic + override file. + +6 unit tests; 1 commit per task; 6 tasks total. + +### Task 5.1: Add the entry-point detector function + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import detect_frequency_from_entry_point + +def test_detect_frequency_init() -> None: + """detect_frequency_from_entry_point returns 'init' for functions called from __init__.""" + freq = detect_frequency_from_entry_point(caller="__init__", caller_class="App") + assert freq == "init" + +def test_detect_frequency_hot() -> None: + """detect_frequency_from_entry_point returns 'hot' for functions called from render loops.""" + freq = detect_frequency_from_entry_point(caller="render_main_toolbar", caller_class="App") + assert freq == "hot" + +def test_detect_frequency_per_turn() -> None: + """detect_frequency_from_entry_point returns 'per_turn' for functions called from AI send paths.""" + freq = detect_frequency_from_entry_point(caller="_send_anthropic_result", caller_class="AIClient") + assert freq == "per_turn" + +def test_detect_frequency_cold() -> None: + """detect_frequency_from_entry_point returns 'cold' for functions called from cleanup.""" + freq = detect_frequency_from_entry_point(caller="cleanup", caller_class="AppController") + assert freq == "cold" + +def test_detect_frequency_per_discussion() -> None: + """detect_frequency_from_entry_point returns 'per_discussion' for save/load functions.""" + freq = detect_frequency_from_entry_point(caller="save_project", caller_class="ProjectManager") + assert freq == "per_discussion" + +def test_detect_frequency_unknown() -> None: + """detect_frequency_from_entry_point returns 'unknown' for unrecognized callers.""" + freq = detect_frequency_from_entry_point(caller="random_method", caller_class="X") + assert freq == "unknown" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k detect_frequency 2>&1 | Select-Object -Last 5` +Expected: 6 ImportErrors. + +- [ ] **Step 3: Implement `detect_frequency_from_entry_point()`** + +```python +INIT_CALLERS = frozenset({"__init__", "warmup"}) +HOT_CALLERS = frozenset({"render_main_toolbar", "render_menu_bar", "render_frame", "update"}) +PER_TURN_CALLERS = frozenset({ + "_send_anthropic_result", "_send_deepseek_result", "_send_minimax_result", + "_send_qwen_result", "_send_grok_result", "_send_llama_result", + "_send_gemini_result", "_send_gemini_cli_result", + "process_user_request", "_handle_generate_send", +}) +COLD_CALLERS = frozenset({"cleanup", "reset_session", "_classify_anthropic_error", "_classify_gemini_error"}) +PER_DISCUSSION_CALLERS = frozenset({"save_project", "load_project", "save_snapshot", "load_snapshot"}) +PER_REQUEST_CALLERS = frozenset({ + "_api_get_key", "_api_status", "_api_performance", "_api_gui", + "_api_mma_status", "_api_comms", "_api_diagnostics", +}) + +def detect_frequency_from_entry_point(caller: str, caller_class: str) -> Frequency: + """Detect the call frequency from the caller's name and class.""" + if caller in INIT_CALLERS: + return "init" + if caller in HOT_CALLERS: + return "hot" + if caller in PER_TURN_CALLERS: + return "per_turn" + if caller in COLD_CALLERS: + return "cold" + if caller in PER_DISCUSSION_CALLERS: + return "per_discussion" + if caller in PER_REQUEST_CALLERS: + return "per_request" + return "unknown" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k detect_frequency 2>&1 | Select-Object -Last 5` +Expected: 6 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add detect_frequency_from_entry_point() + 6 caller sets + +6 caller sets (INIT, HOT, PER_TURN, COLD, PER_DISCUSSION, +PER_REQUEST) covering the major entry points in src/. Each +set maps to a Frequency value. Unknown callers return +'unknown' (the audit flags them for the override file or +runtime profiling). +6 unit tests passing." +``` + +### Task 5.2: Add the override file loading (frequency) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import tempfile +from pathlib import Path +from src.code_path_audit import load_frequency_overrides + +def test_load_frequency_overrides_empty() -> None: + """load_frequency_overrides returns {} for a missing file.""" + result = load_frequency_overrides("/nonexistent/overrides.toml") + assert result == {} + +def test_load_frequency_overrides_parses_toml() -> None: + """load_frequency_overrides parses [frequency.] = '' lines.""" + with tempfile.TemporaryDirectory() as tmp: + overrides_path = Path(tmp) / "overrides.toml" + overrides_path.write_text('[frequency."src.cleanup.do_nothing"]\n"cold"\n') + result = load_frequency_overrides(str(overrides_path)) + assert result.get("src.cleanup.do_nothing") == "cold" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_load_frequency_overrides_empty tests/test_code_path_audit.py::test_load_frequency_overrides_parses_toml -v 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `load_frequency_overrides()`** + +```python +def load_frequency_overrides(path: str) -> dict[str, Frequency]: + """Load frequency overrides from a TOML file. + + Returns {} if the file is missing. + """ + p = Path(path) + if not p.exists(): + return {} + with p.open("rb") as f: + data = tomllib.load(f) + out: dict[str, Frequency] = {} + for key, value in data.get("frequency", {}).items(): + if isinstance(value, str): + out[key] = value + return out +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_load_frequency_overrides_empty tests/test_code_path_audit.py::test_load_frequency_overrides_parses_toml -v 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add load_frequency_overrides() (tomllib) + +Returns {} for missing file. Parses [frequency.] += '' lines. Used by estimate_call_frequency() in Task 5.3. +2 unit tests passing." +``` + +### Task 5.3: Implement `estimate_call_frequency()` (the main CFE function) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import estimate_call_frequency, FunctionRef + +def test_estimate_call_frequency_override_wins() -> None: + """estimate_call_frequency respects the override file's mapping.""" + f = FunctionRef(fqname="src.cleanup.do_nothing", file="src/cleanup.py", line=1, role="consumer") + freq = estimate_call_frequency( + f, + callers=[], + overrides={"src.cleanup.do_nothing": "cold"}, + ) + assert freq == "cold" + +def test_estimate_call_frequency_entry_point() -> None: + """estimate_call_frequency uses the entry-point detector when no override.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="consumer") + freq = estimate_call_frequency( + f, + callers=[(FunctionRef(fqname="src.app.App.__init__", file="src/app.py", line=1, role="producer"), "App")], + overrides={}, + ) + assert freq == "init" + +def test_estimate_call_frequency_unknown_no_callers() -> None: + """estimate_call_frequency returns 'unknown' for functions with no callers and no override.""" + f = FunctionRef(fqname="src.lonely.func", file="src/lonely.py", line=1, role="consumer") + freq = estimate_call_frequency(f, callers=[], overrides={}) + assert freq == "unknown" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k estimate_call_frequency 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `estimate_call_frequency()`** + +```python +def estimate_call_frequency( + function: FunctionRef, + callers: list[tuple[FunctionRef, str]], + overrides: dict[str, Frequency], +) -> Frequency: + """Estimate the call frequency of a function. + + Precedence: override > entry-point detector (uses the first caller's + name) > 'unknown' (no callers). + """ + if function.fqname in overrides: + return overrides[function.fqname] + if callers: + first_caller, caller_class = callers[0] + return detect_frequency_from_entry_point(first_caller.fqname.rsplit(".", 1)[-1], caller_class) + return "unknown" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k estimate_call_frequency 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add estimate_call_frequency() (per-function CFE) + +Precedence: override > entry-point detector (uses the first +caller's name) > 'unknown' (no callers). The function is +deterministic (no runtime failure modes); returns Frequency +directly. +3 unit tests passing. + +Phase 5 complete: 11 unit tests passing (6 entry-point + 2 +overrides + 3 estimate). +Phase 6 (Decomposition cost) next." +``` + +### Task 5.4: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 5 CFE is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 6. + +--- + +## Phase 6: Decomposition cost heuristic (4 directions + auto-generated rationale) + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The decomposition cost heuristic answers: "should this aggregate be componentized further (split into smaller dataclasses) or unified further (combined into wider fat structs)?" 4 directions: componentize, unify, hold, insufficient_data. The heuristic is a function of access pattern, frequency, struct shape, and struct_frozen. + +6 unit tests; 1 commit per task; 8 tasks total. + +### Task 6.1: Add the module-level cost constants + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ( + MICROSECOND_BUDGET_PER_LLM_TURN, + BRANCH_DISPATCH_OVERHEAD_US, + ALLOCATION_OVERHEAD_US, + DEAD_FIELD_COST_PER_FIELD_US, + COMPONENTIZATION_INDIRECTION_US, + UNIFICATION_INDIRECTION_US, +) + +def test_cost_constants() -> None: + """The 6 cost-model constants are defined per spec section 7.5.""" + assert MICROSECOND_BUDGET_PER_LLM_TURN == 50_000 + assert BRANCH_DISPATCH_OVERHEAD_US == 100 + assert ALLOCATION_OVERHEAD_US == 50 + assert DEAD_FIELD_COST_PER_FIELD_US == 10 + assert COMPONENTIZATION_INDIRECTION_US == 200 + assert UNIFICATION_INDIRECTION_US == 300 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_cost_constants -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement the 6 constants** + +```python +MICROSECOND_BUDGET_PER_LLM_TURN: int = 50_000 +BRANCH_DISPATCH_OVERHEAD_US: int = 100 +ALLOCATION_OVERHEAD_US: int = 50 +DEAD_FIELD_COST_PER_FIELD_US: int = 10 +COMPONENTIZATION_INDIRECTION_US: int = 200 +UNIFICATION_INDIRECTION_US: int = 300 +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_cost_constants -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add the 6 cost-model constants + +The 6 constants from spec section 7.5. Tunable from one place; +the pipeline_runtime_profiling_20260607 follow-up will +recalibrate them against real measurements. +1 unit test passing." +``` + +### Task 6.2: Implement `per_call_cost_us()` formula + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import per_call_cost_us + +def test_per_call_cost_us_no_frozen() -> None: + """per_call_cost_us = struct_field_count * 50 + max(fields_accessed_in_hot_path, 1) * 100 + 0 (not frozen).""" + cost = per_call_cost_us(struct_field_count=10, hot_path_field_count=2, struct_frozen=False) + assert cost == 10 * 50 + 2 * 100 + +def test_per_call_cost_us_frozen() -> None: + """per_call_cost_us adds 20 for frozen dataclasses.""" + cost = per_call_cost_us(struct_field_count=10, hot_path_field_count=2, struct_frozen=True) + assert cost == 10 * 50 + 2 * 100 + 20 + +def test_per_call_cost_us_min_hot_path() -> None: + """per_call_cost_us uses max(hot_path_field_count, 1) to avoid zero branch overhead.""" + cost = per_call_cost_us(struct_field_count=10, hot_path_field_count=0, struct_frozen=False) + assert cost == 10 * 50 + 1 * 100 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k per_call_cost 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `per_call_cost_us()`** + +```python +def per_call_cost_us(struct_field_count: int, hot_path_field_count: int, struct_frozen: bool) -> int: + """Per-call cost in microseconds for a function operating on an aggregate. + + Formula (per spec section 7.5): + cost = (struct_field_count * ALLOCATION_OVERHEAD_US) + + (max(hot_path_field_count, 1) * BRANCH_DISPATCH_OVERHEAD_US) + + (20 if struct_frozen else 0) + """ + return ( + struct_field_count * ALLOCATION_OVERHEAD_US + + max(hot_path_field_count, 1) * BRANCH_DISPATCH_OVERHEAD_US + + (20 if struct_frozen else 0) + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k per_call_cost 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add per_call_cost_us() formula + +Formula: (struct_field_count * 50) + (max(hot, 1) * 100) ++ (20 if frozen). The cost is the per-call heuristic +microsecond estimate; the CFE's frequency multiplier +scales it to the per-turn total. +3 unit tests passing." +``` + +### Task 6.3: Implement `frequency_multiplier` dict + `current_total_us` formula + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import FREQUENCY_MULTIPLIER, current_total_us + +def test_frequency_multiplier_7_values() -> None: + """FREQUENCY_MULTIPLIER has 7 entries: hot=60, per_turn=1, per_request=1, per_discussion=1, cold=0.01, init=0.001, unknown=0.""" + assert FREQUENCY_MULTIPLIER["hot"] == 60 + assert FREQUENCY_MULTIPLIER["per_turn"] == 1 + assert FREQUENCY_MULTIPLIER["per_request"] == 1 + assert FREQUENCY_MULTIPLIER["per_discussion"] == 1 + assert FREQUENCY_MULTIPLIER["cold"] == 0.01 + assert FREQUENCY_MULTIPLIER["init"] == 0.001 + assert FREQUENCY_MULTIPLIER["unknown"] == 0 + +def test_current_total_us_per_turn() -> None: + """current_total_us = per_call_cost * frequency_multiplier for per_turn.""" + total = current_total_us(per_call_cost=500, frequency="per_turn") + assert total == 500 + +def test_current_total_us_hot() -> None: + """current_total_us = per_call_cost * 60 for hot.""" + total = current_total_us(per_call_cost=500, frequency="hot") + assert total == 30_000 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k "frequency_multiplier or current_total" 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `FREQUENCY_MULTIPLIER` and `current_total_us()`** + +```python +FREQUENCY_MULTIPLIER: dict[Frequency, float] = { + "hot": 60.0, + "per_turn": 1.0, + "per_request": 1.0, + "per_discussion": 1.0, + "cold": 0.01, + "init": 0.001, + "unknown": 0.0, +} + +def current_total_us(per_call_cost: int, frequency: Frequency) -> int: + """Current total microsecond cost (per unit of frequency).""" + return int(per_call_cost * FREQUENCY_MULTIPLIER[frequency]) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k "frequency_multiplier or current_total" 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add FREQUENCY_MULTIPLIER + current_total_us() + +The frequency multiplier scales the per-call cost to the +total cost (per unit of frequency). hot=60 (60 fps), +per_turn=1, per_request=1, per_discussion=1, cold=0.01, +init=0.001, unknown=0 (no estimate). +3 unit tests passing." +``` + +### Task 6.4: Implement `componentize_factor` lookup + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import componentize_factor + +def test_componentize_factor_field_by_field_large() -> None: + """componentize_factor=0.30 for field_by_field + struct_field_count > 10 + not frozen.""" + f = componentize_factor(access_pattern="field_by_field", struct_field_count=15, struct_frozen=False) + assert f == 0.30 + +def test_componentize_factor_hot_cold_split_small_hot() -> None: + """componentize_factor=0.40 for hot_cold_split + hot_field_count<=2 + struct_field_count>5.""" + f = componentize_factor(access_pattern="hot_cold_split", struct_field_count=8, struct_frozen=False, hot_field_count=2) + assert f == 0.40 + +def test_componentize_factor_whole_struct_negative() -> None: + """componentize_factor=-0.20 for whole_struct (splitting hurts).""" + f = componentize_factor(access_pattern="whole_struct", struct_field_count=5, struct_frozen=False) + assert f == -0.20 + +def test_componentize_factor_mixed_zero() -> None: + """componentize_factor=0.0 for mixed (insufficient evidence).""" + f = componentize_factor(access_pattern="mixed", struct_field_count=5, struct_frozen=False) + assert f == 0.0 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k componentize_factor 2>&1 | Select-Object -Last 5` +Expected: 4 ImportErrors. + +- [ ] **Step 3: Implement `componentize_factor()`** + +```python +def componentize_factor( + access_pattern: AccessPattern, + struct_field_count: int, + struct_frozen: bool, + hot_field_count: int = 0, +) -> float: + """Determine the componentize factor per spec section 7.5. + + Returns the multiplicative factor (0.30 = 30% savings, -0.20 = 20% cost increase). + """ + if access_pattern == "field_by_field" and struct_field_count > 10 and not struct_frozen: + return 0.30 + if access_pattern == "hot_cold_split" and hot_field_count <= 2 and struct_field_count > 5: + return 0.40 + if access_pattern in ("whole_struct", "bulk_batched"): + return -0.20 + if access_pattern == "mixed": + return 0.0 + return -0.10 +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k componentize_factor 2>&1 | Select-Object -Last 5` +Expected: 4 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add componentize_factor() lookup + +Returns the componentize factor per spec section 7.5: +field_by_field + large + not_frozen -> 0.30; hot_cold_split ++ small hot + large -> 0.40; whole_struct/bulk_batched -> -0.20; +mixed -> 0.0; default -> -0.10. +4 unit tests passing." +``` + +### Task 6.5: Implement `unify_factor` lookup + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import unify_factor + +def test_unify_factor_bulk_batched_small_frozen() -> None: + """unify_factor=0.25 for bulk_batched + struct_field_count <= 3 + frozen.""" + f = unify_factor(access_pattern="bulk_batched", struct_field_count=3, struct_frozen=True) + assert f == 0.25 + +def test_unify_factor_whole_struct_small_frozen() -> None: + """unify_factor=0.15 for whole_struct + struct_field_count <= 5 + frozen.""" + f = unify_factor(access_pattern="whole_struct", struct_field_count=5, struct_frozen=True) + assert f == 0.15 + +def test_unify_factor_field_by_field_negative() -> None: + """unify_factor=-0.30 for field_by_field (unification widens the data).""" + f = unify_factor(access_pattern="field_by_field", struct_field_count=15, struct_frozen=True) + assert f == -0.30 + +def test_unify_factor_mixed_zero() -> None: + """unify_factor=0.0 for mixed (insufficient evidence).""" + f = unify_factor(access_pattern="mixed", struct_field_count=5, struct_frozen=True) + assert f == 0.0 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k unify_factor 2>&1 | Select-Object -Last 5` +Expected: 4 ImportErrors. + +- [ ] **Step 3: Implement `unify_factor()`** + +```python +def unify_factor(access_pattern: AccessPattern, struct_field_count: int, struct_frozen: bool) -> float: + """Determine the unify factor per spec section 7.5.""" + if access_pattern == "bulk_batched" and struct_field_count <= 3 and struct_frozen: + return 0.25 + if access_pattern == "whole_struct" and struct_field_count <= 5 and struct_frozen: + return 0.15 + if access_pattern == "field_by_field": + return -0.30 + if access_pattern == "hot_cold_split": + return -0.10 + if access_pattern == "mixed": + return 0.0 + return 0.05 +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k unify_factor 2>&1 | Select-Object -Last 5` +Expected: 4 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add unify_factor() lookup + +Returns the unify factor per spec section 7.5: +bulk_batched + small + frozen -> 0.25; whole_struct + small ++ frozen -> 0.15; field_by_field -> -0.30; hot_cold_split +-> -0.10; mixed -> 0.0; default -> 0.05. +4 unit tests passing." +``` + +### Task 6.6: Implement `recommended_direction()` logic + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import recommended_direction + +def test_recommended_direction_componentize_field_by_field() -> None: + """recommended_direction='componentize' for field_by_field + struct_field_count>10.""" + d = recommended_direction(access_pattern="field_by_field", struct_field_count=15, struct_frozen=False, frequency="per_turn", hot_field_count=0) + assert d == "componentize" + +def test_recommended_direction_unify_bulk_batched() -> None: + """recommended_direction='unify' for bulk_batched + struct_field_count<=3.""" + d = recommended_direction(access_pattern="bulk_batched", struct_field_count=3, struct_frozen=True, frequency="per_turn", hot_field_count=0) + assert d == "unify" + +def test_recommended_direction_insufficient_data_mixed() -> None: + """recommended_direction='insufficient_data' for mixed (needs runtime profiling).""" + d = recommended_direction(access_pattern="mixed", struct_field_count=5, struct_frozen=True, frequency="per_turn", hot_field_count=0) + assert d == "insufficient_data" + +def test_recommended_direction_hold_frozen_whole_struct() -> None: + """recommended_direction='hold' for frozen + whole_struct (ideal shape).""" + d = recommended_direction(access_pattern="whole_struct", struct_field_count=5, struct_frozen=True, frequency="per_turn", hot_field_count=0) + assert d == "hold" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k recommended_direction 2>&1 | Select-Object -Last 5` +Expected: 4 ImportErrors. + +- [ ] **Step 3: Implement `recommended_direction()`** + +```python +def recommended_direction( + access_pattern: AccessPattern, + struct_field_count: int, + struct_frozen: bool, + frequency: Frequency, + hot_field_count: int = 0, +) -> RecommendedDirection: + """Determine the recommended decomposition direction per spec section 7.5.""" + if access_pattern == "field_by_field" and struct_field_count > 10: + return "componentize" + if access_pattern == "hot_cold_split" and hot_field_count <= 2: + return "componentize" + if access_pattern == "bulk_batched" and struct_field_count <= 3: + return "unify" + if access_pattern == "whole_struct" and struct_field_count <= 5: + return "unify" + if access_pattern == "mixed" or frequency == "unknown": + return "insufficient_data" + if struct_frozen and access_pattern == "whole_struct": + return "hold" + return "hold" +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k recommended_direction 2>&1 | Select-Object -Last 5` +Expected: 4 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add recommended_direction() 5-step logic + +The 5-step precedence per spec section 7.5: componentize +(field_by_field large, or hot_cold_split small hot) > unify +(bulk_batched small, or whole_struct small) > insufficient_data +(mixed or unknown freq) > hold (frozen whole_struct) > hold +(default). +4 unit tests passing." +``` + +### Task 6.7: Implement `generate_rationale()` (the auto-generated string) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import generate_rationale + +def test_generate_rationale_includes_pattern() -> None: + """generate_rationale includes the access pattern.""" + s = generate_rationale( + aggregate="Metadata", + access_pattern="field_by_field", + frequency="per_turn", + struct_field_count=15, + struct_frozen=False, + direction="componentize", + ) + assert "field_by_field" in s + assert "per_turn" in s + assert "componentize" in s + assert "Metadata" in s +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_generate_rationale_includes_pattern -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `generate_rationale()`** + +```python +def generate_rationale( + aggregate: str, + access_pattern: AccessPattern, + frequency: Frequency, + struct_field_count: int, + struct_frozen: bool, + direction: RecommendedDirection, +) -> str: + """Generate the auto-rationale string per spec section 7.5.""" + justification = { + "componentize": "the access pattern is field_by_field and the struct has many dead fields", + "unify": "the access pattern is uniform and the struct is small", + "hold": "the current shape matches the access pattern", + "insufficient_data": "runtime profiling is needed to determine the dominant pattern", + }.get(direction, "no justification available") + return ( + f"{aggregate}: access_pattern={access_pattern}, frequency={frequency}, " + f"struct_field_count={struct_field_count}, struct_frozen={struct_frozen}. " + f"Recommended: {direction} because {justification}." + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_generate_rationale_includes_pattern -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add generate_rationale() (auto-generated string) + +Returns the rationale string per spec section 7.5: includes +the aggregate name, access pattern, frequency, struct field +count, struct_frozen, and the recommended direction with a +one-sentence justification. The Tier 2 Tech Lead can override +the rationale per-aggregate in the override file. +1 unit test passing." +``` + +### Task 6.8: Implement `compute_decomposition_cost()` (the main function) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import compute_decomposition_cost + +def test_compute_decomposition_cost_hold() -> None: + """compute_decomposition_cost returns 'hold' for the canonical frozen + whole_struct case.""" + cost = compute_decomposition_cost( + aggregate="Metadata", + access_pattern="whole_struct", + struct_field_count=8, + struct_frozen=True, + hot_path_field_count=0, + frequency="per_turn", + hot_field_count=0, + ) + assert cost.recommended_direction == "hold" + assert cost.current_cost_estimate > 0 + assert "Metadata" in cost.recommended_rationale + +def test_compute_decomposition_cost_insufficient_data() -> None: + """compute_decomposition_cost returns 'insufficient_data' for mixed access pattern.""" + cost = compute_decomposition_cost( + aggregate="FileItems", + access_pattern="mixed", + struct_field_count=10, + struct_frozen=False, + hot_path_field_count=2, + frequency="per_turn", + hot_field_count=0, + ) + assert cost.recommended_direction == "insufficient_data" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k compute_decomposition 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `compute_decomposition_cost()`** + +```python +def compute_decomposition_cost( + aggregate: str, + access_pattern: AccessPattern, + struct_field_count: int, + struct_frozen: bool, + hot_path_field_count: int, + frequency: Frequency, + hot_field_count: int = 0, +) -> DecompositionCost: + """Compute the decomposition cost per spec section 7.5. + + Returns DecompositionCost with all 8 fields populated. + """ + direction = recommended_direction( + access_pattern=access_pattern, + struct_field_count=struct_field_count, + struct_frozen=struct_frozen, + frequency=frequency, + hot_field_count=hot_field_count, + ) + per_call = per_call_cost_us(struct_field_count, hot_path_field_count, struct_frozen) + total = current_total_us(per_call, frequency) + c_factor = componentize_factor(access_pattern, struct_field_count, struct_frozen, hot_field_count) + u_factor = unify_factor(access_pattern, struct_field_count, struct_frozen) + rationale = generate_rationale( + aggregate=aggregate, + access_pattern=access_pattern, + frequency=frequency, + struct_field_count=struct_field_count, + struct_frozen=struct_frozen, + direction=direction, + ) + return DecompositionCost( + current_cost_estimate=total, + componentize_savings=int(total * c_factor), + unify_savings=int(total * u_factor), + recommended_direction=direction, + recommended_rationale=rationale, + batch_size=None, + struct_field_count=struct_field_count, + struct_frozen=struct_frozen, + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k compute_decomposition 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add compute_decomposition_cost() (main entry) + +Combines per_call_cost_us, current_total_us, +componentize_factor, unify_factor, recommended_direction, and +generate_rationale into a single DecompositionCost return. +The function is deterministic (no runtime failure modes); +returns DecompositionCost directly. +2 unit tests passing. + +Phase 6 complete: 20 unit tests passing (1 constants + 3 +per_call + 3 freq_multiplier + 4 componentize + 4 unify + 4 +recommended + 1 rationale + 2 compute). Total so far: 60 +unit tests passing across Phases 1-6. +Phase 7 (Cross-audit integration) next." +``` + +### Task 6.9: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 6 Decomposition cost is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 7. + +--- + +## Phase 7: Cross-audit integration (6 input JSONs + 3-tier mapping) + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The cross-audit integration layer consumes JSON from 6 existing audit scripts + the type registry. The per-aggregate `cross_audit_findings` is computed by 3-tier finding-to-aggregate mapping (function lookup, field lookup, heuristic fallback). The `result_coverage` and `type_alias_coverage` are computed from the findings + the PCG. + +7 unit tests; 1 commit per task; 8 tasks total. + +### Task 7.1: Add the 6 input JSON contracts + `read_input_json()` (the boundary function) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import json +import tempfile +from pathlib import Path +from src.code_path_audit import read_input_json + +def test_read_input_json_success() -> None: + """read_input_json returns Result[dict] on success.""" + with tempfile.TemporaryDirectory() as tmp: + p = Path(tmp) / "ok.json" + p.write_text(json.dumps({"findings": [{"file": "x.py", "line": 1}]})) + result = read_input_json(str(p)) + assert result.ok + assert result.data == {"findings": [{"file": "x.py", "line": 1}]} + +def test_read_input_json_missing_file() -> None: + """read_input_json returns Result with ErrorInfo when the file is missing.""" + result = read_input_json("/nonexistent/file.json") + assert not result.ok + assert len(result.errors) == 1 + assert result.errors[0].kind.value == "io" + assert "Cannot read" in result.errors[0].message + +def test_read_input_json_malformed_json() -> None: + """read_input_json returns Result with ErrorInfo when the JSON is malformed.""" + with tempfile.TemporaryDirectory() as tmp: + p = Path(tmp) / "bad.json" + p.write_text("{invalid json") + result = read_input_json(str(p)) + assert not result.ok + assert result.errors[0].kind.value == "invalid_input" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k read_input_json 2>&1 | Select-Object -Last 5` +Expected: 3 ImportErrors. + +- [ ] **Step 3: Implement `read_input_json()`** + +```python +def read_input_json(path: str) -> Result[dict]: + """Read a JSON file and return Result[dict]. + + Per error_handling.md stdlib I/O boundary pattern: catches + OSError (missing/permission denied) and json.JSONDecodeError + (malformed JSON), converts to ErrorInfo. + """ + p = Path(path) + try: + raw = p.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError) as e: + return Result( + data={}, + errors=[ErrorInfo( + kind=ErrorKind.IO, + message=f"Cannot read {path}: {e}", + source="read_input_json", + original=e, + )], + ) + try: + data = json.loads(raw) + except json.JSONDecodeError as e: + return Result( + data={}, + errors=[ErrorInfo( + kind=ErrorKind.INVALID_INPUT, + message=f"Malformed JSON in {path}: {e}", + source="read_input_json", + original=e, + )], + ) + if not isinstance(data, dict): + return Result( + data={}, + errors=[ErrorInfo( + kind=ErrorKind.INVALID_INPUT, + message=f"JSON root in {path} is not a dict", + source="read_input_json", + )], + ) + return Result(data=data) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k read_input_json 2>&1 | Select-Object -Last 5` +Expected: 3 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add read_input_json() (stdlib I/O boundary) + +Returns Result[dict] per error_handling.md. Catches OSError +(missing/permission denied) and json.JSONDecodeError (malformed +JSON), converts to ErrorInfo. The function is the entry point +for the 6 input JSON contracts. +3 unit tests passing." +``` + +### Task 7.2: Add the 6 input JSON contracts (the constant dict) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import INPUT_JSON_CONTRACTS + +def test_input_json_contracts_6_entries() -> None: + """INPUT_JSON_CONTRACTS has 6 entries: the 5 audit scripts + the type registry.""" + assert len(INPUT_JSON_CONTRACTS) == 6 + assert INPUT_JSON_CONTRACTS["audit_weak_types"]["producer"] == "scripts/audit_weak_types.py --json" + assert INPUT_JSON_CONTRACTS["audit_exception_handling"]["producer"] == "scripts/audit_exception_handling.py --json" + assert INPUT_JSON_CONTRACTS["audit_optional_in_3_files"]["producer"] == "scripts/audit_optional_in_3_files.py --json" + assert INPUT_JSON_CONTRACTS["audit_no_models_config_io"]["producer"] == "scripts/audit_no_models_config_io.py --json" + assert INPUT_JSON_CONTRACTS["audit_main_thread_imports"]["producer"] == "scripts/audit_main_thread_imports.py --json" + assert INPUT_JSON_CONTRACTS["type_registry"]["producer"] == "scripts/generate_type_registry.py --json" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_input_json_contracts_6_entries -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `INPUT_JSON_CONTRACTS`** + +```python +INPUT_JSON_CONTRACTS: dict[str, dict[str, str]] = { + "audit_weak_types": { + "producer": "scripts/audit_weak_types.py --json", + "filename": "audit_weak_types.json", + }, + "audit_exception_handling": { + "producer": "scripts/audit_exception_handling.py --json", + "filename": "audit_exception_handling.json", + }, + "audit_optional_in_3_files": { + "producer": "scripts/audit_optional_in_3_files.py --json", + "filename": "audit_optional_in_3_files.json", + }, + "audit_no_models_config_io": { + "producer": "scripts/audit_no_models_config_io.py --json", + "filename": "audit_no_models_config_io.json", + }, + "audit_main_thread_imports": { + "producer": "scripts/audit_main_thread_imports.py --json", + "filename": "audit_main_thread_imports.json", + }, + "type_registry": { + "producer": "scripts/generate_type_registry.py --json", + "filename": "type_registry.json", + }, +} +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_input_json_contracts_6_entries -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add INPUT_JSON_CONTRACTS (6 input sources) + +The 6 input sources the v2 audit consumes: 5 existing audit +scripts + the type registry. Each entry has producer (the +shell command to run) and filename (the output JSON file). +1 unit test passing." +``` + +### Task 7.3: Implement `find_enclosing_function()` (tier 1 of the 3-tier mapping) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import find_enclosing_function + +def test_find_enclosing_function_match() -> None: + """find_enclosing_function returns the function ref whose (file, line) range contains the finding.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=10, role="consumer") + refs = [ + f, + FunctionRef(fqname="src.x.z", file="src/x.py", line=100, role="consumer"), + ] + result = find_enclosing_function(file="src/x.py", line=15, function_refs=refs) + assert result is f + +def test_find_enclosing_function_no_match() -> None: + """find_enclosing_function returns None when no function contains the finding's (file, line).""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=10, role="consumer") + refs = [f] + result = find_enclosing_function(file="src/y.py", line=15, function_refs=refs) + assert result is None +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k find_enclosing_function 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `find_enclosing_function()`** + +```python +def find_enclosing_function( + file: str, + line: int, + function_refs: list[FunctionRef], +) -> FunctionRef | None: + """Tier 1 of the 3-tier mapping: find the function ref at (file, line). + + The current implementation uses exact file + closest line <= target line. + A more rigorous implementation would use AST ranges from the PCG; + the current heuristic is sufficient for the spec's "function lookup" tier. + """ + candidates = [r for r in function_refs if r.file == file and r.line <= line] + if not candidates: + return None + return max(candidates, key=lambda r: r.line) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k find_enclosing_function 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add find_enclosing_function() (tier 1 mapping) + +Tier 1 of the 3-tier finding-to-aggregate mapping: find +the FunctionRef whose (file, line) contains the finding. Uses +the closest-line heuristic; a more rigorous implementation +would use AST ranges from the PCG (future work). +2 unit tests passing." +``` + +### Task 7.4: Implement `result_coverage()` computation + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import compute_result_coverage + +def test_compute_result_coverage_no_producers() -> None: + """compute_result_coverage returns 0/0 when there are no producers.""" + cov = compute_result_coverage(producers=[], consumers=[], branches_on_errors=set()) + assert cov.total_producers == 0 + assert cov.result_producers == 0 + assert cov.total_consumers == 0 + assert cov.result_consumers == 0 + +def test_compute_result_coverage_full() -> None: + """compute_result_coverage counts producers and consumers correctly.""" + f1 = FunctionRef(fqname="src.a", file="src/a.py", line=1, role="producer") + f2 = FunctionRef(fqname="src.b", file="src/b.py", line=1, role="consumer") + cov = compute_result_coverage( + producers=[f1, f1], + consumers=[f2, f2, f2], + branches_on_errors={f2.fqname}, + ) + assert cov.total_producers == 2 + assert cov.result_producers == 2 + assert cov.total_consumers == 3 + assert cov.result_consumers == 1 + assert "100%" in cov.summary +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k compute_result 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `compute_result_coverage()`** + +```python +def compute_result_coverage( + producers: list[FunctionRef], + consumers: list[FunctionRef], + branches_on_errors: set[str], +) -> ResultCoverage: + """Compute the per-aggregate result coverage. + + result_producers: count of producers whose return type is Result[T] + (caller determines this from the PCG's P1 pass). + result_consumers: count of consumers whose fqname is in branches_on_errors + (caller determines this from AST analysis of the consumer's body). + + The function returns ResultCoverage (not Result[ResultCoverage]) because + the computation is deterministic; the caller passes pre-computed data. + """ + total_producers = len(producers) + result_producers = sum(1 for p in producers if p.fqname in branches_on_errors or "Result" in p.role) + total_consumers = len(consumers) + result_consumers = sum(1 for c in consumers if c.fqname in branches_on_errors) + pct_p = (result_producers / total_producers * 100) if total_producers > 0 else 0 + pct_c = (result_consumers / total_consumers * 100) if total_consumers > 0 else 0 + summary = f"{result_producers}/{total_producers} producers return Result[T] ({pct_p:.0f}%); {result_consumers}/{total_consumers} consumers branch on .errors ({pct_c:.0f}%)" + return ResultCoverage( + total_producers=total_producers, + result_producers=result_producers, + total_consumers=total_consumers, + result_consumers=result_consumers, + summary=summary, + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k compute_result 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add compute_result_coverage() (cross-check of doeh) + +Computes the per-aggregate result coverage: count of +producers returning Result[T] + count of consumers branching +on .errors. The summary is auto-generated. The caller passes +pre-computed branches_on_errors (from AST analysis of the +consumer's body). +2 unit tests passing." +``` + +### Task 7.5: Implement `type_alias_coverage()` computation + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import compute_type_alias_coverage + +def test_compute_type_alias_coverage_no_sites() -> None: + """compute_type_alias_coverage returns 0/0/0 when there are no sites.""" + cov = compute_type_alias_coverage(total_sites=0, typed_sites=0) + assert cov.total_sites == 0 + assert cov.typed_sites == 0 + assert cov.untyped_sites == 0 + +def test_compute_type_alias_coverage_partial() -> None: + """compute_type_alias_coverage computes untyped_sites = total - typed.""" + cov = compute_type_alias_coverage(total_sites=45, typed_sites=38) + assert cov.total_sites == 45 + assert cov.typed_sites == 38 + assert cov.untyped_sites == 7 + assert "84%" in cov.summary + assert "16%" in cov.summary +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k compute_type_alias 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `compute_type_alias_coverage()`** + +```python +def compute_type_alias_coverage(total_sites: int, typed_sites: int) -> TypeAliasCoverage: + """Compute the per-aggregate type alias coverage. + + total_sites: number of entry['key'] / entry.attr accesses in src/ + where the key/attr is in the aggregate's field set. + typed_sites: number of those accesses inside a function that takes + the aggregate as a typed parameter. + untyped_sites: total - typed. + + The function returns TypeAliasCoverage directly (deterministic). + """ + untyped = total_sites - typed_sites + pct_typed = (typed_sites / total_sites * 100) if total_sites > 0 else 0 + pct_untyped = (untyped / total_sites * 100) if total_sites > 0 else 0 + summary = f"{total_sites} total sites; {typed_sites} typed ({pct_typed:.0f}%); {untyped} untyped ({pct_untyped:.0f}%)" + return TypeAliasCoverage( + total_sites=total_sites, + typed_sites=typed_sites, + untyped_sites=untyped, + summary=summary, + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k compute_type_alias 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add compute_type_alias_coverage() (cross-check of dss) + +Computes the per-aggregate type alias coverage: total +field-access sites, typed sites (in typed-parameter +functions), untyped sites (in dict[str, Any] consumers). +The summary is auto-generated. +2 unit tests passing." +``` + +### Task 7.6: Implement `cross_audit_findings_aggregation` (per-aggregate aggregation) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import aggregate_cross_audit_findings + +def test_aggregate_cross_audit_findings_empty() -> None: + """aggregate_cross_audit_findings returns empty CrossAuditFindings for no findings.""" + findings = aggregate_cross_audit_findings( + audit_name="audit_weak_types", + findings=[], + example_file="", + example_line=0, + ) + assert findings.weak_types == () + assert findings.exception_handling == () + assert findings.optional_in_baseline == () + assert findings.config_io_ownership == () + assert findings.import_graph == () + +def test_aggregate_cross_audit_findings_one_audit() -> None: + """aggregate_cross_audit_findings puts 5 findings into the right bucket.""" + findings_list = [ + {"file": "src/a.py", "line": 1}, + {"file": "src/b.py", "line": 2}, + {"file": "src/c.py", "line": 3}, + {"file": "src/d.py", "line": 4}, + {"file": "src/e.py", "line": 5}, + ] + findings = aggregate_cross_audit_findings( + audit_name="audit_weak_types", + findings=findings_list, + example_file="src/a.py", + example_line=1, + ) + assert len(findings.weak_types) == 1 + assert findings.weak_types[0].audit_script == "audit_weak_types" + assert findings.weak_types[0].site_count == 5 + assert findings.weak_types[0].example_file == "src/a.py" +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k aggregate_cross 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `aggregate_cross_audit_findings()`** + +```python +def aggregate_cross_audit_findings( + audit_name: str, + findings: list[dict], + example_file: str, + example_line: int, +) -> CrossAuditFindings: + """Aggregate audit findings into a per-aggregate CrossAuditFindings. + + The function returns CrossAuditFindings with the findings in the + appropriate audit-script bucket (the other 4 buckets are empty). + """ + site_count = len(findings) + note = f"{site_count} sites in producer+consumer functions" if site_count > 0 else "" + finding = CrossAuditFinding( + audit_script=audit_name, + site_count=site_count, + example_file=example_file, + example_line=example_line, + note=note, + ) + empty = () + match audit_name: + case "audit_weak_types": + return CrossAuditFindings( + weak_types=(finding,), + exception_handling=empty, + optional_in_baseline=empty, + config_io_ownership=empty, + import_graph=empty, + ) + case "audit_exception_handling": + return CrossAuditFindings( + weak_types=empty, + exception_handling=(finding,), + optional_in_baseline=empty, + config_io_ownership=empty, + import_graph=empty, + ) + case "audit_optional_in_3_files": + return CrossAuditFindings( + weak_types=empty, + exception_handling=empty, + optional_in_baseline=(finding,), + config_io_ownership=empty, + import_graph=empty, + ) + case "audit_no_models_config_io": + return CrossAuditFindings( + weak_types=empty, + exception_handling=empty, + optional_in_baseline=empty, + config_io_ownership=(finding,), + import_graph=empty, + ) + case "audit_main_thread_imports": + return CrossAuditFindings( + weak_types=empty, + exception_handling=empty, + optional_in_baseline=empty, + config_io_ownership=empty, + import_graph=(finding,), + ) + case _: + return CrossAuditFindings( + weak_types=empty, + exception_handling=empty, + optional_in_baseline=empty, + config_io_ownership=empty, + import_graph=empty, + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k aggregate_cross 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add aggregate_cross_audit_findings() (per-aggregate) + +Aggregates findings from one input audit into a per-aggregate +CrossAuditFindings struct. The 6 audit names dispatch to the +6 buckets via the match statement. Unknown audit names return +empty (defensive default). +2 unit tests passing." +``` + +### Task 7.7: Implement the run-all-6-inputs convenience (the conductor of the cross-audit layer) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import tempfile +from pathlib import Path +from src.code_path_audit import run_all_cross_audit_reads, RESULT, ErrorInfo + +def test_run_all_cross_audit_reads_missing_dir() -> None: + """run_all_cross_audit_reads returns empty dicts when the dir is missing.""" + result = run_all_cross_audit_reads("/nonexistent/audit_inputs") + assert result == {} + +def test_run_all_cross_audit_reads_partial() -> None: + """run_all_cross_audit_reads returns the 6 inputs that exist; missing inputs are empty dicts.""" + with tempfile.TemporaryDirectory() as tmp: + (Path(tmp) / "audit_weak_types.json").write_text('{"findings": []}') + (Path(tmp) / "audit_exception_handling.json").write_text('{"findings": []}') + result = run_all_cross_audit_reads(tmp) + assert "audit_weak_types" in result + assert "audit_exception_handling" in result + assert "audit_optional_in_3_files" not in result or result["audit_optional_in_3_files"] == {} + assert "type_registry" not in result or result["type_registry"] == {} +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k run_all_cross 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `run_all_cross_audit_reads()`** + +```python +def run_all_cross_audit_reads(audit_inputs_dir: str) -> dict[str, dict]: + """Read all 6 input JSONs from audit_inputs_dir. + + Returns a dict keyed by audit_name. Missing files are tolerated; + the value is an empty dict. Malformed files are tolerated; the + value is an empty dict (the read_input_json() Result.errors is + silently dropped for the convenience function; the full read + is available via read_input_json() directly). + """ + out: dict[str, dict] = {} + p = Path(audit_inputs_dir) + if not p.exists(): + return out + for audit_name, contract in INPUT_JSON_CONTRACTS.items(): + json_path = p / contract["filename"] + if not json_path.exists(): + out[audit_name] = {} + continue + result = read_input_json(str(json_path)) + if result.ok: + out[audit_name] = result.data + else: + out[audit_name] = {} + return out +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k run_all_cross 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add run_all_cross_audit_reads() (convenience) + +Reads all 6 input JSONs from audit_inputs_dir. Missing and +malformed files are tolerated (return empty dict). The full +per-file error reporting is available via read_input_json() +directly. The convenience function is used by run_audit() in +Phase 9. +2 unit tests passing. + +Phase 7 complete: 13 unit tests passing across Phases 7 +(3 read_input_json + 1 contracts + 2 find_enclosing + 2 +result_coverage + 2 type_alias + 2 aggregate + 2 run_all). +Total so far: 73 unit tests passing across Phases 1-7. +Phase 8 (v2 DSL) next." +``` + +### Task 7.8: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 7 Cross-audit integration is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 8. + +--- + +## Phase 8: v2 DSL (14 new tagged words + flat-section format) + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The v2 DSL extends the v1 postfix DSL with 14 new tagged words. The format is flat sections (streamable, tag-scannable). 5 unit tests; 1 commit per task; 6 tasks total. + +### Task 8.1: Add the `DSL_WORD_ARITY_V2` constant + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import DSL_WORD_ARITY_V2 + +def test_dsl_word_arity_v2_14_new_words() -> None: + """DSL_WORD_ARITY_V2 has 14 new tagged words (per spec section 5.1).""" + expected_words = { + "kind", "mem-dim", "fn-ref", "access-pattern", "ap-evidence", + "frequency", "freq-evidence", "result-coverage", "type-alias-coverage", + "cross-audit-finding", "cross-audit-findings", "decomp-cost", + "opt-candidate", "is-candidate", + } + assert expected_words.issubset(set(DSL_WORD_ARITY_V2.keys())) + assert DSL_WORD_ARITY_V2["kind"] == 1 + assert DSL_WORD_ARITY_V2["fn-ref"] == 4 + assert DSL_WORD_ARITY_V2["decomp-cost"] == 8 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_dsl_word_arity_v2_14_new_words -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `DSL_WORD_ARITY_V2`** + +```python +DSL_WORD_ARITY_V2: dict[str, int] = { + "kind": 1, + "mem-dim": 1, + "fn-ref": 4, + "access-pattern": 1, + "ap-evidence": 4, + "frequency": 1, + "freq-evidence": 4, + "result-coverage": 4, + "type-alias-coverage": 3, + "cross-audit-finding": 5, + "cross-audit-findings": 5, + "decomp-cost": 8, + "opt-candidate": 7, + "is-candidate": 1, +} +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_dsl_word_arity_v2_14_new_words -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add DSL_WORD_ARITY_V2 (14 new tagged words) + +The 14 new v2 words from spec section 5.1 with their arities. +Preserved from v1: fn, call, mut, exp-op, pair, int, action. +1 unit test passing." +``` + +### Task 8.2: Implement `to_dsl_v2()` writer (flat-section format) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ( + to_dsl_v2, + AggregateProfile, + FunctionRef, + AccessPatternEvidence, + FrequencyEvidence, + ResultCoverage, + TypeAliasCoverage, + CrossAuditFindings, + DecompositionCost, +) +from datetime import date + +def test_to_dsl_v2_includes_aggregate_kind_section() -> None: + """to_dsl_v2 emits the \\ === aggregate_kind === section.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = AggregateProfile( + name="Metadata", + aggregate_kind="typealias", + memory_dim="discussion", + producers=(f,), + consumers=(), + access_pattern="whole_struct", + access_pattern_evidence=(), + frequency="per_turn", + frequency_evidence=(), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "hold", "no data", None, 0, False), + optimization_candidates=(), + is_candidate=False, + ) + dsl = to_dsl_v2(profile, generated_date="2026-06-22") + assert "\\ === aggregate_kind ===" in dsl + assert '"typealias" kind' in dsl + assert "\\ === memory_dim ===" in dsl + assert '"discussion" mem-dim' in dsl +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_to_dsl_v2_includes_aggregate_kind_section -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `to_dsl_v2()` (the flat-section writer)** + +```python +def _atom(s: str) -> str: + """Format a string as a postfix DSL atom (bare or quoted).""" + if any(c in s for c in ('"', "'", " ", "\t", "\n", "(", ")", "{", "}")): + return f'"{s}"' + return s + +def to_dsl_v2(profile: AggregateProfile, generated_date: str = "") -> str: + """Serialize an AggregateProfile to v2 postfix DSL (flat sections).""" + lines: list[str] = [] + lines.append(f'\\ AggregateProfile: "{profile.name}"') + lines.append(f"\\ generated {generated_date} by src.code_path_audit v2") + lines.append("") + lines.append("\\ === aggregate_kind ===") + lines.append(f' "{profile.aggregate_kind}" kind') + lines.append("") + lines.append("\\ === memory_dim ===") + lines.append(f' "{profile.memory_dim}" mem-dim') + lines.append("") + lines.append(f"\\ === producers ({len(profile.producers)} items) ===") + for p in profile.producers: + lines.append(f' "{p.fqname}" "{p.file}" {p.line} "{p.role}" fn-ref') + lines.append("") + lines.append(f"\\ === consumers ({len(profile.consumers)} items) ===") + for c in profile.consumers: + lines.append(f' "{c.fqname}" "{c.file}" {c.line} "{c.role}" fn-ref') + lines.append("") + lines.append("\\ === access_pattern ===") + lines.append(f' "{profile.access_pattern}" access-pattern') + lines.append("") + lines.append(f"\\ === access_pattern_evidence ({len(profile.access_pattern_evidence)} items) ===") + for ev in profile.access_pattern_evidence: + lines.append(f' "{ev.function.fqname}" "{ev.pattern}" {len(ev.field_accesses)} "{ev.confidence}" ap-evidence') + lines.append("") + lines.append("\\ === frequency ===") + lines.append(f' "{profile.frequency}" frequency') + lines.append("") + lines.append(f"\\ === frequency_evidence ({len(profile.frequency_evidence)} items) ===") + for ev in profile.frequency_evidence: + lines.append(f' "{ev.function.fqname}" "{ev.frequency}" "{ev.source}" "{ev.note}" freq-evidence') + lines.append("") + rc = profile.result_coverage + lines.append("\\ === result_coverage ===") + lines.append(f" {rc.total_producers} {rc.result_producers} {rc.total_consumers} {rc.result_consumers} result-coverage") + lines.append("") + tac = profile.type_alias_coverage + lines.append("\\ === type_alias_coverage ===") + lines.append(f" {tac.total_sites} {tac.typed_sites} {tac.untyped_sites} type-alias-coverage") + lines.append("") + lines.append("\\ === cross_audit_findings ===") + for f in profile.cross_audit_findings.weak_types: + lines.append(f' "{f.audit_script}" {f.site_count} "{f.example_file}" {f.example_line} "{f.note}" cross-audit-finding') + for f in profile.cross_audit_findings.exception_handling: + lines.append(f' "{f.audit_script}" {f.site_count} "{f.example_file}" {f.example_line} "{f.note}" cross-audit-finding') + for f in profile.cross_audit_findings.optional_in_baseline: + lines.append(f' "{f.audit_script}" {f.site_count} "{f.example_file}" {f.example_line} "{f.note}" cross-audit-finding') + for f in profile.cross_audit_findings.config_io_ownership: + lines.append(f' "{f.audit_script}" {f.site_count} "{f.example_file}" {f.example_line} "{f.note}" cross-audit-finding') + for f in profile.cross_audit_findings.import_graph: + lines.append(f' "{f.audit_script}" {f.site_count} "{f.example_file}" {f.example_line} "{f.note}" cross-audit-finding') + lines.append(" 5 cross-audit-findings") + lines.append("") + dc = profile.decomposition_cost + lines.append("\\ === decomposition_cost ===") + batch_size_str = str(dc.batch_size) if dc.batch_size is not None else "nil" + lines.append(f" {dc.current_cost_estimate} {dc.componentize_savings} {dc.unify_savings} \"{dc.recommended_direction}\" \"{dc.recommended_rationale}\" {batch_size_str} {dc.struct_field_count} {str(dc.struct_frozen).lower()} decomp-cost") + lines.append("") + lines.append(f"\\ === optimization_candidates ({len(profile.optimization_candidates)} items) ===") + for cand in profile.optimization_candidates: + lines.append(f' "{cand.candidate}" "{cand.direction}" {len(cand.affected_files)} {cand.estimated_savings_us} "{cand.effort}" "{cand.priority}" "{cand.cross_ref}" opt-candidate') + lines.append("") + lines.append("\\ === is_candidate ===") + lines.append(f" {'true' if profile.is_candidate else 'false'} is-candidate") + return "\n".join(lines) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_to_dsl_v2_includes_aggregate_kind_section -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add to_dsl_v2() (flat-section writer) + +The v2 postfix DSL writer. Each section of the +AggregateProfile is a self-contained tagged record; a reader +can scan for '\\\\ === aggregate_kind ===' to find the kind +without parsing the whole file. 14 sections total (the +optional mermaid + markdown are NOT in the DSL; they're +rendered to .mmd + .md files separately). +1 unit test passing." +``` + +### Task 8.3: Implement `to_markdown()` (10 sections) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ( + to_markdown, + AggregateProfile, + FunctionRef, + AccessPatternEvidence, + FrequencyEvidence, + ResultCoverage, + TypeAliasCoverage, + CrossAuditFindings, + DecompositionCost, +) + +def test_to_markdown_10_sections() -> None: + """to_markdown emits the 10 sections per spec section 8.1.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = AggregateProfile( + name="Metadata", + aggregate_kind="typealias", + memory_dim="discussion", + producers=(f,), + consumers=(), + access_pattern="whole_struct", + access_pattern_evidence=(), + frequency="per_turn", + frequency_evidence=(), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "hold", "no data", None, 0, False), + optimization_candidates=(), + is_candidate=False, + ) + md = to_markdown(profile) + assert "# Aggregate Profile: Metadata" in md + assert "## Pipeline summary" in md + assert "## Access pattern" in md + assert "## Frequency" in md + assert "## Result coverage" in md + assert "## Type alias coverage" in md + assert "## Cross-audit findings" in md + assert "## Decomposition cost" in md + assert "## Optimization candidates" in md + assert "## Verdict" in md +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_to_markdown_10_sections -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `to_markdown()`** + +```python +def to_markdown(profile: AggregateProfile) -> str: + """Render the per-aggregate markdown (10 sections per spec section 8.1).""" + lines: list[str] = [] + lines.append(f"# Aggregate Profile: {profile.name}") + lines.append("") + lines.append(f"**Aggregate kind:** {profile.aggregate_kind}") + lines.append(f"**Memory dim:** {profile.memory_dim}") + lines.append(f"**Is candidate:** {profile.is_candidate}") + lines.append("") + lines.append("## Pipeline summary") + lines.append("") + lines.append(f"- Producers: {len(profile.producers)}") + lines.append(f"- Consumers: {len(profile.consumers)}") + lines.append("") + lines.append("## Access pattern") + lines.append("") + lines.append(f"**Dominant pattern:** {profile.access_pattern}") + lines.append(f"**Evidence count:** {len(profile.access_pattern_evidence)}") + lines.append("") + lines.append("## Frequency") + lines.append("") + lines.append(f"**Dominant frequency:** {profile.frequency}") + lines.append(f"**Evidence count:** {len(profile.frequency_evidence)}") + lines.append("") + lines.append("## Result coverage") + lines.append("") + lines.append(f"**Summary:** {profile.result_coverage.summary}") + lines.append("") + lines.append("## Type alias coverage") + lines.append("") + lines.append(f"**Summary:** {profile.type_alias_coverage.summary}") + lines.append("") + lines.append("## Cross-audit findings") + lines.append("") + lines.append("| Audit script | Site count | Example | Note |") + lines.append("|---|---|---|---|") + for f in profile.cross_audit_findings.weak_types: + lines.append(f"| {f.audit_script} | {f.site_count} | {f.example_file}:{f.example_line} | {f.note} |") + for f in profile.cross_audit_findings.exception_handling: + lines.append(f"| {f.audit_script} | {f.site_count} | {f.example_file}:{f.example_line} | {f.note} |") + for f in profile.cross_audit_findings.optional_in_baseline: + lines.append(f"| {f.audit_script} | {f.site_count} | {f.example_file}:{f.example_line} | {f.note} |") + for f in profile.cross_audit_findings.config_io_ownership: + lines.append(f"| {f.audit_script} | {f.site_count} | {f.example_file}:{f.example_line} | {f.note} |") + for f in profile.cross_audit_findings.import_graph: + lines.append(f"| {f.audit_script} | {f.site_count} | {f.example_file}:{f.example_line} | {f.note} |") + lines.append("") + lines.append("## Decomposition cost") + lines.append("") + lines.append(f"**Current cost estimate:** {profile.decomposition_cost.current_cost_estimate} us") + lines.append(f"**Componentize savings:** {profile.decomposition_cost.componentize_savings} us") + lines.append(f"**Unify savings:** {profile.decomposition_cost.unify_savings} us") + lines.append(f"**Recommended direction:** {profile.decomposition_cost.recommended_direction}") + lines.append(f"**Rationale:** {profile.decomposition_cost.recommended_rationale}") + lines.append("") + lines.append("## Optimization candidates") + lines.append("") + if profile.optimization_candidates: + for cand in profile.optimization_candidates: + lines.append(f"- **{cand.direction}** ({cand.effort}, {cand.priority}): {cand.candidate}") + else: + lines.append("_(none)_") + lines.append("") + lines.append("## Verdict") + lines.append("") + lines.append(f"{profile.decomposition_cost.recommended_rationale}") + return "\n".join(lines) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_to_markdown_10_sections -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add to_markdown() (10 sections) + +The per-aggregate markdown renderer. The 10 sections: Header, +Pipeline summary, Access pattern, Frequency, Result coverage, +Type alias coverage, Cross-audit findings, Decomposition cost, +Optimization candidates, Verdict. The Verdict section +duplicates the decomposition_cost.recommended_rationale for +the 1-sentence summary view. +1 unit test passing." +``` + +### Task 8.4: Implement `to_tree()` (box-drawing prefix tree) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import ( + to_tree, + AggregateProfile, + FunctionRef, + ResultCoverage, + TypeAliasCoverage, + CrossAuditFindings, + DecompositionCost, +) + +def test_to_tree_box_drawing() -> None: + """to_tree uses box-drawing characters (├, └, │).""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = AggregateProfile( + name="Metadata", + aggregate_kind="typealias", + memory_dim="discussion", + producers=(f,), + consumers=(), + access_pattern="whole_struct", + access_pattern_evidence=(), + frequency="per_turn", + frequency_evidence=(), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "hold", "no data", None, 0, False), + optimization_candidates=(), + is_candidate=False, + ) + tree = to_tree(profile) + assert "Metadata" in tree + assert "├─" in tree or "└─" in tree + assert "│" in tree +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_to_tree_box_drawing -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `to_tree()`** + +```python +def to_tree(profile: AggregateProfile) -> str: + """Render the per-aggregate prefix tree (box-drawing).""" + lines: list[str] = [f"Metadata: {profile.name}"] + lines.append(f"├─ kind: {profile.aggregate_kind}") + lines.append(f"├─ memory_dim: {profile.memory_dim}") + lines.append(f"├─ producers: [{len(profile.producers)}]") + for p in profile.producers: + lines.append(f"│ └─ {p.fqname} ({p.role})") + lines.append(f"├─ consumers: [{len(profile.consumers)}]") + for c in profile.consumers: + lines.append(f"│ └─ {c.fqname} ({c.role})") + lines.append(f"├─ access_pattern: {profile.access_pattern}") + lines.append(f"├─ frequency: {profile.frequency}") + lines.append(f"├─ result_coverage: {profile.result_coverage.summary}") + lines.append(f"├─ type_alias_coverage: {profile.type_alias_coverage.summary}") + lines.append(f"├─ cross_audit_findings: {len(profile.cross_audit_findings.weak_types) + len(profile.cross_audit_findings.exception_handling) + len(profile.cross_audit_findings.optional_in_baseline) + len(profile.cross_audit_findings.config_io_ownership) + len(profile.cross_audit_findings.import_graph)} findings") + lines.append(f"├─ decomposition_cost: {profile.decomposition_cost.recommended_direction} ({profile.decomposition_cost.current_cost_estimate} us)") + lines.append(f"└─ optimization_candidates: [{len(profile.optimization_candidates)}]") + return "\n".join(lines) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_to_tree_box_drawing -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add to_tree() (box-drawing prefix tree) + +The compact, scannable view of the per-aggregate profile. +Uses ├─, └─, │ box-drawing characters. The 3 levels of +nesting: top-level (kind, memory_dim, etc.) + producers/ +consumers + nested fn-refs. +1 unit test passing." +``` + +### Task 8.5: Implement `parse_dsl_v2()` (round-trip) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import to_dsl_v2, parse_dsl_v2, Result + +def test_parse_dsl_v2_round_trip_aggregate_kind() -> None: + """parse_dsl_v2(to_dsl_v2(profile)) recovers the aggregate_kind section.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = AggregateProfile( + name="Metadata", + aggregate_kind="typealias", + memory_dim="discussion", + producers=(f,), + consumers=(), + access_pattern="whole_struct", + access_pattern_evidence=(), + frequency="per_turn", + frequency_evidence=(), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "hold", "no data", None, 0, False), + optimization_candidates=(), + is_candidate=False, + ) + dsl = to_dsl_v2(profile) + parsed = parse_dsl_v2(dsl) + assert isinstance(parsed, Result) + assert parsed.ok + assert "typealias" in str(parsed.data) + +def test_parse_dsl_v2_malformed() -> None: + """parse_dsl_v2 returns Result with errors for malformed DSL.""" + result = parse_dsl_v2("\\ invalid dsl with no tagged words\n") + assert not result.ok +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k parse_dsl_v2 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `parse_dsl_v2()`** + +```python +import re + +def parse_dsl_v2(text: str) -> Result[dict]: + """Parse a v2 postfix DSL into a nested dict (round-trip). + + Returns Result[dict]. Malformed DSL returns Result with errors. + """ + tokens: list[str] = [] + for line in text.splitlines(): + line = re.sub(r"\\.*", "", line) + if not line.strip(): + continue + i = 0 + while i < len(line): + c = line[i] + if c.isspace(): + i += 1 + continue + if c == '"': + j = line.find('"', i + 1) + if j == -1: + j = len(line) + tokens.append(line[i + 1 : j]) + i = j + 1 + else: + j = i + while j < len(line) and not line[j].isspace(): + j += 1 + tokens.append(line[i:j]) + i = j + stack: list[Any] = [] + i = 0 + while i < len(tokens): + t = tokens[i] + if t == "list" and stack and isinstance(stack[-1], int): + count = stack.pop() + items = stack[-count:] if count > 0 else [] + stack = stack[:-count] if count > 0 else stack + stack.append(items) + i += 1 + continue + if t in DSL_WORD_ARITY_V2: + nargs = DSL_WORD_ARITY_V2[t] + args = stack[-nargs:] if nargs else [] + stack = stack[:-nargs] if nargs else stack + stack.append({"_tag": t, "_args": args}) + i += 1 + continue + if t in ("true", "false"): + stack.append(t == "true") + elif t == "nil": + stack.append(None) + elif t.lstrip("-").isdigit(): + stack.append(int(t)) + else: + stack.append(t) + i += 1 + if len(stack) != 1: + return Result( + data={}, + errors=[ErrorInfo( + kind=ErrorKind.INVALID_INPUT, + message=f"DSL parse error: stack has {len(stack)} items at end (expected 1)", + source="parse_dsl_v2", + )], + ) + return Result(data=stack[0]) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k parse_dsl_v2 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add parse_dsl_v2() (round-trip parser) + +Returns Result[dict]. The 14 new v2 words have their arities +in DSL_WORD_ARITY_V2 (Task 8.1). The 'N list' pattern is +preserved from v1. Malformed DSL returns Result with errors. +2 unit tests passing. + +Phase 8 complete: 5 unit tests passing across Phases 8 +(1 arity + 1 to_dsl + 1 to_markdown + 1 to_tree + 2 parse). +Total so far: 78 unit tests passing across Phases 1-8. +Phase 9 (run_audit + CLI + MCP) next." +``` + +### Task 8.6: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 8 v2 DSL is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 9. + +--- + +## Phase 9: run_audit() main entry + CLI + MCP tool + +**Files:** `src/code_path_audit.py`, `tests/test_code_path_audit.py`. + +The main entry point. The 13-aggregate loop, the per-aggregate profile synthesis, the output rendering, the CLI, and the MCP tool wrapper. + +6 unit tests; 1 commit per task; 6 tasks total. + +### Task 9.1: Add the 13 in-scope aggregates constant + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import AGGREGATES_IN_SCOPE, CANDIDATE_AGGREGATES + +def test_aggregates_in_scope_10_real() -> None: + """AGGREGATES_IN_SCOPE has 10 real aggregates (the 10 canonical TypeAliases).""" + expected = {"Metadata", "FileItem", "FileItems", "CommsLogEntry", "CommsLog", "HistoryMessage", "History", "ToolDefinition", "ToolCall", "Result"} + assert set(AGGREGATES_IN_SCOPE) == expected + +def test_candidate_aggregates_3_placeholders() -> None: + """CANDIDATE_AGGREGATES has the 3 candidate aggregates (NOT on master).""" + expected = {"ToolSpec", "ChatMessage", "ProviderHistory"} + assert set(CANDIDATE_AGGREGATES) == expected +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k "aggregates_in_scope or candidate_aggregates" 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement the 2 constants** + +```python +AGGREGATES_IN_SCOPE: tuple[str, ...] = ( + "Metadata", + "FileItem", + "FileItems", + "CommsLogEntry", + "CommsLog", + "HistoryMessage", + "History", + "ToolDefinition", + "ToolCall", + "Result", +) + +CANDIDATE_AGGREGATES: tuple[str, ...] = ( + "ToolSpec", + "ChatMessage", + "ProviderHistory", +) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k "aggregates_in_scope or candidate_aggregates" 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add AGGREGATES_IN_SCOPE + CANDIDATE_AGGREGATES + +The 10 in-scope aggregates (canonical TypeAliases from +src/type_aliases.py) + the 3 candidate aggregates (NOT on +master; forward-compat placeholders). Total: 13. +2 unit tests passing." +``` + +### Task 9.2: Implement `synthesize_aggregate_profile()` (per-aggregate profile builder) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import synthesize_aggregate_profile, FunctionRef + +def test_synthesize_real_aggregate() -> None: + """synthesize_aggregate_profile returns a real AggregateProfile for a known aggregate.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = synthesize_aggregate_profile( + aggregate="Metadata", + pcg_producers={"Metadata": [f]}, + pcg_consumers={"Metadata": [f]}, + audit_inputs={}, + overrides={}, + is_candidate=False, + ) + assert profile.name == "Metadata" + assert profile.aggregate_kind == "typealias" + assert profile.memory_dim == "discussion" + assert profile.is_candidate is False + +def test_synthesize_candidate_aggregate() -> None: + """synthesize_aggregate_profile returns a candidate placeholder for an unknown aggregate.""" + f = FunctionRef(fqname="src.x.y", file="src/x.py", line=1, role="producer") + profile = synthesize_aggregate_profile( + aggregate="ChatMessage", + pcg_producers={"ChatMessage": []}, + pcg_consumers={"ChatMessage": []}, + audit_inputs={}, + overrides={}, + is_candidate=True, + ) + assert profile.name == "ChatMessage" + assert profile.aggregate_kind == "candidate_dataclass" + assert profile.is_candidate is True + assert profile.producers == () + assert profile.consumers == () +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k synthesize 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `synthesize_aggregate_profile()`** + +```python +def synthesize_aggregate_profile( + aggregate: str, + pcg_producers: dict[str, list[FunctionRef]], + pcg_consumers: dict[str, list[FunctionRef]], + audit_inputs: dict[str, dict], + overrides: dict, + is_candidate: bool, +) -> AggregateProfile: + """Synthesize one AggregateProfile. + + If is_candidate=True, returns a placeholder with empty producers/consumers + and is_candidate=True. If is_candidate=False, the producers/consumers + are sourced from the PCG. + """ + if is_candidate: + return AggregateProfile( + name=aggregate, + aggregate_kind="candidate_dataclass", + memory_dim="discussion" if aggregate == "ChatMessage" else "unknown", + producers=(), + consumers=(), + access_pattern="mixed", + access_pattern_evidence=(), + frequency="unknown", + frequency_evidence=(), + result_coverage=ResultCoverage(0, 0, 0, 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "insufficient_data", "candidate aggregate; would be detected after any_type_componentization_20260621 merges", None, 0, False), + optimization_candidates=(), + is_candidate=True, + ) + producers = tuple(pcg_producers.get(aggregate, [])) + consumers = tuple(pcg_consumers.get(aggregate, [])) + kind: AggregateKind = "typealias" if aggregate in AGGREGATES_IN_SCOPE else "dataclass" + memory_dim = classify_memory_dim( + aggregate, + producers[0].file if producers else "", + overrides.get("memory_dim", {}), + ) + return AggregateProfile( + name=aggregate, + aggregate_kind=kind, + memory_dim=memory_dim, + producers=producers, + consumers=consumers, + access_pattern="whole_struct", + access_pattern_evidence=(), + frequency="per_turn", + frequency_evidence=(), + result_coverage=ResultCoverage(len(producers), len(producers), len(consumers), 0, ""), + type_alias_coverage=TypeAliasCoverage(0, 0, 0, ""), + cross_audit_findings=CrossAuditFindings((), (), (), (), ()), + decomposition_cost=DecompositionCost(0, 0, 0, "hold", "no data", None, 0, False), + optimization_candidates=(), + is_candidate=False, + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k synthesize 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add synthesize_aggregate_profile() (per-aggregate) + +Synthesizes one AggregateProfile. If is_candidate=True, +returns a placeholder (empty producers/consumers, the +inferred AggregateKind + MemoryDim). If is_candidate=False, +sources producers/consumers from the PCG and computes the +memory_dim via the classifier. + +The full per-aggregate synthesis (cross-audit, decomposition +cost, optimization candidates) is in Tasks 9.3-9.4. +2 unit tests passing." +``` + +### Task 9.3: Implement `run_audit()` main entry point + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import tempfile +from pathlib import Path +from src.code_path_audit import run_audit, Result + +def test_run_audit_returns_result() -> None: + """run_audit returns Result[AuditSummary] per error_handling.md.""" + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=tmp, + audit_inputs_dir=tmp, + output_dir=tmp, + date="2026-06-22", + ) + assert isinstance(result, Result) + assert result.ok + +def test_run_audit_produces_13_aggregates() -> None: + """run_audit produces 13 AggregateProfiles (10 in-scope + 3 candidate).""" + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=tmp, + audit_inputs_dir=tmp, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + assert len(result.data.aggregate_profiles) == 13 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k run_audit 2>&1 | Select-Object -Last 5` +Expected: 2 ImportErrors. + +- [ ] **Step 3: Implement `run_audit()` and `AuditSummary`** + +```python +@dataclass(frozen=True) +class AuditSummary: + aggregate_profiles: tuple[AggregateProfile, ...] + output_paths: dict[str, str] + +def run_audit( + src_dir: str, + audit_inputs_dir: str, + output_dir: str, + date: str, +) -> Result[AuditSummary]: + """Run the full v2 audit pipeline. + + 1. Read the 6 input JSONs from audit_inputs_dir. + 2. Build the PCG from src_dir. + 3. For each of the 13 aggregates, synthesize the profile. + 4. Render the 13 per-aggregate files + 4 top-level rollups. + 5. Return Result[AuditSummary] with the profiles + output paths. + + Returns Result[AuditSummary]. Missing inputs are tolerated. + """ + audit_inputs = run_all_cross_audit_reads(audit_inputs_dir) + pcg_result = build_pcg(src_dir) + if not pcg_result.ok: + return Result(data=AuditSummary(aggregate_profiles=(), output_paths={}), errors=pcg_result.errors) + pcg = pcg_result.data + overrides: dict = {} + profiles: list[AggregateProfile] = [] + for aggregate in AGGREGATES_IN_SCOPE: + profile = synthesize_aggregate_profile( + aggregate=aggregate, + pcg_producers=pcg.producers, + pcg_consumers=pcg.consumers, + audit_inputs=audit_inputs, + overrides=overrides, + is_candidate=False, + ) + profiles.append(profile) + for candidate in CANDIDATE_AGGREGATES: + profile = synthesize_aggregate_profile( + aggregate=candidate, + pcg_producers=pcg.producers, + pcg_consumers=pcg.consumers, + audit_inputs=audit_inputs, + overrides=overrides, + is_candidate=True, + ) + profiles.append(profile) + output_dir_p = Path(output_dir) / date + (output_dir_p / "aggregates").mkdir(parents=True, exist_ok=True) + output_paths: dict[str, str] = {} + for profile in profiles: + agg_dir = output_dir_p / "aggregates" + dsl_path = agg_dir / f"{profile.name}.dsl" + md_path = agg_dir / f"{profile.name}.md" + tree_path = agg_dir / f"{profile.name}.tree" + dsl_path.write_text(to_dsl_v2(profile, generated_date=date), encoding="utf-8") + md_path.write_text(to_markdown(profile), encoding="utf-8") + tree_path.write_text(to_tree(profile), encoding="utf-8") + output_paths[profile.name] = str(dsl_path) + return Result(data=AuditSummary(aggregate_profiles=tuple(profiles), output_paths=output_paths)) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k run_audit 2>&1 | Select-Object -Last 5` +Expected: 2 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add run_audit() main entry point + +Returns Result[AuditSummary] per error_handling.md. Reads +6 input JSONs, builds the PCG, synthesizes 13 profiles, +renders 13 per-aggregate files. The 4 top-level rollups are +rendered by render_rollups() in Task 9.4. +2 unit tests passing." +``` + +### Task 9.4: Implement `render_rollups()` (the 4 top-level files) + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import tempfile +from pathlib import Path +from src.code_path_audit import render_rollups, run_audit, Result + +def test_render_rollups_produces_4_files() -> None: + """render_rollups produces summary.md, cross_audit_summary.md, decomposition_matrix.md, candidates.md.""" + with tempfile.TemporaryDirectory() as tmp: + audit_result = run_audit(src_dir=tmp, audit_inputs_dir=tmp, output_dir=tmp, date="2026-06-22") + assert audit_result.ok + rollup_paths = render_rollups(audit_result.data, Path(tmp) / "2026-06-22") + assert "summary.md" in rollup_paths + assert "cross_audit_summary.md" in rollup_paths + assert "decomposition_matrix.md" in rollup_paths + assert "candidates.md" in rollup_paths +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_render_rollups_produces_4_files -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `render_rollups()`** + +```python +def render_rollups(summary: AuditSummary, output_dir: Path) -> dict[str, str]: + """Render the 4 top-level rollup files. + + Returns dict mapping rollup name -> output path. + """ + output_dir.mkdir(parents=True, exist_ok=True) + summary_path = output_dir / "summary.md" + cross_audit_path = output_dir / "cross_audit_summary.md" + decomposition_matrix_path = output_dir / "decomposition_matrix.md" + candidates_path = output_dir / "candidates.md" + profiles = summary.aggregate_profiles + summary_lines: list[str] = ["# Code Path & Data Pipeline Audit Summary", "", f"Generated for {len(profiles)} aggregates", ""] + summary_lines.append("## 4-mem-dim rollup") + summary_lines.append("") + by_dim: dict[str, list[str]] = {} + for p in profiles: + by_dim.setdefault(p.memory_dim, []).append(p.name) + for dim, names in sorted(by_dim.items()): + summary_lines.append(f"- **{dim}** ({len(names)}): {', '.join(names)}") + summary_lines.append("") + summary_lines.append("## Cross-validation verdict") + summary_lines.append("") + for p in profiles: + rc = p.result_coverage + tac = p.type_alias_coverage + summary_lines.append(f"- **{p.name}**: result_coverage={rc.summary}; type_alias_coverage={tac.summary}") + summary_path.write_text("\n".join(summary_lines), encoding="utf-8") + cross_audit_lines: list[str] = ["# Cross-Audit Summary", "", "| Aggregate | weak_types | exception_handling | optional_in_baseline | config_io | import_graph | total |", "|---|---|---|---|---|---|---|"] + for p in profiles: + cf = p.cross_audit_findings + total = len(cf.weak_types) + len(cf.exception_handling) + len(cf.optional_in_baseline) + len(cf.config_io_ownership) + len(cf.import_graph) + cross_audit_lines.append(f"| {p.name} | {len(cf.weak_types)} | {len(cf.exception_handling)} | {len(cf.optional_in_baseline)} | {len(cf.config_io_ownership)} | {len(cf.import_graph)} | {total} |") + cross_audit_path.write_text("\n".join(cross_audit_lines), encoding="utf-8") + deco_lines: list[str] = ["# Decomposition Matrix", "", "## Top 10 candidates by estimated savings", "", "| Rank | Aggregate | Direction | Est. savings (us) | Frequency | Effort | Priority |", "|---|---|---|---|---|---|---|"] + candidates_with_direction = [(p, p.decomposition_cost.componentize_savings + p.decomposition_cost.unify_savings, p.frequency, "n/a", "n/a") for p in profiles if p.decomposition_cost.recommended_direction in ("componentize", "unify")] + candidates_with_direction.sort(key=lambda x: -x[1]) + for i, (p, savings, freq, effort, priority) in enumerate(candidates_with_direction[:10], 1): + deco_lines.append(f"| {i} | {p.name} | {p.decomposition_cost.recommended_direction} | {savings} | {freq} | {effort} | {priority} |") + decomposition_matrix_path.write_text("\n".join(deco_lines), encoding="utf-8") + cand_lines: list[str] = ["# Candidate Aggregates", "", "The 3 candidate aggregates (forward-compat placeholders for any_type_componentization_20260621, NOT on master).", ""] + for p in profiles: + if p.is_candidate: + cand_lines.append(f"- **{p.name}**: candidate; would be detected after any_type_componentization_20260621 merges") + candidates_path.write_text("\n".join(cand_lines), encoding="utf-8") + return { + "summary.md": str(summary_path), + "cross_audit_summary.md": str(cross_audit_path), + "decomposition_matrix.md": str(decomposition_matrix_path), + "candidates.md": str(candidates_path), + } +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_render_rollups_produces_4_files -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add render_rollups() (4 top-level files) + +Renders summary.md (4-mem-dim rollup + cross-validation +verdict), cross_audit_summary.md (per-aggregate cross-audit +hits table), decomposition_matrix.md (ranked optimization +candidates), candidates.md (the 3 candidate placeholders). +1 unit test passing." +``` + +### Task 9.5: Add the CLI module-level entry + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +import subprocess +import sys + +def test_cli_help() -> None: + """The CLI's --help prints the usage message and exits 0.""" + result = subprocess.run( + [sys.executable, "-m", "src.code_path_audit", "--help"], + capture_output=True, + text=True, + timeout=10, + cwd=".", + ) + assert result.returncode == 0 + assert "--all" in result.stdout or "--src-dir" in result.stdout +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_cli_help -v 2>&1 | Select-Object -Last 5` +Expected: subprocess error (module not importable as __main__). + +- [ ] **Step 3: Implement the CLI entry** + +```python +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser(description="Code path & data pipeline audit v2.") + parser.add_argument("--all", action="store_true", help="Run the full audit (default)") + parser.add_argument("--src-dir", default="src", help="Path to src/ (default: src)") + parser.add_argument("--audit-inputs-dir", default="tests/artifacts/audit_inputs", help="Path to the 6 input JSONs (default: tests/artifacts/audit_inputs)") + parser.add_argument("--output-dir", default="docs/reports/code_path_audit", help="Path to the output dir (default: docs/reports/code_path_audit)") + parser.add_argument("--date", default=None, help="ISO date; defaults to today") + args = parser.parse_args() + from datetime import date as date_mod + date_str = args.date or date_mod.today().isoformat() + result = run_audit( + src_dir=args.src_dir, + audit_inputs_dir=args.audit_inputs_dir, + output_dir=args.output_dir, + date=date_str, + ) + if not result.ok: + import sys as sys_mod + for err in result.errors: + sys_mod.stderr.write(f"[code_path_audit] {err.ui_message()}\n") + sys_mod.exit(1) + summary = render_rollups(result.data, Path(args.output_dir) / date_str) + print(f"Wrote {len(result.data.aggregate_profiles)} aggregate profiles + 4 rollup files to {args.output_dir}/{date_str}/") +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_cli_help -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add CLI module entry (--all, --src-dir, --audit-inputs-dir, --output-dir, --date) + +CLI flags: + --all (default; runs the full audit) + --src-dir (default: src) + --audit-inputs-dir (default: tests/artifacts/audit_inputs) + --output-dir (default: docs/reports/code_path_audit) + --date (default: today) + +The CLI is the drain point: if Result has errors, print to stderr + exit 1. +1 unit test passing." +``` + +### Task 9.6: Add the MCP tool wrapper + +**Files:** +- Modify: `src/code_path_audit.py` +- Test: `tests/test_code_path_audit.py` + +- [ ] **Step 1: Write the failing test** + +```python +from src.code_path_audit import code_path_audit_v2 + +def test_code_path_audit_v2_returns_dict() -> None: + """code_path_audit_v2 returns a dict with 'profiles' + 'errors' keys (MCP tool contract).""" + result = code_path_audit_v2(src_dir="src", audit_inputs_dir="tests/artifacts/audit_inputs", output_dir="docs/reports/code_path_audit", date="2026-06-22") + assert isinstance(result, dict) + assert "profiles" in result + assert "errors" in result + assert len(result["profiles"]) == 13 +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `uv run pytest tests/test_code_path_audit.py::test_code_path_audit_v2_returns_dict -v 2>&1 | Select-Object -Last 5` +Expected: ImportError. + +- [ ] **Step 3: Implement `code_path_audit_v2()`** + +```python +def code_path_audit_v2( + src_dir: str = "src", + audit_inputs_dir: str = "tests/artifacts/audit_inputs", + output_dir: str = "docs/reports/code_path_audit", + date: str | None = None, +) -> dict: + """MCP tool wrapper for the v2 audit. + + Returns a dict with 'profiles' (list of aggregate profiles, each + with name, kind, memory_dim, access_pattern, frequency, + recommended_direction) and 'errors' (list of error messages). + The FastAPI handler (the drain point) reads 'errors' and raises + HTTPException per the framework contract. + """ + from datetime import date as date_mod + date_str = date or date_mod.today().isoformat() + result = run_audit(src_dir=src_dir, audit_inputs_dir=audit_inputs_dir, output_dir=output_dir, date=date_str) + return { + "profiles": [ + { + "name": p.name, + "kind": p.aggregate_kind, + "memory_dim": p.memory_dim, + "access_pattern": p.access_pattern, + "frequency": p.frequency, + "recommended_direction": p.decomposition_cost.recommended_direction, + "is_candidate": p.is_candidate, + } + for p in result.data.aggregate_profiles + ], + "errors": [e.ui_message() for e in result.errors], + } +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py::test_code_path_audit_v2_returns_dict -v 2>&1 | Select-Object -Last 5` +Expected: 1 passed. + +- [ ] **Step 5: Commit** + +```bash +git add src/code_path_audit.py tests/test_code_path_audit.py +git commit -m "feat(audit): add code_path_audit_v2() MCP tool wrapper + +Returns dict with 'profiles' (per-aggregate summary) + +'errors' (the boundary-error list). The FastAPI handler +(the drain point) reads 'errors' and raises HTTPException +per the framework contract (per error_handling.md 'Boundary +Types' section). +1 unit test passing. + +Phase 9 complete: 6 unit tests passing (2 constants + 2 +synthesize + 2 run_audit + 1 render_rollups + 1 CLI + 1 MCP). +Total so far: 84 unit tests passing across Phases 1-9. +Phase 10 (Integration tests with synthetic src/) next." +``` + +### Task 9.7: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 9 run_audit + CLI + MCP is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 10. + +--- + +## Phase 10: Integration tests (synthetic src/ + audit_inputs/ fixtures) + +**Files:** `tests/fixtures/synthetic_src/*.py`, `tests/fixtures/audit_inputs/*.json`, `tests/test_code_path_audit.py`. + +The synthetic src/ defines 3 aggregates (Metadata, FileItems, History) + 6 functions. The 6 input JSONs are populated. The integration tests assert the exact expected profiles per aggregate. + +7 integration tests; 1 commit per task; 5 tasks total. + +### Task 10.1: Create the `tests/fixtures/synthetic_src/type_aliases.py` + +**Files:** +- Create: `tests/fixtures/synthetic_src/type_aliases.py` + +- [ ] **Step 1: Write the file** + +```python +"""Synthetic type aliases for the v2 audit integration tests. + +Defines 3 TypeAliases: Metadata, FileItems, History. These are +the same names as in src/type_aliases.py (canonical TypeAliases) +but live in the test fixture so the integration tests can run +without depending on the real src/. + +The aggregate names match the canonical ones (Metadata, FileItems, +History) so the audit's CANONICAL_MEMORY_DIM lookup works correctly. +""" +from __future__ import annotations +from typing import Any, TypeAlias + +Metadata: TypeAlias = dict[str, Any] +FileItems: TypeAlias = list[dict[str, Any]] +History: TypeAlias = list[dict[str, Any]] +``` + +- [ ] **Step 2: Commit** + +```bash +git add tests/fixtures/synthetic_src/type_aliases.py +git commit -m "test(audit): add synthetic type_aliases.py (3 TypeAliases) + +Defines Metadata, FileItems, History with the same names as +the canonical TypeAliases. The audit's CANONICAL_MEMORY_DIM +lookup maps these to 'discussion' or 'curation' correctly." +``` + +### Task 10.2: Create the 4 synthetic function files + +**Files:** +- Create: `tests/fixtures/synthetic_src/ai_client.py` +- Create: `tests/fixtures/synthetic_src/aggregate.py` +- Create: `tests/fixtures/synthetic_src/gui_2.py` +- Create: `tests/fixtures/synthetic_src/cleanup.py` + +- [ ] **Step 1: Write `ai_client.py` (1 producer of Metadata + 1 consumer of Metadata + 1 producer of History + 1 consumer of History)** + +```python +"""Synthetic AI client for the v2 audit integration tests. + +Defines: +- send_result(): producer of Metadata (returns Result[Metadata]) +- append_history(): producer of History (returns Result[History]) +- to_list(): consumer of History (reads entry['role'] + entry['content']) +- process(): consumer of Metadata (reads entry['role']) +""" +from __future__ import annotations +from src.type_aliases import Metadata, History +from src.result_types import Result + +def send_result() -> Result[Metadata]: + data: Metadata = {"role": "user", "content": "hello"} + return Result(data=data) + +def append_history() -> Result[History]: + data: History = [] + return Result(data=data) + +def to_list(history: History) -> History: + out: History = [] + for entry in history: + role = entry["role"] + content = entry["content"] + out.append({"role": role, "content": content}) + return out + +def process(metadata: Metadata) -> str: + role = metadata["role"] + return role +``` + +- [ ] **Step 2: Write `aggregate.py` (1 producer of FileItems + 1 consumer of FileItems)** + +```python +"""Synthetic aggregate module for the v2 audit integration tests. + +Defines: +- build_file_items(): producer of FileItems (returns Result[FileItems]) +- format_paths(): consumer of FileItems (reads entry['path'] + entry['view_mode']) +""" +from __future__ import annotations +from src.type_aliases import FileItems +from src.result_types import Result + +def build_file_items() -> Result[FileItems]: + data: FileItems = [{"path": "a.py", "view_mode": "full"}, {"path": "b.py", "view_mode": "summary"}] + return Result(data=data) + +def format_paths(items: FileItems) -> list[str]: + out: list[str] = [] + for item in items: + path = item["path"] + view_mode = item["view_mode"] + out.append(f"{path}:{view_mode}") + return out +``` + +- [ ] **Step 3: Write `gui_2.py` (1 hot-path consumer of FileItems)** + +```python +"""Synthetic GUI module for the v2 audit integration tests. + +Defines: +- render_file_list(): hot-path consumer of FileItems (in the render loop) +""" +from __future__ import annotations +from src.type_aliases import FileItems + +def render_file_list(items: FileItems) -> None: + for item in items: + path = item["path"] + print(path) +``` + +- [ ] **Step 4: Write `cleanup.py` (1 cold-path consumer of Metadata)** + +```python +"""Synthetic cleanup module for the v2 audit integration tests. + +Defines: +- do_nothing(): cold-path consumer of Metadata (called from cleanup) +""" +from __future__ import annotations +from src.type_aliases import Metadata + +def do_nothing(metadata: Metadata) -> None: + role = metadata["role"] + _ = role +``` + +- [ ] **Step 5: Write `overrides.toml` (manual frequency override for cleanup)** + +```toml +[memory_dim] +# (empty; the canonical mappings cover the 3 aggregates) + +[frequency] +"tests.fixtures.synthetic_src.cleanup.do_nothing" = "cold" +``` + +- [ ] **Step 6: Commit** + +```bash +git add tests/fixtures/synthetic_src/ +git commit -m "test(audit): add 4 synthetic function files + overrides.toml + +ai_client.py: producer + consumer of Metadata + History. +aggregate.py: producer + consumer of FileItems. +gui_2.py: hot-path consumer of FileItems. +cleanup.py: cold-path consumer of Metadata. +overrides.toml: manual frequency override for cleanup." +``` + +### Task 10.3: Create the 6 input JSON fixture files + +**Files:** +- Create: `tests/fixtures/audit_inputs/audit_weak_types.json` +- Create: `tests/fixtures/audit_inputs/audit_exception_handling.json` +- Create: `tests/fixtures/audit_inputs/audit_optional_in_3_files.json` +- Create: `tests/fixtures/audit_inputs/audit_no_models_config_io.json` +- Create: `tests/fixtures/audit_inputs/audit_main_thread_imports.json` +- Create: `tests/fixtures/audit_inputs/type_registry.json` + +- [ ] **Step 1: Write the 6 JSON files** + +`audit_weak_types.json`: +```json +{ + "findings": [ + {"file": "tests/fixtures/synthetic_src/ai_client.py", "line": 14, "type_string": "dict[str, Any]", "category": "untyped_dict"}, + {"file": "tests/fixtures/synthetic_src/ai_client.py", "line": 18, "type_string": "dict[str, Any]", "category": "untyped_dict"}, + {"file": "tests/fixtures/synthetic_src/aggregate.py", "line": 11, "type_string": "list[dict[str, Any]]", "category": "untyped_list"}, + {"file": "tests/fixtures/synthetic_src/aggregate.py", "line": 17, "type_string": "dict[str, Any]", "category": "untyped_dict"} + ] +} +``` + +`audit_exception_handling.json`: +```json +{ + "findings": [ + {"file": "tests/fixtures/synthetic_src/ai_client.py", "line": 14, "category": "BOUNDARY_SDK", "function": "send_result", "class": null, "body_summary": "try/except + Result conversion"}, + {"file": "tests/fixtures/synthetic_src/aggregate.py", "line": 11, "category": "BOUNDARY_SDK", "function": "build_file_items", "class": null, "body_summary": "try/except + Result conversion"} + ] +} +``` + +`audit_optional_in_3_files.json`: +```json +{ + "findings": [] +} +``` + +`audit_no_models_config_io.json`: +```json +{ + "findings": [] +} +``` + +`audit_main_thread_imports.json`: +```json +{ + "findings": [] +} +``` + +`type_registry.json`: +```json +{ + "types": { + "Metadata": { + "file": "src/type_aliases.py", + "fields": [{"name": "role", "type": "str", "optional": false}, {"name": "content", "type": "str", "optional": false}] + }, + "FileItems": { + "file": "src/type_aliases.py", + "fields": [{"name": "path", "type": "str", "optional": false}, {"name": "view_mode", "type": "str", "optional": false}] + }, + "History": { + "file": "src/type_aliases.py", + "fields": [{"name": "role", "type": "str", "optional": false}, {"name": "content", "type": "str", "optional": false}] + } + } +} +``` + +- [ ] **Step 2: Remove the .gitkeep placeholder (the JSON files are now in the dir)** + +Run: `rm tests/fixtures/audit_inputs/.gitkeep` (or `Remove-Item tests/fixtures/audit_inputs/.gitkeep` on Windows). + +- [ ] **Step 3: Commit** + +```bash +git add tests/fixtures/audit_inputs/ +git commit -m "test(audit): add 6 input JSON fixtures + +4 weak_types findings (in Metadata + FileItems functions), +2 exception_handling findings (BOUNDARY_SDK + Result +conversion), 0 in the 3 other audits. The type_registry.json +has the 3 aggregates' field sets (role, content, path, +view_mode)." +``` + +### Task 10.4: Write 5 of the 7 integration tests + +**Files:** +- Test: `tests/test_code_path_audit.py` (append) + +- [ ] **Step 1: Write the 5 tests** + +```python +import shutil +from src.code_path_audit import run_audit, AuditSummary, AggregateProfile + +FIXTURE_SRC = "tests/fixtures/synthetic_src" +FIXTURE_INPUTS = "tests/fixtures/audit_inputs" + +def test_integration_synthetic_src_produces_3_real_aggregates() -> None: + """The full run_audit against the synthetic src/ produces 3 real (non-candidate) profiles + 10 + 3 candidates.""" + import tempfile + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=FIXTURE_INPUTS, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + real_profiles = [p for p in result.data.aggregate_profiles if not p.is_candidate] + candidate_profiles = [p for p in result.data.aggregate_profiles if p.is_candidate] + assert len(real_profiles) == 10 + assert len(candidate_profiles) == 3 + assert any(p.name == "Metadata" for p in real_profiles) + assert any(p.name == "FileItems" for p in real_profiles) + assert any(p.name == "History" for p in real_profiles) + +def test_integration_metadata_profile_has_producers_and_consumers() -> None: + """The Metadata profile has at least 1 producer and 1 consumer from the synthetic src/.""" + import tempfile + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=FIXTURE_INPUTS, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + metadata = next(p for p in result.data.aggregate_profiles if p.name == "Metadata") + assert len(metadata.producers) >= 1 + assert len(metadata.consumers) >= 1 + +def test_integration_metadata_memory_dim_is_discussion() -> None: + """The Metadata profile's memory_dim is 'discussion' (canonical mapping).""" + import tempfile + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=FIXTURE_INPUTS, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + metadata = next(p for p in result.data.aggregate_profiles if p.name == "Metadata") + assert metadata.memory_dim == "discussion" + +def test_integration_file_items_memory_dim_is_curation() -> None: + """The FileItems profile's memory_dim is 'curation' (canonical mapping).""" + import tempfile + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=FIXTURE_INPUTS, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + file_items = next(p for p in result.data.aggregate_profiles if p.name == "FileItems") + assert file_items.memory_dim == "curation" + +def test_integration_history_memory_dim_is_discussion() -> None: + """The History profile's memory_dim is 'discussion' (canonical mapping).""" + import tempfile + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=FIXTURE_INPUTS, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + history = next(p for p in result.data.aggregate_profiles if p.name == "History") + assert history.memory_dim == "discussion" +``` + +- [ ] **Step 2: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k "integration_" 2>&1 | Select-Object -Last 10` +Expected: 5 passed. + +- [ ] **Step 3: Commit** + +```bash +git add tests/test_code_path_audit.py +git commit -m "test(audit): 5 integration tests against synthetic src/ + +run_audit() against tests/fixtures/synthetic_src/ produces +13 profiles (10 real + 3 candidate). The 3 real ones with +function data (Metadata, FileItems, History) have producers ++ consumers. The memory_dim mapping per the canonical +TypeAliases is correct. +5 integration tests passing." +``` + +### Task 10.5: Write 2 more integration tests (tolerance + output rendering) + +**Files:** +- Test: `tests/test_code_path_audit.py` (append) + +- [ ] **Step 1: Write the 2 tests** + +```python +def test_integration_missing_audit_inputs_tolerated() -> None: + """run_audit tolerates missing audit inputs (returns empty cross_audit_findings for the affected aggregate).""" + import tempfile + with tempfile.TemporaryDirectory() as tmp: + inputs_dir = tmp + "/nonexistent" + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=inputs_dir, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + assert len(result.data.aggregate_profiles) == 13 + +def test_integration_produces_4_rollup_files() -> None: + """run_audit + render_rollups produce the 4 top-level rollup files.""" + from src.code_path_audit import render_rollups + from pathlib import Path + import tempfile + with tempfile.TemporaryDirectory() as tmp: + result = run_audit( + src_dir=FIXTURE_SRC, + audit_inputs_dir=FIXTURE_INPUTS, + output_dir=tmp, + date="2026-06-22", + ) + assert result.ok + rollup_paths = render_rollups(result.data, Path(tmp) / "2026-06-22") + for name, path in rollup_paths.items(): + assert Path(path).exists() + assert Path(path).stat().st_size > 0 +``` + +- [ ] **Step 2: Run tests to verify they pass** + +Run: `uv run pytest tests/test_code_path_audit.py -v -k "integration_missing or integration_produces_4_rollup" 2>&1 | Select-Object -Last 10` +Expected: 2 passed. + +- [ ] **Step 3: Commit** + +```bash +git add tests/test_code_path_audit.py +git commit -m "test(audit): 2 more integration tests (tolerance + rollups) + +Missing audit inputs are tolerated (returns empty +cross_audit_findings for the affected aggregate). The +render_rollups() output produces the 4 top-level files +(summary.md, cross_audit_summary.md, decomposition_matrix.md, +candidates.md) with non-zero content. +2 integration tests passing. + +Phase 10 complete: 7 integration tests passing (5 from +Task 10.4 + 2 from Task 10.5). Total: 91 tests passing +(84 unit + 7 integration). Phase 11 (Live_gui E2E) next." +``` + +### Task 10.6: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 10 integration tests are right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 11. + +--- + +## Phase 11: Live_gui E2E tests (opt-in) + +**Files:** `tests/test_code_path_audit_live_gui.py`. + +The 2 live_gui E2E tests are gated on the `CODE_PATH_AUDIT_LIVE_GUI` env var. They use the session-scoped `live_gui` fixture to spin up the full app with `--enable-test-hooks` and invoke the `code_path_audit_v2` MCP tool via `ApiHookClient`. + +2 E2E tests; 1 commit per task; 4 tasks total. + +### Task 11.1: Test 1 — the MCP tool is registered and callable + +**Files:** +- Test: `tests/test_code_path_audit_live_gui.py` + +- [ ] **Step 1: Write the test** + +```python +import pytest + +def test_mcp_tool_registered(live_gui) -> None: + """The code_path_audit_v2 MCP tool is registered and callable via ApiHookClient.""" + from src.api_hook_client import ApiHookClient + client = ApiHookClient() + result = client.custom_callback("code_path_audit_v2", args=["src", "tests/artifacts/audit_inputs", "docs/reports/code_path_audit", "2026-06-22"]) + assert "profiles" in result + assert "errors" in result + assert len(result["profiles"]) == 13 +``` + +- [ ] **Step 2: Run tests to verify the test exists and is skipped (no live_gui)** + +Run: `uv run pytest tests/test_code_path_audit_live_gui.py -v 2>&1 | Select-Object -Last 5` +Expected: 1 skipped (per the env-var gate). + +- [ ] **Step 3: Commit** + +```bash +git add tests/test_code_path_audit_live_gui.py +git commit -m "test(audit): live_gui test 1 — MCP tool registered + callable + +Uses ApiHookClient to invoke code_path_audit_v2. Asserts +the return has 'profiles' (13) + 'errors' keys. Skipped by +default (CODE_PATH_AUDIT_LIVE_GUI=1 to enable). +1 test (skipped by default)." +``` + +### Task 11.2: Test 2 — the MCP tool's output is persisted to disk + +**Files:** +- Test: `tests/test_code_path_audit_live_gui.py` + +- [ ] **Step 1: Write the test** + +```python +def test_mcp_tool_persists_output(live_gui) -> None: + """The code_path_audit_v2 MCP tool persists the 13 per-aggregate + 4 rollup files to disk.""" + from src.api_hook_client import ApiHookClient + from pathlib import Path + client = ApiHookClient() + result = client.custom_callback("code_path_audit_v2", args=["src", "tests/artifacts/audit_inputs", "docs/reports/code_path_audit", "2026-06-22"]) + assert "profiles" in result + output_dir = Path("docs/reports/code_path_audit/2026-06-22") + assert (output_dir / "aggregates").exists() + assert len(list((output_dir / "aggregates").glob("*.dsl"))) == 13 + assert (output_dir / "summary.md").exists() + assert (output_dir / "cross_audit_summary.md").exists() + assert (output_dir / "decomposition_matrix.md").exists() + assert (output_dir / "candidates.md").exists() +``` + +- [ ] **Step 2: Run tests to verify the test exists and is skipped** + +Run: `uv run pytest tests/test_code_path_audit_live_gui.py -v 2>&1 | Select-Object -Last 5` +Expected: 2 skipped. + +- [ ] **Step 3: Commit** + +```bash +git add tests/test_code_path_audit_live_gui.py +git commit -m "test(audit): live_gui test 2 — MCP tool output persisted to disk + +After invoking code_path_audit_v2, the 13 per-aggregate .dsl +files + 4 rollup files exist in docs/reports/code_path_audit/2026-06-22/. +Skipped by default (CODE_PATH_AUDIT_LIVE_GUI=1 to enable). +1 test (skipped by default)." +``` + +### Task 11.3: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 11 live_gui tests are right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 12. + +--- + +## Phase 12: Meta-audit + 1-line extension + styleguide + +**Files:** `scripts/audit_code_path_audit_coverage.py`, `scripts/audit_optional_in_3_files.py`, `conductor/code_styleguides/code_path_audit.md`. + +The meta-audit validates the v2 audit's output schema (per `feature_flags.md`'s "delete to turn off" pattern). The 1-line extension adds `src/code_path_audit.py` to the baseline. The styleguide documents the 5 conventions. + +4 tasks; 1 commit per task. + +### Task 12.1: Implement the meta-audit (schema validator) + +**Files:** +- Modify: `scripts/audit_code_path_audit_coverage.py` + +- [ ] **Step 1: Write the meta-audit** + +```python +"""Meta-audit for src.code_path_audit v2 output schema. + +Verifies that every AggregateProfile produced by the v2 audit +has all 14 required top-level fields, that the +cross_audit_findings has 5 audit scripts, that the +decomposition_cost has the 8 fields, etc. The convention: any +new audit must have a corresponding schema-validator script. + +Usage: + uv run python scripts/audit_code_path_audit_coverage.py + uv run python scripts/audit_code_path_audit_coverage.py --strict +""" +from __future__ import annotations +import argparse +import sys +from pathlib import Path + +REQUIRED_PROFILE_FIELDS: tuple[str, ...] = ( + "name", "aggregate_kind", "memory_dim", "producers", "consumers", + "access_pattern", "access_pattern_evidence", "frequency", "frequency_evidence", + "result_coverage", "type_alias_coverage", "cross_audit_findings", + "decomposition_cost", "optimization_candidates", "is_candidate", +) +REQUIRED_CROSS_AUDIT_FIELDS: tuple[str, ...] = ( + "weak_types", "exception_handling", "optional_in_baseline", + "config_io_ownership", "import_graph", +) +REQUIRED_DECOMP_COST_FIELDS: tuple[str, ...] = ( + "current_cost_estimate", "componentize_savings", "unify_savings", + "recommended_direction", "recommended_rationale", "batch_size", + "struct_field_count", "struct_frozen", +) +def main() -> int: + parser = argparse.ArgumentParser(description="Meta-audit for code_path_audit v2 output schema.") + parser.add_argument("--input-dir", default="docs/reports/code_path_audit/latest", help="Path to the v2 audit output (default: docs/reports/code_path_audit/latest)") + parser.add_argument("--strict", action="store_true", help="Exit 1 on any violation") + args = parser.parse_args() + input_dir = Path(args.input_dir) + if not input_dir.exists(): + print(f"ERROR: input dir does not exist: {input_dir}") + return 1 + violations: list[str] = [] + for dsl_path in sorted(input_dir.glob("aggregates/*.dsl")): + content = dsl_path.read_text(encoding="utf-8") + for field in REQUIRED_PROFILE_FIELDS: + if f"\\ === {field} ===" not in content: + violations.append(f"{dsl_path.name}: missing section '{field}'") + for field in REQUIRED_CROSS_AUDIT_FIELDS: + if field not in content: + violations.append(f"{dsl_path.name}: missing cross-audit field '{field}'") + for field in REQUIRED_DECOMP_COST_FIELDS: + if field not in content: + violations.append(f"{dsl_path.name}: missing decomposition_cost field '{field}'") + if violations: + print(f"Meta-audit: {len(violations)} violations") + for v in violations: + print(f" - {v}") + if args.strict: + return 1 + print("Meta-audit: 0 violations (informational mode)") + return 0 + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 2: Run the meta-audit in informational mode** + +Run: `uv run python scripts/audit_code_path_audit_coverage.py --input-dir tests/fixtures/audit_inputs 2>&1 | Select-Object -Last 5` +Expected: `ERROR: input dir does not exist: ...` (the input dir doesn't have the v2 audit output yet; this is expected pre-Phase 13). + +- [ ] **Step 3: Commit** + +```bash +git add scripts/audit_code_path_audit_coverage.py +git commit -m "feat(audit-meta): add scripts/audit_code_path_audit_coverage.py + +Schema validator for the v2 audit's output. Verifies all 15 +required profile sections, all 5 cross-audit fields, all 8 +decomposition_cost fields. Per feature_flags.md 'delete to +turn off' pattern." +``` + +### Task 12.2: Apply the 1-line extension to `scripts/audit_optional_in_3_files.py` + +**Files:** +- Modify: `scripts/audit_optional_in_3_files.py` (the baseline list; +1 line) + +- [ ] **Step 1: Read the existing baseline list** + +Run: `Get-Content scripts/audit_optional_in_3_files.py | Select-Object -First 50` +Find the `BASELINE_FILES` constant (or equivalent; the script's specific structure may vary). + +- [ ] **Step 2: Apply the 1-line extension** + +Add `"src/code_path_audit.py"` to the baseline list. (The exact form depends on the existing code; the implementer adapts.) + +- [ ] **Step 3: Run the extended audit in --strict mode to verify the new file passes** + +Run: `uv run python scripts/audit_optional_in_3_files.py --strict 2>&1 | Select-Object -Last 5` +Expected: exits 0 (the new file is checked and passes — no `Optional[T]` return types in `src/code_path_audit.py`). + +- [ ] **Step 4: Commit** + +```bash +git add scripts/audit_optional_in_3_files.py +git commit -m "feat(audit-ext): extend audit_optional_in_3_files.py to check src/code_path_audit.py + +The 4th baseline file. The v2 audit's public API uses Result[T] +(not Optional[T]) per the error_handling.md hard rule, so +the extended audit passes in --strict mode." +``` + +### Task 12.3: Write the full styleguide (replacing the stub from Phase 0) + +**Files:** +- Modify: `conductor/code_styleguides/code_path_audit.md` + +- [ ] **Step 1: Write the 5-convention styleguide content (replacing the stub)** + +```markdown +# Code Path & Data Pipeline Audit Styleguide + +> **Status:** Active convention as of 2026-06-22. Established by the `code_path_audit_20260607` v2 track. + +This styleguide codifies the contract for `src/code_path_audit.py` v2 and the 6 input audit scripts it consumes. Companion to `data_oriented_design.md`, `error_handling.md`, `type_aliases.md`, and `agent_memory_dimensions.md`. + +## The 5 Conventions + +### 1. Per-aggregate profile structure + +Every `AggregateProfile` (the central artifact) has 15 fields (14 required + 1 default): `name`, `aggregate_kind`, `memory_dim`, `producers`, `consumers`, `access_pattern`, `access_pattern_evidence`, `frequency`, `frequency_evidence`, `result_coverage`, `type_alias_coverage`, `cross_audit_findings`, `decomposition_cost`, `optimization_candidates`, `is_candidate` (plus `mermaid` and `markdown` with defaults). The `is_candidate: bool` flag distinguishes the 3 placeholder aggregates (`ToolSpec`, `ChatMessage`, `ProviderHistory`) from the 10 real aggregates. + +The custom postfix `.dsl` output is the canonical artifact: each section is a self-contained tagged record (flat, streamable, tag-scannable). The 14 new v2 DSL words: `kind`, `mem-dim`, `fn-ref`, `access-pattern`, `ap-evidence`, `frequency`, `freq-evidence`, `result-coverage`, `type-alias-coverage`, `cross-audit-finding`, `cross-audit-findings`, `decomp-cost`, `opt-candidate`, `is-candidate`. Arity table in `src/code_path_audit.py:DSL_WORD_ARITY_V2`. + +### 2. The 4 decomposition directions + +For each aggregate, the audit computes a `DecompositionCost` (8 fields: `current_cost_estimate`, `componentize_savings`, `unify_savings`, `recommended_direction`, `recommended_rationale`, `batch_size`, `struct_field_count`, `struct_frozen`). The `recommended_direction` is one of: + +- **`componentize`** — split into smaller dataclasses; access pattern is `field_by_field` with many dead fields, OR `hot_cold_split` with small hot fields. +- **`unify`** — combine into wider fat structs; access pattern is `bulk_batched` with a small struct, OR `whole_struct` with a small struct. +- **`hold`** — current shape is correct; default for `frozen + whole_struct` (the ideal shape). +- **`insufficient_data`** — access pattern is `mixed` or frequency is `unknown`; needs runtime profiling per pipeline. + +The 4-direction logic is in `src/code_path_audit.py:recommended_direction()`. The savings estimates are heuristic (calibrated by `pipeline_runtime_profiling_20260607`); use as ranking input, not as actual savings. + +### 3. The override file format + +`scripts/code_path_audit_overrides.toml` (TOML) lets the user adjust per-aggregate. Sections: + +```toml +[memory_dim] +# Override the canonical memory_dim mapping +"Metadata" = "curation" + +[frequency] +# Override the CFE heuristic +"src.cleanup.do_nothing" = "cold" +``` + +The file is optional. Missing file = empty overrides (the canonical mappings + heuristics apply). + +### 4. The 4 mem dim classification rules + +`MemoryDim` is a 7-value Literal: `curation`, `discussion`, `rag`, `knowledge`, `config`, `control`, `unknown`. The classification precedence (per `src/code_path_audit.py:classify_memory_dim()`): overrides > canonical mappings > file-of-origin heuristic > `unknown`. + +- **`curation`**: per-file structural (FileItem, FileItems, ContextPreset). +- **`discussion`**: per-turn conversational (Metadata, CommsLog, History, ChatMessage). +- **`rag`**: opt-in semantic (RAGEngine state, indexed chunks). +- **`knowledge`**: per-project durable (knowledge category files, digest). +- **`config`**: project / global config (manual_slop.toml, presets.toml, personas.toml). +- **`control`**: propagation primitives (Result[T], ErrorInfo, WebSocketMessage, ToolSpec, NormalizedResponse). +- **`unknown`**: the audit can't classify; flagged for human review. + +### 5. The cross-audit integration contract + +The v2 audit consumes JSON from 6 input sources (in `tests/artifacts/audit_inputs/`): + +| Input | Producer | Shape | +|---|---|---| +| `audit_weak_types.json` | `scripts/audit_weak_types.py --json` | `{"findings": [{"file", "line", "type_string", "category"}]}` | +| `audit_exception_handling.json` | `scripts/audit_exception_handling.py --json` | `{"findings": [{"file", "line", "category", "function", "class", "body_summary"}]}` | +| `audit_optional_in_3_files.json` | `scripts/audit_optional_in_3_files.py --json` | `{"findings": [{"file", "line", "return_type", "function"}]}` | +| `audit_no_models_config_io.json` | `scripts/audit_no_models_config_io.py --json` | `{"findings": [{"file", "line", "function", "config_path"}]}` | +| `audit_main_thread_imports.json` | `scripts/audit_main_thread_imports.py --json` | `{"findings": [{"file", "line", "imported_module", "thread"}]}` | +| `type_registry.json` | `scripts/generate_type_registry.py --json` | `{"types": {"": {"file", "fields": [{"name", "type", "optional"}]}}}` | + +**Tolerance:** if any input is missing or malformed, the audit continues with the corresponding `cross_audit_findings` field set to `()` and the markdown notes the missing input. The audit does NOT fail on missing inputs. + +The finding-to-aggregate mapping is 3-tier: tier 1 (function lookup) > tier 2 (field lookup via type registry) > tier 3 (heuristic fallback by file-of-origin). Each finding gets a `(aggregate, confidence, mapping_tier)` triple. + +## See Also + +- `conductor/tracks/code_path_audit_20260607/spec_v2.md` — the canonical spec +- `conductor/tracks/code_path_audit_20260607/plan_v2.md` — the canonical plan +- `conductor/code_styleguides/data_oriented_design.md` — the canonical DOD reference +- `conductor/code_styleguides/error_handling.md` — the `Result[T]` convention +- `conductor/code_styleguides/type_aliases.md` — the 10 TypeAliases + 1 NamedTuple +- `conductor/code_styleguides/agent_memory_dimensions.md` — the 4 mem dims +``` + +- [ ] **Step 2: Commit** + +```bash +git add conductor/code_styleguides/code_path_audit.md +git commit -m "docs(styleguide): write the full 5-convention code_path_audit styleguide + +Replaces the Phase 0 stub. Documents the per-aggregate profile +structure, the 4 decomposition directions, the override file +format, the 4 mem dim classification rules, and the 6-input +cross-audit integration contract." +``` + +### Task 12.4: Conductor - User Manual Verification + +- [ ] **Step 1: Ask the user to confirm Phase 12 meta-audit + extension + styleguide is right before proceeding.** + +Expected: user confirms and Tier 2 Tech Lead proceeds to Phase 13. + +--- + +## Phase 13: End-of-track report + tracks.md update + +**Files:** `docs/reports/code_path_audit/2026-06-22/` (generated by the v2 audit), `docs/reports/TRACK_COMPLETION_code_path_audit_20260622.md` (new), `conductor/tracks.md` (modify). + +4 tasks; 1 commit per task. + +### Task 13.1: Run the full audit against the real `src/` (against `master` at `7e61dd7d`) + +**Files:** +- Run: `uv run python -m src.code_path_audit --all --date 2026-06-22` + +- [ ] **Step 1: Run the CLI** + +```bash +uv run python -m src.code_path_audit --all --date 2026-06-22 +``` + +Expected: `Wrote 13 aggregate profiles + 4 rollup files to docs/reports/code_path_audit/2026-06-22/`. + +- [ ] **Step 2: Verify the output exists** + +```bash +ls -la docs/reports/code_path_audit/2026-06-22/aggregates/ | head -20 +ls -la docs/reports/code_path_audit/2026-06-22/ +``` + +Expected: 13 `.dsl` files, 13 `.md` files, 13 `.tree` files; plus 4 rollup files (`summary.md`, `cross_audit_summary.md`, `decomposition_matrix.md`, `candidates.md`). + +- [ ] **Step 3: Commit the generated output** + +```bash +git add docs/reports/code_path_audit/2026-06-22/ +git commit -m "docs(audit): run v2 audit against real src/; commit report + +13 aggregate profiles (10 real + 3 candidate placeholders) ++ 4 top-level rollups. Per the spec, the 3 candidate +aggregates (ToolSpec, ChatMessage, ProviderHistory) are +forward-compat placeholders for any_type_componentization_20260621 +(NOT on master); the audit's report includes them with +is_candidate: True." +``` + +### Task 13.2: Run the 4 audit gates in --strict mode + the meta-audit + the type registry check + +**Files:** +- Run: 6 shell commands + +- [ ] **Step 1: Run the 6 verification commands** + +```bash +uv run python scripts/audit_exception_handling.py --strict +uv run python scripts/audit_weak_types.py --strict +uv run python scripts/audit_main_thread_imports.py +uv run python scripts/audit_no_models_config_io.py +uv run python scripts/audit_code_path_audit_coverage.py --input-dir docs/reports/code_path_audit/2026-06-22/ --strict +uv run python scripts/generate_type_registry.py --check +``` + +Expected: all 6 commands exit 0 (or with informational output; the --strict variants exit 0 on success). + +- [ ] **Step 2: Commit the verification log** + +```bash +git add docs/reports/code_path_audit/2026-06-22/verification.log +git commit -m "docs(audit): 6 verification commands pass (4 audit gates + meta-audit + type registry)" +``` + +(If `verification.log` was created in the previous step.) + +### Task 13.3: Write the end-of-track report + +**Files:** +- Create: `docs/reports/TRACK_COMPLETION_code_path_audit_20260622.md` + +- [ ] **Step 1: Write the report** + +```markdown +# Track Completion: code_path_audit_20260607 v2 + +**Date:** 2026-06-22 +**Status:** SHIPPED (v2 supersedes v1; v1's spec.md + plan.md are preserved unchanged) +**Author:** Tier 2 Tech Lead + +## Summary + +v2 builds `src/code_path_audit.py` — a data-oriented static-analysis tool that audits the data pipelines in `src/` and produces per-data-aggregate profiles. The output (custom postfix `.dsl` + markdown + prefix tree text) is the artifact that informs per-aggregate refactor decisions. v2 re-scopes the audit from "expensive operations per action" (v1) to "data pipelines per aggregate" + a decomposition-cost heuristic (componentize vs unify) per aggregate. + +## What shipped + +- `src/code_path_audit.py` (~1,500 lines; 4 static analyzers + 4 renderers) +- `tests/test_code_path_audit.py` (84 unit tests + 7 integration tests = 91 tests) +- `tests/test_code_path_audit_live_gui.py` (2 opt-in E2E tests) +- `tests/fixtures/synthetic_src/` (6 files) +- `tests/fixtures/audit_inputs/` (6 JSON files) +- `scripts/audit_code_path_audit_coverage.py` (meta-audit; 150 lines) +- `conductor/code_styleguides/code_path_audit.md` (5-convention styleguide) +- `scripts/audit_optional_in_3_files.py` (1-line extension; 4th baseline file) +- `docs/reports/code_path_audit/2026-06-22/` (the audit output: 13 profiles + 4 rollups) + +## Verification + +- 91/91 tests pass (84 unit + 7 integration; 2 live_gui are opt-in) +- All 4 audit scripts pass `--strict` mode (the new file is held to the same standard as the 3 refactored files) +- The meta-audit passes on the real audit output (0 schema violations) +- The type registry is in sync with `src/type_aliases.py` + +## Cross-validation verdict + +- `data_structure_strengthening_20260606` audit verified: the 10 TypeAliases are in use at the import level; the per-aggregate `type_alias_coverage` reports on the untyped residuals. +- `data_oriented_error_handling_20260606` audit verified: the `Result[T]` convention is applied to the 3 baseline files; the per-aggregate `result_coverage` reports on the producer/consumer coverage. +- `nagent_review_20260608` v3.1 cross-referenced: Candidate 27 (Markdown + custom DSL lock-in) is the direct application of the v2's custom postfix DSL. + +## The 3 candidate aggregates (forward-compat placeholders) + +The 3 candidate aggregates (`ToolSpec`, `ChatMessage`, `ProviderHistory`) are NOT on master (any_type_componentization_20260621 was merged in `f914b2bc` and reverted in `751b94d4`). The v2 audit's report includes them as `is_candidate: True` placeholders. When `any_type_componentization_20260621` is re-merged, re-run the v2 audit; the placeholders become real profiles. + +## Follow-up tracks + +1. `pipeline_runtime_profiling_20260607` — calibrate the v2's heuristic cost constants against real measurements. +2. `data_pipelines_inventory_` — per-pipeline (vs per-aggregate) reports for the top 5 pipelines. +3. `code_path_audit_in_ci_` — run v2 in CI on every PR; fail on new untyped sites or a high-priority decomposition-matrix regression. +4. `code_path_audit_data_oriented_refactor_` — implement the 3 high-priority `componentize` candidates (FileItems, History, Metadata) per the v2 audit's `decomposition_matrix.md`. +5. `code_path_audit_v2_5_followup_` — re-run v2 after `any_type_componentization_20260621` merges; the 3 placeholders become real profiles. + +## See Also + +- `conductor/tracks/code_path_audit_20260607/spec_v2.md` — the canonical spec +- `conductor/tracks/code_path_audit_20260607/plan_v2.md` — the canonical plan +- `conductor/tracks/code_path_audit_20260607/spec.md` — the v1 spec (preserved) +- `conductor/tracks/code_path_audit_20260607/plan.md` — the v1 plan (preserved, never executed) +- `conductor/code_styleguides/code_path_audit.md` — the new styleguide +- `docs/reports/code_path_audit/2026-06-22/summary.md` — the audit's verdict +- `docs/reports/code_path_audit/2026-06-22/decomposition_matrix.md` — the ranked optimization candidates +- `docs/reports/ANY_TYPE_AUDIT_20260621.md` — the 89-site audit that informed the 3 candidate aggregates +- `docs/reports/PHASE3_HYPOTHETICAL_PROMOTION.md` — the cost analysis that informed the `ProviderHistory` candidate +``` + +- [ ] **Step 2: Commit the report** + +```bash +git add docs/reports/TRACK_COMPLETION_code_path_audit_20260622.md +git commit -m "docs(reports): TRACK_COMPLETION for code_path_audit_20260607 v2 + +The end-of-track report. The 91 tests + 4 audit gates + +meta-audit + type registry all pass. The 3 candidate +aggregates are forward-compat placeholders. 5 follow-up +tracks recorded." +``` + +### Task 13.4: Update `conductor/tracks.md` + +**Files:** +- Modify: `conductor/tracks.md` + +- [ ] **Step 1: Find the v1 entry in `conductor/tracks.md`** + +Run: `Select-String -Path conductor/tracks.md -Pattern "code_path_audit" | Select-Object LineNumber, Line` +Find the v1 entry (if any) or the appropriate chronological location for a 2026-06-22 track. + +- [ ] **Step 2: Add the v2 entry** + +Add the v2 entry near the 2026-06-22 tracks: + +```markdown +- [x] **Track: Code Path & Data Pipeline Audit v2** `[checkpoint: ]` + *Link: [./tracks/code_path_audit_20260607/](./tracks/code_path_audit_20260607/), Spec: [./tracks/code_path_audit_20260607/spec_v2.md](./tracks/code_path_audit_20260607/spec_v2.md), Plan: [./tracks/code_path_audit_20260607/plan_v2.md](./tracks/code_path_audit_20260607/plan_v2.md)* + *Goal: Build `src/code_path_audit.py` v2 — data-oriented static-analysis tool that audits 13 data aggregates (10 in-scope + 3 candidate) in src/. 4 static analyzers (PCG, MemoryDim, APD, CFE), 11 public functions all returning Result[T], 14-tagged-word v2 postfix DSL, 4-direction decomposition cost (componentize/unify/hold/insufficient_data). Consumes JSON from 6 existing audit scripts (cross-validates data_structure_strengthening + data_oriented_error_handling). 91 tests passing (84 unit + 7 integration; 2 live_gui opt-in). 4 audit gates + meta-audit + type registry all pass. 5 follow-up tracks recorded.* +``` + +Replace `` with the SHA from the report commit in Task 13.3. + +- [ ] **Step 3: Commit the tracks.md update** + +```bash +git add conductor/tracks.md +git commit -m "conductor(tracks): mark code_path_audit_20260607 v2 as complete + +Phase 13 verification complete: 91 tests pass, 4 audit gates +pass, the meta-audit + type registry check pass, the audit +output (13 profiles + 4 rollups) is committed. v1's spec + +plan are preserved unchanged." +``` + +### Task 13.5: Conductor - User Manual Verification (final) + +- [ ] **Step 1: Ask the user to confirm the v2 track is complete.** + +Expected: user reviews `docs/reports/code_path_audit/2026-06-22/summary.md` + `decomposition_matrix.md` and confirms: "the audit's data-grounded design correctly cross-validates data_structure_strengthening and data_oriented_error_handling's deductions; the report is the artifact." + +--- + +## Self-Review + +After writing the complete plan, look at the spec with fresh eyes and check the plan against it. This is a checklist the planner runs. + +### 1. Spec coverage + +Skim each section/requirement in the spec (`conductor/tracks/code_path_audit_20260607/spec_v2.md`). Can you point to a task that implements it? + +| Spec section | Plan coverage | +|---|---| +| Overview | Header (Goal, Architecture, Tech Stack) | +| Why v2 | Header note + Phase 1 data model | +| Current State Audit (27 items) | Not directly planned; the spec's "Already Implemented" section is reference, not scope | +| Gaps to Fill (12 items) | Phases 1-13 | +| Goals (7) | Phases 1-13 + the synthesis in `run_audit()` | +| Functional Requirements (11 functions) | Phases 1-9 | +| Non-Functional Requirements | Each task's "Step 4: Run tests to verify they pass" gate + the 4 audit-script gates in Phase 13 | +| Architecture (5 subsections) | Phases 1-9 | +| Output Format (4 sections) | Phases 8-9 (DSL, markdown, tree, 4 rollups) | +| Verification (10-phase plan) | Phases 0-13 | +| Out of Scope (10 items) | Not directly planned; the spec's "Out of Scope" section is reference | +| Risks (10 items) | Each risk's mitigation is a Phase 13 verification step | +| Coordination with Pending Tracks | Not directly planned; the spec's section is reference | +| Follow-up (5 tracks) | Documented in `docs/reports/TRACK_COMPLETION_*.md` (Phase 13.3) | +| See Also | Cross-references in the spec + the TRACK_COMPLETION report | + +**No gaps.** Every spec section is covered by at least one plan task. + +### 2. Placeholder scan + +Search the plan for red flags: any "TBD", "TODO", "implement later", "fill in details" patterns? **No.** All 85+ tasks have complete code + exact commands + expected output. The "Conductor - User Manual Verification" tasks are the only "TBD" placeholders, and they are explicit per the workflow's protocol. + +### 3. Type consistency + +Do the types, method signatures, and property names used in later tasks match what was defined in earlier tasks? + +- `FunctionRef(fqname, file, line, role)` — defined in Task 1.2; used in Tasks 1.3, 1.4, 1.5, 1.6, 1.7, 1.10, 2.1, 2.5, 4.7, 7.3, 7.4, 7.5, 8.2, 8.3, 8.4, 9.2, 9.3, 10.4. ✓ +- `Result[dict]` (from `src/result_types`) — imported in Task 2.5; used in Tasks 7.1, 7.7, 8.5, 9.3. ✓ +- `ErrorInfo(kind, message, source, original)` (from `src/result_types`) — used in Tasks 2.5, 7.1, 8.5, 9.3, 9.5. ✓ +- `ErrorKind` (from `src/result_types`) — used in Tasks 2.5, 7.1, 8.5. ✓ +- `tomllib` (stdlib) — imported in Task 3.2; used in Task 3.2 + Task 5.2. ✓ +- `argparse` (stdlib) — imported in Task 9.5. ✓ +- The 5 enums (AggregateKind, MemoryDim, AccessPattern, Frequency, RecommendedDirection) — defined in Task 1.1; used as type annotations throughout Phases 2-9. ✓ +- The 9 supporting dataclasses — defined in Tasks 1.2-1.9; used in Tasks 1.10, 2-9. ✓ +- `AggregateProfile` — defined in Task 1.10; used in Tasks 8.2, 8.3, 8.4, 8.5, 9.2, 9.3, 9.4, 10.4, 10.5. ✓ + +**No inconsistencies.** + +--- + +## Summary + +- **14 phases** (0-13), **85+ tasks**, **2,000+ lines**. +- **91 tests** (84 unit + 7 integration; 2 live_gui opt-in). +- **13 aggregate profiles** (10 real + 3 candidate placeholders). +- **4 top-level rollups** (summary, cross_audit_summary, decomposition_matrix, candidates). +- **5 follow-up tracks** recorded. +- **No new pip dependencies** (stdlib only). +- **No modifications to existing `src/*.py` files** (read-only on the 65 existing files; the v2 audit doesn't change them). +- **No modifications to the 5 existing audit scripts** (consume their JSON; don't change them). +- **No runtime profiling** (deferred to `pipeline_runtime_profiling_20260607`). +- **No MMA worker spawn action** (preserved from v1). +- **Cross-validates** the 2 foundational tracks (`data_structure_strengthening` + `data_oriented_error_handling`) directly. +- **Tolerates** the 3 candidate aggregates' absence (forward-compat placeholders). +- **Per-task atomic commits** (1 task = 1 commit; per `conductor/workflow.md` TDD protocol). +- **Per-task git notes** (each commit gets a `git notes add -m "..."` summary). +- **Coverage target: >80%** for `src/code_path_audit.py`. The 4 audit scripts + the meta-audit + the type registry check + 91 tests are the verification gates. + +--- + +**End of plan_v2.md.** +