Skip to content

📦 WP-136 — refine(configs): unify config names, fold det_yolo_smoke into docs

Engineering log · Phase 12 — Keypoint detection, v0.5.0

🔗 Depends on WP-040, WP-088, WP-099c, WP-125, WP-135 · Needed by none

⬅️ WP-135 · Roadmap · WP-137 ➡️

WP-136 — a filename is not a scratch space for whatever axis is on your mind that day

src/lucid_yolo/configs/ had grown eight files under what read as one two-token scheme (<task>_<tier>.yaml), but a closer look found three separate inconsistencies rather than one: two files used a third token to mean something no sibling file's name carried at all, one word (ablation) meant two structurally different things across two files, and the scheme itself never named the one axis every config actually sets differently -- variant. User direction (mid-session, superseding this row's own first draft) was to stop patching the two collisions and instead put every config on one explicit scheme: <task>_<variant>_<tier>[_<detail>].yaml, variant spelled out in full rather than left as the bare letter the CLI and each file's own variant: field use.

The full rename, driven by what each file's own variant: line already said (checked, not assumed):

old new why
det_smoke.yaml det_nano_smoke.yaml variant: n, task truncated as it already was
seg_smoke.yaml seg_nano_smoke.yaml variant: n
obb_smoke.yaml obb_nano_smoke.yaml variant: n
pose_smoke.yaml pose_nano_smoke.yaml variant: n; pose kept as task: keypoints's established domain term (roadmap's own "Pose-smoke tier" language, not a choice this row introduced)
det_ablations.yaml det_small_ablations.yaml variant: s -- the one config in the directory that is not n, previously invisible in its own filename
overfit_100.yaml det_nano_overfit_100.yaml had no task prefix at all, relying on task's implicit "detect" default; variant: n; 100 kept as WP-040's own detail (image count, not a tier word)
det_yolo_smoke.yaml dropped see below
pose_smoke_ablation.yaml pose_nano_smoke_laplace_nll.yaml ablation collided with det_ablations.yaml's different sense (a whole blueprint tier, sec. 10 tier B, ~120-epoch paired runs, its own sign-matching acceptance against R1 Tables 2/3/5/6) where this file is a same-budget arm of the smoke tier; laplace_nll names the mechanism swapped in, matching how the loss class and the DetectionLitModule docstring already refer to the arm

det_yolo_smoke.yaml was dropped, not renamed. It was first renamed det_smoke_layout.yaml (naming the --data.layout axis, not the one value it took), then reconsidered a second time against det_nano_smoke.yaml sitting beside it in a listing -- "layout" alone read as no more specific than the word it replaced, so the candidate became det_nano_smoke_yolo.yaml, spelling the actual value. Diffing it against det_nano_smoke.yaml at that point (checked, not assumed) found the two differ in exactly three things: the data: block (a YOLO root and its layout: yolo), model.num_classes (3 for the demo root vs COCO's 80 -- a property of the demoed root, not an independent axis), and header prose. Everything else -- schedule, gains, optimizer, variant: n -- is identical. A whole config file whose only content beyond a data override and its dependent class count is header prose is not a ninth point in the scheme; it is three CLI flags on det_nano_smoke.yaml that never needed a file of their own. The prose itself was not lost: docs/DATASETS.md's "Dataset formats" section already carries the deeper mechanics (layout vs format, YOLO_CANDIDATES, data.yaml priority, why segment is unavailable), so the file's comments were largely restating what that section already says more completely. docs/TRAINING.md gains a "A YOLO-format root instead of COCO's" section instead: the three-flag override, why --data.layout yolo is stated rather than probed (A63's undecidable-tie case), the num_classes coupling to the root's own data.yaml, and the segment refusal, each in one paragraph rather than a whole file's worth of header comments.

Variant spelled out, deliberately creating a second vocabulary. Every config's own variant: field and the --variant CLI flag stay bare letters -- that is the real API and does not change here. The filename now spells nano/small for readability at the cost of a reader having to know nano == n when connecting a filename to the config's own content or an override. Confirmed with the user rather than assumed, since nothing else in this codebase (code, docs, or CLI) has ever spelled these out.

What that scheme cannot promise, stated rather than left implicit. A filename's variant token is the config's default; --variant s on det_nano_smoke.yaml still runs an s-scale job under an n-named file, exactly as keypoint_gain 1.0 in a filename would not survive a --model.keypoint_gain override. The name documents the shipped default, not a runtime guarantee -- the same caveat every config's own header comment already states about variant ("this file's default scale, not a claim about the run").

What did not move: the historical record. docs/REPRODUCTION_REPORT.md's rows are explicit about being "the exact commands as they were run at the time" (docs/TRAINING.md), and the ROADMAP.md/ENGINEERING_LOG.md rows for WP-040, WP-099c, and WP-135 describe what those WPs built and named then. WP-129 already set this project's precedent for exactly this situation: moving audit_licenses.py/check_commit_trailers.py into scripts/lint/ did not rewrite WP-084's, WP-109's or WP-115's rows, which still cite the pre-move path. None of those earlier rows are touched here; this section is where all eight old names are cited for the last time.

What did move: everything a reader or the tool itself would act on. README.md, docs/TRAINING.md, AGENTS.md, docs/DATASETS.md (live usage docs, not a historical ledger -- a stale name there is just a broken command); scripts/overfit_micro.py's and scripts/shapes_regression.py's _RECIPE_PATH (functional, not prose -- the old name pointed at a file that no longer exists under it); src/lucid_yolo/cli/train.py's _DEFAULT_CONFIG and its own doctests; tests/ptl/test_cli.py's hardcoded test_configs_dir_non_empty set and every parametrized config-name literal; and every config's own internal header comment that named a sibling file. CHANGELOG.md's still-open ## [Unreleased] section also updated, on the same live-vs-historical reasoning -- it describes what the next release will ship, not what a past WP called something at the time, so a stale name there would mislead exactly the reader it exists for.

No test needed a structural change: tests/ptl/test_cli.py's _CONFIG_PATHS is _CONFIGS_DIR.glob("*.yaml"), so all eight renamed files are discovered and dry-parsed under their new names automatically. The one hardcoded list (test_configs_dir_non_empty's membership assertion) and the several tests parametrized on a literal config name still needed the literals updated by hand, since a glob does not know what a test author meant to assert about.