Files
atomic-design-poc/docs/project/refactor-backlog-setup/refactor-backlog/_status.md
T
ehoandClaude Opus 5 03c6e09306 docs(backlog): CD batch 5 complete
All seven tickets RB-24 to RB-30 merged, one commit per ticket. Records the
actual wave split, since the backlog's own depends-on column missed that
RB-24 rewrites imports in two of RB-28's target files.

RB-24 expanded its own scope to fix a second, real boundary violation that
deleting its acceptance criterion exposed, reviewed and accepted. Two more
findings were shown stale or overstated, on top of the nine from earlier
batches. RB-26 and RB-27 both correctly declined part of their own ticket's
proposed shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:55:39 +02:00

47 KiB

Agent run status

Agent Status Last module processed Last updated Notes
baseline complete all (18 FE + 7 BE modules) 2026-08-26 0 layering violations. 11 observations BL-001..BL-011. Thresholds for Phase 1 in §9 — agent 01's [N] lines = 40 (TS fn) / 60 (C# method) / 400 (file). Read BL-001, BL-002 and BL-004 before filing complexity, duplication or coverage findings.
readability skipped n/a 2026-08-27 skipped — deliberate. BL-001: 23 of the 25 TS functions over CC 10 are reducers / parse* boundaries / validate*, all mandated house idioms; TS fn-length p99 is 34 with only 2 functions over 75 lines. Little left for this agent to find that is not a false positive. Revisit if the CC>10 population grows outside those three shapes.
testability complete all 24 modules 2026-08-26 9 findings TE-001..TE-009; 15 modules explicit "no findings". Corrected BL-004 — libs/shared/domain and libs/beheer/contracts are pure type declarations, 0% is unimprovable (amendment note in 00-baseline.md §10). TE-009 is also a latent correctness bug (dead ActiveOn branches). Acquitted createDraftSync, httpClientFetch, Contracts/Mappers.cs in writing.
ddd-hexagonal skipped n/a 2026-08-27 skipped — deliberate. FE layering is clean (baseline §6: 0 violations, healthy instability gradient, kernel I=5% vs contexts I>=83%); backend Domain/ is verified EF/ASP-free. The agent may only extend existing hexagonal structure, and the one real target (Program.cs) has no Features/ folder to extend — agent 04 already filed that as out-of-mandate OOM-A.
cqrs-light complete all (16 FE + 6 BE modules) 2026-08-26 7 findings CQ-001..CQ-007; 12 modules clean. Corrected BL-007 (see the amendment note in 00-baseline.md §10) and found 3 mutations the baseline missed. Program.cs → Features/+handlers filed as out-of-mandate OOM-A, not a ticket; CQ-006 is its prerequisite. bhp/behandeling named the reference implementation — do not "clean it up".
bdd skipped n/a 2026-08-27 skipped — deliberate. No BDD tooling present, and the prompt forbids proposing any; it self-reduces to a single structural note. gen:behaviour-spec already extracts behaviours from spec names into libs/shared/docs/behaviour-spec.mdx, which covers the intent.
adr-conformance complete all 6 ADRs 2026-08-26 11 findings: 6 code, 5 ADR-fix (architect approval required). Sharpened BL-002 — Principal was never built, so ADR-0002 was untested not falsified (amendment note in 00-baseline.md §10). ADR-0005 fully conformed. Gates: ADR-C-005→ADR-C-004; ADR-C-003 gates contracts/ cleanup. 3 ADR-fixes need a matching CLAUDE.md correction in the same diff.
bio2-compliance complete all modules + 7 control areas 2026-08-27 20 findings BIO-001..BIO-020 (12 defect now, 8 production gate). High: BIO-008 BSN concatenated into the authz audit Resource (Program.cs:674, verified); BIO-004 GET /uploads/{documentId}/content has no authz at all (verified). Answered agent 06's handoff as BIO-002 — a production behandelportal build resolves to the seeded zorgverlener, failing closed on backoffice caps but open on citizen-scoped ones incl. CanRevealBigNummer. Carries the mandatory "compliance review required" list: 16 rows over agents 02/04/06. Also a pre-production checklist (~25 boxes).
consolidation complete (approved) all inputs 2026-08-27 HALTED for human approval (per spec) — 99-backlog.md written, nothing implemented. 33 open tickets RB-01..RB-33 + 5 ADR-fixes + 5 already-shipped, from 47 findings. RB-01 (no authz on upload content) and RB-02 (BSN in the audit Resource) sort above all structural work. Gate relaxed to the 4 agents that ran; a "Coverage of this backlog" note records what the 3 skips leave unowned. Caught two orchestrator errors: CQ-002 is NOT fixed (verified — ApplicationsStore.cancel/AdminCasesStore.delete still swallow errors → RB-20), and CQ-004 shipped with half its compliance criterion unmet (no audit row on PUT /admin/flags/{key}, verified → RB-07, which blocks signing ADR-C-009). OOM-D: re-run the baseline before using it to verify any ticket — ADR-C-006 and BL-008 moved it. Approved 2026-08-27; HALT lifted.

Phase 3 — implementation

CD batch Tickets Status Notes
1 RB-01, RB-02, RB-03, RB-04, RB-05, RB-06 complete Six commits on refactor/adr-c-006-shared-route-guards, one per ticket, each with implementation/rb-0N.md. npm run ci green. Every ticket left a test that was verified red without its fix. Carryover: RB-01's residual belongs to RB-09 (the content endpoint is reached by a plain browser navigation with no identity header — BIO-002); Pii.MaskTail now lives in Domain/People/Pii.cs, use it in RB-11 rather than hand-rolling a second masker; RB-06 additionally deleted SubmissionRules.RejectRegistratie (judgement call, recorded).
2 RB-07, RB-08, RB-09, RB-10, RB-11 complete All five merged; npm run ci green on the fixed gate (see below). RB-07 unblocks signing ADR-C-009 and closes CQ-004's outstanding half. RB-09 makes Production throw at startup when no real IIdentityProvider exists — note an environment that is neither Development nor Production (e.g. Staging) still fails fast, but at GetRequiredService, with a worse message. RB-10 landed parseStoredSession twice, once per app, deliberately — TE-001/BL-002 say extract-to-shared contradicts ADR-0002; RB-13 differentiates them. RB-11 corrected a factual error in BIO-012 (the proefbrief error mapping was inlined, not already a separate function) and left the step-up as a literal moved one layer up to its only caller — BIO-006(c) stays a production gate. RB-01's residual is still open and is not solved by RB-09: the upload-content link is a plain browser navigation with no credential.
3 RB-12, RB-13, RB-14, RB-15, RB-16, RB-17 complete All six merged; npm run ci green (14 steps — RB-14 added one — backend 260/260). RB-12 rejected the ticket's binary framing: of 47 routes only 16 use one of the five admin wrappers; of the remaining 31 only 10 are genuinely public, the other 21 are ownership-scoped inline (ctx.Zorgverlener()/ctx.Caller()) or use another mechanism. The allow-list therefore carries a reason per route, not a blanket "public" label. Known limitation: detection is .Gate("XAdmin") metadata declared at mapping time — a declaration, not a derivation, so it cannot catch a route that declares a gate it does not have. This is RB-19's safety net; read rb-12.md before starting RB-19. RB-13 measured ssp/auth ↔ bhp/auth duplication at 32 lines each side, down from 168 (backlog expected <40); each app holds only its own Principal variant, which is ADR-C-004's own proposed resolution, and ADR-0002's "Known debt" section became an amendment. RB-14 could not be built as written — dotnet list package --vulnerable exits 0 on a High advisory (verified), so a bare - run: would have been a gate that enforces nothing; scripts/dotnet-audit.sh matches the output instead and is shared by ci.yml and ci-local.sh. RB-15 used a third environment name (Staging) in its test, since RB-09 makes Production fail to boot at all.
4 RB-18..RB-23 complete All six merged, one commit per ticket, each on its own merge. npm run ci green on the combined tree after every merge (14 steps, exit 0). Ran as three waves, because three of the six touch Program.cs: A = RB-18/20/21/22 in parallel (no file overlap), B = RB-23 after RB-22 (expand/contract), C = RB-19 alone and last, so it reordered final content. Two tickets were incomplete, both reported rather than worked around. RB-23 found BriefStore.GetOrCreate had a second, unmentioned call site — GET /brief/preview — so the split forced that endpoint to change too or the file would not compile; it got the same Get + 404 treatment. RB-18's real scope is one endpoint, not the nine BIO-018's stale line numbers implied: Submit has exactly one call site (POST /change-requests). RB-22 deliberately left the runResult idiom for BriefAdapter.load(): it hand-rolls try/catch to read the HTTP status, because runResult folds the error to a string and structurally cannot carry a 404. It still reuses the shared problemDetail mapper and models the outcome as the BriefLoadFailure union, not a sentinel string. Accepted — reviewed the diff before merging. Its once-only bound is stronger than the ticket asked: recoverFromMissingBrief never re-enters load(), so CQ-007's retry loop is absent, not merely capped. RB-22 mispredicted one thing harmlessly: it expected the regenerated client to parse a ProblemDetails 404, but Results.NotFound() declares no body so it throws a plain SwaggerException (matching the 17 other bare-404 endpoints). isHttpNotFound reads only .status, so it tolerated both — the pair held because the FE half was written defensively. RB-19 verification, recorded because RB-12's test cannot do it: RB-12 proves a .Gate(...) marker is present, not that it matches the wrapper the handler calls (its own stated declaration-vs-derivation limit). Checked centrally instead — the sorted list of all 47 route strings is identical before and after, and so is every (route, .Gate marker, wrapper actually called in the handler) triple, with zero gate/handler mismatches. gen:api produced an ordering-only diff in swagger.json + api-client.ts (only the two moved and documented endpoints changed position; the other three moves are .ExcludeFromDescription()), committed rather than left to fail the drift job.
5 RB-24..RB-30 complete All seven merged, one commit per ticket. npm run ci green on the combined tree after every merge. Ran as three waves, not the two the backlog implied: RB-24 rewrites imports in brief.store.ts and org-template.store.ts, which are two of RB-28's three targets — a dependency the backlog's "25/26/27 depend on 24" note never mentioned. A = RB-24 alone (the move), then A2 = RB-29 + RB-30 in parallel (backend, no file overlap with the move or each other), B = RB-25 + RB-26 + RB-28 in parallel once RB-24 landed, C = RB-27 alone last, since it depends on RB-25's transport token. RB-24 expanded its own scope, correctly. Deleting the dependency-cruiser carve-out — the ticket's own acceptance criterion — exposed a second, real ui-not-infrastructure violation the old path had hidden: three UI components injected UploadAdapter for nothing but a one-line wrapper over its own exported pure function. The dispatch prompt said to report a second violation, not fix it; the agent judged this one was on the critical path (dep:check cannot pass with the carve-out gone otherwise) and fixed it minimally, reusing the existing pure function. Reviewed before merging — sound. Two more findings were shown to be stale or overstated, on top of the two ADR-fixes found wrong and RB-18/RB-23's incompleteness from batch 4 — nine total now. RB-25 found TE-003 overstated its own blocker: of the four methods named, only upload() and cancel() were actually unfakeable through the missing token — delete()/pollReturning() already went through the exported UploadAdapter. RB-28 found TE-006 already false at the time it was written: brief.store.spec.ts already had a previewLetter success test via jsdom's spyable URL/window stubs, contradicting the finding's "cannot test the success case" claim — the overall three-site diagnosis still held and was shipped as instructed. RB-26 made one real design call, reviewed before merging: planFileSelection must return UploadMsg[] per its literal signature, but an accepted file's real localId needs crypto.randomUUID(), which the ticket itself keeps in the controller. It ships a placeholder localId: '' discriminated by .type alone and never dispatched — verified the index alignment holds for both the multiple-rejection short-circuit and the per-file path. RB-27 left one thing unextracted, correctly: TE-005 lumped abort-vs-error disambiguation into the same extraction as uploadOutcome, but abort fires on a different event with no status/responseText at all — it structurally cannot fit the proposed signature. Left in place as a one-line ternary. The optional currentScenario() move into KeepaliveTransport was also correctly declined — it would have crossed into upload-shell.service.ts, outside this ticket's stated single-file scope. End state of libs/shared/upload (now split across proper layers): every layer that can hold pure logic has one and is spec'd — upload.machine.ts (domain, planFileSelection), upload-shell.service.ts (application, the UPLOAD_TRANSPORT seam), upload-controller.ts (application), upload.adapter.ts (infrastructure, uploadOutcome). Only the XHR/DOM boundary itself stays untested by design — TE-005 was explicit that abstracting XMLHttpRequest away is not wanted, since the file documents why XHR (not fetch) is required.
6 RB-31, RB-32, RB-33 not started
ADR-fix ADR-C-001, ADR-C-003, ADR-C-007, ADR-C-009 complete All four signed and landed by the architect on 2026-08-27, in one commit; doc-only, no code touched. Three carried the mandatory matching CLAUDE.md edit in the same diff (§4 twice, §2 once). ADR-C-009's RB-07 gate was satisfied first — all four clauses of its new test were verified against both OrgTemplateStore and FeatureFlagStore before signing, so the ADR does not ratify a control the code lacks. Two findings were wrong and are corrected in the notes: ADR-C-001 told us to keep an out-of-scope bullet reading "SessionStore is in-memory", which RB-10/RB-13 made false (the session now persists to localStorage; only multi-tab sync is still open), and ADR-C-007 flagged only the .alert half of ADR-0003's point 4 — its "header/side-nav use .nav + a local blue bar" clause is equally false (site-header composes the vendored .titlebar/.logo__*). ADR-C-007 also over-listed one path: public/cibg-huisstijl/ never moved. ADR-C-003's open question was decided explicitly — the 4 hand-written contracts/*.dto.ts stay, because NSwag emits every property optional and flattens RegistrationStatusDto into five optional strings, which would make an illegal state representable (CLAUDE.md §3). Gates released: ADR-C-003 (contracts cleanup) and ADR-C-009 (a third runtime-editable surface). Still pending, untouched: ADR-C-008 → RB-32 — 9 CIBG-GAP markers vs 8 register rows, missing row is language-switcher.

Standing caveat for every batch: dotnet test reports one failure, OpenZaakIntegrationTests.Admin_cases_returns_the_seeded_zaak_mapped_through_real_HTTP_and_JWT, which needs a live OpenZaak container. It fails identically on a stashed tree — it is not caused by any of these tickets. npm run ci does not run it.

Gate integrity — read before trusting any "ci green" in this file

scripts/ci-local.sh chained six of its steps as cmd1 && cmd2 under set -e. Bash exempts every command of an AND-OR list except the last from errexit, so a crash in cmd1 was swallowed: the paired check never ran and the script still printed "local CI passed". Verified directly — bash -c 'set -e; false && true; echo hi' prints hi and exits 0.

This hid a real gen:api crash introduced by RB-09 (dotnet swagger's design-time host defaults to Production, which RB-09 made throw). .github/workflows/ci.yml runs each step as its own - run: and would have caught it, so the local gate was strictly weaker than the remote one — the opposite of its purpose. The worst instance was ng build ssp --localize && ng build behandelportal --localize: a missing English translation in ssp could not fail the run.

Fixed in build: stop ci-local.sh swallowing the first half of every paired step. Every "ci green" recorded for batch 1 and for RB-07/RB-10/RB-11 predates that fix and is therefore weaker than it reads; the batch-2 completion run above is the first one made on the honest gate (13/13 steps, exit 0). Nothing has since been found wrong with batch 1, but it has not been re-verified under the fixed gate either.

Dispatching implementation agents — what actually goes wrong

Batches 2 and 3 ran tickets as parallel agents in git worktrees. Six of seven agent-runs hit at least one of these. Put all of it in the prompt.

  1. The worktree base is not reliable. Four of the six agents were handed a worktree branched from a stale ancestor — batch 3 was three for three, all landing on ae7781e, an unrelated lineage missing every RB ticket and this backlog directory. Make step zero: git log --oneline -8, confirm a named expected commit, git merge the target branch if absent, and report which it was. The one agent that was not told to do this found out by luck.

  2. Agents park on background tasks. Two agents in batch 2 ran npm run ci in the background, then ended their turn waiting for a notification that never usefully arrived; one finished its work twice and never committed it. Ban run_in_background and Monitor, and say explicitly to pass the Bash tool's own timeout: 600000 — a batch-3 agent still auto-backgrounded because "run it in the foreground" alone does not defeat the 120s default.

  3. git checkout <file> destroys the work. Agents verify a test is red by undoing the fix; restoring it with git checkout reverts the whole file. Tell them to undo and redo with edits.

  4. behaviour-spec.mdx conflicts on nearly every merge. It is generated and every agent regenerates it. Resolve centrally with npm run gen:behaviour-spec && git add; tell agents to expect it and never hand-edit.

  5. Concurrent dotnet test runs flake — a burst of SQLite Error 1: 'no such table: Documents'. Partly host pressure, but RB-12 found a real cause: a bare new WebApplicationFactory<Program>() races on the static Db.ConnectionString. Use the house TestWebApplicationFactory + IClassFixture idiom. Do not trust a backend green while another agent is testing.

  6. Agent worktrees live inside the repo, so prettier --check . walks into them — fixed by ignoring .claude/worktrees/ in both .prettierignore and .gitignore.

  7. The stale base is now the rule, not the exception. Batch 4 ran six agents; five were handed a bad base, three of them the same unrelated ae7781e lineage. Across batches 2-4 that is 11 of 13 agent-runs. Every one self-corrected at step zero. Keep the named-expected-commit check as the first instruction in every prompt — it is the highest-value line in there. Give the current HEAD, not the batch's starting commit, when tickets merge sequentially.

  8. A spend limit can kill every agent mid-flight; resume, do not restart. All four wave-A agents died on an org monthly-spend 429, three of them at the final CI re-run. Their worktrees kept the uncommitted work intact. Sending each agent a message resumed it from its own transcript and it finished from exactly where it stopped — nothing was redone. Check git -C <worktree> status before assuming work is lost.

  9. The 99-backlog.md conflict is mechanical and has a fixed recipe. It fired on three of five merges. Cause: the central prettier pass reflows the table's column widths, so the whole table conflicts even though the two sides differ in only one or two status cells. Recipe: take HEAD's table, flip the incoming ticket's cell, re-run prettier. Verify by parsing both sides cell-by-cell and printing only the differing cells before discarding either side — do not eyeball a 33-row table. A ticket's cell reads **done** once merged (matching RB-01..RB-17), not implemented.

  10. For a zero-semantic-change commit, ask for evidence CI cannot give. RB-19's diff is 181 lines in Program.cs and no test can prove it changed nothing. The sorted-route-list diff, plus the route/gate/handler-triple comparison, is what actually made it reviewable. Ask for it in the prompt and re-run it centrally before merging.

  11. gen:api is not always a no-op on a pure reorder. OpenAPI operation order follows mapping order, so a reorder legitimately changes swagger.json and api-client.ts. Tell the agent to prove the diff is ordering-only (sort every line of both versions, diff, expect empty) and to commit the regenerated pair, or CI's drift job fails on a correct change.

  12. The backlog's own "depends on" column is not exhaustive — check actual imports before parallelizing a wave. Batch 5's table said only "25/26/27 depend on 24"; it never mentioned that RB-24 rewrites imports in two of RB-28's three target files. grep -rln for the moved module's import path against every other open ticket's target files, before deciding what runs in parallel — not after a conflict.

Telling agents to report a ticket as wrong pays off. Three did: BIO-012 was factually wrong about the proefbrief error mapping (RB-11), RB-12's wrapper/public binary did not fit the code, and RB-14 as worded would have shipped a non-gate. None of the three would have been caught by a review of the diff alone.