aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md75
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.