Compare commits
2
Commits
57fc72f254
...
b6bd7eea5a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6bd7eea5a | ||
|
|
d4c5a9450a |
@@ -109,6 +109,29 @@ module.exports = function buildConfig(contextAllowed, appName, tsConfigFileName)
|
||||
},
|
||||
},
|
||||
|
||||
// --- Atomic ladder within libs/shared/src/ui (folder = layer, CLAUDE.md decision 2) ---
|
||||
{
|
||||
name: 'atoms-compose-nothing-above',
|
||||
comment: 'An atom composes nothing above it — no molecule or organism. See CLAUDE.md §2.',
|
||||
severity: 'error',
|
||||
from: { path: '^libs/shared/src/ui/atoms/' },
|
||||
to: { path: '^libs/shared/src/ui/(molecules|organisms)/' },
|
||||
},
|
||||
{
|
||||
name: 'molecules-below-organisms',
|
||||
comment: 'A molecule composes nothing above it — no organism. See CLAUDE.md §2.',
|
||||
severity: 'error',
|
||||
from: { path: '^libs/shared/src/ui/molecules/' },
|
||||
to: { path: '^libs/shared/src/ui/organisms/' },
|
||||
},
|
||||
{
|
||||
name: 'design-system-not-layout',
|
||||
comment: 'The design system (ui/) does not depend on layout/ templates. See CLAUDE.md §2.',
|
||||
severity: 'error',
|
||||
from: { path: '^libs/shared/src/ui/' },
|
||||
to: { path: '^libs/shared/src/layout/' },
|
||||
},
|
||||
|
||||
{
|
||||
name: 'no-testing-in-production',
|
||||
comment:
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,143 @@
|
||||
# RD-29 — Enforce the atomic ladder in dependency-cruiser
|
||||
|
||||
Status: done
|
||||
Source: PLAN.md 4c
|
||||
|
||||
## Why
|
||||
|
||||
RD-27 made the ladder **expressible**: `libs/shared/src/ui/` is now `atoms/`, `molecules/`,
|
||||
`organisms/`. Nothing yet makes it **enforced** — an atom importing an organism compiles, lints,
|
||||
tests and ships.
|
||||
|
||||
This ticket adds the three rules. It is the payoff for the move, and PLAN calls it "the real
|
||||
prize".
|
||||
|
||||
## Read first
|
||||
|
||||
- `.dependency-cruiser.base.js:38` — `anyRoot`, and the atomic-layer rules that follow it. The
|
||||
new rules go beside them.
|
||||
- `.dependency-cruiser.base.js:93-99` — `ui-not-infrastructure`, the closest existing rule in
|
||||
shape.
|
||||
- PLAN.md 4c.
|
||||
|
||||
## Decisions (pre-made, don't relitigate)
|
||||
|
||||
1. **Three rules, in `.dependency-cruiser.base.js`, forbidding upward edges only:**
|
||||
|
||||
| Rule name | from | to |
|
||||
| ----------------------------- | -------------------------------- | --------------------------------------------- |
|
||||
| `atoms-compose-nothing-above` | `^libs/shared/src/ui/atoms/` | `^libs/shared/src/ui/(molecules\|organisms)/` |
|
||||
| `molecules-below-organisms` | `^libs/shared/src/ui/molecules/` | `^libs/shared/src/ui/organisms/` |
|
||||
| `design-system-not-layout` | `^libs/shared/src/ui/` | `^libs/shared/src/layout/` |
|
||||
|
||||
`severity: 'error'`, each with a `comment` naming CLAUDE.md decision 2, matching the house
|
||||
style of every other rule in that file.
|
||||
|
||||
Literal `libs/shared/src/...` paths, not `anyRoot`: only `libs/shared` has layer folders. The
|
||||
rules are evaluated once per app cruise, which is harmless — the same tree, the same answer.
|
||||
|
||||
2. **Never "atoms are leaves". Same-layer edges are legitimate and four exist today:**
|
||||
|
||||
```
|
||||
atoms/masked-value -> atoms/button
|
||||
molecules/review-section -> molecules/data-block
|
||||
molecules/task-list -> molecules/choice-list
|
||||
molecules/task-list -> molecules/choice-link
|
||||
```
|
||||
|
||||
A rule forbidding an atom from importing any atom would fail on the first of these. Forbid
|
||||
the layers **above**, nothing else.
|
||||
|
||||
3. **No `pathNot` exemption for specs and stories. This corrects PLAN.**
|
||||
|
||||
PLAN says the exemption is needed "because `async.stories.ts` composes `skeleton`".
|
||||
Measured: `async` is a molecule and `skeleton` is an atom, so that edge points **downward**
|
||||
and is legal under decision 1. The example does not justify an exemption.
|
||||
|
||||
Measured further: **zero upward edges exist anywhere in `libs/shared/src/ui/`, in production
|
||||
code, specs and stories alike.** Nothing needs the exemption today.
|
||||
|
||||
So leave it out. An exemption that nothing needs is dead flexibility, and it silently widens
|
||||
the rule the moment someone does write an upward import in a story. If a story ever earns one
|
||||
— an atom's story demonstrating it inside a molecule is the plausible case — add the
|
||||
exemption then, with that real example in the comment.
|
||||
|
||||
4. **All three land green immediately.** This ticket adds enforcement and changes no application
|
||||
code. If `dep:check` fails after adding them, the rule is written wrongly — do not "fix" the
|
||||
application to satisfy it without saying so.
|
||||
|
||||
5. **Dependency-cruiser, not ESLint.** It is where every other boundary rule in this repository
|
||||
lives, and it emits the architecture graph. Do not add an ESLint variant.
|
||||
|
||||
## Files
|
||||
|
||||
- `.dependency-cruiser.base.js`
|
||||
|
||||
Nothing else. No application code changes.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Add the three rules per decision 1, beside the existing atomic-layer rules.
|
||||
2. Run `npm run dep:check` — it must pass.
|
||||
3. **Prove each rule bites** (see Verification). This is the point of the ticket.
|
||||
4. `git add -A`, then run the acceptance commands.
|
||||
5. Update this ticket's `Status:` to `done` and the README's RD-29 row to `done`.
|
||||
6. Commit all of it together.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
Measured against the tree before handover.
|
||||
|
||||
```bash
|
||||
git grep -c "atoms-compose-nothing-above" -- .dependency-cruiser.base.js # is 0 -> MUST be 1
|
||||
git grep -c "molecules-below-organisms" -- .dependency-cruiser.base.js # is 0 -> MUST be 1
|
||||
git grep -c "design-system-not-layout" -- .dependency-cruiser.base.js # is 0 -> MUST be 1
|
||||
```
|
||||
|
||||
The rules pass on the current tree, and no application file changed (decisions 4 and 5):
|
||||
|
||||
```bash
|
||||
npm run dep:check # exits 0
|
||||
git diff --cached --name-only | grep -v '^docs/' # MUST list only .dependency-cruiser.base.js
|
||||
```
|
||||
|
||||
```bash
|
||||
npm run ci # exits 0
|
||||
```
|
||||
|
||||
`--full` is not required: no story, no `.mdx`, and nothing under `libs/shared/src/ui/**` is
|
||||
edited. The Order table's blank column is correct here.
|
||||
|
||||
## Verification
|
||||
|
||||
**A rule that matches nothing is worse than no rule, because it reads as protection.** Prove
|
||||
each of the three actually fires, one at a time:
|
||||
|
||||
1. Add a temporary import that violates it — for example, in
|
||||
`libs/shared/src/ui/atoms/button/button.component.ts`, import
|
||||
`@shared/ui/molecules/data-row/data-row.component`.
|
||||
2. Run `npm run dep:check` and confirm it fails, naming that rule.
|
||||
3. Revert the temporary import.
|
||||
|
||||
Do this for all three. Report which rule name each violation produced. **Do not commit any
|
||||
temporary import** — `git status` must be clean of them before you commit, and the acceptance
|
||||
command above checks that only the config file changed.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Layer folders or ladder rules for `libs/beheer`. It is a bounded context, not a design system
|
||||
(RD-28 settled this).
|
||||
- `layout/`'s internal structure. It is sanctioned to hold several layers.
|
||||
- Any rule about app contexts' own `ui/` folders. They have no layer folders by design — a
|
||||
context organism does not get its own bucket.
|
||||
- Changing an existing dependency-cruiser rule.
|
||||
|
||||
## Risks
|
||||
|
||||
- **Do not add the spec/stories exemption out of habit** (decision 3). Two other rules in the
|
||||
file have one; these three do not need it, and the ticket explains why.
|
||||
- **Forbid upward, not sideways** (decision 2). Four same-layer edges exist and are correct.
|
||||
- **`from` must not match the layer it forbids.** `design-system-not-layout` starts at
|
||||
`^libs/shared/src/ui/`, which covers all three layer folders; that is intended.
|
||||
- **If `dep:check` goes red, suspect the rule, not the code** (decision 4). Nothing in the tree
|
||||
violates the ladder today.
|
||||
@@ -122,8 +122,8 @@ 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-29 | The 3 atomic-ladder rules in dependency-cruiser | 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 | | done |
|
||||
| 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 |
|
||||
| RD-32 | `fp-tea-atomic-design.md`: 11 broken paths + the broken anchor | 27 | | todo |
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user