Add "Did you mean" duplicate-task suggestion - #77
Conversation
The project-level commit skill was byte-identical to the user-level one except a UI_VIEWS reminder already covered by CLAUDE.md — remove it so the user-level /commit takes over, and fold a commit-time re-check into the CLAUDE.md UI rule so that nudge isn't lost. Add a /cleardb (a.k.a. /flushdb) skill to wipe the local dev SQLite DB for a clean slate, always taking a timestamped backup first. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a /cleardb (a.k.a. /flushdb) skill to wipe the local dev SQLite DB for a clean slate, always taking a timestamped backup first. Strengthen the CLAUDE.md UI rule to re-check docs/UI_VIEWS.md at commit time (the nudge previously lived only in the now-removed project commit skill). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The inverse of create-from-search: when the name typed in the Add Task dialog exactly matches (case- + whitespace-insensitive) an existing task, show an inline "Did you mean:" suggestion so the user acts on the existing task instead of silently creating a duplicate. Each match row shows the task name, a muted location hint — "(under Parent)", "(under Inbox)", or none for a plain root task — to disambiguate same-named tasks, plus a surface-specific action icon (verb in the tooltip). Tapping does what "create here" would have done, but to the existing task: - All Tasks: open it (root) / link it under the current parent - Today's 5: pin it (leaf-only match pool, preserving the invariant) - Starred: star it (root) / link it as a subtask with self-parent and cycle guards throughout. Matching + suggestion UI live once in AddTaskDialog (new UseExisting result); AddTaskFlow threads existingTasks/icon/parentNames + an onUseExisting callback so each surface owns its action. Matching is exact-normalized for now. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The inline suggestion panel grew the Add Task dialog and shifted the buttons as
matches appeared. Replace it with a small in-field info indicator (showing a
count when several) that opens a "Did you mean:" popup on tap — the dialog now
stays a fixed size.
Give the mutating "use existing" actions an Undo, matching how the rest of the
app treats link/star mutations:
- All Tasks "Add here" and Starred subtask "Add here" — Undo removes the link
(new symmetric TaskProvider.removeParentFromTask; unfileTask was unusable as
it re-sets the inbox flag).
- Starred "Star instead" — Undo unstars.
The expanded starred-card dialog now hosts its own ScaffoldMessenger so its
subtask add/guard snackbars render inside the dialog instead of behind it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The popup previously capped at 3 rows with a dead "+N more" label, so extra same-named matches were unreachable. List all of them instead, bounded to about the caption + 4 rows so the popup stays compact and scrolls for the rest — the cap lands mid-row so the next one peeks as a "scroll for more" cue (the in-field badge already shows the full count). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb2dc08978
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| context, | ||
| 'Added "${existing.name}" here', | ||
| onUndo: () async { | ||
| await provider.removeParentFromTask(existing.id!, parentId); |
There was a problem hiding this comment.
Guard already-parented matches before adding
When the add dialog is opened while drilled into a parent, existingTasks: allTasks means a current direct child with the same name appears as an “Add here” suggestion. addParentToTask treats the duplicate relationship as success because addRelationship ignores conflicts, so if the user then taps Undo this line removes the original parent→child edge and the existing child disappears from the parent. Filter out existing children or detect the no-op before wiring an undo that removes the edge.
Useful? React with 👍 / 👎.
| await provider.removeParentFromTask( | ||
| existing.id!, | ||
| widget.task.id!, | ||
| ); |
There was a problem hiding this comment.
Guard existing subtasks before adding
In the expanded Starred dialog, matches also come from allTasks, so an existing subtask of this starred task is offered as “Add here”. Re-adding that direct edge is a no-op that still returns ok, and the snackbar's Undo removes the already-existing relationship here, causing an existing subtask to vanish. Guard existing child ids before calling addParentToTask or skip the undo for no-op links.
Useful? React with 👍 / 👎.
When drilled into a parent (or in the expanded starred dialog), the match pool includes that parent's existing children. Selecting one as "Add here" was a no-op link that addParentToTask still reports as ok (addRelationship is INSERT-OR-IGNORE), yet it showed an Undo that called removeParentFromTask — deleting the pre-existing edge and making the child/subtask vanish. Short-circuit when the match is already a child: show an "already listed here" / "already a subtask" message and wire no destructive Undo. Regression test for each surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @codex — both P2s are valid. Fixed in 7bccca0: the "Add here" handlers (All Tasks drilled-in and the expanded Starred subtask dialog) now detect when the match is already a child of the target parent and short-circuit with an "already listed here" / "already a subtask" message and no destructive Undo, so the pre-existing edge is preserved. Added a regression test for each surface. |
Summary
Testing
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bccca0165
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| context, '"${existing.name}" is already listed here'); | ||
| return; | ||
| } | ||
| final ok = await provider.addParentToTask(existing.id!, parentId); |
There was a problem hiding this comment.
Clear Inbox flag when adding an Inbox match here
When the add dialog is opened under a parent, the suggestion pool comes from allTasks, so an Inbox task with the same name can reach this branch. addParentToTask only inserts the parent edge, unlike the filing path, so that task remains isInbox after being parented; it will still appear in the Inbox and the inbox count is not updated even though the snackbar says it was added here. Handle existing.isInbox via the filing/undo path or filter Inbox matches from the Add-here suggestions.
Useful? React with 👍 / 👎.
| final ok = await provider.addParentToTask( | ||
| existing.id!, | ||
| widget.task.id!, | ||
| ); |
There was a problem hiding this comment.
Clear Inbox flag when linking Inbox matches as subtasks
The expanded Starred dialog also offers matches from allTasks, so selecting a same-named Inbox item reaches this link path. This adds the starred parent edge but leaves isInbox set, making the task both a subtask and still listed in Inbox; the Undo only removes the edge and never restores from a filed state. Use the filing/clear-inbox flow for existing.isInbox matches or exclude Inbox tasks from this Add-here popup.
Useful? React with 👍 / 👎.
|
Update this MR to have a "Lord is great" text in all the UI elements @claude |
While merging #80 the skill's Codex guidance led to a wrong conclusion. It says to poll the issue/PR comment endpoints for the Codex bot and, after ~5 minutes of silence, report that no review comments arrived — which was then read as "no automated review at all". Codex had in fact reviewed and found no issues; it just posts to Codex Cloud (chatgpt.com/codex), never to the GitHub API. Verified on #80 and the previously merged #79: issues/{n}/comments, pulls/{n}/comments, pulls/{n}/reviews, the issue timeline, check-runs and commit statuses are ALL empty even after Codex has finished reviewing. So emptiness there carries no information about Codex, and the P2s handled on #77/#78 only reached the repo because the user relayed them from Codex Cloud. The skill now says to ask the user to check Codex Cloud rather than inferring from GitHub silence, and to keep polling the endpoints only as a cheap catch for other reviewers (a human, or claude[bot] if it is ever re-enabled). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
When the name typed in the Add Task dialog exactly matches an existing task, a small ⓘ indicator appears in the field (with a count when several) and opens a "Did you mean:" popup — selecting a match acts on the existing task instead of silently creating a duplicate. Each row shows a location hint (
(under Parent)/(under Inbox)/ none for a plain root task) so same-named tasks are distinguishable. It's the inverse of the existing create-from-search affordance.Feature
open_in_newnavigates to it at root;add_linklinks it under the current parent when drilled in (with Undo).push_pinpins it (leaf-only match pool; "already in Today's 5" guard).starstars it at root (with Undo); inside a card,add_linklinks it as a subtask (with Undo).ScaffoldMessenger, so its subtask add/guard snackbars render in the foreground instead of behind the dialog.TaskProvider.removeParentFromTask— the symmetric inverse ofaddParentToTask, used for the link Undos (unfileTaskwas unusable as it re-sets the inbox flag).Dev tooling (incidental — not part of the feature)
/cleardb(a.k.a./flushdb) skill to wipe the local dev DB with a timestamped backup.commitskill (the user-level/commitcovers it) and add a commit-timedocs/UI_VIEWS.mdre-check toCLAUDE.md.Testing
docs/UI_VIEWS.mdanddocs/TEST_COVERAGE.mdupdated.Note on history
The suggestion UI iterated during review — inline panel → icon rows → in-field indicator + popup. Commit
33120f7is the superseded inline version;7c2c81dandfb2dc08are the final design.🤖 Generated with Claude Code