diff options
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 75 |
1 files changed, 43 insertions, 32 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index 6041e28..b390bab 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -237,7 +237,7 @@ taking that too literally. | 162 | Delete fails while a sync is renaming the file underneath it | defect | S | open, 2026-08-25, found by hand. `Cannot move <file> to <folder>`. NOT a Delete defect and not item 158's: mbsync renames an uploaded file to add its `,U=<uid>` infix, and notmuch keeps the pre-`U=` name until that sync's `notmuch new` runs, so `moveMessages` renames a path that no longer exists. Truthful, harmless and SELF-HEALING, which is why it reads as intermittent: verified a ghost present mid-sync and gone after. The message names a folder as though the folder were the problem. Delete reaches the real mail server, so read `CLAUDE.md` before touching it | | 163 | The message pane shows a stale path and reports the message unreadable | defect | S | open, 2026-08-25, found by hand. Same root as 162 and a DIFFERENT site: the model keeps the filename a row was loaded with, mbsync renames the file to add `,U=<uid>`, and `MimeParser` then opens a path that no longer exists and honestly reports "could not be parsed". notmuch is CORRECT by then; the UI is behind, so 162's fix (refuse the write while a sync runs) does not touch this. The read path needs to recover rather than refuse | -| 164 | Every newly synced draft carries `inbox` | defect | S | open, 2026-08-25, found by hand. Measured `draft inbox unread` on a draft this application wrote. `strip_inbox_from_sent()` in `assets/hooks/post-new` reads `qtmaildirconf.sent_folders()` only, and `qtmaildirconf.py` has no drafts equivalent, so the carve-out never covers a drafts folder. **Contradicts a shipped 0.27.0 changelog entry** claiming both are kept out of the inbox, so it is a documentation defect as well. Item 158's measurement was correct and did not cover this: `index_file` assigns no tags, but mbsync's upload and the next `notmuch new` re-tag the file | +| 164 | A draft this application saved keeps `inbox` | defect | S | open, 2026-08-25, **cause corrected 2026-08-25**. The first diagnosis blamed a missing drafts helper and was WRONG: `NOT_ARRIVALS` in `qtmaildirconf.py` is `("sent", "drafts")`, the folder list includes every account's drafts folder, and `notmuch count` confirms the carve-out query MATCHES the affected draft. The carve-out is scoped to `tag:new`, and the draft carries `inbox` while `tag:new` is 0, so it was never in scope when the hook ran. Measured separately: an mbsync-style rename does NOT re-add `new.tags`, so the retag theory is out too. What remains unestablished is WHICH pass tagged it; establish that before writing code | Sizes are rough: XS under an hour, S a sitting, M a session. @@ -1434,45 +1434,56 @@ reporting. `recoverStaleThread()` already exists for the neighbouring problem since two files can carry the same id. - The placeholder wording is correct and should stay for the genuine case. -## 164. Every newly synced draft carries `inbox` +## 164. A draft this application saved keeps `inbox` -**Observed (developer, 2026-08-25):** `notmuch search --output=tags` on a draft -this application had just written reported `draft inbox unread`. +**Observed (developer, 2026-08-25):** `notmuch search --output=tags` on a +draft this application had just written reported `draft inbox unread`. -**Cause (verified in the hook):** `strip_inbox_from_sent()` in -`assets/hooks/post-new` builds its query from -`qtmaildirconf.sent_folders()`, and `assets/hooks/qtmaildirconf.py` exposes -`sent_folders()` and `sent_query()` and **no drafts equivalent**. The -carve-out therefore never covers a drafts folder, and every draft that -completes a sync round trip is tagged `inbox` by `notmuch new` like any other -newly indexed file. +**The first cause recorded here was WRONG, and the correction is the useful +part.** It said `strip_inbox_from_sent()` reads a sent-only folder list and +that `qtmaildirconf.py` has no drafts equivalent. Neither is true: -**Item 158's measurement was right and did not cover this.** That item -measured `notmuch_database_index_file` assigning NO tags, which is true and is -why a freshly autosaved draft is clean. The tagging happens later: mbsync -uploads the file, renames it, and the next `notmuch new` indexes it as new -mail. +- `NOT_ARRIVALS` is `("sent", "drafts")`, so `sent_folders()` already returns + both. The name says "sent" and the contents do not, which is what made the + wrong reading plausible. +- Run against the real config it returns every account's drafts folder. +- `notmuch count "(<carve-out query>) and id:<the draft>"` returns **1**. The + query the hook builds MATCHES the affected message. -**This contradicts shipped documentation.** The 0.27.0 changelog says "Sent -mail and drafts no longer appear in the inbox", and the drafts half has never -been true. Fixing the hook and correcting the entry are one item. +So the folder list and the query are correct, and the fix is not there. -**Approach.** A `drafts_folders()` / `drafts_query()` pair beside the sent -ones, and one more carve-out call, or a single function taking the folder kind -so the two cannot drift. +**What is actually established.** + +- The carve-out is scoped to `SCOPE = "tag:new"` (`post-new:106`). +- The affected draft carries `inbox`, and `notmuch count tag:new` is **0**. +- The installed hooks are SYMLINKS into this repository, so the code read is + the code that runs. Verified rather than assumed. +- An mbsync-style rename does **not** re-apply `new.tags`: measured in a + throwaway database, a file renamed to add `,U=4` and reindexed kept the tags + it had. The "the rename retags it" theory is therefore also out. + +**What is NOT established, and must be before any code is written:** which +pass put `inbox` on this file, and why it was not carrying `tag:new` when the +hook's carve-out ran. The likely shape is an ordering one, since item 158 +indexes a draft from the application itself, outside `notmuch new`, and a file +already known to the database is not a new file on the next pass. But that is +a hypothesis and the last two hypotheses here were both wrong. + +**Approach.** Reproduce first, in a throwaway database, driving the real +sequence: index the file the way `indexDraftFile()` does, then rename it the +way mbsync does, then `notmuch new`, then the hook. Only then decide whether +the fix belongs in the hook's scope, in item 158's indexing, or in neither. **Constraints.** -- **This is a two-repo change in spirit but not in fact.** The hooks moved - into this repository in 0.27.0, so `mailrules.py` is not involved and the - shared-format procedure does not apply. Check that before assuming - otherwise: `CLAUDE.md` still describes the hook as shipping from `mailctl` - in places. -- The hook must keep refusing to consume `tag:new` when a carve-out fails. - Clearing the marker while the rules did not run orphans that mail - permanently, which the sent half already gets right. +- **The hook tags real mail unattended every ten minutes.** Nothing here is + worth a speculative change. +- The 0.27.0 changelog claims sent mail and drafts both stay out of the inbox. + Whatever the cause, that claim is currently false for drafts and the entry + needs correcting with the fix. - Only `inbox` may be touched. A draft legitimately carries `draft` and `unread`, and `maildir.synchronize_flags` means removing `unread` rewrites the filename and reaches the server. -- `test_post_new.py` and `test_qtmaildirconf.py` are in the same directory and - must both be extended; the sent carve-out has tests to copy. +- The hook must keep refusing to consume `tag:new` when a carve-out fails. +- `test_post_new.py` and `test_qtmaildirconf.py` both live beside the hook and + have sent-carve-out tests to copy. |
