Skip to content

release: promote beta to main#1332

Merged
steilerDev merged 119 commits into
mainfrom
beta
Apr 21, 2026
Merged

release: promote beta to main#1332
steilerDev merged 119 commits into
mainfrom
beta

Conversation

@steilerDev
Copy link
Copy Markdown
Owner

Release Summary

Standalone release promoting beta to main. The headline change is area tree hierarchy across the application — work items, household items, Budget Overview, and Budget Sources now surface the full area ancestry. Budget Sources gains inline expansion, multi-select mass-move, and clickable summary tiles. Budget Overview gains area-grouped cost breakdown and print styling. Vendors moves into the Settings section. TypeScript is upgraded to 6.0 with noUncheckedIndexedAccess enabled.

Changes

Features

Area tree hierarchy

Budget Overview

Budget Sources

Navigation

Fixes

Budget / Areas

Print

Server / Reliability

i18n

CI / Docker

Chores / Refactoring

Tests / E2E

Change Inventory

Backend (server/, shared/) — 147 files

  • New endpoints / services
    • server/src/routes/budgetSources.ts, server/src/routes/budgetSources.budgetLines.test.ts, server/src/routes/budgetSources.move.test.ts — budget-lines GET + move PATCH
    • server/src/services/areaService.ts, server/src/services/areaService.ancestors.test.ts, server/src/services/areaService.resolveAreaFilter.test.ts — area resolution helpers
  • Area enrichment
    • server/src/routes/workItems.ts, server/src/routes/workItems.ancestors.test.ts
    • server/src/routes/householdItems.ts, server/src/routes/householdItems.ancestors.test.ts
    • server/src/routes/budgetOverview.ts, server/src/routes/budgetOverview.breakdown.test.ts
  • Reliability
    • server/src/plugins/rateLimitPlugin.ts — resilient rate-limit key
    • server/src/app.ts — narrower trustProxy
  • Config
    • server/src/plugins/config.ts — new env-var surface
  • Shared types
    • shared/src/types/area.ts, shared/src/types/budgetSource.ts, shared/src/types/workItem.ts, shared/src/types/householdItem.ts
  • TS 6.0 migration
    • server/src/db/disposables.tsusing/Disposable pattern
    • tsconfig.base.jsonnoUncheckedIndexedAccess
  • Test coverage — backfill across 95+ route/service .test.ts files

Frontend (client/) — 247 files

  • New shared components
    • client/src/components/AreaBreadcrumb/ — breadcrumb component
    • client/src/components/MassMoveModal/ — mass-move modal
  • Budget Overview
    • client/src/pages/BudgetOverviewPage/ — area-grouped breakdown, print styling
    • client/src/components/CostBreakdownTable/ — nested rows, area grouping
    • client/src/components/BudgetSummaryCard/, client/src/components/SourceUtilizationCard/ — clickable tile filters
  • Budget Sources
    • client/src/pages/BudgetSourcesPage/ — hierarchical tree, dense columnar rows
    • client/src/components/SourceBudgetLinePanel/ — multi-select, mass-move, nested cards, item links
  • Work Items / Household Items
    • client/src/pages/WorkItemsPage/, client/src/pages/WorkItemDetailPage/, client/src/pages/WorkItemCreatePage/
    • client/src/pages/HouseholdItemsPage/, client/src/pages/HouseholdItemDetailPage/
    • client/src/components/WorkItemPicker/, client/src/components/HouseholdItemPicker/
  • Filters
    • client/src/components/DataTable/filters/EnumFilter.tsx — arbitrary hierarchy depth, "No Area" sentinel
  • Navigation
    • client/src/components/Sidebar/, client/src/components/SubNav/ — Vendors under Settings
  • i18n
    • client/src/i18n/en/ and client/src/i18n/de/ — new keys, orphan cleanup, glossary additions

E2E Tests (e2e/) — 45 files

  • New test coverage
    • e2e/tests/budget/budget-overview-print.spec.ts — print styling
    • e2e/tests/budget/budget-source-lines.spec.ts, e2e/tests/budget/budget-source-move.spec.ts, e2e/tests/budget/budget-sources.spec.ts
    • e2e/tests/work-items/work-item-breadcrumb.spec.ts, work-item-embeds-breadcrumb.spec.ts, no-area-filter.spec.ts
    • e2e/tests/household-items/household-item-breadcrumb.spec.ts, household-item-dep-breadcrumb.spec.ts, no-area-filter.spec.ts
    • e2e/tests/diary/diary-source-entity-breadcrumb.spec.ts
    • e2e/tests/invoices/invoice-budget-line-area-breadcrumb.spec.ts
  • Page object updates — Budget Overview/Sources, Work Items, Household Items, Milestones, Invoices, Timeline, Vendors

Docs / Config / CI

  • .github/workflows/release.yml — Docker Scout compare
  • Dockerfile, docker-compose.yml — build updates
  • .env.example — new env surface
  • CLAUDE.md, CLA.md, .gitignore — project doc updates
  • package.json, package-lock.json, all workspace package.json files — TS 6.0 and dep bumps
  • .claude/ — agent memory, agent definitions, skills, implementation checklist

Manual Validation Checklist

Area tree hierarchy

  • Work item list: Open /work-items — each row shows the full area ancestor breadcrumb
  • Work item detail: Open a work item — area breadcrumb is visible in the header / sidebar
  • Work item embeds: Open a diary entry, invoice, or household item that references a work item — the reference shows the area breadcrumb
  • Household items: Open /household-items list and a detail page — area breadcrumb renders correctly
  • Pickers: Create a work item or household item, open a picker (work item, household item, budget line) — each row shows the area as a secondary line
  • No Area filter: On work items and household items lists, open the area filter — the "No Area" option appears and filters correctly
  • Deep hierarchy: Filter by a parent area — matches include all descendants (no empty result bug)

Budget Overview

  • Area breakdown: Open /budget/overview — cost breakdown is grouped by area, not category; nested rows are indented; "No Area" bucket replaces "Unassigned"
  • Expandable tree: Click into a nested area — rows expand showing children
  • Clickable tiles: Click a summary tile (e.g., Total Budget) — all matching budget lines are selected
  • Print: Print the Budget Overview page (browser print dialog) — page margins, title spacing, and nested group boxes render cleanly; AppShell chrome is suppressed; inner item separators are visible

Budget Sources

  • Source list: Open /budget/sources — each row expands inline to show grouped budget lines
  • Area tree: Inside an expanded source, budget lines are grouped by area with a hierarchical tree and dense columnar layout; horizontal dividers separate work item groups
  • Multi-select: Select multiple budget lines across groups — selection state is preserved across area expansions
  • Mass-move: Use the mass-move modal to reassign selected lines to a different source — assignment persists after reload
  • Work item links: Budget line rows link to the underlying work item (correct ancestor resolved)

Navigation

  • Vendors moved: Vendors is no longer in the top-level Budget subnav — it now appears under Settings
  • Deep links: Old /vendors URLs still resolve or redirect correctly

Reliability / Infrastructure

  • Reverse proxy: When running behind a reverse proxy, rate-limiting uses the correct client IP (no X-Forwarded-For spoof surface); trustProxy is narrowed
  • i18n: Switch language to German — Budget Sources line panel is fully translated; no missing-key warnings in console

Regressions to spot-check

  • General navigation across Dashboard, Work Items, Household Items, Budget, Gantt, Diary, Invoices, Vendors, Settings — no console errors
  • Dark mode toggle still works across all new components
  • Responsive layout (tablet, mobile) works across Budget pages

Testing

  • DockerHub beta image: docker pull steilerdev/cornerstone:beta
  • PR-specific image: docker pull steilerdev/cornerstone:pr-<pr-number> (available after CI runs)

steilerDev and others added 30 commits March 25, 2026 19:44
…ot assigned (#1200)

* fix(work-items): display vendor name in assignedTo column when user not assigned

The assignedTo column on the work items list previously only displayed
assigned user names, silently ignoring assigned vendors. Updated the column's
render function to check assignedUser first, then fall back to assignedVendor,
then show '—' if neither is set.

Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>

* chore: update review metrics for PR #1200

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>
…d error handling (#1203)

The backup creation flow had multiple issues causing SQLITE_CANTOPEN errors:

1. Used system tar binary which is unavailable in DHI production image —
   replaced with Node.js tar package (pure JS, no system dependencies)
2. No pre-flight writability check on backup directory — added probe file test
3. SQLite backup and tar errors propagated as generic 500 — wrapped in new
   BackupFailedError with BACKUP_FAILED error code
4. DB snapshot was written to backup dir instead of data dir, so it was never
   included in the tar archive — corrected to write into data dir
5. Post-tar cleanup referenced wrong path — corrected to use dbSnapshotPath

Fixes #1201

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude backend-developer (Haiku) <noreply@anthropic.com>
* feat(backup): set sensible default for BACKUP_DIR

- Add default value '/backups' to BACKUP_DIR configuration in config.ts
- Create /backups directory in Docker deps stage for production stage copying
- Copy /backups directory with correct node:node ownership in production stage
- Declare /backups as a Docker VOLUME
- Add cornerstone-backups volume to docker-compose.yml service
- Add backup env vars to commented environment block in docker-compose.yml
- Uncomment BACKUP_DIR with updated comment in .env.example

Fixes #1199

Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>

* refactor(backup): make backupDir required, clean up non-null assertions

Make `backupDir` a required field in `AppConfig` since it now always has
the '/backups' default. Remove non-null assertions in backups.ts and
simplify guard conditions in backupService.ts to only check backupEnabled.
Update tests to reflect the new defaults and remove the now-unreachable
backupDir-undefined guard test.

Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(backup): remove unreachable BACKUP_NOT_CONFIGURED tests

With BACKUP_DIR defaulting to /backups, backupEnabled is always true
when no env var is set. The 4 tests asserting 503 BACKUP_NOT_CONFIGURED
for missing BACKUP_DIR now test an unreachable code path.

Co-Authored-By: Claude qa-integration-tester (Haiku 4.5) <noreply@anthropic.com>

* docs: update BACKUP_DIR default in CLAUDE.md env var table

Reflects the new /backups default added in the previous commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(e2e): mock 503 response in backup not-configured E2E test

BACKUP_DIR now defaults to /backups, so the testcontainer always has
backups enabled. The "not configured" E2E test must now mock the 503
API response instead of relying on the testcontainer not having
BACKUP_DIR set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>
The CLA workflow fails on PRs with agent-authored commits because
"Frontend Developer" and "Backend Developer" are not GitHub users
and were not in the allowlist. The contributor-assistant action
falls back to the git author name when GitHub login is null, so
adding these names to the allowlist resolves the check.

Fixes #1203

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Metrics were being written to .claude/metrics/review-metrics.jsonl and
as HTML comments on PR reviews. Remove both approaches entirely — no
metrics are collected going forward.

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI test shards now collect coverage and a new Coverage Report job merges
shard results into a single artifact (coverage-summary.json + coverage-final.json)
retained for 30 days. A zero-dependency merge script (scripts/merge-coverage.mjs)
handles the Istanbul JSON merging without adding nyc as a dependency.

Agent policy changes prevent coverage regression:
- dev-team-lead review now enforces test file parity (blocking)
- qa-integration-tester must run --coverage locally before committing
- e2e-test-engineer must verify route coverage inventory
- Implementation checklist updated with parity + E2E route requirements

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#1210)

* fix(date-range-picker): prevent phase reset after start date selection

The DateRangePicker was resetting to 'selecting-start' phase immediately
after the user selected a start date. This occurred because the useEffect
synced phase from props, and the parent DateFilter component did not call
onChange for a complete range until both dates were selected.

The fix introduces pendingStartDate internal state to track intermediate
(uncommitted-to-parent) selection state. The problematic useEffect is
replaced with a narrower version that only responds when the parent
externally clears both values (startDate='' AND endDate=''). This way,
the component doesn't lose its UI state during the two-phase selection
flow until the parent explicitly resets.

All internal uses of startDate (prop) that tracked the selection flow are
now updated to use pendingStartDate instead. The startDate prop is now
only used for initial view calculation and detecting external clear.

Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>

* test(date-range-picker): add regression tests for phase-persistence fix (#1178)

Cover the pendingStartDate internal state introduced by the fix:
- Phase persists to selecting-end immediately after clicking start date
  within the same mounted instance (regression guard for issue #1178)
- Hover range preview and dayDisabled class both use pendingStartDate
  rather than the startDate prop after a start-date click
- Single-instance two-click flow: start → end without a prop update
  between clicks emits the correct onChange('start', 'end') call
- Escape during selecting-end resets pendingStartDate and phase
- Clicking before / on pendingStartDate during selecting-end handled
- External clear (both props → '') resets phase via rerender
- Partial prop update (only startDate changes) does NOT reset phase

Update 'advances to selecting-end phase' test to verify phase within the
same mounted instance instead of a separate render call.

Rewrite 'when startDate prop changes... externally' to use rerender on a
single component instance (correct test technique for the narrowed useEffect).

Add integration test to DateFilter.test.tsx: single-instance two-click
flow verifying DateFilter does not call onChange for partial selections
and calls onChange once with full range format after second click.

Fixes #1178

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(date-range-picker): fix three props sync tests broken by narrowed useEffect

The production fix narrowed the reset useEffect so it only fires when both
startDate and endDate props are exactly ''. Three tests were written against
the old behavior (phase derived from prop values on mount) and now fail:

1. 'when both startDate and endDate props are externally cleared to ""' — was
   mounting with non-empty props and asserting phase='end' before rerender;
   fixed by mounting with non-empty props, clicking a day to reach selecting-end
   internally, then rerendering with empty props so the useEffect fires.

2. 'when endDate prop is set externally, phase remains at selecting-end' — was
   asserting that mounting with only startDate set produces phase='end' (old
   behavior). Rewritten to assert that mounting with dates set produces
   phase='start' (new behavior: phase is internal-only).

3. Regression block 'external clear via rerender' — same root cause as (1);
   was mounting with non-empty props and asserting 'end' before rerender.
   Fixed with the same click-first pattern.

Fixes #1178

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* chore: trigger CI retry

* fix(date-range-picker): restore useEffect branch for external startDate changes

The narrowed useEffect only handled the clear case (both props empty), but
also needs to handle when startDate changes externally from '' to a value
(while endDate stays ''). This is needed for DateFilter integration where
localFrom updates asynchronously via useEffect.

Also fixes 3 test assertions that didn't account for the useState initial
value being derived from the startDate prop.

Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* chore: update review metrics for PR #1210

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>
Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…2E (#1204) (#1211)

* test(photos): add unit tests for usePhotos hook and photoApi client

Adds 95%+ statement coverage for client/src/hooks/usePhotos.ts and
client/src/lib/photoApi.ts. Tests cover loading/success/error states,
uploadPhoto (XHR-based with progress tracking), deletePhoto (optimistic
removal), updatePhoto (optimistic update), refresh, and all URL helper
functions. Includes upload progress lifecycle, FormData construction,
XHR event handling (load/error/abort), and entity change refetch.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(invoices): add unit and integration tests for invoice budget line service and standalone invoice routes

Adds 95%+ coverage tests for three previously uncovered server files:
- invoiceBudgetLineService.test.ts: unit tests for all 5 exported functions
  (list, create, update, delete, getForInvoice) with full error path coverage
- invoiceBudgetLines.test.ts: integration tests for all 4 route handlers
  (GET/POST/PATCH/DELETE) via app.inject(), including auth, validation, 404,
  BudgetLineAlreadyLinkedError, and ItemizedSumExceedsInvoiceError scenarios
- standaloneInvoices.test.ts: integration tests for GET /api/invoices
  (pagination, filtering by status/vendorId/amount/date/dueDate/q, all sortBy
  options) and GET /api/invoices/:invoiceId (cross-vendor lookup, budget lines)

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): add unit tests for milestone pages and invoices page

Add Jest unit tests for zero-coverage client-side pages and API modules:

- MilestonesPage: loading skeleton, populated data, error/empty states, actions menu,
  delete flow with confirmation modal, client-side filtering
- MilestoneDetailPage: loading/404/error states, view mode, edit mode with form pre-fill
  and validation, save/cancel, delete confirmation, linked items display, unlink buttons
- MilestoneCreatePage: form rendering, validation (title required, date required),
  successful submission with navigation, API error handling, submit button state
- InvoicesPage: loading skeleton, populated data (invoice numbers, vendor names, summary
  cards), error state, empty state, create invoice modal with full validation flow,
  actions menu (view navigation), API client tests already existed

Fixes #1204

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): add unit tests for vendor/trade/area client utilities

Add 95%+ coverage unit tests for 10 previously uncovered source files:

API client layer:
- areasApi.test.ts — fetchAreas, fetchArea, createArea, updateArea, deleteArea
- tradesApi.test.ts — fetchTrades, fetchTrade, createTrade, updateTrade, deleteTrade
- vendorContactsApi.test.ts — listVendorContacts, createVendorContact, updateVendorContact, deleteVendorContact
- davTokensApi.test.ts — getDavTokenStatus, generateDavToken, revokeDavToken, getDavProfileUrl
- timelineApi.test.ts — getTimeline

Utility:
- areaTreeUtils.test.ts — buildTree (depth-first ordering, sortOrder/alpha sort, cycle guard, orphan handling)

React hooks:
- useAreas.test.ts — CRUD lifecycle, loading/error states, refetch
- useTrades.test.ts — CRUD lifecycle, loading/error states, refetch
- useVendorContacts.test.ts — empty vendorId guard, optimistic updates, error propagation
- useDavToken.test.ts — generate/revoke/clearNewToken lifecycle, status refresh

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): add page and API client unit tests for vendors, user management, household items, and work item budgets/milestones

Adds five new test files targeting previously uncovered client-side code (Gap 4 + Gap 6):

- client/src/lib/workItemBudgetsApi.test.ts (24 tests): fetchWorkItemBudgets, createWorkItemBudget, updateWorkItemBudget, deleteWorkItemBudget — validates URL construction, request bodies, response extraction, and error propagation.
- client/src/lib/workItemMilestonesApi.test.ts (21 tests): getWorkItemMilestones, addRequiredMilestone, removeRequiredMilestone, addLinkedMilestone, removeLinkedMilestone — validates HTTP method, URL path with milestoneId, and error handling.
- client/src/pages/VendorsPage/VendorsPage.test.tsx (20 tests): loading/data/error states, create vendor modal (validation, API call, error display), action menu, delete confirmation.
- client/src/pages/UserManagementPage/UserManagementPage.test.tsx (23 tests): loading/data/error states, client-side filter, action menu (edit disabled for deactivated users), edit modal (pre-fill, validation, API call, cancel), deactivate modal (confirm, error).
- client/src/pages/HouseholdItemsPage/HouseholdItemsPage.test.tsx (20 tests): loading/data/error states, action menu, delete confirmation with error handling, graceful degradation when vendor/category fetch fails.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): fix TypeScript duplicate property error in areaTreeUtils.test.ts

The makeArea helper used an object literal that set `id` and `name` both
explicitly and again via spread `...overrides`, triggering TS2783 (property
specified more than once). Refactor to build defaults object first then merge.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): add unit and integration tests for photos service and route

Adds 96 tests covering the server-side photo upload infrastructure:
- photoService.test.ts (51 tests): unit tests for uploadPhoto, getPhoto,
  getPhotosForEntity, updatePhoto, reorderPhotos, deletePhoto,
  deletePhotosForEntity, and getPhotoFilePath. sharp is mocked via
  jest.unstable_mockModule to avoid native binary dependency.
- photos.test.ts (45 tests): integration tests for all 8 photo route
  handlers using app.inject() with multipart form bodies, mock photoService,
  and real auth/session via buildApp().

Coverage achieved:
- photoService.ts: 97.82% statements, 91.66% branches, 100% functions
- photos.ts: 91.91% statements, 85.18% branches, 100% functions/lines
  (uncovered lines are unreachable auth guards inside route handlers)

Part of #1204: close server-side coverage gaps.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* chore(memory): update qa agent memory with Gap 5 test patterns

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): fix TypeScript type errors and add server-side route/service tests

Fix TypeScript type errors in three client test files:
- HouseholdItemsPage.test.tsx: correct ApiClientError arg order (statusCode first),
  type jest.fn() mocks for useAreas createArea/updateArea/deleteArea methods
- UserManagementPage.test.tsx: correct ApiClientError arg order, type
  jest.fn() mocks for refreshAuth/logout as () => Promise<void>
- VendorsPage.test.tsx: correct ApiClientError arg order, type jest.fn()
  mocks for useTrades createTrade/updateTrade/deleteTrade methods

Add 5 server-side integration/unit tests (Gap 4):
- householdItemSubsidyPayback.test.ts (route): subsidy payback calculation endpoint
- workItemMilestones.test.ts (route): work item milestone dependency routes
- householdItemBudgetService.test.ts: HI budget line CRUD and calculations
- householdItemWorkItemService.test.ts: HI-to-work-item association service
- workItemMilestoneService.test.ts: work item milestone dependency service

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(coverage): fix HouseholdItemStatus enum values in workItemService test

Replace invalid status values 'ordered', 'delivered', and 'cancelled'
with valid HouseholdItemStatus enum values: 'purchased', 'arrived', 'scheduled'.
HouseholdItemStatus = 'planned' | 'purchased' | 'scheduled' | 'arrived'.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(client): fix top-level await import crash in page component tests

Move `await import()` into `beforeEach` using the established lazy-load
pattern. Jest's --experimental-vm-modules runner does not support
top-level await in test files.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(client): add invoiceBudgetLinesApi unit tests

33 tests covering fetchInvoiceBudgetLines, createInvoiceBudgetLine,
updateInvoiceBudgetLine, and deleteInvoiceBudgetLine.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(e2e): add milestones E2E tests and page objects

3 POMs (MilestonesPage, MilestoneCreatePage, MilestoneDetailPage) and
50 test cases covering CRUD flows, responsive layout, dark mode, and
smoke tests. Closes Gap 1 E2E coverage.

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(e2e): add invoices, settings/manage E2E tests and page objects

- InvoicesPage + InvoiceDetailPage POMs with CRUD tests (list, create,
  detail, edit, delete, status filter, responsive, dark mode)
- HouseholdItemEditPage POM for edit page coverage
- Settings/Manage E2E tests covering all 4 tabs (Areas, Trades, Budget
  Categories, HI Categories) with CRUD, URL deep-linking, responsive
- Agent memory updates

Closes Gap 2 + Gap 6 E2E coverage.

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(client): fix DataTable dual-render and mock isolation issues in page tests

Fix 39 failing tests across VendorsPage, UserManagementPage, and
HouseholdItemsPage by addressing three root causes:

1. Missing preferencesApi mock — DataTable's useColumnPreferences hook
   calls listPreferences() on mount; without a mock the async failure
   caused repeated re-renders and cascading re-fetches (fetchVendors
   called 16×, listHouseholdItems 13×, listUsers 3×).

2. Unstable Map reference in useTableState mock — returning
   `filters: new Map()` inside the mock factory creates a new Map
   instance on every render, triggering infinite useEffect re-fetch
   loops in pages that include filters in their dependency array.
   Fixed by hoisting stable Map constants outside the mock factory.

3. DataTable dual-render (table rows + mobile cards) — getByText/
   getByTestId throw "found multiple elements". Replaced with
   getAllByText/getAllByTestId throughout, and switched toHaveBeenCalledTimes(1)
   to toHaveBeenCalled() for the same reason.

Also: use mockReset() instead of mockClear() in beforeEach to drain
stale mockResolvedValueOnce queues from prior tests, and fix
button-finder predicates to match actual translation output
(t('vendors.buttons.create') = "Add Vendor"; modal footer button
text is "Delete Item" not "Delete").

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(client): fix duplicate-render and deferred-promise issues in page/hook tests

Fix remaining test failures in MilestonesPage, MilestoneCreatePage,
MilestoneDetailPage, InvoicesPage, and usePhotos:

- MilestonesPage: delete modal confirm button text is "Delete Milestone"
  (from t('milestones.delete.delete')), not /^delete$/i; update regex
  to /delete milestone/i. Also apply getAllByText/getAllByTestId pattern
  for DataTable dual-render (table rows + mobile cards).

- MilestoneCreatePage/MilestoneDetailPage: form validation tests use
  fireEvent.submit() instead of clicking the submit button to bypass
  native HTML required validation and let the JS handler run. Fix
  non-404 error tests to assert /not found/i since milestone stays null
  when fetch fails. Use getAllByText for /completed/i which appears in
  multiple elements.

- InvoicesPage: apply getAllByText/getAllByTestId throughout for
  DataTable dual-render. Chain mockResolvedValue (not Once) for
  fetchAllInvoices in error state tests to handle possible re-fetch
  calls triggered by useTableState effect. Fix amount input label
  selector to /^amount/i.

- usePhotos: use a deferred promise in the progress wrapper test so the
  upload does not resolve before the progress assertion runs.

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(client): fix deferred-promise contamination in usePhotos and InvoicesPage

usePhotos test 17: replaced immediate Promise.resolve() with a deferred
promise to prevent upload resolution from flushing before the progress
assertion, fixing the uploadProgress.get() = undefined failure and the
result.current = null contamination in tests 18-32.

InvoicesPage: switched modal tests from mockResolvedValueOnce to
mockResolvedValue so useTableState-driven extra loadInvoices calls don't
consume the only mock and cause DataTable error banners to appear alongside
modal validation alerts. Fixed getByText/getByTestId → getAllByText/
getAllByTestId for DataTable dual-render duplicates. Changed user.type on
number inputs to fireEvent.change for reliable controlled-input state.

Fixes #1204

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(client): fix areaTreeUtils sortOrder for deterministic ordering

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(server): fix additionalProperties test assertions for Fastify AJV behavior

Fastify's AJV compiler is configured with removeAdditional=true by default,
which strips unknown properties from request bodies and querystrings rather
than rejecting them with 400. Three tests incorrectly expected 400 when sending
unknown fields — corrected to expect the actual strip-and-succeed behavior.

- invoiceBudgetLines: POST and PATCH tests with unknownField now expect 201/200
- standaloneInvoices: GET with unknownParam now expects 200 (param stripped)

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* test(server): fix householdItemBudgetService tests — add required budgetSourceId

The service requires budgetSourceId on create. Added defaultSourceId
to all create calls and fixed test assertions to match actual service
behavior (budgetSourceId cannot be cleared).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(memory): update QA agent memory with test fix patterns

* fix(e2e): correct milestone API response shape in helpers and smoke tests

POST /api/milestones returns MilestoneDetail directly (no { milestone: {...} }
wrapper), but createMilestoneViaApi() and Scenarios 4 & 5 were trying to read
body.milestone.id, causing TypeError: Cannot read properties of undefined
(reading 'id') when running against the real Docker container.

Fix: read body.id directly (MilestoneDetail shape per API contract).

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>

* fix(ci): rename coverage shard files to avoid merge collision

Each test shard produces coverage/coverage-final.json. With
merge-multiple, files overwrite each other. Rename to
coverage-shard-N.json before upload so all shards survive download.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(e2e): fix area/trade delete selectors and Promise.race in invoice POM

Area and trade delete buttons in ManagePage only have the text "Delete"
with no aria-label containing the entity name. The tests were using
`getByRole('button', { name: \`Delete \${areaName}\` })` which would never
match. Fix: scope via the itemRow filtered by entity name, then target
the "Delete" button within that row.

Also replace Promise.race() with Promise.any() in InvoicesPage.waitForLoaded()
to prevent the losing waitFor() calls from becoming dangling unhandled
rejections after the race resolves, which could cause flaky failures in
concurrent shard runs.

Fixes #1204

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>

* chore(memory): update E2E agent memory with fix patterns

---------

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
chore: sync main into beta
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.34.1 to 4.35.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@3869755...c10b806)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](docker/login-action@b45d80f...4907a6d)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](docker/build-push-action@d08e5c3...bcafcac)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [docker/scout-action](https://github.com/docker/scout-action) from 1.20.3 to 1.20.4.
- [Release notes](https://github.com/docker/scout-action/releases)
- [Commits](docker/scout-action@8910519...bacf462)

---
updated-dependencies:
- dependency-name: docker/scout-action
  dependency-version: 1.20.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4.0.5 to 5.0.0.
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](actions/deploy-pages@d6db901...cd2ce8f)

---
updated-dependencies:
- dependency-name: actions/deploy-pages
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/configure-pages/releases)
- [Commits](actions/configure-pages@983d773...45bfe01)

---
updated-dependencies:
- dependency-name: actions/configure-pages
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.5.0 to 3.0.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](dependabot/fetch-metadata@21025c7...ffa630c)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…cted (#1241) (#1244)

* test(work-items): add unit + integration tests for areaId CSV filter (#1241)

Add 11 unit tests to listWorkItems() and 4 integration tests via
app.inject() covering the new resolveAreaIds helper:
- Single leaf / parent / root ID expansion
- CSV of two non-adjacent IDs
- CSV of parent + descendant (dedup)
- Array input path
- Empty string (filter skipped)
- Unknown ID (0 results)
- CSV with empty segments
- Whitespace-only segments
- All-whitespace CSV (filter skipped)
- Route-level: parent→child hierarchy, CSV union, unknown ID, empty area

Also extends insertTestArea() in workItems.test.ts to accept parentId.

Fixes #1241

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <noreply@anthropic.com>

* test(e2e): add E2E tests for work items area filter hierarchy and CSV (#1241)

Validates the areaId filter bug fix: parent area selection now expands to
include descendant work items, and CSV multi-area selection returns the union.

Fixes #1241

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com>

* fix(work-items): accept CSV/array of area IDs in list filter (#1241)

Adds a resolveAreaIds helper to workItemService that normalises the
areaId filter value from either a comma-separated string or an array of
strings into a proper array before building the SQL IN clause. Previously
a single area ID passed as a plain string was treated as a scalar and the
generated query returned zero results for parent areas.

Fixes #1241

Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>

* docs(api): document CSV/array support for work-items areaId filter

Updates wiki submodule pointer to include API contract documentation
for the areaId CSV/array filter parameter added in da67e29.

Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>

* test: fix time-bomb tests unmasked by date 2026-04-16

Two distinct time-bomb patterns fixed:

1. Calendar picker tests (DateRangePicker, DateFilter): Tests relied on the
   real system clock to determine which month the picker displays by default.
   Once the real date advanced past March 2026, assertions on specific day
   numbers (e.g. clicking "15" and expecting "2026-03-15") started failing
   because the picker now shows April 2026+. Fix: pin system time to
   2026-03-15T12:00:00Z via jest.useFakeTimers in beforeAll/afterAll with
   doNotFake for async timers (setTimeout, setInterval, etc.) so React
   Testing Library's act/findBy* internals continue to work correctly.

2. Scheduler autoReschedule test (workItemService): The "allows updating both
   dates simultaneously" fixture used 2026-04-01/10 as target dates. The
   scheduler's "today floor" clamps not_started item start dates to today,
   so once the real date advanced past 2026-04-01 the update silently
   overwrote the provided dates. Fix: shift target dates to 2099-04-01/10,
   well beyond any realistic system clock advance.

Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <noreply@anthropic.com>

* fix(docker): ensure server/node_modules exists for production COPY

The production stage does `COPY --from=deps /app/server/node_modules/
server/node_modules/`. When npm hoists all of @cornerstone/server's
production dependencies to the root node_modules/, the workspace-local
server/node_modules/ directory is never created and the COPY fails with
"failed to calculate checksum ... /app/server/node_modules: not found".

Create empty workspace node_modules directories in the deps stage right
after `npm ci --omit=dev` so the subsequent COPYs succeed regardless of
npm's hoisting decisions. Also covers client/ and shared/ defensively
in case future Dockerfile edits add COPYs for those paths.

Unblocks all open PRs (CI Docker build failing since ~20:44 UTC).

Co-Authored-By: Claude product-architect (Sonnet 4.6) <noreply@anthropic.com>

---------

Co-authored-by: Frontend Developer <frontend-developer@cornerstone.local>
Co-authored-by: Claude qa-integration-tester (Sonnet 4.6) <noreply@anthropic.com>
Co-authored-by: Frank Steiler <frank@steiler.de>
…nse (#1242) (#1252)

* feat(budget): add area-grouped budget summaries to budget overview API

Add areaSummaries array and unassignedSummary object to GET /api/budget/overview
response. areaSummaries contains a flat list of all area nodes with planned/actual
totals rolled up through the area hierarchy. unassignedSummary aggregates budget
totals for work items with no area assignment.

- shared/src/types/budgetOverview.ts: add AreaBudgetSummary interface, extend
  BudgetOverview with areaSummaries and unassignedSummary fields
- shared/src/index.ts: export AreaBudgetSummary
- server/src/services/budgetOverviewService.ts: implement area aggregation logic
  using getDescendantIds for subtree rollup; compute work item → area mappings;
  aggregate planned/actual amounts per area
- wiki/API-Contract.md: update GET /api/budget/overview schema, JSON example, and
  documentation notes; add AreaBudgetSummary interface definition

Fixes #1242

Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>

* docs(api): bump wiki pointer for budget overview area summaries

Fixes #1242

Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>

* test(budget-overview): add area aggregation tests + update fixtures

Add 32 unit/integration tests covering all area budget summary spec
scenarios. Update 7 client fixture files to include the new
`areaSummaries: []` and `unassignedSummary: null` fields required by
the updated BudgetOverview type.

Fixes #1242

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <noreply@anthropic.com>

---------

Co-authored-by: Frank Steiler <frank@steiler.de>
Co-authored-by: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com>
…1255)

* chore(ci): remove CLA check and stale doc references

The CLA workflow is optional (not a required status check on beta or
main — branch rulesets require only Quality Gates / E2E Gates). As the
sole contributor, the self-signed CLA provides no value.

- Delete CLA.md, .github/workflows/cla.yml, .github/cla/signatures.json
- Strip stale "+ CLA" mentions from /develop, /epic-close, /release skill
  docs

* fix(milestones): bypass browser native validation to surface custom error banner

HTML5 required on the title/targetDate inputs blocked form submission
before the onSubmit handler could call setError(...), so the custom
error banner (role=alert) never rendered. E2E Scenarios 6/7 on the
milestone create page asserted on a null banner text and failed across
desktop/tablet/mobile.

Adding noValidate to both the create form and the detail-page edit
form lets the JS validation handler run and populate the errorBanner
as the tests expect. The required attribute stays for screen-reader
hinting.

* fix(subnav): drop misused list/listitem role overrides

The SubNav component forced role='list' on the container <div> and
role='listitem' on each NavLink. Overriding the default role of an
<a> to 'listitem' makes it invisible to role=link accessibility queries,
breaking several E2E tests that navigate via getByRole('link', ...)
after the #1188 PageLayout refactor (invoices Budget subnav, settings
subnav across desktop/tablet/mobile).

Removing both role attributes lets NavLink expose its native anchor
role. Update SubNav unit tests to assert link semantics accordingly.

* test(e2e): fix broken locators and races exposed by recent CI

- MilestonesPage.getMilestoneTitles: the cardCell class does not exist
  on DataTableCard (which uses cardRow/cardValue). Scope the card
  iterator to top-level .card children (class^='card_') and read the
  title from .cardValue. Fixes the 3 mobile milestones scenarios (API
  create, delete confirm, delete cancel).

- invoices.spec.ts Scenario 2: pending summary count is returned from
  the same API response as the list, but a brief interleaving window
  after invoice creation causes the list render to show stale counts.
  Poll the summary count with expect.poll instead of a one-shot read.

- invoices.spec.ts Scenario 7: DataTable renders both the desktop
  table and the mobile cards simultaneously and toggles visibility
  via CSS media queries. locator('.invoiceLink').first() picked the
  hidden desktop <a> on mobile. Add :visible so the currently shown
  link is clicked.

- area-filter.spec.ts Scenario 1: the Area column is defaultVisible
  false, so its in-header Filter by Area button is never rendered.
  Drop the secondary assertion on the filter-button visibility; the
  core URL-based areaId filter behaviour is still fully validated.

* test(e2e): tighten WorkItemsPage emptyState locator

[class*=emptyState] can match unrelated elements with classes like
emptyStateTitle/emptyStateDescription. Scope to the CSS-module-hashed
prefix emptyState_ so only the EmptyState component wrapper is picked
up. Aimed at the tablet filter-empty-state flake on shard 12.

* test(e2e): update SubNav-dependent tests to use role=link

After the SubNav role override was dropped, tests that relied on
role=listitem on nav tabs need to use role=link. Covers:

- budget/vendors.spec.ts (Vendors tab)
- budget/budget-sources.spec.ts (Sources tab)
- budget/budget-overview.spec.ts (four budget tabs)
- budget/subsidy-programs.spec.ts (Subsidies tab)
- admin/backup-restore.spec.ts (Backups tab) + remove outdated comment
- i18n/i18n.spec.ts (Auftragnehmer tab)

* test: scope SubNav link queries and fix invoice-create status default

- MilestoneCreatePage.test.tsx: the back-link regex `/back|milestones/i`
  now collides with the SubNav Milestones tab (both role=link after the
  SubNav role override was dropped). Tighten to `/←\s*milestones/i`.

- invoices.spec.ts Scenario 1 (Budget subnav tabs): scope
  getByRole('link', 'Overview') to the Budget <nav> landmark so it
  does not collide with the project-logo link's aria-label
  ("Go to project overview").

- invoices.spec.ts Scenario 2 (Create invoice pending count): the
  create form defaults status to 'quotation' now (previously 'pending').
  Explicitly select 'pending' on the status select so the test's
  pending-summary assertion actually reflects the created invoice.

- settings-manage.spec.ts: same scoping fix for Profile / Manage
  SubNav tabs.

* test(milestones): narrow detail-page back-link regex

Same collision as MilestoneCreatePage after the SubNav role change: the
/back|milestones/i regex now matches both the header back link and the
SubNav Milestones tab. Tighten to /←\s*milestones/i.

* test: second-pass SubNav scoping fixes

- MilestoneDetailPage.test.tsx (not-found state): the not-found branch
  renders without SubNav and uses 'milestones.detail.backLink' which
  translates to 'Back to Milestones', not the header-format arrow.
  Restore the broader 'back to milestones' regex for this case.

- settings-manage.spec.ts (subnav test): add 'exact: true' so the
  'Manage' link query does not collide with 'User Management' tab.

---------

Co-authored-by: Frank Steiler <frank@steiler.de>
…maining color (#1335, #1336) (#1337)

- Move Show lines / Edit / Delete into a single right-aligned action group in each
  source card's header row so they sit next to the label and badges (#1335)
- Unify button padding so all three buttons share a consistent height; stretch
  edge-to-edge on mobile preserving 44px touch targets
- Use the existing --color-danger-text-on-light token for the negative-remaining
  state so overspend is visible but not dominant (#1336)

Fixes #1335
Fixes #1336

Co-authored-by: Frank Steiler <frank@steiler.de>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.3.0-beta.55 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

steilerdev-cornerstone-bot Bot and others added 7 commits April 20, 2026 16:58
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.1 to 4.35.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@c10b806...95e58e9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](dependabot/fetch-metadata@ffa630c...25dd0e3)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the dev-dependencies group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [prettier](https://github.com/prettier/prettier) | `3.8.2` | `3.8.3` |
| [stylelint](https://github.com/stylelint/stylelint) | `17.6.0` | `17.8.0` |
| [typescript](https://github.com/microsoft/TypeScript) | `6.0.2` | `6.0.3` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.58.1` | `8.58.2` |
| [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) | `5.6.6` | `5.6.7` |
| [webpack](https://github.com/webpack/webpack) | `5.106.1` | `5.106.2` |


Updates `prettier` from 3.8.2 to 3.8.3
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](prettier/prettier@3.8.2...3.8.3)

Updates `stylelint` from 17.6.0 to 17.8.0
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](stylelint/stylelint@17.6.0...17.8.0)

Updates `typescript` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](microsoft/TypeScript@v6.0.2...v6.0.3)

Updates `typescript-eslint` from 8.58.1 to 8.58.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.58.2/packages/typescript-eslint)

Updates `html-webpack-plugin` from 5.6.6 to 5.6.7
- [Release notes](https://github.com/jantimon/html-webpack-plugin/releases)
- [Changelog](https://github.com/jantimon/html-webpack-plugin/blob/main/CHANGELOG.md)
- [Commits](jantimon/html-webpack-plugin@v5.6.6...v5.6.7)

Updates `webpack` from 5.106.1 to 5.106.2
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](webpack/webpack@v5.106.1...v5.106.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: stylelint
  dependency-version: 17.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.58.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: html-webpack-plugin
  dependency-version: 5.6.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: webpack
  dependency-version: 5.106.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the prod-dependencies group with 2 updates: [ical-generator](https://github.com/sebbo2002/ical-generator) and [react-i18next](https://github.com/i18next/react-i18next).


Updates `ical-generator` from 10.1.0 to 10.2.0
- [Release notes](https://github.com/sebbo2002/ical-generator/releases)
- [Changelog](https://github.com/sebbo2002/ical-generator/blob/develop/CHANGELOG.md)
- [Commits](sebbo2002/ical-generator@v10.1.0...v10.2.0)

Updates `react-i18next` from 17.0.3 to 17.0.4
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](i18next/react-i18next@v17.0.3...v17.0.4)

---
updated-dependencies:
- dependency-name: ical-generator
  dependency-version: 10.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-dependencies
- dependency-name: react-i18next
  dependency-version: 17.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#1339)

Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 3.0.0 to 3.1.1.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](actions/create-github-app-token@f8d387b...1b10c78)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 3.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…1341)

Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 4.0.0 to 5.0.0.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](actions/upload-pages-artifact@7b1f4a7...fc324d3)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.3.0-beta.56 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

steilerdev-cornerstone-bot Bot and others added 2 commits April 21, 2026 06:00
…ge; group GH Actions updates (#1347)

- Bump actions/setup-node to v6.4.0 (supersedes #1343)
- Bump actions/upload-artifact to v7.0.1 (supersedes #1342)
- Remove .github/workflows/dependabot-auto-merge.yml (no longer used)
- Group all github-actions Dependabot updates into a single PR via
  `groups.github-actions` pattern `*`
- Drop stale reference to dependabot-auto-merge.yml from CLAUDE.md

Closes #1342
Closes #1343

Co-authored-by: Frank Steiler <frank@steiler.de>
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.3.0-beta.57 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

…) (#1348)

- Restructure source card so the bar chart and summary rows span the full card width; header line contains [name + badges] on the left and [Show lines] [Edit] [Delete] right-aligned on the right
- Remove the now-unused .sourceInfo wrapper; .sourceRowHeader moves inside the view-mode fragment only
- Restyle .expandToggle at rest to match the Edit button so the three header buttons read as one cohesive action group
- Keep the primary-based active indicator on the expanded state
- Add three DOM-structure regression tests

Fixes #1346

Co-authored-by: Frank Steiler <frank@steiler.de>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.3.0-beta.58 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Refresh user-facing documentation to reflect the area-tree hierarchy
rework, Budget Overview and Budget Sources redesigns, Vendors move into
Settings, tightened reverse-proxy handling, and the TypeScript 6.0
upgrade included in the upcoming stable release.

- docs/src: update work items, household items, budget, and
  getting-started guides to cover area breadcrumbs across the app, the
  "No Area" filter, the area-grouped Budget Overview with clickable
  tiles and print styling, the inline-expansion / multi-select /
  mass-move workflow on Budget Sources, and the narrower trustProxy
  behavior for reverse-proxy users.
- docs/src/guides/budget/vendors-and-invoices.md: document the move of
  Vendors into the Settings section while invoices stay in Budget.
- README.md: refresh the Features section (Work Items, Areas & Trades,
  Budget Management) without touching the protected NOTE block.
- RELEASE_SUMMARY.md: rewrite for this release - area hierarchy,
  Budget Overview + Sources redesign, navigation, reliability, and
  TypeScript 6.0 tooling; note no database migration is required and
  describe the trustProxy behavior change for self-hosters.
- .env.example: add a short note next to TRUST_PROXY explaining that
  only the first proxy hop is trusted and rate limiting is now
  spoof-resistant.

Co-authored-by: Frank Steiler <frank@steiler.de>
Co-authored-by: Claude docs-writer (Opus 4.6) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.3.0-beta.59 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@steilerDev steilerDev merged commit dd1862d into main Apr 21, 2026
44 checks passed
@steilerDev steilerDev deleted the beta branch April 21, 2026 07:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant