Why option-type docs are one index + a dozen deep-dives, not 52 stubs
The question: Would it help to add a markdown file to each option-type folder in
framework/includes/option-types?
Context
The framework ships 52 option types and only 4 folders had any docs (in two different naming conventions). Meanwhile nearly every option-type bug we hit is the same shape: a contract that isn't visible from the code signature. Recent examples, all from real sessions:
multi-inlinerenders children from a hardcoded whitelist inview.php; a child type it doesn't know saves fine but draws a blank row, no error. And captions come from$cfg['title'], not'label'. Both cost a full round-trip to rediscover.predefined-colors-color-picker-compactsaves{ predefined, custom }, not a hex string.typographyis canonical;typography-v2is a thin alias.unit-inputsubmits a JSON string that_get_value_from_inputdecodes.icon-v2needs its pack enqueued before render.
None of that is guessable from get_type(). It's exactly what a short doc-next-to-the-code fixes.
Options considered
| Option | Trade-off |
|---|---|
| Leave it undocumented | Zero effort, but we keep paying the same rediscovery tax, and the multi-picker CLAUDE.md section already proves we resort to documenting the worst offenders anyway. |
An AGENTS.md in all 52 folders | Uniform, but ~40 of them are trivial (switch, text, slider) so most files would restate the obvious. Stubs train people to ignore the folder, and 52 files decay 52 ways. |
| Two tiers: one index + selective deep-dives | One root file maps all 52 (the thing you read first); deep-dives exist only where the contract bites. Highest signal, least rot. |
Decision
Two tiers, and standardize the filename on AGENTS.md.
- Tier 1 — one root
option-types/AGENTS.mdindex. A table of all 52: purpose, saved value shape, aliases, and a 📖/⚠ marker pointing to the deep-dive. This is the map — it's what would have said "multi-inline routes children through a whitelist" up front. - Tier 2 — a deep-dive
AGENTS.mdonly for the gotcha-prone ~dozen (multi-inline,typography,color-picker,rgba-color-picker,icon-v2,predefined-colors-color-picker-compact,unit-input, plus the pre-existingmulti-picker,background-pro,spacing). Each follows one template: What it is · Saved value shape · How to consume · Gotchas · Canonical snippet. - Naming:
AGENTS.mdfor agent-facing contract docs (matches the existing "read theAGENTS.mdnearest the code before editing" rule). A genuine human maintenance note staysREADME.md— e.g.icon/README.mdis a "how to regenerate the icon data" procedure, not a usage contract, so it was left as-is.
Two rules keep them alive: document the contract, not the code (a prose restatement of the PHP is what drifts), and the cross-cutting rules stay in CLAUDE.md — the folder docs are the local deep-dive and link back, so the two never disagree.
Follow-up: the USE vs BUILD split
A natural next question — should each folder also document how to code the option type? — resolves
the same way. There are two contracts: the USE contract (a schema author writing 'type' => '…'
and consumers reading the value) genuinely differs per type, so it earns per-folder docs. The
BUILD contract (authoring the class itself — the _render / _get_value_from_input /
_enqueue_static lifecycle, registration, the min-asset trap, the alias pattern) is ~95% identical
across all 51 types. Duplicating it into 51 folders would be the stub trap in its worst form —
identical copies that drift. So the build contract lives once in a single AUTHORING.md at the
option-types root, and per-folder deep-dives carry only the implementation gotchas that are specific to
that type (e.g. multi-inline's render whitelist), right next to its USE notes. Shared knowledge
centralized, per-type knowledge only where it earns its place — the same principle, one level down.
Why
A doc's value is the delta between what the code shows and what you need to know. For a switch that
delta is ~zero; for multi-inline it's a whole afternoon. Spending equal words on both is what makes
doc folders rot. The index covers the cheap 80% in one maintainable file, and the deep-dives spend
real words only where the contract has already drawn blood — which, not coincidentally, is the set we
kept re-learning the hard way.
Status: Accepted. Root index + 10 folder deep-dives written; new deep-dives added when a type's contract bites, not preemptively.
