Calendar: rejected Journal ETag stays cached and prevents normal edit retries #790

Open
opened 2026-10-02 13:10:49 +00:00 by kayg · 4 comments
Owner

Context

Round 7b review for #427. Branch job/calcard-series, head 5f7fdb96706aca0c457a6b7851f548f7f618ac85. This branch replaces the fresh stable-entry read used by the base Journal writer with a retained module ETag.

Evidence

apps/web/src/lib/calendar/journal.ts:58 returns any known cached ETag. patchEntry:67 and deleteEntry:101 do not remove it when the request fails with 412. forgetDays:54 clears only day promises. The Calendar error handler at apps/web/src/routes/calendar/[view]/[date]/+page.svelte:719 refreshes Calendar data, whose range payload contains no ETags.

Failure sequence

Read Journal revision R0. Another Installation or DAV client changes that entry to R1. Try to edit it: patchEntry submits R0 and correctly gets 412. The Calendar rolls back and refreshes its display. Try again from that refreshed display: the module still returns R0, so the retry also gets 412. The rejected ETag survives all forgetDays calls. A fresh Journal-day read or document reload can recover, but a normal Calendar range refresh cannot.

Validation

A local Bun check imports the reviewed Journal adapter and stubs only transport. After an external fixture edit to R1, two calls to the actual patchEntry, with forgetDays between them, both send R0 and fail. No live server or User files were changed.

Expected

Discard a rejected ETag and update the write precondition from an authoritative response before an explicit retry. A refreshed visible Log must not retain the failed write's old precondition. Keep current-entry and original-Undo revisions separate.

Test idea

Read an entry, update it in a second Installation, then edit it in the first Calendar. After the conflict refresh, retry one deliberate edit. Require success with the refreshed precondition and preservation of unrelated newer fields. Repeat for DELETE.

Severity

Non-blocking: the server preserves data and returns a conflict, but editing can remain stuck until a Journal read or reload.

Duplicate check: all open and closed issue titles and stale-write searches. No Journal rejected-ETag retry issue was found.

## Context Round 7b review for #427. Branch `job/calcard-series`, head `5f7fdb96706aca0c457a6b7851f548f7f618ac85`. This branch replaces the fresh stable-entry read used by the base Journal writer with a retained module ETag. ## Evidence `apps/web/src/lib/calendar/journal.ts:58` returns any known cached ETag. `patchEntry:67` and `deleteEntry:101` do not remove it when the request fails with 412. `forgetDays:54` clears only day promises. The Calendar error handler at `apps/web/src/routes/calendar/[view]/[date]/+page.svelte:719` refreshes Calendar data, whose range payload contains no ETags. ## Failure sequence Read Journal revision R0. Another Installation or DAV client changes that entry to R1. Try to edit it: patchEntry submits R0 and correctly gets 412. The Calendar rolls back and refreshes its display. Try again from that refreshed display: the module still returns R0, so the retry also gets 412. The rejected ETag survives all `forgetDays` calls. A fresh Journal-day read or document reload can recover, but a normal Calendar range refresh cannot. ## Validation A local Bun check imports the reviewed Journal adapter and stubs only transport. After an external fixture edit to R1, two calls to the actual `patchEntry`, with `forgetDays` between them, both send R0 and fail. No live server or User files were changed. ## Expected Discard a rejected ETag and update the write precondition from an authoritative response before an explicit retry. A refreshed visible Log must not retain the failed write's old precondition. Keep current-entry and original-Undo revisions separate. ## Test idea Read an entry, update it in a second Installation, then edit it in the first Calendar. After the conflict refresh, retry one deliberate edit. Require success with the refreshed precondition and preservation of unrelated newer fields. Repeat for DELETE. ## Severity Non-blocking: the server preserves data and returns a conflict, but editing can remain stuck until a Journal read or reload. Duplicate check: all open and closed issue titles and stale-write searches. No Journal rejected-ETag retry issue was found.
Author
Owner

Started on job/datafix, base 2f4482ded066d9c5d9c59130377907f7fd2916c9 (job/merge-round-7a). Fetched origin and checked both origin/dev and origin/job/merge-round-7a: already up to date. The base lacks task_views.rs, the Journal ETag cache and primitives #665–#668. Those exist on the audited feature branches. Requested prerequisite integration clarification; meanwhile tracing and fixing the Calendar Undo defect present on this base. No push or deploy.

Started on `job/datafix`, base `2f4482ded066d9c5d9c59130377907f7fd2916c9` (`job/merge-round-7a`). Fetched origin and checked both origin/dev and origin/job/merge-round-7a: already up to date. The base lacks task_views.rs, the Journal ETag cache and primitives #665–#668. Those exist on the audited feature branches. Requested prerequisite integration clarification; meanwhile tracing and fixing the Calendar Undo defect present on this base. No push or deploy.
Author
Owner

Confirmed #777 on the specified base. New title/move/resize Undo tests fail on the old adapter because it fetches the latest ETag, then accepts the old inverse. The fix passes the forward response ETag into the field inverse and publishes its response only after server acceptance.

For #790, tested the same deliberate PATCH and DELETE retry regressions against the audited origin/job/calcard-series adapter. Both fail with a second 412. Both pass with the specified base's fresh stable-entry read. Retain this behavior during integration; do not restore the unbounded module ETag map.

Read the audit evidence at rev-7b-data/review-findings.md (the file is named review-findings.md, not audit-findings.md). The specified base still lacks the #731 route and #665–#668 primitives. Prerequisite branch integration remains unspecified.

Confirmed #777 on the specified base. New title/move/resize Undo tests fail on the old adapter because it fetches the latest ETag, then accepts the old inverse. The fix passes the forward response ETag into the field inverse and publishes its response only after server acceptance. For #790, tested the same deliberate PATCH and DELETE retry regressions against the audited `origin/job/calcard-series` adapter. Both fail with a second 412. Both pass with the specified base's fresh stable-entry read. Retain this behavior during integration; do not restore the unbounded module ETag map. Read the audit evidence at `rev-7b-data/review-findings.md` (the file is named review-findings.md, not audit-findings.md). The specified base still lacks the #731 route and #665–#668 primitives. Prerequisite branch integration remains unspecified.
Author
Owner

Partial completion: Journal fixes committed; saved Tasks view fix remains an unverified candidate.

Branch job/datafix, head 4345529d69f2e4b83ba200e53f4878b6300f8532. Base 2f4482ded066d9c5d9c59130377907f7fd2916c9. Fetched and merged origin/dev and origin/job/merge-round-7a once before the final gates. No push or deploy. Issues remain open.

Built (#777, #790)
Journal Undo sends a field inverse with the forward acknowledgement revision. A later write causes 412. An accepted inverse applies the canonical response to the current Calendar model. It never restores a retained full day map. Normal PATCH and DELETE read the current stable block identity without using a retained day revision. Retained edits also check the fields they intend to change. Composer edits now use the shared write and Undo flow, including cross-day edits. Linked Note conversion checks its retained title. A rejected edit or Undo refresh accepts current server Logs. Accepted writes still protect their canonical Logs from a delayed Calendar projection.

Regression evidence: the original Undo adapter failed 3 tests; the audited #790 cache module failed the PATCH and DELETE deliberate-retry tests; the retained-field guard failed before its fix; and the rejected-refresh order failed its new test (1 failed, 13 passed). The current focused Calendar slice passed all 26 tests. No existing status assertion was weakened. A read-only reviewer confirmed the two final conflict fixes.

Files
apps/web/src/lib/calendar/journal.ts, journal.test.ts, edits.ts, edits.test.ts; apps/web/src/routes/calendar/[view]/[date]/+page.svelte; apps/web/e2e/journal-datafix.mjs; tests/adversarial/journal-revisions.mjs; bench/calendar-weekstate-609.mjs; docs/perf/2026-10-02-datafix.md. The two Python authorization files came from the authorized base merge, not this fix.

Gates and production evidence
Root cargo fmt --check exited 0 with no output. No Rust crate changed on job/datafix; crate clippy/test are not applicable to its Journal changes.

Final bun run check output:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/datafix/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

The full web suite before the last review fix passed:

 Test Files  154 passed (154)
      Tests  1081 passed (1081)
   Start at  16:06:32
   Duration  2341.22s (transform 35%, import 27%, environment 23%, tests 10%, setup 5%)

After the review fix, the focused slice output was:

 Test Files  2 passed (2)
      Tests  26 passed (26)
   Start at  18:36:45
   Duration  7.71s (transform 90%, import 9%, tests 1%)

The final full rerun was stopped at the time limit after two 30-second failures in unchanged WebMCP tests. It is NOT a final full-suite pass:

 ❯ |unit| src/lib/webmcp/generated.test.ts (9 tests | 2 failed) 73051ms
   × contract writes need confirmation and recheck Apps revocation 30023ms
   × a read checks access once and a write sees revocation during confirmation 30033ms
error: script "test" exited with code 130

A single isolated rerun of that unchanged file passed without assertion changes:

 Test Files  1 passed (1)
      Tests  9 passed (9)
   Start at  19:16:24
   Duration  8.66s (transform 85%, import 9%, tests 6%)

The final whole-web gate still needs a completed run on this head. The isolated result supports timing/load as the immediate failure, but does not replace the full gate.

Updated production build output:

✓ built in 33.15s
✓ built in 56ms
✓ built in 2m
> Using @sveltejs/adapter-static
  Wrote site to "build"

Real local server output, final build:

Journal datafix: original Undo returned 412; later title remains on disk and in Calendar; twelve macOS screenshots captured.

The one bounded revision API round passed earlier:

Journal revision round: stale PATCH and DELETE rejected; deliberate retries passed.

The follow-up production run skipped that API round. Screenshots use macOS platform emulation, 390/820/1440 px, light/dark. They show Calendar and the settled Composer. Claude must review visual quality.

calendar: light 390, light 820, light 1440, dark 390, dark 820, dark 1440

composer: light 390, light 820, light 1440, dark 390, dark 820, dark 1440

Saved Tasks views (#731): known gap
The assigned base and origin/dev have no saved Tasks views feature. The audited feature exists on origin/job/tasks-mode (f620390c724ee08540d38b0ba69c3d12225fc1e4). The audited Journal cache exists on origin/job/calcard-series, and shared #665–#668 primitives are also absent from the assigned base. No unrelated feature branch was merged.

A separate source copy has a candidate fix. It uses the existing Notes content ETag and match_etag, plus calternal-fs replace_if. Every saved view read/write returns its complete-source revision. PUT requires If-Match and rejects a stale configuration. The form retains its original configuration and revision across background refresh. A conflict refreshes the Index and never replays the old configuration. Closing the form clears cancelled drafts. Contract/client changes, a stale-view/Files regression, missing-precondition coverage and a derived contract check are included. One round-trip fixture now supplies the revision it read; its status assertions remain unchanged. The shared E2E harness has a minimal optional-header test hook. A saved-view profile extends the existing Tasks sampler.

Candidate patch against the Tasks feature branch. Local source: target/tmp/taskviews-validation; local patch: artifacts/datafix/issue-731-candidate.patch. This candidate is NOT on the job branch and is NOT ready to merge. Rust old-code execution did not reach the test before the product patch was prepared; the old client did fail the stale replacement regression. The candidate client passed its focused regression and its frontend suite:

 Test Files  152 passed (152)
      Tests  1020 passed (1020)
   Start at  17:09:03
   Duration  4284.66s (transform 39%, import 24%, environment 18%, tests 12%, setup 7%)

Candidate cargo fmt --check exited 0 with no output. The candidate final bun run check passed after its form cleanup change:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/datafix/target/tmp/taskviews-validation/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

Rust Notes and server clippy/test did not finish. The first compilation was stopped after more than three hours in dependency builds and sccache. The direct clippy attempt is time-limited; no Rust test success is claimed. Live Tasks checks and its HDD profile were not run.

The candidate still needs integration with the feature branch, Notes and server Rust gates, generated-contract verification at runtime, live Tasks concurrency checks, required Tasks screenshots and HDD measurements. The #790 regression must stay when the cached calcard-series source is integrated; the assigned base already used uncached entry reads.

Performance
The perf VM lock was busy. This is a local model-only run, load average 128.02/127.22/120.83; no HTTP/HDD qualification. Average before/after p50/p95: 4.32/11.01 → 5.25/21.9 µs; CPU 0.0306 → 0.0377 s; RSS 49.84 → 54.22 MiB; burst CPU 0.0105 → 0.0177 s. Worst (31,000 Logs): 68.66/480.37 → 116.19/290.16 µs; CPU 0.112 → 0.164 s; RSS 60.79 → 61.83 MiB; burst CPU 0.0636 → 0.1377 s. The checked day hint reduced the initial canonical-response CPU from 1.3898 to 0.164 s per 1,000 calls. docs/perf/baseline.json has no Log-inverse baseline or threshold. Its existing date-window profile measures a different operation; it is context only. The canonical response costs more CPU than the old field projection. A quiet-host release comparison remains needed.

UX gaps closed
Composer edits now register Undo, cross-day edits use the same write flow, a retained edit cannot silently replace another client's edited field, refused Undo preserves later edits, conflicts refresh current Logs, and linked Note title edits use the same retained-field guard. In the Tasks candidate, background refresh cannot silently promote a stale open form to a newer revision, and cancelled form drafts are reset.

UX gaps left
The Tasks candidate has no live production verification or screenshot set. Its source prerequisite is not integrated. HTTP/HDD performance and real Apple-client checks are not completed. Visual acceptance belongs to Claude. Normal Journal keyboard Undo was checked on the production build; this job did not repeat every existing pointer/touch action.

Decisions
Undo is conservative: any post-acknowledgement change refuses the inverse instead of rebasing a retained edit automatically. Normal retained edits check only their edited fields; unrelated fresh fields are preserved. A saved Task view uses the complete .base source ETag, so another view or extension change can conservatively require review. Reuse existing helpers and stores; do not add parallel caches for absent #665–#668 implementations. Keep missing-feature work as a candidate patch instead of merging an unrelated feature branch.

Cleanup completed: both web production builds and .svelte-kit/output directories removed. cargo clean exited 0:

Removed 6711 files, 2.2GiB total

The working tree is clean. Work stopped within the four-hour limit.

Partial completion: Journal fixes committed; saved Tasks view fix remains an unverified candidate. Branch `job/datafix`, head `4345529d69f2e4b83ba200e53f4878b6300f8532`. Base `2f4482ded066d9c5d9c59130377907f7fd2916c9`. Fetched and merged `origin/dev` and `origin/job/merge-round-7a` once before the final gates. No push or deploy. Issues remain open. **Built (#777, #790)** Journal Undo sends a field inverse with the forward acknowledgement revision. A later write causes 412. An accepted inverse applies the canonical response to the current Calendar model. It never restores a retained full day map. Normal PATCH and DELETE read the current stable block identity without using a retained day revision. Retained edits also check the fields they intend to change. Composer edits now use the shared write and Undo flow, including cross-day edits. Linked Note conversion checks its retained title. A rejected edit or Undo refresh accepts current server Logs. Accepted writes still protect their canonical Logs from a delayed Calendar projection. Regression evidence: the original Undo adapter failed 3 tests; the audited #790 cache module failed the PATCH and DELETE deliberate-retry tests; the retained-field guard failed before its fix; and the rejected-refresh order failed its new test (1 failed, 13 passed). The current focused Calendar slice passed all 26 tests. No existing status assertion was weakened. A read-only reviewer confirmed the two final conflict fixes. **Files** `apps/web/src/lib/calendar/journal.ts`, `journal.test.ts`, `edits.ts`, `edits.test.ts`; `apps/web/src/routes/calendar/[view]/[date]/+page.svelte`; `apps/web/e2e/journal-datafix.mjs`; `tests/adversarial/journal-revisions.mjs`; `bench/calendar-weekstate-609.mjs`; `docs/perf/2026-10-02-datafix.md`. The two Python authorization files came from the authorized base merge, not this fix. **Gates and production evidence** Root `cargo fmt --check` exited 0 with no output. No Rust crate changed on `job/datafix`; crate clippy/test are not applicable to its Journal changes. Final `bun run check` output: ``` $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/datafix/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` The full web suite before the last review fix passed: ``` Test Files 154 passed (154) Tests 1081 passed (1081) Start at 16:06:32 Duration 2341.22s (transform 35%, import 27%, environment 23%, tests 10%, setup 5%) ``` After the review fix, the focused slice output was: ``` Test Files 2 passed (2) Tests 26 passed (26) Start at 18:36:45 Duration 7.71s (transform 90%, import 9%, tests 1%) ``` The final full rerun was stopped at the time limit after two 30-second failures in unchanged WebMCP tests. It is NOT a final full-suite pass: ``` ❯ |unit| src/lib/webmcp/generated.test.ts (9 tests | 2 failed) 73051ms × contract writes need confirmation and recheck Apps revocation 30023ms × a read checks access once and a write sees revocation during confirmation 30033ms error: script "test" exited with code 130 ``` A single isolated rerun of that unchanged file passed without assertion changes: ``` Test Files 1 passed (1) Tests 9 passed (9) Start at 19:16:24 Duration 8.66s (transform 85%, import 9%, tests 6%) ``` The final whole-web gate still needs a completed run on this head. The isolated result supports timing/load as the immediate failure, but does not replace the full gate. Updated production build output: ``` ✓ built in 33.15s ✓ built in 56ms ✓ built in 2m > Using @sveltejs/adapter-static Wrote site to "build" ``` Real local server output, final build: ``` Journal datafix: original Undo returned 412; later title remains on disk and in Calendar; twelve macOS screenshots captured. ``` The one bounded revision API round passed earlier: ``` Journal revision round: stale PATCH and DELETE rejected; deliberate retries passed. ``` The follow-up production run skipped that API round. Screenshots use macOS platform emulation, 390/820/1440 px, light/dark. They show Calendar and the settled Composer. Claude must review visual quality. calendar: [light 390](https://git.kayg.org/attachments/63d661eb-da0f-49df-80a9-b4b817cd53b8), [light 820](https://git.kayg.org/attachments/eb3c0636-ae18-49d2-b5e9-dadbdd2cb81e), [light 1440](https://git.kayg.org/attachments/76d342fe-498e-41c8-b8a5-a4dee9045750), [dark 390](https://git.kayg.org/attachments/9bd9a879-31c8-4c1e-99d0-c3aa3ff1a248), [dark 820](https://git.kayg.org/attachments/948dfcc7-5e47-4ac9-a5c8-2cf7a6aedf4b), [dark 1440](https://git.kayg.org/attachments/59077cd5-e09f-4530-a480-fd60e7b4ca1b) composer: [light 390](https://git.kayg.org/attachments/4578f0af-dd18-4e95-8cfb-219d16214588), [light 820](https://git.kayg.org/attachments/e16706f6-b06e-46b4-a4d4-a1f00fc9c5c1), [light 1440](https://git.kayg.org/attachments/5d4bfade-c1a7-402a-9fbe-28491b9b5cb5), [dark 390](https://git.kayg.org/attachments/94d20b15-dd39-46dd-b7be-287599b5f73b), [dark 820](https://git.kayg.org/attachments/a790ee53-65ff-4664-ba59-1f661d6d256e), [dark 1440](https://git.kayg.org/attachments/0a2428e1-ef63-4956-b14b-9f02bd5f1e50) **Saved Tasks views (#731): known gap** The assigned base and `origin/dev` have no saved Tasks views feature. The audited feature exists on `origin/job/tasks-mode` (`f620390c724ee08540d38b0ba69c3d12225fc1e4`). The audited Journal cache exists on `origin/job/calcard-series`, and shared #665–#668 primitives are also absent from the assigned base. No unrelated feature branch was merged. A separate source copy has a candidate fix. It uses the existing Notes content ETag and `match_etag`, plus `calternal-fs` `replace_if`. Every saved view read/write returns its complete-source revision. PUT requires If-Match and rejects a stale configuration. The form retains its original configuration and revision across background refresh. A conflict refreshes the Index and never replays the old configuration. Closing the form clears cancelled drafts. Contract/client changes, a stale-view/Files regression, missing-precondition coverage and a derived contract check are included. One round-trip fixture now supplies the revision it read; its status assertions remain unchanged. The shared E2E harness has a minimal optional-header test hook. A saved-view profile extends the existing Tasks sampler. [Candidate patch against the Tasks feature branch](https://git.kayg.org/attachments/7df79b86-c9a6-4854-a92f-4854d084cabd). Local source: `target/tmp/taskviews-validation`; local patch: `artifacts/datafix/issue-731-candidate.patch`. This candidate is NOT on the job branch and is NOT ready to merge. Rust old-code execution did not reach the test before the product patch was prepared; the old client did fail the stale replacement regression. The candidate client passed its focused regression and its frontend suite: ``` Test Files 152 passed (152) Tests 1020 passed (1020) Start at 17:09:03 Duration 4284.66s (transform 39%, import 24%, environment 18%, tests 12%, setup 7%) ``` Candidate `cargo fmt --check` exited 0 with no output. The candidate final `bun run check` passed after its form cleanup change: ``` $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/datafix/target/tmp/taskviews-validation/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Rust Notes and server clippy/test did not finish. The first compilation was stopped after more than three hours in dependency builds and sccache. The direct clippy attempt is time-limited; no Rust test success is claimed. Live Tasks checks and its HDD profile were not run. The candidate still needs integration with the feature branch, Notes and server Rust gates, generated-contract verification at runtime, live Tasks concurrency checks, required Tasks screenshots and HDD measurements. The #790 regression must stay when the cached calcard-series source is integrated; the assigned base already used uncached entry reads. **Performance** The perf VM lock was busy. This is a local model-only run, load average 128.02/127.22/120.83; no HTTP/HDD qualification. Average before/after p50/p95: 4.32/11.01 → 5.25/21.9 µs; CPU 0.0306 → 0.0377 s; RSS 49.84 → 54.22 MiB; burst CPU 0.0105 → 0.0177 s. Worst (31,000 Logs): 68.66/480.37 → 116.19/290.16 µs; CPU 0.112 → 0.164 s; RSS 60.79 → 61.83 MiB; burst CPU 0.0636 → 0.1377 s. The checked day hint reduced the initial canonical-response CPU from 1.3898 to 0.164 s per 1,000 calls. `docs/perf/baseline.json` has no Log-inverse baseline or threshold. Its existing date-window profile measures a different operation; it is context only. The canonical response costs more CPU than the old field projection. A quiet-host release comparison remains needed. **UX gaps closed** Composer edits now register Undo, cross-day edits use the same write flow, a retained edit cannot silently replace another client's edited field, refused Undo preserves later edits, conflicts refresh current Logs, and linked Note title edits use the same retained-field guard. In the Tasks candidate, background refresh cannot silently promote a stale open form to a newer revision, and cancelled form drafts are reset. **UX gaps left** The Tasks candidate has no live production verification or screenshot set. Its source prerequisite is not integrated. HTTP/HDD performance and real Apple-client checks are not completed. Visual acceptance belongs to Claude. Normal Journal keyboard Undo was checked on the production build; this job did not repeat every existing pointer/touch action. **Decisions** Undo is conservative: any post-acknowledgement change refuses the inverse instead of rebasing a retained edit automatically. Normal retained edits check only their edited fields; unrelated fresh fields are preserved. A saved Task view uses the complete `.base` source ETag, so another view or extension change can conservatively require review. Reuse existing helpers and stores; do not add parallel caches for absent #665–#668 implementations. Keep missing-feature work as a candidate patch instead of merging an unrelated feature branch. Cleanup completed: both web production builds and `.svelte-kit/output` directories removed. `cargo clean` exited 0: ``` Removed 6711 files, 2.2GiB total ``` The working tree is clean. Work stopped within the four-hour limit.
Author
Owner

Independent read-only follow-up for #777, reviewed job/datafix at
4345529d6. P2: an open Composer retains its failed edit precondition.

apps/web/src/routes/calendar/[view]/[date]/+page.svelte:1508 captures
item.log when Composer opens. Lines 1522–1532 compare every Save with
that same Log and pass it to editLog. Lines 878–892 then derive retained
preconditions from it. The conflict refresh at lines 760–763 replaces
Calendar data, but cannot replace the captured Composer baseline.
apps/web/src/lib/composer/Composer.svelte:1083 keeps the dialog open.

Open title A and type B. A second Installation saves C. The first Save
correctly conflicts and Calendar refreshes to C. Save B again from the
same dialog: the retained title is still A. The adapter's GET returns C
and the local guard fails again before PATCH. Closing and reopening is
required to obtain a current baseline. This is the retained-field form
of #790's stuck retry; the old ETag cache is no longer the cause.

Fix: preserve the draft and let an explicit retry use a refreshed baseline.
Do not silently replace the initial baseline before reporting conflict.
Use the actual pre-write fields for the retry's inverse. Reuse the existing
edit lifecycle rather than a separate unguarded PATCH.

Test the mounted Composer with two Saves from the same dialog. Require the
first to conflict and the deliberate second to save B. Undo must restore C
and preserve newer unrelated fields. The adapter retry tests in
journal.test.ts:107 omit retained fields and do not prove this behavior.
No build or runtime test ran in this LIGHT review.

Independent read-only follow-up for #777, reviewed `job/datafix` at `4345529d6`. P2: an open Composer retains its failed edit precondition. `apps/web/src/routes/calendar/[view]/[date]/+page.svelte:1508` captures `item.log` when Composer opens. Lines 1522–1532 compare every Save with that same Log and pass it to `editLog`. Lines 878–892 then derive retained preconditions from it. The conflict refresh at lines 760–763 replaces Calendar data, but cannot replace the captured Composer baseline. `apps/web/src/lib/composer/Composer.svelte:1083` keeps the dialog open. Open title A and type B. A second Installation saves C. The first Save correctly conflicts and Calendar refreshes to C. Save B again from the same dialog: the retained title is still A. The adapter's GET returns C and the local guard fails again before PATCH. Closing and reopening is required to obtain a current baseline. This is the retained-field form of #790's stuck retry; the old ETag cache is no longer the cause. Fix: preserve the draft and let an explicit retry use a refreshed baseline. Do not silently replace the initial baseline before reporting conflict. Use the actual pre-write fields for the retry's inverse. Reuse the existing edit lifecycle rather than a separate unguarded PATCH. Test the mounted Composer with two Saves from the same dialog. Require the first to conflict and the deliberate second to save B. Undo must restore C and preserve newer unrelated fields. The adapter retry tests in `journal.test.ts:107` omit retained fields and do not prove this behavior. No build or runtime test ran in this LIGHT review.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#790
No description provided.