aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-25 10:21:54 +0200
committerDanilo M. <danix@danix.xyz>2026-08-25 10:21:54 +0200
commitbfc5f78758f527f969204b61ad2317470a454e59 (patch)
treec6e1fccc21ac42d6614cb25ea257a617bb2dccad
parent0dee84687f65cb200e46d2f612c9c94887a126eb (diff)
downloadqtmaildir-bfc5f78758f527f969204b61ad2317470a454e59.tar.gz
qtmaildir-bfc5f78758f527f969204b61ad2317470a454e59.zip
docs(backlog): correct item 164, the first cause was wrong
The entry blamed a missing drafts helper. Neither half of that was true. NOT_ARRIVALS in qtmaildirconf.py is ("sent", "drafts"), so sent_folders() already returns both; the function name says sent and its contents do not, which is what made the wrong reading plausible. Run against the real config it returns every account's drafts folder, and notmuch count over the carve-out query and the affected message id returns 1: the query the hook builds MATCHES the draft. The folder list and the query are correct and the fix is not there. A second theory is also recorded as dead. 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. What is established: the carve-out is scoped to tag:new, the draft carries inbox, and tag:new is 0, so it was never in scope when the hook ran. The installed hooks are symlinks into this repository, verified rather than assumed, so the code read is the code that runs. What is not established is which pass tagged the file. The likely shape is an ordering one, since item 158 indexes a draft from the application itself and a file already known to the database is not new on the next pass, but that is a third hypothesis and the first two were both wrong. The entry now calls for a reproducer driving the real sequence before any code is written, since the hook tags real mail unattended every ten minutes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UUQS6n3cmsFrsjCNmwNtf8
-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.