Files

8.6 KiB

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 <list> before metadata_promotion_20260624 in the message.

The Metadata dataclass (Phase 0)

# 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)

# 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:

# 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:

# 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)

# 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)

# 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