# Tier 2 Startup Brief: metadata_promotion_20260624 ## Context This is the actual fix for the 4.01e22 combinatoric explosion. Promotes `Metadata: TypeAlias = dict[str, Any]` to a typed `@dataclass(frozen=True, slots=True)` and migrates all 695 consumer functions + 213 access sites to direct field access. **Recommendation:** Run in parallel with `code_path_audit_phase_3_provider_state_20260624` (the 27-call-site provider_state migration). The two tracks are orthogonal — phase 3 touches `provider_state` infrastructure, this track touches `Metadata` consumers. No merge conflicts expected. The `code_path_audit_phase_3_provider_state_20260624` track is listed as `blocked_by` in metadata.json but the blocking is recommended, not strict. If the user wants this track to start first, update metadata.json accordingly. ## MANDATORY Pre-Action Reading (per agent protocol) 1. `AGENTS.md` (project root) — operating rules 2. `conductor/workflow.md` — the workflow 3. `conductor/edit_workflow.md` — the edit workflow 4. `conductor/code_styleguides/data_oriented_design.md` — the "Prefer Fewer Types" principle (the canonical rationale) 5. `conductor/code_styleguides/error_handling.md` — the `Result[T]` convention (Rule #0: read first) 6. `conductor/code_styleguides/type_aliases.md` — the 10 TypeAliases convention 7. `docs/reports/SSDL_CAMPAIGN_ABORTED_20260624.md` — the post-mortem explaining why this is a type-dispatch problem, NOT a nil-check problem 8. `src/type_aliases.py` (current 30 lines) 9. `scripts/code_path_audit/code_path_audit.py` (consumer detection) 10. `scripts/code_path_audit/code_path_audit_ssdl.py` (effective codepaths metric) **First commit of this track must include** `TIER-2 READ before metadata_promotion_20260624` in the message. ## The Metadata dataclass (Phase 0) ```python # src/type_aliases.py: REPLACE line 5 # BEFORE: Metadata: TypeAlias = dict[str, Any] # AFTER: @dataclass(frozen=True, slots=True) class Metadata: role: str = "" content: Any = None tool_calls: Any = None tool_call_id: str = "" name: str = "" args: Any = None source_tier: str = "main" model: str = "unknown" id: str = "" ts: str = "" description: str = "" depends_on: tuple[str, ...] = () status: str = "" manual_block: bool = False completed_tickets: int = 0 auto_start: bool = False command: str = "" script: str = "" output: Any = None error: str = "" tier: str = "" path: str = "" full_path: str = "" filename: str = "" mtime: float = 0.0 size: int = 0 # ... ~150-180 distinct keys from the .get + [] site analysis ... def to_dict(self) -> dict[str, Any]: return {k: v for k, v in asdict(self).items() if v is not None or k in _NON_NULL_KEYS} @classmethod def from_dict(cls, raw: dict[str, Any]) -> 'Metadata': valid_fields = {f.name for f in fields(cls)} return cls(**{k: v for k, v in raw.items() if k in valid_fields}) ``` The exact list of fields is determined by the union of distinct keys used across all 213 access sites. The spec §FR1 has the seed list; the worker should expand it based on `git grep -hoE` output during Phase 0. ## Migration pattern (per consumer site) ```python # BEFORE: x = entry.get('model', 'unknown') y = entry.get('input_tokens', 0) or 0 z = entry.get('source_tier', 'main') if entry.get('manual_block', False): ... role = entry['role'] if 'depends_on' in entry: deps = entry['depends_on'] # AFTER (with Metadata dataclass): x = entry.model or 'unknown' y = entry.input_tokens or 0 z = entry.source_tier or 'main' if entry.manual_block: ... role = entry.role if entry.depends_on: deps = entry.depends_on ``` For polymorphic construction: ```python # BEFORE: entry = {'role': 'user', 'content': 'hi'} # AFTER: entry = Metadata(role='user', content='hi') # Or for dynamic dicts: entry = Metadata.from_dict(raw_dict) ``` For JSON serialization: ```python # BEFORE: json.dumps(entry) # AFTER: json.dumps(entry.to_dict()) ``` ## Phased migration order The 695 consumers distribute across 5 sub-aggregates. Migrate sub-aggregate by sub-aggregate: 1. **CommsLogEntry** (~150 sites): `session_logger.py`, `multi_agent_conductor.py`, `app_controller.py` 2. **HistoryMessage** (~80 sites): `ai_client.py` per-vendor history 3. **FileItem** (~200 sites): `aggregate.py`, `app_controller.py`, `gui_2.py` 4. **ToolDefinition + ToolCall** (~150 sites): `mcp_client.py`, `ai_client.py` tool loop section 5. **Metadata direct usage** (~115 sites): the catch-all (gui_2.py general, models.py, paths.py, etc.) ## Effective codepaths metric Expected progression: | Phase | Effective codepaths | Consumers | |---|---|---:| | Baseline (master) | 4.014e+22 | 695 | | After Phase 1 (CommsLogEntry) | ~4e+19 | ~545 (150 migrated away) | | After Phase 2 (HistoryMessage) | ~3e+19 | ~465 | | After Phase 3 (FileItem) | ~2e+18 | ~265 | | After Phase 4 (ToolDefinition+ToolCall) | ~1e+17 | ~115 | | After Phase 5 (Metadata direct) | ~5e+15 | ~0 | These are estimates based on the assumption that each migration removes ~2 branches per consumer. The actual drops depend on the specific code. Re-measure after each phase. ## Pre-flight verification (before Phase 0) ```bash # Verify the current state uv run python -c " import sys sys.path.insert(0, 'scripts/code_path_audit') sys.path.insert(0, 'src') from code_path_audit import build_pcg from code_path_audit_ssdl import count_branches_in_function pcg = build_pcg('src').data metadata_consumers = pcg.consumers.get('Metadata', []) total = sum(2 ** count_branches_in_function(f, 'src') for f in metadata_consumers) print(f'Baseline: {total:.3e} ({len(metadata_consumers)} consumers)') " # Expect: 4.014e+22 (695 consumers) # Verify the 213 access sites git grep -E "\.get\('[a-z_]+'," HEAD -- 'src/*.py' | wc -l # Expect: 107 git grep -E "\[[ ]*'[a-z_]+'[ ]*\]" HEAD -- 'src/*.py' | wc -l # Expect: 106 # Verify the 5 sub-aggregate TypeAliases all point to Metadata git show HEAD:src/type_aliases.py | grep "TypeAlias" # Expect: # CommsLogEntry: TypeAlias = Metadata # HistoryMessage: TypeAlias = Metadata # FileItem: TypeAlias = Metadata # ToolDefinition: TypeAlias = Metadata # ToolCall: TypeAlias = Metadata # Verify all 7 audit gates pass uv run python scripts/audit_weak_types.py --strict uv run python scripts/generate_type_registry.py --check uv run python scripts/audit_main_thread_imports.py uv run python scripts/audit_no_models_config_io.py uv run python scripts/audit_code_path_audit_coverage.py --input-dir docs/reports/code_path_audit/latest --strict uv run python scripts/audit_exception_handling.py --strict uv run python scripts/audit_optional_in_3_files.py --strict # All exit 0 ``` ## Post-track verification (after Phase 6) ```bash # VC1: Metadata is @dataclass git show HEAD:src/type_aliases.py | head -20 # Expect: @dataclass(frozen=True, slots=True) class Metadata: # VC2: 0 .get sites on Metadata consumers git grep -E "\.get\('[a-z_]+'," HEAD -- 'src/*.py' | wc -l # Expect: <20 (only legitimate non-Metadata uses) # VC3: 0 subscript sites on Metadata consumers git grep -E "\[[ ]*'[a-z_]+'[ ]*\]" HEAD -- 'src/*.py' | wc -l # Expect: <20 # VC4: 12+ tests pass uv run python -m pytest tests/test_metadata_dataclass.py -v # VC5: 5 sub-aggregate TypeAliases all point to Metadata git show HEAD:src/type_aliases.py | grep "TypeAlias = Metadata" # VC6: Effective codepaths drops by >= 2 orders of magnitude uv run python -c " import sys sys.path.insert(0, 'scripts/code_path_audit') sys.path.insert(0, 'src') from code_path_audit import build_pcg from code_path_audit_ssdl import count_branches_in_function pcg = build_pcg('src').data metadata_consumers = pcg.consumers.get('Metadata', []) total = sum(2 ** count_branches_in_function(f, 'src') for f in metadata_consumers) print(f'Post-track: {total:.3e} (baseline: 4.014e+22)') " # Expect: < 1e+20 ``` ## See also - `conductor/tracks/metadata_promotion_20260624/spec.md` — the full spec (10 VCs) - `conductor/tracks/metadata_promotion_20260624/plan.md` — the 5-phase plan - `conductor/tracks/metadata_promotion_20260624/metadata.json` — the metadata - `conductor/tracks/metadata_promotion_20260624/state.toml` — the state - `docs/reports/SSDL_CAMPAIGN_ABORTED_20260624.md` — the post-mortem explaining the type-dispatch root cause - `conductor/tracks/any_type_componentization_20260621/plan.md` — the grandparent plan - `src/type_aliases.py` — the current Metadata definition - `scripts/code_path_audit/code_path_audit.py` — the consumer detection - `scripts/code_path_audit/code_path_audit_ssdl.py` — the effective codepaths metric - `conductor/code_styleguides/data_oriented_design.md` — the "Prefer Fewer Types" principle