15. The source tree splits by domain/type; library origin is a tag, not a folder¶
Date: 2026-07-06
Status¶
Accepted
Context¶
A module carries three orthogonal axes: its domain (core vs light), its type (effect / modifier / layout / driver), and its library (the origin it was learned from (MoonLight, WLED, MoonModules, projectMM-native). The src/, docs/, test/, and assets/ trees all had to pick which axes become folders.
Domain and type are unambiguous: every module has exactly one domain and one type. Library is not: an effect's origin is frequently blended, not a single fact: DistortionWavesEffect cites MoonLight + WLED + v1 + v2; GameOfLifeEffect cites MoonLight + MoonModules + v1; several modules have no clear single origin. A folder axis forces one answer to a multi-valued question, and a wrong or shifting answer costs a multi-file move (src + assets + tests + the registered doc path). Library also duplicates a dimension the tags() emoji already carries, and the emoji can carry several origins where a folder cannot. The end user does not care about a module's library except as a UI filter, which the emoji chip already provides.
Decision¶
The tree is <core|light> / <type> / Module, flat within a type. Library does not become a folder level; it rides where it is free and non-duplicative:
- In code / assets / tests: the
tags()emoji (drives the UI origin-filter; may be multi-valued). Leaf files are flat within their type folder:src/light/effects/DistortionWaves.h,docs/assets/light/effects/DistortionWaves.gif,test/unit/light/unit_DistortionWaves.cpp. - In docs: library rides in the page dimension, not a folder: one catalog page per type (
effects.md) with library sections inside, splitting to per-library page names (effects_wled.md) only when a section outgrows its page. A doc page is forgiving about fuzzy origin: a blended-lineage effect goes on one page with its full origin in the row's tags, and mis-filing is a one-line edit, not a multi-file move.docsis thus the one area wheretypeis expressed as part of a page name rather than a folder, because the docs compact to per-type/per-library pages, and library, the only axis with an explosion problem, rides along in that name.
| core/light | type | leaf | library | |
|---|---|---|---|---|
| src | light/ |
effects/ |
DistortionWaves.h |
tag in tags() |
| assets | light/ |
effects/ |
DistortionWaves.gif |
— |
| tests | light/ |
effects/ |
unit_DistortionWaves.cpp |
— |
| docs | light/ |
the page name (effects.md, later effects_<library>.md) |
(row inside) | the page split |
Consequences¶
Every drawback of library-as-folder is dropped at once: fuzzy-origin filing, two-places-disagree, reclassification churn, sparse subfolders, and deep paths all disappear. A module's origin can be blended or can change without a file move; only the tags() emoji and, at most, a one-line catalog-row edit change.
The one open growth path (non-blocking): when a library's section outgrows its catalog page, split it to a per-library page name (effects_wled.md, …), a lift, not a rewrite, since the flat page names and within-page sections are already in place for it.
The live catalog pages (docs/moonmodules/light/{effects,modifiers,layouts}.md) and docs/coding-standards.md cite this decision for why the tree is shaped the way it is.