Task retitle exposes the old canonical checkbox as an extra inline Task #940

Open
opened 2026-10-02 17:43:05 +00:00 by kayg · 5 comments
Owner

Evidence

Round 2 Apple staging matrix (#476), image c4a61e8cf090170f35b1bed3350d9de20c83ecd5, test User only.

  1. Create a standalone Task through POST /api/v1/notes/tasks with {"text":"R2 web task due 2026-10-08 p1","standalone":true}. 201. GET /notes/tasks shows one file Task with ID c5817113-900e-468b-8e0b-53de7d7aca6e.
  2. PATCH its properties to priority low, due October 10 with current If-Match. 200.
  3. POST /api/v1/notes/<id>/retitle with {"title":"R2 web task edited"} and current If-Match. 200.
  4. GET /notes/tasks now shows the renamed file Task and an extra inline Task Notes/20261002-r2-web-task-edited-c5817113.md#L0, with the old title R2 web task and no date or priority.
  5. GET the Note: frontmatter title is R2 web task edited, but body remains \n- [ ] R2 web task\n. The new inline Task persists through native Reminders sync. Deleting the Note removes both projections.

Expected

Retitle a standalone Task without creating a second Task. Keep its canonical body line consistent, or preserve the canonical Task exclusion independently of title matching. Do not treat this as a Mac-only bug: the extra row is in the Tasks API.

No product change was made in this test-only job. Non-secret snapshots are retained with #476 evidence.

## Evidence Round 2 Apple staging matrix (#476), image `c4a61e8cf090170f35b1bed3350d9de20c83ecd5`, test User only. 1. Create a standalone Task through `POST /api/v1/notes/tasks` with `{"text":"R2 web task due 2026-10-08 p1","standalone":true}`. 201. GET `/notes/tasks` shows one file Task with ID `c5817113-900e-468b-8e0b-53de7d7aca6e`. 2. PATCH its properties to priority low, due October 10 with current If-Match. 200. 3. POST `/api/v1/notes/<id>/retitle` with `{"title":"R2 web task edited"}` and current If-Match. 200. 4. GET `/notes/tasks` now shows the renamed file Task **and an extra inline Task** `Notes/20261002-r2-web-task-edited-c5817113.md#L0`, with the old title `R2 web task` and no date or priority. 5. GET the Note: frontmatter title is `R2 web task edited`, but body remains `\n- [ ] R2 web task\n`. The new inline Task persists through native Reminders sync. Deleting the Note removes both projections. ## Expected Retitle a standalone Task without creating a second Task. Keep its canonical body line consistent, or preserve the canonical Task exclusion independently of title matching. Do not treat this as a Mac-only bug: the extra row is in the Tasks API. No product change was made in this test-only job. Non-secret snapshots are retained with #476 evidence.
Author
Owner

Started #940 on job/fix-940, base origin/job/reminders-643 at bc66ed31d6, as requested. Read CLAUDE.md, CONTEXT.md, DESIGN §§30–31, 40–42, issue #940 and #643/#647 final reports. Tracing canonical checkbox exclusion and shared retitle writers. No dependencies or UI changes planned.

Started #940 on job/fix-940, base origin/job/reminders-643 at bc66ed31d62bb020115b27cfe3f5bb2e1f38b743, as requested. Read CLAUDE.md, CONTEXT.md, DESIGN §§30–31, 40–42, issue #940 and #643/#647 final reports. Tracing canonical checkbox exclusion and shared retitle writers. No dependencies or UI changes planned.
Author
Owner

Root cause: the Note retitle route calls store::transition → plan_rename, which updates title/slug and the first heading but leaves the root checkbox unchanged. extract_task_index excludes the unanchored root only while its parsed title matches frontmatter. Thus the old root becomes a second inline Task after Note retitle. Both Task properties PATCH and DAV update_existing already use set_task_root_checkbox_title. The fix will reuse that helper before persisting the rename intent, so recovery also gets consistent bytes. The staging report documents an API retitle, not an Apple-origin retitle; the regression will replay that sequence and a subsequent Apple-shaped full-resource PUT. No exact staging PUT payload is included in the report; captured Apple replay fixtures establish the protocol shape.

Root cause: the Note retitle route calls store::transition → plan_rename, which updates title/slug and the first heading but leaves the root checkbox unchanged. extract_task_index excludes the unanchored root only while its parsed title matches frontmatter. Thus the old root becomes a second inline Task after Note retitle. Both Task properties PATCH and DAV update_existing already use set_task_root_checkbox_title. The fix will reuse that helper before persisting the rename intent, so recovery also gets consistent bytes. The staging report documents an API retitle, not an Apple-origin retitle; the regression will replay that sequence and a subsequent Apple-shaped full-resource PUT. No exact staging PUT payload is included in the report; captured Apple replay fixtures establish the protocol shape.
Author
Owner

Review finding reproduced: the new core regression projected 3 rows for frontmatter title Parent #work: File Task, its generated mirror, and its real anchored child. The expected count is 2. Literal ^anchor, date and priority syntax share this path. The shared mirror matcher now treats frontmatter title text literally; only an anchor or metadata outside that title belongs to the checkbox. Projection, title writes and status writes use the same matcher. The scan still parses each checkbox once and keeps no extra copy of its raw text. This extends the canonical-exclusion fix explicitly allowed by #940. No existing test assertion is changed. Dev integration preserved both sides and kept deployed Journal snapshot migration 0024; the not-yet-merged #643/#647 wire epoch migration is now 0025, with identical SQL.

Review finding reproduced: the new core regression projected 3 rows for frontmatter title `Parent #work`: File Task, its generated mirror, and its real anchored child. The expected count is 2. Literal `^anchor`, date and priority syntax share this path. The shared mirror matcher now treats frontmatter title text literally; only an anchor or metadata outside that title belongs to the checkbox. Projection, title writes and status writes use the same matcher. The scan still parses each checkbox once and keeps no extra copy of its raw text. This extends the canonical-exclusion fix explicitly allowed by #940. No existing test assertion is changed. Dev integration preserved both sides and kept deployed Journal snapshot migration 0024; the not-yet-merged #643/#647 wire epoch migration is now 0025, with identical SQL.
Author
Owner

The #940 writer fix and regression tests are committed at 612ccfa03e4de6ec80cc9e6635d427f24ef3d2a6. The focused HTTP/DAV replay passed:

test standalone_task_retitle_and_apple_put_keep_one_task ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 140.52s

Task core clippy/tests and Notes clippy passed. The full Notes run returned:

test result: FAILED. 172 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1320.93s

Sequential unchanged checks passed daily_and_composer_preserve_unrelated_bytes, journal_patch_storm_finishes_with_one_winner_and_stale_preconditions and journal_read_does_not_wait_for_reconciliation. The existing imap::tests::captured_append_store_expunge_executes_on_real_home still reached its 10-second Elapsed(()) timeout at imap.rs:1537. No existing expectation was changed. The new retitle writer test, rename crash-recovery test and Reminder wire-epoch migration test passed in the full run. Server gates are running with two test threads.

The #940 writer fix and regression tests are committed at `612ccfa03e4de6ec80cc9e6635d427f24ef3d2a6`. The focused HTTP/DAV replay passed: ```text test standalone_task_retitle_and_apple_put_keep_one_task ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 140.52s ``` Task core clippy/tests and Notes clippy passed. The full Notes run returned: ```text test result: FAILED. 172 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1320.93s ``` Sequential unchanged checks passed `daily_and_composer_preserve_unrelated_bytes`, `journal_patch_storm_finishes_with_one_winner_and_stale_preconditions` and `journal_read_does_not_wait_for_reconciliation`. The existing `imap::tests::captured_append_store_expunge_executes_on_real_home` still reached its 10-second `Elapsed(())` timeout at `imap.rs:1537`. No existing expectation was changed. The new retitle writer test, rename crash-recovery test and Reminder wire-epoch migration test passed in the full run. Server gates are running with two test threads.
Author
Owner

Fix ready for the orchestrator's real Mac staging retest. Full branch gates are not all green; see the unchanged test failures below.

Branch: job/fix-940. Base: origin/job/reminders-643 at bc66ed31d62bb020115b27cfe3f5bb2e1f38b743. Integrated origin/dev once in ce6d0c7dc. Head: 612ccfa03e4de6ec80cc9e6635d427f24ef3d2a6. Atomic fix commits: 16a9613c0 and 612ccfa03. No push, deploy or issue closure.

Fixed #940: a standalone Task retitle now updates its canonical checkbox in the durable Note rename plan. The Task keeps its ID. Prose and real child Tasks keep their bytes. The shared projector and checkbox writers also match the literal frontmatter title when it contains Task syntax.

Regression evidence: the new HTTP/DAV replay failed before the fix because the body retained - [ ] R2 web task after the Note title changed. The new core regression failed with three projected rows instead of two for Parent #work. Both tests retain existing expectations and fixtures. The replay uses the captured macOS 27 PUT envelope; identity, title, date and priority are normalized for this case. The staging journal query returned no records for the reported Task ID, so this is not its exact native PUT.

UX gaps closed: the extra Inline Task after retitle; literal titles containing tags, block syntax, dates, priority and status marks; correct mirror status writes without changing real children. Coverage includes same-path and moving retitles, CRLF, ordinary Notes and files without a matching mirror.

UX gaps left / known gaps: the orchestrator must rerun the real Mac staging check. Existing files already damaged by a prior mismatched title are not guessed or repaired automatically.

Decisions: use the existing canonical checkbox splice before saving the rename intent, so crash recovery writes the same bytes. Interpret a canonical mirror title literally; only metadata and anchors outside that title have Task meaning. Preserve origin/dev migration 24 and move the prerequisite Reminder wire epoch migration to 25, with unchanged SQL. No UI changes, new dependencies or public API additions.

For the merge round: rerun cargo test -p calternal-plugin-notes --test apple_replay standalone_task_retitle_and_apple_put_keep_one_task on the combined branch. Run the real Mac staging workflow from #476: retitle a standalone Task in Reminders and verify one Task ID, one checkbox, unchanged other Markdown, and no old-title Inline Task. Full adversarial matrices, full web suites and native interoperability belong to the merge round under the latest verification policy. No performance measurement is required for this non-performance issue.

Files: crates/calternal-notes-core/src/tasks/{line,extract}.rs; crates/plugins/notes/src/{store,reminders_tests}.rs; crates/plugins/notes/tests/apple_replay.rs. The merge retained both module comments in tasks_api.rs, tasks_dav.rs and store.rs, and renamed the prerequisite migration to crates/plugins/notes/migrations/0025_reminder_authoritative_wire_epoch.sql.

Gate completion output below is verbatim. cargo fmt --check exited 0 with no output. Each Cargo command used CARGO_PROFILE_DEV_DEBUG=line-tables-only, CARGO_INCREMENTAL=0, CARGO_BUILD_JOBS=4, the preset job target, and worktree target/tmp. Server tests used two threads.

cargo clippy -p calternal-notes-core --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 50s

cargo test -p calternal-notes-core

    Finished `test` profile [unoptimized + debuginfo] target(s) in 49.05s
test result: ok. 520 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s
test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.74s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.33s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 50s

cargo test -p calternal-plugin-notes

    Finished `test` profile [unoptimized + debuginfo] target(s) in 18m 15s
test result: FAILED. 172 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1320.93s

cargo test -p calternal-plugin-notes --lib -- imap::tests::captured_append_store_expunge_executes_on_real_home tests::daily_and_composer_preserve_unrelated_bytes tests::journal_patch_storm_finishes_with_one_winner_and_stale_preconditions tests::journal_read_does_not_wait_for_reconciliation --test-threads=1

    Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 37s
test result: FAILED. 3 passed; 1 failed; 0 ignored; 0 measured; 172 filtered out; finished in 93.80s

cargo test -p calternal-plugin-notes --test apple_replay standalone_task_retitle_and_apple_put_keep_one_task -- --test-threads=1

    Finished `test` profile [unoptimized + debuginfo] target(s) in 3.35s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 140.52s

cargo clippy -p calternal-server --all-targets -- -D warnings

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 5m 47s

cargo test -p calternal-server

    Finished `test` profile [unoptimized + debuginfo] target(s) in 43m 04s
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 109 filtered out; finished in 65.13s
test result: FAILED. 106 passed; 1 failed; 3 ignored; 0 measured; 0 filtered out; finished in 157.77s

Known gate failures: Notes had three timeout failures and one Journal read 404. The unchanged Journal read, PATCH storm and reconciliation tests all passed sequentially. The existing IMAP replay still timed out at imap.rs:1537 (10-second read deadline). The server subprocess runner failed because full_app_setup_session_config_and_backup reached Elapsed(()) at wire.rs:7679 while waiting up to five seconds for archived User deletion. No existing test expectation was changed. The server check initially failed because embedded frontend assets were absent; frozen-lockfile install and an unchanged production frontend build succeeded, then server clippy passed. No tracked frontend or lockfile changed.

For the merge round, recheck the remaining timeouts with:

cargo test -p calternal-plugin-notes --lib imap::tests::captured_append_store_expunge_executes_on_real_home -- --exact --test-threads=1
cargo test -p calternal-server wire::tests::live_apps_run_in_separate_processes -- --exact --test-threads=1

These must complete the captured IMAP exchange and archived User deletion within their unchanged deadlines. Rerun the combined branch's full gates once. Native Mac staging retitle remains required; this job did not claim native interoperability or run the full adversarial matrices, web suites or performance profiles.

Cleanup: cargo clean exited 0. Its output was:

     Removed 16105 files, 7.5GiB total

Removed apps/web/build and apps/web/.svelte-kit. Tracked worktree is clean. Review artifacts and full gate logs remain in gitignored artifacts/; no screenshots or build output were committed.

Fix ready for the orchestrator's real Mac staging retest. Full branch gates are not all green; see the unchanged test failures below. Branch: `job/fix-940`. Base: `origin/job/reminders-643` at `bc66ed31d62bb020115b27cfe3f5bb2e1f38b743`. Integrated `origin/dev` once in `ce6d0c7dc`. Head: `612ccfa03e4de6ec80cc9e6635d427f24ef3d2a6`. Atomic fix commits: `16a9613c0` and `612ccfa03`. No push, deploy or issue closure. Fixed #940: a standalone Task retitle now updates its canonical checkbox in the durable Note rename plan. The Task keeps its ID. Prose and real child Tasks keep their bytes. The shared projector and checkbox writers also match the literal frontmatter title when it contains Task syntax. Regression evidence: the new HTTP/DAV replay failed before the fix because the body retained `- [ ] R2 web task` after the Note title changed. The new core regression failed with three projected rows instead of two for `Parent #work`. Both tests retain existing expectations and fixtures. The replay uses the captured macOS 27 PUT envelope; identity, title, date and priority are normalized for this case. The staging journal query returned no records for the reported Task ID, so this is not its exact native PUT. UX gaps closed: the extra Inline Task after retitle; literal titles containing tags, block syntax, dates, priority and status marks; correct mirror status writes without changing real children. Coverage includes same-path and moving retitles, CRLF, ordinary Notes and files without a matching mirror. UX gaps left / known gaps: the orchestrator must rerun the real Mac staging check. Existing files already damaged by a prior mismatched title are not guessed or repaired automatically. Decisions: use the existing canonical checkbox splice before saving the rename intent, so crash recovery writes the same bytes. Interpret a canonical mirror title literally; only metadata and anchors outside that title have Task meaning. Preserve origin/dev migration 24 and move the prerequisite Reminder wire epoch migration to 25, with unchanged SQL. No UI changes, new dependencies or public API additions. For the merge round: rerun `cargo test -p calternal-plugin-notes --test apple_replay standalone_task_retitle_and_apple_put_keep_one_task` on the combined branch. Run the real Mac staging workflow from #476: retitle a standalone Task in Reminders and verify one Task ID, one checkbox, unchanged other Markdown, and no old-title Inline Task. Full adversarial matrices, full web suites and native interoperability belong to the merge round under the latest verification policy. No performance measurement is required for this non-performance issue. Files: `crates/calternal-notes-core/src/tasks/{line,extract}.rs`; `crates/plugins/notes/src/{store,reminders_tests}.rs`; `crates/plugins/notes/tests/apple_replay.rs`. The merge retained both module comments in `tasks_api.rs`, `tasks_dav.rs` and `store.rs`, and renamed the prerequisite migration to `crates/plugins/notes/migrations/0025_reminder_authoritative_wire_epoch.sql`. Gate completion output below is verbatim. `cargo fmt --check` exited 0 with no output. Each Cargo command used `CARGO_PROFILE_DEV_DEBUG=line-tables-only`, `CARGO_INCREMENTAL=0`, `CARGO_BUILD_JOBS=4`, the preset job target, and worktree `target/tmp`. Server tests used two threads. `cargo clippy -p calternal-notes-core --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 50s ``` `cargo test -p calternal-notes-core` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 49.05s test result: ok. 520 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s test result: ok. 19 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.74s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.33s test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 50s ``` `cargo test -p calternal-plugin-notes` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 18m 15s test result: FAILED. 172 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1320.93s ``` `cargo test -p calternal-plugin-notes --lib -- imap::tests::captured_append_store_expunge_executes_on_real_home tests::daily_and_composer_preserve_unrelated_bytes tests::journal_patch_storm_finishes_with_one_winner_and_stale_preconditions tests::journal_read_does_not_wait_for_reconciliation --test-threads=1` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 3m 37s test result: FAILED. 3 passed; 1 failed; 0 ignored; 0 measured; 172 filtered out; finished in 93.80s ``` `cargo test -p calternal-plugin-notes --test apple_replay standalone_task_retitle_and_apple_put_keep_one_task -- --test-threads=1` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 3.35s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 140.52s ``` `cargo clippy -p calternal-server --all-targets -- -D warnings` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 5m 47s ``` `cargo test -p calternal-server` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 43m 04s test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 109 filtered out; finished in 65.13s test result: FAILED. 106 passed; 1 failed; 3 ignored; 0 measured; 0 filtered out; finished in 157.77s ``` Known gate failures: Notes had three timeout failures and one Journal read 404. The unchanged Journal read, PATCH storm and reconciliation tests all passed sequentially. The existing IMAP replay still timed out at `imap.rs:1537` (10-second read deadline). The server subprocess runner failed because `full_app_setup_session_config_and_backup` reached `Elapsed(())` at `wire.rs:7679` while waiting up to five seconds for archived User deletion. No existing test expectation was changed. The server check initially failed because embedded frontend assets were absent; frozen-lockfile install and an unchanged production frontend build succeeded, then server clippy passed. No tracked frontend or lockfile changed. For the merge round, recheck the remaining timeouts with: ```sh cargo test -p calternal-plugin-notes --lib imap::tests::captured_append_store_expunge_executes_on_real_home -- --exact --test-threads=1 cargo test -p calternal-server wire::tests::live_apps_run_in_separate_processes -- --exact --test-threads=1 ``` These must complete the captured IMAP exchange and archived User deletion within their unchanged deadlines. Rerun the combined branch's full gates once. Native Mac staging retitle remains required; this job did not claim native interoperability or run the full adversarial matrices, web suites or performance profiles. Cleanup: `cargo clean` exited 0. Its output was: ```text Removed 16105 files, 7.5GiB total ``` Removed `apps/web/build` and `apps/web/.svelte-kit`. Tracked worktree is clean. Review artifacts and full gate logs remain in gitignored `artifacts/`; no screenshots or build output were committed.
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#940
No description provided.