# SSDL Campaign Aborted: Post-Mortem **Date:** 2026-06-24 **Campaign:** `metadata_ssdl_defusing_20260624` (umbrella) + 3 children **Status:** ABORTED **Author:** Tier 1 (post-mortem) ## What this campaign was A 3-child campaign to defuse the `Metadata` aggregate's combinatoric explosion (4.01e22 effective codepaths) via Fleury's SSDL techniques: 1. `metadata_nil_sentinel_20260624` — Nil Sentinel 2. `metadata_generational_handle_20260624` — Generational Handle 3. `metadata_field_cache_20260624` — Immediate-Mode Field Cache The 3 children were based on the parent `code_path_audit_20260607` Finding 1, which proposed "6 nil-check functions" and 3 SSDL defusing techniques. ## What actually happened ### Phase 1: Spec authoring (the original mistake) The spec was authored based on text from the parent code path audit's AUDIT_REPORT.md, which stated: - "6 nil-check functions" (per Finding 1) - "3 specific techniques" (nil sentinel, generational handle, field cache) - 4.01e22 effective codepaths - 3466 branch points - 123 field-access sites The Tier 1 author (me) cited this without running the actual SSDL detector to verify. I did not read the canonical styleguides (`error_handling.md`, `data_oriented_design.md`) before authoring the spec. This violated the convention's Rule #0: "READ THIS STYLEGUIDE FIRST." ### Phase 2: Tier 2 implementation (the verification) Tier 2 picked up child 1 (`metadata_nil_sentinel_20260624`) and: 1. **Could only find 1 function to migrate** (`_build_files_section_from_items` in `src/aggregate.py`), not 6. The function was migrated to use `NIL_METADATA = {}` defensively, but the actual nil-check it had (`if path is None:`) was a `str` check, NOT a `Metadata` check. 2. **The budget gate (≥10% drop in `compute_effective_codepaths`) failed.** Post-child-1 measurement: 4.014e+22 (within rounding error of the 4.01e+22 baseline). The 10% threshold was mathematically near-impossible due to exponential dominance in the sum. 3. **The SSDL detector found 73 nil-check functions** across the codebase — but most are on `_gemini_client`, `_anthropic_client`, `path`, `adapter`, etc., NOT on `Metadata` values. The 1 migration in `src/aggregate.py` was a `path` check refactored to `if not path:`, not a Metadata nil-check. 4. **The "6 nil-check functions" was a static text string** in `src/code_path_audit_gen.py:108`, not a runtime measurement. The text was hardcoded in the AUDIT_REPORT.md generator, not derived from the SSDL detector. ### Phase 3: Cancellation (the new followup) The campaign was cancelled. The salvage: - `NIL_METADATA = {}` in `src/aggregate.py` (1 line) - `tests/test_metadata_nil_sentinel.py` (5 tests) Both are useful primitives for future use. They stay in the codebase. ## The root cause of the 4.01e22 Per the canonical styleguide `data_oriented_design.md` (the Mike Acton + Ryan Fleury principles): > "**Prefer Fewer Types** — A helpful lesson for me was in reframing error information... The metastasizing of types creates more required codepaths." The 4.01e22 is **not from nil-checks**. It's from `Metadata: TypeAlias = dict[str, Any]`. Every consumer function that does `entry.get('key', default)` is a runtime type-dispatch branch. The combinatoric explosion is from the unknown type, not from missing sentinels. The actual fix is **`any_type_componentization`**: promote `dict[str, Any]` to typed `@dataclass` instances. After promotion: - `entry.get('key', default)` becomes `entry.field_name` (direct attribute access, 0 branches) - The combinatoric explosion collapses at the source The parent `any_type_componentization_20260621` track did this for 48/89 sites, but the call-site migrations were reverted at `751b94d4`. The 3 surviving modules (`src/mcp_tool_specs.py`, `src/openai_schemas.py`, `src/provider_state.py`) are orphaned on master — they exist but nothing imports them. ## The new followup `code_path_audit_phase_2_20260624` is the actual followup. It re-applies the 48 call-site migrations + addresses the 11 pre-existing audit violations (4 NG1 + 7 NG2). After it ships, the 4.01e22 should drop by orders of magnitude. ## Lessons learned 1. **Read the canonical styleguides BEFORE writing specs.** The `data_oriented_design.md` styleguide has the "Prefer Fewer Types" principle. The `error_handling.md` styleguide has Rule #0. Neither was read before the SSDL spec was authored. 2. **Run the detectors BEFORE relying on the audit's text.** The "6 nil-check functions" was a static text string, not a measurement. Always verify with the actual detector (`src/code_path_audit_ssdl.detect_nil_check_pattern`). 3. **Verify the 4.01e22 number is from the source the fix addresses.** The combinatoric explosion was from `dict[str, Any]` type-dispatch, not from nil-checks. The fix is type promotion, not nil sentinels. 4. **Don't propose followups to fix something that wasn't measured.** The SSDL techniques (nil sentinel, generational handle, field cache) are valid Fleury techniques, but they don't apply when the cause is missing type structure, not missing sentinels. 5. **The SSDL campaign's salvageable artifact is `NIL_METADATA`.** The `NIL_*` pattern is the convention. The Metadata instance of it is now a primitive for future use, not a campaign outcome. ## See also - `conductor/code_styleguides/error_handling.md` — the `NIL_*` sentinel convention (Rule #0: read first) - `conductor/code_styleguides/data_oriented_design.md` — the "Prefer Fewer Types" principle (Ryan Fleury's combinatoric explosion) - `conductor/code_styleguides/type_aliases.md` — the 10 TypeAliases (the canonical names for shapes) - `docs/reports/SSDL_CAMPAIGN_ABORTED_20260624.md` — this post-mortem - `conductor/tracks/code_path_audit_phase_2_20260624/spec.md` — the actual followup - `conductor/tracks/any_type_componentization_20260621/plan.md` — the parent plan whose 48 call-site migrations are the actual fix - `docs/reports/code_path_audit/2026-06-22/AUDIT_REPORT.md` — the source of the 4.01e22 baseline - `src/code_path_audit_ssdl.py` — the `detect_nil_check_pattern` + `compute_effective_codepaths` measurement infrastructure