release: promote beta to main - #2075
Conversation
## Summary - Replaces the diary page's broken numbered pager with scroll-driven batch loading: a reusable `useInfiniteScroll` hook (`client/src/hooks/`) plus a reusable `InfiniteScrollFooter` component (`client/src/components/`), consumed by `DiaryPage`. - Fixes the underlying root cause of the broken pager (the debounced-search-sync effect unconditionally resetting `page=1` on every `searchParams` identity change via `useSearchParams()`). - Went through 3 review rounds: R1 fixed 4 findings plus 2 QA-identified races (epoch-guard for stale in-flight fetches on filter/search reset, hardcoded i18n announcement, stale `page` URL param on search); R2 fixed epoch-gated consumer metadata (`onPageApplied`/`onPageFailed`), made `InfiniteScrollFooter` fully reusable (label props + `testIdPrefix`, no `diary` namespace coupling), removed a duplicate loading indicator, and fixed announcement pluralization; R3 switched the pluralized announcement keys to native i18next `_one`/`_other` suffixes and registered the new shared component/hook in CLAUDE.md's Component Reuse Policy. Refs #2060 Fixes #2061 Fixes #2062 Two bugs were introduced and fixed within this PR's own new code during review (`useInfiniteScroll`'s reset/in-flight race, and a hardcoded i18n announcement string) — filed as #2061/#2062 for review-trail visibility and closed here rather than left open, since neither ever shipped to a merged branch. Issue #2060 itself stays **In Progress** (not closed by this PR) — it is a standalone story (no parent epic to run `/epic-close`) with 7 UAT scenarios that have not yet been executed; several ACs (AC4 scroll-position stability, AC19 focus-indicator visibility in both themes, AC22 mobile behavior) are appearance-and-feel claims that need manual UAT sign-off before Done. Two non-blocking follow-ups were filed during review and are intentionally deferred (not fixed in this PR): - #2064 — a first-batch load failure offers no retry affordance (footer is gated on `entries.length > 0`) - #2065 — the "Load more" button loses keyboard focus while its own batch loads (shared-component a11y issue, remedy owned by ux-designer's Style Guide) ## Test plan - [x] Unit tests pass (`useInfiniteScroll`, `InfiniteScrollFooter`, `DiaryPage` — 95%+ coverage) - [x] Integration tests pass - [x] E2E coverage: scroll-trigger, keyboard "Load more", dedupe, end-of-list, empty state, filter/search reset (including stale `page` param removal), error/retry, legacy `?page=` bookmark, responsive/dark-mode - [x] CI Quality Gates pass (typecheck, tests, build, audit) - [x] CI E2E Gates pass (all 16 shards) - [ ] Manual UAT-1 through UAT-7 (tracked on #2060, not yet run) Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude product-owner <noreply@anthropic.com> Co-Authored-By: Claude ux-designer <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude product-owner <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com> Co-Authored-By: Claude ux-designer <noreply@anthropic.com>
…ld rows (#2046) ## Summary - Extends `/budget/invoices` with a "Show only open items" toggle: pending invoices and invoices with pending deposits appear with their pending deposits nested as expandable child rows. - Adds server-side `openOnly` filtering/ordering (earliest-open-due default sort), a new per-invoice `openAmount` figure, and global `openPayable`/`refundsDue` summary totals — all derived from a single shared aggregate so the per-row and header figures always agree. - Extends the shared `DataTable` component with an opt-in, backward-compatible expandable-child-row capability (used by no other page today). - Fixes a pre-existing bug where invoice status badges rendered with no color due to a wrong CSS module reference. Refs #2046 ## Test plan - [x] Unit tests pass (441+ across new/modified files, 95%+ coverage on new code) - [x] Integration tests pass - [x] E2E: 19 new scenarios (S1-S19, S21) covering filtering, ordering, pagination, responsive/dark-mode, accessibility - [x] German translations added with verified key parity - [x] CI Quality Gates green - [x] Reviewed by product-architect, security-engineer, product-owner, ux-designer — all approved 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude backend-developer <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude product-owner <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude security-engineer <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com> Co-Authored-By: Claude ux-designer <noreply@anthropic.com>
…lisions (#2046) ## Summary Follow-up to PR #2066 (issue #2046). Full E2E execution after that merge revealed the story's own acceptance test file (`invoices-open-items.spec.ts`) failed entirely — `Quality Gates` doesn't run full E2E shards on beta PRs, so this wasn't caught before merge. - Fixed a Playwright/React timing race in `setOpenItemsOnly()` (one-shot `setChecked()` against a controlled checkbox). - Fixed a real dual-mount bug: three badges rendered into both the desktop table and mobile card DOM with identical `data-testid`s. Added `ColumnDef.renderCard` overrides giving mobile instances `-mobile-` suffixed testids, matching the existing deposit-row convention. - Fixed two E2E test-instrument bugs (programmatic `.focus()` not triggering `:focus-visible`; an `[aria-expanded]` locator colliding with an unrelated column-settings button). - Added regression tests proving the desktop/mobile testid disambiguation actually works (previous assertions were tolerant of either count). Refs #2046 ## Verification - Real Docker/testcontainers execution: 36/36 E2E scenarios passing across desktop/tablet/mobile, run twice (serial and CI-matching parallelism) - This PR's own CI run: all 16 E2E shards + E2E Smoke Tests green, not just Quality Gates - Jest: 66/66 across the two InvoicesPage test files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
…updates Remediates Dependabot bump — see PR description.
… 13 updates Remediates Dependabot bump (13 updates) — see PR description and review comments for security and changelog analysis. Co-Authored-By: Claude backend-developer <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude security-engineer <noreply@anthropic.com>
Remediates 15 orphan Dependabot alerts via root package.json overrides (fast-uri, js-yaml, ip-address, and the npm CLI's bundled undici/ip-address). See PR description for details. Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com>
…ith 20 updates Remediates the remaining Dependabot dev-dependencies group findings: jest.fn<any>() typing breaks from jest 30.5.1, a fontkit type-guard fix, and SearchPicker-family test timing (global testTimeout raise + a real assertion-ordering race fix). See PR description and commit history for details. Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
- Bumps `github/codeql-action/upload-sarif` 4.37.8 -> 4.37.9 (default CodeQL bundle 2.26.4). - Bumps `actions/deploy-pages` 5.0.0 -> 5.0.1 (adds backoff and jitter to deployment polling). - Both actions remain SHA-pinned; no action input/output surface changed. Remediates Dependabot bump - see PR description.
Detailed ValidationRun against A. Invoices — "Show only open items" (#2046)A1. Toggle narrows the list correctly
A2. Open deposits appear as expandable child rows
A3. Header totals reconcile with the rows
A4. Default ordering
A5. Toggle interaction with pagination and filters
A6. Status badge colours (regression fix)
A7. Mobile / narrow viewport (dual-mount fix)
A8. Dark mode
B. Diary — infinite scroll (#2060, UAT-1 … UAT-7)
B1. Scroll-driven loading (UAT-1)
B2. Scroll-position stability (UAT-2 / AC4)
B3. Keyboard "Load more" (UAT-3)
B4. No duplicates, clean end-of-list (UAT-4)
B5. Focus indicator in both themes (UAT-5 / AC19)
B6. Filter / search reset (UAT-6)
B7. Mobile (UAT-7 / AC22)
B8. Legacy bookmark
C. Dependency sweep — smoke onlyNo user-visible surface, but the bumps touch Fastify, i18next/react-i18next, Jest, and the npm CLI's bundled deps, plus overrides for C1. Log in and out (exercises Fastify session/cookie handling). |
CI gate failed — blocked on #2076
Not a flake
Shards 1-4 were healthy on the same run, so the runner was fine. Root causeReal-timer The decisive evidence is a control group inside the failing file: the two fake-timer tests at lines 79/100 mount the same The Fix in progress#2076 converts all 57 real-timer This PR will be re-gated once the fix lands on |
- Bumps `fastify` 5.12.1 -> 5.12.3 (5.12.2 was a security release for four HIGH-severity advisories; none exploitable in this codebase, closed defensively), `i18next` 26.4.0 -> 26.4.2 and `react-i18next` 17.0.12 -> 17.0.13 (types-only). - Hardens the SearchPicker and HouseholdItemPicker suites onto fake timers, removing a wall-clock dependency that predates this bump (introduced on beta by 038a943). Shard 6 goes from 4 failures to green; shard 5 from 10 to 0. No assertions weakened, no testTimeout raised. - Residual repo-wide test-timing exposure tracked in #2078. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
… tests Quality Gates was failing on the v2.15.0 promotion (#2075) with 60s test timeouts in picker-family suites. Three hypotheses were falsified by measurement before landing on the cause: - The dev-dependencies bump: bisection on #2070 reproduced identical failures on the OLD jest/testing-library versions, and a binary diff of the two user-event tarballs found no hot-path change. - Fake timers: beta already ships SearchPicker.test.tsx and HouseholdItemPicker.test.tsx fully converted, and BOTH still timed out. - Worker contention: in shard 5 the other 77 suites finished at 19:23:29 and SearchPicker.test.tsx then ran alone on the runner until 19:49:23 -- 25m54s with zero contention -- and still blew the ceiling. The actual cause is a machine-speed gap: GitHub-hosted runners are ~1.8x slower single-threaded than the dev boxes the 60s ceiling was calibrated on (SearchPicker 2326s CI vs 1330s local; WorkItemPicker 436s vs 224s). At ~39s/test the heaviest tests cross 60s and the rest do not. - Raise --testTimeout to 240000 in the CI shard invocation only. Verified via --showConfig that the CLI flag lands in globalConfig, dodging the projects[].testTimeout trap. jest.config.ts stays at 60000 so it remains a tight dev-side regression detector. - Record every rejected lever with its evidence in ci.yml so they are not re-proposed, and add a runner-capacity diagnostic step. - Raise scripts/ci-wait.sh defaults (2400s beta / 3600s main). The old 600s/900s were below even a healthy run and reported TIMEOUT ~25min before a verdict existed. - Convert WorkItemPicker.test.tsx (10 sites) and the remaining 6 sites in HouseholdItemPicker.breadcrumb.test.tsx to fake timers. This removes a real wall-clock dependency but is explicitly NOT the fix. - Correct jest.config.ts and agent memory, which asserted a mechanism the measurements refuted. This is a stopgap. The ~21s per-test CPU cost is unexplained and stays open in #2078; next step is node --cpu-prof on a single dropdown test. A green Quality Gates is not evidence the slowness was solved. Refs #2076 Refs #2078 Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Raise --testTimeout to 240000 in the CI shard invocation only; jest.config.ts stays at 60000 as the dev-side regression detector - Root cause: GitHub runners are ~1.8x slower single-threaded than dev boxes, so the 60s ceiling was miscalibrated; three other hypotheses were falsified by measurement first - Convert WorkItemPicker (10 sites) and the 6 remaining HouseholdItemPicker.breadcrumb sites to fake timers; correct jest.config.ts and agent memory that asserted a refuted mechanism - Raise scripts/ci-wait.sh defaults, which were below even a healthy run Stopgap only: the ~21s per-test CPU cost is unexplained and stays open in #2078. Refs #2076 Refs #2078 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
- Bumps `eslint` 10.9.1 -> 10.10.0 and `stylelint` 17.14.1 -> 17.15.0. No new findings from eslint 10.10.0's added rule checks; stylelint clean. - Repairs the lockfile: Dependabot's regeneration dropped the client/node_modules/webpack-dev-server@5.2.6 entry forced by the root overrides block, breaking `npm ci` with EUSAGE across all CI jobs. Regenerated with a full `npm install`; purely additive (843 insertions, 0 deletions), `npm ci` verified. - Shard-5 timeouts were unrelated to these bumps and are resolved upstream by e3ccf55 (CI-only testTimeout headroom). Underlying per-test CPU cost stays open in #2078. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Claude backend-developer <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude docs-writer <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 2.15.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Release Summary
Promotes
beta(v2.15.0-beta.6) tomainas v2.15.0: two user-facing features — the Invoices "Show only open items" view with nested open deposits, and scroll-driven batch loading on the Diary page replacing the broken numbered pager — plus a full dependency-security sweep (prod, dev, GitHub Actions, and 15 orphan advisory remediations).Changes
Features
/budget/invoicesshowing pending invoices and invoices with pending deposits, with the pending deposits nested as expandable child rows. Adds server-sideopenOnlyfiltering with earliest-open-due default ordering, a per-invoiceopenAmount, andopenPayable/refundsDueheader totals — all derived from one shared aggregate so row and header figures always agree. Extends the sharedDataTablewith an opt-in, backward-compatible expandable-child-row capability.useInfiniteScrollhook andInfiniteScrollFootercomponent, both registered in CLAUDE.md's Component Reuse Policy. Fixes the pager's root cause (a debounced search-sync effect resettingpage=1on everysearchParamsidentity change).Fixes
data-testids, now disambiguated viaColumnDef.renderCardoverrides with-mobile-suffixes. Plus a Playwright/React timing race insetOpenItemsOnly()and two E2E test-instrument bugs.pageURL param on search.Chores / Dependencies
package.jsonoverrides (fast-uri,js-yaml,ip-address, and the npm CLI's bundledundici/ip-address).jest.fn<any>()typing migration), a fontkit type-guard fix, and a real SearchPicker assertion-ordering race fix.github/codeql-action/upload-sarif-> 4.37.9,actions/deploy-pages-> 5.0.1); all remain SHA-pinned.Change Inventory
Backend (
server/,shared/)server/src/services/invoiceService.ts—openOnlyfiltering, ordering,openAmount, summary totalsserver/src/services/shared/depositAggregateUtils.ts(new) — shared open-amount aggregateserver/src/routes/standaloneInvoices.ts—openOnlyquery paramserver/src/app.ts— minorshared/src/types/invoice.ts—openAmount,openPayable,refundsDuetypesinvoiceService.openItems.test.ts,depositAggregateUtils.openAmounts.test.ts,invoiceService.test.ts,standaloneInvoices.test.tsFrontend (
client/)InvoicesPage.tsx,InvoicesPage.module.css,openItemsUtils.ts(new),lib/invoicesApi.tsDiaryPage.tsx,DiaryPage.module.cssDataTable/(DataTable.tsx,DataTableCard.tsx,DataTableHeader.tsx,DataTableRow.tsx,DataTable.module.css),InfiniteScrollFooter/(new),Badge/Badge.module.cssuseInfiniteScroll.ts(new),useTableState.tsen/budget.json,de/budget.json,en/diary.json,de/diary.jsonDataTable.expandableRows.test.tsx,InvoicesPage.openItems.test.tsx,openItemsUtils.test.ts,useInfiniteScroll.test.tsx,InfiniteScrollFooter.test.tsx,DiaryPage.test.tsxE2E Tests (
e2e/)e2e/tests/invoices/invoices-open-items.spec.ts(new, 20 scenarios)e2e/tests/diary/diary-list.spec.ts— rewritten for infinite scrolle2e/pages/InvoicesPage.ts,e2e/pages/DiaryPage.ts— page objectse2e/tests/invoices/invoices.spec.ts,e2e/tests/navigation/dashboard.spec.tsDocs / Config
package.json,package-lock.json,client/package.json,server/package.json,e2e/package.json— dependency bumps and security overridesjest.config.ts— corrected diagnosis comment (ceiling stays 60000, see below)scripts/ci-wait.sh— timeout defaults raised to 2400s beta / 3600s main.github/workflows/ci.yml,.github/workflows/release.ymlCLAUDE.md— Component Reuse Policy entries forInfiniteScrollFooter/useInfiniteScroll.claude/agent-memory/**,.claude/checklists/implementation-checklist.mdwikisubmodule pointerManual Validation Checklist
/budget/invoices, enable "Show only open items". Confirm the list narrows to pending invoices plus invoices that have pending deposits, and that invoices with pending deposits show an expander revealing those deposits as child rows./diary, scroll to the bottom and confirm the next batch loads automatically; confirm the "Load more" button works via keyboard; confirm no duplicate entries appear and an end-of-list state is reached.page=param./diary?page=3and confirm it loads sensibly rather than erroring.CI Timeout Stopgap (read before merging)
Quality Gatesblocked this promotion for several runs with 60-second Jesttimeouts in the picker-family component suites — never assertion failures.
Three hypotheses were falsified by measurement before the cause was found:
old jest/testing-library versions, and a binary diff of the two
user-eventtarballs found the hot path byte-identical.userEvent.betaalready shippedSearchPickerandHouseholdItemPickerfully converted to fake timers, and both still timedout.
and
SearchPicker.test.tsxthen ran alone on the runner until 19:49:23 —25m54s with zero contention — and still blew the ceiling.
The actual cause is a machine-speed gap: GitHub-hosted runners are ~1.8x
slower single-threaded than the dev boxes the 60s ceiling was calibrated on
(SearchPicker 2326s CI vs 1330s local; WorkItemPicker 436s vs 224s). At
~39s/test the heaviest tests cross 60s and the rest do not.
e3ccf553raises--testTimeoutto 240000 in the CI shard invocationonly;
jest.config.tsstays at 60000 so it remains a tight dev-sideregression detector. Every rejected lever is recorded with its evidence in
ci.ymlso it is not re-proposed.This is a stopgap, not a fix. A green
Quality Gateshere is not evidencethe slowness was solved — the suite was only given more room (shard 5 still
takes ~42 minutes). The unexplained ~21s per-test CPU cost stays open in
#2078; the next step there is
node --cpu-profon a single dropdown test.Known Open Items (not blocking promotion)
entries.length > 0).Testing
docker pull steilerdev/cornerstone:betadocker pull steilerdev/cornerstone:pr-2075🤖 Generated with Claude Code