Skip to content

fix(mobile): fence the to-do undo delete to its auth session and cap drafts on code points - #8566

Merged
iscekic merged 2 commits into
mainfrom
drain/todo-undo-auth-fence
Oct 24, 2026
Merged

iscekic merged 2 commits into
mainfrom
drain/todo-undo-auth-fence

Conversation

@iscekic

@iscekic iscekic commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Evidence

Evidence: demonstrated — on the Android emulator the to-do delete was exercised against the running local backend and database: deleting and then signing out inside the undo window sent no delete request and kept the row, a control delete after the window sent one request and removed the row, and a new-session draft whose cap boundary fell inside an emoji was saved through the real to-do create path as a well-formed title and notes.

Android runtime evidence (this branch's JavaScript)

The app ran this branch's JavaScript from the Metro dev server, on the omp36 emulator, signed into a locally seeded fake account. No production accounts or data were used. The backend and database were the local ones.

Undo-delete fence, judged from the backend request log and the stored to-do rows:

  • The delete-request count in the backend log was read before the run.
  • Delete then sign out: the row was swiped to delete and sign-out was confirmed inside the five-second window. The delete-request count did not change, the row stayed in the database, and after signing back in the item was listed again.
  • Control delete with the session intact: the delete-request count grew by exactly one and the row was gone from the database.
  • Every successful delete showed the undo toast and hid the row during the window.

Draft caps through the real conversion path (new session, then discard, then save as to-do), with the draft text entered on the emulator:

  • Title: ordinary characters followed by an emoji and trailing text, placed so that the title cap falls between the halves of the emoji. The saved to-do reported a title length of one unit less than the cap, ending in an ordinary character, with no lone surrogate stored.
  • Notes: a short first line, then ordinary characters, then an emoji and trailing text, placed so that the notes cap falls inside the emoji. The saved to-do reported the notes one unit short of their cap, ending in an ordinary character, and the first line as the title.

Screenshots of the observed states are attached to a comment on this pull request.

Regression coverage

apps/mobile/src/lib/hooks/use-todos.remove-with-undo.test.ts exercises the real hook with a controlled tRPC boundary and a real query cache: same-session deletion, sign-out during the window, replacement of the auth epoch, and a request failure after sign-out. The review correction reads through useTodoItems and its held-delete selector, asserting that the row is hidden while held and visible again after the skipped delete releases it.

apps/mobile/src/lib/todos/todo-model.test.ts covers an astral character straddling each field cap and a title whose complete astral character ends exactly at the cap.

Verification at this head: pnpm --filter kilo-app exec vitest run src/lib/hooks/use-todos.remove-with-undo.test.ts src/lib/todos/todo-model.test.ts — 2 files, 23 tests passed.

Change

  • removeWithUndo captures the initiating auth epoch. The request, failure toast, and query invalidation are restricted to that session while sign-out is inactive. A skipped delete releases its held row. Leaving the screen still commits for the same session; Undo remains available during the window.
  • todoFromDraft caps titles and notes on code-point boundaries while preserving the existing UTF-16 code-unit budgets of 500 and 10,000. Over-limit drafts continue to truncate silently.

Production prerequisites

None.

Proxy answers

The owner's proxy gave these answers in the owner's place.

  1. Question: Keep silent truncation or refuse over-limit drafts? Answer: Keep silent truncation, cut on a code point. Confidence: 0.80. Precedent: Minimize work without reducing quality; the approved scope asks for a surrogate-safe cut.
  2. Question: Release the held row after a skipped delete, or keep it hidden until refresh? Answer: Release it; skip the query invalidation. Confidence: 0.80. Precedent: Make reasonable implementation decisions; query invalidation follows the same auth-epoch check.
  3. Question: Allow temporary session-local judge credential routing while retaining all publication checks? Answer: Temporarily use the existing judge credential, then restore the original configuration. Confidence: 0.75. Precedent: Resolve operational blockers autonomously without weakening checks.

The undo timer sends the held delete five seconds later. Capture the auth
epoch when the timer is scheduled and skip the request, the failure toast and
the cache refresh once that session is gone or a sign-out is under way,
mirroring the fence linkStartedTodoSession already uses.

Also cap a draft's title and notes on a code point: an emoji that straddles
the router's code-unit limit is dropped whole instead of leaving an unpaired
surrogate.

Tests cover a sign-out, an account switch, and a failure that lands after a
sign-out inside the undo window, plus an emoji straddling each cap.
@iscekic iscekic added drain Work item from the findings drain janitor:features-workflows Janitor finding family: features-workflows labels Oct 22, 2026
@iscekic

iscekic commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Visual proof of the to-do undo path, captured on the Android emulator against the local dev stack with a local fake account and locally seeded to-dos.

Sequence:

  1. The to-do list with two items.
  2. Deleting one item shows the undo toast; the delete is held for the undo window.
  3. Signing out inside that window and signing back in still lists both items, and no delete request reached the local API for that run.
  4. With the session left intact, the item is removed from the list once the window elapses, matching the local database.

The draft-cap change is covered by the module tests; typing an astral emoji exactly at either boundary on the emulator was not exercised.

To-do list with two open items

Undo toast shown after deleting an item

Both items listed again after signing back in

List after the undo window elapsed, the deleted item is gone

@kilo-code-bot

kilo-code-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The follow-up replaces the raw-cache assertion with a consumer-level read through the held-delete select, which now proves the sign-out branch releases the held id; no new issues in the changed test code.

Assumptions: [INFERENCE] Mobile tests and typecheck were not executed in this read-only review; findings come from source inspection only.

Files Reviewed (4 files)
  • apps/mobile/src/lib/hooks/use-todos.remove-with-undo.test.ts - no issues
  • apps/mobile/src/lib/hooks/use-todos.ts - no issues (targeted re-check)
  • apps/mobile/src/lib/todos/todo-model.ts - no issues (targeted re-check)
  • apps/mobile/src/lib/todos/todo-model.test.ts - no issues (targeted re-check)
Previous Review Summary (commit 55e5c5f)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 55e5c5f)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The auth-epoch fence on the undo delete and the code-point draft cap are correct; the one finding is a test assertion that does not exercise the new held-id release path.

Assumptions: [INFERENCE] Mobile tests and typecheck were not executed in this read-only review; findings come from source inspection only.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/mobile/src/lib/hooks/use-todos.remove-with-undo.test.ts 143 Cache assertion reads the raw cache, which heldDeletes never filters, so it does not prove the sign-out branch released the held id
Files Reviewed (4 files)
  • apps/mobile/src/lib/hooks/use-todos.ts - no issues
  • apps/mobile/src/lib/hooks/use-todos.remove-with-undo.test.ts - 1 issue
  • apps/mobile/src/lib/todos/todo-model.ts - no issues
  • apps/mobile/src/lib/todos/todo-model.test.ts - no issues

Reviewed by deepseek-flash-latest · Input: 57.2K · Output: 3.2K · Cached: 176.4K

Review guidance: REVIEW.md from base branch main

The undo-window test read the raw query cache, which never filters held ids,
so its length assertion could not distinguish a released id. Read the list
through `useTodoItems` instead: the row is hidden while held and listed again
after a skipped delete.
@iscekic iscekic added merge:blocked:insufficient-e2e Verification does not cover the change merge:human Substantive functional risk requires human review. labels Oct 22, 2026
@iscekic

iscekic commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Android verification of this branch against the local backend, with a locally seeded fake account. Captures show the list after a new-session draft was saved as a to-do, the undo toast after a delete, the signed-out state reached inside the undo window, and the item listed again after signing back in. The stored rows were checked in the local database: the sign-out run added no delete request and kept the row, while a control delete after the window removed it.

To-do list after saving a new-session draft as a to-do

Undo toast after deleting a to-do

Signed-out state reached inside the undo window

The kept to-do listed after signing back in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain janitor:features-workflows Janitor finding family: features-workflows merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants