# v0.23 private text-note lifecycle

Local invented-data checkpoint. All 45 parity capabilities and all 14 original findings retain their separate status. This stage improves private text notes only. No live SHS, deployment, phone or full offline approval is implied.

## Choose the right action

| Situation | Action and effect |
|---|---|
| Text is only in the editor | **Discard this unfinished editor** removes that editor after confirmation. Retained notes, proposals and a separate unfinished correction reason remain. Keep instead waits for a confirmed encrypted device write. |
| Note was genuinely never sent | **Cancel retained note** removes only its device text and records an opaque closed identity. No network request is made. |
| Send was attempted or its reply is missing | Cancellation contacts the server with the same identity. It confirms either a durable cancellation that refuses delayed sends, or the earlier saved log. Failure leaves text and uncertainty visible; **Check cancellation** resolves cancellation only. |
| Earlier text already became a saved log | Review that result. **Remove confirmed device copy** removes only the local copy; the saved log and history remain. It is a separate choice from cancellation. |
| A proposed correction remains beside a saved earlier revision | **Inspect exact saved log** shows the current saved text, work date and version. **Apply proposed text to saved log** uses ordinary edit permission and history. Owners supply a correction reason. No second log is created. |
| Applying failed or its reply was lost | Both texts and the exact edit identity remain. **Check correction outcome** retries that edit; **Return correction to review** first durably blocks a delayed earlier attempt or reveals its saved result. Inspect again before a fresh attempt. |
| Proposal is no longer wanted | **Discard proposed correction** resolves an attempted edit before removing proposed text and its reason. If it already applied, the saved outcome is shown. No saved log/history is undone. |

Escape and **Keep reviewing** dismiss confirmations without changing notes; focus returns to the initiating control. The simulated-DOM test covers React focus restoration and cancel events. Native browser dialog focus trapping remains pending the signed-in walkthrough.

Validation, stale versions, lost replies and local-storage failures preserve original/proposed text and operation identity. Another note cannot replace an unfinished correction reason. Editor discard preserves a separate reason, and review close preserves a separate unfinished editor. Unretained editor/reason text is encrypted in memory only, blocks updates and warns before leaving. It is not durable until Keep succeeds.

## Protocol and storage

Eight typed routes (four added here) require current session/CSRF, membership, exact actor/business/job/customer/device scope and active assignment. Checks precede receipt reuse. `/cancel` writes a terminal operation receipt; `/saved` inspects the exact created log; `/apply-correction` calls the same versioned log-edit helper as the ordinary field workflow; `/resolve-correction` records either applied or durably cancelled for the exact edit ID. Existing photo references, quantities and units are preserved by text correction.

Receipt and log/history commits share registry/tenant locking and transactions. A delayed delivery or revision cannot supersede cancellation; a delayed apply cannot supersede a resolved correction attempt. Lost replies never imply non-delivery. Server cancellation uses a distinct fingerprint so v0.22 code refuses a terminal row instead of reviving it.

IndexedDB upgrades from version 1 to 2 in the same database. Device identity, settings and encrypted notes remain. Atomic `closed` metadata plus note deletion prevents late local acknowledgement or retained-editor retry from restoring a removed ID. Old version-1 clients fail closed after upgrade; close older tabs if upgrading is blocked. Closed IDs contain no text, keys, saved-log ID or time and are not pruned in this prototype. This prevents safe downgrade to an older client on an upgraded store; it does not change server recovery format.

The existing two-minute key lease, hidden-tab lock, current-access checks and memory-only key policy remain. Foreign or revoked access cannot inspect/decrypt another actor's entries just to clear them. Password/membership/customer/dataset changes can strand ciphertext permanently. Production key recovery, retention limits, forensic erasure and full-day/cold-offline use remain OPEN. Browser eviction/deletion, quota and device loss can lose device-only work; server backups do not include it.

## Evidence and reproduction

Run the normal client tests and backend tests. For actual React interaction tests, install `tests/component` with its pinned pnpm lock (`--frozen-lockfile --ignore-scripts`), then run `node scripts/check-device-note-interactions.mjs` from the project root. The runner uses the frontend React/Vite installation plus jsdom 30.1.3 and fake-indexeddb 6.2.7. These are isolated test dependencies, not product dependencies. Its dialog open/close shim is explicit. This is simulated DOM, not authenticated browser or real-device evidence.

See `device-note-lifecycle-checks.json` and `device-note-lifecycle-evidence/` for executed counts, logs, simulated failure cases and real Chrome storage proof. The Chrome fixture imports the exact production store on a separate anonymous origin. It never exposes the private application, creates a browser session or signs in.

A final-package owner walkthrough is prepared separately at `http://note-review-v23.localhost:8796/workspace` with one invented active job. Its ordinary login is left unsigned pending fresh permission. The earlier one-time v0.20.2 owner permission was already fulfilled. No staff credential or session is injected. Signed-in UI, phone/standalone/background, production integration and full offline parity remain unverified until independently evidenced.

Schema and recovery remain format **12**, backup metadata **0.20.0**. New receipts use the existing operations table; confirmed logs/history and cancellation/correction receipts survive restore and a second backup. Restored credentials/dataset identity cannot unlock old device ciphertext. Prior packages, old QA data and the running v0.19 review are preserved.

Materials/photos, timers/team size or reconstructed time, invoices/payments, portal replies, generic replay and push remain OPEN for full parity, not agreed exclusions.

## Maintenance repair found by full regression

The first full run passed 735 and failed seven existing large-image/recovery checks. The decoder repeatedly called `communicate`, then retried with no input after a short monitor timeout. On this Python runtime that could stop feeding the remaining image bytes. The failure also reproduced with normal local process access. One continuous pipe exchange now runs alongside the same time/memory/output supervisor; no file-size, pixel, output, memory or time bound was increased, and originals still travel through stdin without an added image file. The 32 format/recovery tests, including a deliberately slow initial reader, pass; the final full suite passes 743. No photo offline queue or new format was added.

The implementation uses the standard [subprocess interface](https://docs.python.org/3.12/library/subprocess.html#subprocess.Popen.communicate) and [Future result timeout](https://docs.python.org/3.12/library/concurrent.futures.html#concurrent.futures.Future.result) to keep transfer and supervision separate.
