diff --git a/conductor/tracks.md b/conductor/tracks.md index 71699403..fc0b5be4 100644 --- a/conductor/tracks.md +++ b/conductor/tracks.md @@ -28,6 +28,7 @@ Tracks that are unblocked and ready to start. Ordered by **dependency** (blocked | 6d-1 | A | [Result Migration Sub-Track 1: Review Pass](#track-result-migration-sub-track-1-review-pass-2026-06-17) | spec ✓, plan ✓, metadata ✓, state ✓; **shipped 2026-06-17** (43 sites classified: 23 compliant + 1 migration-target + 8 PATTERN_1/2 + 9 compliant + 1 audit-script-bug; 10 new heuristics added; 3 audit-script bugs documented) | `result_migration_20260616` (umbrella); `exception_handling_audit_20260616` (shipped 2026-06-16) | (**NEW 2026-06-17**; sub-track 1 of 5; 43 sites classified; no production code change; T-shirt S; per-site decisions feed sub-tracks 2-4; 3 audit-script bugs documented for sub-track 2 Phase 1) | | 6d-2 | A | [Result Migration Sub-Track 2: Small Files + Audit-Script Bug Fixes](#track-result-migration-sub-track-2-small-files--audit-script-bug-fixes-2026-06-17) | spec ✓, plan ✓, metadata ✓, state ✓, **shipped 2026-06-18** (Phase 10 REJECTED for sliming 21 sites via 5 laundering heuristics; Phase 11 REDOES the 21 sites: 5 full Result migrations in warmup.py + 2 helper extracts + 14 documented; Phase 12 = ACTUAL full Result[T] migration: 16 sites in api_hooks.py + 27 sites in 16 small files; Heuristic #19 REMOVED; visit_Try bug FIXED; Heuristic D ADDED; Drain Points section in styleguide; **Phase 12 REJECTED for false test claim**; **Phase 13 = script crash fixed (UTF-8 reconfigure in run_tests_batched.py) + 3 failures investigated on parent commit (0 regressions) + 4 pre-existing Gemini 503 tests documented with @pytest.mark.skip + test_execution_sim_live switched from gemini_cli to gemini per user directive (STILL FAILS, reported for diff track); 11/11 tiers actually run; 9 PASS clean + 2 PASS with documented issues) | `result_migration_20260616` (umbrella); `result_migration_review_pass_20260617` (shipped 2026-06-17) | (**NEW 2026-06-17**; sub-track 2 of 5; 37 files (35 SMALL + 2 MEDIUM) with 76 sites; Phase 1 = 3 audit-script bugs fixed; Phases 3-8 = 49 sites migrated; Phase 10 = 26 SILENT_SWALLOW + 14 new UNCLEAR sites via full Result + 5 new heuristics; **Phase 10 REJECTED; Phase 11 = 5 full Result + 2 helper extracts + 14 documented; 5 laundering heuristics REVERTED; Heuristic A ADDED; Phase 12 = ACTUAL migration of all sites + styleguide Drain Points; Phase 13 = test count verification; 2 reported issues for diff tracks**) | | 6d-3 | A | [Result Migration Sub-Track 3: App Controller](#track-result-migration-sub-track-3-app-controller-2026-06-18) | spec ✓, plan ✓, metadata ✓, state ✓, **active**; migrates 45 sites in `src/app_controller.py` to `Result[T]` (32 INTERNAL_BROAD_CATCH + 8 INTERNAL_SILENT_SWALLOW + 4 INTERNAL_RETHROW + 1 INTERNAL_OPTIONAL_RETURN); 22 sites stay as-is (15 BOUNDARY_FASTAPI + 2 BOUNDARY_SDK + 4 INTERNAL_COMPLIANT + 1 INTERNAL_PROGRAMMER_RAISE). **Phase 1 = fix the 2 known regressions** (test_tool_presets_execution::test_tool_ask_approval + test_extended_sims::test_execution_sim_live) caused by the half-migrated `session_logger.log_tool_call` call site in `_offload_entry_payload` (lines 3715, 3721). 5-file-commit pattern from `doeh_test_thinking_cleanup_20260615` (1 source + 1 test + 1 plan + 1 metadata + 1 state per task). 6 phases: (1) Setup + fix regressions; (2) 32 broad-catch → 4 bulk batches; (3) 8 silent-swallow → 2 batches with logging.debug per Heuristic #19; (4) 4 rethrow classified + 1 optional migrated; (5) Verify + audit + end-of-track report. | `result_migration_20260616` (umbrella); `result_migration_small_files_20260617` (shipped 2026-06-18) | (**NEW 2026-06-18**; sub-track 3 of 5; scope: 1 source file (src/app_controller.py) modified across 6 phases; 45 migration sites organized into 4 bulk batches + 3 single-site tasks; 1 new test file (test_app_controller_result.py) + 2 test files updated; 4 metadata/plan/state files; 1 end-of-track report; 18 atomic commits. **Scope larger than umbrella's T-shirt estimate** (45 migration + 22 stay = 67 total, not the estimated 22 + 34 = 56); the audit's per-category output is the source of truth, not the umbrella's T-shirt estimate**) | +| 6d-4 | A | [Result Migration Sub-Track 4: gui_2.py](#track-result-migration-sub-track-4-gui_2py-20260619) | spec ✓, plan ✓, metadata ✓, state ✓, **ready to start**; migrates 42 sites in `src/gui_2.py` (38 INTERNAL_BROAD_CATCH + 13 INTERNAL_SILENT_SWALLOW + 2 INTERNAL_RETHROW + 2 UNCLEAR); adds 3 new drain-plane render functions + 1 new test file. **Anti-sliming protocol: 13 phases cap each phase at <=10 sites with per-phase styleguide re-read + per-site audit pre/post check + per-phase invariant test.** | `result_migration_app_controller_20260618` (sub-track 3, SHIPPED 2026-06-19 with Phase 7; data plane ready) | (**NEW 2026-06-19**; sub-track 4 of 5; scope: 1 source file (src/gui_2.py) modified across 13 phases; 42 migration sites organized into 12 migration phases + 3 setup phases; 1 new test file (tests/test_gui_2_result.py) with 55+ tests; 4 metadata/plan/state/spec files; 1 end-of-track report; 60+ atomic commits. **Extra-long phase structure per user directive (2026-06-19) to prevent Tier 2 sliming.**) | | 6e | A (meta-tooling) | [Tier 2 Autonomous Sandbox (unattended track execution)](#track-tier-2-autonomous-sandbox-new-2026-06-16) | spec ✓, plan ✓, **shipped 2026-06-16** (9 phases, 24 default-on tests + 4 opt-in tests + 1 smoke e2e) | (none — independent; **NEW 2026-06-16**; meta-tooling; eliminates the `permission: ask` bottleneck for well-regularized tracks via a 3-layer enforcement stack: OpenCode permission system + Windows restricted token + git hooks) | | 7 | — | [UI Polish (Five Issues)](#track-ui-polish-five-issues) | spec ✓, plan ✓, ready to start (Phases 1/4/5 shipped; Phases 2/3 code shipped but tests broken — fixed by track 6a) | (none — independent) | | 7a | B | [SQLite-Granularity Inline Docs for gui_2.py](#track-sqlite-granularity-inline-docs-for-gui_2py) | spec ✓, plan ✓, complete | (none — independent) | diff --git a/conductor/tracks/result_migration_gui_2_20260619/metadata.json b/conductor/tracks/result_migration_gui_2_20260619/metadata.json new file mode 100644 index 00000000..15d8b654 --- /dev/null +++ b/conductor/tracks/result_migration_gui_2_20260619/metadata.json @@ -0,0 +1,106 @@ +{ + "id": "result_migration_gui_2_20260619", + "name": "Result Migration - Sub-Track 4 (gui_2.py)", + "date": "2026-06-19", + "type": "refactor", + "priority": "A", + "spec": "conductor/tracks/result_migration_gui_2_20260619/spec.md", + "plan": "conductor/tracks/result_migration_gui_2_20260619/plan.md", + "status": "active", + "umbrella": "result_migration_20260616", + "sub_track_index": 4, + "blocked_by": { + "result_migration_app_controller_20260618": "shipped 2026-06-19 (with Phase 7); the data plane (8 controller state attributes) is ready" + }, + "blocks": { + "result_migration_baseline_cleanup": "blocked by this track; date TBD when this track ships" + }, + "scope": { + "new_files": [ + "tests/test_gui_2_result.py", + "docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md", + "tests/artifacts/PHASE1_SITE_INVENTORY.md" + ], + "modified_files": [ + "src/gui_2.py", + "conductor/tracks.md", + "conductor/tracks/result_migration_gui_2_20260619/state.toml", + "conductor/tracks/result_migration_gui_2_20260619/metadata.json", + "conductor/tracks/result_migration_gui_2_20260619/plan.md", + "conductor/tracks/result_migration_gui_2_20260619/spec.md", + "conductor/tracks/result_migration_20260616/spec.md" + ], + "deleted_files": [] + }, + "verification_criteria": [ + "src/gui_2.py has zero INTERNAL_BROAD_CATCH sites (38 migrated across Phases 3, 4, 5)", + "src/gui_2.py has zero INTERNAL_SILENT_SWALLOW sites (13 migrated in Phase 10; per error_handling.md:530 logging is NOT a drain)", + "src/gui_2.py has zero INTERNAL_RETHROW sites (2 classified or migrated in Phase 11 per Pattern 1/2/3)", + "src/gui_2.py has zero UNCLEAR sites (2 classified in Phase 12)", + "src/gui_2.py has the 3 new drain-plane render functions: render_controller_error_modal, _render_worker_error_indicator, _render_last_request_errors_modal (Phase 2)", + "tests/test_gui_2_result.py has 55+ tests (42 site tests + 13 invariant tests), all pass", + "uv run python scripts/audit_exception_handling.py --src src/gui_2.py --strict exits 0", + "11-tier batched test suite passes with no new regressions", + "Per-phase audit gates verified: each phase's invariant test confirms the expected count drop", + "TIER-2 READ styleguide acknowledged in commit message at start of every phase (13 styleguide-ack commits)", + "Git history shows 60+ atomic commits (42 site migrations + 13 phase setup commits + 3 infra commits + 2 docs commits)", + "docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md covers all 13 phases", + "conductor/tracks.md row updated to 'shipped 2026-06-XX'", + "umbrella spec count updated to reflect actual scope (42 migration + 6 infra = 48 sites in this sub-track)" + ], + "regressions_and_pre_existing_failures": [], + "pre_existing_failures_remaining": [], + "deferred_to_followup_tracks": [ + { + "title": "Sub-track 5: result_migration_baseline_cleanup", + "description": "Close the remaining 77 violations in the 3 refactored baseline files (mcp_client.py, ai_client.py, rag_engine.py). Per umbrella sub-track 5.", + "track_status": "planned (blocked by this track)" + } + ], + "estimated_effort": { + "method": "scope (per workflow.md Tier 1 Track Initialization Rules). NO day estimates.", + "scope": "1 source file (src/gui_2.py) modified across 13 phases; 42 migration sites + 6 infra sites organized into 12 migration phases (3-12) + 1 setup phase (0) + 1 inventory phase (1) + 1 drain-plane phase (2) + 1 verification phase (13); 1 new test file (tests/test_gui_2_result.py) with 55+ tests; 4 metadata/plan/state/spec files; 1 end-of-track report; 1 site inventory doc. 60+ atomic commits." + }, + "risk_register": [ + { + "risk": "Tier 2 invents a laundering heuristic for the 2 UNCLEAR sites (L1349 from sub-track 1's review pass)", + "likelihood": "medium", + "mitigation": "Phase 12 forces explicit classification with comment per site; the Phase 7 heuristic (sub-track 3) already classifies correctly; 5 regression-guard tests in tests/test_audit_heuristics.py lock the heuristic" + }, + { + "risk": "Tier 2 doesn't migrate INTERNAL_SILENT_SWALLOW sites that 'look like' logging-only but aren't actually drained (the sliming pattern)", + "likelihood": "medium", + "mitigation": "Phase 1 inventory forces explicit classification per site BEFORE coding (tests/artifacts/PHASE1_SITE_INVENTORY.md); Phase 10's audit gate enforces 0 INTERNAL_SILENT_SWALLOW; styleguide re-read at start of Phase 10 explicitly calls out the sliming risk" + }, + { + "risk": "gui_2.py's render loop changes break the immediate-mode frame", + "likelihood": "medium", + "mitigation": "Render-loop sites are isolated in Phase 3 (Batch A); visual verification via live_gui tests; per-site unit tests verify success-path output is identical" + }, + { + "risk": "Scope grows as Tier 2 finds more sites mid-migration", + "likelihood": "low", + "mitigation": "Phase 1 inventory freezes the 42-site list; new sites discovered mid-migration are tracked but NOT migrated in this track (added to a follow-up)" + }, + { + "risk": "User's principle ('logging is NOT a drain') is misapplied", + "likelihood": "low", + "mitigation": "Styleguide re-read at start of each phase; commit-message acknowledgment ('TIER-2 READ ...'); 13 invariant tests verify per-phase progress" + }, + { + "risk": "Thread-safety violation in worker sites (Phase 7)", + "likelihood": "low", + "mitigation": "app._worker_errors_lock is already in place (sub-track 3 Phase 6); multi-thread unit test (test_worker__thread_safe_under_concurrent_appends) verifies" + }, + { + "risk": "11-tier batched suite times out before all tiers run (per result_migration_small_files_20260617 Phase 12->13 incident)", + "likelihood": "medium", + "mitigation": "Phase 13 uses uv run python scripts/run_tests_batched.py (the fixed script from sub-track 2 Phase 13.1); if it times out, Tier 2 reports and the user decides" + }, + { + "risk": "Per-phase audit gate shows wrong count (heuristic misclassification)", + "likelihood": "low", + "mitigation": "The audit heuristic was verified by 5 regression-guard tests in sub-track 3 Phase 7; if a count is wrong, Tier 2 reports" + } + ] +} \ No newline at end of file diff --git a/conductor/tracks/result_migration_gui_2_20260619/plan.md b/conductor/tracks/result_migration_gui_2_20260619/plan.md new file mode 100644 index 00000000..3c9078e1 --- /dev/null +++ b/conductor/tracks/result_migration_gui_2_20260619/plan.md @@ -0,0 +1,1290 @@ +# Result Migration — Sub-Track 4 (gui_2.py) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use `mma-tier3-worker` (recommended) or `mma-tier2-tech-lead` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Migrate all 42 migration-target sites in `src/gui_2.py` (the immediate-mode ImGui rendering layer) to the data-oriented `Result[T]` convention, using the 8 controller state attributes added by sub-track 3 as the data plane and adding 3 new render functions as the drain plane. + +**Architecture:** Per-site `_result` helpers return `Result[T]` where T is the data the caller needs. Callers check `.ok` and either accumulate in the appropriate controller attribute (data plane) or trigger a drain modal (Pattern 2 from `error_handling.md`). The drain plane (3 new render functions) reads from the controller's data plane and renders errors as popups/status indicators. + +**Tech Stack:** Python 3.11+, imgui_bundle (immediate-mode ImGui), dearpygui (legacy), pytest. Existing infrastructure: `Result[T]` from `src/result_types.py:91-105`, audit script at `scripts/audit_exception_handling.py`, 5 regression-guard tests at `tests/test_audit_heuristics.py`, 8 controller state attributes (added by sub-track 3 Phase 6). + +--- + +## Anti-Sliming Protocol (MANDATORY for every phase) + +The previous sub-tracks slimed when scope felt tight (sub-track 2 Phase 10 slimed 21 of 26 sites via 5 laundering heuristics; sub-track 3 Phase 3 slimed 8 sites via logging.debug bodies). This protocol prevents sliming: + +1. **Pre-phase styleguide re-read** (commit 1 of the phase): Read `conductor/code_styleguides/error_handling.md` end-to-end. Commit message MUST include "TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase N." + +2. **Audit pre-check** (per site, before migration): Run `uv run python scripts/audit_exception_handling.py --src src/gui_2.py --json 2>&1 | grep -A 2 "L"` to confirm the site's category BEFORE migration. Capture in commit body. + +3. **Red** (1 commit per site): Write the unit test in `tests/test_gui_2_result.py`. Run test — MUST FAIL. Commit. + +4. **Green** (1 commit per site): Migrate the site. Use the migration pattern for the phase. Run test — MUST PASS. Commit. + +5. **Audit post-check** (per site, after migration): Same command. Confirm the site moved out of the violation category. Capture in commit body. + +6. **Phase invariant test** (1 commit at end of phase): `test_phase_N__invariant` verifies the per-phase count drop. + +7. **If a site "resists migration":** DO NOT invent a heuristic. Report to the user (Tier 1). The user decides whether to fix forward or defer. + +8. **Per-file atomic commits:** 1 site = 1 commit (per `workflow.md` "ATOMIC PER-TASK COMMITS"). + +--- + +## File Structure + +**Files created (3):** +- `tests/test_gui_2_result.py` — the new test file (≥55 tests: 42 site tests + 13 invariant tests) +- `docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md` — the end-of-track report (Phase 13) +- `tests/artifacts/PHASE1_SITE_INVENTORY.md` — the site inventory (Phase 1) + +**Files modified (4):** +- `src/gui_2.py` — the migration target (42 sites + 3 new render functions in Phase 2) +- `conductor/tracks.md` — add the new track row (Phase 0) +- `conductor/tracks/result_migration_gui_2_20260619/state.toml` — task statuses +- `conductor/tracks/result_migration_gui_2_20260619/metadata.json` — verification criteria + +**Files NOT modified:** +- `scripts/audit_exception_handling.py` — the audit heuristic is correct from sub-track 3 Phase 7; do not change +- `src/app_controller.py` — the data plane is correct from sub-track 3 Phase 6; this track only consumes it +- `tests/test_audit_heuristics.py` — the 5 regression-guard tests are correct; do not change + +--- + +## Phase 0: Setup + Styleguide Re-Read (3 tasks) + +**Focus:** Initialize the track, update tracks.md, Tier 2 reads the styleguide end-to-end, acknowledge in commit message. + +### Task 0.1: Update `conductor/tracks.md` + +**Files:** +- Modify: `conductor/tracks.md` (add new row in the "Active Tracks (Current Queue)" table, after row 6d-3) + +- [ ] **Step 1: Read the current tracks.md row for sub-track 3** + +Run: `grep -n "result_migration_app_controller_20260618" conductor/tracks.md | head -5` + +- [ ] **Step 2: Add the new row after sub-track 3** + +Insert in the "Active Tracks (Current Queue)" table (between row 6d-3 and row 6e): + +``` +| 6d-4 | A | [Result Migration Sub-Track 4: gui_2.py](#track-result-migration-sub-track-4-gui_2py-20260619) | spec ✓, plan pending, **ready to start** | `result_migration_app_controller_20260618` (sub-track 3, SHIPPED 2026-06-19 with Phase 7) | +``` + +- [ ] **Step 3: Commit** + +```bash +git add conductor/tracks.md +git commit -m "conductor(tracks): add result_migration_gui_2_20260619 row" +``` + +### Task 0.2: Tier 2 reads the styleguide end-to-end + +**Files:** (no file changes; verification is the commit message) + +- [ ] **Step 1: Read `conductor/code_styleguides/error_handling.md` end-to-end** + +This file is 989 lines. Read all sections: +1. The 5 Patterns (lines 22-130) +2. The Data Model (lines 132-150) +3. Decision Tree (lines 152-171) +4. Anti-Patterns (lines 173-194) +5. Examples (lines 196-210) +6. Hard Rules (lines 212-251) +7. When to Use This Convention (lines 253-272) +8. Boundary Types (lines 274-355) +9. Drain Points (lines 356-518) ← CRITICAL: 5 drain patterns + heuristic D +10. Broad-Except Distinction (lines 520-583) ← CRITICAL: narrow+log = violation +11. Constructors Can Raise (lines 584-624) +12. Re-Raise Patterns (lines 625-693) +13. Audit Script (lines 695-774) +14. Migration Playbook (lines 776-792) +15. AI Agent Checklist (lines 809-974) ← CRITICAL: 5 MUST-DO + 7 MUST-NOT-DO + 3 boundary patterns + +- [ ] **Step 2: Acknowledge the read in an empty commit** + +```bash +git commit --allow-empty -m "chore: TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase 0" +``` + +### Task 0.3: Phase 0 checkpoint + +- [ ] **Step 1: Create empty commit marking Phase 0 complete** + +```bash +git commit --allow-empty -m "conductor(plan): mark Phase 0 complete (setup + styleguide re-read)" +``` + +- [ ] **Step 2: Update state.toml Phase 0 status** + +```toml +phase_0 = { status = "completed", checkpointsha = "", name = "Setup + styleguide re-read" } +``` + +- [ ] **Step 3: Commit state.toml** + +```bash +git add conductor/tracks/result_migration_gui_2_20260619/state.toml +git commit -m "conductor(state): mark Phase 0 complete" +``` + +--- + +## Phase 1: Site Inventory + Classification (3 tasks) + +**Focus:** Run the audit, walk every finding, classify each of the 42 migration-target sites. + +### Task 1.1: Run the audit + capture JSON output + +- [ ] **Step 1: Run audit and save JSON** + +```bash +uv run python scripts/audit_exception_handling.py --src src/gui_2.py --json > tests/artifacts/PHASE1_AUDIT.json +``` + +- [ ] **Step 2: Verify JSON was generated** + +```bash +ls -la tests/artifacts/PHASE1_AUDIT.json +``` + +Expected: file exists, size > 1KB + +### Task 1.2: Walk the audit + write the inventory + +**Files:** +- Create: `tests/artifacts/PHASE1_SITE_INVENTORY.md` + +- [ ] **Step 1: Extract the 42 migration-target sites** + +```bash +uv run python -c " +import json +data = json.load(open('tests/artifacts/PHASE1_AUDIT.json')) +gui = [f for f in data['files'] if 'gui_2' in f.get('filename', '')][0] +findings = gui['findings'] +migration_targets = [f for f in findings if f.get('category') in ('INTERNAL_BROAD_CATCH', 'INTERNAL_SILENT_SWALLOW', 'INTERNAL_RETHROW', 'UNCLEAR')] +print(f'Total migration-target sites: {len(migration_targets)}') +for f in migration_targets: + print(f\"L{f['line']}: [{f['category']}]\") +" > tests/artifacts/PHASE1_MIGRATION_TARGETS.txt +``` + +- [ ] **Step 2: Verify count = 42** + +```bash +cat tests/artifacts/PHASE1_MIGRATION_TARGETS.txt | head -1 +``` + +Expected: `Total migration-target sites: 42` + +- [ ] **Step 3: For each migration-target site, write the inventory entry** + +For each line in `PHASE1_MIGRATION_TARGETS.txt` (skip the first "Total" line), read the actual code at that line and write to `tests/artifacts/PHASE1_SITE_INVENTORY.md`: + +```markdown +# Phase 1 Site Inventory — gui_2.py + +Generated: +Total migration-target sites: 42 + +| Line | Category | Current code (5 lines around) | Target migration | Drain point | +|---|---|---|---|---| +| L | | | | | +| ... | +``` + +Use this command template to extract the code around each line: +```bash +sed -n ',p' src/gui_2.py +``` + +For "Target migration", reference the per-phase pattern (e.g., "render-loop Result[bool] helper" for Phase 3, "modal Result[bool] helper + drain modal" for Phase 4, etc.). + +For "Drain point", specify one of: +- `app._last_request_errors.append(...)` — data plane +- `app._worker_errors.append(...)` + lock — data plane (thread-safe) +- `app._startup_timeline_errors.append(...)` — data plane +- `app._signal_handler_error = ...` — data plane +- `app._inject_preview_error = ...` — data plane +- `app._mcp_config_parse_error = ...` — data plane +- `app._save_project_error = ...` — data plane +- `app._model_fetch_errors[p] = ...` — data plane +- `imgui.open_popup("Error")` — immediate drain (Pattern 2) +- `sys.stderr.write(...) + sys.exit(1)` — Pattern 3 (app termination) + +- [ ] **Step 4: Commit the inventory** + +```bash +git add tests/artifacts/PHASE1_AUDIT.json tests/artifacts/PHASE1_MIGRATION_TARGETS.txt tests/artifacts/PHASE1_SITE_INVENTORY.md +git commit -m "conductor(plan): Phase 1 site inventory — 42 migration-target sites classified" +``` + +### Task 1.3: Phase 1 invariant test + checkpoint + +**Files:** +- Create: `tests/test_gui_2_result.py` (initial creation; will be extended each phase) +- Modify: `conductor/tracks/result_migration_gui_2_20260619/state.toml` + +- [ ] **Step 1: Create the test file with Phase 1 invariant test** + +```python +"""Tests for gui_2.py Result[T] migration (sub-track 4 of result_migration_20260616). + +Per the anti-sliming protocol, each phase has an invariant test that locks +the per-phase progress. Per-site tests are added per phase. +""" +import json +import subprocess +from pathlib import Path + + +def _load_gui_2_audit() -> dict: + """Re-run the audit and return the gui_2.py findings.""" + audit_json = Path("tests/artifacts/PHASE1_AUDIT.json") + if not audit_json.exists(): + # Re-run the audit if the cached JSON is missing + subprocess.run( + ["uv", "run", "python", "scripts/audit_exception_handling.py", + "--src", "src/gui_2.py", "--json"], + check=True, capture_output=True, + ) + # (caller must re-read; this helper is for tests that assume the JSON exists) + return json.loads(audit_json.read_text()) + + +def test_phase_1_invariant_inventory_complete(): + """Phase 1 invariant: the inventory file exists and has 42 rows.""" + inventory = Path("tests/artifacts/PHASE1_SITE_INVENTORY.md") + assert inventory.exists(), "PHASE1_SITE_INVENTORY.md must exist" + content = inventory.read_text() + # Count the table rows (lines starting with | L and a digit) + import re + row_count = len(re.findall(r"^\| L\d+", content, re.MULTILINE)) + assert row_count == 42, f"Expected 42 sites in inventory, found {row_count}" + + +def test_phase_1_invariant_baseline_captured(): + """Phase 1 invariant: the audit JSON captures the baseline counts.""" + data = _load_gui_2_audit() + gui = [f for f in data["files"] if "gui_2" in f.get("filename", "")][0] + assert gui["violation_count"] >= 38, f"Expected ≥38 violations, found {gui['violation_count']}" + assert gui["suspicious_count"] >= 2, f"Expected ≥2 suspicious, found {gui['suspicious_count']}" + assert gui["unclear_count"] >= 2, f"Expected ≥2 unclear, found {gui['unclear_count']}" +``` + +- [ ] **Step 2: Run the test — it should PASS (the inventory was committed in Task 1.2)** + +```bash +uv run python -m pytest tests/test_gui_2_result.py::test_phase_1_invariant_inventory_complete tests/test_gui_2_result.py::test_phase_1_invariant_baseline_captured -v +``` + +Expected: 2 PASSED + +- [ ] **Step 3: Update state.toml Phase 1** + +```toml +phase_1 = { status = "completed", checkpointsha = "", name = "Site inventory + classification (42 sites)" } +``` + +- [ ] **Step 4: Commit** + +```bash +git add tests/test_gui_2_result.py conductor/tracks/result_migration_gui_2_20260619/state.toml +git commit -m "conductor(plan): mark Phase 1 complete (42-site inventory + invariant test)" +``` + +--- + +## Phase 2: Drain Plane Wiring (4 tasks) + +**Focus:** Add the 3 new render functions that read from the controller's data plane and render errors as popups / status indicators. This is the drain plane. + +### Task 2.1: Add `render_controller_error_modal` + +**Files:** +- Modify: `src/gui_2.py` (add new module-level render function near `render_error_tint` at line ~6700) + +- [ ] **Step 1: Add the new render function** + +Add to `src/gui_2.py` (find the existing `render_error_tint` function and add this BELOW it): + +```python +def render_controller_error_modal(app: 'App') -> None: + """Drain plane: render accumulated controller errors as a popup modal. + + Reads from 8 controller attributes added by sub-track 3 Phase 6: + - app._last_request_errors: per-request RAG + symbol resolution errors + - app._worker_errors + lock: background worker errors + - app._startup_timeline_errors: first-frame + warmup errors + - app._signal_handler_error: signal install errors + - app._inject_preview_error: context preview errors + - app._mcp_config_parse_error: MCP config parse errors + - app._save_project_error: project save errors + - app._model_fetch_errors: per-provider model fetch errors + + For each non-empty attribute, opens an imgui.open_popup(...) modal with + the errors displayed. Per error_handling.md:396-407 Pattern 2 (GUI error + display = drain). + + Pattern: this function returns None (drain terminates propagation). + Thread-safe: app._worker_errors_lock is acquired on read. + """ + # 1. Per-request errors (most common; surface first) + if hasattr(app, "_last_request_errors") and app._last_request_errors: + for op_name, err in app._last_request_errors: + if imgui.begin_popup(f"Error: last_request[{op_name}]"): + imgui.text(f"Operation: {op_name}") + imgui.separator() + imgui.text_wrapped(err.ui_message() if hasattr(err, "ui_message") else str(err)) + imgui.separator() + if imgui.button("Dismiss"): + app._last_request_errors = [(n, e) for n, e in app._last_request_errors if e != err] + imgui.close_current_popup() + imgui.end_popup() + break # show one at a time + + # 2. Worker errors (thread-safe read) + worker_errors: list = [] + if hasattr(app, "_worker_errors_lock"): + with app._worker_errors_lock: + worker_errors = list(app._worker_errors) if hasattr(app, "_worker_errors") else [] + if worker_errors: + if imgui.begin_popup("Error: workers"): + imgui.text(f"{len(worker_errors)} worker error(s):") + imgui.separator() + for op_name, err in worker_errors[:10]: # cap at 10 to avoid spam + imgui.bullet_text(f"[{op_name}] {err.ui_message() if hasattr(err, 'ui_message') else str(err)}") + if len(worker_errors) > 10: + imgui.text(f"... and {len(worker_errors) - 10} more") + imgui.separator() + if imgui.button("Clear worker errors"): + with app._worker_errors_lock: + app._worker_errors.clear() + imgui.close_current_popup() + imgui.end_popup() + + # 3. Single-error attributes + for attr_name in ("_signal_handler_error", "_inject_preview_error", + "_mcp_config_parse_error", "_save_project_error"): + err = getattr(app, attr_name, None) + if err is None: + continue + popup_name = f"Error: {attr_name.lstrip('_')}" + if imgui.begin_popup(popup_name): + imgui.text(f"Source: {attr_name}") + imgui.separator() + imgui.text_wrapped(err.ui_message() if hasattr(err, "ui_message") else str(err)) + imgui.separator() + if imgui.button("Dismiss"): + setattr(app, attr_name, None) + imgui.close_current_popup() + imgui.end_popup() + break + + # 4. Startup timeline errors (list) + timeline_errors = getattr(app, "_startup_timeline_errors", None) + if timeline_errors: + if imgui.begin_popup("Error: startup_timeline"): + imgui.text(f"{len(timeline_errors)} startup error(s):") + imgui.separator() + for op_name, err in timeline_errors: + imgui.bullet_text(f"[{op_name}] {err.ui_message() if hasattr(err, 'ui_message') else str(err)}") + imgui.end_popup() + + # 5. Per-provider model fetch errors (dict) + model_errors = getattr(app, "_model_fetch_errors", None) + if model_errors: + if imgui.begin_popup("Error: model_fetch"): + imgui.text(f"{len(model_errors)} provider(s) failed:") + imgui.separator() + for provider, err in model_errors.items(): + imgui.bullet_text(f"[{provider}] {err.ui_message() if hasattr(err, 'ui_message') else str(err)}") + imgui.end_popup() +``` + +**SAFETY:** This function uses `hasattr` guards because the 8 attributes are added by sub-track 3 Phase 6 — if the controller is older (e.g., during a partial migration), some attributes may not exist yet. The `imgui.begin_popup(...)` / `imgui.end_popup()` are balanced. + +- [ ] **Step 2: Verify the function compiles** + +```bash +uv run python -c "from src import gui_2; assert hasattr(gui_2, 'render_controller_error_modal'); print('OK')" +``` + +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add src/gui_2.py +git commit -m "feat(gui_2): add render_controller_error_modal (drain plane Pattern 2)" +``` + +### Task 2.2: Add `_render_worker_error_indicator` + +**Files:** +- Modify: `src/gui_2.py` + +- [ ] **Step 1: Add the indicator function below `render_controller_error_modal`** + +```python +def _render_worker_error_indicator(app: 'App') -> None: + """Status-bar widget: shows worker error count; click opens the modal. + + Visible only when app._worker_errors is non-empty. The indicator is a + small red-text label in the status bar; clicking it calls + imgui.open_popup("Error: workers") which is handled by + render_controller_error_modal. + """ + if not hasattr(app, "_worker_errors"): + return + worker_count = 0 + if hasattr(app, "_worker_errors_lock"): + with app._worker_errors_lock: + worker_count = len(app._worker_errors) + else: + worker_count = len(app._worker_errors) + if worker_count == 0: + return + imgui.push_style_color(imgui.Col_.text, 1.0, 0.4, 0.4, 1.0) # red + if imgui.small_button(f"[!] {worker_count} worker error(s)"): + imgui.open_popup("Error: workers") + imgui.pop_style_color() + imgui.same_line() +``` + +**SAFETY:** Thread-safe read via the lock. Uses `imgui.push_style_color` / `imgui.pop_style_color` balanced. + +- [ ] **Step 2: Verify it compiles** + +```bash +uv run python -c "from src import gui_2; assert hasattr(gui_2, '_render_worker_error_indicator'); print('OK')" +``` + +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add src/gui_2.py +git commit -m "feat(gui_2): add _render_worker_error_indicator (status bar)" +``` + +### Task 2.3: Add `_render_last_request_errors_modal` + +**Files:** +- Modify: `src/gui_2.py` + +- [ ] **Step 1: Add the per-request error modal** + +```python +def _render_last_request_errors_modal(app: 'App') -> None: + """Per-request error modal: shows errors from the most recent AI request. + + Called from _handle_generate_send after each AI request completes. + Only opens if app._last_request_errors is non-empty. The user can + dismiss errors via a button; clearing the list resets the data plane. + """ + if not hasattr(app, "_last_request_errors"): + return + if not app._last_request_errors: + return + if not imgui.is_popup_open("Error: last_request"): + imgui.open_popup("Error: last_request") + if imgui.begin_popup_modal("Error: last_request")[0]: + imgui.text(f"{len(app._last_request_errors)} error(s) during last request:") + imgui.separator() + for op_name, err in app._last_request_errors: + imgui.bullet_text(f"[{op_name}] {err.ui_message() if hasattr(err, 'ui_message') else str(err)}") + imgui.separator() + if imgui.button("Dismiss all"): + app._last_request_errors.clear() + imgui.close_current_popup() + imgui.end_popup() +``` + +**SAFETY:** Modal is opened only if errors exist; the `imgui.begin_popup_modal(...)[0]` pattern is the standard imgui-bundle modal idiom. The `app._last_request_errors.clear()` is main-thread-only (no lock needed; same thread that appended). + +- [ ] **Step 2: Verify it compiles** + +```bash +uv run python -c "from src import gui_2; assert hasattr(gui_2, '_render_last_request_errors_modal'); print('OK')" +``` + +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add src/gui_2.py +git commit -m "feat(gui_2): add _render_last_request_errors_modal (per-request errors)" +``` + +### Task 2.4: Phase 2 invariant test + checkpoint + +**Files:** +- Modify: `tests/test_gui_2_result.py` +- Modify: `conductor/tracks/result_migration_gui_2_20260619/state.toml` + +- [ ] **Step 1: Add Phase 2 invariant test** + +Append to `tests/test_gui_2_result.py`: + +```python +def test_phase_2_invariant_drain_plane_render_functions_exist(): + """Phase 2 invariant: the 3 drain plane render functions exist.""" + from src import gui_2 + assert hasattr(gui_2, "render_controller_error_modal"), "render_controller_error_modal missing" + assert hasattr(gui_2, "_render_worker_error_indicator"), "_render_worker_error_indicator missing" + assert hasattr(gui_2, "_render_last_request_errors_modal"), "_render_last_request_errors_modal missing" + + +def test_phase_2_invariant_audit_still_passes(): + """Phase 2 invariant: introducing the drain plane did not create new violations.""" + import subprocess + r = subprocess.run( + ["uv", "run", "python", "scripts/audit_exception_handling.py", "--src", "src/gui_2.py"], + capture_output=True, text=True, + ) + # The audit's output for gui_2.py should still show the original counts + assert "gui_2.py" in r.stdout + # No new SILENT_SWALLOW or BROAD_CATCH introduced by the new functions + # (the new functions are clean Result-based; the audit should not flag them) + gui_line = [l for l in r.stdout.split("\n") if "gui_2.py" in l and "INTERNAL" not in l] + assert gui_line, f"Expected gui_2.py line in audit output, got: {r.stdout}" +``` + +- [ ] **Step 2: Run the new tests + Phase 1 tests (regression check)** + +```bash +uv run python -m pytest tests/test_gui_2_result.py -v +``` + +Expected: 4 PASSED (2 Phase 1 + 2 Phase 2) + +- [ ] **Step 3: Update state.toml Phase 2** + +```toml +phase_2 = { status = "completed", checkpointsha = "", name = "Drain plane wiring (3 new render functions)" } +``` + +- [ ] **Step 4: Commit** + +```bash +git add tests/test_gui_2_result.py conductor/tracks/result_migration_gui_2_20260619/state.toml +git commit -m "conductor(plan): mark Phase 2 complete (drain plane: 3 render functions + 2 invariant tests)" +``` + +--- + +## Migration Pattern (used by Phases 3-12) + +Every migration follows this pattern. The `_result` helper convention: + +```python +# BEFORE (in src/gui_2.py) +def _render_x(app, ...): + try: + do_something() + except Exception as e: + sys.stderr.write(f"Error: {e}\n") # SLIMING: logging-only, NOT a drain + +# AFTER +def _render_x_result(app, ...) -> Result[bool]: + """Drain-aware: returns Result[bool] so caller can check .ok and route to data plane.""" + try: + do_something() + return Result(data=True) + except Exception as e: + return Result(data=False, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, message=str(e), + source="gui_2._render_x_result", original=e, + )]) + +def _render_x(app, ...): + """Legacy wrapper. Checks .ok; routes errors to controller's data plane.""" + result = _render_x_result(app, ...) + if not result.ok: + app._last_request_errors.append(("_render_x", result.errors[0])) + return # drain to data plane; sub-track 4's render_controller_error_modal surfaces it +``` + +The unit test pattern: + +```python +def test__returns_result_on_success(app_with_x): + """Migrated helper returns Result.ok=True on success.""" + app, _ = app_with_x + result = gui_2._render_x_result(app, ...) + assert result.ok + assert result.data is True + assert result.errors == [] + + +def test__returns_result_with_error_on_failure(app_with_x_failing): + """Migrated helper returns Result.ok=False with ErrorInfo on failure.""" + app, _ = app_with_x_failing + result = gui_2._render_x_result(app, ...) + assert not result.ok + assert result.errors + assert result.errors[0].kind == ErrorKind.INTERNAL + assert result.errors[0].source == "gui_2._render_x_result" + + +def test__legacy_wrapper_accumulates_to_data_plane(app_with_x_failing): + """Legacy wrapper routes errors to app._last_request_errors (data plane).""" + app, _ = app_with_x_failing + gui_2._render_x(app, ...) + assert len(app._last_request_errors) >= 1 + assert app._last_request_errors[-1][0] == "_render_x" +``` + +**For render-loop sites (Phase 3):** the legacy wrapper returns early on error to avoid breaking the immediate-mode frame. +**For modal sites (Phase 4):** the legacy wrapper triggers `imgui.open_popup("Error: ...")`. +**For event handlers (Phase 5):** the legacy wrapper appends to `app._last_request_errors` (per-event accumulator). +**For signal handlers (Phase 6):** the helper itself is the drain (Pattern 3). +**For worker sites (Phase 7):** the legacy wrapper uses `app._report_worker_error`. +**For setters (Phase 8):** the legacy wrapper uses `app._report_worker_error`. +**For helpers (Phase 9):** the legacy wrapper appends to the appropriate controller attribute. +**For silent-swallow sites (Phase 10):** identical to the pattern above; the only difference is that the source site was logging-only. + +--- + +## Phase 3: INTERNAL_BROAD_CATCH Batch A — Render-Loop Sites (≤10 sites) + +**Focus:** The broad-catch sites inside render-loop functions called every frame from `render_main_interface`. Per-site tasks use the migration pattern above. + +### Task 3.0: Phase 3 styleguide re-read + site list confirmation + +- [ ] **Step 1: Re-read `conductor/code_styleguides/error_handling.md` lines 396-407 (Pattern 2 GUI error display = drain) and 462-476 (what is NOT a drain)** + +```bash +sed -n '356,518p' conductor/code_styleguides/error_handling.md +``` + +- [ ] **Step 2: Confirm the Batch A site list from the Phase 1 inventory** + +```bash +grep -E "Phase 3|Batch A|render-loop" tests/artifacts/PHASE1_SITE_INVENTORY.md | head -20 +``` + +- [ ] **Step 3: Styleguide ack commit (empty)** + +```bash +git commit --allow-empty -m "chore: TIER-2 READ conductor/code_styleguides/error_handling.md lines 356-518 (Pattern 2 drain) before Phase 3" +``` + +### Task 3.1: Migrate site L (representative example) + +**Files:** +- Modify: `src/gui_2.py:L` +- Modify: `tests/test_gui_2_result.py` + +- [ ] **Step 1: Write failing test** + +```python +def test_l_returns_result_on_success(): + """ returns Result.ok=True on success.""" + from src import gui_2 + # Build a minimal app mock with the controller attributes this site reads + app = make_mock_app_with_() + result = gui_2.__result(app, ...) + assert result.ok + assert result.data is True + assert result.errors == [] + + +def test_l_returns_result_with_error_on_failure(): + """ returns Result.ok=False with ErrorInfo on failure.""" + from src import gui_2 + app = make_mock_app_with__failing() + result = gui_2.__result(app, ...) + assert not result.ok + assert result.errors + assert result.errors[0].source == "gui_2.__result" +``` + +- [ ] **Step 2: Run test, verify FAIL** + +```bash +uv run python -m pytest tests/test_gui_2_result.py::test_l_returns_result_on_success -v +``` + +Expected: FAIL with `AttributeError: module 'src.gui_2' has no attribute '__result'` + +- [ ] **Step 3: Migrate the site** + +In `src/gui_2.py`, find the existing `_` function (around L) and: +1. Extract a new `__result(app, ...) -> Result[bool]` helper ABOVE the original +2. The original `_` becomes the legacy wrapper that calls the helper and routes errors to `app._last_request_errors` + +```python +def __result(app: 'App', ...) -> Result[bool]: + """Drain-aware variant of _. Per anti-sliming protocol.""" + try: + # ... original body ... + return Result(data=True) + except Exception as e: + return Result(data=False, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, message=str(e), + source="gui_2.__result", original=e, + )]) + +def _(app: 'App', ...): + """Legacy wrapper. Routes errors to app._last_request_errors.""" + result = __result(app, ...) + if not result.ok: + app._last_request_errors.append(("_", result.errors[0])) + return + # ... original success path ... +``` + +- [ ] **Step 4: Run test, verify PASS** + +```bash +uv run python -m pytest tests/test_gui_2_result.py::test_l_returns_result_on_success tests/test_gui_2_result.py::test_l_returns_result_with_error_on_failure -v +``` + +Expected: 2 PASSED + +- [ ] **Step 5: Audit pre/post check** + +```bash +# PRE (capture before; should show the site as INTERNAL_BROAD_CATCH) +uv run python scripts/audit_exception_handling.py --src src/gui_2.py --json 2>&1 | python -c " +import json, sys +data = json.loads(sys.stdin.read()) +gui = [f for f in data['files'] if 'gui_2' in f.get('filename', '')][0] +site = [f for f in gui['findings'] if f.get('line') == ] +print('PRE:', site[0]['category'] if site else 'NOT FOUND') +" +# POST (after migration; should show the site in INTERNAL_COMPLIANT or moved out of V) +# (run after the migration code is saved) +``` + +- [ ] **Step 6: Commit** + +```bash +git add src/gui_2.py tests/test_gui_2_result.py +git commit -m "refactor(gui_2): migrate L _ to Result[T] (Phase 3)" +``` + +### Tasks 3.2 through 3.10: Migrate remaining Batch A sites + +For each remaining site in Batch A (lines L, L, ... L), repeat Task 3.1's structure: +- Step 1: Write failing test (with the site name + line number) +- Step 2: Run, verify FAIL +- Step 3: Migrate the site (extract `_result` helper + legacy wrapper) +- Step 4: Run test, verify PASS +- Step 5: Audit pre/post check +- Step 6: Commit (one per site) + +If the exact line numbers are unknown, run the audit to find them: +```bash +uv run python scripts/audit_exception_handling.py --src src/gui_2.py --json | python -c " +import json, sys +data = json.loads(sys.stdin.read()) +gui = [f for f in data['files'] if 'gui_2' in f.get('filename', '')][0] +for f in gui['findings']: + if f.get('category') == 'INTERNAL_BROAD_CATCH': + print(f\"L{f['line']}: {f.get('excerpt', '')[:80]}\") +" | head -10 +``` + +### Task 3.11: Phase 3 invariant test + checkpoint + +- [ ] **Step 1: Add Phase 3 invariant test** + +Append to `tests/test_gui_2_result.py`: + +```python +def test_phase_3_invariant_broad_catch_batch_a_count_dropped(): + """Phase 3 invariant: Batch A sites moved out of INTERNAL_BROAD_CATCH.""" + import subprocess, json + r = subprocess.run( + ["uv", "run", "python", "scripts/audit_exception_handling.py", "--src", "src/gui_2.py", "--json"], + capture_output=True, text=True, check=True, + ) + data = json.loads(r.stdout) + gui = [f for f in data["files"] if "gui_2" in f.get("filename", "")][0] + # Find the Batch A sites from Phase 1 inventory + batch_a_lines = [int(line) for line in ] + remaining_in_v = [ + f for f in gui["findings"] + if f.get("line") in batch_a_lines and f.get("category") in ("INTERNAL_BROAD_CATCH", "INTERNAL_SILENT_SWALLOW") + ] + assert not remaining_in_v, ( + f"Phase 3 Batch A sites still in V category: {[(f['line'], f['category']) for f in remaining_in_v]}" + ) +``` + +Replace `` with the actual list (e.g., `[123, 456, 789]`). + +- [ ] **Step 2: Run all Phase 3 tests** + +```bash +uv run python -m pytest tests/test_gui_2_result.py -v -k "phase_3 or l or l or ..." +``` + +Expected: all PASSED + +- [ ] **Step 3: Update state.toml Phase 3** + +```toml +phase_3 = { status = "completed", checkpointsha = "", name = "INTERNAL_BROAD_CATCH Batch A — render-loop sites (≤10)" } +``` + +- [ ] **Step 4: Commit** + +```bash +git add tests/test_gui_2_result.py conductor/tracks/result_migration_gui_2_20260619/state.toml +git commit -m "conductor(plan): mark Phase 3 complete (Batch A: ≤10 render-loop sites + invariant test)" +``` + +--- + +## Phase 4: INTERNAL_BROAD_CATCH Batch B — Modal/Dialog Sites (≤10 sites) + +**Focus:** The broad-catch sites inside modal functions (`render_approve_script_modal`, `render_patch_modal`, etc.). The migration pattern is identical to Phase 3 EXCEPT the legacy wrapper triggers `imgui.open_popup("Error: ...")` immediately on failure (Pattern 2 drain) instead of appending to `app._last_request_errors`. + +### Task 4.0: Phase 4 styleguide re-read + +- [ ] **Step 1: Re-read the Pattern 2 section (lines 396-407)** + +- [ ] **Step 2: Styleguide ack commit (empty)** + +```bash +git commit --allow-empty -m "chore: TIER-2 READ conductor/code_styleguides/error_handling.md Pattern 2 (GUI error display) before Phase 4" +``` + +### Tasks 4.1 through 4.N: Migrate Batch B sites + +For each Batch B site (use the Phase 1 inventory to find the lines): +- Step 1: Write failing test (with site name + line number) +- Step 2: Run, verify FAIL +- Step 3: Migrate — extract `_result` helper + legacy wrapper that triggers `imgui.open_popup`: + +```python +def __result(app: 'App', ...) -> Result[bool]: + try: + # ... original body ... + return Result(data=True) + except Exception as e: + return Result(data=False, errors=[ErrorInfo( + kind=ErrorKind.INTERNAL, message=str(e), + source="gui_2.__result", original=e, + )]) + +def _(app: 'App', ...): + result = __result(app, ...) + if not result.ok: + imgui.open_popup(f"Error: _") # Pattern 2 drain + return + # ... original success path ... +``` + +- Step 4: Run test, verify PASS +- Step 5: Audit pre/post check +- Step 6: Commit (one per site; message format: `refactor(gui_2): migrate L _ to Result[T] (Phase 4)`) + +### Task 4.N+1: Phase 4 invariant test + checkpoint + +```python +def test_phase_4_invariant_broad_catch_batch_b_count_dropped(): + """Phase 4 invariant: Batch B sites moved out of INTERNAL_BROAD_CATCH.""" + # Same pattern as Phase 3 invariant, with batch_b_lines list + batch_b_lines = + # ... (same as Phase 3 invariant) +``` + +Update state.toml: +```toml +phase_4 = { status = "completed", checkpointsha = "", name = "INTERNAL_BROAD_CATCH Batch B — modal/dialog sites (≤10)" } +``` + +--- + +## Phase 5: INTERNAL_BROAD_CATCH Batch C — Event Handler Sites (≤10 sites) + +**Focus:** The broad-catch sites inside event handlers (`_handle_approve_ask`, `_handle_save_anyway_click`, etc.). The legacy wrapper routes errors to `app._last_request_errors` (the per-event accumulator). + +Same structure as Phase 3, but the site categories are event handlers. Use the Phase 1 inventory for line numbers. + +### Task 5.0: Phase 5 styleguide re-read + ack commit + +### Tasks 5.1 through 5.N: Migrate Batch C sites + +For each Batch C site: +- Extract `_handle__result(app, ...) -> Result[bool]` helper +- Legacy wrapper appends to `app._last_request_errors` with `("_handle_", result.errors[0])` + +### Task 5.N+1: Phase 5 invariant test + checkpoint + +--- + +## Phase 6: Signal Handler Sites (≤5 sites) + +**Focus:** The INTERNAL_RETHROW sites in signal handlers (`_init_actions` + similar). Per Pattern 3 from `error_handling.md:409-419`, the drain is `sys.stderr.write(...) + sys.exit(1)`. + +### Task 6.0: Phase 6 styleguide re-read (Pattern 3 lines 409-419) + ack commit + +### Tasks 6.1 through 6.N: Migrate signal handler sites + +For each signal handler site, the `_result` helper itself is the drain: + +```python +def _install__result(app: 'App') -> Result[None]: + """Signal handler installer. On failure, drain via Pattern 3 (stderr + sys.exit).""" + try: + # ... install signal handler ... + return Result(data=None) + except Exception as e: + sys.stderr.write(f"FATAL: failed to install signal handler: {e}\n") + sys.exit(1) + # Note: no return after sys.exit; sys.exit raises SystemExit which propagates as drain +``` + +The unit test captures `sys.exit`: + +```python +def test_install__result_drains_via_sys_exit_on_failure(monkeypatch): + """Signal handler install failure triggers Pattern 3 drain (sys.exit).""" + from src import gui_2 + monkeypatch.setattr(gui_2.sys, "exit", lambda code: (_ for _ in ()).throw(SystemExit(code))) + # ... trigger the failure path ... + with pytest.raises(SystemExit): + gui_2._install__result(make_mock_app()) +``` + +### Task 6.N+1: Phase 6 invariant test + checkpoint + +--- + +## Phase 7: Worker / Background Sites (≤5 sites) + +**Focus:** The broad-catch sites in worker closures (callbacks invoked from `_io_pool`). Use `app._report_worker_error(op_name, result)` helper (added in sub-track 3 Phase 6 Group 6.5). + +### Task 7.0: Phase 7 styleguide re-read + ack commit + +### Tasks 7.1 through 7.N: Migrate worker sites + +For each worker site: +- Extract `_do__result(app, ...) -> Result[None]` helper +- Legacy wrapper calls `app._report_worker_error("_do_", result)` on failure + +The thread-safety invariant test: + +```python +def test_worker__thread_safe_under_concurrent_appends(): + """Worker error accumulator is thread-safe (sub-track 3 Phase 6 lock).""" + from src import gui_2 + import threading + app = make_mock_app_with_workers_lock() + errors = [] + def worker(i): + for _ in range(100): + result = gui_2._do__result(app, f"payload_{i}") + if not result.ok: + app._report_worker_error(f"_do__{i}", result) + threads = [threading.Thread(target=worker, args=(i,)) for i in range(10)] + for t in threads: t.start() + for t in threads: t.join() + assert len(app._worker_errors) == 1000 # all 1000 failures accumulated +``` + +### Task 7.N+1: Phase 7 invariant test + checkpoint + +--- + +## Phase 8: Property Setter / State Sites (≤5 sites) + +**Focus:** The broad-catch sites in property setters / state mutations. Use `_report_worker_error` per sub-track 3 Phase 6 Group 6.3 pattern. + +### Task 8.0: Phase 8 styleguide re-read + ack commit + +### Tasks 8.1 through 8.N: Migrate setter sites + +For each setter: +- Extract `_set__result(app, value) -> Result[None]` helper +- Legacy setter calls `app._report_worker_error("_set_", result)` on failure + +### Task 8.N+1: Phase 8 invariant test + checkpoint + +--- + +## Phase 9: Helper / Utility Sites (≤5 sites) + +**Focus:** Module-level helpers (`_check_auto_refresh_context_preview`, etc.). Each helper returns `Result[T]`; callers accumulate in the appropriate controller attribute. + +### Task 9.0: Phase 9 styleguide re-read + ack commit + +### Tasks 9.1 through 9.N: Migrate helper sites + +Same pattern as Phase 3 (render-loop Result[T] helpers). + +### Task 9.N+1: Phase 9 invariant test + checkpoint + +--- + +## Phase 10: INTERNAL_SILENT_SWALLOW Migrations (≤13 sites) + +**Focus:** The 13 logging-only except bodies. **Per the user's principle (2026-06-17), logging is NOT a drain.** Each site MUST be migrated to `Result[T]` propagation. No narrowing + logging; no pass after logging; no "intentional silent recovery." + +### Task 10.0: Phase 10 styleguide re-read + ack commit (CRITICAL — this is the sliming-prone phase) + +- [ ] **Step 1: Re-read the Broad-Except Distinction table (lines 520-540)** + +```bash +sed -n '520,540p' conductor/code_styleguides/error_handling.md +``` + +- [ ] **Step 2: Re-read the "What is NOT a drain point" section (lines 462-476)** + +```bash +sed -n '462,476p' conductor/code_styleguides/error_handling.md +``` + +- [ ] **Step 3: Styleguide ack commit (empty) — explicitly call out the sliming risk** + +```bash +git commit --allow-empty -m "chore: TIER-2 READ conductor/code_styleguides/error_handling.md lines 462-540 (logging NOT a drain) before Phase 10 — NO silent recovery, NO narrowing+logging" +``` + +### Tasks 10.1 through 10.N: Migrate silent-swallow sites + +For each silent-swallow site: +- Extract `__result(app, ...) -> Result[T]` helper +- The except body MUST return `Result(data=, errors=[ErrorInfo(original=e)])` +- NO `logging.error(...)` in the except body +- NO `sys.stderr.write(...)` in the except body +- NO `pass` in the except body +- The legacy wrapper appends to `app._last_request_errors` (or another controller attribute) + +**Audit post-check is critical:** verify the site is no longer in `INTERNAL_SILENT_SWALLOW` AND no new `INTERNAL_SILENT_SWALLOW` was introduced elsewhere. + +### Task 10.N+1: Phase 10 invariant test + checkpoint + +```python +def test_phase_10_invariant_silent_swallow_count_zero(): + """Phase 10 invariant: 0 INTERNAL_SILENT_SWALLOW sites in src/gui_2.py.""" + import subprocess, json + r = subprocess.run( + ["uv", "run", "python", "scripts/audit_exception_handling.py", "--src", "src/gui_2.py", "--json"], + capture_output=True, text=True, check=True, + ) + data = json.loads(r.stdout) + gui = [f for f in data["files"] if "gui_2" in f.get("filename", "")][0] + silent = [f for f in gui["findings"] if f.get("category") == "INTERNAL_SILENT_SWALLOW"] + assert not silent, f"Expected 0 INTERNAL_SILENT_SWALLOW sites, found {len(silent)}: {[f['line'] for f in silent]}" +``` + +--- + +## Phase 11: INTERNAL_RETHROW Classification (≤2 sites) + +**Focus:** The 2 INTERNAL_RETHROW sites. Classify per Pattern 1/2/3 from `error_handling.md:625-690`. If a site does not fit any pattern, migrate to `Result[T]` (NOT classified as "suspicious"). + +### Task 11.0: Phase 11 styleguide re-read (Re-Raise Patterns lines 625-690) + ack commit + +### Tasks 11.1 through 11.N: Classify each INTERNAL_RETHROW site + +For each site: +- Read the site code +- Determine which of the 3 patterns it fits: + - Pattern 1: catch + convert + raise as different type (compliant if convert is meaningful) + - Pattern 2: catch + log + re-raise (compliant if log provides value beyond re-raise) + - Pattern 3: catch + cleanup + re-raise via try/finally (compliant; canonical cleanup) +- If compliant: add a comment explaining which pattern it follows; no code change needed +- If NOT compliant: migrate to Result[T] using the standard pattern + +### Task 11.N+1: Phase 11 invariant test + checkpoint + +--- + +## Phase 12: UNCLEAR Classification (≤2 sites) + +**Focus:** The 2 UNCLEAR sites. Read each, classify compliant (with comment) or migrate. + +### Task 12.0: Phase 12 styleguide re-read + ack commit + +### Tasks 12.1 through 12.N: Classify each UNCLEAR site + +For each site: +- Read the site code AND 5 lines of context around it +- Determine if it's truly UNCLEAR or if the Phase 7 heuristic misclassified it +- If the heuristic is wrong: report to user (Tier 1); do NOT change the heuristic in this track +- If the site is genuinely compliant: add a comment explaining why +- If the site needs migration: use the standard migration pattern + +### Task 12.N+1: Phase 12 invariant test + checkpoint + +```python +def test_phase_12_invariant_unclear_count_zero(): + """Phase 12 invariant: 0 UNCLEAR sites in src/gui_2.py.""" + # (Same pattern as Phase 10 invariant, but for UNCLEAR) +``` + +--- + +## Phase 13: Audit Gate + End-of-Track Report (5 tasks) + +**Focus:** Verify all gates, run the full batched suite, write the report, mark the track complete. + +### Task 13.1: Run the strict audit gate + +- [ ] **Step 1: Run `uv run python scripts/audit_exception_handling.py --src src/gui_2.py --strict`** + +```bash +uv run python scripts/audit_exception_handling.py --src src/gui_2.py --strict +``` + +Expected: exit 0; no violations + +- [ ] **Step 2: If exit non-zero, identify the failing sites and report to user** + +```bash +uv run python scripts/audit_exception_handling.py --src src/gui_2.py --strict 2>&1 | grep -E "VIOLATION|src/gui_2.py" +``` + +### Task 13.2: Run the unit tests + +- [ ] **Step 1: Run `tests/test_gui_2_result.py`** + +```bash +uv run python -m pytest tests/test_gui_2_result.py -v +``` + +Expected: all PASSED (≥55 tests) + +### Task 13.3: Run the 11-tier batched suite + +- [ ] **Step 1: Run the fixed batched script** + +```bash +uv run python scripts/run_tests_batched.py +``` + +Expected: 11/11 tiers PASS + +- [ ] **Step 2: If any tier fails, save the log to `tests/artifacts/PHASE13_TEST_RUN_.log` and report** + +### Task 13.4: Write the end-of-track report + +**Files:** +- Create: `docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md` + +- [ ] **Step 1: Write the report (template below)** + +```markdown +# Track Completion: Result Migration — Sub-Track 4 (gui_2.py) + +**Track ID:** `result_migration_gui_2_20260619` +**Date:** +**Status:** SHIPPED + +## 1. Header / Scope Summary + +<1-2 sentence summary> + +## 2. Phase-by-Phase Summary + +<13 sections, one per phase, with audit count delta> + +## 3. Audit Results (Pre vs Post) + +| Category | Pre-Phase-0 | Post-Phase-13 | +|---|---|---| +| INTERNAL_BROAD_CATCH | 38 | 0 | +| INTERNAL_SILENT_SWALLOW | 13 | 0 | +| INTERNAL_RETHROW | 2 | 0 | +| UNCLEAR | 2 | 0 | +| INTERNAL_COMPLIANT | 12 | | + +## 4. Last 3 Failures Encountered + +<1-2 sentences per failure> + +## 5. Files Modified + +| Path | Lines | Description | +|---|---|---| + +## 6. Git State + + + +## 7. Recommendation + + + +## 8. Post-Completion Fixes (if any) + + +``` + +- [ ] **Step 2: Commit the report** + +```bash +git add docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md +git commit -m "docs(reports): TRACK_COMPLETION_result_migration_gui_2_20260619 (13 phases complete)" +``` + +### Task 13.5: Final checkpoint + tracks.md update + umbrella count + +- [ ] **Step 1: Phase 13 checkpoint commit** + +```bash +git commit --allow-empty -m "conductor(checkpoint): Phase 13 complete — sub-track 4 SHIPPED" +``` + +- [ ] **Step 2: Update `conductor/tracks.md` row to "shipped 2026-06-XX"** + +- [ ] **Step 3: Update umbrella spec count** + +```bash +# Edit conductor/tracks/result_migration_20260616/spec.md +# Update the per-sub-track count: sub-track 4 = 42 migration sites + 6 infra = 48 +``` + +- [ ] **Step 4: Final commit** + +```bash +git add conductor/tracks.md conductor/tracks/result_migration_20260616/spec.md conductor/tracks/result_migration_gui_2_20260619/state.toml conductor/tracks/result_migration_gui_2_20260619/metadata.json +git commit -m "conductor(plan): sub-track 4 SHIPPED — tracks.md updated, umbrella count updated" +``` + +--- + +## Summary + +**13 phases, ~60+ atomic commits, 42 migration sites + 6 infra + 55+ tests + 1 report.** + +| Dimension | Count | +|---|---| +| Source files modified | 1 (`src/gui_2.py`) | +| Test files created | 1 (`tests/test_gui_2_result.py`) | +| Migration sites | 42 (38 V + 2 S + 2 UNCLEAR) | +| Infra sites | 6 (3 new render functions + 3 helper integrations) | +| Tests | ≥55 (42 site + 13 invariant) | +| Phases | 13 | +| Atomic commits | ≥60 | + +--- + +## Self-Review + +**1. Spec coverage:** All 14 VCs in spec.md §8 are covered by tasks in this plan. VC-1 (audit --strict) is Task 13.1. VC-2 (0 INTERNAL_BROAD_CATCH) is Tasks 3-5 + Phase 3-5 invariants. VC-3 (0 INTERNAL_SILENT_SWALLOW) is Task 10 + Phase 10 invariant. VC-4 (0 UNCLEAR) is Task 12 + Phase 12 invariant. VC-5 (0 INTERNAL_RETHROW) is Task 11 + Phase 11 invariant. VC-6 (3 new render functions) is Tasks 2.1-2.3. VC-7 (≥55 tests) is the per-phase test additions. VC-8 (11-tier batched) is Task 13.3. VC-9 (per-phase audit gates) is the per-phase invariant tests. VC-10 (styleguide ack commits) is the per-phase Task 0 commit. VC-11 (≥60 commits) is the per-site commits. VC-12 (report) is Task 13.4. VC-13 (tracks.md) is Task 13.5. VC-14 (umbrella count) is Task 13.5. + +**2. Placeholder scan:** No "TBD", "TODO", "implement later", "fill in details" in this plan. All migration patterns show concrete code. All tasks show concrete commands. + +**3. Type consistency:** `Result[bool]` / `Result[None]` / `Result[T]` used consistently across all migration tasks. `app._last_request_errors`, `app._worker_errors`, `app._report_worker_error` referenced consistently per sub-track 3 Phase 6 contract. + +**4. Anti-sliming protocol:** Enforced via (a) styleguide re-read at start of every phase, (b) per-site audit pre/post check, (c) per-phase invariant test, (d) per-file atomic commits, (e) explicit instruction in Phase 10 (the sliming-prone phase) that narrowing+logging is forbidden. + +--- + +--- + +--- \ No newline at end of file diff --git a/conductor/tracks/result_migration_gui_2_20260619/spec.md b/conductor/tracks/result_migration_gui_2_20260619/spec.md new file mode 100644 index 00000000..fcdb1a20 --- /dev/null +++ b/conductor/tracks/result_migration_gui_2_20260619/spec.md @@ -0,0 +1,452 @@ +# Track Specification: Result Migration — Sub-Track 4 (gui_2.py) + +**Track ID:** `result_migration_gui_2_20260619` +**Status:** Active (spec approved 2026-06-19) +**Priority:** A (completes the data-oriented error handling convention for the largest source file) +**Owner:** Tier 2 Tech Lead +**Type:** refactor (13 phases; anti-sliming protocol enforced per phase) +**Scope:** 54 sites across 1 source file (`src/gui_2.py`, 260KB / 7282 lines) + 1 new test file + 3 new render functions +**Parent tracks:** `result_migration_20260616` (umbrella), `result_migration_app_controller_20260618` (sub-track 3, SHIPPED 2026-06-19 with Phase 7), `result_migration_small_files_20260617` (sub-track 2, SHIPPED 2026-06-18), `result_migration_review_pass_20260617` (sub-track 1, SHIPPED 2026-06-17), `data_oriented_error_handling_20260606` (convention ancestor, SHIPPED 2026-06-12) + +> **Note on effort estimates:** per Tier 1 rules (see `conductor/workflow.md` §"Tier 1 Track Initialization Rules"), this spec does NOT include day estimates. Effort is measured by scope (N files, M sites, N phases). The user / Tier 2 agent decides the actual pacing. + +--- + +## 0. TL;DR + +This is sub-track 4 of the 5-sub-track `result_migration_20260616` umbrella. It migrates `src/gui_2.py` (the largest source file in the codebase; the immediate-mode ImGui rendering layer) to the data-oriented `Result[T]` convention. The umbrella originally estimated 55 sites at T-shirt XL; the current audit shows 54 sites (38 V + 2 S + 2 UNCLEAR + 12 C) — the UNCLEAR count dropped 14→2 after sub-track 1's review pass and sub-track 3 Phase 7's heuristic tightening reclassified them. + +**Why 13 phases (not the umbrella's "1-2 phases"):** per the user's directive (2026-06-19), this track uses an **anti-sliming protocol** with extra phases to give Tier 2 well-defined, narrow scope per phase. The previous sub-tracks slimed when scope felt tight (sub-track 2 Phase 10 slimed 21 of 26 sites via 5 laundering heuristics; sub-track 3 Phase 3 slimed 8 sites via logging.debug bodies). The 13-phase structure caps each phase at ~10 sites with explicit per-phase audit gates. + +**What this track consumes from sub-track 3:** 8 controller state attributes added by Phase 6 (`_last_request_errors`, `_worker_errors` + lock, `_startup_timeline_errors`, `_signal_handler_error`, `_inject_preview_error`, `_mcp_config_parse_error`, `_save_project_error`, `_model_fetch_errors`). These are the **data plane**; sub-track 4 adds the **drain plane** (3 new render functions) and migrates the 42 migration-target sites to feed their errors into the data plane. + +**What this track enables:** sub-track 5 (`result_migration_baseline_cleanup`) which closes the 77 violations in the 3 refactored baseline files (mcp_client.py, ai_client.py, rag_engine.py). Once gui_2.py is migrated, the data-oriented convention is **fully applied** to all 65 src/ files except the baseline. + +--- + +## 1. Overview + +### 1.1 The State Before This Track (as of 2026-06-19) + +Per `uv run python scripts/audit_exception_handling.py --src src/gui_2.py`: + +``` +src/gui_2.py (V=38, S=2, ?=2, C=12, total=54) + INTERNAL_BROAD_CATCH 25 + INTERNAL_SILENT_SWALLOW 13 + UNCLEAR 2 + INTERNAL_RETHROW 2 + INTERNAL_COMPLIANT 12 +``` + +**Migration target: 38 V + 2 S + 2 UNCLEAR = 42 sites.** The 12 INTERNAL_COMPLIANT sites stay as-is. The 38 broad-catches are the bulk; the 13 silent-swallows are the sliming-prone ones. + +### 1.2 The Goal + +Migrate all 42 migration-target sites to the data-oriented convention, using the 8 controller state attributes as the data plane and adding 3 new render functions as the drain plane. After this track ships: + +- 0 `INTERNAL_SILENT_SWALLOW` in `src/gui_2.py` (every logging-only except body is replaced with Result propagation). +- 0 `INTERNAL_BROAD_CATCH` in `src/gui_2.py` (every `except Exception` is converted to a `_result` helper + caller checks `.ok`). +- 0 `UNCLEAR` in `src/gui_2.py` (the 2 remaining sites are classified compliant or migrated). +- 0 `INTERNAL_RETHROW` (the 2 re-raise sites are classified as Pattern 1/2/3 from `error_handling.md` or migrated). +- `audit_exception_handling.py --src src/gui_2.py --strict` exits 0. +- 11-tier batched test suite passes with no new regressions. + +### 1.3 The 13-Phase Structure (Anti-Sliming Protocol) + +The umbrella estimated "1-2 phases" for sub-track 4. The user's directive (2026-06-19) is to use **extra phases** so Tier 2 has narrow, well-defined scope per phase. **No phase has more than 10 migration sites.** Every phase has a per-phase audit gate. Every phase starts with a styleguide re-read. + +| Phase | Sites | Tests | Audit gate | +|---|---|---|---| +| 0. Setup + styleguide re-read | 0 | 0 | n/a | +| 1. Site inventory + classification | 0 | 0 | inventory doc complete | +| 2. Drain plane wiring (3 new render functions) | 0 | 3 | render functions render without crash | +| 3. INTERNAL_BROAD_CATCH batch A (render-loop) | ≤10 | ≤10 | INTERNAL_BROAD_CATCH count drops by batch A count | +| 4. INTERNAL_BROAD_CATCH batch B (modal/dialog) | ≤10 | ≤10 | count drops by batch B | +| 5. INTERNAL_BROAD_CATCH batch C (event handlers) | ≤10 | ≤10 | count drops by batch C | +| 6. Signal handler sites | ≤5 | ≤5 | drain verified (Pattern 3 from styleguide) | +| 7. Worker / background sites | ≤5 | ≤5 | thread-safety verified | +| 8. Property setter / state sites | ≤5 | ≤5 | side-effect chain verified | +| 9. Helper / utility sites | ≤5 | ≤5 | stateless verified | +| 10. INTERNAL_SILENT_SWALLOW migrations | ≤13 | ≤13 | count drops to 0 | +| 11. INTERNAL_RETHROW classification | ≤2 | ≤2 | all classified per Pattern 1/2/3 | +| 12. UNCLEAR classification | ≤2 | ≤2 | count drops to 0 | +| 13. Audit gate + end-of-track report | 0 | 1 invariant test | `--strict` exits 0; 11/11 tiers PASS | + +**Total: ~42 migration sites + 6 infra sites + 55+ tests + 1 report, in 13 phases.** + +--- + +## 2. Current State Audit (as of commit `f2fef7d2`) + +### 2.1 Already Implemented (DO NOT re-implement) + +These are the conventions and infrastructure already in place. Sub-track 4 MUST use them; sub-track 4 MUST NOT recreate them. + +| Item | Location | What it does | +|---|---|---| +| `Result[T]` dataclass | `src/result_types.py:91-105` | The data-oriented container | +| `ErrorInfo` dataclass + `ErrorKind` enum | `src/result_types.py:117-130` | The canonical error type | +| `audit_exception_handling.py --strict` gate | `scripts/audit_exception_handling.py:1-1100` | The CI gate | +| `_is_fastapi_handler` heuristic (Phase 7 tightening) | `scripts/audit_exception_handling.py:318-460` | BOUNDARY_FASTAPI only when except body raises HTTPException or returns Result | +| `_except_body_drains_via_http_exception_or_result` | `scripts/audit_exception_handling.py:333` | Drain point detection | +| `_except_body_has_logging` | `scripts/audit_exception_handling.py:365` | Logging body detection | +| 5 regression-guard tests | `tests/test_audit_heuristics.py` | Lock the heuristic | +| `_last_request_errors` attribute | `src/app_controller.py:862` | Per-request error accumulator | +| `_worker_errors` + `_worker_errors_lock` | `src/app_controller.py` (Phase 6 Group 6.5) | Worker error accumulator | +| `_startup_timeline_errors` | `src/app_controller.py` (Phase 6 Group 6.2) | Startup error accumulator | +| `_signal_handler_error` | `src/app_controller.py` (Phase 6 Group 6.1) | Signal handler error | +| `_inject_preview_error` | `src/app_controller.py` (Phase 6 Group 6.3) | Inject preview error | +| `_mcp_config_parse_error` | `src/app_controller.py` (Phase 6 Group 6.3) | MCP config parse error | +| `_save_project_error` | `src/app_controller.py` (Phase 6 Group 6.3) | Project save error | +| `_model_fetch_errors` | `src/app_controller.py` (Phase 6 Group 6.4) | Per-provider model fetch errors | +| `_report_worker_error` helper | `src/app_controller.py` (Phase 6 Group 6.5) | Worker error drain | +| `_rag_search_result` helper | `src/app_controller.py:3475` | RAG search returns Result | +| `_symbol_resolution_result` helper | `src/app_controller.py` (Phase 6 Group 6.6) | Symbol resolution returns Result | +| `_execute_gui_task_result` helper | `src/app_controller.py` (Phase 6 Group 6.6) | GUI task returns Result | +| `error_handling.md` Drain Points section | `conductor/code_styleguides/error_handling.md:356-516` | The 5 drain patterns + heuristic D | +| `error_handling.md` Broad-Except table | `conductor/code_styleguides/error_handling.md:520-540` | `narrow + log = INTERNAL_SILENT_SWALLOW` (the rule) | + +### 2.2 Gaps to Fill (This Track's Scope) + +The umbrella originally estimated 55 sites; the current audit shows 54. The migration target is **42 sites** (38 V + 2 S + 2 UNCLEAR). Plus 6 infra sites for the drain plane. + +**Per-file breakdown (gui_2.py only):** +- 25 INTERNAL_BROAD_CATCH (the bulk; render-loop + modal + event-handler batches) +- 13 INTERNAL_SILENT_SWALLOW (logging-only except bodies — the sliming-prone ones per the user's principle) +- 2 UNCLEAR (need manual classification in Phase 12) +- 2 INTERNAL_RETHROW (need Pattern 1/2/3 classification in Phase 11) + +**Infrastructure gaps:** +- 3 new render functions for the drain plane (error modal consumer, worker error indicator, last-request errors modal) +- 1 new test file (`tests/test_gui_2_result.py`) with ≥55 tests +- 1 new invariant test per phase (13 total) to lock per-phase progress + +--- + +## 3. Goals + +### 3.1 Primary Goal + +Migrate all 42 migration-target sites in `src/gui_2.py` to the data-oriented `Result[T]` convention, with each site's error either accumulating in one of the 8 controller state attributes (the data plane) OR triggering a drain modal immediately. + +### 3.2 Secondary Goals + +1. **Establish the drain plane** in gui_2.py: 3 new render functions (`render_error_tint_modal` consumer, `_render_worker_error_indicator`, `_render_last_request_errors_modal`) that read from the controller's data plane. +2. **Verify per-phase audit gates**: each phase's audit command shows the expected count drop. +3. **No new regressions**: 11/11 batched test tiers PASS at track end. +4. **Per-site unit tests**: 1 test per migrated site (≥42) + 1 invariant test per phase (13). +5. **No sliming**: per-phase protocol with styleguide re-read + audit gate. + +### 3.3 Non-Goals + +- Adding new error sites (this track migrates EXISTING `try/except`, not adds new ones). +- Changing the audit heuristic (sub-track 3 Phase 7 already tightened it; this track uses the existing heuristic). +- Migrating `tests/` files (the `public_api_migration_and_ui_polish_20260615` track already migrated 22 test files; the remaining tests are out of scope). +- Migrating `src/gui_2.py:1349` (the +1 site from sub-track 1's review pass) — that's already correctly classified by the Phase 7 heuristic; verify in Phase 12. +- Sub-track 5 (baseline cleanup) — separate track after this one ships. + +--- + +## 4. Functional Requirements + +### 4.1 Drain Plane Infrastructure (Phase 2) + +**FR-DP-1** `src/gui_2.py` adds a new render function `render_controller_error_modal(app: App)` that: +- Reads `app._last_request_errors`, `app._worker_errors`, `app._startup_timeline_errors`, `app._signal_handler_error`, `app._inject_preview_error`, `app._mcp_config_parse_error`, `app._save_project_error`, `app._model_fetch_errors`. +- For each non-empty attribute, opens an `imgui.open_popup(f"Error: {attr_name}")` with the errors displayed. +- Returns nothing (drain point per `error_handling.md:396-407` Pattern 2). + +**FR-DP-2** `src/gui_2.py` adds `_render_worker_error_indicator(app: App)` that: +- Renders a small status-bar widget (e.g., `[!] 3 worker errors`). +- Click opens `render_controller_error_modal`. +- Visible only when `app._worker_errors` is non-empty. + +**FR-DP-3** `src/gui_2.py` adds `_render_last_request_errors_modal(app: App)` that: +- Reads `app._last_request_errors` and shows per-request errors. +- Called from `_handle_generate_send` after each AI request completes. +- Modal opens only if errors accumulated during the request. + +### 4.2 INTERNAL_BROAD_CATCH Migrations (Phases 3, 4, 5) + +**FR-BC-1** For each of the 25 INTERNAL_BROAD_CATCH sites, the migration follows this pattern: +1. Extract a `_render__result(app, ...)` helper that returns `Result[T]` (T = the data the caller needs: `bool`, `dict`, `str`, `None`, etc.). +2. The helper's except body returns `Result(data=, errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="gui_2.", original=e)])`. +3. The caller checks `.ok` and `.errors`. On error, the caller either accumulates in the appropriate controller attribute OR triggers `render_controller_error_modal` immediately. + +**FR-BC-2** Batch A (Phase 3, render-loop sites): the ~10 broad-catch sites inside `render_*` functions called every frame. Failures here cannot crash the render loop; the migration must guarantee `try/finally` cleanup or `Result` propagation that doesn't propagate to the outer render frame. + +**FR-BC-3** Batch B (Phase 4, modal/dialog sites): the ~8 broad-catch sites inside modal functions (e.g., `render_approve_script_modal`, `render_patch_modal`). Failures here CAN trigger `imgui.open_popup` to show the error inline (Pattern 2). + +**FR-BC-4** Batch C (Phase 5, event handler sites): the ~7 broad-catch sites inside event handlers (e.g., `_handle_approve_ask`, `_handle_save_anyway_click`). Failures here accumulate in `app._last_request_errors` or a similar per-event accumulator. + +### 4.3 Signal Handler Sites (Phase 6) + +**FR-SH-1** The 2 INTERNAL_RETHROW sites in signal handlers (`_init_actions` + similar) are migrated to Pattern 3 from `error_handling.md:409-419`: `sys.stderr.write(...) + sys.exit(1)` IS the drain. The except body MUST NOT swallow the error; it MUST terminate the app or trigger an intentional drain. + +### 4.4 Worker / Background Sites (Phase 7) + +**FR-WB-1** The ~5 broad-catch sites in worker closures (callbacks invoked from `_io_pool`) use `app._report_worker_error(op_name, result)` helper (added in sub-track 3 Phase 6 Group 6.5) to drain errors to `app._worker_errors`. Thread-safety: `app._worker_errors_lock` is acquired on every append. + +### 4.5 Property Setter / State Sites (Phase 8) + +**FR-PS-1** The ~3 broad-catch sites in property setters / state mutations: each setter extracts a `_set__result(app, value) -> Result[None]` helper; the legacy setter calls `_report_worker_error` on failure (per sub-track 3 Phase 6 Group 6.3 pattern for `_save_active_project`). + +### 4.6 Helper / Utility Sites (Phase 9) + +**FR-HU-1** The ~3 broad-catch sites in module-level helpers (e.g., `_check_auto_refresh_context_preview`): each helper returns `Result[T]`; callers check `.ok` and accumulate in the appropriate controller attribute. + +### 4.7 INTERNAL_SILENT_SWALLOW Migrations (Phase 10) + +**FR-SS-1** The 13 INTERNAL_SILENT_SWALLOW sites (logging-only except bodies) are the sliming-prone ones. Per the user's principle (2026-06-17) and `error_handling.md:530`, **logging is NOT a drain**. Each site MUST be migrated to `Result[T]` propagation. No narrowing + logging; no pass after logging; no "intentional silent recovery." + +### 4.8 INTERNAL_RETHROW Classification (Phase 11) + +**FR-RT-1** The 2 INTERNAL_RETHROW sites are classified per the 3 legitimate patterns from `error_handling.md:625-690`: +- Pattern 1: Catch + convert + raise as different type (compliant if convert is meaningful). +- Pattern 2: Catch + log + re-raise (compliant if log provides value beyond re-raise). +- Pattern 3: Catch + cleanup + re-raise via `try/finally` (compliant; canonical cleanup pattern). + +If a site does not fit any pattern, it is migrated to Result[T] (NOT classified as "suspicious" — sliming). + +### 4.9 UNCLEAR Classification (Phase 12) + +**FR-UC-1** The 2 UNCLEAR sites are read individually; each is classified compliant (with a comment explaining why) or migrated. The audit script's heuristic should already classify them; verify the classification is correct per the Phase 7 heuristic (`_is_fastapi_handler` + drain detection). + +### 4.10 Tests (per phase) + +**FR-T-1** Every migration site has a unit test in `tests/test_gui_2_result.py` that verifies: +- The helper returns `Result[T]` with `data=` on success. +- The helper returns `Result[T]` with `errors=[ErrorInfo(...)]` on failure (mock the inner call to raise). +- The caller checks `.ok` and either accumulates or triggers a drain. + +**FR-T-2** Every phase has 1 invariant test in `tests/test_gui_2_result.py` named `test_phase_N__invariant` that verifies the per-phase audit gate (e.g., `test_phase_3_invariant_broad_catch_batch_a_dropped`). + +--- + +## 5. Non-Functional Requirements + +**NFR-1** `audit_exception_handling.py --src src/gui_2.py --strict` exits 0 at end of Phase 13. +**NFR-2** 11-tier batched test suite passes with no new regressions at end of Phase 13. +**NFR-3** All new code uses 1-space indentation per `conductor/product-guidelines.md` "AI-Optimized Compact Style." +**NFR-4** Per-file atomic commits (1 site = 1 commit) per `conductor/workflow.md`. +**NFR-5** Every migration phase's commit message includes "TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase N" per the AI Agent Checklist. +**NFR-6** No diagnostic noise in production code (no `[X_DIAG] sys.stderr.write(...)` lines left uncommitted). +**NFR-7** No `@pytest.mark.skip` markers added (per `conductor/workflow.md` Skip-Marker Policy). +**NFR-8** No new `Optional[T]` return types (the convention's `Result[T]` ban in refactored files). +**NFR-9** No new `try/except` sites added that have logging-only except bodies (the sliming pattern). +**NFR-10** Hot reload is NOT used for verification (per `live_gui_test_fixes_20260618` findings; hot reload is fragile). Use live_gui tests instead. + +--- + +## 6. Architecture Reference + +- `conductor/code_styleguides/error_handling.md` — the canonical convention. **READ END-TO-END** at start of each phase. +- `conductor/code_styleguides/error_handling.md:356-516` — Drain Points section (5 patterns + Heuristic D). +- `conductor/code_styleguides/error_handling.md:462-476` — "What is NOT a drain point" (logging is NOT a drain). +- `conductor/code_styleguides/error_handling.md:520-540` — Broad-Except Distinction table. +- `conductor/code_styleguides/error_handling.md:809-940` — AI Agent Checklist (5 MUST-DO + 7 MUST-NOT-DO). +- `conductor/tracks/result_migration_app_controller_20260618/spec.md` §12-§21 — sub-track 3's Phase 6 addendum (the pattern this track mirrors). +- `conductor/tracks/result_migration_small_files_20260617/spec.md` — sub-track 2's sliming precedent (Phase 10→11 redo). +- `conductor/tracks/result_migration_review_pass_20260617/spec.md` — sub-track 1's UNCLEAR classification pattern. +- `docs/guide_gui_2.md` — gui_2.py architecture guide (the App class lifecycle, render function delegation pattern). +- `docs/guide_app_controller.md` — AppController + state attributes (the data plane this track consumes). +- `scripts/audit_exception_handling.py:318-460` — the Phase 7 audit heuristic (5 regression-guard tests in `tests/test_audit_heuristics.py` lock the behavior). + +--- + +## 7. Per-Phase Migration Strategy + +Each phase follows the **anti-sliming protocol**: + +1. **Pre-phase styleguide re-read** (commits 1 of the phase): Tier 2 reads `error_handling.md` end-to-end. Commit message: "TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end before Phase N." +2. **Site inventory check** (only Phase 1): Tier 2 walks the audit's JSON output for the phase's sites, classifies each (current code, target migration, drain point), writes the classification to `tests/artifacts/PHASE_SITE_INVENTORY.md`. +3. **Red** (1 commit per site): Write the unit test in `tests/test_gui_2_result.py`. Run test — must FAIL. +4. **Audit pre-check** (no commit): `uv run python scripts/audit_exception_handling.py --src src/gui_2.py 2>&1 | grep ""` to confirm the site's category BEFORE migration. +5. **Green** (1 commit per site): Migrate the site. Use a `_result` helper + the appropriate controller attribute OR a drain modal. Run test — must PASS. +6. **Audit post-check** (no commit): Same command. Confirm the site moved out of the violation category. +7. **Phase invariant test** (1 commit at end of phase): `test_phase_N__invariant` verifies the per-phase count drop. +8. **Per-file atomic commit** per `workflow.md`. + +If a site "resists migration" in any phase, Tier 2 MUST report (per `workflow.md` "Per-Task Decision Protocol") — not invent a heuristic. The user (Tier 1) decides whether to fix forward or defer. + +### 7.1 Phase 0: Setup + Styleguide Re-Read +**Tasks:** +- Create track directory (already exists: `conductor/tracks/result_migration_gui_2_20260619/`) +- Update `conductor/tracks.md` with new row +- Tier 2 reads `conductor/code_styleguides/error_handling.md` end-to-end +- Acknowledge in commit message + +**Verify:** No code; verification is the commit message. + +### 7.2 Phase 1: Site Inventory + Classification +**Tasks:** +- Run `uv run python scripts/audit_exception_handling.py --src src/gui_2.py --json > tests/artifacts/PHASE1_AUDIT.json` +- Walk every finding; for the 42 migration-target sites, record: line, category, current code, target migration pattern, drain point +- Write `tests/artifacts/PHASE1_SITE_INVENTORY.md` (markdown table) + +**Verify:** The inventory doc has 42 rows + is committed. + +### 7.3 Phase 2: Drain Plane Wiring +**Tasks:** +- Add `render_controller_error_modal(app: App)` (read all 8 controller attributes; drain to imgui popup) +- Add `_render_worker_error_indicator(app: App)` (status-bar widget) +- Add `_render_last_request_errors_modal(app: App)` (per-request error modal) +- Wire each render function into the appropriate existing call sites +- 3 unit tests verifying each render function renders without crash when attributes are populated / empty + +**Verify:** The 3 render functions exist; the 3 tests pass; `audit --strict` still passes (no new violations introduced). + +### 7.4 Phase 3: INTERNAL_BROAD_CATCH Batch A (Render-Loop) +**Scope:** The ~10 broad-catch sites in render-loop functions (sites called every frame from `render_main_interface`). + +**Migration pattern:** +- Each `_render_` function extracts a `_render__result(app, ...) -> Result[bool]` helper. +- The helper's except body returns `Result(data=False, errors=[ErrorInfo(...)])`. +- The caller checks `.ok`; if False, the helper's caller logs to `app._last_request_errors` (or a render-loop-specific accumulator). + +**Verify:** `--strict` exits 0 for the batch A sites; 10 unit tests pass; render-loop output is identical for success paths. + +### 7.5 Phase 4: INTERNAL_BROAD_CATCH Batch B (Modal/Dialog) +**Scope:** The ~8 broad-catch sites in modal functions (`render_approve_script_modal`, `render_patch_modal`, etc.). + +**Migration pattern:** +- Each modal extracts a `__result(app, ...) -> Result[bool]` helper. +- On error, the caller triggers `render_controller_error_modal` immediately (Pattern 2 drain). + +**Verify:** 8 unit tests pass; modal error messages render correctly when triggered. + +### 7.6 Phase 5: INTERNAL_BROAD_CATCH Batch C (Event Handlers) +**Scope:** The ~7 broad-catch sites in event handlers (`_handle_approve_ask`, etc.). + +**Migration pattern:** +- Each handler extracts a `_handle__result(app, ...) -> Result[bool]` helper. +- On error, the caller accumulates in `app._last_request_errors` (the data plane). + +**Verify:** 7 unit tests pass; the per-event accumulator is populated correctly. + +### 7.7 Phase 6: Signal Handler Sites +**Scope:** The 2 INTERNAL_RETHROW sites in `_init_actions` + similar. + +**Migration pattern:** Pattern 3 from styleguide: `sys.stderr.write(...) + sys.exit(1)` is the drain. The migration extracts a `_install__result() -> Result[None]` helper; on failure, the helper writes to stderr + calls `sys.exit(1)`. + +**Verify:** 2 unit tests pass; app termination is triggered correctly (use a test fixture that captures `sys.exit`). + +### 7.8 Phase 7: Worker / Background Sites +**Scope:** The ~5 broad-catch sites in worker closures. + +**Migration pattern:** Use `app._report_worker_error(op_name, result)` helper (added in sub-track 3 Phase 6 Group 6.5). Thread-safety: `app._worker_errors_lock` is acquired on every append. + +**Verify:** 5 unit tests pass; thread-safety is verified with a multi-thread test that appends concurrently. + +### 7.9 Phase 8: Property Setter / State Sites +**Scope:** The ~3 broad-catch sites in property setters / state mutations. + +**Migration pattern:** Per sub-track 3 Phase 6 Group 6.3 pattern: extract `_set__result(app, value) -> Result[None]`; legacy setter calls `_report_worker_error` on failure. + +**Verify:** 3 unit tests pass. + +### 7.10 Phase 9: Helper / Utility Sites +**Scope:** The ~3 broad-catch sites in module-level helpers. + +**Migration pattern:** Each helper returns `Result[T]`; callers check `.ok` and accumulate in the appropriate controller attribute. + +**Verify:** 3 unit tests pass. + +### 7.11 Phase 10: INTERNAL_SILENT_SWALLOW Migrations +**Scope:** The 13 INTERNAL_SILENT_SWALLOW sites (logging-only except bodies). + +**Migration pattern:** Per the user's principle (logging is NOT a drain). Each site extracts a `__result(app, ...) -> Result[T]` helper; the except body returns `Result(data=, errors=[ErrorInfo(original=e)])`. No narrowing + logging; no pass after logging. + +**Verify:** 13 unit tests pass; `--strict` audit shows 0 INTERNAL_SILENT_SWALLOW. + +### 7.12 Phase 11: INTERNAL_RETHROW Classification +**Scope:** The 2 INTERNAL_RETHROW sites. + +**Migration pattern:** Classify per Pattern 1/2/3 from `error_handling.md:625-690`. If a site does not fit any pattern, migrate to `Result[T]` (NOT classified as "suspicious"). + +**Verify:** 2 unit tests pass; the 2 sites are either classified compliant or migrated to Result. + +### 7.13 Phase 12: UNCLEAR Classification +**Scope:** The 2 UNCLEAR sites. + +**Migration pattern:** Read each site individually; classify compliant (with comment) or migrate. Verify the Phase 7 heuristic classifies correctly. + +**Verify:** 2 unit tests pass; `--strict` audit shows 0 UNCLEAR. + +### 7.14 Phase 13: Audit Gate + End-of-Track Report +**Tasks:** +- Run `uv run python scripts/audit_exception_handling.py --src src/gui_2.py --strict` — verify exit 0 +- Run `uv run python scripts/run_tests_batched.py` — verify 11/11 tiers PASS +- Run `uv run python -m pytest tests/test_gui_2_result.py -v` — verify all tests pass +- Write `docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md` +- Update `conductor/tracks.md` row to "shipped" +- Update umbrella spec count +- Phase 13 checkpoint commit with git note + +**Verify:** `--strict` exits 0; 11/11 tiers PASS; report is committed; tracks.md updated. + +--- + +## 8. Verification Criteria + +The track is "complete" when ALL of the following hold: + +- **VC-1** `audit_exception_handling.py --src src/gui_2.py --strict` exits 0. +- **VC-2** 0 INTERNAL_BROAD_CATCH sites in `src/gui_2.py` (25 → 0). +- **VC-3** 0 INTERNAL_SILENT_SWALLOW sites in `src/gui_2.py` (13 → 0). +- **VC-4** 0 UNCLEAR sites in `src/gui_2.py` (2 → 0). +- **VC-5** 0 INTERNAL_RETHROW sites in `src/gui_2.py` (2 → 0 or classified compliant). +- **VC-6** 3 new render functions exist: `render_controller_error_modal`, `_render_worker_error_indicator`, `_render_last_request_errors_modal`. +- **VC-7** `tests/test_gui_2_result.py` exists with ≥55 tests (42 site tests + 13 invariant tests), all pass. +- **VC-8** 11-tier batched test suite passes with no new regressions. +- **VC-9** Per-phase audit gates verified (each phase's commit shows the expected count drop in the audit output). +- **VC-10** Tier 2 acknowledged styleguide re-read at start of each phase (commit message contains "TIER-2 READ conductor/code_styleguides/error_handling.md end-to-end"). +- **VC-11** Git history shows ≥60 atomic commits (42 site migrations + 13 phase setup commits + 3 infra commits + 2 docs commits). +- **VC-12** End-of-track report at `docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md` covers all 13 phases. +- **VC-13** `conductor/tracks.md` row updated to "shipped 2026-06-XX." +- **VC-14** Umbrella spec count updated to reflect actual scope (42 migration sites + 6 infra sites = 48 sites in this sub-track; umbrella total now ~272 sites across all 5 sub-tracks). + +--- + +## 9. Out of Scope + +- **Sub-track 5** (`result_migration_baseline_cleanup`) — separate track; this track's shipping is the dependency. +- **Migrating `tests/` files** — out of scope per `conductor/tracks/data_oriented_error_handling_20260606/spec.md`. +- **Adding new `try/except` sites** — this track migrates EXISTING sites only. +- **Changing the audit heuristic** — sub-track 3 Phase 7 already tightened it; this track uses the existing heuristic. +- **Hot reload verification** — fragile per `live_gui_test_fixes_20260618`; use live_gui tests instead. +- **Removing the legacy wrappers** — when extracting `_result` helpers, the legacy wrappers are preserved (per sub-track 3 Phase 6 Group 6.3 pattern for `_save_active_project`); a follow-up track can migrate callers to the `_result` variants. +- **Wire-up of the 8 controller state attributes** — sub-track 3 Phase 6 already added the attributes; this track only consumes them. + +--- + +## 10. Risks + +| ID | Risk | Likelihood | Mitigation | +|---|---|---|---| +| R4-1 | Tier 2 invents a laundering heuristic for the 2 UNCLEAR sites at gui_2.py:1349 | medium | Phase 12 forces explicit classification with comment; the Phase 7 heuristic already classifies it; 5 regression-guard tests in `tests/test_audit_heuristics.py` lock the behavior | +| R4-2 | Tier 2 doesn't migrate INTERNAL_SILENT_SWALLOW sites that "look like" logging-only but aren't drained | medium | Phase 1 inventory forces explicit classification per site BEFORE coding; Phase 10's audit gate enforces 0 silent-swallow | +| R4-3 | gui_2.py's render loop changes break the immediate-mode frame | medium | Render-loop sites are isolated in Phase 3; visual verification via live_gui tests; per-site unit tests verify success-path output is identical | +| R4-4 | Scope grows as Tier 2 finds more sites mid-migration | low | Phase 1 inventory freezes the 42-site list; if new sites are discovered, they're tracked but NOT migrated in this track (added to a follow-up) | +| R4-5 | The user's principle ("logging is NOT a drain") is misapplied | low | Styleguide re-read at start of each phase; commit-message acknowledgment; 13 invariant tests verify per-phase progress | +| R4-6 | Thread-safety violation in worker sites (Phase 7) | low | `app._worker_errors_lock` is already in place (sub-track 3 Phase 6); multi-thread unit test verifies | +| R4-7 | The 11-tier batched suite times out before all tiers run (per `result_migration_small_files_20260617` Phase 12→13 incident) | medium | Phase 13 uses `uv run python scripts/run_tests_batched.py` (the fixed script from sub-track 2 Phase 13.1); if it times out, Tier 2 reports and the user decides | +| R4-8 | Per-phase audit gate shows wrong count (heuristic misclassification) | low | The audit heuristic was verified by 5 regression-guard tests in sub-track 3 Phase 7; if a count is wrong, Tier 2 reports | + +--- + +## 11. See Also + +- `conductor/code_styleguides/error_handling.md` — the canonical convention (READ at start of each phase) +- `conductor/code_styleguides/data_oriented_design.md` — the canonical DOD reference +- `docs/AGENTS.md` §"The 4 memory dimensions" — the cross-cutting lens +- `docs/guide_gui_2.md` — gui_2.py architecture guide +- `docs/guide_app_controller.md` — AppController state attributes (the data plane) +- `conductor/tracks/result_migration_20260616/spec.md` — the umbrella spec +- `conductor/tracks/result_migration_app_controller_20260618/spec.md` — sub-track 3 (the data plane source) +- `conductor/tracks/result_migration_small_files_20260617/spec.md` — sub-track 2 (the sliming precedent) +- `conductor/tracks/result_migration_review_pass_20260617/spec.md` — sub-track 1 (the UNCLEAR classification precedent) +- `conductor/tracks/live_gui_test_fixes_20260618/spec.md` — the hot-reload fragility findings (do NOT use hot reload) +- `scripts/audit_exception_handling.py` — the audit script (the gate) +- `tests/test_audit_heuristics.py` — the heuristic regression-guard tests \ No newline at end of file diff --git a/conductor/tracks/result_migration_gui_2_20260619/state.toml b/conductor/tracks/result_migration_gui_2_20260619/state.toml new file mode 100644 index 00000000..cf0927c6 --- /dev/null +++ b/conductor/tracks/result_migration_gui_2_20260619/state.toml @@ -0,0 +1,189 @@ +# Track state for result_migration_gui_2_20260619 +# Updated by Tier 2 Tech Lead as tasks complete + +[meta] +track_id = "result_migration_gui_2_20260619" +name = "Result Migration - Sub-Track 4 (gui_2.py)" +status = "active" +current_phase = 0 +last_updated = "2026-06-19" +umbrella = "result_migration_20260616" +sub_track_index = 4 +anti_sliming_protocol = "ENABLED — per-phase styleguide re-read + per-site audit pre/post check + per-phase invariant test; 13 phases cap each phase at <=10 sites" + +[blocked_by] +result_migration_app_controller_20260618 = "shipped 2026-06-19 (with Phase 7); data plane ready" + +[blocks] +result_migration_baseline_cleanup = "blocked by this track; date TBD when this track ships" + +[phases] +phase_0 = { status = "pending", checkpointsha = "", name = "Setup + styleguide re-read (3 tasks)" } +phase_1 = { status = "pending", checkpointsha = "", name = "Site inventory + classification (3 tasks; 42 sites in PHASE1_SITE_INVENTORY.md)" } +phase_2 = { status = "pending", checkpointsha = "", name = "Drain plane wiring (4 tasks; 3 new render functions + 2 invariant tests)" } +phase_3 = { status = "pending", checkpointsha = "", name = "INTERNAL_BROAD_CATCH Batch A — render-loop sites (<=10 sites)" } +phase_4 = { status = "pending", checkpointsha = "", name = "INTERNAL_BROAD_CATCH Batch B — modal/dialog sites (<=10 sites)" } +phase_5 = { status = "pending", checkpointsha = "", name = "INTERNAL_BROAD_CATCH Batch C — event handler sites (<=10 sites)" } +phase_6 = { status = "pending", checkpointsha = "", name = "Signal handler sites (<=5 sites; Pattern 3 drain)" } +phase_7 = { status = "pending", checkpointsha = "", name = "Worker / background sites (<=5 sites; thread-safety)" } +phase_8 = { status = "pending", checkpointsha = "", name = "Property setter / state sites (<=5 sites)" } +phase_9 = { status = "pending", checkpointsha = "", name = "Helper / utility sites (<=5 sites)" } +phase_10 = { status = "pending", checkpointsha = "", name = "INTERNAL_SILENT_SWALLOW migrations (<=13 sites; logging NOT a drain)" } +phase_11 = { status = "pending", checkpointsha = "", name = "INTERNAL_RETHROW classification (<=2 sites; Pattern 1/2/3)" } +phase_12 = { status = "pending", checkpointsha = "", name = "UNCLEAR classification (<=2 sites)" } +phase_13 = { status = "pending", checkpointsha = "", name = "Audit gate + end-of-track report (5 tasks; --strict exits 0; 11/11 tiers PASS)" } + +[tasks] +# Phase 0: Setup + styleguide re-read (3 tasks) +t0_1 = { status = "pending", commit_sha = "", description = "Update conductor/tracks.md with the new track row" } +t0_2 = { status = "pending", commit_sha = "", description = "Tier 2 reads conductor/code_styleguides/error_handling.md end-to-end; acknowledge in commit message" } +t0_3 = { status = "pending", commit_sha = "", description = "Phase 0 checkpoint commit; update state.toml Phase 0 status" } + +# Phase 1: Site inventory + classification (3 tasks) +t1_1 = { status = "pending", commit_sha = "", description = "Run audit --src src/gui_2.py --json > tests/artifacts/PHASE1_AUDIT.json" } +t1_2 = { status = "pending", commit_sha = "", description = "Walk the audit + write tests/artifacts/PHASE1_SITE_INVENTORY.md (42 rows)" } +t1_3 = { status = "pending", commit_sha = "", description = "Create tests/test_gui_2_result.py with 2 Phase 1 invariant tests; Phase 1 checkpoint" } + +# Phase 2: Drain plane wiring (4 tasks) +t2_1 = { status = "pending", commit_sha = "", description = "Add render_controller_error_modal(app) — reads 8 controller attributes; renders popups" } +t2_2 = { status = "pending", commit_sha = "", description = "Add _render_worker_error_indicator(app) — status bar widget with click-to-expand modal" } +t2_3 = { status = "pending", commit_sha = "", description = "Add _render_last_request_errors_modal(app) — per-request error modal" } +t2_4 = { status = "pending", commit_sha = "", description = "Add 2 Phase 2 invariant tests; Phase 2 checkpoint" } + +# Phase 3: INTERNAL_BROAD_CATCH Batch A — render-loop sites (<=10) +t3_0 = { status = "pending", commit_sha = "", description = "Phase 3 styleguide re-read (Pattern 2 lines 396-407) + ack commit" } +t3_1 = { status = "pending", commit_sha = "", description = "Migrate first Batch A site (representative example with full code in plan.md)" } +t3_2 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 2" } +t3_3 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 3" } +t3_4 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 4" } +t3_5 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 5" } +t3_6 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 6" } +t3_7 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 7" } +t3_8 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 8" } +t3_9 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 9" } +t3_10 = { status = "pending", commit_sha = "", description = "Migrate Batch A site 10 (if present)" } +t3_11 = { status = "pending", commit_sha = "", description = "Add Phase 3 invariant test (batch_a_count_dropped); Phase 3 checkpoint" } + +# Phase 4: INTERNAL_BROAD_CATCH Batch B — modal/dialog sites (<=10) +t4_0 = { status = "pending", commit_sha = "", description = "Phase 4 styleguide re-read (Pattern 2) + ack commit" } +t4_1 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 1 (modal pattern: legacy wrapper triggers imgui.open_popup on failure)" } +t4_2 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 2" } +t4_3 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 3" } +t4_4 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 4" } +t4_5 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 5" } +t4_6 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 6" } +t4_7 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 7" } +t4_8 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 8" } +t4_9 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 9 (if present)" } +t4_10 = { status = "pending", commit_sha = "", description = "Migrate Batch B site 10 (if present)" } +t4_11 = { status = "pending", commit_sha = "", description = "Add Phase 4 invariant test; Phase 4 checkpoint" } + +# Phase 5: INTERNAL_BROAD_CATCH Batch C — event handler sites (<=10) +t5_0 = { status = "pending", commit_sha = "", description = "Phase 5 styleguide re-read + ack commit" } +t5_1 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 1 (event handler pattern: legacy wrapper appends to app._last_request_errors)" } +t5_2 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 2" } +t5_3 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 3" } +t5_4 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 4" } +t5_5 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 5" } +t5_6 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 6" } +t5_7 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 7" } +t5_8 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 8 (if present)" } +t5_9 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 9 (if present)" } +t5_10 = { status = "pending", commit_sha = "", description = "Migrate Batch C site 10 (if present)" } +t5_11 = { status = "pending", commit_sha = "", description = "Add Phase 5 invariant test; Phase 5 checkpoint" } + +# Phase 6: Signal handler sites (<=5) +t6_0 = { status = "pending", commit_sha = "", description = "Phase 6 styleguide re-read (Pattern 3 lines 409-419) + ack commit" } +t6_1 = { status = "pending", commit_sha = "", description = "Migrate signal handler site 1 (Pattern 3: sys.stderr.write + sys.exit(1))" } +t6_2 = { status = "pending", commit_sha = "", description = "Migrate signal handler site 2" } +t6_3 = { status = "pending", commit_sha = "", description = "Migrate signal handler site 3 (if present)" } +t6_4 = { status = "pending", commit_sha = "", description = "Migrate signal handler site 4 (if present)" } +t6_5 = { status = "pending", commit_sha = "", description = "Migrate signal handler site 5 (if present)" } +t6_6 = { status = "pending", commit_sha = "", description = "Add Phase 6 invariant test; Phase 6 checkpoint" } + +# Phase 7: Worker / background sites (<=5) +t7_0 = { status = "pending", commit_sha = "", description = "Phase 7 styleguide re-read + ack commit" } +t7_1 = { status = "pending", commit_sha = "", description = "Migrate worker site 1 (use app._report_worker_error; thread-safety via lock)" } +t7_2 = { status = "pending", commit_sha = "", description = "Migrate worker site 2" } +t7_3 = { status = "pending", commit_sha = "", description = "Migrate worker site 3" } +t7_4 = { status = "pending", commit_sha = "", description = "Migrate worker site 4" } +t7_5 = { status = "pending", commit_sha = "", description = "Migrate worker site 5" } +t7_6 = { status = "pending", commit_sha = "", description = "Add Phase 7 invariant test + thread-safety test; Phase 7 checkpoint" } + +# Phase 8: Property setter / state sites (<=5) +t8_0 = { status = "pending", commit_sha = "", description = "Phase 8 styleguide re-read + ack commit" } +t8_1 = { status = "pending", commit_sha = "", description = "Migrate setter site 1 (per sub-track 3 Phase 6 Group 6.3 pattern)" } +t8_2 = { status = "pending", commit_sha = "", description = "Migrate setter site 2" } +t8_3 = { status = "pending", commit_sha = "", description = "Migrate setter site 3" } +t8_4 = { status = "pending", commit_sha = "", description = "Migrate setter site 4 (if present)" } +t8_5 = { status = "pending", commit_sha = "", description = "Migrate setter site 5 (if present)" } +t8_6 = { status = "pending", commit_sha = "", description = "Add Phase 8 invariant test; Phase 8 checkpoint" } + +# Phase 9: Helper / utility sites (<=5) +t9_0 = { status = "pending", commit_sha = "", description = "Phase 9 styleguide re-read + ack commit" } +t9_1 = { status = "pending", commit_sha = "", description = "Migrate helper site 1" } +t9_2 = { status = "pending", commit_sha = "", description = "Migrate helper site 2" } +t9_3 = { status = "pending", commit_sha = "", description = "Migrate helper site 3" } +t9_4 = { status = "pending", commit_sha = "", description = "Migrate helper site 4 (if present)" } +t9_5 = { status = "pending", commit_sha = "", description = "Migrate helper site 5 (if present)" } +t9_6 = { status = "pending", commit_sha = "", description = "Add Phase 9 invariant test; Phase 9 checkpoint" } + +# Phase 10: INTERNAL_SILENT_SWALLOW migrations (<=13) — CRITICAL anti-sliming phase +t10_0 = { status = "pending", commit_sha = "", description = "Phase 10 styleguide re-read (lines 462-540 logging NOT a drain) + ack commit (explicit sliming risk)" } +t10_1 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 1 (NO narrowing+logging; full Result[T] propagation)" } +t10_2 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 2" } +t10_3 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 3" } +t10_4 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 4" } +t10_5 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 5" } +t10_6 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 6" } +t10_7 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 7" } +t10_8 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 8" } +t10_9 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 9" } +t10_10 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 10" } +t10_11 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 11" } +t10_12 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 12" } +t10_13 = { status = "pending", commit_sha = "", description = "Migrate silent-swallow site 13" } +t10_14 = { status = "pending", commit_sha = "", description = "Add Phase 10 invariant test (silent_swallow_count_zero); Phase 10 checkpoint" } + +# Phase 11: INTERNAL_RETHROW classification (<=2) +t11_0 = { status = "pending", commit_sha = "", description = "Phase 11 styleguide re-read (Re-Raise Patterns lines 625-690) + ack commit" } +t11_1 = { status = "pending", commit_sha = "", description = "Classify INTERNAL_RETHROW site 1 per Pattern 1/2/3 or migrate to Result[T]" } +t11_2 = { status = "pending", commit_sha = "", description = "Classify INTERNAL_RETHROW site 2" } +t11_3 = { status = "pending", commit_sha = "", description = "Add Phase 11 invariant test; Phase 11 checkpoint" } + +# Phase 12: UNCLEAR classification (<=2) +t12_0 = { status = "pending", commit_sha = "", description = "Phase 12 styleguide re-read + ack commit" } +t12_1 = { status = "pending", commit_sha = "", description = "Classify UNCLEAR site 1 (or migrate if non-compliant)" } +t12_2 = { status = "pending", commit_sha = "", description = "Classify UNCLEAR site 2" } +t12_3 = { status = "pending", commit_sha = "", description = "Add Phase 12 invariant test; Phase 12 checkpoint" } + +# Phase 13: Audit gate + end-of-track report (5 tasks) +t13_1 = { status = "pending", commit_sha = "", description = "Run audit --src src/gui_2.py --strict; verify exit 0" } +t13_2 = { status = "pending", commit_sha = "", description = "Run tests/test_gui_2_result.py -v; verify all PASSED" } +t13_3 = { status = "pending", commit_sha = "", description = "Run scripts/run_tests_batched.py; verify 11/11 tiers PASS" } +t13_4 = { status = "pending", commit_sha = "", description = "Write docs/reports/TRACK_COMPLETION_result_migration_gui_2_20260619.md" } +t13_5 = { status = "pending", commit_sha = "", description = "Final checkpoint + tracks.md update + umbrella count update" } + +[verification] +phase_0_complete = false +phase_1_complete = false +phase_2_complete = false +phase_3_complete = false +phase_4_complete = false +phase_5_complete = false +phase_6_complete = false +phase_7_complete = false +phase_8_complete = false +phase_9_complete = false +phase_10_complete = false +phase_11_complete = false +phase_12_complete = false +phase_13_complete = false +audit_strict_exits_0 = false +batched_suite_11_of_11_pass = false +site_inventory_has_42_rows = false +drain_plane_render_functions_exist = false +silent_swallow_count_zero = false +rethrow_count_zero = false +unclear_count_zero = false +broad_catch_count_zero = false \ No newline at end of file