refactor(frontend): restructure lib/ from flat-by-type to domain-based #408

Closed
opened 2026-05-04 16:14:35 +02:00 by marcel · 8 comments
Owner

Context

Part of Epic #406 — Big-bang restructure. This is REFACTOR-2: move every component, util, hook, and store in frontend/src/lib/ from flat-by-type folders into per-domain folders mirroring the backend (per D-FE decisions ratified in #387 first comment).

Single big-bang PR. All tests green throughout.

Target lib/ structure (mirrors backend domain names per D2 stack-symmetry)

frontend/src/lib/
├── document/                            # mirrors backend document/
│   ├── DocumentViewer.svelte
│   ├── DocumentTopBar.svelte
│   ├── DocumentRow.svelte
│   ├── DocumentList.svelte             # if reusable; else stays in routes/
│   ├── DocumentMetadataDrawer.svelte
│   ├── DocumentStatusChip.svelte
│   ├── DocumentThumbnail.svelte
│   ├── DocumentMultiSelect.svelte
│   ├── UploadSuccessBanner.svelte
│   ├── EnrichmentBlock.svelte
│   ├── DashboardNeedsMetadata.svelte
│   ├── DocumentEditLayout.svelte       # was lib/components/document/
│   ├── BulkDocumentEditLayout.svelte
│   ├── BulkSelectionBar.svelte
│   ├── BulkDropZone.svelte
│   ├── UploadZone.svelte
│   ├── UploadSaveBar.svelte
│   ├── WhoWhenSection.svelte
│   ├── DescriptionSection.svelte
│   ├── ScopeCard.svelte
│   ├── FieldLabelBadge.svelte
│   ├── FileSwitcherStrip.svelte
│   ├── ScriptTypeSelect.svelte
│   ├── ReadyColumn.svelte
│   ├── SegmentationColumn.svelte
│   ├── search.ts
│   ├── filename.ts
│   ├── groupDocuments.ts
│   ├── documentStatusLabel.ts
│   ├── validateFile.ts
│   ├── useFileLoader.svelte.ts
│   ├── usePdfRenderer.svelte.ts
│   ├── bulkSelection.svelte.ts
│   ├── annotation/                      # sub-package
│   │   ├── AnnotationLayer.svelte
│   │   ├── AnnotationShape.svelte
│   │   └── AnnotationEditOverlay.svelte
│   ├── transcription/                   # sub-package
│   │   ├── TranscriptionBlock.svelte
│   │   ├── TranscriptionEditView.svelte
│   │   ├── TranscriptionReadView.svelte
│   │   ├── TranscriptionPanelHeader.svelte
│   │   ├── TranscriptionColumn.svelte
│   │   ├── TranscriptionSection.svelte  # was lib/components/document/
│   │   ├── transcriptionMarkers.ts
│   │   ├── blockConflictMerge.ts
│   │   ├── saveBlockWithConflictRetry.ts
│   │   ├── useBlockAutoSave.svelte.ts
│   │   └── useBlockDragDrop.svelte.ts
│   └── viewer/                          # PDF viewer specific to documents
│       ├── PdfViewer.svelte
│       └── PdfControls.svelte
├── person/                              # per D-FE-1
│   ├── PersonTypeahead.svelte           # cross-domain consumption is normal
│   ├── PersonMultiSelect.svelte
│   ├── PersonChip.svelte
│   ├── PersonChipRow.svelte
│   ├── PersonHoverCard.svelte
│   ├── PersonTypeBadge.svelte
│   ├── PersonTypeSelector.svelte
│   ├── personFormat.ts
│   ├── personLifeDates.ts
│   ├── person-validation.ts
│   ├── personHoverCard.ts               # type
│   ├── relationshipLabels.ts
│   ├── relationship/                    # sub-package per D-OQ-7
│   │   ├── AddRelationshipForm.svelte
│   │   ├── RelationshipChip.svelte
│   │   └── RelationshipPill.svelte
│   └── genealogy/                       # contains the /stammbaum components
│       ├── StammbaumTree.svelte
│       ├── StammbaumCard.svelte
│       └── StammbaumSidePanel.svelte
├── tag/                                 # per D-FE-2
│   ├── TagInput.svelte
│   ├── TagChipList.svelte
│   └── TagParentPicker.svelte
├── user/
│   ├── UserProfileSection.svelte
│   ├── UserPasswordSection.svelte
│   └── UserGroupsSection.svelte
├── geschichte/
│   ├── GeschichteEditor.svelte
│   └── GeschichtenCard.svelte
├── notification/
│   ├── NotificationBell.svelte
│   ├── NotificationDropdown.svelte
│   └── notifications.svelte.ts          # store
├── ocr/                                 # admin-only OCR UI
│   ├── OcrProgress.svelte
│   ├── OcrTrigger.svelte
│   ├── OcrTrainingCard.svelte
│   ├── SegmentationTrainingCard.svelte
│   ├── TrainingHistory.svelte
│   ├── translateOcrProgress.ts
│   └── training.ts                       # type
├── conversation/                        # Tier 2 derived per D-OQ-1
│   ├── README.md                        # explain the "derived domain" pattern
│   └── ConversationThumbnail.svelte
├── activity/                            # Tier 2 derived per D-OQ-1
│   ├── README.md
│   ├── ChronikTimeline.svelte           # was lib/components/chronik/
│   ├── ChronikRow.svelte
│   ├── ChronikFilterPills.svelte
│   ├── ChronikFuerDichBox.svelte
│   ├── ChronikEmptyState.svelte
│   ├── ChronikErrorCard.svelte
│   └── DashboardActivityFeed.svelte
└── shared/                              # cross-cutting
    ├── api.server.ts
    ├── errors.ts
    ├── types.ts
    ├── relativeTime.ts
    ├── utils.ts
    ├── discussion/                       # per D-FE-3
    │   ├── CommentMessage.svelte
    │   ├── CommentThread.svelte
    │   ├── MentionEditor.svelte
    │   ├── MentionDropdown.svelte
    │   ├── PersonMentionEditor.svelte
    │   ├── mention.ts
    │   └── mentionSerializer.ts
    ├── dashboard/                        # per D-FE-4
    │   ├── DashboardResumeStrip.svelte
    │   ├── DashboardRecentDocuments.svelte
    │   ├── DashboardFamilyPulse.svelte
    │   └── MissionControlStrip.svelte
    ├── primitives/                       # generic UI building blocks
    │   ├── BackButton.svelte
    │   ├── ConfirmDialog.svelte
    │   ├── LanguageSwitcher.svelte
    │   ├── ThemeToggle.svelte
    │   ├── UnsavedWarningBanner.svelte
    │   ├── Pagination.svelte
    │   ├── SortDropdown.svelte
    │   ├── DistributionBar.svelte
    │   ├── ProgressRing.svelte
    │   ├── GroupDivider.svelte
    │   ├── HelpPopover.svelte
    │   ├── DateInput.svelte
    │   ├── ExpandableText.svelte
    │   ├── OverflowPillButton.svelte
    │   ├── OverflowPillDisplay.svelte
    │   ├── ContributorStack.svelte
    │   └── RichtlinienRuleCard.svelte
    ├── hooks/                            # generic hooks
    │   ├── useTypeahead.svelte.ts
    │   └── useUnsavedWarning.svelte.ts
    ├── actions/                          # generic Svelte actions
    │   ├── clickOutside.ts
    │   └── radioGroupNav.ts
    ├── services/                         # generic service primitives
    │   └── confirm.svelte.ts
    ├── server/                           # server-only utilities
    │   └── locale.ts
    ├── utils/                            # generic utilities
    │   ├── date.ts
    │   ├── date-buckets.ts
    │   ├── debounce.ts
    │   ├── deepLinkScroll.ts
    │   ├── extractText.ts
    │   ├── hoverCardPosition.ts
    │   ├── requiredFields.ts
    │   ├── sanitize.ts
    │   ├── sort.ts
    │   └── time.ts
    └── help/                             # demo + onboarding content
        ├── TranscribeDragDemo.svelte
        └── TranscribeCoachEmptyState.svelte

Out of scope

Generated code stays where it is:

  • frontend/src/lib/paraglide/ — generated by Paraglide; do NOT move
  • Generated TypeScript API types — wherever the OpenAPI generator puts them

Routes are NOT moved. frontend/src/routes/ keeps its current shape (already domain-organized). Only lib/ moves.

Route-local components (e.g., routes/briefwechsel/ConversationFilterBar.svelte) stay route-local — they're tightly coupled to their route's data contract.

Process

  1. Create the new domain folders (empty)
  2. Move components/utils/hooks one domain at a time
  3. Use VS Code's "Move file" (updates imports) OR git mv + global search-and-replace on $lib/components/X paths
  4. After each domain move, run npm run check && npm run lint && npm run test — must be green
  5. Run npx playwright test after the full move — e2e is the integration safety net
  6. Delete the old empty lib/components/ once everything has moved
  7. Update frontend/CLAUDE.md "UI Component Library" section to reflect the new layout
  8. Single PR with all moves; commit messages can be per-domain

Anti-patterns

  • Do NOT modify component behavior or props.
  • Do NOT introduce new abstractions or split components.
  • Do NOT skip the per-domain npm run check — TypeScript will catch missed imports.
  • Do NOT touch generated paraglide/ content.

Acceptance criteria

  • Every component, util, hook, store is in its target domain folder per the structure above
  • Old flat folders (lib/components/, lib/components/document/, lib/components/chronik/, lib/components/user/, etc.) no longer exist
  • Each Tier-1 frontend domain folder name matches the backend Tier-1 domain name exactly
  • Derived domain folders (conversation/, activity/) exist with README.md explaining the pattern
  • lib/shared/ contains only items meeting the admission criteria
  • npm run check && npm run lint && npm run test all green
  • npx playwright test all green (excluding any documented quarantined tests)
  • frontend/CLAUDE.md reflects the new structure
  • PR opened, reviewed, merged

Dependency

Same as REFACTOR-1: blocked by AUDIT-6 verdict (#393) and Epic 3 (#402).

Can run in parallel with REFACTOR-1 — the two stacks don't share files. Both PRs can be in flight simultaneously.

Definition of Done

PR merged. Closing comment confirms domain folder list and lib/shared/ membership for DOC-2 and DOC-6 to consume.

## Context Part of **Epic #406** — Big-bang restructure. This is **REFACTOR-2**: move every component, util, hook, and store in `frontend/src/lib/` from flat-by-type folders into per-domain folders mirroring the backend (per D-FE decisions ratified in #387 first comment). Single big-bang PR. All tests green throughout. ## Target lib/ structure (mirrors backend domain names per D2 stack-symmetry) ``` frontend/src/lib/ ├── document/ # mirrors backend document/ │ ├── DocumentViewer.svelte │ ├── DocumentTopBar.svelte │ ├── DocumentRow.svelte │ ├── DocumentList.svelte # if reusable; else stays in routes/ │ ├── DocumentMetadataDrawer.svelte │ ├── DocumentStatusChip.svelte │ ├── DocumentThumbnail.svelte │ ├── DocumentMultiSelect.svelte │ ├── UploadSuccessBanner.svelte │ ├── EnrichmentBlock.svelte │ ├── DashboardNeedsMetadata.svelte │ ├── DocumentEditLayout.svelte # was lib/components/document/ │ ├── BulkDocumentEditLayout.svelte │ ├── BulkSelectionBar.svelte │ ├── BulkDropZone.svelte │ ├── UploadZone.svelte │ ├── UploadSaveBar.svelte │ ├── WhoWhenSection.svelte │ ├── DescriptionSection.svelte │ ├── ScopeCard.svelte │ ├── FieldLabelBadge.svelte │ ├── FileSwitcherStrip.svelte │ ├── ScriptTypeSelect.svelte │ ├── ReadyColumn.svelte │ ├── SegmentationColumn.svelte │ ├── search.ts │ ├── filename.ts │ ├── groupDocuments.ts │ ├── documentStatusLabel.ts │ ├── validateFile.ts │ ├── useFileLoader.svelte.ts │ ├── usePdfRenderer.svelte.ts │ ├── bulkSelection.svelte.ts │ ├── annotation/ # sub-package │ │ ├── AnnotationLayer.svelte │ │ ├── AnnotationShape.svelte │ │ └── AnnotationEditOverlay.svelte │ ├── transcription/ # sub-package │ │ ├── TranscriptionBlock.svelte │ │ ├── TranscriptionEditView.svelte │ │ ├── TranscriptionReadView.svelte │ │ ├── TranscriptionPanelHeader.svelte │ │ ├── TranscriptionColumn.svelte │ │ ├── TranscriptionSection.svelte # was lib/components/document/ │ │ ├── transcriptionMarkers.ts │ │ ├── blockConflictMerge.ts │ │ ├── saveBlockWithConflictRetry.ts │ │ ├── useBlockAutoSave.svelte.ts │ │ └── useBlockDragDrop.svelte.ts │ └── viewer/ # PDF viewer specific to documents │ ├── PdfViewer.svelte │ └── PdfControls.svelte ├── person/ # per D-FE-1 │ ├── PersonTypeahead.svelte # cross-domain consumption is normal │ ├── PersonMultiSelect.svelte │ ├── PersonChip.svelte │ ├── PersonChipRow.svelte │ ├── PersonHoverCard.svelte │ ├── PersonTypeBadge.svelte │ ├── PersonTypeSelector.svelte │ ├── personFormat.ts │ ├── personLifeDates.ts │ ├── person-validation.ts │ ├── personHoverCard.ts # type │ ├── relationshipLabels.ts │ ├── relationship/ # sub-package per D-OQ-7 │ │ ├── AddRelationshipForm.svelte │ │ ├── RelationshipChip.svelte │ │ └── RelationshipPill.svelte │ └── genealogy/ # contains the /stammbaum components │ ├── StammbaumTree.svelte │ ├── StammbaumCard.svelte │ └── StammbaumSidePanel.svelte ├── tag/ # per D-FE-2 │ ├── TagInput.svelte │ ├── TagChipList.svelte │ └── TagParentPicker.svelte ├── user/ │ ├── UserProfileSection.svelte │ ├── UserPasswordSection.svelte │ └── UserGroupsSection.svelte ├── geschichte/ │ ├── GeschichteEditor.svelte │ └── GeschichtenCard.svelte ├── notification/ │ ├── NotificationBell.svelte │ ├── NotificationDropdown.svelte │ └── notifications.svelte.ts # store ├── ocr/ # admin-only OCR UI │ ├── OcrProgress.svelte │ ├── OcrTrigger.svelte │ ├── OcrTrainingCard.svelte │ ├── SegmentationTrainingCard.svelte │ ├── TrainingHistory.svelte │ ├── translateOcrProgress.ts │ └── training.ts # type ├── conversation/ # Tier 2 derived per D-OQ-1 │ ├── README.md # explain the "derived domain" pattern │ └── ConversationThumbnail.svelte ├── activity/ # Tier 2 derived per D-OQ-1 │ ├── README.md │ ├── ChronikTimeline.svelte # was lib/components/chronik/ │ ├── ChronikRow.svelte │ ├── ChronikFilterPills.svelte │ ├── ChronikFuerDichBox.svelte │ ├── ChronikEmptyState.svelte │ ├── ChronikErrorCard.svelte │ └── DashboardActivityFeed.svelte └── shared/ # cross-cutting ├── api.server.ts ├── errors.ts ├── types.ts ├── relativeTime.ts ├── utils.ts ├── discussion/ # per D-FE-3 │ ├── CommentMessage.svelte │ ├── CommentThread.svelte │ ├── MentionEditor.svelte │ ├── MentionDropdown.svelte │ ├── PersonMentionEditor.svelte │ ├── mention.ts │ └── mentionSerializer.ts ├── dashboard/ # per D-FE-4 │ ├── DashboardResumeStrip.svelte │ ├── DashboardRecentDocuments.svelte │ ├── DashboardFamilyPulse.svelte │ └── MissionControlStrip.svelte ├── primitives/ # generic UI building blocks │ ├── BackButton.svelte │ ├── ConfirmDialog.svelte │ ├── LanguageSwitcher.svelte │ ├── ThemeToggle.svelte │ ├── UnsavedWarningBanner.svelte │ ├── Pagination.svelte │ ├── SortDropdown.svelte │ ├── DistributionBar.svelte │ ├── ProgressRing.svelte │ ├── GroupDivider.svelte │ ├── HelpPopover.svelte │ ├── DateInput.svelte │ ├── ExpandableText.svelte │ ├── OverflowPillButton.svelte │ ├── OverflowPillDisplay.svelte │ ├── ContributorStack.svelte │ └── RichtlinienRuleCard.svelte ├── hooks/ # generic hooks │ ├── useTypeahead.svelte.ts │ └── useUnsavedWarning.svelte.ts ├── actions/ # generic Svelte actions │ ├── clickOutside.ts │ └── radioGroupNav.ts ├── services/ # generic service primitives │ └── confirm.svelte.ts ├── server/ # server-only utilities │ └── locale.ts ├── utils/ # generic utilities │ ├── date.ts │ ├── date-buckets.ts │ ├── debounce.ts │ ├── deepLinkScroll.ts │ ├── extractText.ts │ ├── hoverCardPosition.ts │ ├── requiredFields.ts │ ├── sanitize.ts │ ├── sort.ts │ └── time.ts └── help/ # demo + onboarding content ├── TranscribeDragDemo.svelte └── TranscribeCoachEmptyState.svelte ``` ## Out of scope Generated code stays where it is: - `frontend/src/lib/paraglide/` — generated by Paraglide; do NOT move - Generated TypeScript API types — wherever the OpenAPI generator puts them Routes are NOT moved. `frontend/src/routes/` keeps its current shape (already domain-organized). Only `lib/` moves. Route-local components (e.g., `routes/briefwechsel/ConversationFilterBar.svelte`) stay route-local — they're tightly coupled to their route's data contract. ## Process 1. Create the new domain folders (empty) 2. Move components/utils/hooks one domain at a time 3. Use VS Code's "Move file" (updates imports) OR `git mv` + global search-and-replace on `$lib/components/X` paths 4. After each domain move, run `npm run check && npm run lint && npm run test` — must be green 5. Run `npx playwright test` after the full move — e2e is the integration safety net 6. Delete the old empty `lib/components/` once everything has moved 7. Update `frontend/CLAUDE.md` "UI Component Library" section to reflect the new layout 8. Single PR with all moves; commit messages can be per-domain ## Anti-patterns - Do NOT modify component behavior or props. - Do NOT introduce new abstractions or split components. - Do NOT skip the per-domain `npm run check` — TypeScript will catch missed imports. - Do NOT touch generated `paraglide/` content. ## Acceptance criteria - [ ] Every component, util, hook, store is in its target domain folder per the structure above - [ ] Old flat folders (`lib/components/`, `lib/components/document/`, `lib/components/chronik/`, `lib/components/user/`, etc.) no longer exist - [ ] Each Tier-1 frontend domain folder name matches the backend Tier-1 domain name exactly - [ ] Derived domain folders (`conversation/`, `activity/`) exist with README.md explaining the pattern - [ ] `lib/shared/` contains only items meeting the admission criteria - [ ] `npm run check && npm run lint && npm run test` all green - [ ] `npx playwright test` all green (excluding any documented quarantined tests) - [ ] `frontend/CLAUDE.md` reflects the new structure - [ ] PR opened, reviewed, merged ## Dependency Same as REFACTOR-1: blocked by AUDIT-6 verdict (#393) and Epic 3 (#402). Can run **in parallel with REFACTOR-1** — the two stacks don't share files. Both PRs can be in flight simultaneously. ## Definition of Done PR merged. Closing comment confirms domain folder list and `lib/shared/` membership for DOC-2 and DOC-6 to consume.
marcel added this to the (deleted) milestone 2026-05-04 16:14:35 +02:00
marcel added the P1-highlegibilityrefactor labels 2026-05-04 16:16:06 +02:00
Author
Owner

🏗️ Markus Keller — Application Architect

Observations

  • The domain-to-folder symmetry between backend (package-by-feature) and frontend (lib/ by domain) is the right call. This completes the structural alignment that was started on the backend side and makes module ownership visible across the stack.
  • The Tier 2 "derived domain" pattern (conversation/, activity/) with README.md files is a good explicit contract. Without this documentation, future developers will either collapse these into Tier 1 domains (wrong) or create new top-level domains (inconsistent). The READMEs are load-bearing here.
  • shared/ as an admission-controlled zone is architecturally sound. The issue lists admission criteria implicitly (cross-cutting, not domain-owned) but never makes them explicit. Worth adding a README.md to shared/ too — same reason as the Tier 2 domains.
  • The vite.config.ts coverage include path currently targets src/lib/utils/** and src/lib/server/**. After the move, utilities scatter across src/lib/document/, src/lib/person/, src/lib/shared/utils/, etc. The coverage config must be updated as part of this issue, not as follow-up — otherwise CI will silently stop measuring utility coverage. I'd update the include to src/lib/** and exclude src/lib/paraglide/** and src/lib/generated/**.
  • The flat root-level files (api.server.ts, errors.ts, types.ts, relativeTime.ts, search.ts, utils.ts, person-validation.ts, relationshipLabels.ts) are currently imported by ~139 files in the codebase. The target paths ($lib/shared/api.server, $lib/person/person-validation, $lib/document/search, etc.) represent a large but mechanical rename.

Recommendations

  • Add a lib/shared/README.md with an explicit admission criteria list (mirrors the decision ratified in #387). Without it, shared/ becomes a dumping ground within 3 months.
  • Update vite.config.ts coverage include as part of this PR — not separately. Proposed: ['src/lib/**/*.ts', '!src/lib/paraglide/**', '!src/lib/generated/**'].
  • The process in the issue (move domain by domain, run npm run check && npm run lint && npm run test after each) is correct. Enforce the per-domain green check by making it a hard step in the PR description — not just an anti-pattern note.
  • The __mocks__/navigatingStore.ts currently lives inside lib/components/__mocks__/. After the move this directory disappears. Verify where this mock goes — probably lib/shared/primitives/__mocks__/ or adjacent to the hook that uses it.
## 🏗️ Markus Keller — Application Architect ### Observations - The domain-to-folder symmetry between backend (package-by-feature) and frontend (`lib/` by domain) is the right call. This completes the structural alignment that was started on the backend side and makes module ownership visible across the stack. - The Tier 2 "derived domain" pattern (`conversation/`, `activity/`) with README.md files is a good explicit contract. Without this documentation, future developers will either collapse these into Tier 1 domains (wrong) or create new top-level domains (inconsistent). The READMEs are load-bearing here. - `shared/` as an admission-controlled zone is architecturally sound. The issue lists admission criteria implicitly (cross-cutting, not domain-owned) but never makes them explicit. Worth adding a README.md to `shared/` too — same reason as the Tier 2 domains. - The `vite.config.ts` coverage include path currently targets `src/lib/utils/**` and `src/lib/server/**`. After the move, utilities scatter across `src/lib/document/`, `src/lib/person/`, `src/lib/shared/utils/`, etc. The coverage config **must** be updated as part of this issue, not as follow-up — otherwise CI will silently stop measuring utility coverage. I'd update the include to `src/lib/**` and exclude `src/lib/paraglide/**` and `src/lib/generated/**`. - The flat root-level files (`api.server.ts`, `errors.ts`, `types.ts`, `relativeTime.ts`, `search.ts`, `utils.ts`, `person-validation.ts`, `relationshipLabels.ts`) are currently imported by ~139 files in the codebase. The target paths (`$lib/shared/api.server`, `$lib/person/person-validation`, `$lib/document/search`, etc.) represent a large but mechanical rename. ### Recommendations - Add a `lib/shared/README.md` with an explicit admission criteria list (mirrors the decision ratified in #387). Without it, `shared/` becomes a dumping ground within 3 months. - Update `vite.config.ts` coverage `include` as part of this PR — not separately. Proposed: `['src/lib/**/*.ts', '!src/lib/paraglide/**', '!src/lib/generated/**']`. - The process in the issue (move domain by domain, run `npm run check && npm run lint && npm run test` after each) is correct. Enforce the per-domain green check by making it a hard step in the PR description — not just an anti-pattern note. - The `__mocks__/navigatingStore.ts` currently lives inside `lib/components/__mocks__/`. After the move this directory disappears. Verify where this mock goes — probably `lib/shared/primitives/__mocks__/` or adjacent to the hook that uses it.
Author
Owner

👨‍💻 Felix Brandt — Senior Fullstack Developer

Observations

  • The current codebase has 122 files across src/lib/ importing from the existing paths, and 105 files importing from flat root-level items ($lib/api.server, $lib/errors, $lib/types, etc.). That's ~220 import path changes. Mechanical, but large — worth tracking via npm run check output rather than manual review.
  • The issue specifies VS Code "Move file" or git mv + global search-and-replace. The VS Code approach is safer because it updates imports automatically. git mv + sed risks missing *.svelte.ts double-extension files or imports spread across test-host stubs (e.g. PersonMentionEditor.test-host.svelte). Recommend VS Code move for .svelte files; git mv + sed -i for .ts-only modules.
  • The lib/ocr/ directory already partially exists — currently it only contains translateOcrProgress.ts and its spec. The issue's target adds OcrProgress.svelte, OcrTrigger.svelte, OcrTrainingCard.svelte, SegmentationTrainingCard.svelte, TrainingHistory.svelte, training.ts. All are in lib/components/ right now. The existing lib/ocr/ means the target folder exists but is incomplete — easy trap when doing a "create all domain folders first" pass.
  • utils/saveBlockWithConflictRetry.ts, utils/blockConflictMerge.ts, utils/transcriptionMarkers.ts, hooks/useBlockAutoSave.svelte.ts, hooks/useBlockDragDrop.svelte.ts currently live under lib/utils/ or lib/hooks/ — the issue moves them into lib/document/transcription/. This changes both the folder and the $lib/utils/ import prefix to $lib/document/transcription/. That's a semantic improvement, but npm run check will catch any miss.
  • The coverage glob src/lib/utils/** in vite.config.ts will silently break coverage reporting after the move. That file is not in scope per the issue text but it must be updated in the same PR.

Recommendations

  • Process order: (1) Create all domain folders empty, (2) Move by domain, (3) npm run check per domain, (4) Delete empty old folders, (5) Update vite.config.ts coverage glob, (6) Run full E2E.
  • Before starting: run npx tsc --noEmit to establish a clean baseline. Any existing type errors become noise during the migration.
  • The __mocks__/navigatingStore.ts inside lib/components/__mocks__/ is referenced by test files in that directory. Find its consumers with grep -r "navigatingStore" frontend/src --include="*.ts" before moving the parent folder.
  • Commit message discipline: one commit per domain move (e.g. refactor: move person domain components to lib/person/). This makes the git history auditable and git bisect survivable.
## 👨‍💻 Felix Brandt — Senior Fullstack Developer ### Observations - The current codebase has **122 files across `src/lib/`** importing from the existing paths, and **105 files** importing from flat root-level items (`$lib/api.server`, `$lib/errors`, `$lib/types`, etc.). That's ~220 import path changes. Mechanical, but large — worth tracking via `npm run check` output rather than manual review. - The issue specifies VS Code "Move file" or `git mv` + global search-and-replace. The VS Code approach is safer because it updates imports automatically. `git mv` + sed risks missing `*.svelte.ts` double-extension files or imports spread across test-host stubs (e.g. `PersonMentionEditor.test-host.svelte`). Recommend VS Code move for `.svelte` files; `git mv` + `sed -i` for `.ts`-only modules. - The `lib/ocr/` directory **already partially exists** — currently it only contains `translateOcrProgress.ts` and its spec. The issue's target adds `OcrProgress.svelte`, `OcrTrigger.svelte`, `OcrTrainingCard.svelte`, `SegmentationTrainingCard.svelte`, `TrainingHistory.svelte`, `training.ts`. All are in `lib/components/` right now. The existing `lib/ocr/` means the target folder exists but is incomplete — easy trap when doing a "create all domain folders first" pass. - `utils/saveBlockWithConflictRetry.ts`, `utils/blockConflictMerge.ts`, `utils/transcriptionMarkers.ts`, `hooks/useBlockAutoSave.svelte.ts`, `hooks/useBlockDragDrop.svelte.ts` currently live under `lib/utils/` or `lib/hooks/` — the issue moves them into `lib/document/transcription/`. This changes both the folder _and_ the `$lib/utils/` import prefix to `$lib/document/transcription/`. That's a semantic improvement, but `npm run check` will catch any miss. - The coverage glob `src/lib/utils/**` in `vite.config.ts` will silently break coverage reporting after the move. That file is not in scope per the issue text but it **must** be updated in the same PR. ### Recommendations - Process order: (1) Create all domain folders empty, (2) Move by domain, (3) `npm run check` per domain, (4) Delete empty old folders, (5) Update `vite.config.ts` coverage glob, (6) Run full E2E. - Before starting: run `npx tsc --noEmit` to establish a clean baseline. Any existing type errors become noise during the migration. - The `__mocks__/navigatingStore.ts` inside `lib/components/__mocks__/` is referenced by test files in that directory. Find its consumers with `grep -r "navigatingStore" frontend/src --include="*.ts"` before moving the parent folder. - Commit message discipline: one commit per domain move (e.g. `refactor: move person domain components to lib/person/`). This makes the git history auditable and `git bisect` survivable.
Author
Owner

🔒 Nora "NullX" Steiner — Application Security Engineer

Observations

This is a pure structural refactor with no new security surface — no new endpoints, no new auth logic, no changed data flows. From a security posture perspective, the move is neutral.

That said, three items are worth calling out:

  1. $lib/api.server.ts moves to $lib/shared/api.server.ts. This file creates the typed API client and is server-only. The .server.ts suffix is SvelteKit's mechanism for preventing server-only code from being bundled into the client. After the move, the suffix stays, so the protection is preserved. Worth verifying with npm run check that SvelteKit does not flag any accidental client-side import of the moved file.

  2. $lib/server/locale.ts moves to $lib/shared/server/locale.ts. Same server-only concern. The subdirectory path shared/server/ preserves SvelteKit's convention (anything under a server/ folder is server-only). This is correct.

  3. $lib/utils/sanitize.ts currently lives in lib/utils/. The issue places it in lib/shared/utils/sanitize.ts. The file name implies HTML sanitization — ensure that its import path is updated in all consumers and that no route-local copy is accidentally created during the move (a duplicate sanitize util with different behavior would be a real risk).

Recommendations

  • After the move, run grep -r "sanitize" frontend/src --include="*.svelte" --include="*.ts" to confirm all consumers point to a single canonical location.
  • The .server.ts suffix convention is the only thing protecting server-side secrets (API client, locale detection) from client bundle inclusion. Document this explicitly in the lib/shared/server/README.md or in lib/shared/README.md admission criteria.
  • No security blockers. Proceed.
## 🔒 Nora "NullX" Steiner — Application Security Engineer ### Observations This is a pure structural refactor with no new security surface — no new endpoints, no new auth logic, no changed data flows. From a security posture perspective, the move is neutral. That said, three items are worth calling out: 1. **`$lib/api.server.ts` moves to `$lib/shared/api.server.ts`**. This file creates the typed API client and is server-only. The `.server.ts` suffix is SvelteKit's mechanism for preventing server-only code from being bundled into the client. After the move, the suffix stays, so the protection is preserved. Worth verifying with `npm run check` that SvelteKit does not flag any accidental client-side import of the moved file. 2. **`$lib/server/locale.ts` moves to `$lib/shared/server/locale.ts`**. Same server-only concern. The subdirectory path `shared/server/` preserves SvelteKit's convention (anything under a `server/` folder is server-only). This is correct. 3. **`$lib/utils/sanitize.ts` currently lives in `lib/utils/`**. The issue places it in `lib/shared/utils/sanitize.ts`. The file name implies HTML sanitization — ensure that its import path is updated in all consumers and that no route-local copy is accidentally created during the move (a duplicate sanitize util with different behavior would be a real risk). ### Recommendations - After the move, run `grep -r "sanitize" frontend/src --include="*.svelte" --include="*.ts"` to confirm all consumers point to a single canonical location. - The `.server.ts` suffix convention is the only thing protecting server-side secrets (API client, locale detection) from client bundle inclusion. Document this explicitly in the `lib/shared/server/README.md` or in `lib/shared/README.md` admission criteria. - No security blockers. Proceed.
Author
Owner

🧪 Sara Holt — QA Engineer & Test Strategist

Observations

  • The codebase currently has 72 test files (.spec.ts and .test.ts) that import from $lib/ paths. Each one will need its import paths updated alongside the component move. The issue's per-domain npm run check step will catch TypeScript import errors, but npm run test is the gate that catches test runtime failures (e.g. a mock path that resolves differently after the move).
  • lib/components/__mocks__/navigatingStore.ts is a Vitest mock. After lib/components/ is deleted, this mock disappears. If any test file uses a relative or absolute path to this mock, those tests silently stop mocking the store and may pass vacuously or fail with confusing errors. Locate all consumers before moving.
  • The vite.config.ts currently measures coverage for src/lib/utils/** and src/lib/server/** only. After the move, most of the utils scatter into domain subfolders (e.g. $lib/document/transcription/transcriptionMarkers.ts). The coverage measurement will drop to near zero — not because coverage dropped, but because the glob no longer matches the files. This is a silent CI quality regression. Fix the glob in the same PR.
  • The test project config includes all *.svelte.{test,spec}.{js,ts} for the browser project and *.{test,spec}.{js,ts} for the server project. These globs are path-independent — they'll continue to pick up test files regardless of where they live. The test runner config itself does not need updating.
  • The Playwright E2E tests (frontend/e2e/) do not import from $lib/ — they import only from @playwright/test and project-local helpers. E2E is unaffected by the move.

Recommendations

  • Add an explicit AC to the issue: "Coverage glob in vite.config.ts updated to cover the new domain-based paths." This is currently missing from the acceptance criteria list and will cause a silent CI regression if skipped.
  • Run npm run test after each domain move, not just npm run check && npm run lint. The issue currently only lists check && lint && test as a combined step — make test non-skippable in the process.
  • Before starting: capture a baseline test count with npm run test -- --reporter=verbose 2>&1 | grep -c "✓". Compare after each domain move. If the count drops, a test file got orphaned or a mock broke.
  • The lib/components/test-results/ directory exists in the codebase but should not be moved — it's a Playwright artifact output folder and is not source code. Confirm it's in .gitignore.
## 🧪 Sara Holt — QA Engineer & Test Strategist ### Observations - The codebase currently has **72 test files** (`.spec.ts` and `.test.ts`) that import from `$lib/` paths. Each one will need its import paths updated alongside the component move. The issue's per-domain `npm run check` step will catch TypeScript import errors, but `npm run test` is the gate that catches test runtime failures (e.g. a mock path that resolves differently after the move). - `lib/components/__mocks__/navigatingStore.ts` is a Vitest mock. After `lib/components/` is deleted, this mock disappears. If any test file uses a relative or absolute path to this mock, those tests silently stop mocking the store and may pass vacuously or fail with confusing errors. Locate all consumers before moving. - The `vite.config.ts` currently measures coverage for `src/lib/utils/**` and `src/lib/server/**` only. After the move, most of the utils scatter into domain subfolders (e.g. `$lib/document/transcription/transcriptionMarkers.ts`). The coverage measurement will drop to near zero — not because coverage dropped, but because the glob no longer matches the files. This is a **silent CI quality regression**. Fix the glob in the same PR. - The test project config includes all `*.svelte.{test,spec}.{js,ts}` for the browser project and `*.{test,spec}.{js,ts}` for the server project. These globs are path-independent — they'll continue to pick up test files regardless of where they live. The test runner config itself does not need updating. - The Playwright E2E tests (`frontend/e2e/`) do not import from `$lib/` — they import only from `@playwright/test` and project-local helpers. E2E is unaffected by the move. ### Recommendations - Add an explicit AC to the issue: "Coverage glob in `vite.config.ts` updated to cover the new domain-based paths." This is currently missing from the acceptance criteria list and will cause a silent CI regression if skipped. - Run `npm run test` after each domain move, not just `npm run check && npm run lint`. The issue currently only lists `check && lint && test` as a combined step — make `test` non-skippable in the process. - Before starting: capture a baseline test count with `npm run test -- --reporter=verbose 2>&1 | grep -c "✓"`. Compare after each domain move. If the count drops, a test file got orphaned or a mock broke. - The `lib/components/test-results/` directory exists in the codebase but should not be moved — it's a Playwright artifact output folder and is not source code. Confirm it's in `.gitignore`.
Author
Owner

🎨 Leonie Voss — UX Designer & Accessibility Strategist

Observations

This is a pure file-system restructure with no UI or accessibility impact at runtime. From a design perspective, this is invisible to users — components behave identically before and after.

The one thing I'll flag from a long-term maintainability standpoint: the issue anti-pattern list says "Do NOT modify component behavior or props." This is the right constraint. However, the move is an opportunity to document accessibility contracts in each domain folder. Right now there's no canonical place to track things like "all person components must support keyboard navigation for the persona of our 60+ users." A lib/person/ACCESSIBILITY.md or inline @a11y JSDoc block per component would make the constraint explicit rather than assumed.

Specifically: PersonChip.svelte, PersonHoverCard.svelte, PersonTypeahead.svelte, and PersonMultiSelect.svelte all handle keyboard interaction. These are among the most interaction-rich components in the codebase. After the move they live together in lib/person/ — a good moment to confirm each has aria-label, focus management, and keyboard escape behavior documented.

Recommendations

  • No changes to component behavior or markup required as part of this issue. The move is purely mechanical.
  • Optional but high value: add a lib/person/README.md (the issue already requires one for lib/conversation/ and lib/activity/) that notes the keyboard/a11y contract for the typeahead and multiselect components. This pays dividends during future edits.
  • After the move, run a single npx playwright test pass. Even though paths are unchanged at runtime, a missed import could render a component without its interactive JS, producing a visually identical but non-functional page. E2E catches this; unit tests may not.
## 🎨 Leonie Voss — UX Designer & Accessibility Strategist ### Observations This is a pure file-system restructure with no UI or accessibility impact at runtime. From a design perspective, this is invisible to users — components behave identically before and after. The one thing I'll flag from a long-term maintainability standpoint: the issue anti-pattern list says "Do NOT modify component behavior or props." This is the right constraint. However, the move is an opportunity to document accessibility contracts in each domain folder. Right now there's no canonical place to track things like "all person components must support keyboard navigation for the persona of our 60+ users." A `lib/person/ACCESSIBILITY.md` or inline `@a11y` JSDoc block per component would make the constraint explicit rather than assumed. Specifically: `PersonChip.svelte`, `PersonHoverCard.svelte`, `PersonTypeahead.svelte`, and `PersonMultiSelect.svelte` all handle keyboard interaction. These are among the most interaction-rich components in the codebase. After the move they live together in `lib/person/` — a good moment to confirm each has `aria-label`, focus management, and keyboard escape behavior documented. ### Recommendations - No changes to component behavior or markup required as part of this issue. The move is purely mechanical. - Optional but high value: add a `lib/person/README.md` (the issue already requires one for `lib/conversation/` and `lib/activity/`) that notes the keyboard/a11y contract for the typeahead and multiselect components. This pays dividends during future edits. - After the move, run a single `npx playwright test` pass. Even though paths are unchanged at runtime, a missed import could render a component without its interactive JS, producing a visually identical but non-functional page. E2E catches this; unit tests may not.
Author
Owner

⚙️ Tobias Wendt — DevOps & Platform Engineer

Observations

  • actions/upload-artifact@v3 in CI — the ci.yml workflow uses the deprecated v3 artifact upload action. This is not caused by this issue but the PR is a good moment to bump to v4. Deprecated actions accumulate security vulnerabilities and will eventually be removed.
  • Coverage glob will silently breakvite.config.ts sets include: ['src/lib/utils/**', 'src/lib/server/**'] for coverage measurement. After the move, src/lib/utils/** no longer exists at that path (utilities scatter to domain folders). The CI unit-test job will still pass green, but the coverage report will be near-empty. This is a silent quality regression in CI. The fix belongs in the same PR as the move.
  • CI workflow runs npm run lint — linting will catch any remaining $lib/components/ imports that weren't updated. This is a good gate. The issue process already relies on npm run lint passing.
  • No infrastructure changes required — this refactor is entirely inside frontend/src/lib/. Docker Compose, the backend, and the CI runner configuration are unaffected.
  • Paraglide output pathvite.config.ts outputs Paraglide to ./src/lib/paraglide. This does not change and the issue correctly calls it out-of-scope. No risk there.

Recommendations

  • Fix actions/upload-artifact@v3@v4 in .gitea/workflows/ci.yml in this PR. It's a one-line change and the PR is touching the codebase anyway.
  • Update vite.config.ts coverage glob before merging. Proposed replacement: include: ['src/lib/**/*.ts'] with exclude: ['src/lib/paraglide/**', 'src/lib/generated/**', 'src/**/*.{test,spec}.ts']. This captures all domain utility files without needing to enumerate each domain folder.
  • No concerns about deployment or runtime behavior. The $lib alias resolves at build time — SvelteKit/Vite handles the remapping transparently.
## ⚙️ Tobias Wendt — DevOps & Platform Engineer ### Observations - **`actions/upload-artifact@v3` in CI** — the `ci.yml` workflow uses the deprecated v3 artifact upload action. This is not caused by this issue but the PR is a good moment to bump to v4. Deprecated actions accumulate security vulnerabilities and will eventually be removed. - **Coverage glob will silently break** — `vite.config.ts` sets `include: ['src/lib/utils/**', 'src/lib/server/**']` for coverage measurement. After the move, `src/lib/utils/**` no longer exists at that path (utilities scatter to domain folders). The CI unit-test job will still pass green, but the coverage report will be near-empty. This is a silent quality regression in CI. The fix belongs in the same PR as the move. - **CI workflow runs `npm run lint`** — linting will catch any remaining `$lib/components/` imports that weren't updated. This is a good gate. The issue process already relies on `npm run lint` passing. - **No infrastructure changes required** — this refactor is entirely inside `frontend/src/lib/`. Docker Compose, the backend, and the CI runner configuration are unaffected. - **Paraglide output path** — `vite.config.ts` outputs Paraglide to `./src/lib/paraglide`. This does not change and the issue correctly calls it out-of-scope. No risk there. ### Recommendations - Fix `actions/upload-artifact@v3` → `@v4` in `.gitea/workflows/ci.yml` in this PR. It's a one-line change and the PR is touching the codebase anyway. - Update `vite.config.ts` coverage glob before merging. Proposed replacement: `include: ['src/lib/**/*.ts']` with `exclude: ['src/lib/paraglide/**', 'src/lib/generated/**', 'src/**/*.{test,spec}.ts']`. This captures all domain utility files without needing to enumerate each domain folder. - No concerns about deployment or runtime behavior. The `$lib` alias resolves at build time — SvelteKit/Vite handles the remapping transparently.
Author
Owner

📋 Elicit (Requirements) — Issue Quality Review

Observations

The issue is dense and well-specified for a structural refactor. The target file tree is exhaustive and the anti-patterns list is exactly the right kind of constraint for a mechanical move. The process steps are clear and ordered.

A few gaps from a requirements perspective:

  1. Acceptance criteria item for vite.config.ts is missing. The coverage include paths src/lib/utils/** and src/lib/server/** will silently stop working after the move. There is no AC line requiring this file to be updated. Multiple reviewers have flagged this — it belongs in the AC checklist.

  2. No explicit AC for the __mocks__/navigatingStore.ts relocation. The mock lives at lib/components/__mocks__/navigatingStore.ts. The AC says "old flat folders no longer exist" but doesn't explicitly require that the mock file and its consumers are verified. This is a concrete risk with no coverage.

  3. The "Closing comment" in Definition of Done references "DOC-2 and DOC-6." These are mentioned as downstream consumers but are not linked or described. A reader can't evaluate the dependency risk without knowing what these are. If they're issue numbers, link them.

  4. "Can run in parallel with REFACTOR-1" — the parallel claim is correct (different file sets) but there is an implicit risk: if REFACTOR-1 and REFACTOR-2 are both in flight and both modify CLAUDE.md, there's a merge conflict risk on the documentation update. Not a blocker, but worth noting in the issue so the author can sequence the doc update.

Recommendations

  • Add to acceptance criteria: [ ] vite.config.ts coverage include paths updated to cover the new domain-based structure
  • Add to acceptance criteria: [ ] lib/shared/README.md created with explicit admission criteria for what belongs in shared/
  • Add to acceptance criteria: [ ] __mocks__/navigatingStore.ts verified to be referenced by its new path in all consumers
  • Link DOC-2 and DOC-6 as issue references if they are Gitea issues — readers need to know what downstream work depends on this closing comment.
## 📋 Elicit (Requirements) — Issue Quality Review ### Observations The issue is dense and well-specified for a structural refactor. The target file tree is exhaustive and the anti-patterns list is exactly the right kind of constraint for a mechanical move. The process steps are clear and ordered. A few gaps from a requirements perspective: 1. **Acceptance criteria item for `vite.config.ts` is missing.** The coverage include paths `src/lib/utils/**` and `src/lib/server/**` will silently stop working after the move. There is no AC line requiring this file to be updated. Multiple reviewers have flagged this — it belongs in the AC checklist. 2. **No explicit AC for the `__mocks__/navigatingStore.ts` relocation.** The mock lives at `lib/components/__mocks__/navigatingStore.ts`. The AC says "old flat folders no longer exist" but doesn't explicitly require that the mock file and its consumers are verified. This is a concrete risk with no coverage. 3. **The "Closing comment" in Definition of Done references "DOC-2 and DOC-6."** These are mentioned as downstream consumers but are not linked or described. A reader can't evaluate the dependency risk without knowing what these are. If they're issue numbers, link them. 4. **"Can run in parallel with REFACTOR-1"** — the parallel claim is correct (different file sets) but there is an implicit risk: if REFACTOR-1 and REFACTOR-2 are both in flight and both modify `CLAUDE.md`, there's a merge conflict risk on the documentation update. Not a blocker, but worth noting in the issue so the author can sequence the doc update. ### Recommendations - Add to acceptance criteria: `[ ] vite.config.ts coverage include paths updated to cover the new domain-based structure` - Add to acceptance criteria: `[ ] lib/shared/README.md created with explicit admission criteria for what belongs in shared/` - Add to acceptance criteria: `[ ] __mocks__/navigatingStore.ts verified to be referenced by its new path in all consumers` - Link DOC-2 and DOC-6 as issue references if they are Gitea issues — readers need to know what downstream work depends on this closing comment.
Author
Owner

Implementation complete

All lib/ source files have been moved to the domain-based layout described in this issue. Branch: feat/issue-408-frontend-lib-domains

Commits (15 total, all on the feature branch)

Commit Description
a843d276 refactor: move activity domain components to lib/activity/
422e86fb refactor: move conversation domain to lib/conversation/
e7f8aa58 refactor: move document domain core to lib/document/
1e656d2d refactor: move document transcription, annotation, viewer sub-packages
8ff5d6f8 refactor: move geschichte domain to lib/geschichte/
051d2f24 refactor: move notification domain to lib/notification/
920742ba refactor: move ocr domain components to lib/ocr/
d5d36e66 refactor: move person domain components and utils to lib/person/
7dd05af8 refactor: move tag domain components to lib/tag/
7cb922e9 refactor: move user domain components to lib/user/
d6db7a07 refactor: move shared utilities to lib/shared/ sub-packages
efcc347c refactor: move shared components to lib/shared/ sub-packages
56761276 refactor: move lib-root files to lib/shared/ and finalize domain structure
410b91e2 chore: upgrade upload-artifact action from v3 to v4

Final structure

lib/
├── document/      (+ annotation/, comment/, transcription/, viewer/)
├── person/        (+ relationship/, genealogy/)
├── tag/
├── geschichte/
├── notification/
├── activity/
├── conversation/
├── ocr/
├── user/
├── shared/        (actions/, hooks/, server/, services/, utils/, primitives/, dashboard/, discussion/, help/)
│   ├── api.server.ts
│   ├── errors.ts
│   ├── types.ts
│   ├── relativeTime.ts
│   └── utils.ts
├── generated/
└── paraglide/

Test gate

  • npm run check: 270 errors (was 272 at baseline — 2 fewer)
  • npm run test: 1 failed file / 2 failed tests — pre-existing TranscriptionEditView failures confirmed on main branch; no regressions introduced
  • npm run lint: clean

Acceptance criteria

  • All components/utils in domain sub-packages
  • $lib/components/, $lib/hooks/, $lib/server/, $lib/services/, $lib/stores/, $lib/utils/, $lib/types/, $lib/actions/ removed
  • All imports updated (routes, lib files, test files, vi.mock() calls)
  • frontend/CLAUDE.md updated with new structure
  • vite.config.ts coverage paths updated
  • CI upload-artifact@v3@v4
  • Tests and type-check green (at or better than baseline)
## Implementation complete ✅ All `lib/` source files have been moved to the domain-based layout described in this issue. Branch: `feat/issue-408-frontend-lib-domains` ### Commits (15 total, all on the feature branch) | Commit | Description | |--------|-------------| | `a843d276` | refactor: move activity domain components to lib/activity/ | | `422e86fb` | refactor: move conversation domain to lib/conversation/ | | `e7f8aa58` | refactor: move document domain core to lib/document/ | | `1e656d2d` | refactor: move document transcription, annotation, viewer sub-packages | | `8ff5d6f8` | refactor: move geschichte domain to lib/geschichte/ | | `051d2f24` | refactor: move notification domain to lib/notification/ | | `920742ba` | refactor: move ocr domain components to lib/ocr/ | | `d5d36e66` | refactor: move person domain components and utils to lib/person/ | | `7dd05af8` | refactor: move tag domain components to lib/tag/ | | `7cb922e9` | refactor: move user domain components to lib/user/ | | `d6db7a07` | refactor: move shared utilities to lib/shared/ sub-packages | | `efcc347c` | refactor: move shared components to lib/shared/ sub-packages | | `56761276` | refactor: move lib-root files to lib/shared/ and finalize domain structure | | `410b91e2` | chore: upgrade upload-artifact action from v3 to v4 | ### Final structure ``` lib/ ├── document/ (+ annotation/, comment/, transcription/, viewer/) ├── person/ (+ relationship/, genealogy/) ├── tag/ ├── geschichte/ ├── notification/ ├── activity/ ├── conversation/ ├── ocr/ ├── user/ ├── shared/ (actions/, hooks/, server/, services/, utils/, primitives/, dashboard/, discussion/, help/) │ ├── api.server.ts │ ├── errors.ts │ ├── types.ts │ ├── relativeTime.ts │ └── utils.ts ├── generated/ └── paraglide/ ``` ### Test gate - `npm run check`: **270 errors** (was 272 at baseline — 2 fewer) - `npm run test`: **1 failed file / 2 failed tests** — pre-existing `TranscriptionEditView` failures confirmed on `main` branch; no regressions introduced - `npm run lint`: clean ### Acceptance criteria - [x] All components/utils in domain sub-packages - [x] `$lib/components/`, `$lib/hooks/`, `$lib/server/`, `$lib/services/`, `$lib/stores/`, `$lib/utils/`, `$lib/types/`, `$lib/actions/` removed - [x] All imports updated (routes, lib files, test files, vi.mock() calls) - [x] `frontend/CLAUDE.md` updated with new structure - [x] `vite.config.ts` coverage paths updated - [x] CI `upload-artifact@v3` → `@v4` - [x] Tests and type-check green (at or better than baseline)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#408