fix: layer-tag mismatches + libs/beheer title rule (RD-28)

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 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-05 08:26:47 +02:00
co-authored by Claude Sonnet 5
parent 57fc72f254
commit d4c5a9450a
6 changed files with 163 additions and 9 deletions
+4 -4
View File
@@ -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/<Atoms|Molecules|Organisms|Templates|Devtools>/<Name>`;
a component in an app context's `ui/` is titled `Domein/<Context>/<Name>` — full stop,
regardless of which atomic layer it is (a context organism doesn't get its own
`Organisms/` bucket).
component is titled `Design System/<Atoms|Molecules|Organisms|Templates|Devtools>/<Name>`;
a component in an app context's `ui/`, or in `libs/beheer/ui`, is titled
`Domein/<Context>/<Name>` — full stop, regardless of which atomic layer it is (a context
organism doesn't get its own `Organisms/` bucket).
## Conventions
@@ -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/<Name>`.
- `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.
+1 -1
View File
@@ -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 |
+2 -2
View File
@@ -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
@@ -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({
@@ -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