things-api 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +4 -4
  2. package/dist/audit/schema.d.ts +8 -0
  3. package/dist/audit/schema.js.map +1 -1
  4. package/dist/cli/commands/area.js +1 -1
  5. package/dist/cli/commands/install-skill.js +9 -2
  6. package/dist/cli/commands/install-skill.js.map +1 -1
  7. package/dist/cli/commands/project.js +1 -1
  8. package/dist/cli/commands/project.js.map +1 -1
  9. package/dist/cli/commands/reads.js +73 -4
  10. package/dist/cli/commands/reads.js.map +1 -1
  11. package/dist/cli/commands/repeat-flags.d.ts +11 -1
  12. package/dist/cli/commands/repeat-flags.js +23 -0
  13. package/dist/cli/commands/repeat-flags.js.map +1 -1
  14. package/dist/cli/commands/todo.js +50 -3
  15. package/dist/cli/commands/todo.js.map +1 -1
  16. package/dist/cli/commands/writes.js +202 -61
  17. package/dist/cli/commands/writes.js.map +1 -1
  18. package/dist/cli/glyphs.js +9 -10
  19. package/dist/cli/glyphs.js.map +1 -1
  20. package/dist/cli/help.js +18 -13
  21. package/dist/cli/help.js.map +1 -1
  22. package/dist/cli/read-driver.d.ts +9 -3
  23. package/dist/cli/read-driver.js +3 -1
  24. package/dist/cli/read-driver.js.map +1 -1
  25. package/dist/cli/render.d.ts +33 -2
  26. package/dist/cli/render.js +60 -9
  27. package/dist/cli/render.js.map +1 -1
  28. package/dist/cli/skill.d.ts +24 -0
  29. package/dist/cli/skill.js +40 -0
  30. package/dist/cli/skill.js.map +1 -1
  31. package/dist/client.d.ts +88 -15
  32. package/dist/client.js +31 -5
  33. package/dist/client.js.map +1 -1
  34. package/dist/contracts.d.ts +13 -2
  35. package/dist/contracts.js +1 -1
  36. package/dist/contracts.js.map +1 -1
  37. package/dist/index.d.ts +5 -4
  38. package/dist/index.js +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/mcp/server.js +187 -36
  41. package/dist/mcp/server.js.map +1 -1
  42. package/dist/model/entities.d.ts +97 -40
  43. package/dist/model/entities.js +0 -4
  44. package/dist/model/entities.js.map +1 -1
  45. package/dist/model/mappers.js +24 -43
  46. package/dist/model/mappers.js.map +1 -1
  47. package/dist/read/area-view.js +6 -6
  48. package/dist/read/area-view.js.map +1 -1
  49. package/dist/read/detail.js +51 -3
  50. package/dist/read/detail.js.map +1 -1
  51. package/dist/read/filter-contract.d.ts +1 -1
  52. package/dist/read/filter-contract.js +3 -0
  53. package/dist/read/filter-contract.js.map +1 -1
  54. package/dist/read/log-boundary.d.ts +46 -2
  55. package/dist/read/log-boundary.js +43 -2
  56. package/dist/read/log-boundary.js.map +1 -1
  57. package/dist/read/predicates.d.ts +13 -0
  58. package/dist/read/predicates.js +13 -0
  59. package/dist/read/predicates.js.map +1 -1
  60. package/dist/read/project-view.js +5 -5
  61. package/dist/read/project-view.js.map +1 -1
  62. package/dist/read/queries.d.ts +12 -0
  63. package/dist/read/queries.js +14 -0
  64. package/dist/read/queries.js.map +1 -1
  65. package/dist/read/search-rank.js +2 -2
  66. package/dist/read/search-rank.js.map +1 -1
  67. package/dist/read/shape.d.ts +30 -26
  68. package/dist/read/shape.js +130 -101
  69. package/dist/read/shape.js.map +1 -1
  70. package/dist/read/snapshot.js +2 -2
  71. package/dist/read/snapshot.js.map +1 -1
  72. package/dist/read/stage.d.ts +36 -18
  73. package/dist/read/stage.js +30 -13
  74. package/dist/read/stage.js.map +1 -1
  75. package/dist/read/views.d.ts +62 -0
  76. package/dist/read/views.js +134 -14
  77. package/dist/read/views.js.map +1 -1
  78. package/dist/surface-copy.d.ts +2 -2
  79. package/dist/surface-copy.js +2 -2
  80. package/dist/write/batch.d.ts +28 -3
  81. package/dist/write/batch.js +162 -46
  82. package/dist/write/batch.js.map +1 -1
  83. package/dist/write/clear-reminder.js +3 -1
  84. package/dist/write/clear-reminder.js.map +1 -1
  85. package/dist/write/clone.d.ts +4 -0
  86. package/dist/write/clone.js +531 -0
  87. package/dist/write/clone.js.map +1 -0
  88. package/dist/write/commands.js +196 -29
  89. package/dist/write/commands.js.map +1 -1
  90. package/dist/write/guards.d.ts +1 -1
  91. package/dist/write/guards.js +44 -12
  92. package/dist/write/guards.js.map +1 -1
  93. package/dist/write/make-repeating-project.d.ts +2 -3
  94. package/dist/write/make-repeating-project.js +2 -73
  95. package/dist/write/make-repeating-project.js.map +1 -1
  96. package/dist/write/move.js +53 -1
  97. package/dist/write/move.js.map +1 -1
  98. package/dist/write/operations.d.ts +134 -11
  99. package/dist/write/operations.js +5 -1
  100. package/dist/write/operations.js.map +1 -1
  101. package/dist/write/pipeline.d.ts +38 -0
  102. package/dist/write/pipeline.js +71 -6
  103. package/dist/write/pipeline.js.map +1 -1
  104. package/dist/write/pre-state.d.ts +41 -0
  105. package/dist/write/pre-state.js +71 -8
  106. package/dist/write/pre-state.js.map +1 -1
  107. package/dist/write/preserve-modified.d.ts +55 -0
  108. package/dist/write/preserve-modified.js +106 -0
  109. package/dist/write/preserve-modified.js.map +1 -0
  110. package/dist/write/promote-clone.d.ts +32 -0
  111. package/dist/write/promote-clone.js +620 -0
  112. package/dist/write/promote-clone.js.map +1 -0
  113. package/dist/write/reorder.js +49 -2
  114. package/dist/write/reorder.js.map +1 -1
  115. package/dist/write/resolution-timestamps.js +29 -7
  116. package/dist/write/resolution-timestamps.js.map +1 -1
  117. package/dist/write/reversibility.js +32 -14
  118. package/dist/write/reversibility.js.map +1 -1
  119. package/dist/write/scope-guard.js +2 -0
  120. package/dist/write/scope-guard.js.map +1 -1
  121. package/dist/write/undo.d.ts +12 -2
  122. package/dist/write/undo.js +145 -22
  123. package/dist/write/undo.js.map +1 -1
  124. package/dist/write/vectors/applescript.js +9 -0
  125. package/dist/write/vectors/applescript.js.map +1 -1
  126. package/dist/write/vectors/simulator.js +242 -10
  127. package/dist/write/vectors/simulator.js.map +1 -1
  128. package/dist/write/vectors/ui-certification.d.ts +1 -1
  129. package/dist/write/vectors/ui-certification.js +10 -3
  130. package/dist/write/vectors/ui-certification.js.map +1 -1
  131. package/dist/write/verify/delta.d.ts +55 -10
  132. package/dist/write/verify/delta.js +73 -10
  133. package/dist/write/verify/delta.js.map +1 -1
  134. package/package.json +1 -1
  135. package/schema/envelope.schema.json +25 -0
  136. package/skills/things-cli/SKILL.md +10 -7
  137. package/skills/things-cli/references/banner.md +1 -1
  138. package/skills/things-cli/references/contracts.md +17 -9
  139. package/skills/things-cli/references/{model.md → data-model.md} +5 -2
  140. package/skills/things-cli/references/ordering.md +2 -0
@@ -20,6 +20,14 @@ export const REVERSIBILITY = {
20
20
  class: "reversible",
21
21
  note: "inverse deletes the duplicated project to the Trash",
22
22
  },
23
+ "todo.clone": {
24
+ class: "reversible",
25
+ note: "inverse trashes the minted clone to-do (single leg); irreversible only if the clone's uuid was never discovered",
26
+ },
27
+ "project.clone": {
28
+ class: "reversible",
29
+ note: "inverse trashes the minted clone project (and the children it carried) to the Trash; irreversible only if the clone's uuid was never discovered",
30
+ },
23
31
  "area.add": {
24
32
  class: "reversible",
25
33
  ack: "permanent",
@@ -58,12 +66,12 @@ export const REVERSIBILITY = {
58
66
  // ---- delete / restore ---------------------------------------------------
59
67
  "todo.delete": {
60
68
  class: "reversible-with-loss",
61
- note: "inverse restores from the Trash but lands in the Inbox DE-SCHEDULED (E15) — the prior list/schedule was not captured by the delete",
69
+ note: "inverse restores from the Trash but lands in the Inbox DE-SCHEDULED (E15) — the prior list/schedule was not captured by the delete. EXCEPTION: deleting a repeating TEMPLATE is IRREVERSIBLE headlessly (the app forbids restoring a template out to a list, AS 301) — no undo token is emitted and the result discloses Trash ▸ Put Back as the only revival (SERDEL S2/S3, ruling 2026-08-13)",
62
70
  },
63
71
  "todo.restore": { class: "reversible", note: "inverse re-deletes the to-do to the Trash" },
64
72
  "project.delete": {
65
73
  class: "reversible",
66
- note: "inverse restores the project IN PLACE (P06) — schedule/area/children keep their state",
74
+ note: "inverse restores the project IN PLACE (P06) — schedule/area/children keep their state. EXCEPTION: deleting a repeating project TEMPLATE is IRREVERSIBLE headlessly (Put Back only) — no undo token, disclosed in the result (ruling 2026-08-13)",
67
75
  },
68
76
  "project.restore": { class: "reversible", note: "inverse re-deletes the project to the Trash" },
69
77
  // ---- field updates ------------------------------------------------------
@@ -164,22 +172,10 @@ export const REVERSIBILITY = {
164
172
  class: "irreversible",
165
173
  note: "a created heading has no headless delete surface (heading delete is interactive-only) — archive it in the app instead",
166
174
  },
167
- "todo.make-repeating": {
168
- class: "irreversible",
169
- note: "making a to-do repeat is an identity REPLACEMENT (UI2-a): the original uuid is destroyed and a new template row is born — there is no un-repeat that restores the original",
170
- },
171
175
  "todo.convert-to-project": {
172
176
  class: "irreversible",
173
177
  note: "converting a to-do to a project is an identity REPLACEMENT (UI2-d): the to-do uuid is destroyed and a new project uuid is born (notes preserved); the app offers no convert-back",
174
178
  },
175
- "project.make-repeating": {
176
- class: "irreversible",
177
- note: "making a project repeat is an identity REPLACEMENT (UIC4-b): the original project uuid is destroyed and a new template project is born (area preserved, start normalized to someday) — there is no un-repeat that restores the original",
178
- },
179
- "project.add-repeating": {
180
- class: "irreversible",
181
- note: "the composite CREATES a project then promotes it (identity replacement, UIC4-b): the created uuid is destroyed by the promote and a new repeating template is born — no automated inverse captures it; delete the resulting repeating project in the app",
182
- },
183
179
  "project.promote-heading": {
184
180
  class: "irreversible",
185
181
  note: "promoting a heading to a project is an identity REPLACEMENT (UI2-d): the heading uuid is destroyed, a new project is promoted into the parent's area and children reparent; no convert-back",
@@ -200,6 +196,23 @@ export const REVERSIBILITY = {
200
196
  class: "conditional",
201
197
  note: "the project's rule mutates in place (identity preserved, UIC2-a); re-drives reschedule with the captured prior rule (same faithfulness boundary as todo.reschedule-repeat) — invertible when the prior rule was captured, decodable, and dialog-expressible; irreversible otherwise. A per-instance reminder time is not restored",
202
198
  },
199
+ // ---- promote-via-clone (make/add-repeating) → undo is trash-both (+restore) --
200
+ "todo.make-repeating": {
201
+ class: "reversible-with-loss",
202
+ note: "promote-via-clone: the minted template + its current instance are the inverse's target — undo TRASHES BOTH (the SERDEL trash-both) and restores the original to-do from the Trash (it lands in the Inbox DE-SCHEDULED, E15). The trashed template leaves no tombstone and its own un-delete is GUI-only (Trash ▸ Put Back), so this undo cannot itself be undone headlessly",
203
+ },
204
+ "project.make-repeating": {
205
+ class: "reversible-with-loss",
206
+ note: "promote-via-clone: undo TRASHES the minted template + its current instance (trash-both) and restores the original project IN PLACE (P06). The trashed template leaves no tombstone and its own un-delete is GUI-only (Put Back), so this undo cannot itself be undone headlessly",
207
+ },
208
+ "todo.add-repeating": {
209
+ class: "reversible",
210
+ note: "the composite creates a to-do then promotes it; undo TRASHES the minted template + its current instance (SERDEL trash-both) — the whole created series moves to the Trash. Re-creating the series is GUI-only (the template leaves no tombstone; use Trash ▸ Put Back), so this undo is not itself headlessly undoable",
211
+ },
212
+ "project.add-repeating": {
213
+ class: "reversible",
214
+ note: "the composite creates a project then promotes it; undo TRASHES the minted template + its current instance (SERDEL trash-both) — the whole created series moves to the Trash. Re-creating the series is GUI-only, so this undo is not itself headlessly undoable",
215
+ },
203
216
  // ---- ui-vector reversible pairs -----------------------------------------
204
217
  "todo.pause-repeat": {
205
218
  class: "reversible",
@@ -217,6 +230,11 @@ export const REVERSIBILITY = {
217
230
  class: "reversible",
218
231
  note: "inverse pauses the project's repeat (UIC2-a): resume clears the paused flag, pause re-sets it",
219
232
  },
233
+ // ---- irreversible: the log-move boundary only advances ------------------
234
+ "log-now": {
235
+ class: "irreversible",
236
+ note: "logging moves completed items into the Logbook by advancing the log-move boundary (TMSettings.manualLogDate); no official surface can rewind it (TIMEZ/settings-stamp laws), so there is no inverse. Nothing is deleted — the items simply become logged",
237
+ },
220
238
  };
221
239
  /** The ops whose class is `irreversible` — must equal undo.ts's IRREVERSIBLE keys. */
222
240
  export function irreversibleOps() {
@@ -1 +1 @@
1
- {"version":3,"file":"reversibility.js","sourceRoot":"","sources":["../../src/write/reversibility.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reversibility.js","sourceRoot":"","sources":["../../src/write/reversibility.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,8 @@ export function evaluateScope(db, op, params, pre, scope) {
26
26
  return blocked("the sidebar area order is global, above any container scope");
27
27
  case "trash.empty":
28
28
  return blocked("emptying the Trash permanently deletes every trashed item library-wide, including ones outside the active scope");
29
+ case "log-now":
30
+ return blocked("logging moves every completed item library-wide into the Logbook, including ones outside the active scope");
29
31
  case "tag.update":
30
32
  return blocked("renaming or re-nesting a tag changes shared state visible to every tagged item across the whole library");
31
33
  case "tag.delete":
@@ -1 +1 @@
1
- {"version":3,"file":"scope-guard.js","sourceRoot":"","sources":["../../src/write/scope-guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope-guard.js","sourceRoot":"","sources":["../../src/write/scope-guard.ts"],"names":[],"mappings":"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"}
@@ -24,7 +24,7 @@ export interface UndoStep {
24
24
  /**
25
25
  * STRUCTURAL axes this step overwrites (getField paths into the decoded
26
26
  * target: `status`, `trashed`, `project`/`project.uuid`, `area`/`area.uuid`,
27
- * `heading.uuid`, `start`/`startDate`/`todaySection`). Before executing,
27
+ * `heading.uuid`, `start`/`startDate`/`today`/`evening`). Before executing,
28
28
  * `checkStepPrecondition` verifies the target's CURRENT value on each still
29
29
  * equals the recorded `observed` after-value — an out-of-band move/status/
30
30
  * schedule change already there blocks rather than being clobbered. Set only
@@ -123,10 +123,20 @@ export declare function scanAuditIntegrity(records: AuditRecord[]): AuditIntegri
123
123
  * The undoable targets to unwind, NEWEST FIRST (undo unwinds a stack).
124
124
  *
125
125
  * - `txn` wins when present: the ONE record whose undo token matches (or an
126
- * empty array — the caller distinguishes not-found from already-undone).
126
+ * empty array — the caller distinguishes not-found from already-undone). The
127
+ * already-undone exclusion below does NOT apply to `--txn`: surgical targeting
128
+ * keeps the loud "already undone" error (runUndo raises it before selecting).
127
129
  * - otherwise the last `last` records, optionally narrowed to actor `by`
128
130
  * (exact match; "*"/undefined = every actor). `by` NEVER matches an
129
131
  * `undo:<actor>` record — those are excluded from undoability entirely.
132
+ *
133
+ * ALREADY-UNDONE records are EXCLUDED from `--last`/`--by` selection (Change 3),
134
+ * so `--last N` always means "the N newest NOT-YET-undone mutations" and a
135
+ * repeated `undo --last N` walks progressively deeper into history instead of
136
+ * re-selecting the same records (whose inverses would trip the precondition
137
+ * guard). IRREVERSIBLE records are NOT excluded — they were never undone, so
138
+ * they keep COUNTING toward N and being reported every pass (reaching silently
139
+ * past one to undo something older would be a dangerous surprise).
130
140
  */
131
141
  export declare function selectUndoTargets(records: AuditRecord[], selector?: UndoSelector): AuditRecord[];
132
142
  export declare const IRREVERSIBLE: Partial<Record<string, string>>;
@@ -27,7 +27,7 @@
27
27
  * `observed` after-values: CONTENT fields (title/notes/deadline/reminder/
28
28
  * tags, keyed 1:1 to the inverse step's params) and the STRUCTURAL axes each
29
29
  * step names in its `guardFields` — status, container (project/area/
30
- * heading), schedule (start/startDate/todaySection), and trashed state. A
30
+ * heading), schedule (start/startDate/today/evening), and trashed state. A
31
31
  * field an out-of-band edit already moved is NOT clobbered — the step is
32
32
  * refused (blocked, naming what moved) and unwinding stops.
33
33
  * `--acknowledge-out-of-band-changes` (MCP `acknowledge_out_of_band_changes`)
@@ -46,6 +46,7 @@ import { localToday } from "../model/dates.js";
46
46
  import { getField } from "./verify/delta.js";
47
47
  import { isRepeatingTemplate, loadTarget } from "./pre-state.js";
48
48
  import { isUiDriveOp } from "./operations.js";
49
+ import { restoreModDates } from "./preserve-modified.js";
49
50
  import { ruleToInverseParams } from "./repeat-rule.js";
50
51
  import { runMutation } from "./pipeline.js";
51
52
  import { runReorder } from "./reorder.js";
@@ -141,14 +142,39 @@ export function scanAuditIntegrity(records) {
141
142
  }
142
143
  return { orphanedIntents, newestOrphanIntent };
143
144
  }
145
+ /**
146
+ * The set of mutation undo-tokens that have ALREADY been undone: derived from
147
+ * the inverse records on the trail (an `ok` record under an `undo:*` actor whose
148
+ * `undoOf` back-references the mutation it reversed). This is the same linkage
149
+ * the `--txn` already-undone check reads, generalized to a set.
150
+ */
151
+ function undoneTokens(records) {
152
+ const undone = new Set();
153
+ for (const r of records) {
154
+ if (r.result === "ok" && r.actor.startsWith("undo:") && typeof r.undoOf === "string") {
155
+ undone.add(r.undoOf);
156
+ }
157
+ }
158
+ return undone;
159
+ }
144
160
  /**
145
161
  * The undoable targets to unwind, NEWEST FIRST (undo unwinds a stack).
146
162
  *
147
163
  * - `txn` wins when present: the ONE record whose undo token matches (or an
148
- * empty array — the caller distinguishes not-found from already-undone).
164
+ * empty array — the caller distinguishes not-found from already-undone). The
165
+ * already-undone exclusion below does NOT apply to `--txn`: surgical targeting
166
+ * keeps the loud "already undone" error (runUndo raises it before selecting).
149
167
  * - otherwise the last `last` records, optionally narrowed to actor `by`
150
168
  * (exact match; "*"/undefined = every actor). `by` NEVER matches an
151
169
  * `undo:<actor>` record — those are excluded from undoability entirely.
170
+ *
171
+ * ALREADY-UNDONE records are EXCLUDED from `--last`/`--by` selection (Change 3),
172
+ * so `--last N` always means "the N newest NOT-YET-undone mutations" and a
173
+ * repeated `undo --last N` walks progressively deeper into history instead of
174
+ * re-selecting the same records (whose inverses would trip the precondition
175
+ * guard). IRREVERSIBLE records are NOT excluded — they were never undone, so
176
+ * they keep COUNTING toward N and being reported every pass (reaching silently
177
+ * past one to undo something older would be a dangerous surprise).
152
178
  */
153
179
  export function selectUndoTargets(records, selector = {}) {
154
180
  const undoable = undoableRecords(records);
@@ -156,9 +182,11 @@ export function selectUndoTargets(records, selector = {}) {
156
182
  const match = undoable.find((r) => undoToken(r) === selector.txn);
157
183
  return match === undefined ? [] : [match];
158
184
  }
185
+ const undone = undoneTokens(records);
186
+ const notUndone = undoable.filter((r) => !undone.has(undoToken(r)));
159
187
  const byActor = selector.by === undefined || selector.by === "*"
160
- ? undoable
161
- : undoable.filter((r) => r.actor === selector.by);
188
+ ? notUndone
189
+ : notUndone.filter((r) => r.actor === selector.by);
162
190
  return byActor.slice(-Math.max(1, selector.last ?? 1)).toReversed();
163
191
  }
164
192
  /**
@@ -209,17 +237,13 @@ export const IRREVERSIBLE = {
209
237
  "area.delete": "areas are deleted permanently — there is nothing to restore (A25)",
210
238
  "tag.delete": "tags are deleted permanently — assignments already cascaded (A26)",
211
239
  "trash.empty": "emptying the Trash hard-deletes every row — nothing to restore (A27)",
240
+ "log-now": "logging advances the log-move boundary (manualLogDate) to move completed items into the " +
241
+ "Logbook; no official surface can rewind the boundary (TIMEZ/settings-stamp), so there is " +
242
+ "no inverse — nothing is deleted, the items are simply logged",
212
243
  "project.add-heading": "a created heading can only be removed by deleting it, which has no headless surface " +
213
244
  "(heading delete is interactive-only) — archive it in the app instead",
214
- "todo.make-repeating": "making a to-do repeat is an identity replacement (UI2-a): the original uuid is destroyed " +
215
- "and a new template row is born — there is no un-repeat that restores the original",
216
245
  "todo.convert-to-project": "converting a to-do to a project is an identity replacement (UI2-d): the to-do uuid is " +
217
246
  "destroyed and a new project is born — the app offers no convert-back",
218
- "project.make-repeating": "making a project repeat is an identity replacement (UIC4-b): the original project uuid is " +
219
- "destroyed and a new template project is born — there is no un-repeat that restores the original",
220
- "project.add-repeating": "the composite creates a project then promotes it (identity replacement, UIC4-b): the " +
221
- "created uuid is destroyed by the promote and a new repeating template is born — delete the " +
222
- "resulting repeating project in the app",
223
247
  "project.promote-heading": "promoting a heading to a project is an identity replacement (UI2-d): the heading uuid is " +
224
248
  "destroyed and a new project is born — no convert-back",
225
249
  "project.move-heading-to-project": "moving a heading to another project is app-reversible (move it back) but has no wired " +
@@ -264,13 +288,15 @@ function decodedRuleOf(record) {
264
288
  }
265
289
  /**
266
290
  * Reconstruct the scheduling step that restores a to-do's pre-op placement
267
- * from the captured pre-values (start / startDate / todaySection / reminder).
291
+ * from the captured pre-values (start / startDate / evening-marker / reminder).
268
292
  */
269
293
  function scheduleSteps(uuid, record, todayIso, op = "todo.update") {
270
294
  const notes = [];
271
295
  const start = preField(record, "start");
272
296
  const startDate = preField(record, "startDate");
273
- const section = preField(record, "todaySection");
297
+ // Pre-op Today placement rides the presence-keyed `evening` marker (the retired
298
+ // `todaySection` word is gone): `evening === true` ⇔ the former "evening".
299
+ const wasEvening = preField(record, "evening") === true;
274
300
  const reminder = preField(record, "reminder");
275
301
  // start === "inbox" is a to-do-only pre-state (projects never live in the
276
302
  // Inbox), so the move step below stays a todo.move.
@@ -285,10 +311,10 @@ function scheduleSteps(uuid, record, todayIso, op = "todo.update") {
285
311
  // startDate == today maps back to the today/evening keywords (which also
286
312
  // keep the reminder-clear path open); other dates restore literally.
287
313
  if (startDate === todayIso)
288
- when = section === "evening" ? "evening" : "today";
314
+ when = wasEvening ? "evening" : "today";
289
315
  else {
290
316
  when = startDate;
291
- if (section === "evening") {
317
+ if (wasEvening) {
292
318
  notes.push("the item was a STALE evening entry (past startDate) — the date is restored but " +
293
319
  "the evening bucket cannot be (bucket placement is today-only)");
294
320
  }
@@ -559,6 +585,71 @@ export function planUndo(record, now, allRecords = [], current) {
559
585
  notes,
560
586
  };
561
587
  }
588
+ // A clone (todo.clone / project.clone): the summary record heads the txn whose
589
+ // legs (the base add, checklist checks, terminal-state flips) are ordinary
590
+ // `leg`-role mutations excluded from independent undo. The single undoable unit
591
+ // is the whole clone, and its inverse is trashing the minted root — a project
592
+ // clone's minted children ride to the Trash with it (shallow delete), so no
593
+ // per-child inverse is replayed (the child uuids are captured on the record for
594
+ // the audit trail, not for undo).
595
+ case "todo.clone": {
596
+ if (uuid === null)
597
+ return irreversible("the clone's uuid was never discovered");
598
+ return {
599
+ target,
600
+ kind: "invertible",
601
+ steps: [{ op: "todo.delete", params: { uuid } }],
602
+ notes,
603
+ };
604
+ }
605
+ case "project.clone": {
606
+ if (uuid === null)
607
+ return irreversible("the clone's uuid was never discovered");
608
+ notes.push("the cloned project (and every child it minted) moves to the Trash");
609
+ return {
610
+ target,
611
+ kind: "invertible",
612
+ steps: [{ op: "project.delete", params: { uuid } }],
613
+ notes,
614
+ };
615
+ }
616
+ // Promote-via-clone (make/add-repeating): the SUMMARY record captured the
617
+ // minted template, its current instance, and — for make-repeating — the
618
+ // trashed original. Undo is the SERDEL trash-both (delete BOTH rows — a
619
+ // template delete is a plain, publicly-allowed delete now, ruling 2026-08-13),
620
+ // then (make-repeating only) restore the original from the Trash. This undo
621
+ // cannot itself be undone headlessly — a trashed template's only revival is
622
+ // the app's Trash ▸ Put Back (SERDEL S2/S3) — disclosed here.
623
+ case "todo.make-repeating":
624
+ case "project.make-repeating":
625
+ case "todo.add-repeating":
626
+ case "project.add-repeating": {
627
+ const obs = record.observed ?? {};
628
+ const templateUuid = obs["templateUuid"];
629
+ if (typeof templateUuid !== "string") {
630
+ return irreversible("the minted template uuid was not captured on this record — remove the repeating " +
631
+ "series in the Things app (delete the template, then its current instance)");
632
+ }
633
+ const kind = record.op.startsWith("project.") ? "project" : "todo";
634
+ const deleteOp = kind === "project" ? "project.delete" : "todo.delete";
635
+ const restoreOp = kind === "project" ? "project.restore" : "todo.restore";
636
+ const steps = [{ op: deleteOp, params: { uuid: templateUuid } }];
637
+ const instanceUuid = obs["instanceUuid"];
638
+ if (typeof instanceUuid === "string" && instanceUuid !== templateUuid) {
639
+ steps.push({ op: deleteOp, params: { uuid: instanceUuid } });
640
+ }
641
+ const originalUuid = obs["originalUuid"];
642
+ if (typeof originalUuid === "string") {
643
+ steps.push({ op: restoreOp, params: { uuid: originalUuid } });
644
+ notes.push(kind === "todo"
645
+ ? "the original to-do is restored from the Trash — it lands in the Inbox de-scheduled (E15)"
646
+ : "the original project is restored from the Trash in place (P06)");
647
+ }
648
+ notes.push("removing the series trashes the minted template AND its current instance (trash-both, " +
649
+ "SERDEL); this undo cannot itself be undone headlessly — a trashed template's only " +
650
+ "revival is the app's Trash ▸ Put Back");
651
+ return { target, kind: "invertible", steps, notes };
652
+ }
562
653
  case "area.add": {
563
654
  if (uuid === null)
564
655
  return irreversible("the created uuid was never discovered");
@@ -735,7 +826,8 @@ export function planUndo(record, now, allRecords = [], current) {
735
826
  const requestedWhen = (record.requested["when"] ?? record.requested["reminder"]) !== undefined;
736
827
  // Schedule axes the restore overwrites — guarded against the recorded
737
828
  // after-state so an out-of-band re-schedule blocks rather than clobbers.
738
- const scheduleGuard = ["start", "startDate", "todaySection"];
829
+ // Today placement is the `today`/`evening` markers (todaySection retired).
830
+ const scheduleGuard = ["start", "startDate", "today", "evening"];
739
831
  let scheduleMerged = false;
740
832
  if (requestedWhen) {
741
833
  const schedule = scheduleSteps(uuid, record, todayIso);
@@ -799,7 +891,7 @@ export function planUndo(record, now, allRecords = [], current) {
799
891
  op: "project.update",
800
892
  params,
801
893
  ...(projectScheduleMerged && {
802
- options: { guardFields: ["start", "startDate", "todaySection"] },
894
+ options: { guardFields: ["start", "startDate", "today", "evening"] },
803
895
  }),
804
896
  },
805
897
  ],
@@ -996,10 +1088,10 @@ export function planUndo(record, now, allRecords = [], current) {
996
1088
  // the generalized WG-7 guard would refuse (and re-completing is wrong)
997
1089
  // otherwise. A creation-date restore is status-safe.
998
1090
  if (typeof stoppedPre === "string")
999
- patch["completionDate"] = stoppedPre;
1091
+ patch["completedAt"] = stoppedPre;
1000
1092
  if (typeof createdPre === "string")
1001
- patch["creationDate"] = createdPre;
1002
- if (patch["completionDate"] === undefined && patch["creationDate"] === undefined) {
1093
+ patch["createdAt"] = createdPre;
1094
+ if (patch["completedAt"] === undefined && patch["createdAt"] === undefined) {
1003
1095
  return irreversible("the pre-op timestamps were not captured");
1004
1096
  }
1005
1097
  notes.push("timestamps restore at DAY precision (noon local) — the original sub-day time is " +
@@ -1326,7 +1418,7 @@ export function planUndo(record, now, allRecords = [], current) {
1326
1418
  "date to attach to; re-schedule it, then re-add the reminder");
1327
1419
  }
1328
1420
  const when = startDate === todayIso
1329
- ? current.todaySection === "evening"
1421
+ ? current.derived.evening === true
1330
1422
  ? "evening"
1331
1423
  : "today"
1332
1424
  : startDate;
@@ -1464,7 +1556,8 @@ const AXIS_LABEL = {
1464
1556
  "heading.uuid": "heading",
1465
1557
  start: "schedule",
1466
1558
  startDate: "schedule",
1467
- todaySection: "schedule",
1559
+ today: "schedule",
1560
+ evening: "schedule",
1468
1561
  };
1469
1562
  /**
1470
1563
  * NOT GUARDED (no captured after-state to compare against, so left unguarded —
@@ -1614,6 +1707,14 @@ export async function runUndo(deps, auditDirPath, options = {}, onItem) {
1614
1707
  break;
1615
1708
  }
1616
1709
  }
1710
+ // Preview the symmetric umd-restore (a --preserve-modified original).
1711
+ if (record.preModDates !== undefined) {
1712
+ const n = Object.values(record.preModDates).filter((v) => typeof v === "number").length;
1713
+ if (n > 0) {
1714
+ plan.notes.push(`would restore the modification date on ${n} row(s) after the inverse (this undo ` +
1715
+ "stays off the changes/watch timeline, symmetric with the --preserve-modified original)");
1716
+ }
1717
+ }
1617
1718
  item = { plan, results: preview, outcome: "dry-run" };
1618
1719
  }
1619
1720
  else {
@@ -1670,6 +1771,28 @@ export async function runUndo(deps, auditDirPath, options = {}, onItem) {
1670
1771
  break;
1671
1772
  }
1672
1773
  }
1774
+ // Symmetric umd-restore (2026-08-13 ruling): a forward op that ran with
1775
+ // --preserve-modified recorded each touched row's pre-write
1776
+ // userModificationDate on `preModDates`. After the inverse legs land, put
1777
+ // those umd values back (floor(preUmd)) so the UNDO is ALSO off the
1778
+ // changes/watch timeline — matching the forward op's silence. Best-effort:
1779
+ // a failed restore is disclosed, never fatal (the inverse already stands).
1780
+ // Records without preModDates (the default) are untouched.
1781
+ if (!failed && record.preModDates !== undefined) {
1782
+ const umdTargets = Object.entries(record.preModDates)
1783
+ .filter((e) => typeof e[1] === "number")
1784
+ .map(([tUuid, preUmd]) => ({ uuid: tUuid, preUmd }));
1785
+ if (umdTargets.length > 0) {
1786
+ const restore = await restoreModDates(deps.db, deps.vectors, umdTargets);
1787
+ if (restore.restored > 0) {
1788
+ plan.notes.push(`restored the modification date on ${restore.restored} row(s) so this undo stays ` +
1789
+ "off the changes/watch timeline (symmetric with the --preserve-modified original)");
1790
+ }
1791
+ for (const f of restore.failures) {
1792
+ plan.notes.push(`could not restore the modification date on ${f.uuid}: ${f.detail} (non-fatal)`);
1793
+ }
1794
+ }
1795
+ }
1673
1796
  const okCount = results.filter((r) => r.kind === "ok").length;
1674
1797
  item = {
1675
1798
  plan,