aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-28 19:17:06 +0200
committerDanilo M. <danix@danix.xyz>2026-08-28 19:17:06 +0200
commitdb38ca136c45f34f5fa2d342ee180f068073be37 (patch)
treee5ec786f47989bb594fca896802849b365056ddc /docs/superpowers/specs
parentae74237ca11640ceb68887b2c6d09ecf1befb342 (diff)
downloadqtmaildir-db38ca136c45f34f5fa2d342ee180f068073be37.tar.gz
qtmaildir-db38ca136c45f34f5fa2d342ee180f068073be37.zip
docs: close items 170, 176 and 177
Diffstat (limited to 'docs/superpowers/specs')
-rw-r--r--docs/superpowers/specs/2026-08-28-thread-row-identity-design.md29
1 files changed, 26 insertions, 3 deletions
diff --git a/docs/superpowers/specs/2026-08-28-thread-row-identity-design.md b/docs/superpowers/specs/2026-08-28-thread-row-identity-design.md
index 3d972cb..09aed6b 100644
--- a/docs/superpowers/specs/2026-08-28-thread-row-identity-design.md
+++ b/docs/superpowers/specs/2026-08-28-thread-row-identity-design.md
@@ -319,9 +319,32 @@ load in flight, which is the trap item 119's rule-count work already recorded.
Delete and Archive absent on reply rows.
3. **Worker.** `loadThreadDigest`, the value struct, its own generation.
4. **Pane.** The dashboard widget, stacked with the message view.
-5. **Item 176.** The undo fix, which this makes easier: a thread action's undo
- covering the whole thread is now honestly what the user asked for, so the
- remaining exposure is only the multi-row message case.
+5. **Item 176.** The undo fix.
+
+ **This step's reasoning was wrong, and what shipped is the opposite of what
+ is written below. Corrected 2026-08-28, after the work was done.** The
+ paragraph said: a thread action's undo covering the whole thread is now
+ honestly what the user asked for, so the remaining exposure is only the
+ multi-row message case. That reads as sound and is not, which is why it is
+ corrected here rather than deleted: someone will otherwise reason their way
+ back to it.
+
+ The flaw is that "what the user asked for" is the SCOPE of the write, and an
+ undo is not the inverse of a scope, it is the inverse of an EFFECT. The user
+ asking to mark a whole conversation read does not make it honest to mark the
+ whole conversation unread afterwards, because most of it was already read
+ before they touched anything. Item 177 makes the thread the honest scope of
+ the WRITE and changes nothing about the undo: measured on a conversation of
+ 44 messages holding 2 unread, the undo left 43 unread either way. Making a
+ row mean a conversation does not reduce the exposure, it makes the largest
+ case reachable by the ordinary gesture rather than by a submenu entry.
+
+ So `ThreadTagCommand::undo()` restricts its write to the ids
+ `NotmuchWorker::applyTags()` reported as actually changed, exactly as
+ `MessageTagCommand` does. The two share a `TagCommand` base for that reason.
+ `sendThreadTagChange` keeps the thread as its REPAINT scope while taking
+ `onlyMessageIds` for the write, because the card that changed on screen and
+ the messages that changed on disk are genuinely different sets.
This is one coherent change rather than a decomposition candidate. Splitting it
across releases would ship a state where rows mean one thing and actions