Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
6ff5759
Unify budget-line creation form on invoice pages (#1402)
steilerDev May 10, 2026
82265ae
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 10, 2026
3fb948a
feat(invoice): add deposit support (schema, CRUD API, cascade) (#1403…
steilerDev May 10, 2026
a46f7a9
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 10, 2026
e1c47f2
chore(deps): bump the github-actions group with 2 updates (#1397)
dependabot[bot] May 11, 2026
c145a9a
chore(deps-dev): bump the dev-dependencies group with 6 updates (#1398)
dependabot[bot] May 11, 2026
073c94d
chore(deps): bump the prod-dependencies group with 5 updates (#1399)
dependabot[bot] May 11, 2026
f689f1a
chore(deps): bump fast-uri from 3.1.0 to 3.1.2 (#1400)
dependabot[bot] May 11, 2026
472cb8f
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 11, 2026
f61ed78
feat(invoice,budget): invoice deposits UI + deposit-aware budget roll…
steilerDev May 11, 2026
86712c1
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 11, 2026
7ce0ebd
fix(invoice): make status breakdown summary deposit-aware (#1412)
steilerDev May 12, 2026
dd271d1
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 12, 2026
0b0b36f
chore(invoice,budget): dedupe split helper, extract OverflowMenu, sur…
steilerDev May 12, 2026
d5eb3cb
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 12, 2026
f8e5db9
chore(i18n): bump glossary lastUpdated to 2026-05-12 (#1416)
steilerDev May 12, 2026
be35805
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 12, 2026
3de3c99
chore(deps): drop dead transitive lockfile entries (audit fix) (#1417)
steilerDev May 12, 2026
df4c770
chore(deps): bump github/codeql-action in the github-actions group (#…
dependabot[bot] May 15, 2026
3acb2c4
chore(deps-dev): bump @protobufjs/utf8 from 1.1.0 to 1.1.1 (#1418)
dependabot[bot] May 15, 2026
573d39c
chore(deps): bump the prod-dependencies group with 6 updates (#1410)
dependabot[bot] May 15, 2026
c24cf2b
chore(deps-dev): bump the dev-dependencies group with 6 updates (#1409)
dependabot[bot] May 15, 2026
3a92185
chore: remove .claude/settings.json (#1420)
steilerDev May 15, 2026
3ef772c
fix(budget-invoice-ux): implement overdue indicator and budget line m…
steilerDev May 16, 2026
61e218c
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 16, 2026
50aba2b
fix(diary): auto-draft + immediate photo upload (#1426)
steilerDev May 17, 2026
a033dac
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
7581c05
chore: add Claude Code worktree settings (#1437)
steilerDev May 17, 2026
b484559
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
0bcb95c
fix(diary): immediate draft on type-card click; photo refresh; hide-d…
steilerDev May 17, 2026
a2aa175
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
460a2b0
fix(budget): include quotations in actualCost and populate vendor fie…
steilerDev May 17, 2026
1d64423
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
6668f7c
test: remove stale skipped tests and rewrite unskippable ones (#1443)
steilerDev May 17, 2026
69720da
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
a5cda99
fix(overflow-menu): elevate portal z-index to prevent click intercept…
steilerDev May 17, 2026
69d4187
fix(tests): stabilize three intermittent test flakes (#1430, #1431, #…
steilerDev May 17, 2026
32f0a86
fix(diary): replace hide-drafts checkbox with chip in DiaryFilterBar …
steilerDev May 17, 2026
4b3f807
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
100b77e
test(e2e): re-enable 8 stale skips — i18n + budget-print (#1447)
steilerDev May 17, 2026
8a0ad5a
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
73c61f8
test(e2e): re-skip 2 print tests blocked on production bugs #1450 / #…
steilerDev May 17, 2026
a2cb4c6
feat(budget): quoted amount as single muted value + vendor on second …
steilerDev May 17, 2026
368a502
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
2cdb0d5
chore: trivial docs bump to refire promotion CI (#1479)
steilerDev May 17, 2026
5766853
fix(overflow-menu): tolerate tiny scroll-into-view nudges before clos…
steilerDev May 17, 2026
1e63e24
feat(photos): add annotator foundation with Select/Rectangle/Highligh…
steilerDev May 17, 2026
79c22f7
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 17, 2026
f11104f
fix(overflow-menu): drop conflicting menuTop/menuBottom CSS classes i…
steilerDev May 18, 2026
3df3ab8
test(e2e): zero-pad month in invoice-deposits-ux dueDate generator (#…
steilerDev May 18, 2026
607b394
feat(photo-annotator): add geometric tools (arrow, line, ellipse) (#1…
steilerDev May 18, 2026
8f13c61
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
90200f1
feat(photo-annotator): add text and callout tools (#1476) (#1489)
steilerDev May 18, 2026
8ba074e
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
cbe96e8
feat(photo-annotator): add measurement and freehand tools (#1477) (#1…
steilerDev May 18, 2026
1aa4d69
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
2823ad2
feat(photo-annotator): polish (E2E, a11y, security, i18n) (#1478) (#1…
steilerDev May 18, 2026
3abd329
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
b1b712b
fix(photo-annotator): align coordinates with rendered image and EXIF …
steilerDev May 18, 2026
3c31203
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
71ba84e
fix(photo-annotator): add return statements in commitInlineInput to p…
steilerDev May 18, 2026
dc6f341
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
677dce2
fix(photos): polish edit icon, annotator arrow icon, add edit button …
steilerDev May 18, 2026
e690b58
feat(photo-annotator): scale stroke widths and font sizes with image …
steilerDev May 18, 2026
5ae694e
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
0b43de4
feat(photos): block annotation on signed diary entries (#1496)
steilerDev May 18, 2026
1feb484
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
97f7675
fix(photo-annotator): keep pointer events within the image bounds (#1…
steilerDev May 18, 2026
f114286
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
5dc74dc
fix(photo-annotator): scale stroke widths and font sizes with image r…
steilerDev May 18, 2026
9582913
fix(photo-annotator): selection visibility, reduced default sizes, sc…
steilerDev May 18, 2026
26720af
feat(photos): switch annotation upload from PNG to WebP at q=0.92 (#1…
steilerDev May 18, 2026
9a7f9fd
fix(photo-annotator): real fixes for arrow tip, icon, selection, edit…
steilerDev May 18, 2026
a610158
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
204e938
fix(photo-annotator): position SVG after layout settles for portrait …
steilerDev May 18, 2026
a710e30
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
af0cebb
fix(photo-annotator): use Unicode glyphs for select/undo/redo icons (…
steilerDev May 18, 2026
4b0b77d
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
0d29a20
fix(photo-annotator): clearer selection halo and click-to-edit on tex…
steilerDev May 18, 2026
1c7c498
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
a72ddb8
fix(photo-annotator): callouts auto-flow text, inset from border, shr…
steilerDev May 18, 2026
802828d
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
0d49d86
fix(photo-annotator): inline text input renders as the committed text…
steilerDev May 18, 2026
6292f7c
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
4c4df55
fix(photo-annotator): use SVG getScreenCTM for clicks, drop stale pos…
steilerDev May 18, 2026
53096b0
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
e6018cc
fix(photo-annotator): swallow pointer events while inline input is op…
steilerDev May 18, 2026
1c5df2c
fix(photos): edit button opens viewer directly in annotator mode (#1509)
steilerDev May 18, 2026
84b1fcf
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 18, 2026
59f3e1c
fix(photo-annotator): scale callout text padding with font size (#1513)
steilerDev May 19, 2026
e8a5984
fix(photo-annotator): color/width/font pickers update selected shape,…
steilerDev May 19, 2026
cd52157
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
6b618ab
fix(photo-annotator): inline input matches rendered shape position an…
steilerDev May 19, 2026
4c121b3
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
9fbeabc
feat(photos): photo metadata sidepanel with upload date, description,…
steilerDev May 19, 2026
4150dec
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
138b835
fix(photos): cache-bust thumbnail URL when annotation changes (#1517)
steilerDev May 19, 2026
47a4299
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
220adb4
fix(photos): pin photo metadata sidepanel, align viewer close button …
steilerDev May 19, 2026
b0220ed
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
39735d6
fix(photo-annotator): sync undoStack with UPDATE_SHAPE so live edits …
steilerDev May 19, 2026
1f33890
fix(photo-annotator): shift size scale down — drop largest, add small…
steilerDev May 19, 2026
b7b32d6
feat(photo-annotator): edit from annotated image with Reset button (#…
steilerDev May 19, 2026
579e0cf
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
4b0b128
fix(photo-annotator): widen handle hit-area, raise click-vs-drag thre…
steilerDev May 19, 2026
f6835b5
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
e5fd08c
fix(photo-annotator): colour picker updates non-text shapes' stroke t…
steilerDev May 19, 2026
375b1d3
feat(photos): lightbox delete button for non-locked entries (#1524)
steilerDev May 19, 2026
d8d702c
fix(photo-viewer): hide metadata sidepanel on mobile (#1525)
steilerDev May 19, 2026
f4b74a5
refactor(photo-annotator): port to react-konva (#1526)
steilerDev May 19, 2026
db11037
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
ae44b5f
fix(photo-annotator): restore text/callout/measurement inline input (…
steilerDev May 19, 2026
1d61cf0
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
032e65f
fix(photo-annotator): drop callout, unify line/arrow/measurement (#1529)
steilerDev May 19, 2026
9fd434a
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 19, 2026
1f615d0
fix(photo-annotator): repair selection model (deselect, tool-switch, …
steilerDev May 19, 2026
d64ca14
fix(photo-annotator): inline input now anchors to canvasArea, not vie…
steilerDev May 20, 2026
3f32c84
fix(photo-annotator): line-family shapes use endpoint handles; anchor…
steilerDev May 20, 2026
5496378
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 20, 2026
8ce7bf3
fix(photo-annotator): repair line-family drag (endpoints, body, undoS…
steilerDev May 21, 2026
23c4711
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 21, 2026
6a7b966
fix(photo-annotator): text shape sizing via dropdown only; chrome pol…
steilerDev May 21, 2026
3533d9f
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 21, 2026
802041e
chore: merge main back into beta
May 21, 2026
baff9df
Merge pull request #1539 from steilerDev/chore/sync-main-into-beta
steilerDev May 21, 2026
13afaa6
docs: update documentation for release (#1540)
steilerDev May 21, 2026
70b4cea
style: auto-fix lint and format [skip ci]
steilerdev-cornerstone-bot[bot] May 21, 2026
af2a1d5
docs(claude): document workflow_dispatch workaround for auto-fix [ski…
steilerDev May 21, 2026
c78bfc8
chore: retrigger promotion CI (#1543)
steilerDev May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ SECURE_COOKIES=true

# ─── Diary ─────────────────────────────────────────────
# DIARY_AUTO_EVENTS=true # Set to false to disable automatic diary event logging
# PHOTO_STORAGE_PATH=/app/data/photos # Directory for diary photo attachments
# DIARY_DRAFT_RETENTION_DAYS=30 # Days a draft entry sits untouched before automatic cleanup (0 = disabled)
# PHOTO_STORAGE_PATH=/app/data/photos # Directory for diary photo attachments (originals + annotated copies)
# PHOTO_MAX_FILE_SIZE_MB=20 # Maximum photo upload size in MB

# ─── OIDC ──────────────────────────────────────────────
Expand Down
14 changes: 14 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,20 @@ echo "Waiting for Quality Gates + E2E Gates..."; SECONDS=0; while true; do if [

Replace `<PR>` 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.

#### Auto-fix Bot `[skip ci]` Workaround

The auto-fix bot (`.github/workflows/auto-fix.yml`) pushes cosmetic lint/format commits to `beta` with `[skip ci]` in the commit message. When that push lands on top of a recently-merged squash on `beta`, it advances the HEAD of any open `beta -> main` PR (e.g., the promotion PR) to a SHA that has **no CI runs** — leaving the PR `MERGEABLE` but `BLOCKED` on required checks that will never report.

Symptoms: `gh pr checks <PR>` returns nothing for `Quality Gates` / `E2E Gates`; `gh run list --commit <sha>` is empty; PR is `BLOCKED` even though the prior HEAD's checks passed.

**Fix:** the `Quality Gates` workflow exposes `workflow_dispatch:` for exactly this case. Re-trigger CI manually on the affected branch:

```bash
gh workflow run ci.yml --repo steilerDev/cornerstone --ref beta
```

GitHub reports the resulting `Quality Gates` / `E2E Gates` check buckets against the branch HEAD SHA, which is the same SHA the PR uses — so the required checks are satisfied automatically and the PR unblocks. Use the same polling pattern above (main variant for promotion PRs) to wait for completion. Do not push an empty/no-op commit just to retrigger CI — the dispatch is cleaner and avoids cluttering the commit log.

### 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:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ A self-hosted home building project management tool for homeowners. Track work i

- **Work Items** -- Manage construction tasks with statuses, dates, area assignments, notes, subtasks, dependencies, and keyboard shortcuts -- every work item shows its full area ancestor path (e.g. `House / Ground Floor / Kitchen`) as a breadcrumb across lists, detail pages, pickers, and every place it is referenced
- **Areas & Trades** -- Organize your project with hierarchical areas (rooms, floors, zones) and trade specialties (Electrical, Plumbing, etc.) for vendors; a dedicated "No Area" filter surfaces items that have not been classified yet
- **Budget Management** -- Budget categories, financing sources with inline expansion and mass-move of attached lines, multi-budget-line invoice linking with itemized amounts, subsidies with caps, quotation tracking, and an area-grouped overview dashboard with source attribution badges on every line, a per-source filter (URL-persisted, server-side) that updates totals and subsidy math, clickable summary tiles, and print-friendly export
- **Budget Management** -- Budget categories, financing sources with inline expansion and mass-move of attached lines, multi-budget-line invoice linking with itemized amounts, staged-payment deposits for invoices paid in instalments (deposit-aware paid/claimed rollups), subsidies with caps, quotation tracking, and an area-grouped overview dashboard with source attribution badges on every line, a per-source filter (URL-persisted, server-side) that updates totals and subsidy math, clickable summary tiles, and print-friendly export
- **Timeline & Gantt Chart** -- Interactive Gantt chart with dependency arrows, critical path, zoom controls, milestones, and CPM-based auto-scheduling
- **Calendar View** -- Monthly and weekly calendar grids with work items and milestones
- **Household Items** -- Track furniture, appliances, and fixtures with categories, area assignment, delivery scheduling, budget integration, and work item linking
- **Project Dashboard** -- At-a-glance project health with budget, timeline, invoice, and subsidy cards, mini Gantt preview, and customizable layout
- **Construction Diary** -- Daily logs, site visits, delivery records, automatic system events, photo attachments, and digital signature capture
- **Construction Diary** -- Daily logs, site visits, delivery records, automatic system events, photo attachments with in-browser annotation (rectangles, arrows, text, measurements, freehand), auto-saved drafts, and digital signature capture
- **Document Integration** -- Browse and link documents from Paperless-ngx to work items, household items, and invoices
- **Advanced List Views** -- Filter, sort, paginate, and customize columns across all list pages with the shared DataTable system
- **Backup & Restore** -- Manual and scheduled backups with configurable retention, restore from the settings UI
Expand Down
80 changes: 54 additions & 26 deletions RELEASE_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,70 @@
# v2.5.0 Release Summary
# v2.6.0 Release Summary

A focused release that adds **Backup & Restore**, tightens budget VAT handling, and slims down the Budget Overview page. Migration 0031 backfills `includes_vat` on existing budget lines, runs automatically on first start, and requires no manual intervention.
A feature-packed release that brings **in-browser photo annotation**, **staged-payment deposits** for invoices, and a much smoother **diary draft flow**. Migration 0032 (invoice deposits) runs automatically on first start -- no manual steps required.

## What's New

- **Backup & Restore** -- Cornerstone now ships with a built-in backup feature that snapshots your entire app data directory (SQLite database + diary photos) into a single `tar.gz` archive. Trigger backups manually from the admin UI, run them on a cron schedule, set a retention limit, and restore from any archive in two clicks. Mount a separate volume to `/backups` (or wherever you point `BACKUP_DIR`) and you're set. See the [Backups guide](https://steilerDev.github.io/cornerstone/guides/backup) for setup, scheduling, and restore steps. (#1386)
### Photo Annotation

## Improvements
Mark up diary photos directly in the browser. Open any diary photo in the viewer, click **Annotate**, and you get a full drawing canvas with nine tools:

- **Consistent VAT handling across budget lines** (#1385) -- Direct pricing mode now applies the same VAT multiplier as unit pricing (quantity × unit price), so the **Price includes VAT** checkbox behaves identically regardless of which pricing mode you use. Planned amounts are now always stored as gross internally, which means the Budget Overview, Available Funds row, and printed reports compare every line on a like-for-like basis. The `includes_vat` flag is now `NOT NULL` at the database level (defaults to `true`); migration 0031 backfills any pre-existing `NULL` values.
- **Select**, **Rectangle**, **Highlight**, **Arrow**, **Line**, **Ellipse**, **Text**, **Measurement** (dimension line with end ticks and a movable label), and **Freehand**
- Six colours, four stroke widths, five font sizes -- pick before you draw, or change them on a selected shape to update it live
- Drag to move, endpoint handles to reshape, undo/redo for everything including live edits
- Annotated copies are saved as separate WebP files (quality 0.92); the original is preserved, and you can switch between **View original** and **View annotated** at any time
- A **Reset to original** button discards your current annotations and starts over
- Photo grids and viewers expose a quick-action **Edit** button that opens the viewer directly in annotation mode

## Bug Fixes
Signed diary entries cannot be annotated -- finish your markup before collecting signatures.

- **Budget Overview is now the breakdown** (#1389) -- The Budget Health hero card has been removed from the top of the page. The overview now goes straight from the title bar into the Cost Breakdown Table. The Min / Avg / Max perspective toggle, source-filter, and Available Funds row all live inside the table and remain unchanged. The page is faster, prints cleaner, and removes a layer of summary metrics that mostly duplicated what the breakdown already shows.
- **Source name now prints on the Budget Overview** (#1390) -- Print viewports (around 600-720 px) used to trigger the mobile breakpoint, which collapsed the source attribution badge to just a colored dot -- great on a phone, useless on a printout. Print mode now forces the full source name visible with a border-based color treatment, so the printed report shows the actual source attached to each budget line.
- **Broken docs links on the Budget Overview page** (#1384) -- The "Related Pages" links to Work Items and Household Items pointed to non-existent `/overview` sub-paths and now point to the correct guide indices.
See the [Photo Annotation guide](https://steilerDev.github.io/cornerstone/guides/diary/photo-annotation) for the full walkthrough.

## What to Update
### Invoice Deposits (Staged Payments)

```bash
docker pull steilerdev/cornerstone:latest
```
Track invoices that are paid in stages -- a deposit on signing, a milestone payment, and a final balance.

Restart your container. Migration 0031 runs automatically on first start.
- Add any number of deposits to an invoice from the **Deposits** section on the invoice detail page
- Each deposit has its own amount, due date, status (Pending / Paid / Claimed), paid/claimed dates, and optional description
- The form refuses to save if deposits would exceed the invoice total
- Quick actions to **mark paid** and **mark claimed** -- with revert support to fix mistakes
- Budget rollups across every linked work item and household item are now **deposit-aware**: `actualCostPaid` and `actualCostClaimed` reflect deposit-level status, so paid and claimed amounts show real cash flow rather than waiting for the full invoice to settle
- Deposits cascade-delete with the parent invoice

If you want to enable the new backup feature, mount a backup volume:
See the [Invoice Deposits guide](https://steilerDev.github.io/cornerstone/guides/budget/invoice-deposits).

```bash
docker run -d \
--name cornerstone \
-p 3000:3000 \
-v cornerstone-data:/app/data \
-v cornerstone-backups:/backups \
steilerdev/cornerstone:latest
```
### Diary Drafts Overhaul

Creating a diary entry no longer needs a separate "create" step.

- Click a type card (Daily Log, Site Visit, Delivery, Issue, General Note) and Cornerstone immediately creates a **draft entry** and opens the edit page -- you start typing right away
- Photos uploaded to a draft persist immediately, so you don't have to remember to "save" before attaching them
- Auto-save runs continuously with a live status indicator (`Saving...`, `Saved`, or "save failed -- will retry")
- Drafts are tagged with a **Draft** badge and **hidden from the diary list by default**; a dedicated **Drafts** filter chip toggles their visibility
- Click **Save** to promote the draft to a full entry, or **Discard Draft** to delete it (and its photos) permanently
- Abandoned drafts are cleaned up automatically after `DIARY_DRAFT_RETENTION_DAYS` days (default: 30; set to `0` to disable)

### Photo Viewer Improvements

Optionally set `BACKUP_CADENCE` (e.g., `0 2 * * *` for daily at 2 AM) and `BACKUP_RETENTION` (e.g., `7` to keep a week's worth of archives).
- New **photo metadata sidepanel** with upload date, description, and area assignment
- **Edit** quick-action button on the photo grid that opens the viewer directly in annotation mode
- **Delete photo** action from the lightbox for entries that aren't signed
- Thumbnail cache busting -- annotated thumbnails update immediately
- Mobile-friendly: the metadata sidepanel collapses on small screens

---
## Configuration

New environment variable:

| Variable | Default | Description |
| ---------------------------- | ------- | ----------------------------------------------------------------------------- |
| `DIARY_DRAFT_RETENTION_DAYS` | `30` | Days an untouched draft sits before automatic cleanup. Set to `0` to disable. |

No other configuration changes are required. Migration 0032 adds the `invoice_deposits` table and runs automatically on container start.

## What to Update

```bash
docker pull steilerdev/cornerstone:latest
```

_Released: 2026-05-03_
Restart your container. Schema migrations run on first boot.
Original file line number Diff line number Diff line change
Expand Up @@ -356,28 +356,13 @@ describe('PhotoAnnotator', () => {
}
});

it('shows Reset button when photo.annotatedAt is set', async () => {
const annotatedAt = '2026-05-17T10:00:00.000Z';
await renderAnnotator({ annotatedAt });
// Reset button — text from t('reset') = "Reset to original"
expect(screen.getByRole('button', { name: /Reset to original/i })).toBeInTheDocument();
});

it('clicking Reset button opens confirmation modal', async () => {
it('does NOT show in-annotator Reset button on an annotated photo', async () => {
// Reset button was removed; the PhotoViewer "Clear annotations" entry-point covers this.
await renderAnnotator({ annotatedAt: '2026-05-17T10:00:00.000Z' });

const resetBtn = screen.getByRole('button', { name: /Reset to original/i });
fireEvent.click(resetBtn);

// Modal renders with a dialog role (from the Modal stub)
await waitFor(() => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
});
expect(screen.queryByTestId('annotator-reset')).not.toBeInTheDocument();
});

it('does NOT show Reset button when photo has no annotations (annotatedAt is null)', async () => {
// Reset button is conditional on photo.annotatedAt — it only appears for previously-annotated
// photos. When annotatedAt is null the button must be absent (E2E compatibility: round-7 behavior).
it('does NOT show Reset button when photo has no annotations', async () => {
await renderAnnotator({ annotatedAt: null });
expect(screen.queryByTestId('annotator-reset')).not.toBeInTheDocument();
});
Expand Down
Loading
Loading