docs(stammbaum): layout glossary + AC3 deferral SQL (#361)
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m41s
CI / OCR Service Tests (pull_request) Successful in 23s
CI / Backend Unit Tests (pull_request) Successful in 3m51s
CI / fail2ban Regex (pull_request) Successful in 45s
CI / Semgrep Security Scan (pull_request) Successful in 23s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m4s

@Elicit on PR #693: two doc gaps that block traceability on this PR.

1. docs/GLOSSARY.md: add a Stammbaum section with the layout vocabulary
   introduced by #689 and #361 — Stammbaum, seeded rank, sibling block,
   loose spouse, parented, anchor index, intra-family marriage, marriage
   dot, canonical fixture. Removes the Pending placeholder.

2. docs/adr/026: commit the AC3 reachability probe (the SQL that returned
   "0 of 942 unseeded persons match the predicate" in May 2026) directly
   into the ADR. A future architect re-evaluating the deferral can rerun
   it verbatim — reproducibility of the decision is itself a requirement.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-28 20:44:49 +02:00
parent 9bdd9fb3a5
commit b8ad64dd13
2 changed files with 45 additions and 2 deletions

View File

@@ -103,6 +103,30 @@ _See also [DocumentStatus lifecycle](#documentstatus-lifecycle)._
---
## Stammbaum (Family-Tree Layout) Terms
**Stammbaum** `[user-facing]` — the genealogy / family-tree view of the archive, accessible at `/stammbaum`. Renders every `Person` as a node positioned by `PersonRelationship` edges (`PARENT_OF`, `SPOUSE_OF`) into rows that correspond to generations. The browser-side layout pipeline lives at `frontend/src/lib/person/genealogy/`.
_See also [PersonRelationship](#person-person)._
**seeded rank** (`Person.generation`) — the imported generation index on a `Person` (G 0 = founders, increasing downward), used as a strict row anchor in `buildLayout.ts`. The iterative fallback heuristic never overrides a seeded rank, and spouse-pulldown never pulls a seeded rank — only unseeded nodes (no `generation`) flow through the heuristic.
**sibling block** — a layout unit holding the children of a single parent-set at one generation, used inside `buildLayout.ts`. Each block has a center computed from the parents' midpoint; blocks are then packed left-to-right within a generation row. Two adjacent sibling blocks at the same rank can be merged if a `SPOUSE_OF` edge crosses them (intra-family marriage, AC2).
**loose spouse** — a person at a given generation who is a spouse of someone in a sibling block but is not themselves a parented child of anyone in the graph. Loose spouses are attached adjacent to their parented partner (right side per Leonie's UX rule) so the spouse line stays short.
_Not to be confused with [parented](#parented-layout)_ — loose is the absence of parent edges into the graph.
**parented** `[layout]` — a layout flag on a sibling-block member indicating that the person has at least one `PARENT_OF` edge incoming from a node already in the graph at the prior generation. Parented members are the layout anchors of their block (the block is centred so the average index of parented members sits under the parents' midpoint); non-parented members (loose spouses) ride along on the side.
**anchor index** — within a sibling block, the average position of `parented` member indices. The block is shifted horizontally so this index, multiplied by `NODE_W + COL_GAP`, lines up under the midpoint of the block's parents — keeping every parent-child connector orthogonal (90°).
**intra-family marriage** — a `SPOUSE_OF` edge where both endpoints are parented members of *different* sibling blocks at the same rank (i.e. both have parents in the graph, but the parent sets differ). Layout merges the two blocks so the spouses sit adjacent at the join boundary; latent in current data (0 cases in the May-2026 canonical snapshot) but covered by a synthetic regression test in `buildLayout.test.ts`.
**marriage dot** — the SVG circle drawn at the midpoint of a `SPOUSE_OF` connector in the Stammbaum tree (`StammbaumTree.svelte`). Radius is `r=6` (12 px diameter) so the marker meets WCAG 1.4.11 (3:1 non-text contrast) when it stacks to disambiguate multiple marriages on the same focal person.
**canonical fixture** (Stammbaum) — `frontend/src/lib/person/genealogy/__fixtures__/stammbaum.json`, a pinned `/api/network` snapshot used by `buildLayout.test.ts` for structural-property assertions against real data. Captured locally via `frontend/scripts/capture-network-fixture.mjs` with explicit credentials and a localhost backend; never invoked from CI. Sanity-gated by `validateFixture.ts` (≥ 50 nodes / ≥ 5 generations / ≥ 1 SPOUSE_OF edge / ≥ 1 multi-spouse person).
---
## Other Domain Terms
**Aktivität / Aktivitäten** `[user-facing]` — the family activity feed accessible at `/aktivitaeten`. Shows recent documents, transcriptions, comments, and Geschichten as a chronological timeline.
@@ -145,4 +169,3 @@ _Terms flagged as potentially ambiguous that have not yet been formally defined
- Terms surfaced by Epic 1 audit findings (#388#392) — review audit reports under `docs/audits/` when available and add any term flagged as ambiguous.
- `OcrBatchService` vs `OcrAsyncRunner` — both handle async OCR orchestration; their division of responsibility should be clarified here.
- `Stammbaum` — the genealogy tree view; relationship to `PersonRelationship` entity.

View File

@@ -86,7 +86,27 @@ threshold, so `packBlocks.ts` is **not** yet warranted.
with `generation`, P themselves was not) and belongs in the canonical import
sheet rather than `buildLayout`. **Revisit trigger:** first canonical fixture
containing a parented unseeded spouse — at which point this ADR is updated
in place or superseded by an ADR-027.
in place or superseded by an ADR-027. **Reproducible verification query**
(PostgreSQL — paste into a read-only psql session against
`familienarchiv_archive`):
```sql
-- AC3 reachability probe. Returns one row per unseeded person who has at
-- least one parent edge whose parent IS seeded. A non-zero count means the
-- AC3 layout branch becomes reachable for that person and ADR-026 should
-- be revisited. Last run May 2026: 0 rows.
SELECT p.id, p.display_name
FROM persons p
WHERE p.generation IS NULL
AND EXISTS (
SELECT 1
FROM person_relationships r
JOIN persons parent ON parent.id = r.person_id
WHERE r.relation_type = 'PARENT_OF'
AND r.related_person_id = p.id
AND parent.generation IS NOT NULL
);
```
- **AC6 — Bundle-impact gate (≤ 40 kB gzipped on `/stammbaum`).** Moot under
this ADR; reactivates only under ADR-027 (dagre adoption).
- **AC7 — Visual regression at 320 / 768 / 1440.** `toHaveScreenshot()`