feat: Remove the Briefwechsel view; retarget its links to document search #716
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
The Briefwechsel view (
/briefwechsel) is a standalone bilateral letter-timeline between two people. It is not in the main navigation — its only inbound product link is the "Häufige Korrespondenten" card on a person's detail page. It is backed by a dedicated backend endpoint and repository methods that nothing else uses.We are removing this view entirely. The one inbound link will instead deep-link into the existing document search (
/documents) with the sender and receiver pre-filtered.User story
Decisions made (deliberate — do not "improve" them)
senderId=A&receiverId=B. Verified semantics: document search appliessender.id = AANDreceivers contains B(helper specs inDocumentSpecifications.java~:27/:32, combined via.and()inDocumentService~:535-536), so it shows only the A→B direction. The Briefwechsel view usedfindConversation, which is bidirectional ((sender=A AND recv=B) OR (sender=B AND recv=A)); the reverse direction (replies) is intentionally dropped. Accepted behavior change, not a bug. A bidirectional "between these two people" search filter is a separate future enhancement./briefwechselURL is allowed to 404. Confirmed it lands on the styled app error page (frontend/src/routes/+error.svelteexists).×Ncount stays bilateral. The badge counts correspondence in both directions (relationship-strength signal), while the destination search shows only A→B — so the badge may exceed the result count. Accepted + documented, not recomputed: add a one-line clarification (see "what to change") so the number reads as "shared letters," not "search results."Removal method (authoritative)
The acceptance criterion "no dangling references remain" is the real spec. Drive removal by grepping for:
briefwechsel,getConversation,findConversation,findSinglePersonCorrespondence,getConversationFiltered, and each removed i18n key. The lists below are known references confirmed during review — a starting point, not a complete set.Frontend — remove
frontend/src/routes/briefwechsel/in full:+page.svelte,+page.server.ts, and the 8 components (ConversationFilterBar,ConversationTimeline,CorrespondentSuggestionsDropdown,CorrespondenzFilterControls,CorrespondenzHero,CorrespondenzPersonBar,SinglePersonHintBar) + all co-located*.test.ts/*.spec.ts.frontend/e2e/briefwechsel-rows.visual.spec.ts,frontend/e2e/briefwechsel-a11y.spec.ts, fixturefrontend/e2e/fixtures/bilateral-correspondence.ts(imported only by those two specs — safe).briefwechsel-*-*.pngalong with the spec, or they rot in the snapshot store.korrespondenz_recent_personslocalStorage usage (lived only in the Briefwechsel hero).Frontend — KEEP (do not delete — would break the build / other pages)
frontend/src/lib/conversation/ConversationThumbnail.svelte(+ its.spec.ts) — shared vialib/document/ThumbnailRow.svelte:58, used across the app. Not Briefwechsel-only.conv_sort_newestandconv_sort_oldest— used by the survivingPersonDocumentList.svelte:65(verified: the only survivingconv_*consumer). Do not blanket-remove allconv_*.Frontend — also touch (missed by the original inventory)
frontend/e2e/stammbaum.spec.ts:7— currently atest('… /briefwechsel still renders without 404')that asserts the route does NOT 404, contradicting the new AC. Trap: this test lives inside atest.skip()describe block (skipped for #363), so a 404 guard left here would never run. Delete this assertion here, and add the 404 guard to an ACTIVE spec (e.g.auth.spec.tsor a small dedicated spec) so it actually executes.frontend/e2e/auth.spec.ts:29— remove/briefwechselfrom the protected-route loop['/documents/new', '/persons', '/briefwechsel']. Keep/documents/newand/persons. (/documentssits behind the sameauthenticated()rule — coverage not meaningfully reduced.)Backend — remove
DocumentController.getConversation()—GET /api/documents/conversation(DocumentController.java:449).DocumentService.getConversationFiltered(...)(:965) and the dead 2-argDocumentService.getConversation(personA, personB)(:906, zero callers).DocumentRepository.findConversation(...)andDocumentRepository.findSinglePersonCorrespondence(...).DocumentServiceTest.javahas twogetConversationFilteredblocks (~:988and ~:1610) — remove both;DocumentRepositoryTest.java(thefindSinglePersonCorrespondencesection).npm run generate:apiafterward (backend running with the endpoint already removed) and commit the regenerated TS client in the same change. (Nobackend/api_tests/*.httpreferences the endpoint — verified.)i18n — remove (only after rewriting their usages)
conv_*keys exceptconv_sort_newest/conv_sort_oldest(kept — see above).nav_conversations(verified unreferenced in code — safe).doc_conversation_titleandperson_correspondents_hint— still referenced by the surviving card (CoCorrespondentsList.svelte:34and:28). Rewrite those usages first (see below), then delete the keys. Key-first deletion breaks the build.person_co_correspondents_heading(the surviving card heading).Scope — what to change (not remove)
frontend/src/routes/persons/[id]/CoCorrespondentsList.svelte::33):href="/documents?senderId={personId}&receiverId={c.id}"(was/briefwechsel?...).coCorrespondents+personId(a UUID) — no name for A. Add apersonName: stringprop; the parent already has it:persons/[id]/+page.svelte:71renders the card andperson.displayNameis in scope (already passed to another child at:102). PasspersonName={person.displayName}.person_correspondents_search_title, filled{ A: personName, B: c.name })::28): "klicken, um Briefe zu durchsuchen" (de) + matching en/es, replacingperson_correspondents_hint.:45–59, commented<!-- Chat icon -->) — that symbol promises a two-way conversation. Swap it for a search/magnifier or document icon (keeparia-hidden="true").×Nbadge (decision 5): the count is bilateral (shared letters), not the search-result count. A small caption/titleon the badge or a word in the hint is enough — don't recompute the number.CoCorrespondentsList.svelte.test.tshas 3 stale spots, not one: the href assertion (:60), the hint-text assertion (:30), and everyrender()now needs the newpersonNameprop (or the title throws on undefined).<a>with a discernible accessible name and the existingfocus-visiblering.Acceptance criteria
Documentation updates (blockers — PR does not merge until docs match code)
Full grep set (
grep -rln briefwechsel docs/ CLAUDE.md frontend/CLAUDE.md). Edit these:CLAUDE.md(root route table) andfrontend/CLAUDE.md:32(second route table).docs/ARCHITECTURE.mdanddocs/architecture/c4-diagrams.md— remove Briefwechsel prose.docs/architecture/c4/l3-frontend-3c-people-stories.puml— remove the Briefwechsel route node.docs/architecture/c4/l3-backend-*.puml— the conversation endpoint is referenced in prose within two component descriptions (not a standalone node) — update those.docs/GLOSSARY.md— the "Briefwechsel" term has two touchpoints (~:140and ~:158); update/remove both.docs/specs/briefwechsel-thumbnail-rows-spec.htmlanddocs/specs/conversations-narrow-column.html— delete or mark superseded.docs/adr/028-…(next free number — note a022collision already exists, so do not reuse low numbers) recording the bilateral → unidirectional regression (Context / Decision / Consequence).docs/superpowers/plans/2026-05-06-c4-diagram-accuracy-audit.md).⚠️ Do NOT edit historical records —
docs/adr/003-chronik-…,docs/adr/005-thumbnail-…, and dated analyses (docs/app-analysis-2026-04-12.md,docs/import-migration/…) mention Briefwechsel as point-in-time context. ADRs are immutable; the new ADR supersedes, it does not rewrite them.Optional structural cleanup
lib/conversation/holds a single component (ConversationThumbnail.svelte), whose only consumer islib/document/ThumbnailRow.svelte:58. Recommended: moveConversationThumbnailintolib/document/, update the import, delete the emptylib/conversation/folder, and fix the now-stalefrontend/CLAUDE.mdlib description. If keeping scope minimal, at least fix that description.Non-functional notes
title+ visible text); new icon staysaria-hidden; no 44px/contrast change./documentssenderId/receiverIdfilters already have test coverage and work as the redirect target (verified+page.server.ts:36-37).Out of scope
Open questions
Implemented on
feat/issue-716-remove-briefwechselThe Briefwechsel view is fully removed (frontend + backend) and its one inbound link now deep-links into document search. Worked grep-driven per the spec; the "no dangling references" AC holds.
Commits (11, atomic)
2c23c75d/documents?senderId=A&receiverId=B; newpersonNameprop, search-action title naming both persons, magnifier icon (was chat bubble), badgetitleclarifying the ×N count is bilateral; newperson_correspondents_search_*keys in de/en/es; test updated (href, hint, prop, both-names title)d623ab79/briefwechselroute in full (page, server load, 8 components, co-located tests) + e2e (briefwechsel-rows.visual,briefwechsel-a11y,bilateral-correspondencefixture, and the stalekorrespondenz.spec.tsthat targeted the route's former/korrespondenzpath — caught by grep, not in the original inventory)0b4dbaa3briefwechsel-removed.spec.tsasserting/briefwechsel→ 404 on the styled error page; removed the contradicting assertion from thetest.skip()block instammbaum.spec.ts; dropped/briefwechselfrom theauth.spec.tsprotected-route loop3cf4df2econv_*exceptconv_sort_newest/oldest, plusnav_conversations,doc_conversation_title,person_correspondents_hint)1b25c961ConversationThumbnail(+ spec) intolib/document/, update import, delete emptylib/conversation/, fixfrontend/CLAUDE.mdlib map5b64d8d6GET /api/documents/conversation+ handler (deadSortimport)55bc44b7getConversationFiltered+ dead 2-arggetConversation; both service test blocks8b7cbc5bfindConversation+findSinglePersonCorrespondence+ repo test section (deadLocalDateimport)9c1107fd/api/documents/conversationpath +getConversationop from the generated TS client2fbd16dbARCHITECTURE.md, C4 frontend/backend diagrams,GLOSSARY.md; delete the two superseded design specs36457f28Decisions honoured (deliberate, not "improved")
/briefwechsel404s.title.Verification
clean package— BUILD SUCCESS;DocumentServiceTest(169) andDocumentRepositoryTest(29) green.frontend/src,frontend/e2e,backend/src— only intentional matches remain: the new 404 guard, and a comment in the immutable Flyway migrationV62__index_fk_columns.sql(its FK indexes still serve document search).CoCorrespondentsList) and e2e run on CI per project convention.Notes / minor scope calls
028/029were taken since the spec was written.briefwechsel-*.pngbaselines existed, so that cleanup was a no-op.docs/specs/that merely show Briefwechsel in static nav chrome were left as frozen point-in-time artifacts (same treatment the spec gives the dated analyses); only the two Briefwechsel-specific specs were superseded.ConversationThumbnailchosen (confirmed with maintainer).Next: open a PR (
/review-pr 716after pushing) — ready for review.