Files
ehoandClaude Opus 5 12f17d9d73 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>
2026-09-08 23:00:38 +02:00

5.5 KiB

WP-29 — Stamdata beheer editor (low-code, PR-emitting)

Status: done (0e77faf) Phase: follow-on — ADR-0004 realization (not part of the 2026-07-02 showcase audit)

Why

ADR-0004 (stamdata-as-code) named a future low-code editor that commits a PR as its mitigation for "a non-developer may need dev assistance to edit C#", and floated a data-file format as the alternative to typed-C# constants when hand-editing ergonomics outweigh maximal compile-time safety. This WP realizes both: an admin-only maintenance editor that reads the stamdata catalog, edits rows in a grid, and produces the edited JSON data-file the admin drops into the repo — the existing CI build + StamdataValidationTests stay the authority. No production database, no runtime write path (ADR-0004 unchanged).

Read first

  • ADR-0004 (docs/reference/architecture/0004-stamdata-as-code.md) — the model this obeys.
  • ADR-0001 (BFF-lite + decision DTOs) — the endpoints are screen-shaped, admin-gated reads.
  • src/app/brief/** (WP-23/26) — the org-template admin editor is the closest prior art (root store + machine + capability guard + admin role).

Decisions (pre-made, don't relitigate)

  • Read-only endpoints only. GET /stamdata (catalog) and GET /stamdata/{table}?peildatum= (schema + rows). No POST/PUT/DELETE — the edit lands as a reviewed PR, not a write. The stamdata:edit capability / CanEditStamdata gate the reads (naming is the enforce-twin of a future edit capability; deliberate).
  • Generic, schema-driven. One endpoint pair + one grid editor serve every table. Columns are reflected from the typed record (StamdataTable.Of<T>); the FE renders inputs by column type (date/number/enum/text). A new table is one line in StamdataCatalog — no new endpoint, UI, or test. (Catalog of one today; this is the shape ADR-0004 prescribed.)
  • Data-file format for professions. professions.json (embedded resource) replaces the hardcoded C# dictionary, deserialized into ProfessionMapping. This trades compile-time value checking (gate #1) for editor ergonomics — the value gate becomes StamdataValidationTests (gate #2), exactly the trade-off ADR-0004's consequences listed.
  • Valid-time. geldigVan/geldigTot (half-open [van, tot)); a table is temporal iff it has both columns. peildatum previews "which rows applied on date X". Professions.ByProgram preserves pre-valid-time behaviour by filtering to rows active today.
  • Apply path = download → PR. The editor's download() serializes the draft to {table}.json; the admin commits it. mutation-command is intentionally not used.
  • Admin-only, resource-independent authz — same shape as org-template management (role IS the decision), denials audited (no PII).

Files

  • Backend: backend/src/BigRegister.Api/Stamdata/{StamdataCatalog,StamdataTable,StamdataFile,ProfessionMapping}.cs (new), Professions.cs (now loads the data-file), professions.json (new), BigRegister.Api.csproj (embed Stamdata\*.json); Program.cs (two GET endpoints + StamdataAdmin gate), Contracts/Dtos.cs (3 DTOs), Domain/Authorization/Authz.cs (stamdata:edit + CanEditStamdata); tests StamdataEndpointTests.cs (new), StamdataValidationTests.cs (generic Every_catalog_table_is_valid).
  • Frontend: src/app/beheer/** (contracts / domain + specs / infrastructure + spec / application / ui + organism story); app.routes.ts (guarded lazy route), shared/domain/capability.ts + shared/infrastructure/me.adapter.ts (stamdata:edit), eslint.config.mjs (beheer boundary rules), tsconfig.json (@beheer/* alias); regenerated backend/swagger.json + src/app/shared/infrastructure/api-client.ts.

Acceptance criteria

  • GET /stamdata and GET /stamdata/{table} return admin-only (403 + audit otherwise).
  • professions served from professions.json; ProfessionMapping typed; behaviour of Professions.ByProgram unchanged for all-current rows.
  • Generic build gate: Every_catalog_table_is_valid covers every catalog table (keys non-blank, no overlapping validity, well-formed windows).
  • beheer/stamdata route capability-guarded; page shows denial for non-admin; grid renders from reflected schema; edits update dirty/change-count; download() yields a valid {table}.json; peildatum before 2000-01-01 → zero rows.
  • Full gate GREEN both sides; npm run gen:api leaves no drift; new stamdata story passes axe.

Verification

cd backend && dotnet test && dotnet format --verify-no-changes; npm run lint && npm run check:tokens && npm test && npm run build && npm run build-storybook && npm run test-storybook:ci; npm run gen:api && git diff --exit-code -- backend/swagger.json src/app/shared/infrastructure/api-client.ts. Live: /beheer/stamdata?role=admin renders + edits + downloads; without ?role=admin denies.

Out of scope

  • Runtime persistence of edits (would contradict ADR-0004) — the download-to-PR path is the design.
  • Migrating PolicyQuestions / document-categories into the catalog (each is a later one-liner).
  • A write-back "commit a PR on the admin's behalf" integration (the download() seam is where it slots in).

Risks

  • Data-file weakens compile-time value safety for professions — mitigated by StamdataValidationTests running in CI (a bad value fails the build, never prod).
  • Generic reflection assumes the record's first property is the key and camelCase JSON names — documented conventions in StamdataTable; covered by the endpoint schema test.