docs(legibility): migrate CLAUDE.md rules into human docs; mark migrated content #401
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
Part of Epic #394 — Documentation. This is DOC-7: the cleanup pass on existing
CLAUDE.mdfiles. Today these files contain a mix of (a) LLM-only behavioral instructions and (b) genuine architectural rules and conventions that humans need too. Anything in category (b) needs to live in a human-readable doc — and the CLAUDE.md needs to point there instead of duplicating.Per the Legibility Rubric, this addresses C5.3 (Major) and supports C7.
Files to process
CLAUDE.mdfrontend/CLAUDE.mdbackend/CLAUDE.mdocr-service/CLAUDE.mddocs/CLAUDE.mdscripts/CLAUDE.md.devcontainer/CLAUDE.mdRequired pattern
For each CLAUDE.md, classify every section into one of three buckets:
Bucket 1 — Human-relevant rules / conventions / architecture
Examples (from current CLAUDE.md): backend layering rules, frontend styling conventions, entity code style, error handling pattern, date handling, brand colors.
Action: migrate to the appropriate human doc:
docs/ARCHITECTURE.md(DOC-2)CONTRIBUTING.md(DOC-4)docs/GLOSSARY.md(DOC-3)README.md(DOC-6)Then in the original CLAUDE.md, replace the section with a one-line pointer:
*Migrated to docs/ARCHITECTURE.md §3.*Bucket 2 — LLM-only behavioral instructions
Examples: "always run docker commands on a single line for easy copy-paste", "use TaskCreate to track progress", "never write production code without TDD".
Action: keep in CLAUDE.md as-is. These are legitimately LLM-targeted.
Bucket 3 — Stale / obsolete / contradicted-by-refactor content
Examples (post-refactor): the "Layering Rules (strictly enforced)" section in
backend/CLAUDE.mddocuments the layered package structure that the refactor replaces. After REFACTOR-1, that section needs rewriting to document the domain-based package structure.Action: delete or rewrite. Note in this issue's PR description what was removed.
Anti-patterns
Acceptance criteria
Dependency
Hard dependency on DOC-1, DOC-2, DOC-3, DOC-4 (the human docs must exist before content can migrate into them) and REFACTOR-1, REFACTOR-2 (because some CLAUDE.md content describes the current layered structure).
This issue is therefore one of the LAST docs issues to land — most likely sequenced after Epic 4.
Definition of Done
All 7 CLAUDE.md files processed; PR merged. Closing comment includes the per-file migration summary.
🏗️ Markus Keller — Senior Application Architect
Observations
docs/ARCHITECTURE.md) and DOC-4 (CONTRIBUTING.md) do not yet exist as files — currently onlyCOLLABORATING.mdandCODESTYLE.mdexist at root. The issue says "hard dependency on DOC-1/2/3/4," so the implementer must not create pointer-only CLAUDE.md entries pointing at files that don't exist yet.controller/,service/,repository/, etc. as top-level packages. The issue correctly classifies this as Bucket 3. Do not migrate this section — rewrite it to document the post-refactor domain structure.docs/CLAUDE.mdandscripts/CLAUDE.mdare primarily human-relevant operational docs (folder structure, script descriptions, ADR format). These are almost entirely Bucket 1 and should largely migrate to their respectiveREADME.mdfiles per the issue's DOC-6 pattern.Recommendations
docs/ARCHITECTURE.md §3." Do this mapping as a PR description table, not as ad-hoc edits. The issue's acceptance criteria require this anyway.README.mdsections.docs/ARCHITECTURE.md— you'd be migrating a description of a structure that no longer exists post-refactor.*Migrated to docs/ARCHITECTURE.md §3.*— this is good. Make the section header a link too, so the LLM can navigate:*See [docs/ARCHITECTURE.md §Backend Package Structure](../docs/ARCHITECTURE.md#backend-package-structure).*👨💻 Felix Brandt — Senior Fullstack Developer
Observations
backend/CLAUDE.md. For example, the "Package Structure", "Layering Rules", "Entity Code Style", "Services", "Error Handling", and "Security" sections appear in both files. This is the clearest Bucket 1 content — it's architectural convention that human contributors need../mvnw spring-boot:run,npm run dev, etc.) is LLM-useful but also human-useful. I'd classify it as Bucket 2 (keep in CLAUDE.md) and also put it in the respective domain README files so humans find it. Duplication here is acceptable — commands are stable and low-maintenance to keep in sync.frontend/CLAUDE.mdhas a clean structure already: API Client Pattern, Form Actions Pattern, Date Handling, UI Component Library, Styling Conventions. Most of this is Bucket 1 and overlaps withCODESTYLE.md. Specifically the "API Client Pattern" and "Date Handling" sections are developer conventions that belong in the frontend README or CODESTYLE.scripts/CLAUDE.mdis almost entirely Bucket 1 — it documents what each script does and how to run it. This belongs in ascripts/README.md(DOC-6 pattern). The CLAUDE.md can then be reduced to: "Scripts are inscripts/. Seescripts/README.mdfor usage."Recommendations
scripts/CLAUDE.mdanddocs/CLAUDE.mdare almost pure Bucket 1 operational content. Createscripts/README.mdand verifydocs/README.md(or the existingdocs/CLAUDE.mdcan just become a pointer). This builds confidence in the classification pattern before tackling the denser files.frontend/CLAUDE.md: The "Key UI Components" table (PersonTypeahead, PersonMultiSelect, etc.) is exactly the kind of component inventory a human contributor needs. It belongs in the frontend README, not in CLAUDE.md. The "Vite Proxy" and "i18n" sections are similarly human-relevant.🔒 Nora Steiner (NullX) — Application Security Engineer
Observations
@RequirePermission,DomainException.forbidden(), parameterized JPQL queries) documents the security model. This is Bucket 1 — it describes conventions that human contributors must follow during code review. If it stays only in CLAUDE.md and not indocs/ARCHITECTURE.mdorCODESTYLE.md, a human reviewer won't find it.backend/CLAUDE.mdsecurity section lists available permissions (READ_ALL,WRITE_ALL,ADMIN, etc.) — this is a human reference for permission scoping during feature work. It belongs indocs/ARCHITECTURE.md §Securityor a dedicateddocs/security-guide.md. Note thatdocs/security-guide.mdalready exists. Check whether this content would duplicate or complement it.ocr-service/CLAUDE.mddocuments security-relevant environment variables:TRAINING_TOKEN,ALLOWED_PDF_HOSTS. These are operational security controls. A human deploying the service needs to know about them. They belong inocr-service/README.md(or the existing Docker Compose documentation), not just in an LLM instruction file.Recommendations
docs/security-guide.mdbefore deciding on the target doc. Prefer consolidating into the existing security guide rather than scattering across multiple files.@RequirePermissionusage example is both a developer guide (Bucket 1 → CODESTYLE.md or ARCHITECTURE.md) and an LLM reminder (Bucket 2 — keep a brief pointer). A one-line LLM hint + a pointer to the full security model is the right split.🧪 Sara Holt — QA Engineer & Test Strategist
Observations
This issue has no test deliverable in the traditional sense — there are no new tests to write for a documentation refactor. The verification mechanism is the CLEANUP-5 audit re-run mentioned in the acceptance criteria, which will check C5.3 (the Legibility Rubric metric this addresses). That audit is the acceptance gate.
The acceptance criteria are well-formed and verifiable:
One gap in the acceptance criteria: there's no explicit check that pointer links resolve correctly. A CLAUDE.md pointer like
*Migrated to docs/ARCHITECTURE.md §3.*can be written and merged even if the target section heading doesn't match. This is a common failure mode in doc migrations.The dependency on DOC-1 through DOC-4 and REFACTOR-1/2 means this issue cannot be tested in isolation. The CLEANUP-5 audit presumably runs against the full state of the docs after all predecessors land.
Recommendations
🎨 Leonie Voss — UX Designer & Accessibility Strategist
Observations
*Migrated to docs/ARCHITECTURE.md §3.*— this is italic text with no link. A developer reading the CLAUDE.md in a terminal or in a raw file view (not a rendered Markdown viewer) gets a file path but no clickable anchor. This is a minor friction point but worth noting.docs/CLAUDE.mdcurrently documents the specs folder, ADR format, architecture diagrams, and infrastructure runbooks. This is entirely human-relevant. After migration, thedocs/folder's CLAUDE.md can potentially be deleted entirely if its content migrates to adocs/README.md. A CLAUDE.md with only pointer lines and no actual LLM guidance provides no value.Recommendations
*Migrated → [docs/ARCHITECTURE.md §Backend Package Structure](../docs/ARCHITECTURE.md#backend-package-structure)*— renders as a clickable link in Gitea, navigable in most editors.docs/CLAUDE.mdandscripts/CLAUDE.mdlikely fall into this "potentially deletable after migration" category. Worth a note in the PR description if that turns out to be the case.⚙️ Tobias Wendt — DevOps & Platform Engineer
Observations
ocr-service/CLAUDE.mddocuments the environment variables table (KRAKEN_MODEL_PATH,TRAINING_TOKEN,ALLOWED_PDF_HOSTS, batch size configs, etc.). This is operational documentation that a human deploying or configuring the service needs. It is currently only in CLAUDE.md. It belongs inocr-service/README.md(DOC-6)..devcontainer/CLAUDE.md(96 lines) documents the dev container configuration: Java 21 + Node 24 features, VS Code extensions, port forwarding, known limitations. This is entirely human-relevant. It belongs in a devcontainerREADME.mdor directly indevcontainer.jsonas comments. The CLAUDE.md for the devcontainer is the clearest case of a file that is 100% Bucket 1 and could be replaced with a pointer after migration.scripts/CLAUDE.mddocuments scripts includingreset-db.sh(described as "Destructive operation — only for development!"). This kind of safety warning must not be lost in migration — if it moves toscripts/README.md, it needs to be equally prominent there.Recommendations
ocr-service/CLAUDE.md: all environment variable documentation is Bucket 1 →ocr-service/README.md. The ocr-service README already exists and already covers some of this. Merge, don't duplicate..devcontainer/CLAUDE.md: classify everything as Bucket 1. After migration, this file can be reduced to a single pointer line or deleted.scripts/CLAUDE.mdmigration preserves the destructive-operation warnings with the same visual emphasis (emoji or bold text) in the targetscripts/README.md. A plain text migration that loses the warning prominence is a ops risk.📋 Elicit — Requirements Engineer
Observations
The issue is well-specified. It has a clear bucket classification model, explicit migration targets, an anti-pattern list, and measurable acceptance criteria. This is better than average for a documentation chore.
One ambiguity in the classification boundary: The issue distinguishes Bucket 1 (human-relevant rules) from Bucket 2 (LLM-only behavioral instructions), but some content is genuinely both. For example, the API client pattern ("use
!result.response.okfor error checking, notif (result.error)") is:The issue's current framing implies a section must go into one bucket. A clearer rule: classify by primary audience. If the rule exists mainly because an LLM would get it wrong without the reminder, it's Bucket 2. If the rule exists because a human might get it wrong during review, it's Bucket 1. In practice many sections have both audiences — the migration should not delete LLM-targeted reminders just because the underlying convention also belongs in a human doc.
The "Bucket 3 — Stale/Obsolete" classification has a dependency risk: the backend CLAUDE.md documents the current layered package structure (
controller/,service/,repository/), which will be wrong after REFACTOR-1. But REFACTOR-1 hasn't landed yet. If this DOC-7 issue is implemented before REFACTOR-1, the Bucket 3 content should be tagged for post-REFACTOR-1 rewrite, not deleted yet. The issue notes this sequencing ("most likely sequenced after Epic 4") — but it's worth making this a hard acceptance-criteria gate, not just a note.The acceptance criteria require a "per-file summary" in the PR description. This is a good traceability mechanism, but the format isn't specified. Recommend requiring a table:
File | Sections migrated | Sections kept | Sections deleted/rewritten | Migration targets.Recommendations
🗳️ Decision Queue — Action Required
3 decisions need your input before implementation starts.
Architecture / Sequencing
<!-- TODO: rewrite post-REFACTOR-1 -->comment and remove from migration scope. Either way, an explicit acceptance criterion gate should be added. (Raised by: Elicit, Markus)Documentation
Dual-audience content classification — Some sections serve both LLMs and humans (e.g. the API Client Pattern's
!result.response.okreminder infrontend/CLAUDE.md). The issue's bucket model implies a section goes into exactly one bucket. Do you want to formalize: "classify by primary audience; dual-audience content gets canonical text in the human doc + a brief pointer-reminder in CLAUDE.md"? Or is the intent that dual-audience content just stays in Bucket 2 (CLAUDE.md) and only clearly human-only architectural rules migrate? (Raised by: Elicit, Felix)Pointer link format — The issue proposes
*Migrated to docs/ARCHITECTURE.md §3.*as the replacement text. Leonie flags that this is plain italic text with no clickable link, which is harder to navigate. Do you want to standardize on Markdown links (*See [docs/ARCHITECTURE.md §Section](../docs/ARCHITECTURE.md#section)*) or is the simpler text-only format intentional for some reason (e.g. terminal-readability for LLMs)? This choice should be consistent across all 7 files. (Raised by: Leonie)✅ Decision Queue — Resolved
The 3 decisions raised in #401#issuecomment-6299:
1. Bucket-3 deletion gate for backend CLAUDE.md → skip Bucket-3 deletions until REFACTOR-1 merges (Option A)
Markus and Elicit converge on this: backend CLAUDE.md's Package Structure and Layering Rules sections describe the current layered structure that REFACTOR-1 replaces. If DOC-7 deletes them now, the project loses its only authoritative description of the current structure before REFACTOR-1 has produced its replacement.
Resolution:
<!-- TODO: rewrite post-REFACTOR-1 — see Epic 4 -->.This adds DOC-7's hard dependency on REFACTOR-1 for completion, but not for the PR itself — the PR can ship most of the work and leave a tagged TODO.
2. Dual-audience content classification → canonical to human doc + brief pointer-reminder in CLAUDE.md (formalize Elicit's framing)
Elicit and Felix both surfaced this. Some content (e.g. "use
!result.response.ok, notif (result.error)") serves both audiences:result.error— that breaks when the spec has no error responses defined") is an LLM-targeted note that prevents a common generation mistake → stays in CLAUDE.md.Formalized rule (per epic-level D3 + this issue's framing):
For dual-audience sections:
The reminder lines stay in CLAUDE.md when they exist mainly to prevent LLM mistakes. The canonical text lives in the human doc.
This is consistent with the epic-level Decision Queue D3 "pointer-comment policy" — DOC-7 just adds the optional reminder line for LLM-helpful content.
3. Pointer link format → Markdown links, not plain text (per Leonie)
Standardize on the linked form across all CLAUDE.md migrations:
Example:
Plain text loses no terminal-readability worth keeping (the LLM can still parse the link target), but renders as a clickable link in Gitea, VS Code Markdown preview, and most editors. No reason to use the unlinked form.
📌 Additional persona feedback to fold into implementation
Markus: map every section to a target before touching files — present the mapping as a PR description table, not as ad-hoc edits. Required by AC anyway.
Markus: root CLAUDE.md and backend CLAUDE.md duplicate architectural content almost word-for-word. Resolve the duplication by migrating to the canonical human doc once and leaving pointers in both CLAUDE.md files.
Markus:
docs/CLAUDE.mdandscripts/CLAUDE.mdare almost entirely Bucket 1. After migration they may collapse to pointer-only files — flag for potential deletion (per Leonie's recommendation).Felix: start easiest first —
scripts/CLAUDE.mdanddocs/CLAUDE.mdare mostly pure Bucket 1 operational content. Builds confidence in the classification pattern before tackling root + backend + frontend (the dense ones).Felix:
frontend/CLAUDE.md"Key UI Components" table (PersonTypeahead, PersonMultiSelect, TagInput) belongs in the relevant frontend domain READMEs (#400) — split it: PersonTypeahead →lib/person/README.md, TagInput →lib/tag/README.md. Don't keep a generic "components" table in CLAUDE.md.Nora: cross-check all security-sensitive sections against
docs/security-guide.mdbefore deciding migration target. Prefer consolidating into the existing security guide rather than scattering. Specifically: backend CLAUDE.md's Security/Permissions section, ocr-service/CLAUDE.md's TRAINING_TOKEN/ALLOWED_PDF_HOSTS, root CLAUDE.md's Security/Permissions reference.Nora: flag every security-relevant migration in the per-file PR summary table — reviewer must verify those classifications.
Sara: add AC: "All pointer links in CLAUDE.md files resolve to existing headings." Verifiable with a markdown link checker (
markdown-link-checkor grep + custom script). Broken pointer links are invisible during review but confusing to future readers.Sara: PR description's per-file summary should include a "pointer text added to CLAUDE.md" column for direct mapping verification.
Leonie: flag Tobias's point —
.devcontainer/CLAUDE.mdis 100% Bucket 1; after migration it can be reduced to a pointer line OR deleted entirely. Same fordocs/CLAUDE.md. Make this an explicit decision per file.Tobias: preserve destructive-operation warnings with the same visual emphasis when migrating
scripts/CLAUDE.md→scripts/README.md. The "Destructive — only for development!" callout onreset-db.shmust survive the migration.Tobias:
ocr-service/CLAUDE.md→ checkocr-service/README.md(DOC-6 / #400) — already exists and already covers some of this. Merge, don't duplicate.Elicit: structured per-file migration table format:
Required in PR description.
Elicit: explicit AC sequencing — DOC-7's PR ships fully when DOC-2 (#396), DOC-4 (#398), DOC-5 (#399), DOC-6 (#400) are all merged. Do not merge DOC-7 before its migration targets exist.
🔗 Sequencing summary (so future-you doesn't re-derive it)
DOC-7 is the last doc PR. Its dependencies:
Suggested order: DOC-3 → DOC-1 → DOC-2 → DOC-4 → DOC-5 → DOC-6 (stub phase with refactor) → REFACTOR-1 → REFACTOR-2 → DOC-6 (complete) → DOC-7 (this issue) → CLEANUP-5 audit re-run.
Status: Ready when its dependencies land. Authoring can start as soon as DOC-2, DOC-4, DOC-5 are merged (Bucket-1 migrations have somewhere to go).
✅ DOC-7 implemented — PR #445
All 7 CLAUDE.md files processed. Branch:
feat/issue-401-claude-migration.Per-file summary
scripts/CLAUDE.mdscripts/README.md.devcontainer/CLAUDE.md.devcontainer/README.mddocs/CLAUDE.mddocs/README.mdocr-service/CLAUDE.mdocr-service/README.mdDOC-6)backend/CLAUDE.mdroot CLAUDE.mdfrontend/CLAUDE.mdFixes applied during migration
backend/CLAUDE.mdstaleerrors.tspath fixed →frontend/src/lib/shared/errors.tsANNOTATE_ALLandBLOG_WRITEreset-db.shpreserved inscripts/README.md(per Tobias)Merge order
PR #445 must merge after DOC-2 (#441), DOC-4 (#442), DOC-5 (#443), DOC-6 (#444) — pointer links target files introduced by those PRs.