audit(rollup): produce global readiness scorecard from subsystem audits #393

Closed
opened 2026-05-04 16:05:09 +02:00 by marcel · 1 comment
Owner

Context

Part of Epic #387 — Codebase Legibility Audit. This is AUDIT-6: the synthesis step that turns five per-subsystem reports into one Legibility Readiness Scorecard that the user (and eventually the evaluators) can read end-to-end.

This issue is blocked by AUDIT-1 (#388), AUDIT-2 (#389), AUDIT-3 (#390), AUDIT-4 (#391), AUDIT-5 (#392). All five must be closed before this one starts.

The output is a Markdown document under docs/LEGIBILITY-READINESS.md.

Inputs

  • The five subsystem reports under docs/audits/audit-{frontend,backend,ocr,db,rest}-report.md
  • Reference bundle in first comment of #387

Required content of docs/LEGIBILITY-READINESS.md

1. Executive summary

One paragraph: what state the codebase is in for human evaluation, written so Anja and Tobias could read it cold.

2. Per-subsystem health table

Subsystem Critical Major Minor Cosmetic Health Top blocker

3. Aggregate rubric scorecard

For each rubric category C1–C10, total counts across all subsystems:

Category PASS FAIL-Critical FAIL-Major FAIL-Minor Aggregate verdict

4. Top-10 prioritized actions

Across all subsystem reports, the ten highest-leverage actions in priority order. Each ≤2 sentences, with which subsystem they affect and which rubric check they unblock.

5. Refactor readiness verdict

Explicit answer: is the codebase ready for the big-bang refactor (Epic 4)?

  • 🟢 = yes, run REFACTOR-1 / REFACTOR-2 in parallel
  • 🟡 = yes after specific prerequisites (list them)
  • 🔴 = no, address Critical findings first (list them)

6. Evaluation readiness verdict

Explicit answer: is the codebase ready to send to Anja and Tobias?

  • 🟢 = yes, send now
  • 🟡 = yes after specific tasks (list them; should be Epic 5 cleanup tasks)
  • 🔴 = no, run Epics 2–5 first

7. Open questions for the user

Any boundary decision that AUDIT-1..5 surfaced but couldn't resolve.

Acceptance criteria

  • All 5 subsystem audits (#388, #389, #390, #391, #392) are closed
  • docs/LEGIBILITY-READINESS.md exists on a feature branch with all 7 sections
  • §5 verdict is justified by reference to specific rubric findings
  • §6 verdict is justified by reference to specific rubric findings
  • Top-10 actions in §4 each link to their source audit report
  • PR opened and merged before this issue is closed

Definition of Done

docs/LEGIBILITY-READINESS.md committed on main. Closing comment on this issue states the §5 and §6 verdicts succinctly so the user sees the result without opening the file. Issue closed via Closes #N in the merge commit.

Dispatch

This one is best done by you (Marcel) or a single agent with full context, NOT a parallel Explore agent — the synthesis step needs to read all five reports in full and form a coherent narrative.

## Context Part of **Epic #387** — Codebase Legibility Audit. This is **AUDIT-6**: the synthesis step that turns five per-subsystem reports into one **Legibility Readiness Scorecard** that the user (and eventually the evaluators) can read end-to-end. This issue is **blocked by AUDIT-1 (#388), AUDIT-2 (#389), AUDIT-3 (#390), AUDIT-4 (#391), AUDIT-5 (#392)**. All five must be closed before this one starts. The output is a Markdown document under `docs/LEGIBILITY-READINESS.md`. ## Inputs - The five subsystem reports under `docs/audits/audit-{frontend,backend,ocr,db,rest}-report.md` - Reference bundle in **first comment of #387** ## Required content of `docs/LEGIBILITY-READINESS.md` ### 1. Executive summary One paragraph: what state the codebase is in for human evaluation, written so Anja and Tobias could read it cold. ### 2. Per-subsystem health table | Subsystem | Critical | Major | Minor | Cosmetic | Health | Top blocker | |---|---|---|---|---|---|---| ### 3. Aggregate rubric scorecard For each rubric category C1–C10, total counts across all subsystems: | Category | PASS | FAIL-Critical | FAIL-Major | FAIL-Minor | Aggregate verdict | |---|---|---|---|---|---| ### 4. Top-10 prioritized actions Across all subsystem reports, the ten highest-leverage actions in priority order. Each ≤2 sentences, with which subsystem they affect and which rubric check they unblock. ### 5. Refactor readiness verdict Explicit answer: **is the codebase ready for the big-bang refactor (Epic 4)?** - 🟢 = yes, run REFACTOR-1 / REFACTOR-2 in parallel - 🟡 = yes after specific prerequisites (list them) - 🔴 = no, address Critical findings first (list them) ### 6. Evaluation readiness verdict Explicit answer: **is the codebase ready to send to Anja and Tobias?** - 🟢 = yes, send now - 🟡 = yes after specific tasks (list them; should be Epic 5 cleanup tasks) - 🔴 = no, run Epics 2–5 first ### 7. Open questions for the user Any boundary decision that AUDIT-1..5 surfaced but couldn't resolve. ## Acceptance criteria - [ ] All 5 subsystem audits (#388, #389, #390, #391, #392) are closed - [ ] `docs/LEGIBILITY-READINESS.md` exists on a feature branch with all 7 sections - [ ] §5 verdict is justified by reference to specific rubric findings - [ ] §6 verdict is justified by reference to specific rubric findings - [ ] Top-10 actions in §4 each link to their source audit report - [ ] PR opened and merged before this issue is closed ## Definition of Done `docs/LEGIBILITY-READINESS.md` committed on `main`. Closing comment on this issue states the §5 and §6 verdicts succinctly so the user sees the result without opening the file. Issue closed via `Closes #N` in the merge commit. ## Dispatch This one is best done by you (Marcel) or a single agent with full context, NOT a parallel Explore agent — the synthesis step needs to read all five reports in full and form a coherent narrative.
marcel added this to the Codebase Legibility milestone 2026-05-04 16:05:09 +02:00
marcel added the P1-highauditlegibility labels 2026-05-04 16:05:47 +02:00
Author
Owner

AUDIT-6 — Global Legibility Readiness Scorecard

Synthesis of the five subsystem audits (#388 frontend, #389 backend, #390 ocr-service, #391 db, #392 rest-of-repo) against RUBRIC-LEGIBILITY-001. Read-only rollup; no code or docs modified.


1. Executive summary

Familienarchiv is a working full-stack family-archive application — Spring Boot 4 + SvelteKit 2 + Python OCR microservice + PostgreSQL + MinIO — and the code itself is largely sound: layering inside each subsystem is mostly disciplined, tests are well-named and order-independent, and non-obvious decisions tend to carry good why-comments. The legibility gap is in the wrapper around the code, not the code. There is no human-targeted README.md at the repo root (the LLM-targeted CLAUDE.md is the de facto front door), no docs/ARCHITECTURE.md, no docs/GLOSSARY.md to disambiguate Person ≠ AppUser and the German/English term split (Briefwechsel/Chronik/Geschichte/Stammbaum/Richtlinien), the backend is layer-packaged rather than domain-packaged with 11 cross-domain repository injections plus 2 controller-injects-repository violations, the frontend's lib/components/ is a flat 79-file bucket spanning ≥9 domains, and the users table maps to entity AppUser while persons is a separate concept — a stranger will conflate them within 30 seconds. A first-time evaluator (Anja or Tobias) cannot today form a correct mental model from the documentation alone or locate every file for a domain in ≤2 minutes.


2. Per-subsystem health table

Subsystem Critical Major Minor Cosmetic Health Top blocker
frontend (#388) 5 21 5 0 🔴 Flat 79-file lib/components/ bucket spanning ≥9 domains; no real frontend README (the sv create stub)
backend (#389) 6 12 5 0 🔴 Layer-packaged with 11 cross-domain repo injections + 2 controller→repo violations (C6.1/C6.2)
ocr-service (#390) 0 9 4 0 🟡 Stale CLAUDE.md endpoint table (/training/submit doesn't exist; missing BLLA_MODEL_PATH env var) and no human-facing README
db (#391) 1 3 4 0 🔴 users table → AppUser entity name mismatch sharing lexical space with persons; zero COMMENT ON TABLE; no docs/SCHEMA.md
rest-of-repo (#392) 1 10 3 0 🔴 No root README.md; CLAUDE.md is the de facto front door; docs/specs/ has 47 unindexed HTML files
Aggregate 13 55 21 0 🔴 Missing front door + domain-clarity docs + backend layering violations

3. Aggregate rubric scorecard

Counts aggregated across all five subsystem reports, applicable checks only (N/A and deferred excluded). Aggregate verdict uses the same threshold as per-subsystem: 🟢 if 0 Critical AND ≥80% pass; 🟡 if 0 Critical AND ≥50%; 🔴 otherwise.

Category PASS FAIL-Critical FAIL-Major FAIL-Minor Aggregate verdict
C1 First Contact 1 2 7 3 🔴
C2 Local Bootstrap 6 0 6 4 🟡
C3 Domain Clarity 2 7 4 1 🔴
C4 Findability 6 2 3 1 🔴
C5 Conventions 2 0 9 2 🔴
C6 Layering & Boundaries 2 2 2 1 🔴
C7 Documentation Quality 6 0 8 2 🔴
C8 Test Trustworthiness 7 1 (unverified) 2 0 🔴 (Critical = mutation-test deferral on backend C8.1)
C9 Operability 1 0 6 3 🔴
C10 Code Quality Sanity 9 0 6 4 🔴

Overall aggregate: 🔴. Only C2 (Local Bootstrap) clears the 🟡 threshold; every other category has Critical fails or pass-rate below 50%. The pattern is unambiguous: code-quality categories (C6 layering, C8 tests, C10 sanity) fail on a small number of identifiable hotspots, while doc-quality categories (C1, C3, C5, C7, C9) fail across the board because the human-facing documentation layer is largely absent.


4. Top-10 prioritized actions

Selected for maximum unblock per unit of work — each closes multiple rubric categories or removes a refactor-gating risk.

  1. Author root README.md — ≤3-sentence purpose, 5 components table, prerequisites with versions, single bootstrap command, default-creds note, links to COLLABORATING / CODESTYLE / docs / Gitea issues. Source: AUDIT-5 (#392). Unblocks: C1.1, C1.2, C1.4, C2.2, C2.3.
  2. Author docs/ARCHITECTURE.md + docs/GLOSSARY.md — narrative wrapping docs/architecture/c4-diagrams.md; glossary covers Person ≠ AppUser, Briefwechsel/Conversation, Chronik/Activity, Geschichte ≠ Document, Kurrent vs Sütterlin, the four scriptType values. Source: AUDIT-2 (#389), AUDIT-3 (#390), AUDIT-4 (#391), AUDIT-5 (#392). Unblocks: C1.3, C3.1, C3.2, C3.3.
  3. Fix the 11 cross-domain repository injections (C6.2) and 2 controller-injects-repository violations (C6.1) before REFACTOR-1 runsMassImportService/Thumbnail*/TranscriptionQueueService/training exports/SenderModelService/OcrTrainingService/TranscriptionService/AnnotationService/PasswordResetService → introduce read/write helpers on owning services; StatsController → new StatsService; AuthE2EControllerPasswordResetService.findE2EToken(email). Source: AUDIT-2 (#389) §9. Unblocks: C6.1, C6.2 — and removes the gating risk for REFACTOR-1.
  4. Carve lib/components/ into lib/<domain>/{component,store,util,hook,type} per the canonical set plus lib/shared/{ui,discussion,dashboard,i18n} — REFACTOR-2 territory; mapping spelled out file-by-file in AUDIT-1 §5. Source: AUDIT-1 (#388). Unblocks: C3.5, C3.6, C4.1, C4.2, C6.3, C6.4.
  5. Migrate backend to domain packages (document/, person/, tag/, user/, geschichte/, notification/, ocr/, shared/{file-storage,audit,dashboard,import,transcription-queue,security,error-handling,config}) — REFACTOR-1 territory, but only after action #3 lands so the package move is purely cosmetic. Source: AUDIT-2 (#389). Unblocks: C3.4, C4.1, C4.2.
  6. Migrate every CLAUDE.md to a README.md companion per AUDIT-5 §2a (root, docs, scripts, .devcontainer, infra; plus per-subsystem backend/README.md, frontend/README.md, ocr-service/README.md); convert each CLAUDE.md into a thin pointer. Replace backend/HELP.md (Initializr boilerplate) with real backend/README.md; replace frontend/README.md (sv create stub) with real frontend README; promote ocr-service/CLAUDE.md to ocr-service/README.md and fix the stale /training/submit endpoint table + missing BLLA_MODEL_PATH env var. Source: AUDIT-1 (#388), AUDIT-2 (#389), AUDIT-3 (#390), AUDIT-5 (#392). Unblocks: C2.5, C5.3, C7.1.
  7. Pick one error pattern and migrate — convert all RuntimeException/IllegalArgumentException/IllegalStateException in MassImportService:159, FileService:91,167, RestClientOcrClient:218,250,288, PolygonConverter, DocumentService:967 to DomainException; convert controller ResponseStatusException validation to @Valid + DomainException. Source: AUDIT-2 (#389) C5.2. Unblocks: C5.2 — and gives the frontend a uniform code to translate.
  8. Run PIT mutation testing on DocumentService (971 LOC), PersonService, TranscriptionService, MassImportService, NotificationService, OcrAsyncRunner + frontend mutation pass on Person*, Transcription*, Annotation*, Document*, bulkSelection, mention, blockConflictMerge, saveBlockWithConflictRetry specs — without this, REFACTOR-1/REFACTOR-2's "tests still green" signal is unreliable for the largest blast-radius services. Source: AUDIT-1 (#388) §9.1, AUDIT-2 (#389) §9. Unblocks: C8.1 (currently Unverified-Critical).
  9. Author docs/SCHEMA.md (domain-grouped table overview) + add a one-time migration to rename usersapp_users (and users_groups, comment_mentions.user_id, audit_log.actor_id cascading FKs) OR add COMMENT ON TABLE users IS 'AppUser accounts — NOT historical persons. See persons.'; rename tagtags and add @Table(name="tags") to Tag.java; patch V37/V43 sequence holes with skipped-stub migrations; resolve audit-log REVOKE no-op (split DB roles or drop the REVOKE with a why-comment); fix or delete the stale scripts/schema.sql snapshot. Source: AUDIT-4 (#391). Unblocks: C3.3, C7.5, C9.3, C10.4.
  10. Repo hygiene + dead-code sweep before refactor — extend .gitignore to cover proofshot-artifacts/, .agent/, .claude/worktrees/, root node_modules/, .pytest_cache/, **/test-results.locked/, **/.svelte-kit.old/; git rm -r --cached the existing committed copies; delete the 6 frontend/src/lib/paraglide_bak*/ dirs (~4 MB); delete dead frontend components (OcrProgress.svelte, DocumentStatusChip.svelte, ExpandableText.svelte, DashboardRecentDocuments.svelte, lib/index.ts, lib/utils.ts); delete model/DocumentSort.java (duplicate); delete routes/demo/; remove stray console.log in routes/api/tags/+server.ts:27; index docs/specs/ with a README/index.html marking each spec Active/Superseded/Implemented; migrate docs/TODO-backend.md and docs/TODO-frontend.md items to Gitea then delete; pick between docs/style-guide.html and docs/STYLEGUIDE.md. Source: AUDIT-1 (#388) §7, AUDIT-2 (#389) §7, AUDIT-5 (#392) §7. Unblocks: C1.5, C10.1, C10.4.

5. Refactor readiness verdict (gate for Epic 4)

🔴 — NO, address Critical findings first.

Running REFACTOR-1 and REFACTOR-2 against the current codebase would carry the existing layering violations into the new domain packages, where they would be visible as cross-package imports rather than fixed — masking the real defect under a cosmetic move. Specific gating concerns:

  • C6.2 cross-domain repository injections (11 sites)MassImportService → DocumentRepository; Thumbnail* (3 services) → DocumentRepository; TranscriptionQueueService → DocumentRepository; Segmentation/TrainingDataExportServiceDocument/Block/Annotation repos (3 each); SenderModelService → TranscriptionBlockRepository; OcrTrainingService → TranscriptionBlockRepository; TranscriptionService → AnnotationRepository; AnnotationService → TranscriptionBlockRepository; PasswordResetService → AppUserRepository. Source: AUDIT-2 §3 C6.2 + §9. Each must be replaced with a service-level method on the owning domain before the package move.
  • C6.1 controller-injects-repository violations (2 sites)StatsController injects PersonRepository + DocumentRepository; AuthE2EController injects PasswordResetTokenRepository. Source: AUDIT-2 §3 C6.1.
  • C8.1 mutation-validation deferred — neither audit could verify the test suites that protect the largest refactor-blast-radius services would catch a regression. Without a mutation pass, the "tests still green" signal during REFACTOR-1/REFACTOR-2 is unreliable. Source: AUDIT-1 §9.1, AUDIT-2 §3 C8.1.
  • Boundary decisions still pending — thumbnails as shared/file-storage or document.thumbnail? Document.trainingLabel field stays in document or moves to ocr? AppUser notification-preference fields stay on User or move to notification? Page-local-vs-lib/ paradigm for the 39+ frontend components currently inside routes/? PersonMentionEditor → person or shared/discussion? Source: AUDIT-1 §9.4, AUDIT-2 §9.

Path to 🟢: complete actions #3, #7, #8 from §4 (fix layering violations, unify error handling, run mutation tests), then ratify the open boundary decisions in §7 below. The users → app_users rename should run as a separate, dedicated migration before or after REFACTOR-1 — never bundled — because it touches FKs in 6+ tables.


6. Evaluation readiness verdict (gate for sending to Anja/Tobias)

🔴 — NO, run Epics 2–5 first.

A first-time reader cannot form a correct mental model from the documentation alone today, and several of the rubric's measurable success criteria fail outright:

  • O1 (sketch domains and data flow in ≤30 min): fails — no docs/ARCHITECTURE.md, no domain enumeration in any human-targeted doc; the canonical domain set lives only in #387 first comment.
  • O2 (≥8 of 10 "find or place" questions): fails — lib/components/ is a flat 79-file bucket; backend is layer-packaged; "find every Person file" requires scanning ≥6 different parents (AUDIT-1 C4.1).
  • O3 (full stack from README.md in ≤15 min): fails outright — there is no root README.md. CLAUDE.md opens with "This file provides guidance to Claude Code".
  • O4 (zero "ask Marcel"/"non-obvious"/"Claude generated"): passes (one of the few rubric items that passes cleanly; grep is clean across all subsystems).

Specific blockers that must be cleared before sending:

  1. Root README.md (action #1)
  2. docs/ARCHITECTURE.md + docs/GLOSSARY.md (action #2) — Tobias will not get past Briefwechsel, Chronik, Stammbaum, Richtlinien, Aktivitäten, Geschichte without it; Anja will not get past PersonAppUser without it.
  3. Real backend/README.md (replacing HELP.md) and real frontend/README.md (replacing the sv stub) and real ocr-service/README.md (action #6).
  4. The users table → AppUser entity name confusion resolved (action #9) — this is the single thing most likely to make Tobias close the IDE saying "this is weird".
  5. docs/specs/ indexed (action #10) — 47 ungrouped HTML files are the single biggest discoverability failure of docs/.
  6. docs/TODO-backend.md and docs/TODO-frontend.md migrated to Gitea then deleted — they actively contradict the project's own workflow rule.

The Epic 5 cleanup tasks (action #10) alone are not sufficient. Actions #1, #2, #6, and #9 are documentation-only and could conceivably ship without REFACTOR-1/REFACTOR-2, but doing so would have the docs describe the old structure that the refactor is about to replace — not useful. Recommendation: ship Epics 2–5 in the order Critical-fixes → REFACTOR-1 → REFACTOR-2 → docs migration → cleanup, and re-run AUDIT-6 before sending.


7. Open questions for the user

  1. Thumbnails — shared/file-storage or document.thumbnail? AUDIT-2 §5 default is shared/file-storage since thumbnails are a file-storage concern over any document type, but the entity-coupling argument for document.thumbnail is also valid. Pick one before REFACTOR-1.
  2. Document.trainingLabel field — stays in document or moves to ocr? AUDIT-2 §5 / AUDIT-4 §5: the field lives on Document but its only consumer is the OCR training pipeline. Same question for the document_training_labels table.
  3. AppUser notification-preference fields (isNotifyOnReply, isNotifyOnMention) — stay on AppUser or move to notification? AUDIT-2 §5.
  4. /users/[id] (Person profile) vs /admin/users/[id] (AppUser admin) — rename, merge, or document? AUDIT-1 §7, §10.4. The semantic overlap directly threatens the Person ≠ AppUser glossary distinction.
  5. PersonMentionEditorperson/ or shared/discussion/? AUDIT-1 §5. Is "person mention" a person concept or a discussion concept?
  6. EnrichmentBlock, DistributionBar, ConversationThumbnail — boundary calls. AUDIT-1 §5 lists each as ambiguous between two homes.
  7. Page-local components inside routes/ (39+ files) — co-location stays as a legitimate paradigm, or everything moves to lib/<domain>/? AUDIT-1 §5, §9.2. REFACTOR-2 must answer this before moving anything; otherwise the result will be inconsistent.
  8. users table rename — execute the full users → app_users migration (touches FKs in 6+ tables) or settle for a COMMENT ON TABLE clarification? AUDIT-4 §3 C3.3. The full rename is correct but expensive; the comment is cheap but doesn't fix the lexical confusion in code that reads users directly.
  9. Audit-log append-only — split DB roles so V46/V47 REVOKE actually bites, or drop the REVOKE statements with a comment? AUDIT-4 §3 C7.5 / §7. Currently the migration claims protection it doesn't deliver.
  10. docs/style-guide.html (1403 lines) vs docs/STYLEGUIDE.md (389 lines) — which is the source of truth? AUDIT-5 §5, §10.
  11. docs/security-guide.md (persona-voiced "Nora 'NullX' Steiner") — deliverable doc or training material? AUDIT-5 §5.
  12. Permission enum drift — Permission.ANNOTATE_ALL and Permission.BLOG_WRITE exist in Permission.java but are not in backend/CLAUDE.md's documented list of 6. AUDIT-2 §8. Document or remove.
  13. AdminController — slice per-domain (import endpoints to shared/import/, backfill endpoints with their owners) or keep as a thin façade? AUDIT-2 §5.
  14. TranscriptionQueueServiceshared/transcription-queue or document.transcription/queue/? AUDIT-2 §5. The queue is a read view over Document; canonical set says shared/, but the read coupling is to one domain only.
# AUDIT-6 — Global Legibility Readiness Scorecard Synthesis of the five subsystem audits (#388 frontend, #389 backend, #390 ocr-service, #391 db, #392 rest-of-repo) against `RUBRIC-LEGIBILITY-001`. Read-only rollup; no code or docs modified. --- ## 1. Executive summary Familienarchiv is a working full-stack family-archive application — Spring Boot 4 + SvelteKit 2 + Python OCR microservice + PostgreSQL + MinIO — and the code itself is largely sound: layering inside each subsystem is mostly disciplined, tests are well-named and order-independent, and non-obvious decisions tend to carry good why-comments. **The legibility gap is in the wrapper around the code, not the code.** There is no human-targeted `README.md` at the repo root (the LLM-targeted `CLAUDE.md` is the de facto front door), no `docs/ARCHITECTURE.md`, no `docs/GLOSSARY.md` to disambiguate `Person ≠ AppUser` and the German/English term split (Briefwechsel/Chronik/Geschichte/Stammbaum/Richtlinien), the backend is layer-packaged rather than domain-packaged with 11 cross-domain repository injections plus 2 controller-injects-repository violations, the frontend's `lib/components/` is a flat 79-file bucket spanning ≥9 domains, and the `users` table maps to entity `AppUser` while `persons` is a separate concept — a stranger will conflate them within 30 seconds. A first-time evaluator (Anja or Tobias) cannot today form a correct mental model from the documentation alone or locate every file for a domain in ≤2 minutes. --- ## 2. Per-subsystem health table | Subsystem | Critical | Major | Minor | Cosmetic | Health | Top blocker | |---|---:|---:|---:|---:|---|---| | frontend (#388) | 5 | 21 | 5 | 0 | 🔴 | Flat 79-file `lib/components/` bucket spanning ≥9 domains; no real frontend README (the `sv create` stub) | | backend (#389) | 6 | 12 | 5 | 0 | 🔴 | Layer-packaged with 11 cross-domain repo injections + 2 controller→repo violations (C6.1/C6.2) | | ocr-service (#390) | 0 | 9 | 4 | 0 | 🟡 | Stale `CLAUDE.md` endpoint table (`/training/submit` doesn't exist; missing `BLLA_MODEL_PATH` env var) and no human-facing README | | db (#391) | 1 | 3 | 4 | 0 | 🔴 | `users` table → `AppUser` entity name mismatch sharing lexical space with `persons`; zero `COMMENT ON TABLE`; no `docs/SCHEMA.md` | | rest-of-repo (#392) | 1 | 10 | 3 | 0 | 🔴 | No root `README.md`; `CLAUDE.md` is the de facto front door; `docs/specs/` has 47 unindexed HTML files | | **Aggregate** | **13** | **55** | **21** | **0** | **🔴** | Missing front door + domain-clarity docs + backend layering violations | --- ## 3. Aggregate rubric scorecard Counts aggregated across all five subsystem reports, applicable checks only (N/A and deferred excluded). Aggregate verdict uses the same threshold as per-subsystem: 🟢 if 0 Critical AND ≥80% pass; 🟡 if 0 Critical AND ≥50%; 🔴 otherwise. | Category | PASS | FAIL-Critical | FAIL-Major | FAIL-Minor | Aggregate verdict | |---|---:|---:|---:|---:|---| | C1 First Contact | 1 | 2 | 7 | 3 | 🔴 | | C2 Local Bootstrap | 6 | 0 | 6 | 4 | 🟡 | | C3 Domain Clarity | 2 | 7 | 4 | 1 | 🔴 | | C4 Findability | 6 | 2 | 3 | 1 | 🔴 | | C5 Conventions | 2 | 0 | 9 | 2 | 🔴 | | C6 Layering & Boundaries | 2 | 2 | 2 | 1 | 🔴 | | C7 Documentation Quality | 6 | 0 | 8 | 2 | 🔴 | | C8 Test Trustworthiness | 7 | 1 (unverified) | 2 | 0 | 🔴 (Critical = mutation-test deferral on backend C8.1) | | C9 Operability | 1 | 0 | 6 | 3 | 🔴 | | C10 Code Quality Sanity | 9 | 0 | 6 | 4 | 🔴 | **Overall aggregate: 🔴.** Only C2 (Local Bootstrap) clears the 🟡 threshold; every other category has Critical fails or pass-rate below 50%. The pattern is unambiguous: code-quality categories (C6 layering, C8 tests, C10 sanity) fail on a small number of identifiable hotspots, while doc-quality categories (C1, C3, C5, C7, C9) fail across the board because the human-facing documentation layer is largely absent. --- ## 4. Top-10 prioritized actions Selected for maximum unblock per unit of work — each closes multiple rubric categories or removes a refactor-gating risk. 1. **Author root `README.md`** — ≤3-sentence purpose, 5 components table, prerequisites with versions, single bootstrap command, default-creds note, links to COLLABORATING / CODESTYLE / docs / Gitea issues. Source: AUDIT-5 (#392). Unblocks: C1.1, C1.2, C1.4, C2.2, C2.3. 2. **Author `docs/ARCHITECTURE.md` + `docs/GLOSSARY.md`** — narrative wrapping `docs/architecture/c4-diagrams.md`; glossary covers Person ≠ AppUser, Briefwechsel/Conversation, Chronik/Activity, Geschichte ≠ Document, Kurrent vs Sütterlin, the four `scriptType` values. Source: AUDIT-2 (#389), AUDIT-3 (#390), AUDIT-4 (#391), AUDIT-5 (#392). Unblocks: C1.3, C3.1, C3.2, C3.3. 3. **Fix the 11 cross-domain repository injections (C6.2) and 2 controller-injects-repository violations (C6.1) before REFACTOR-1 runs** — `MassImportService`/`Thumbnail*`/`TranscriptionQueueService`/training exports/`SenderModelService`/`OcrTrainingService`/`TranscriptionService`/`AnnotationService`/`PasswordResetService` → introduce read/write helpers on owning services; `StatsController` → new `StatsService`; `AuthE2EController` → `PasswordResetService.findE2EToken(email)`. Source: AUDIT-2 (#389) §9. Unblocks: C6.1, C6.2 — and removes the gating risk for REFACTOR-1. 4. **Carve `lib/components/` into `lib/<domain>/{component,store,util,hook,type}` per the canonical set** plus `lib/shared/{ui,discussion,dashboard,i18n}` — REFACTOR-2 territory; mapping spelled out file-by-file in AUDIT-1 §5. Source: AUDIT-1 (#388). Unblocks: C3.5, C3.6, C4.1, C4.2, C6.3, C6.4. 5. **Migrate backend to domain packages** (`document/`, `person/`, `tag/`, `user/`, `geschichte/`, `notification/`, `ocr/`, `shared/{file-storage,audit,dashboard,import,transcription-queue,security,error-handling,config}`) — REFACTOR-1 territory, but only after action #3 lands so the package move is purely cosmetic. Source: AUDIT-2 (#389). Unblocks: C3.4, C4.1, C4.2. 6. **Migrate every `CLAUDE.md` to a `README.md` companion** per AUDIT-5 §2a (root, docs, scripts, .devcontainer, infra; plus per-subsystem `backend/README.md`, `frontend/README.md`, `ocr-service/README.md`); convert each `CLAUDE.md` into a thin pointer. Replace `backend/HELP.md` (Initializr boilerplate) with real `backend/README.md`; replace `frontend/README.md` (`sv create` stub) with real frontend README; promote `ocr-service/CLAUDE.md` to `ocr-service/README.md` and fix the stale `/training/submit` endpoint table + missing `BLLA_MODEL_PATH` env var. Source: AUDIT-1 (#388), AUDIT-2 (#389), AUDIT-3 (#390), AUDIT-5 (#392). Unblocks: C2.5, C5.3, C7.1. 7. **Pick one error pattern and migrate** — convert all `RuntimeException`/`IllegalArgumentException`/`IllegalStateException` in `MassImportService:159`, `FileService:91,167`, `RestClientOcrClient:218,250,288`, `PolygonConverter`, `DocumentService:967` to `DomainException`; convert controller `ResponseStatusException` validation to `@Valid` + `DomainException`. Source: AUDIT-2 (#389) C5.2. Unblocks: C5.2 — and gives the frontend a uniform `code` to translate. 8. **Run PIT mutation testing on `DocumentService` (971 LOC), `PersonService`, `TranscriptionService`, `MassImportService`, `NotificationService`, `OcrAsyncRunner`** + frontend mutation pass on `Person*`, `Transcription*`, `Annotation*`, `Document*`, `bulkSelection`, `mention`, `blockConflictMerge`, `saveBlockWithConflictRetry` specs — without this, REFACTOR-1/REFACTOR-2's "tests still green" signal is unreliable for the largest blast-radius services. Source: AUDIT-1 (#388) §9.1, AUDIT-2 (#389) §9. Unblocks: C8.1 (currently Unverified-Critical). 9. **Author `docs/SCHEMA.md` (domain-grouped table overview)** + add a one-time migration to rename `users` → `app_users` (and `users_groups`, `comment_mentions.user_id`, `audit_log.actor_id` cascading FKs) **OR** add `COMMENT ON TABLE users IS 'AppUser accounts — NOT historical persons. See persons.'`; rename `tag` → `tags` and add `@Table(name="tags")` to `Tag.java`; patch V37/V43 sequence holes with skipped-stub migrations; resolve audit-log REVOKE no-op (split DB roles or drop the REVOKE with a why-comment); fix or delete the stale `scripts/schema.sql` snapshot. Source: AUDIT-4 (#391). Unblocks: C3.3, C7.5, C9.3, C10.4. 10. **Repo hygiene + dead-code sweep before refactor** — extend `.gitignore` to cover `proofshot-artifacts/`, `.agent/`, `.claude/worktrees/`, root `node_modules/`, `.pytest_cache/`, `**/test-results.locked/`, `**/.svelte-kit.old/`; `git rm -r --cached` the existing committed copies; delete the 6 `frontend/src/lib/paraglide_bak*/` dirs (~4 MB); delete dead frontend components (`OcrProgress.svelte`, `DocumentStatusChip.svelte`, `ExpandableText.svelte`, `DashboardRecentDocuments.svelte`, `lib/index.ts`, `lib/utils.ts`); delete `model/DocumentSort.java` (duplicate); delete `routes/demo/`; remove stray `console.log` in `routes/api/tags/+server.ts:27`; index `docs/specs/` with a README/index.html marking each spec Active/Superseded/Implemented; migrate `docs/TODO-backend.md` and `docs/TODO-frontend.md` items to Gitea then delete; pick between `docs/style-guide.html` and `docs/STYLEGUIDE.md`. Source: AUDIT-1 (#388) §7, AUDIT-2 (#389) §7, AUDIT-5 (#392) §7. Unblocks: C1.5, C10.1, C10.4. --- ## 5. Refactor readiness verdict (gate for Epic 4) **🔴 — NO, address Critical findings first.** Running REFACTOR-1 and REFACTOR-2 against the current codebase would carry the existing layering violations into the new domain packages, where they would be visible as cross-package imports rather than fixed — masking the real defect under a cosmetic move. Specific gating concerns: - **C6.2 cross-domain repository injections (11 sites)** — `MassImportService → DocumentRepository`; `Thumbnail*` (3 services) → `DocumentRepository`; `TranscriptionQueueService → DocumentRepository`; `Segmentation/TrainingDataExportService` → `Document/Block/Annotation` repos (3 each); `SenderModelService → TranscriptionBlockRepository`; `OcrTrainingService → TranscriptionBlockRepository`; `TranscriptionService → AnnotationRepository`; `AnnotationService → TranscriptionBlockRepository`; `PasswordResetService → AppUserRepository`. Source: AUDIT-2 §3 C6.2 + §9. Each must be replaced with a service-level method on the owning domain *before* the package move. - **C6.1 controller-injects-repository violations (2 sites)** — `StatsController` injects `PersonRepository` + `DocumentRepository`; `AuthE2EController` injects `PasswordResetTokenRepository`. Source: AUDIT-2 §3 C6.1. - **C8.1 mutation-validation deferred** — neither audit could verify the test suites that protect the largest refactor-blast-radius services would catch a regression. Without a mutation pass, the "tests still green" signal during REFACTOR-1/REFACTOR-2 is unreliable. Source: AUDIT-1 §9.1, AUDIT-2 §3 C8.1. - **Boundary decisions still pending** — thumbnails as `shared/file-storage` or `document.thumbnail`? `Document.trainingLabel` field stays in `document` or moves to `ocr`? `AppUser` notification-preference fields stay on User or move to `notification`? Page-local-vs-`lib/` paradigm for the 39+ frontend components currently inside `routes/`? `PersonMentionEditor` → person or shared/discussion? Source: AUDIT-1 §9.4, AUDIT-2 §9. **Path to 🟢:** complete actions #3, #7, #8 from §4 (fix layering violations, unify error handling, run mutation tests), then ratify the open boundary decisions in §7 below. The `users → app_users` rename should run as a **separate, dedicated migration** before or after REFACTOR-1 — never bundled — because it touches FKs in 6+ tables. --- ## 6. Evaluation readiness verdict (gate for sending to Anja/Tobias) **🔴 — NO, run Epics 2–5 first.** A first-time reader cannot form a correct mental model from the documentation alone today, and several of the rubric's measurable success criteria fail outright: - **O1 (sketch domains and data flow in ≤30 min):** fails — no `docs/ARCHITECTURE.md`, no domain enumeration in any human-targeted doc; the canonical domain set lives only in #387 first comment. - **O2 (≥8 of 10 "find or place" questions):** fails — `lib/components/` is a flat 79-file bucket; backend is layer-packaged; "find every Person file" requires scanning ≥6 different parents (AUDIT-1 C4.1). - **O3 (full stack from `README.md` in ≤15 min):** fails outright — there is no root `README.md`. `CLAUDE.md` opens with "This file provides guidance to Claude Code". - **O4 (zero "ask Marcel"/"non-obvious"/"Claude generated"):** **passes** (one of the few rubric items that passes cleanly; `grep` is clean across all subsystems). Specific blockers that must be cleared before sending: 1. Root `README.md` (action #1) 2. `docs/ARCHITECTURE.md` + `docs/GLOSSARY.md` (action #2) — Tobias will not get past `Briefwechsel`, `Chronik`, `Stammbaum`, `Richtlinien`, `Aktivitäten`, `Geschichte` without it; Anja will not get past `Person` ≠ `AppUser` without it. 3. Real `backend/README.md` (replacing `HELP.md`) and real `frontend/README.md` (replacing the `sv` stub) and real `ocr-service/README.md` (action #6). 4. The `users` table → `AppUser` entity name confusion resolved (action #9) — this is the single thing most likely to make Tobias close the IDE saying "this is weird". 5. `docs/specs/` indexed (action #10) — 47 ungrouped HTML files are the single biggest discoverability failure of `docs/`. 6. `docs/TODO-backend.md` and `docs/TODO-frontend.md` migrated to Gitea then deleted — they actively contradict the project's own workflow rule. The Epic 5 cleanup tasks (action #10) alone are not sufficient. Actions #1, #2, #6, and #9 are documentation-only and could conceivably ship without REFACTOR-1/REFACTOR-2, but doing so would have the docs describe the *old* structure that the refactor is about to replace — not useful. Recommendation: ship Epics 2–5 in the order Critical-fixes → REFACTOR-1 → REFACTOR-2 → docs migration → cleanup, and re-run AUDIT-6 before sending. --- ## 7. Open questions for the user 1. **Thumbnails — `shared/file-storage` or `document.thumbnail`?** AUDIT-2 §5 default is `shared/file-storage` since thumbnails are a file-storage concern over any document type, but the entity-coupling argument for `document.thumbnail` is also valid. Pick one before REFACTOR-1. 2. **`Document.trainingLabel` field — stays in `document` or moves to `ocr`?** AUDIT-2 §5 / AUDIT-4 §5: the field lives on `Document` but its only consumer is the OCR training pipeline. Same question for the `document_training_labels` table. 3. **`AppUser` notification-preference fields (`isNotifyOnReply`, `isNotifyOnMention`) — stay on `AppUser` or move to `notification`?** AUDIT-2 §5. 4. **`/users/[id]` (Person profile) vs `/admin/users/[id]` (AppUser admin) — rename, merge, or document?** AUDIT-1 §7, §10.4. The semantic overlap directly threatens the Person ≠ AppUser glossary distinction. 5. **`PersonMentionEditor` — `person/` or `shared/discussion/`?** AUDIT-1 §5. Is "person mention" a person concept or a discussion concept? 6. **`EnrichmentBlock`, `DistributionBar`, `ConversationThumbnail` — boundary calls.** AUDIT-1 §5 lists each as ambiguous between two homes. 7. **Page-local components inside `routes/` (39+ files) — co-location stays as a legitimate paradigm, or everything moves to `lib/<domain>/`?** AUDIT-1 §5, §9.2. REFACTOR-2 must answer this before moving anything; otherwise the result will be inconsistent. 8. **`users` table rename — execute the full `users → app_users` migration (touches FKs in 6+ tables) or settle for a `COMMENT ON TABLE` clarification?** AUDIT-4 §3 C3.3. The full rename is correct but expensive; the comment is cheap but doesn't fix the lexical confusion in code that reads `users` directly. 9. **Audit-log append-only — split DB roles so V46/V47 `REVOKE` actually bites, or drop the REVOKE statements with a comment?** AUDIT-4 §3 C7.5 / §7. Currently the migration claims protection it doesn't deliver. 10. **`docs/style-guide.html` (1403 lines) vs `docs/STYLEGUIDE.md` (389 lines) — which is the source of truth?** AUDIT-5 §5, §10. 11. **`docs/security-guide.md` (persona-voiced "Nora 'NullX' Steiner") — deliverable doc or training material?** AUDIT-5 §5. 12. **Permission enum drift — `Permission.ANNOTATE_ALL` and `Permission.BLOG_WRITE` exist in `Permission.java` but are not in `backend/CLAUDE.md`'s documented list of 6.** AUDIT-2 §8. Document or remove. 13. **`AdminController` — slice per-domain (import endpoints to `shared/import/`, backfill endpoints with their owners) or keep as a thin façade?** AUDIT-2 §5. 14. **`TranscriptionQueueService` — `shared/transcription-queue` or `document.transcription/queue/`?** AUDIT-2 §5. The queue is a read view over Document; canonical set says `shared/`, but the read coupling is to one domain only.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#393