Skip to content

release: promote beta to main#1700

Open
steilerDev wants to merge 50 commits into
mainfrom
beta
Open

release: promote beta to main#1700
steilerDev wants to merge 50 commits into
mainfrom
beta

Conversation

@steilerDev

Copy link
Copy Markdown
Owner

Release Summary

This release ships three user-facing features — Paperless-first invoice creation with LLM auto-itemize, a mobile-first photo capture flow with orientation metadata, and diary daily-log enhancements (vendor + work start/end time with computed duration) — alongside a substantial code-quality sweep (zero ESLint warnings, no any, consistent type imports), dependency bumps, and test-infra hardening.

Changes

Features

Fixes

Chores / Refactoring

Change Inventory

Backend (server/, shared/)

  • Invoices/auto-itemize: routes/invoiceAutoItemize.ts, services/invoiceAutoItemizeService.ts, services/invoiceService.ts, shared/types/invoiceAutoItemize.ts
  • Paperless: routes/paperless.ts, services/paperlessService.ts, routes/dav.ts, shared/types/paperless.ts
  • Photos/orientations: routes/photos.ts, services/photoService.ts, routes/orientations.ts, services/orientationService.ts, migrations 0037_orientations.sql, 0038_add_photo_orientation.sql, shared/types/photo.ts, shared/types/orientation.ts
  • Diary: services/diaryService.ts, shared/types/diary.ts, services/calendarIcal.ts
  • LLM/budget extraction: services/budgetExtraction/openAICompatibleProvider.ts, prompts.ts
  • Schema: db/schema.ts

Frontend (client/)

  • Invoices: components/invoices/InvoicePaperlessPickerModal.tsx, budget/invoice components
  • Photos: components/photos/ (PhotoUpload, PhotoMetadataModal, PhotoMetadataSidepanel, PhotoViewer, PhotoAnnotator, PhotoCard)
  • Orientations: components/OrientationPicker/
  • Diary: components/diary/ (DiaryEntryForm, DiaryFilterBar, DiaryMetadataSummary, DiaryEntryCard, Signature*)
  • Documents: components/documents/ (DocumentCard, LinkedDocumentCard, LinkedDocumentsSection, DocumentBrowser)
  • i18n: en/de updates across budget, common, diary, documents, photoViewer, settings namespaces + glossary

E2E Tests (e2e/)

  • New: orientations.spec.ts, photo-capture-flow.spec.ts, invoices/paperless-first-invoice*.spec.ts, invoices/invoice-auto-itemize-page.spec.ts, diary/diary-daily-log-time-vendor.spec.ts, diary/diary-mobile-filters.spec.ts
  • New page objects: OrientationsPage, PaperlessInvoiceReviewPage, PaperlessPickerModal, DiaryEntryEditPage, InvoicesPage

Docs / Config

  • CLAUDE.md, eslint.config.js, jest.config.ts, root + workspace package.json / lockfile, scripts/check-single-dep-version.sh

Manual Validation Checklist

  • Paperless-first invoice: Create an invoice by picking a Paperless document; confirm LLM auto-itemize populates line items and totals correctly (including VAT gross-up for net line items).
  • Photo capture (mobile): On a mobile viewport, capture/upload a photo; confirm the metadata modal appears and orientation metadata is saved and displayed.
  • Diary daily log: Add a daily-log entry with a vendor and work start/end time; confirm the computed duration displays correctly.
  • Diary mobile filters: On mobile, open the diary filter panel; confirm filters and search are usable and anchored to the bar.
  • Documents unlink: Open a linked document card; confirm the unlink action is discoverable in the top-right overlay.
  • Orientations: Open the Orientations management tab; confirm CRUD works and styling is correct.
  • Dark mode + responsive: Spot-check the above on desktop/tablet/mobile and in dark mode.

Testing

  • DockerHub beta image: `docker pull steilerdev/cornerstone:beta`
  • PR-specific image: `docker pull steilerdev/cornerstone:pr-`

steilerDev and others added 30 commits June 13, 2026 13:33
…d duration to daily log (#1673)

* feat(diary): add optional vendor and work start/end time with computed duration to daily log

Extends daily_log diary entries with three new optional fields: vendor
(via SearchPicker), work start time, and work end time (both HH:mm 24h
inputs). The server resolves vendorName from vendorId for display. A
computed duration (hours, 2 decimal places) is calculated client-side
and shown inline between the time inputs. Cross-field validation rejects
end ≤ start. DiaryMetadataSummary renders the new fields for saved
entries. German translations and full unit + E2E test coverage included.

Fixes #1672

Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]>
Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]>
Co-Authored-By: Claude translator (Sonnet 4.6) <[email protected]>
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]>
Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]>

* test(diary): add computeWorkDuration to formatters mock factories

DiaryMetadataSummary now re-exports computeWorkDuration from formatters.js,
so any test file that mocks formatters.js and transitively imports
DiaryMetadataSummary must include the export or the suite fails with a
SyntaxError. Add computeWorkDuration to the mock factories in DiaryPage.test.tsx
and DiaryEntryDetailPage.area.test.tsx (matching the style of each file's
existing computeActualDuration stub).

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]>

---------

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…items (#1678)

Previously, when auto-itemizing an invoice, line items where `includesVat`
is `false` (net prices) were summed at their raw stored value without
applying the applicable VAT rate. This caused the computed auto-itemize
total to be ~19% lower than the actual gross amount, producing a false
variance on the invoice and triggering a spurious `TOTAL_MISMATCH` error
during itemized-sum validation.

The fix introduces a shared helper `effectiveLineAmount(line)` in
`@cornerstone/shared` that returns the gross-equivalent amount for any
line: `amount * (1 + vatRate)` for net lines and `amount` for gross
(includesVat=true) lines. All total-computation paths in
`invoiceAutoItemizeService` now use this helper. Storage is unchanged —
line amounts are still persisted as net; the VAT gross-up is applied
only at aggregation time.

Fixes #1677

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
#1684)

Hoisted 52 inline `import()` type annotations to top-of-file `import type`
declarations across 23 test and page-object files (14 client, 4 server, 5 e2e).
No runtime behavior change — purely mechanical lint compliance.

Fixes #1458

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…ollision (#1685)

Add an exact-version override (10.3.0) for the `konva` package in the root
`package.json` so the entire workspace tree hoists a single Konva copy.
Without this override the root package had no Konva entry while
`@cornerstone/client` depended on it directly, allowing npm to silently
resolve two different Konva versions under certain install conditions and
causing TypeScript type-collision errors.

Also adds `scripts/check-single-dep-version.sh`, a guardrail script that
asserts exactly one version of a given package is present across the
`node_modules` tree. The script is wired into the Static Analysis job in
`ci.yml` so that any future Konva version drift fails CI immediately.

Fixes #1646

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…rlay (#1680)

Replaces the hidden context-menu unlink action with a visible overlay button
on LinkedDocumentCard that appears on hover/focus. Also fixes a bug where the
unlink-confirmation modal's Cancel button rendered a raw i18n key instead of
the translated string (was using `t('button.cancel')` without the required
`common:` namespace prefix).

Fixes #1680

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…re usable (#1688) (#1689)

Fixes #1688

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…arnings (#1469) (#1690)

Adds justified inline eslint-disable directives (with -- reason comments) and
renames stale `react-hooks/exhaustive-deps` directives to the real rule
`@eslint-react/exhaustive-deps` across 45 client/src/ files.

All changes are comment-only or behavior-neutral dependency-array reformats;
no logic or rendering behavior is altered. The inline disables are explicitly
permitted by the issue's acceptance criteria where refactoring would require
non-trivial state-model changes.

Verified result: zero remaining set-state-in-effect errors, zero
@eslint-react/exhaustive-deps errors, zero stale-rule "Definition for rule
not found" errors, zero unused-disable directives. Net ESLint error count
drops by 20 vs beta HEAD with no new errors introduced.

Fixes #1469

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…-pattern (#1568) (#1686)

* test(infra): fix per-render useNavigate jest.fn() anti-pattern in LinkedDocumentsSection

Replace `useNavigate: () => jest.fn()` with a stable module-scope mock
(`const mockNavigate = jest.fn()`) that is cleared in beforeEach.

The per-render pattern allocates a new function object on every component
render/re-render within a test, which inflates Jest worker heap over time
when the test suite has many renders. The stable module-scope mock reuses
the same function across all renders in a test run.

This is Phase 4 from issue #1568 (Jest memory audit). Phases 2-3 (shared
formatters mock factory via static import) were investigated but cannot be
delivered: adding a static `import` before `jest.unstable_mockModule` in
Jest 30 ESM VM mode causes the mock to fail to intercept the real module in
CI (shards 1, 2), even though the factory itself is sound. The inline factory
pattern is required for reliable mock registration in Jest ESM.

Fixes #1568

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <[email protected]>

* chore(memory): document Jest ESM static-import mock constraint from issue #1568 investigation

Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <[email protected]>

---------

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude qa-integration-tester (Sonnet 4.6) <[email protected]>
…1463) (#1691)

Replace `any` with `unknown` or precise types where the shape is known,
and mark genuine boundary escape hatches (Fastify decorators, dynamic
drizzle table rows, schema-erased DB handles, WebDAV inject verbs) with
reasoned per-line eslint-disable comments. Type-only changes; no runtime
or test-behavior changes.

Fixes #1463

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude backend-developer (Haiku 4.5) <[email protected]>
Added a Node-scripts flat-config block in eslint.config.js (Node globals
+ no-console: off for scripts/**) ordered after the General rules block,
clearing 171 no-undef/no-console problems in scripts/*.mjs.

Real fixes in server/src (calendarIcal, diaryService: no-useless-assignment),
shared/src (diary.ts: no-empty-object-type interface→type alias), and
client/src (no-unused-vars, no-case-declarations, preserve-caught-error
cause, IIFE-in-JSX extraction, useState setter renames).

Justified inline disables in client/src where no stable id exists for
keys, render-time `new Date()` is intentional, DOM-measurement
useEffect/setState patterns are correct, and naming collisions exist in
LocaleContext/ThemeContext. Test file disables cover
error-boundaries/rules-of-hooks edge cases. Dead-variable removals in
e2e/tests (no-useless-assignment, no-unused-vars, no-self-assign) with
no behavior or assertion changes.

Full-project `eslint .` now reports 0 errors and 0 warnings (down from
857 at the start of the #1455 tracking issue; all 16 child issues
already closed).

Fixes #1455

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…h, jsx-no-children-prop) (#1696)

- Rename local `async function fetch()` in useTimeline.ts to `loadTimeline`
  to eliminate the false-positive @eslint-react/web-api-no-leaked-fetch
  warning (the function is not the Web Fetch API, but shares the name)
- Add justified eslint-disable-next-line for LinkedDocumentsSection.tsx
  where `systemLinkedIds.fetch()` is a custom hook method, not the Web
  Fetch API — the rule cannot distinguish call sites by type
- Move `children` from the React.createElement props object to the 3rd
  positional argument at two call sites in PhotoMetadataSidepanel.test.tsx
  to fix jsx-no-children-prop; behavior-identical refactor

All changes are behavior-preserving. `eslint .` now reports 0 errors and
0 warnings across the full client workspace, completing the clean-lint
baseline established by #1455.

Ref #1455

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
… orientation metadata (#1674)

Mobile-first photo upload: Take photo / Upload photo split on touch devices, per-photo metadata capture modal (description, hierarchical area, orientation) with non-blocking background upload queue, new user-configurable Orientation entity (settings CRUD + nullable photos.orientation_id FK, SET NULL on delete), OrientationPicker showing name + description, orientation field in the photo metadata sidepanel, and the orientation-selector empty-state hint. Fixes #1674. Fixes #1675.
#1681)

Adds a Paperless-first invoice creation flow on Budget → Invoices: when Paperless + LLM are configured, "New Invoice" opens a document picker (searchable correspondent filter, hide-already-linked default ON, open-in-Paperless link, manual-entry escape). Selecting a document runs a stateless LLM auto-itemize preview (LLM picks the vendor from the app vendor list), and the invoice + document link + budget-line itemizations are created atomically only after human review.

New endpoints: GET /api/paperless/correspondents, POST /api/invoices/auto-itemize/preview, POST /api/invoices/auto-itemize/commit (ADR-032). No DB schema changes.

Fixes #1679
…1693)

Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 4.36.1 to 4.36.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@87557b9...8aad20d)

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

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

| Package | From | To |
| --- | --- | --- |
| [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) | `5.8.12` | `5.8.19` |
| [concurrently](https://github.com/open-cli-tools/concurrently) | `10.0.1` | `10.0.3` |
| [prettier](https://github.com/prettier/prettier) | `3.8.3` | `3.8.4` |
| [semantic-release](https://github.com/semantic-release/semantic-release) | `25.0.3` | `25.0.5` |
| [stylelint](https://github.com/stylelint/stylelint) | `17.12.0` | `17.13.0` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.60.1` | `8.61.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.9.1` | `25.9.3` |


Updates `@eslint-react/eslint-plugin` from 5.8.12 to 5.8.19
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.8.19/plugins/eslint-plugin)

Updates `concurrently` from 10.0.1 to 10.0.3
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](open-cli-tools/concurrently@v10.0.1...v10.0.3)

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

Updates `semantic-release` from 25.0.3 to 25.0.5
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](semantic-release/semantic-release@v25.0.3...v25.0.5)

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

Updates `typescript-eslint` from 8.60.1 to 8.61.0
- [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.61.0/packages/typescript-eslint)

Updates `@types/node` from 25.9.1 to 25.9.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 5.8.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: concurrently
  dependency-version: 10.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: prettier
  dependency-version: 3.8.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: semantic-release
  dependency-version: 25.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: stylelint
  dependency-version: 17.13.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: "@types/node"
  dependency-version: 25.9.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… 8 updates (#1695)

* chore(deps): bump the prod-dependencies group with 8 updates

Bumps the prod-dependencies group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [ical-generator](https://github.com/sebbo2002/ical-generator) | `10.2.0` | `11.0.0` |
| [sharp](https://github.com/lovell/sharp) | `0.34.5` | `0.35.1` |
| [tar](https://github.com/isaacs/node-tar) | `7.5.15` | `7.5.16` |
| [i18next](https://github.com/i18next/i18next) | `26.2.0` | `26.3.1` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.6` | `19.2.7` |
| [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.2.6` | `19.2.7` |
| [react-konva](https://github.com/konvajs/react-konva) | `19.2.4` | `19.2.5` |
| [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) | `7.15.1` | `7.17.0` |


Updates `ical-generator` from 10.2.0 to 11.0.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.2.0...v11.0.0)

Updates `sharp` from 0.34.5 to 0.35.1
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](lovell/sharp@v0.34.5...v0.35.1)

Updates `tar` from 7.5.15 to 7.5.16
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](isaacs/node-tar@v7.5.15...v7.5.16)

Updates `i18next` from 26.2.0 to 26.3.1
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](i18next/i18next@v26.2.0...v26.3.1)

Updates `react` from 19.2.6 to 19.2.7
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react)

Updates `react-dom` from 19.2.6 to 19.2.7
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react-dom)

Updates `react-konva` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/konvajs/react-konva/releases)
- [Commits](https://github.com/konvajs/react-konva/commits)

Updates `react-router-dom` from 7.15.1 to 7.17.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/[email protected]/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: ical-generator
  dependency-version: 11.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: prod-dependencies
- dependency-name: sharp
  dependency-version: 0.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-dependencies
- dependency-name: tar
  dependency-version: 7.5.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-dependencies
- dependency-name: i18next
  dependency-version: 26.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-dependencies
- dependency-name: react
  dependency-version: 19.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-dependencies
- dependency-name: react-dom
  dependency-version: 19.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-dependencies
- dependency-name: react-konva
  dependency-version: 19.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-dependencies
- dependency-name: react-router-dom
  dependency-version: 7.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-dependencies
...

Signed-off-by: dependabot[bot] <[email protected]>

* fix(deps): repair react override mismatch in prod-dependencies bump

Dependabot's prod-dependencies bump updated the react/react-dom edges in
client/ and docs/ to 19.2.7 but left the root package.json `overrides`
pinned at 19.2.6. The override forced the top-level react node to 19.2.6
while workspaces requested 19.2.7, leaving duplicate React copies in the
tree. This crashed the client bundle at runtime ("Cannot read properties
of null (reading 'useRef')") and failed all E2E, and corrupted the tree
such that `npm audit signatures` mis-resolved the @docusaurus/react-loadable
alias (ETARGET [email protected]) — failing Static Analysis.

Bump the overrides to 19.2.7 to match the workspace edges and regenerate
the lockfile with a clean `npm install`. react and react-dom now dedupe to
a single 19.2.7 across all workspaces.

Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude backend-developer (Haiku 4.5) <[email protected]>
…tTests (#1697)

- Add `workerGracefulExitTimeout: 2000` to the top-level Jest config; the
  500 ms default is too tight for jsdom/file-watcher cleanup in the
  resource-constrained sandbox and produces spurious force-kill warnings.
- Add `test:collect` and `test:collect:json` npm scripts using Jest 30.4.0's
  `--collectTests` flag to enumerate all test suites and names without
  executing them.
- Document `test:collect` in CLAUDE.md Common Commands table.

Fixes #1573

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude qa-integration-tester (Sonnet 4.5) <[email protected]>
…) (#1698)

OrientationsTab referenced undefined CSS module classes so orientation
rows rendered completely unstyled. Mapped all class references to the
existing defined classes used by TradesTab and other Manage tabs
(itemsList/itemRow/itemInfo/itemDetails/itemSortOrder/itemActions/
editActions/saveButton) and restructured view-mode JSX to match
TradesTab layout.

Added 20 unit tests covering both view and edit modes of OrientationsTab,
and a deep-link E2E test verifying the Orientations tab is selected when
navigating directly to #orientations.

Fixes #1687

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…1714)

Adopt @floating-ui/[email protected] (ADR-033) for SearchPicker dropdown positioning, replacing the hand-rolled portal/getBoundingClientRect/rAF approach. useFloating fixed-strategy + offset/flip/shift/size + autoUpdate + FloatingPortal keeps the dropdown anchored to its input during mobile momentum scroll and soft-keyboard/visualViewport shifts — the cases the prior rAF fix (#1712) could not handle. FloatingPortal preserves the #1601 anti-clipping; an isPositioned gate avoids the first-frame flash. Drops the hide() middleware (it blanked the dropdown inside modals).

Fixes #1708

Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]>
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]>
Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]>
Co-Authored-By: Claude product-architect (Sonnet 4.5) <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.8.0-beta.25 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

chore: reconcile main into beta (recover from mis-targeted #1710 squash)
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.8.0-beta.26 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Frank Steiler and others added 2 commits June 16, 2026 16:19
Adds a required-status-check workflow that fails any pull request targeting
'main' whose head branch is not 'beta'. Mark the 'guard' job as a required
check on the main ruleset to block out-of-band promotions (the #1710 class of
incident, where a beta-based fix branch was squash-merged directly into main).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
ci: enforce that PRs into main originate from beta
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.8.0-beta.27 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

…in CI (#1718)

* fix(e2e): harden Scenario 14 against slow getDiaryEntry response in CI

Register a page.waitForResponse for GET /api/diary-entries/:id BEFORE
clicking the draft card, then await it after waitForURL completes. This
ensures the heading/draftBadge assertions only fire after the API
response has arrived, not after an arbitrary 15s wall-clock timeout.

Also align both toBeVisible() timeouts to 45_000 (matching test.slow()'s
3× budget) so CI load spikes cannot exhaust the deadline.

The previous { timeout: 15_000 } overrides bypassed test.slow()'s
tripled expect.timeout and caused intermittent failures when the server
response exceeded 15s under 8-worker CI load.

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]>

* fix(ci): install chrome-headless-shell for Playwright 1.60 and bump browser cache key

Since Playwright 1.49+, `chrome-headless-shell` is a SEPARATE browser binary
from `chromium` and must be explicitly installed. The previous install command
(`npx playwright install chromium webkit`) omitted it, so every E2E test that
uses the headless-shell channel failed with:

  browserType.launch: Executable doesn't exist at
  .../chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell

Fix: add `chromium-headless-shell` to the warmup install step.

The browser cache key is bumped from `playwright-v3` to `playwright-v4` (all
four restore/save references) so any stale cached browser set that is missing
the headless-shell binary is bypassed and a correct, complete set is re-seeded.
The apt system-deps cache key (`apt-v3-playwright-*`) is intentionally left
unchanged — only the `~/.cache/ms-playwright` browser cache key changes.

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]>

* fix(ci): add Playwright browser install-on-cache-miss fallback to smoke and shard jobs

The E2E Cache Warmup job installs browsers and saves them under the
`playwright-v4-{version}-{os}` cache key. However, the saved cache is
not reliably visible to downstream jobs (e2e-smoke, e2e) — they receive
"Cache not found for input keys: playwright-v4-..." and subsequently
fail with "browserType.launch: Executable doesn't exist at
.../chromium_headless_shell-1223/...".

Changes:
- Add `id: browser-cache` to the "Restore browser cache" step in both
  the smoke job and the shard matrix job (was already present in the
  warmup job).
- After the "Install Playwright system dependencies" step in each of
  those two jobs, add a new conditional step that runs
  `npx playwright install chromium chromium-headless-shell webkit` only
  when `steps.browser-cache.outputs.cache-hit != 'true'`.

This guarantees browsers are present regardless of cache visibility,
eliminating the "Executable doesn't exist" failure class that causes
entire shards to fail when the warmup cache is not populated or not
accessible on the runner.

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]>

---------

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.8.0-beta.28 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

* docs: update documentation for v2.8.0 release

Document Paperless-first invoice creation with auto-itemize, mobile-first
photo capture with orientation tagging, diary daily-log vendor/work-hours,
and the relocated document unlink action. Refresh README, RELEASE_SUMMARY,
and add a new Capturing Photos guide.

Co-Authored-By: Claude docs-writer (Sonnet 4.6) <[email protected]>

* chore: add E2E test.slow() timeout-override lesson to implementation checklist

Captures the v2.8.0 release lesson: test.slow() triples the project
expect.timeout but an explicit per-assertion { timeout } override negates
the tripling, causing intermittent CI failures (diary Scenario 14).

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude docs-writer (Sonnet 4.6) <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.8.0-beta.29 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

steilerDev pushed a commit that referenced this pull request Jun 16, 2026
The auto-fix bot pushed a [skip ci] commit to beta after the docs merge,
advancing the v2.8.0 promotion PR #1700 HEAD to a commit with no CI runs.
This clean-titled commit advances beta with a real (markdown-only) diff so
CI fires and pull_request:synchronize re-runs on #1700.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
* chore: retrigger promotion CI (post-docs auto-fix skip-ci)

The auto-fix bot pushed a [skip ci] commit to beta after the docs merge,
advancing the v2.8.0 promotion PR #1700 HEAD to a commit with no CI runs.
This clean-titled commit advances beta with a real (markdown-only) diff so
CI fires and pull_request:synchronize re-runs on #1700.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* chore: nudge promotion CI synchronize

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Frank Steiler <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
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