From d4c5a9450a2e730a68dc86f63c51bec41402104f Mon Sep 17 00:00:00 2001 From: Edwin van den Houdt Date: Sat, 5 Sep 2026 08:26:47 +0200 Subject: [PATCH] fix: layer-tag mismatches + libs/beheer title rule (RD-28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two components disagreed with their own story title. async.component.ts had no layer tag; add `Molecule:` to its class header, not to the Convenience: comment on the ASYNC array (that comment describes an export, not a layer, and stays as it is). breadcrumb.component.ts had `Chrome:`; rename it to `Molecule:` to match its story title. CLAUDE.md and layers.mdx both claimed `libs/beheer/ui` is Design System. The code disagrees: its story title is `Domein/Beheer/...`, because libs/beheer is a bounded context that lives under libs/ only because two apps share it. Fix the two doc lines to match the code; the story title does not change. The ticket's own tag-count check asserted 68 after the edits, but adding a new tag to async.component.ts (which had none) is a net +1 over the Chrome-to-Molecule rename (a wash) — the true post-edit count is 69. Corrected the number in the ticket text rather than deleting the mandated tag to force the wrong count. Also fixed pre-existing invalid nested-backtick markdown in the ticket's decision 4 bullets: prettier mis-rewrapped it and dropped spaces, so the phrasing was rewritten in valid CommonMark with no change in meaning. npm run ci --full passes. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 8 +- .../readable-codebase/RD-28-layer-tags.md | 154 ++++++++++++++++++ docs/project/readable-codebase/README.md | 2 +- libs/shared/docs/layers.mdx | 4 +- .../layout/breadcrumb/breadcrumb.component.ts | 2 +- .../src/ui/molecules/async/async.component.ts | 2 +- 6 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 docs/project/readable-codebase/RD-28-layer-tags.md diff --git a/CLAUDE.md b/CLAUDE.md index 3c7401a..f81bc1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -210,10 +210,10 @@ each app has its **own Storybook instance** (`.storybook-ssp/`, `.storybook-beha WP-67 — a single merged tsconfig can't resolve both apps' `@auth/*` at once), each globbing its own app's stories plus both shared libraries'. **Story titles mirror the sidebar's Design System/Domein split** (see `libs/shared/docs/layers.mdx`): a `libs/shared/ui|layout` -or `libs/beheer/ui` component is titled `Design System//`; -a component in an app context's `ui/` is titled `Domein//` — full stop, -regardless of which atomic layer it is (a context organism doesn't get its own -`Organisms/` bucket). +component is titled `Design System//`; +a component in an app context's `ui/`, or in `libs/beheer/ui`, is titled +`Domein//` — full stop, regardless of which atomic layer it is (a context +organism doesn't get its own `Organisms/` bucket). ## Conventions diff --git a/docs/project/readable-codebase/RD-28-layer-tags.md b/docs/project/readable-codebase/RD-28-layer-tags.md new file mode 100644 index 0000000..e2bd377 --- /dev/null +++ b/docs/project/readable-codebase/RD-28-layer-tags.md @@ -0,0 +1,154 @@ +# RD-28 — Two layer-tag fixes, and the `libs/beheer` title rule + +Status: done +Source: PLAN.md 4a (the two mislabels) and 4b (keep the tags) + +## Why + +RD-27 made the folder equal the layer. Three statements about a component's layer now exist — +the folder, the header comment's tag, and the story title — and a three-way agreement that has +never once disagreed is cheap documentation. + +It disagrees in exactly two places. And one repository-wide rule in CLAUDE.md is simply wrong +about `libs/beheer`. + +## Read first + +- `libs/shared/src/ui/molecules/async/async.component.ts:45` — the class header, with no layer + tag; and line 154, the `Convenience:` comment on the `ASYNC` array, which is **correct and + stays**. +- `libs/shared/src/layout/breadcrumb/breadcrumb.component.ts:9` — `/** Chrome: …`, against a + story titled `Design System/Molecules/Breadcrumb`. +- `CLAUDE.md:213` and `libs/shared/docs/layers.mdx:13` — the two lines that claim + `libs/beheer/ui` is Design System. +- PLAN.md 4a's third paragraph and 4b. + +## Decisions (pre-made, don't relitigate) + +1. **Exactly two components are mislabelled. Verified by scanning all 36**, comparing each + component's first layer tag against its own story title: + + | File | Story title | Tag today | Becomes | + | ------------------------------------------- | ----------- | --------- | ----------- | + | `ui/molecules/async/async.component.ts` | Molecules | _(none)_ | `Molecule:` | + | `layout/breadcrumb/breadcrumb.component.ts` | Molecules | `Chrome:` | `Molecule:` | + + Every other component already agrees. Do not "tidy" any other tag. + +2. **`async.component.ts` gets the tag added to its class header at line 45, not to line 154.** + The `Convenience:` comment at 154 describes the `ASYNC` array export — "import this array to + get the wrapper + all slot directives" — which is an accurate description of a convenience + export, not a layer claim. Leave it exactly as it is. + +3. **Keep all 68 layer-tag comments. Delete none.** PLAN 4b reversed an earlier proposal to + strip them: the tag prefixes a real one-line description, so removing the word leaves the + sentence and buys nothing. Only the tags in `libs/shared/src/ui/` are made redundant by the + new folders; the ones in `apps/**/ui/` are the sole carrier of the layer, because a context + component's title deliberately omits it. + +4. **`libs/beheer` is Domein, not Design System. The docs are wrong; the code is right.** + `stamdata-table-editor.stories.ts` is titled `Domein/Beheer/Stamdata Table Editor`, and that + is correct: `libs/beheer` **is** a bounded context, which lives in `libs/` only because two + apps share it. It is not part of the design system, and it correctly has no layer folders. + + Amend the two doc lines that say otherwise: + + - `CLAUDE.md:213` — drop the words "or `libs/beheer/ui`" from the Design System title rule, + and say that a `libs/beheer/ui` component is titled `Domein/Beheer/`. + - `libs/shared/docs/layers.mdx:13` — drop the parenthetical "(or `libs/beheer/ui`)", and put + `libs/beheer` on the Domein side of the same sentence. + + **Do not change the story title.** This ticket fixes the documentation to match the code. + +5. **No code behaviour changes anywhere.** Two comment words and two doc sentences. + +## Files + +- `libs/shared/src/ui/molecules/async/async.component.ts` +- `libs/shared/src/layout/breadcrumb/breadcrumb.component.ts` +- `CLAUDE.md` +- `libs/shared/docs/layers.mdx` + +## Steps + +1. Add `Molecule: ` to `async.component.ts`'s class header (decision 2). +2. Change `Chrome:` to `Molecule:` in `breadcrumb.component.ts`. +3. Amend `CLAUDE.md:213` and `layers.mdx:13` per decision 4. +4. `git add -A`, then run the acceptance commands. +5. Update this ticket's `Status:` to `done`, the README's RD-28 row to `done`, and that row's + `--full`? column to `yes` (see Verification). +6. Commit all of it together. + +## Acceptance criteria + +Measured against the tree before handover. + +**The real check is the scan itself.** Run it; it must print nothing but `--- done`: + +```bash +for f in $(git ls-files 'libs/shared/src/ui/**/*.component.ts' 'libs/shared/src/layout/**/*.component.ts'); do + d=$(dirname $f) + title=$(grep -ho "title: 'Design System/[A-Za-z]*" $d/*.stories.ts 2>/dev/null | head -1 | sed "s/.*Design System\///") + tag=$(grep -oE "\b(Atom|Molecule|Organism|Template|Chrome|Convenience|Devtool)s?:" $f | head -1 | tr -d ':') + exp=$(echo "$title" | sed 's/s$//') + [ "$tag" = "$exp" ] || echo "MISMATCH $f | title=${title:-NONE} | tag=${tag:-NONE}" +done +echo "--- done" +``` + +The two edits, and the comment that must survive (decisions 1 and 2): + +```bash +git grep -c "Chrome:" -- libs/shared/src/layout/breadcrumb/breadcrumb.component.ts # is 1 -> MUST be 0 +git grep -c "Molecule:" -- libs/shared/src/ui/molecules/async/async.component.ts # is 0 -> MUST be 1 +git grep -c "Convenience:" -- libs/shared/src/ui/molecules/async/async.component.ts # is 1 -> MUST still be 1 +``` + +Nothing was stripped (decision 3): + +```bash +git grep -ohE "\b(Atom|Molecule|Organism|Template|Chrome|Convenience|Devtool)s?:" -- libs apps | wc -l # is 68 before the edits -> MUST be 69 after +``` + +Note: the pre-edit tree measures 68. Decision 2 adds a brand-new `Molecule:` tag to +`async.component.ts` (which had no tag at all), a net +1; the `Chrome:` → `Molecule:` +rename in `breadcrumb.component.ts` is a wash. 68 unchanged would mean decision 2 was +not applied. This is the same class of miss the README's ticket-writing rules log for +RD-11/RD-14/RD-20/RD-27: add up every decision that touches the counted thing before +writing the number. + +The docs changed and the code did not (decision 4): + +```bash +grep -c 'or `libs/beheer/ui` component is titled' CLAUDE.md # is 1 -> MUST be 0 +grep -c '(or `libs/beheer/ui`)' libs/shared/docs/layers.mdx # is 1 -> MUST be 0 +git grep -c "Domein/Beheer/Stamdata Table Editor" -- libs/beheer # is 1 -> MUST still be 1 +``` + +```bash +npm run ci --full # exits 0 +``` + +## Verification + +**`--full` is required, although the README's Order table leaves that column blank.** This +ticket edits `layers.mdx`, and the README's own rule says an `.mdx` edit needs `--full`. Fix the +column to `yes` in the same commit — the same correction RD-18 needed. + +The tag count of 68 is the guard against a well-meant tidy-up. Decision 3 says keep every one. + +## Out of scope + +- Every other layer tag (decision 1). The scan found no other disagreement. +- The `libs/beheer` story title (decision 4). The code is right. +- Layer folders for `libs/beheer`. It is a context; it does not want them. +- The dependency-cruiser ladder rules — RD-29, which depends on this ticket only by ordering. + +## Risks + +- **`async.component.ts` has two comments that look like tags.** Line 154's `Convenience:` is + about the `ASYNC` array export and is accurate. Edit line 45's class header instead, and leave + 154 alone — the acceptance commands check both. +- **Do not "fix" the beheer story title.** The instinct is to make the code match CLAUDE.md; here + CLAUDE.md is the thing that is wrong. +- **Do not delete a tag** (decision 3). The count must hold at 68. diff --git a/docs/project/readable-codebase/README.md b/docs/project/readable-codebase/README.md index 25f225d..87ae2bf 100644 --- a/docs/project/readable-codebase/README.md +++ b/docs/project/readable-codebase/README.md @@ -122,7 +122,7 @@ two. Note that RD-15 exists because 22 abandoned agent worktrees are still on di | RD-25 | `org-template-editor` to `sample-letter.ts` + labels + 2 children | 02 | yes | done | | RD-26 | `letter-canvas`: inline the labels + `letter-line`; keep one disable | 02 | yes | done | | RD-27 | **The layer move:** 33 `git mv` + 28 specifiers + 8 MDX imports | 21 | yes | done | -| RD-28 | Layer-tag fixes + the `libs/beheer` title rule | 27 | | todo | +| RD-28 | Layer-tag fixes + the `libs/beheer` title rule | 27 | yes | done | | RD-29 | The 3 atomic-ladder rules in dependency-cruiser | 27 | | todo | | RD-30 | Archive the finished backlogs (16,300 lines) + an archive README | 01 | | todo | | RD-31 | `ARCHITECTURE.md` section 6a: symbols not lines, 2 dead paths, new names | 03, 08, 16 | | todo | diff --git a/libs/shared/docs/layers.mdx b/libs/shared/docs/layers.mdx index 4bd8d40..2715ad9 100644 --- a/libs/shared/docs/layers.mdx +++ b/libs/shared/docs/layers.mdx @@ -9,8 +9,8 @@ This project is **domain-driven**: the code is organised first by **bounded cont with dependencies pointing inward. The Storybook sidebar is laid out to **be** that architecture, not just document it: **Foundations** (this curriculum) → **Design System** (reusable, domain-free) → **Domein** (the app-local DDD contexts). If a component lives -under a context's `ui/`, it's in Domein; everything else in `libs/shared/ui`/`layout` -(or `libs/beheer/ui`) is Design System. See [Atomic design](?path=/docs/foundations-atomic-design--docs) +under a context's `ui/`, or under `libs/beheer/ui`, it's in Domein; everything else in +`libs/shared/ui`/`layout` is Design System. See [Atomic design](?path=/docs/foundations-atomic-design--docs) for the Atoms → Molecules → Organisms → Templates ladder inside Design System. ## Two apps, two shared libraries diff --git a/libs/shared/src/layout/breadcrumb/breadcrumb.component.ts b/libs/shared/src/layout/breadcrumb/breadcrumb.component.ts index 36c78d8..384e862 100644 --- a/libs/shared/src/layout/breadcrumb/breadcrumb.component.ts +++ b/libs/shared/src/layout/breadcrumb/breadcrumb.component.ts @@ -6,7 +6,7 @@ export interface BreadcrumbItem { link?: string; // omit on the current (last) page } -/** Chrome: breadcrumb navigation, styled for the CIBG titlebar (`.titlebar .title`) — +/** Molecule: breadcrumb navigation, styled for the CIBG titlebar (`.titlebar .title`) — plain links with a chevron `::after` from the CIBG Icons font, current page as an unlinked, bold span. Domain-free — the caller supplies the trail. */ @Component({ diff --git a/libs/shared/src/ui/molecules/async/async.component.ts b/libs/shared/src/ui/molecules/async/async.component.ts index 85db1c9..41d7bbf 100644 --- a/libs/shared/src/ui/molecules/async/async.component.ts +++ b/libs/shared/src/ui/molecules/async/async.component.ts @@ -43,7 +43,7 @@ export class AsyncErrorDirective { } /** - * Renders exactly ONE of loading / empty / error / loaded for a signal-based + * Molecule: renders exactly ONE of loading / empty / error / loaded for a signal-based * resource (e.g. httpResource). Built on a RemoteData tagged union (see * core/remote-data.ts), so the states are mutually exclusive by construction — * the UI can never show two at once ("impossible states"). Unprovided slots