docs: archive the finished backlogs (RD-30)
Two backlog trees are complete: `docs/project/backlog/` (75 files, every WP done) and `docs/project/refactor-backlog-setup/` (the arc before it). Move both under `docs/project/archive/` with `git mv`, so history stays intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them, because it points at the now-archived backlog README. Add `docs/project/archive/README.md`. It states that these trees are historical and names the two directories that are still live. Repoint every inbound reference named in RD-30's Files table: CLAUDE.md, the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the `document-feature` and `new-ssp` skills, and the readable-codebase PLAN, README, and RD-19 ticket. Fix two upward-relative links inside the moved WP files (WP-68, WP-69) that gained a directory level and would otherwise break. Repoint `.prettierignore`'s two agent-prompt exclusions to their new path, so prettier keeps leaving those files' exact wording alone. Mark RD-30 done and check off its acceptance criteria; flip its README row to done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
# RB-24 — `libs/shared/upload` moves into `infrastructure/`/`domain/`/`application/`; the depcruise carve-out is deleted
|
||||
|
||||
Status: **implemented** · 2026-08-27 · Source finding: `06-adr-conformance.md` ADR-C-002 ·
|
||||
`99-backlog.md` RB-24, "Merges" table row for RB-25/26/27
|
||||
|
||||
## What was wrong
|
||||
|
||||
`libs/shared/src/upload/` held five files outside the folder-per-layer convention every
|
||||
other context follows. `upload.adapter.ts` injects `ApiClient` and opens a raw
|
||||
`XMLHttpRequest` — a genuine network adapter — yet sat outside `infrastructure/`.
|
||||
`upload.machine.ts` was the only Elm-style machine (of 9 in the repo) outside a `domain/`
|
||||
folder. The exception was hard-coded into the enforcement itself:
|
||||
`.dependency-cruiser.base.js`'s `apiclient-infrastructure-only` rule read
|
||||
`from: { pathNot: '/infrastructure/|^libs/shared/src/upload/' }` — carved around the
|
||||
violation instead of the violation being fixed, which is why the baseline scan reported 0
|
||||
violations despite this.
|
||||
|
||||
## What changed
|
||||
|
||||
| From `libs/shared/src/upload/` | To |
|
||||
| -------------------------------- | ----------------------------------------------------- |
|
||||
| `upload.adapter.ts` | `libs/shared/src/infrastructure/upload.adapter.ts` |
|
||||
| `upload.machine.ts` + `.spec.ts` | `libs/shared/src/domain/upload.machine.ts` (+ spec) |
|
||||
| `upload-controller.ts` | `libs/shared/src/application/upload-controller.ts` |
|
||||
| `upload-shell.service.ts` | `libs/shared/src/application/upload-shell.service.ts` |
|
||||
|
||||
All five moves used `git mv`. `libs/shared/src/upload/` no longer exists.
|
||||
|
||||
**Import updates.** 24 consumer files import from `@shared/upload/*` (found with
|
||||
`grep -rln "shared/upload" apps libs --include=*.ts`, filtered to exclude the unrelated
|
||||
`@shared/ui/upload/*` component folder, which was not touched). All 24 files' import paths
|
||||
were rewritten to the new locations (30 import statements total, some files import more
|
||||
than one symbol). No export was renamed, no file was split, no logic changed in any of
|
||||
these 24 files beyond the import path string.
|
||||
|
||||
**Within the five moved files**, three had relative imports (`./upload.adapter`,
|
||||
`./upload.machine`) that now crossed layers and were rewritten to `@shared/*` aliases:
|
||||
`upload.adapter.ts`'s import of `DocumentCategory` from `./upload.machine` →
|
||||
`@shared/domain/upload.machine`; `upload-controller.ts`'s imports of `UploadAdapter` and
|
||||
`upload.machine` symbols → `@shared/infrastructure/...` / `@shared/domain/...`;
|
||||
`upload-shell.service.ts` likewise. `upload.machine.spec.ts` needed no import change — it
|
||||
and `upload.machine.ts` moved into the same `domain/` folder together, so its `./upload.machine`
|
||||
import stayed correct; `git diff --find-renames` confirms this file as a 0-line-changed
|
||||
pure rename.
|
||||
|
||||
**The carve-out.** `.dependency-cruiser.base.js`'s `apiclient-infrastructure-only` rule:
|
||||
`from: { pathNot: '/infrastructure/|^libs/shared/src/upload/' }` → `from: { pathNot: '/infrastructure/' }`,
|
||||
comment updated to drop the now-false "(+ shared/upload)" parenthetical. One further
|
||||
consequence: `docs/reference/architecture/dependencies.md`'s "Atomic-layer rules"
|
||||
paragraph stated the same carve-out in prose ("the generated `ApiClient` is a value only
|
||||
inside `infrastructure/` (+ `libs/shared/src/upload`)") — corrected in the same diff, since
|
||||
leaving it would document a rule that no longer exists.
|
||||
|
||||
## A second, real violation the move exposed — fixed, not just reported
|
||||
|
||||
Deleting the carve-out did not by itself make `dep:check` pass. A **separate,
|
||||
pre-existing** rule — `ui-not-infrastructure` (`ui/`+`layout/` may not import
|
||||
`infrastructure/` as a value) — had never fired against `upload.adapter.ts`, because
|
||||
before this move the file's path did not contain `/infrastructure/` at all. Three UI
|
||||
components were injecting `UploadAdapter` directly:
|
||||
`apps/ssp/src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts`,
|
||||
`apps/ssp/src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts`,
|
||||
and `apps/ssp/src/app/brief/ui/org-template.page.ts`. Once `upload.adapter.ts` physically
|
||||
moved into `infrastructure/`, `dep:check` correctly flagged all three:
|
||||
|
||||
```
|
||||
error ui-not-infrastructure: .../registratie-wizard.component.ts → libs/shared/src/infrastructure/upload.adapter.ts
|
||||
error ui-not-infrastructure: .../herregistratie-wizard.component.ts → libs/shared/src/infrastructure/upload.adapter.ts
|
||||
error ui-not-infrastructure: .../org-template.page.ts → libs/shared/src/infrastructure/upload.adapter.ts
|
||||
```
|
||||
|
||||
This is judged in-scope to fix, not a second unrelated finding to merely report, for three
|
||||
reasons. First, the ticket's own DoD is explicit: "if `dep:check` fails after the
|
||||
deletion, the move is incomplete, so fix the move rather than restoring the clause."
|
||||
Second, all three call sites used `UploadAdapter` for exactly one thing —
|
||||
`.contentUrl(documentId)`, a thin wrapper around the adapter's own already-exported,
|
||||
injection-free pure function `uploadContentUrl(documentId)` (its doc comment: "Pure (no
|
||||
injection) so a store can build a letterhead-logo `src` without pulling `ApiClient` into
|
||||
its dependency graph" — written for precisely this case). `apps/ssp/src/app/brief/application/brief.store.ts`
|
||||
already used that pure function directly; the three UI files had independently reinvented
|
||||
`inject(UploadAdapter)` + `.contentUrl()` instead. Third, the fix is mechanical and stays
|
||||
inside the ADR's own established idiom — no new architecture, no touch to any RB-25/26/27
|
||||
target:
|
||||
|
||||
- `libs/shared/src/application/upload-controller.ts` — the object `createUploadController`
|
||||
returns gained one more method, `previewUrlFor(documentId)`, built on the existing pure
|
||||
`uploadContentUrl`. Both wizard components already hold a `createUploadController`
|
||||
instance (`uploadCtl`) for their other upload effects; their `previewUrlFor` field now
|
||||
delegates to `uploadCtl.previewUrlFor` instead of injecting `UploadAdapter` itself.
|
||||
- `apps/ssp/src/app/brief/application/org-template.store.ts` (already injects
|
||||
`UploadAdapter` legitimately — it's application layer) gained one more computed-style
|
||||
field, `previewUrlFor`, on the same pure `uploadContentUrl`. `org-template.page.ts` now
|
||||
reads `this.store.previewUrlFor` instead of injecting `UploadAdapter`.
|
||||
|
||||
No behaviour changed: `uploadContentUrl(id)` and `uploadAdapter.contentUrl(id)` return the
|
||||
identical string (the method is a one-line pass-through to the function), and the
|
||||
`demo-*` short-circuit in the two wizards moved into `upload-controller.ts`'s new method
|
||||
verbatim.
|
||||
|
||||
## Verification
|
||||
|
||||
- **`upload.machine.spec.ts` passes unchanged.** `git diff --find-renames=30%` shows it as
|
||||
a 0-insertion/0-deletion pure rename — no content changed, including its own imports
|
||||
(both files moved into `domain/` together, so its `./upload.machine` relative import
|
||||
needed no edit). No spec content changed anywhere in this ticket.
|
||||
- `npm run dep:check`: **passes for both apps** with the carve-out clause removed —
|
||||
`✔ no dependency violations found (344 modules, 1200 dependencies cruised)` (ssp),
|
||||
`✔ no dependency violations found (226 modules, 588 dependencies cruised)` (behandelportal).
|
||||
- `npm run lint`: clean.
|
||||
- `npm test`: **43+6+24+4 = 77 test files, 274+37+138+23 = 472 tests, all passing**
|
||||
(ssp / behandelportal / shared / beheer).
|
||||
- `npm run build`: both apps build (pre-existing, unrelated warnings about
|
||||
`/cibg-huisstijl/css/huisstijl.min.css` and `/letter.css` not being found at build time —
|
||||
present before this ticket, vendored assets resolved at serve/deploy time, not a
|
||||
regression from this move).
|
||||
- **Coverage, `libs/shared/src/domain/`** (`npm run test:coverage` narrowed to `shared`):
|
||||
the folder now includes `upload.machine.ts` at 98.82% statements / 91.8% branches / 100%
|
||||
functions / 98.36% lines (84/85, 56/61, 28/28, 60/61) — the "well-specced machine" ADR-C-002
|
||||
predicted landing in a folder the baseline reported at "0% spec reach across 3 files"
|
||||
(`capability.ts`, `feature-flag.ts`, `role.ts`, which this ticket does not touch and which
|
||||
remain unspecced — that gap is pre-existing and out of this ticket's scope).
|
||||
|
||||
## Non-TypeScript references to the old path — findings
|
||||
|
||||
Checked `.storybook-ssp/`, `.storybook-behandelportal/`, `angular.json`, no vitest config
|
||||
file exists separately (Angular's builder owns test config), both `.dependency-cruiser.*.js`
|
||||
files, and `libs/shared/docs/*.mdx`.
|
||||
|
||||
- **Storybook config, angular.json, dependency-cruiser app configs**: no reference to
|
||||
`shared/upload` or `libs/shared/src/upload` in any of these. Nothing to change.
|
||||
- **`.dependency-cruiser.base.js`**: the one real reference — the carve-out clause itself,
|
||||
deleted (see above).
|
||||
- **`docs/reference/architecture/dependencies.md`**: one prose reference to the same
|
||||
carve-out, corrected in this diff (see above) since it directly describes the rule this
|
||||
ticket edits.
|
||||
- **`libs/shared/docs/*.mdx`**: no `.mdx` file references `libs/shared/src/upload` or
|
||||
`@shared/upload`. `atomic-design.mdx` and `machines.mdx` mention `upload.machine.ts` and
|
||||
`shared/ui/upload/...` by filename/short-path only, never the full old directory path —
|
||||
both remain accurate (the filename didn't change; `ui/upload/` is the untouched sibling
|
||||
folder).
|
||||
- **`apps/ssp/src/locale/messages.xlf`, `messages.en.xlf`, `apps/behandelportal/src/locale/messages.en.xlf`**:
|
||||
each carries a handful of `<context context-type="sourcefile">src/app/shared/upload/upload.machine.ts</context>`
|
||||
/`upload.adapter.ts` annotations — auto-generated by Angular's `$localize` extractor,
|
||||
informational only (they tell a translator where a string originated; they are not
|
||||
read by the build or by `i18nMissingTranslation`). Left as-is: regenerating them is
|
||||
`npm run extract-i18n`'s job for the source-locale file and does not touch the
|
||||
hand-maintained `messages.en.xlf` translations at all, and this ticket's scope is the
|
||||
move plus import updates, not a translation-tooling refresh. They will self-correct
|
||||
the next time `extract-i18n` runs for an unrelated reason.
|
||||
- **`docs/project/backlog/*.md`, `docs/project/refactor-backlog-setup/refactor-backlog/*.md`**:
|
||||
several planning/history documents (WP-25, WP-74, the baseline scan, `02-testability.md`,
|
||||
`06-adr-conformance.md`, `07-bio2-compliance.md`, `99-backlog.md`, `rb-01.md`, `rb-09.md`)
|
||||
reference the old path — expected, since most of them describe or cite the violation
|
||||
this ticket resolves, as history. Not edited, except `99-backlog.md`'s RB-24 status cell
|
||||
(see below).
|
||||
|
||||
## What RB-25/26/27 now find where
|
||||
|
||||
- **RB-25** (`UPLOAD_TRANSPORT` injection token, replacing `inject(KeepaliveTransport)`):
|
||||
`KeepaliveTransport` and `UploadShellService` are both now in
|
||||
`libs/shared/src/application/upload-shell.service.ts` (unchanged content, new path). The
|
||||
token belongs in `application/` alongside them — nothing about the token's shape or
|
||||
location changes because of this move.
|
||||
- **RB-26** (`planFileSelection` in `upload.machine.ts`): the machine is now
|
||||
`libs/shared/src/domain/upload.machine.ts`. `createUploadController`'s `onFileSelected`
|
||||
callback — the accept/reject decision RB-26 targets — is in
|
||||
`libs/shared/src/application/upload-controller.ts` (also renumbered, otherwise
|
||||
unchanged; it also now exports one more method, `previewUrlFor`, added by this ticket —
|
||||
see above). RB-26 should extend `upload.machine.ts` in its new location; no import path
|
||||
in that file needs touching beyond what this ticket already did.
|
||||
- **RB-27** (`uploadOutcome(status, responseText)` out of the XHR closure): the XHR closure
|
||||
is in `libs/shared/src/infrastructure/upload.adapter.ts`'s `xhrUpload` method — same
|
||||
file, same method, new path only. `load`/`error`/`abort` handlers, `parseError`, and
|
||||
`genericError` are all still exactly where they were, just under `infrastructure/`.
|
||||
|
||||
## `npm run ci`
|
||||
|
||||
Result and step count reported in the final answer.
|
||||
Reference in New Issue
Block a user