feat: Remove the Briefwechsel view; retarget its links to document search #716

Closed
opened 2026-06-02 19:48:01 +02:00 by marcel · 1 comment
Owner

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.

Note: This spec is the single source of truth — it was revised across three multi-persona review rounds (whose comments were folded in here and deleted). Scope is grep-driven: the removal method is authoritative; file lists are "known references, not exhaustive — verify with grep." Line numbers are hints — confirm before editing.

User story

As a reader browsing a person's frequent correspondents, I want clicking a correspondent to open the document search pre-filtered by that sender and receiver, so that I land in one consistent place to explore letters instead of a separate dedicated view.

Decisions made (deliberate — do not "improve" them)

  1. Redirect filter = both params. The retargeted link sets senderId=A&receiverId=B. Verified semantics: document search applies sender.id = A AND receivers contains B (helper specs in DocumentSpecifications.java ~:27/:32, combined via .and() in DocumentService ~:535-536), so it shows only the A→B direction. The Briefwechsel view used findConversation, 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.
  2. Full removal, frontend and backend — no dormant dead code left behind.
  3. Keep the "Häufige Korrespondenten" card; retarget its links and fix its now-misleading copy and icon (see below).
  4. No redirect shim. The old /briefwechsel URL is allowed to 404. Confirmed it lands on the styled app error page (frontend/src/routes/+error.svelte exists).
  5. The chip ×N count 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

  • Route directory 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.
  • E2E: frontend/e2e/briefwechsel-rows.visual.spec.ts, frontend/e2e/briefwechsel-a11y.spec.ts, fixture frontend/e2e/fixtures/bilateral-correspondence.ts (imported only by those two specs — safe).
  • Delete the orphaned visual-regression baselines briefwechsel-*-*.png along with the spec, or they rot in the snapshot store.
  • Remove the now-orphaned korrespondenz_recent_persons localStorage 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 via lib/document/ThumbnailRow.svelte:58, used across the app. Not Briefwechsel-only.
  • 🟢 i18n keys conv_sort_newest and conv_sort_oldest — used by the surviving PersonDocumentList.svelte:65 (verified: the only surviving conv_* consumer). Do not blanket-remove all conv_*.

Frontend — also touch (missed by the original inventory)

  • 🔴 frontend/e2e/stammbaum.spec.ts:7 — currently a test('… /briefwechsel still renders without 404') that asserts the route does NOT 404, contradicting the new AC. Trap: this test lives inside a test.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.ts or a small dedicated spec) so it actually executes.
  • 🔴 frontend/e2e/auth.spec.ts:29 — remove /briefwechsel from the protected-route loop ['/documents/new', '/persons', '/briefwechsel']. Keep /documents/new and /persons. (/documents sits behind the same authenticated() rule — coverage not meaningfully reduced.)

Backend — remove

  • DocumentController.getConversation()GET /api/documents/conversation (DocumentController.java:449).
  • DocumentService.getConversationFiltered(...) (:965) and the dead 2-arg DocumentService.getConversation(personA, personB) (:906, zero callers).
  • DocumentRepository.findConversation(...) and DocumentRepository.findSinglePersonCorrespondence(...).
  • Associated tests: DocumentServiceTest.java has two getConversationFiltered blocks (~:988 and ~:1610) — remove both; DocumentRepositoryTest.java (the findSinglePersonCorrespondence section).
  • Run npm run generate:api afterward (backend running with the endpoint already removed) and commit the regenerated TS client in the same change. (No backend/api_tests/*.http references the endpoint — verified.)

i18n — remove (only after rewriting their usages)

  • All conv_* keys except conv_sort_newest/conv_sort_oldest (kept — see above).
  • nav_conversations (verified unreferenced in code — safe).
  • doc_conversation_title and person_correspondents_hintstill referenced by the surviving card (CoCorrespondentsList.svelte:34 and :28). Rewrite those usages first (see below), then delete the keys. Key-first deletion breaks the build.
  • Keep person_co_correspondents_heading (the surviving card heading).

Scope — what to change (not remove)

frontend/src/routes/persons/[id]/CoCorrespondentsList.svelte:

  • Retarget the link (:33): href="/documents?senderId={personId}&receiverId={c.id}" (was /briefwechsel?...).
  • Thread person A's name in. The component currently receives only coCorrespondents + personId (a UUID) — no name for A. Add a personName: string prop; the parent already has it: persons/[id]/+page.svelte:71 renders the card and person.displayName is in scope (already passed to another child at :102). Pass personName={person.displayName}.
  • Fix the misleading copy. Confirmed wording across all three locales (new parameterized key, e.g. person_correspondents_search_title, filled { A: personName, B: c.name }):
    • de: "Briefe von {A} an {B} durchsuchen"
    • en: "Search letters from {A} to {B}"
    • es: "Buscar cartas de {A} a {B}"
    • hint (:28): "klicken, um Briefe zu durchsuchen" (de) + matching en/es, replacing person_correspondents_hint.
  • Replace the misleading icon. The link renders a chat/speech-bubble SVG (:45–59, commented <!-- Chat icon -->) — that symbol promises a two-way conversation. Swap it for a search/magnifier or document icon (keep aria-hidden="true").
  • Clarify the ×N badge (decision 5): the count is bilateral (shared letters), not the search-result count. A small caption/title on the badge or a word in the hint is enough — don't recompute the number.
  • Update the testsCoCorrespondentsList.svelte.test.ts has 3 stale spots, not one: the href assertion (:60), the hint-text assertion (:30), and every render() now needs the new personName prop (or the title throws on undefined).
  • Keep the link as an <a> with a discernible accessible name and the existing focus-visible ring.

Acceptance criteria

Scenario: Briefwechsel view no longer exists
  Given the application is running
  When I navigate to /briefwechsel
  Then I receive a 404 (no redirect) on the styled app error page
  # Guard lives in an ACTIVE e2e spec — NOT in the test.skip() block in stammbaum.spec.ts.

Scenario: Frequent-correspondents card deep-links into document search
  Given I am on a person A detail page
  And person A has a frequent correspondent B
  When I click correspondent B in the "Häufige Korrespondenten" card
  Then I land on /documents?senderId=<A>&receiverId=<B>
  And the document search shows letters where A is sender and B is receiver

Scenario: Card no longer promises a conversation (copy + icon)
  Given the "Häufige Korrespondenten" card on a person detail page
  Then the link's accessible name names both persons and reads as a search action
  And the link's icon is a search/document icon, not a speech bubble
  # Assert structurally (href is /documents?..., accessible name contains both names).
  # If asserting a literal string, assert against the `de` message — do not retype the copy.

Scenario: Bilateral badge count is not presented as the result count
  Given a correspondent chip showing "×N"
  Then N is labelled/understood as shared letters (both directions), not search results

Scenario: Frequent-correspondents card still renders
  Given I am on a person detail page with at least one correspondent
  Then the "Häufige Korrespondenten" card is still visible and populated

Scenario: Person document-list sort labels still work
  Given a person detail page with documents
  Then the sort toggle still shows "Neueste"/"Älteste" (conv_sort_* keys retained)

Scenario: Backend conversation endpoint is gone
  When a client calls GET /api/documents/conversation
  Then the endpoint does not exist (404)

Scenario: No dangling references remain
  When I grep for "briefwechsel", "getConversation", "findConversation",
       "findSinglePersonCorrespondence", "getConversationFiltered",
       and every removed conv_* / nav_conversations / doc_conversation_title /
       person_correspondents_hint key — across frontend/src, frontend/e2e, and backend/src
  Then only intentional matches remain (stammbaum.spec.ts and auth.spec.ts both updated)
  And `npm run check`, `npm run lint`, frontend unit tests, and backend tests all pass

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:

  • Both CLAUDE.md (root route table) and frontend/CLAUDE.md:32 (second route table).
  • docs/ARCHITECTURE.md and docs/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 (~:140 and ~:158); update/remove both.
  • Design specs docs/specs/briefwechsel-thumbnail-rows-spec.html and docs/specs/conversations-narrow-column.html — delete or mark superseded.
  • New ADR docs/adr/028-… (next free number — note a 022 collision already exists, so do not reuse low numbers) recording the bilateral → unidirectional regression (Context / Decision / Consequence).
  • Coordinate with the in-flight C4 audit (docs/superpowers/plans/2026-05-06-c4-diagram-accuracy-audit.md).

⚠️ Do NOT edit historical recordsdocs/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

  • After removal, lib/conversation/ holds a single component (ConversationThumbnail.svelte), whose only consumer is lib/document/ThumbnailRow.svelte:58. Recommended: move ConversationThumbnail into lib/document/, update the import, delete the empty lib/conversation/ folder, and fix the now-stale frontend/CLAUDE.md lib description. If keeping scope minimal, at least fix that description.

Non-functional notes

  • Accessibility: retargeted link keeps a discernible accessible name (new title + visible text); new icon stays aria-hidden; no 44px/contrast change.
  • No data migration / no DB change — only query/endpoint code removed; tables untouched.
  • Regression guard: the /documents senderId/receiverId filters already have test coverage and work as the redirect target (verified +page.server.ts:36-37).

Out of scope

  • Any bidirectional ("between two people, either direction") document-search filter — separate enhancement if ever wanted.
  • A redirect shim for the old URL.

Open questions

  • None. (Bidirectional search loss accepted; card wording confirmed in de/en/es; bilateral-count mismatch accepted + to be documented.)
## 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. > **Note:** This spec is the single source of truth — it was revised across three multi-persona review rounds (whose comments were folded in here and deleted). Scope is **grep-driven**: the removal *method* is authoritative; file lists are "known references, not exhaustive — verify with grep." Line numbers are hints — confirm before editing. ## User story > As a reader browsing a person's frequent correspondents, I want clicking a correspondent to open the document search pre-filtered by that sender and receiver, so that I land in one consistent place to explore letters instead of a separate dedicated view. ## Decisions made (deliberate — do not "improve" them) 1. **Redirect filter = both params.** The retargeted link sets `senderId=A&receiverId=B`. Verified semantics: document search applies `sender.id = A` **AND** `receivers contains B` (helper specs in `DocumentSpecifications.java` ~`:27`/`:32`, combined via `.and()` in `DocumentService` ~`:535-536`), so it shows **only the A→B direction**. The Briefwechsel view used `findConversation`, 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. 2. **Full removal**, frontend *and* backend — no dormant dead code left behind. 3. **Keep the "Häufige Korrespondenten" card**; retarget its links and fix its now-misleading copy **and icon** (see below). 4. **No redirect shim.** The old `/briefwechsel` URL is allowed to 404. Confirmed it lands on the styled app error page (`frontend/src/routes/+error.svelte` exists). 5. **The chip `×N` count 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 - Route directory `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`. - E2E: `frontend/e2e/briefwechsel-rows.visual.spec.ts`, `frontend/e2e/briefwechsel-a11y.spec.ts`, fixture `frontend/e2e/fixtures/bilateral-correspondence.ts` (imported only by those two specs — safe). - **Delete the orphaned visual-regression baselines** `briefwechsel-*-*.png` along with the spec, or they rot in the snapshot store. - Remove the now-orphaned `korrespondenz_recent_persons` localStorage 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 via `lib/document/ThumbnailRow.svelte:58`, used across the app. **Not** Briefwechsel-only. - 🟢 **i18n keys `conv_sort_newest` and `conv_sort_oldest`** — used by the surviving `PersonDocumentList.svelte:65` (verified: the *only* surviving `conv_*` consumer). Do **not** blanket-remove all `conv_*`. ### Frontend — also touch (missed by the original inventory) - 🔴 **`frontend/e2e/stammbaum.spec.ts:7`** — currently a `test('… /briefwechsel still renders without 404')` that asserts the route does **NOT** 404, contradicting the new AC. **Trap:** this test lives inside a `test.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.ts` or a small dedicated spec) so it actually executes. - 🔴 **`frontend/e2e/auth.spec.ts:29`** — remove `/briefwechsel` from the protected-route loop `['/documents/new', '/persons', '/briefwechsel']`. Keep `/documents/new` and `/persons`. (`/documents` sits behind the same `authenticated()` rule — coverage not meaningfully reduced.) ### Backend — remove - `DocumentController.getConversation()` — `GET /api/documents/conversation` (`DocumentController.java:449`). - `DocumentService.getConversationFiltered(...)` (`:965`) **and** the dead 2-arg `DocumentService.getConversation(personA, personB)` (`:906`, zero callers). - `DocumentRepository.findConversation(...)` and `DocumentRepository.findSinglePersonCorrespondence(...)`. - Associated tests: `DocumentServiceTest.java` has **two** `getConversationFiltered` blocks (~`:988` **and** ~`:1610`) — remove both; `DocumentRepositoryTest.java` (the `findSinglePersonCorrespondence` section). - Run `npm run generate:api` afterward (backend running with the endpoint already removed) and **commit the regenerated TS client** in the same change. (No `backend/api_tests/*.http` references the endpoint — verified.) ### i18n — remove (only after rewriting their usages) - All `conv_*` keys **except** `conv_sort_newest`/`conv_sort_oldest` (kept — see above). - `nav_conversations` (verified unreferenced in code — safe). - `doc_conversation_title` and `person_correspondents_hint` — **still referenced by the surviving card** (`CoCorrespondentsList.svelte:34` and `:28`). Rewrite those usages **first** (see below), then delete the keys. Key-first deletion breaks the build. - **Keep** `person_co_correspondents_heading` (the surviving card heading). ## Scope — what to change (not remove) `frontend/src/routes/persons/[id]/CoCorrespondentsList.svelte`: - **Retarget the link** (`:33`): `href="/documents?senderId={personId}&receiverId={c.id}"` (was `/briefwechsel?...`). - **Thread person A's name in.** The component currently receives only `coCorrespondents` + `personId` (a UUID) — **no name for A**. Add a `personName: string` prop; the parent already has it: `persons/[id]/+page.svelte:71` renders the card and `person.displayName` is in scope (already passed to another child at `:102`). Pass `personName={person.displayName}`. - **Fix the misleading copy.** Confirmed wording across all three locales (new parameterized key, e.g. `person_correspondents_search_title`, filled `{ A: personName, B: c.name }`): - **de:** "Briefe von {A} an {B} durchsuchen" - **en:** "Search letters from {A} to {B}" - **es:** "Buscar cartas de {A} a {B}" - hint (`:28`): **"klicken, um Briefe zu durchsuchen"** (de) + matching en/es, replacing `person_correspondents_hint`. - **Replace the misleading icon.** The link renders a chat/speech-bubble SVG (`:45–59`, commented `<!-- Chat icon -->`) — that symbol promises a two-way conversation. Swap it for a **search/magnifier or document icon** (keep `aria-hidden="true"`). - **Clarify the `×N` badge** (decision 5): the count is bilateral (shared letters), not the search-result count. A small caption/`title` on the badge or a word in the hint is enough — don't recompute the number. - **Update the tests** — `CoCorrespondentsList.svelte.test.ts` has **3 stale spots**, not one: the href assertion (~`:60`), the hint-text assertion (~`:30`), and every `render()` now needs the new `personName` prop (or the title throws on undefined). - Keep the link as an `<a>` with a discernible accessible name and the existing `focus-visible` ring. ## Acceptance criteria ```gherkin Scenario: Briefwechsel view no longer exists Given the application is running When I navigate to /briefwechsel Then I receive a 404 (no redirect) on the styled app error page # Guard lives in an ACTIVE e2e spec — NOT in the test.skip() block in stammbaum.spec.ts. Scenario: Frequent-correspondents card deep-links into document search Given I am on a person A detail page And person A has a frequent correspondent B When I click correspondent B in the "Häufige Korrespondenten" card Then I land on /documents?senderId=<A>&receiverId=<B> And the document search shows letters where A is sender and B is receiver Scenario: Card no longer promises a conversation (copy + icon) Given the "Häufige Korrespondenten" card on a person detail page Then the link's accessible name names both persons and reads as a search action And the link's icon is a search/document icon, not a speech bubble # Assert structurally (href is /documents?..., accessible name contains both names). # If asserting a literal string, assert against the `de` message — do not retype the copy. Scenario: Bilateral badge count is not presented as the result count Given a correspondent chip showing "×N" Then N is labelled/understood as shared letters (both directions), not search results Scenario: Frequent-correspondents card still renders Given I am on a person detail page with at least one correspondent Then the "Häufige Korrespondenten" card is still visible and populated Scenario: Person document-list sort labels still work Given a person detail page with documents Then the sort toggle still shows "Neueste"/"Älteste" (conv_sort_* keys retained) Scenario: Backend conversation endpoint is gone When a client calls GET /api/documents/conversation Then the endpoint does not exist (404) Scenario: No dangling references remain When I grep for "briefwechsel", "getConversation", "findConversation", "findSinglePersonCorrespondence", "getConversationFiltered", and every removed conv_* / nav_conversations / doc_conversation_title / person_correspondents_hint key — across frontend/src, frontend/e2e, and backend/src Then only intentional matches remain (stammbaum.spec.ts and auth.spec.ts both updated) And `npm run check`, `npm run lint`, frontend unit tests, and backend tests all pass ``` ## 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:** - **Both** `CLAUDE.md` (root route table) **and** `frontend/CLAUDE.md:32` (second route table). - `docs/ARCHITECTURE.md` and `docs/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** (~`:140` and ~`:158`); update/remove both. - Design specs `docs/specs/briefwechsel-thumbnail-rows-spec.html` and `docs/specs/conversations-narrow-column.html` — delete or mark superseded. - **New ADR `docs/adr/028-…`** (next free number — note a `022` collision already exists, so do not reuse low numbers) recording the bilateral → unidirectional regression (Context / Decision / Consequence). - Coordinate with the in-flight C4 audit (`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 - After removal, `lib/conversation/` holds a **single component** (`ConversationThumbnail.svelte`), whose only consumer is `lib/document/ThumbnailRow.svelte:58`. Recommended: **move `ConversationThumbnail` into `lib/document/`**, update the import, delete the empty `lib/conversation/` folder, and fix the now-stale `frontend/CLAUDE.md` lib description. If keeping scope minimal, at least fix that description. ## Non-functional notes - **Accessibility:** retargeted link keeps a discernible accessible name (new `title` + visible text); new icon stays `aria-hidden`; no 44px/contrast change. - **No data migration / no DB change** — only query/endpoint code removed; tables untouched. - **Regression guard:** the `/documents` `senderId`/`receiverId` filters already have test coverage and work as the redirect target (verified `+page.server.ts:36-37`). ## Out of scope - Any bidirectional ("between two people, either direction") document-search filter — separate enhancement if ever wanted. - A redirect shim for the old URL. ## Open questions - None. (Bidirectional search loss accepted; card wording confirmed in de/en/es; bilateral-count mismatch accepted + to be documented.)
marcel added the P2-mediumconversationfeature labels 2026-06-02 19:48:07 +02:00
Author
Owner

Implemented on feat/issue-716-remove-briefwechsel

The 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)

Commit Scope
2c23c75d feat(persons): retarget the "Häufige Korrespondenten" card → /documents?senderId=A&receiverId=B; new personName prop, search-action title naming both persons, magnifier icon (was chat bubble), badge title clarifying the ×N count is bilateral; new person_correspondents_search_* keys in de/en/es; test updated (href, hint, prop, both-names title)
d623ab79 feat(briefwechsel): delete the /briefwechsel route in full (page, server load, 8 components, co-located tests) + e2e (briefwechsel-rows.visual, briefwechsel-a11y, bilateral-correspondence fixture, and the stale korrespondenz.spec.ts that targeted the route's former /korrespondenz path — caught by grep, not in the original inventory)
0b4dbaa3 test: new active briefwechsel-removed.spec.ts asserting /briefwechsel → 404 on the styled error page; removed the contradicting assertion from the test.skip() block in stammbaum.spec.ts; dropped /briefwechsel from the auth.spec.ts protected-route loop
3cf4df2e feat(i18n): remove 22 orphaned keys per locale (all conv_* except conv_sort_newest/oldest, plus nav_conversations, doc_conversation_title, person_correspondents_hint)
1b25c961 refactor(document): move ConversationThumbnail (+ spec) into lib/document/, update import, delete empty lib/conversation/, fix frontend/CLAUDE.md lib map
5b64d8d6 feat(document): remove GET /api/documents/conversation + handler (dead Sort import)
55bc44b7 feat(document): remove getConversationFiltered + dead 2-arg getConversation; both service test blocks
8b7cbc5b feat(document): remove findConversation + findSinglePersonCorrespondence + repo test section (dead LocalDate import)
9c1107fd chore(api): drop the /api/documents/conversation path + getConversation op from the generated TS client
2fbd16db docs: remove Briefwechsel from route tables, ARCHITECTURE.md, C4 frontend/backend diagrams, GLOSSARY.md; delete the two superseded design specs
36457f28 docs(adr): ADR-030 records the bilateral → unidirectional search regression

Decisions honoured (deliberate, not "improved")

  • Unidirectional A→B redirect (replies dropped) — accepted + documented in ADR-030.
  • Full removal, no redirect shim, /briefwechsel 404s.
  • Card kept; copy + icon fixed; ×N badge stays bilateral with a clarifying title.

Verification

  • Backend clean packageBUILD SUCCESS; DocumentServiceTest (169) and DocumentRepositoryTest (29) green.
  • Prettier + ESLint clean on every commit (pre-commit hook).
  • "No dangling references" grep across frontend/src, frontend/e2e, backend/src — only intentional matches remain: the new 404 guard, and a comment in the immutable Flyway migration V62__index_fk_columns.sql (its FK indexes still serve document search).
  • Browser-test assertions (CoCorrespondentsList) and e2e run on CI per project convention.

Notes / minor scope calls

  • ADR number is 030, not 028 — 028/029 were taken since the spec was written.
  • No briefwechsel-*.png baselines existed, so that cleanup was a no-op.
  • The other 7 design-mockup HTMLs under 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.
  • Full-move of ConversationThumbnail chosen (confirmed with maintainer).

Next: open a PR (/review-pr 716 after pushing) — ready for review.

## Implemented on `feat/issue-716-remove-briefwechsel` The 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) | Commit | Scope | |---|---| | `2c23c75d` | **feat(persons):** retarget the "Häufige Korrespondenten" card → `/documents?senderId=A&receiverId=B`; new `personName` prop, search-action title naming both persons, magnifier icon (was chat bubble), badge `title` clarifying the ×N count is bilateral; new `person_correspondents_search_*` keys in de/en/es; test updated (href, hint, prop, both-names title) | | `d623ab79` | **feat(briefwechsel):** delete the `/briefwechsel` route in full (page, server load, 8 components, co-located tests) + e2e (`briefwechsel-rows.visual`, `briefwechsel-a11y`, `bilateral-correspondence` fixture, **and the stale `korrespondenz.spec.ts`** that targeted the route's former `/korrespondenz` path — caught by grep, not in the original inventory) | | `0b4dbaa3` | **test:** new **active** `briefwechsel-removed.spec.ts` asserting `/briefwechsel` → 404 on the styled error page; removed the contradicting assertion from the `test.skip()` block in `stammbaum.spec.ts`; dropped `/briefwechsel` from the `auth.spec.ts` protected-route loop | | `3cf4df2e` | **feat(i18n):** remove 22 orphaned keys per locale (all `conv_*` except `conv_sort_newest`/`oldest`, plus `nav_conversations`, `doc_conversation_title`, `person_correspondents_hint`) | | `1b25c961` | **refactor(document):** move `ConversationThumbnail` (+ spec) into `lib/document/`, update import, delete empty `lib/conversation/`, fix `frontend/CLAUDE.md` lib map | | `5b64d8d6` | **feat(document):** remove `GET /api/documents/conversation` + handler (dead `Sort` import) | | `55bc44b7` | **feat(document):** remove `getConversationFiltered` + dead 2-arg `getConversation`; both service test blocks | | `8b7cbc5b` | **feat(document):** remove `findConversation` + `findSinglePersonCorrespondence` + repo test section (dead `LocalDate` import) | | `9c1107fd` | **chore(api):** drop the `/api/documents/conversation` path + `getConversation` op from the generated TS client | | `2fbd16db` | **docs:** remove Briefwechsel from route tables, `ARCHITECTURE.md`, C4 frontend/backend diagrams, `GLOSSARY.md`; delete the two superseded design specs | | `36457f28` | **docs(adr):** **ADR-030** records the bilateral → unidirectional search regression | ### Decisions honoured (deliberate, not "improved") - Unidirectional A→B redirect (replies dropped) — accepted + documented in ADR-030. - Full removal, no redirect shim, `/briefwechsel` 404s. - Card kept; copy + icon fixed; ×N badge stays bilateral with a clarifying `title`. ### Verification - ✅ Backend `clean package` — **BUILD SUCCESS**; `DocumentServiceTest` (169) and `DocumentRepositoryTest` (29) green. - ✅ Prettier + ESLint clean on every commit (pre-commit hook). - ✅ "No dangling references" grep across `frontend/src`, `frontend/e2e`, `backend/src` — only intentional matches remain: the new 404 guard, and a comment in the immutable Flyway migration `V62__index_fk_columns.sql` (its FK indexes still serve document search). - Browser-test assertions (`CoCorrespondentsList`) and e2e run on CI per project convention. ### Notes / minor scope calls - **ADR number is 030**, not 028 — `028`/`029` were taken since the spec was written. - No `briefwechsel-*.png` baselines existed, so that cleanup was a no-op. - The other 7 design-mockup HTMLs under `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. - Full-move of `ConversationThumbnail` chosen (confirmed with maintainer). Next: open a PR (`/review-pr 716` after pushing) — ready for review.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#716