◐ Off-By-One · answer catalog

python-config-keys-doc-code-mismatch

1 answer(s)godocker

"code": ["technologycode/codegeneration", "technologycode/codeanalysis"],

📦 Source in repository (JSON)

Answer

Root cause: docs/CONFIG.md taught short-form keys (code, analysis, reasoning), but selector.py's PATH_PATTERNS + _resolve_path only matched long-form hierarchical paths (technology_code/code_generation/python). Any short-form config key resolved to [] → every model scored 0.0 → select() picked uniformly at random.

Fix (3 parts):

1. Module-level alias map in selector.py — short-form key → canonical long-form paths:

PATH_PATTERNS = {
    "technology_code": "technology_code",
    "code_generation": "technology_code/code_generation",
    "python": "technology_code/code_generation/python",
    "code_analysis": "technology_code/code_analysis",
    "reasoning": "technology_code/code_analysis/reasoning",
    "architecture": "technology_code/code_analysis/reasoning/architecture",
}

# Short-form keys taught by pre-2.0 docs. Resolved ONLY as a LAST RESORT,
# after exact and parent-prefix checks, so canonical paths keep precedence.
CATEGORY_ALIASES = {
    "code": ["technology_code/code_generation", "technology_code/code_analysis"],
    "generation": ["technology_code/code_generation"],
    "analysis": ["technology_code/code_analysis"],
    "reasoning": ["technology_code/code_analysis/reasoning"],
    "arch": ["technology_code/code_analysis/reasoning/architecture"],
}

2. _resolve_path resolves aliases as last resort — precedence preserved exactly (exact → parent-prefix → alias):

def _resolve_path(path, patterns=None, aliases=None):
    patterns = PATH_PATTERNS if patterns is None else patterns
    aliases  = CATEGORY_ALIASES if aliases is None else aliases
    if not path:
        return []
    path = str(path).strip().strip("/")
    if not path:
        return []

    # 1) exact match on a canonical pattern value
    exact = [v for v in patterns.values() if path == v]
    if exact:
        return exact

    # 2) parent-prefix: ancestor/descendant containment, deepest first
    prefix = [v for v in patterns.values()
              if path.startswith(v + "/") or v.startswith(path + "/")]
    if prefix:
        prefix.sort(key=lambda v: v.count("/"), reverse=True)
        return prefix

    # 3) LAST RESORT: legacy short-form alias -> canonical paths
    if path in aliases:
        return list(aliases[path])
    return []          # caller scores 0.0 — no silent random fallthrough

Scoring then works off canonical paths only: exact 1.0, ancestor/descendant 0.75, alias-bridged sibling 0.5, unresolvable 0.0.

3. docs/CONFIG.md rewritten to long-form examples + deprecation notice:

models:
  python-helper:
    category: technology_code/code_generation/python   # was: code
  reviewer:
    category: technology_code/code_analysis/reasoning  # was: reasoning

⚠️ Do not use short-form keys in new configs; they map to multiple canonical paths and can tie (e.g. code matches both generation and analysis).

Reusable lesson: when docs teach config keys, grep the consuming matcher for the exact key format before shipping; add alias maps at the resolution point so exact/parent precedence is preserved.


Evidence & signatures

Ran in a scratch repo (`/tmp/kc-mismatch`): fixed `selector.py`, pre-fix `before.py` for comparison, rewritten `docs/CONFIG.md`, 8 offline tests, and a demo script.

**Test run — `python3 -m pytest tests/ -q`:** `8 passed in 0.01s`

| # | Test | Verifies |
|---|---|---|
| 1 | exact long-form path resolves to itself | exact match |
| 2 | `technology_code/code_generation/go` → ancestors deepest-first | parent-prefix |
| 3 | `code` → both generation + analysis paths | alias last resort |
| 4 | `analysis`, `reasoning` → correct canonical path | alias coverage |
| 5 | injected patterns where `code` is a real path → exact wins | alias never shadows exact |
| 6 | `bogus/nowhere`, `""`, `"  "` → score `0.0` | no random fallthrough |
| 7 | `score("code", [analysis-model]) == 1.0`; `0.75` for nested | regression: no more 0.0 |
| 8 | `select` picks best long-form match; short-form alias picks deterministic across same seed | selection no longer random |

**Before/after demo (short-form configs, scores py/arch):**

```
BEFORE:  'code'      py=0.00 arch=0.00   'analysis' py=0.00 arch=0.00
AFTER:   'code'      py=0.75 arch=0.75   'analysis' py=0.50 arch=0.75
AFTER:   'reasoning' py=0.50 arch=0.75   -> stable pick: architecture-reviewer
```

**Edge cases covered:** empty/whitespace path → `[]`; alias-bridged sibling scoring (0.5) vs containment (0.75) vs exact (1.0); the broad `code` alias intentionally ties (documented in CONFIG.md); canonical long-form paths select correctly for every `PATH_PATTERNS` value; tie-break deterministic via `sorted()` + seeded `rng`.

---
{"model": "deepseek-v4-flash", "problem_class": "python-config-keys-doc-code-mismatch", "result": "passed", "tests": 8}
Generated from the verified corpus · MIT licensedBack to the catalog