From 6429f82eacabc3951617aadf04f4585fae1df530 Mon Sep 17 00:00:00 2001 From: Frank Steiler Date: Tue, 28 Apr 2026 12:33:53 +0200 Subject: [PATCH 1/7] docs: update documentation for v2.4.2 release (#1378) Update user-facing docs to reflect the v2.4.2 bug-fix release: - budget-overview.md: clarify that the source filter recalculates the Available Funds and Remaining Budget (Cost + Net) totals and the Pending/Paid/Quotation summary cards in real time, and that deselected source rows are hidden from print output. - linking-documents.md: document the "Hide already-linked documents" checkbox and note it now defaults to unchecked, so re-linking is no longer gated by clearing the filter. - RELEASE_SUMMARY.md: rewrite as a v2.4.2 changelog covering all six bug fixes (#1366 source filter totals, #1369 picker default, #1370 scroll wheel, #1371 VAT round-trip, #1372 vendor blur, #1373 summary cards refresh). Verified .env.example is in sync with server/src/plugins/config.ts and all process.env.* references in server/src. Co-authored-by: Frank Steiler Co-authored-by: Claude docs-writer (Opus 4.6) --- RELEASE_SUMMARY.md | 55 +++++-------------- docs/src/guides/budget/budget-overview.md | 5 +- .../src/guides/documents/linking-documents.md | 2 + 3 files changed, 19 insertions(+), 43 deletions(-) diff --git a/RELEASE_SUMMARY.md b/RELEASE_SUMMARY.md index 6c7b68f05..e08ae7b15 100644 --- a/RELEASE_SUMMARY.md +++ b/RELEASE_SUMMARY.md @@ -1,47 +1,20 @@ -## What's New +# v2.4.2 Release Summary -This release sharpens the **Budget Overview** so every cost in your project is traceable to the source funding it. Every line in the Cost Breakdown now carries a colored source attribution badge, the **Available Funds** row expands into a per-source Cost / Payback / Net view, and clicking a source detail row toggles it on or off -- with the entire breakdown (totals, projections, and subsidy math) recalculated server-side and the selection persisted in the URL so you can bookmark, share, or refresh a filtered view. +A small bug-fix release that tightens up the Budget and Document workflows introduced in v2.4. No new features, no breaking changes, no migration required -- pull and restart. -### What's new +## Bug Fixes -**Source attribution everywhere** +- **Budget source filter now drives every total on the page.** The "Available Funds" and "Remaining Budget" columns in the Cost Breakdown table -- as well as the Pending, Paid, and Quotation summary cards above the table -- now correctly reflect the active source filter. Previously they always showed unfiltered totals, which made the per-source filter misleading when you only wanted to see the picture for a single source. +- **Document picker shows all documents by default.** The "Hide already-linked documents" checkbox in the document picker now starts unchecked, so every document is immediately visible. You no longer have to clear the filter before you can re-link a document that is already attached elsewhere. +- **Mouse wheel no longer changes numeric fields.** Scrolling the page with your mouse wheel while the cursor sits over an Amount or budget field will not accidentally increment or decrement the value -- a common source of silent edits when scrolling long invoice forms. +- **VAT checkbox round-trips correctly.** The VAT / tax checkbox on invoices now preserves its state when you reopen an invoice for editing. Previously the saved state was not always reflected in the form. +- **Vendor picker no longer clears on blur.** Selecting a vendor in the Add Invoice form and then clicking elsewhere on the page (e.g. into the Amount field) no longer clears the selection. +- **Budget Overview summary cards refresh with the source filter.** The Pending, Paid, and Quotation summary cards on the Budget Overview page now refresh correctly when you toggle the source filter, so the headline numbers always match the rows below them. -- Every leaf-level budget line in the Cost Breakdown shows a deterministic, color-coded badge for its financing source -- the same source always gets the same color across the table, between sessions, and in light and dark mode. -- On desktop the badge shows the full source name; on mobile it collapses to a colored dot to keep rows compact. -- A 10-slot palette guarantees consistent contrast in both themes; budget lines without a source assignment use a reserved "Unassigned" color. +## What to Update -**Available Funds row redesign** +```bash +docker pull steilerdev/cornerstone:latest +``` -- Click the **Available Funds** row to expand it and see one row per financing source. -- Each source row shows three numbers in dedicated columns: **Cost** (allocated against budget lines, perspective-aware), **Payback** (subsidy payback against lines funded from that source), and **Net** (remaining headroom after cost and payback). -- It's the fastest way to spot which source is most depleted, which one is being subsidized, and which one still has slack. - -**Per-source filter** - -- Click any source detail row to toggle that source on or off. The entire breakdown -- summary tiles, projected cost range, remaining budget, every nested area row -- recalculates against the visible set. -- The selection is persisted in the URL as `?deselectedSources=,` so a filtered view can be bookmarked, shared, or reloaded without losing state. -- Press **Escape** while a source row is focused to clear all deselections in one go. -- A small "X of N selected" caption appears next to **Available Funds** while a filter is active. - -**Server-side filter pipeline** - -- The new filter is wired through to a single endpoint: `GET /api/budget/breakdown?deselectedSources=...`. -- Subsidy payback math is computed against the filtered set on the server, so subsidies that no longer have qualifying budget lines drop out cleanly instead of double-counting. -- Filter changes round-trip in a single request -- no client-side recomputation drift. - -### Fixes - -- Source detail rows stay visible even when every source is deselected, so the filter is never a dead-end. -- Dark mode contrast and focus ring are corrected on the Cost Breakdown error banner. - -### Tooling - -- The `Quality Gates` workflow now supports `workflow_dispatch`, making it easy to re-run gates manually during a promotion. - -### Breaking changes - -None. Existing bookmarks, links, URLs, and data model are preserved. The `?deselectedSources=` query parameter is purely additive -- omit it (or leave it empty) to get the unchanged behavior. - -### Migration notes - -No database migration is required. The new filter parameter is server-validated and silently ignores unknown source IDs, so older clients that don't send the parameter continue to work without changes. +Restart your container -- no database migration is required. diff --git a/docs/src/guides/budget/budget-overview.md b/docs/src/guides/budget/budget-overview.md index d50bebe45..1cbb011ba 100644 --- a/docs/src/guides/budget/budget-overview.md +++ b/docs/src/guides/budget/budget-overview.md @@ -79,13 +79,14 @@ This makes it easy to spot which source is most depleted, which one is being sub Click any source detail row inside the **Available Funds** expansion to toggle that source on or off. Deselected sources are dropped from the entire breakdown: -- The summary tiles, projected cost range, remaining budget, and every nested area row recalculate against the visible set. +- The summary tiles, projected cost range, every nested area row, and the **Available Funds** and **Remaining Budget** (Cost + Net) totals all recalculate against the visible set in real time as you toggle. - A small "X of N selected" caption appears next to **Available Funds** while a filter is active. - The selection is **persisted in the URL** as `?deselectedSources=,` so you can bookmark a filtered view, share it with your bank or partner, or refresh without losing state. - Press **Escape** while focused on a source row to clear all deselections in one go. - Source detail rows stay visible even when every source is deselected -- the filter is never a dead-end; you can always click your way back in. +- When you print a filtered view, deselected source rows are hidden from the printed output so the report only shows the sources you actually have selected. -Filtering happens **server-side** (`GET /api/budget/breakdown?deselectedSources=...`), which means subsidy payback math stays consistent with the visible set: subsidies that no longer have any qualifying budget lines drop out cleanly instead of double-counting. +Filtering happens **server-side** (`GET /api/budget/breakdown?deselectedSources=...`), which means subsidy payback math stays consistent with the visible set: subsidies that no longer have any qualifying budget lines drop out cleanly instead of double-counting. The Pending, Paid, and Quotation summary cards at the top of the page also refresh in step with the filter -- pick the sources you care about and the headline numbers update without leaving the page. ## Financing Source Summary diff --git a/docs/src/guides/documents/linking-documents.md b/docs/src/guides/documents/linking-documents.md index abbc0e085..a3edc56f9 100644 --- a/docs/src/guides/documents/linking-documents.md +++ b/docs/src/guides/documents/linking-documents.md @@ -31,6 +31,8 @@ Screenshots for the documents feature require a connected Paperless-ngx instance The document is immediately linked and appears in the Documents section. A screen reader announcement confirms the link was created. +The picker shows **all documents** by default. If your document store is large and you only want to see documents that are not yet linked anywhere in Cornerstone, tick the **Hide already-linked documents** checkbox at the top of the picker -- it stays unchecked unless you turn it on, so you never have to clear it just to find a document you intend to re-link. + :::note Each document can only be linked once to the same entity. If you try to link a document that is already attached, Cornerstone will show a message that the document is already linked. ::: From af21a0aa7886ef4af7103ea62be46f0ff1ee236d Mon Sep 17 00:00:00 2001 From: Frank Steiler Date: Tue, 28 Apr 2026 12:40:29 +0200 Subject: [PATCH 2/7] chore: update implementation checklist with E2E assertion and locator lessons (#1379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two patterns added from v2.4.2 release fix loop: - Post-mutation assertions must use Playwright retrying assertions (toContainText) not textContent() + sync expect — waitForResponse resolves before React re-renders - Text regex locators must be updated when UI labels change; prefer data-testid or broad patterns resilient to minor rewording Co-authored-by: Frank Steiler Co-authored-by: Claude Sonnet 4.6 --- .claude/checklists/implementation-checklist.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.claude/checklists/implementation-checklist.md b/.claude/checklists/implementation-checklist.md index 9a998ec2e..d2c49db96 100644 --- a/.claude/checklists/implementation-checklist.md +++ b/.claude/checklists/implementation-checklist.md @@ -75,6 +75,8 @@ This checklist is updated after each epic's lessons-learned sync (see `/epic-clo - [ ] **95% coverage target**: New and modified code must meet the 95% unit test coverage target. The QA agent must run each new test file with `--coverage` and verify 95%+ statement coverage before committing. - [ ] **No mocking of internal modules**: Integration tests should use real implementations where possible. Only mock external services and system boundaries. - [ ] **E2E route coverage**: Every application route must have at least smoke-level E2E test coverage. The E2E test engineer verifies route coverage as part of every E2E task. +- [ ] **E2E post-mutation assertions**: After actions that trigger a data mutation (clicking a button, selecting a picker item), use Playwright's retrying assertions (`toContainText()`, `toHaveText()`, `toBeVisible()`) rather than reading the DOM immediately with `textContent()` + sync `expect()`. `waitForResponse()` resolves at the network level before React re-renders the DOM — a sync DOM read immediately after it will see stale content. +- [ ] **E2E text locators after label changes**: When a production PR renames a UI label, update all E2E test locators that match that text. Regex locators like `/hide linked/i` silently break when the label changes to "Hide already-linked documents" (no contiguous match). Prefer `data-testid` attributes for stability; when using text regex, keep the pattern broad enough to survive minor rewording (e.g. `/hide.*linked/i`). ## i18n — Translations From 38a475fc473c7ce32fe455ee3c4e296229411bd3 Mon Sep 17 00:00:00 2001 From: Frank Steiler Date: Tue, 28 Apr 2026 21:01:00 +0200 Subject: [PATCH 3/7] chore: optimize CLAUDE.md for token reduction and accuracy (#1380) - Reduce file from 528 to 468 lines (~11% smaller) - Merge two i18n sections into one (Internationalization & Translation) - Condense Coverage Enforcement from 3 subsections to 3 bullets - Merge Sandbox Test Constraints into Local Validation Policy - Remove Package Dependency Graph and Build Order subsections (derivable from code) - Trim orchestrator delegation list (references Agent Team table instead) - Remove Prerequisites subsection (obvious from Tech Stack table) - Condense Documentation Site subsection to one line - Condense Acceptance & Validation narrative - Fix env vars table: remove unused CLIENT_DEV_PORT; add 9 missing vars (SESSION_DURATION, SECURE_COOKIES, TRUST_PROXY, OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, PHOTO_MAX_FILE_SIZE_MB, PHOTO_STORAGE_PATH, DIARY_AUTO_EVENTS) Co-authored-by: Frank Steiler Co-authored-by: Claude Sonnet 4.6 --- CLAUDE.md | 113 +++++++++++++----------------------------------------- 1 file changed, 27 insertions(+), 86 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5968802b0..df38d6378 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,19 +72,7 @@ The GitHub Projects board uses 5 statuses: Backlog, Todo, In Progress, Done, Won **Mark stories in-progress before starting work.** When beginning a story, immediately move its GitHub Issue to "In Progress" on the Projects board. -**The orchestrator delegates, never implements.** Must NEVER write production code, tests, or architectural artifacts. Delegate all implementation: - -- **Implementation specs** → `dev-team-lead` agent (produces specs, reviews code, commits) -- **Backend code** → `backend-developer` agent (Haiku, launched by orchestrator with dev-team-lead specs) -- **Frontend code** → `frontend-developer` agent (Haiku, launched by orchestrator with dev-team-lead specs) -- **Non-English translations** → `translator` agent (Sonnet, launched by orchestrator with dev-team-lead Translator Specs) -- **Unit/integration tests** → `qa-integration-tester` agent (launched by orchestrator with dev-team-lead specs) -- **E2E browser tests** → `e2e-test-engineer` agent (launched by orchestrator with dev-team-lead specs) -- **Visual specs, design tokens, brand assets, CSS files** → `ux-designer` agent -- **Schema/API design, ADRs, wiki** → `product-architect` agent -- **Story definitions** → `product-owner` agent -- **Security reviews** → `security-engineer` agent -- **User-facing documentation** (docs site + README) → `docs-writer` agent +**The orchestrator delegates, never implements.** Must NEVER write production code, tests, or architectural artifacts. Route all work per the Agent Team table above. ### Orchestration Skills @@ -100,7 +88,7 @@ The orchestrator uses four skills to drive work. Each skill contains the full op ## Acceptance & Validation -Every epic follows a two-phase validation lifecycle. **Development phase** (`/develop`): PO defines acceptance criteria, QA + E2E + security review each story/bug PR — PRs auto-merge after CI green + all reviewers approved. **Epic validation phase** (`/epic-close`): refinement, E2E coverage confirmation, UAT scenarios fed to e2e-test-engineer, promotion, then docs update. Use `/epic-run` to execute the entire lifecycle in a single session. The only human gate is promotion from `beta` → `main`, where the user reviews a comprehensive summary with change inventory, validation report, and manual validation checklist. If the user provides feedback via `/tmp/notes.md`, fixes are applied autonomously (PO groups items into issues, `/develop` fixes each group) and the promotion PR is re-created — looping until the user approves. Documentation runs after approval to reflect the final state. +Every epic has two phases: **Development** (`/develop`) where QA + E2E + security review each story PR; and **Epic validation** (`/epic-close`) where E2E coverage is confirmed and UAT runs before promotion. The only human gate is `beta` → `main` — the user approves after reviewing the change inventory. Feedback goes to `/tmp/notes.md`; fixes loop autonomously until approved. ### Key Rules @@ -130,7 +118,9 @@ All commits follow [Conventional Commits](https://www.conventionalcommits.org/): - CI auto-fix bot: `npm run lint:fix` + `npm run format` + `npm audit fix` (runs on `beta` push, creates PR if changes needed) - CI Quality Gates: typecheck + test + build (runs on every PR) -To validate your work: **commit and push**. After pushing, **always wait for the required CI gates to pass** before proceeding to the next step. +To validate your work: **commit and push**. After pushing, **always wait for the required CI gates to pass** before proceeding to the next step. When running tests locally: only run specific files (`npx jest path/to/specific.test.ts --maxWorkers=1`), never the full suite — the sandbox is resource-constrained and CI owns full validation. + +The only exception is the QA agent running a specific test file it just wrote (e.g., `npx jest path/to/new.test.ts`) to verify correctness before committing — but never `npm test` (the full suite). #### CI Gate Polling (canonical pattern) @@ -160,16 +150,6 @@ echo "Waiting for Quality Gates + E2E Gates..."; SECONDS=0; while true; do if [ Replace `` with the PR number. The polling loop handles the "checks not yet reported" edge case — an empty bucket means we retry after 30s. Timeouts prevent agents from polling indefinitely if CI hangs. -The only exception is the QA agent running a specific test file it just wrote (e.g., `npx jest path/to/new.test.ts`) to verify correctness before committing — but never `npm test` (the full suite). - -### Sandbox Test Execution Constraints - -The sandbox environment is resource-constrained. When running tests locally: - -- **Never run the full test suite** (`npm test`) — only run tightly scoped, specific test files (e.g., `npx jest path/to/specific.test.ts`) -- **Always use a single worker** — pass `--maxWorkers=1` to Jest for any local test execution -- Rely on CI for full suite validation - ### GitHub Rate-Limit Retry Policy When `gh` or `git push` commands fail with a GitHub rate-limit error (primary API limit, secondary abuse limit, or `HTTP 403`/`HTTP 429` with a rate-limit message), retry with **exponential backoff** instead of aborting: @@ -298,21 +278,6 @@ cornerstone/ src/ # Markdown content (guides, getting-started, development) ``` -### Package Dependency Graph - -``` -@cornerstone/shared <-- @cornerstone/server - <-- @cornerstone/client -@cornerstone/e2e (standalone — runs against built app via testcontainers) -@cornerstone/docs (standalone — Docusaurus, deployed to GitHub Pages) -``` - -### Build Order - -`shared` (tsc) -> `client` (webpack build) -> `server` (tsc) - -The `docs` workspace is NOT part of the application build (`npm run build`). Build it separately with `npm run docs:build`. - ## Dependency Policy - **Always use the latest stable (LTS if applicable) version** of a package when adding or upgrading dependencies @@ -398,38 +363,14 @@ Before creating a new UI component, check if an existing shared component can be ### Coverage Enforcement -Coverage is tracked and enforced through three complementary mechanisms: - -**1. CI Coverage Reports (automated)** - -Every PR triggers coverage collection across 6 Jest shards. The `Coverage Report` CI job merges shard results and uploads a `coverage-report` artifact containing `coverage-summary.json` (per-file and total percentages) and `coverage-final.json` (raw Istanbul data). Coverage reports are retained for 30 days. - -- Test shards run with `--coverage --coverageReporters=json` -- The merge script (`scripts/merge-coverage.mjs`) combines shard data and prints a text summary in CI logs -- To inspect coverage for a PR: download the `coverage-report` artifact from the CI run - -**2. Test File Parity (enforced by dev-team-lead)** - -During `[MODE: review]`, the dev-team-lead verifies that **every new or modified production file has a corresponding test file**. Production files added without test files result in `VERDICT: CHANGES_REQUIRED` with a fix spec routed to `qa-integration-tester`. This prevents untested code from entering the codebase. - -**3. Local Coverage Verification (enforced by qa-integration-tester)** +Coverage is enforced through three mechanisms: -The QA agent runs coverage on each new test file before committing: - -```bash -npx jest path/to/file.test.ts --coverage --coverageReporters=text --maxWorkers=1 -``` - -This verifies 95%+ coverage on the corresponding source file before the code leaves the agent. +- **CI**: 6 Jest shards upload a `coverage-report` artifact (retained 30 days) — inspect via the CI run for per-file percentages. +- **Test file parity**: dev-team-lead `[MODE: review]` rejects production files without a corresponding test file (`VERDICT: CHANGES_REQUIRED` → routed to `qa-integration-tester`). +- **Local**: QA runs `npx jest path/to/file.test.ts --coverage --coverageReporters=text --maxWorkers=1` before committing; 95%+ required. ## Development Workflow -### Prerequisites - -- Node.js >= 24 -- npm >= 11 -- Docker (for container builds) - ### Getting Started ```bash @@ -458,7 +399,7 @@ npm run dev # Start server (port 3000) + client dev server (po ### Documentation Site -Docusaurus 3.x site deployed to GitHub Pages at `https://steilerDev.github.io/cornerstone/`. Deployed via the `docs-deploy` job in `.github/workflows/release.yml` on stable releases (screenshots are auto-captured from the released Docker image). Content: `docs/src/` (user guides, end users) · `wiki/` (architecture/ADRs, agents) · `README.md` (GitHub visitors) · `CLAUDE.md` (AI agents). +Docusaurus site in `docs/` deployed to GitHub Pages on stable releases via `.github/workflows/release.yml`. ### Database Migrations @@ -473,8 +414,16 @@ Hand-written SQL files in `server/src/db/migrations/` with a numeric prefix (e.g | `DATABASE_URL` | `/app/data/cornerstone.db` | SQLite database path | | `LOG_LEVEL` | `info` | Log level (trace/debug/info/warn/error/fatal) | | `NODE_ENV` | `production` | Environment | -| `CLIENT_DEV_PORT` | `5173` | Webpack dev server port (development only) | +| `SESSION_DURATION` | `604800` | Session duration in seconds (default: 7 days) | +| `SECURE_COOKIES` | `true` | Enable HTTPS-only cookie flag | +| `TRUST_PROXY` | `false` | Trust X-Forwarded-* headers from a reverse proxy | +| `OIDC_ISSUER` | (none) | OpenID Connect issuer URL | +| `OIDC_CLIENT_ID` | (none) | OIDC application client ID | +| `OIDC_CLIENT_SECRET` | (none) | OIDC application client secret | | `EXTERNAL_URL` | (none) | Public-facing base URL (e.g., `https://myhouse.example.com`) for reverse-proxy setups | +| `PHOTO_MAX_FILE_SIZE_MB` | `20` | Maximum photo upload size in MB | +| `PHOTO_STORAGE_PATH` | `{DB_DIR}/photos` | Directory for photo storage | +| `DIARY_AUTO_EVENTS` | `true` | Enable automatic diary event creation | | `PAPERLESS_URL` | (none) | Paperless-ngx instance base URL | | `PAPERLESS_API_TOKEN` | (none) | Paperless-ngx API authentication token | | `PAPERLESS_EXTERNAL_URL` | (none) | Browser-facing URL for Paperless-ngx links (falls back to `PAPERLESS_URL` if unset) | @@ -506,22 +455,14 @@ When tests fail during development, a structured diagnostic protocol determines - **Protocol owner**: The `dev-team-lead` runs the diagnostic decision tree during `[MODE: review]` when test failures are present in the review input. See the dev-team-lead agent definition for the full classification table and escalation rules. - **Test agents report, not diagnose**: `qa-integration-tester` and `e2e-test-engineer` submit structured failure reports but do not determine whether the fault lies in code or tests — that judgment belongs to the dev-team-lead. -### Internationalization (i18n) +### Internationalization & Translation The application supports multiple locales (English and German) via `i18next` and `react-i18next`. All agents must follow these conventions: -- **Frontend**: All user-facing strings must use `t()` from react-i18next — never hardcode text in JSX. Translation files live in `client/src/i18n/{lang}/{namespace}.json`. Dev agents write English (`en`) keys only. -- **Translator owns non-English locales**: The `translator` agent handles all non-English translations and enforces glossary compliance. Dev agents do not write German or other non-English translations. -- **Backend**: API error responses must use `ErrorCode` enum values (machine-readable codes), not human-readable messages. The frontend translates error codes into locale-specific messages via `translateApiError()`. The `CURRENCY` env var (default: `EUR`) is exposed via `GET /api/config`. -- **Formatting**: Use `formatDate`, `formatCurrency`, `formatPercent` from `client/src/lib/formatters.ts` — these read the locale from i18next automatically. Never use raw `toLocaleDateString()` or `Intl.NumberFormat` directly. -- **Testing**: QA must verify translation keys exist in both locales. E2E tests must verify locale detection and switching behavior. -- **Specs**: Dev-team-lead specs must include i18n requirements — translation namespace, English keys to add, and a Translator Spec section for the translator agent. - -### Translation & Glossary Convention - -- **Glossary**: `client/src/i18n/glossary.json` — single source of truth for domain term translations (multi-locale) -- **Dev agents write English only**: frontend-developer adds `en` keys. Never writes non-English translations. -- **Translator owns all non-English locales**: translates new keys + enforces glossary compliance -- **Glossary scope**: Domain-specific terms only (Work Item, Invoice, etc.), not common UI words -- **Glossary updates**: Translator proposes additions for new domain terms; product-owner approves terminology -- **Adding a locale**: Add locale code to `glossary.json` `_meta.locales`, add translations for all terms, create `client/src/i18n/{locale}/` directory with namespace files, register in `client/src/i18n/index.ts` +- **Frontend**: All user-facing strings must use `t()` — never hardcode text in JSX. Translation files: `client/src/i18n/{lang}/{namespace}.json`. Dev agents write English (`en`) keys only; never write non-English translations. +- **Translator owns non-English locales**: `translator` agent translates new keys and enforces glossary compliance. +- **Glossary**: `client/src/i18n/glossary.json` — domain-specific terms only (Work Item, Invoice, etc.). Translator proposes new terms; product-owner approves. To add a locale: update `glossary.json` `_meta.locales`, create `client/src/i18n/{locale}/` namespace files, register in `client/src/i18n/index.ts`. +- **Backend**: API error responses use `ErrorCode` enum values; frontend translates via `translateApiError()`. `CURRENCY` env var (default: `EUR`) exposed via `GET /api/config`. +- **Formatting**: Use `formatDate`, `formatCurrency`, `formatPercent` from `client/src/lib/formatters.ts` — never raw `toLocaleDateString()` or `Intl.NumberFormat`. +- **Testing**: QA verifies keys exist in both locales. E2E verifies locale detection and switching. +- **Specs**: Dev-team-lead specs must include translation namespace, English keys to add, and a Translator Spec section. From 80cc0466d217c9628a40cbf8c8705e8176cd2567 Mon Sep 17 00:00:00 2001 From: "cornerstone-bot[bot]" Date: Tue, 28 Apr 2026 19:03:05 +0000 Subject: [PATCH 4/7] style: auto-fix lint and format [skip ci] --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index df38d6378..37b8aceb2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -416,7 +416,7 @@ Hand-written SQL files in `server/src/db/migrations/` with a numeric prefix (e.g | `NODE_ENV` | `production` | Environment | | `SESSION_DURATION` | `604800` | Session duration in seconds (default: 7 days) | | `SECURE_COOKIES` | `true` | Enable HTTPS-only cookie flag | -| `TRUST_PROXY` | `false` | Trust X-Forwarded-* headers from a reverse proxy | +| `TRUST_PROXY` | `false` | Trust X-Forwarded-\* headers from a reverse proxy | | `OIDC_ISSUER` | (none) | OpenID Connect issuer URL | | `OIDC_CLIENT_ID` | (none) | OIDC application client ID | | `OIDC_CLIENT_SECRET` | (none) | OIDC application client secret | From 72838a60a15edf08f477b7f90ed820e4377e0459 Mon Sep 17 00:00:00 2001 From: Frank Steiler Date: Wed, 29 Apr 2026 08:30:38 +0200 Subject: [PATCH 5/7] docs: add release date to RELEASE_SUMMARY (#1382) Co-authored-by: Frank Steiler Co-authored-by: Claude Sonnet 4.6 --- RELEASE_SUMMARY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_SUMMARY.md b/RELEASE_SUMMARY.md index e08ae7b15..8991b4679 100644 --- a/RELEASE_SUMMARY.md +++ b/RELEASE_SUMMARY.md @@ -18,3 +18,7 @@ docker pull steilerdev/cornerstone:latest ``` Restart your container -- no database migration is required. + +--- + +*Released: 2026-04-28* From 76fef5f981306ed902f565e1f3c939cf2248ce39 Mon Sep 17 00:00:00 2001 From: "cornerstone-bot[bot]" Date: Wed, 29 Apr 2026 06:32:35 +0000 Subject: [PATCH 6/7] style: auto-fix lint and format [skip ci] --- RELEASE_SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_SUMMARY.md b/RELEASE_SUMMARY.md index 8991b4679..b60613157 100644 --- a/RELEASE_SUMMARY.md +++ b/RELEASE_SUMMARY.md @@ -21,4 +21,4 @@ Restart your container -- no database migration is required. --- -*Released: 2026-04-28* +_Released: 2026-04-28_ From c8422c2282166d08fd5078a5b6b470b1e232e425 Mon Sep 17 00:00:00 2001 From: Frank Steiler Date: Wed, 29 Apr 2026 08:51:20 +0200 Subject: [PATCH 7/7] docs: add related pages section to budget overview guide (#1383) Co-authored-by: Frank Steiler Co-authored-by: Claude Sonnet 4.6 --- docs/src/guides/budget/budget-overview.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/src/guides/budget/budget-overview.md b/docs/src/guides/budget/budget-overview.md index 1cbb011ba..9ed5b1026 100644 --- a/docs/src/guides/budget/budget-overview.md +++ b/docs/src/guides/budget/budget-overview.md @@ -117,3 +117,10 @@ If the exported PDF does not match what you see on screen, make sure your browse ::: ![Budget overview dashboard](/img/screenshots/budget-overview-light.png) + +## Related Pages + +- [Work Items](../work-items/overview) — track progress and link budget lines to construction tasks +- [Household Items](../household-items/overview) — manage furniture and appliance purchases with their own budget lines +- [Financing Sources](financing-sources) — detailed view of each financing source and its allocated lines +- [Subsidies](subsidies) — manage subsidy applications and their impact on your budget