things-api 0.17.0 → 0.19.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.
- package/README.md +16 -9
- package/deputy/AppIcon.icns +0 -0
- package/deputy/VERSION +1 -0
- package/deputy/helpers-Info.plist +27 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Info.plist +22 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Resources/AppIcon.icns +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/_CodeSignature/CodeResources +128 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Info.plist +27 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Resources/AppIcon.icns +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/_CodeSignature/CodeResources +137 -0
- package/deputy/reader/Info.plist +22 -0
- package/deputy/reader/entitlements.plist +45 -0
- package/deputy/reader/main.swift +580 -0
- package/deputy/src/main.swift +133 -0
- package/deputy/src/osascript.swift +102 -0
- package/deputy/src/server.swift +373 -0
- package/deputy/src/sqlite.swift +121 -0
- package/deputy/src/tcc.swift +58 -0
- package/dist/audit/schema.d.ts +13 -1
- package/dist/audit/schema.js.map +1 -1
- package/dist/capability.d.ts +160 -0
- package/dist/capability.js +416 -0
- package/dist/capability.js.map +1 -0
- package/dist/cli/commands/doctor.js +181 -2
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/helpers.d.ts +17 -0
- package/dist/cli/commands/helpers.js +275 -0
- package/dist/cli/commands/helpers.js.map +1 -0
- package/dist/cli/commands/op-result.d.ts +15 -0
- package/dist/cli/commands/op-result.js +45 -0
- package/dist/cli/commands/op-result.js.map +1 -0
- package/dist/cli/commands/reads.js +47 -1
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/setup.d.ts +9 -0
- package/dist/cli/commands/setup.js +63 -83
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/writes.d.ts +1 -1
- package/dist/cli/commands/writes.js +262 -109
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/dry-run.d.ts +1 -1
- package/dist/cli/dry-run.js +1 -1
- package/dist/cli/glyphs.d.ts +23 -4
- package/dist/cli/glyphs.js +46 -19
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +53 -27
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/helpers-check.d.ts +10 -0
- package/dist/cli/helpers-check.js +59 -0
- package/dist/cli/helpers-check.js.map +1 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +3 -1
- package/dist/cli/period.js +1 -1
- package/dist/cli/period.js.map +1 -1
- package/dist/cli/read-driver.js +26 -1
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +31 -4
- package/dist/cli/render.js +127 -15
- package/dist/cli/render.js.map +1 -1
- package/dist/client.d.ts +22 -2
- package/dist/client.js +52 -15
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +31 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +6 -2
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/baselines/db-v26.js +8 -4
- package/dist/db/baselines/db-v26.js.map +1 -1
- package/dist/db/baselines/db-v27.d.ts +42 -0
- package/dist/db/baselines/db-v27.js +47 -0
- package/dist/db/baselines/db-v27.js.map +1 -0
- package/dist/db/baselines/index.js +2 -1
- package/dist/db/baselines/index.js.map +1 -1
- package/dist/db/locate.d.ts +4 -1
- package/dist/db/locate.js +3 -1
- package/dist/db/locate.js.map +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.js +9 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/deputy/bridge-worker.d.ts +1 -0
- package/dist/deputy/bridge-worker.js +79 -0
- package/dist/deputy/bridge-worker.js.map +1 -0
- package/dist/deputy/bridge.d.ts +14 -0
- package/dist/deputy/bridge.js +70 -0
- package/dist/deputy/bridge.js.map +1 -0
- package/dist/deputy/client.d.ts +19 -0
- package/dist/deputy/client.js +104 -0
- package/dist/deputy/client.js.map +1 -0
- package/dist/deputy/db-facade.d.ts +18 -0
- package/dist/deputy/db-facade.js +71 -0
- package/dist/deputy/db-facade.js.map +1 -0
- package/dist/deputy/files.d.ts +1 -0
- package/dist/deputy/files.js +16 -0
- package/dist/deputy/files.js.map +1 -0
- package/dist/deputy/install.d.ts +319 -0
- package/dist/deputy/install.js +1239 -0
- package/dist/deputy/install.js.map +1 -0
- package/dist/deputy/notice.d.ts +6 -0
- package/dist/deputy/notice.js +28 -0
- package/dist/deputy/notice.js.map +1 -0
- package/dist/deputy/notices.d.ts +31 -0
- package/dist/deputy/notices.js +94 -0
- package/dist/deputy/notices.js.map +1 -0
- package/dist/deputy/osa.d.ts +21 -0
- package/dist/deputy/osa.js +72 -0
- package/dist/deputy/osa.js.map +1 -0
- package/dist/deputy/protocol.d.ts +140 -0
- package/dist/deputy/protocol.js +143 -0
- package/dist/deputy/protocol.js.map +1 -0
- package/dist/deputy/routing.d.ts +84 -0
- package/dist/deputy/routing.js +422 -0
- package/dist/deputy/routing.js.map +1 -0
- package/dist/deputy/shortcuts-exec.d.ts +10 -0
- package/dist/deputy/shortcuts-exec.js +50 -0
- package/dist/deputy/shortcuts-exec.js.map +1 -0
- package/dist/diagnose.d.ts +57 -2
- package/dist/diagnose.js +105 -3
- package/dist/diagnose.js.map +1 -1
- package/dist/direct-setup.d.ts +78 -0
- package/dist/direct-setup.js +484 -0
- package/dist/direct-setup.js.map +1 -0
- package/dist/host-access.d.ts +63 -0
- package/dist/host-access.js +165 -0
- package/dist/host-access.js.map +1 -0
- package/dist/index.d.ts +31 -5
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +27 -1
- package/dist/mcp/server.js +300 -97
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/mappers.d.ts +12 -3
- package/dist/model/mappers.js +6 -1
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/recurrence.d.ts +5 -1
- package/dist/model/recurrence.js +19 -1
- package/dist/model/recurrence.js.map +1 -1
- package/dist/model/template-projection.d.ts +26 -0
- package/dist/model/template-projection.js +113 -0
- package/dist/model/template-projection.js.map +1 -0
- package/dist/op-result.d.ts +55 -0
- package/dist/op-result.js +161 -0
- package/dist/op-result.js.map +1 -0
- package/dist/read/detail.js +7 -1
- package/dist/read/detail.js.map +1 -1
- package/dist/read/filter-contract.d.ts +1 -1
- package/dist/read/filter-contract.js +4 -0
- package/dist/read/filter-contract.js.map +1 -1
- package/dist/read/queries.js +8 -1
- package/dist/read/queries.js.map +1 -1
- package/dist/read/shape.js +4 -0
- package/dist/read/shape.js.map +1 -1
- package/dist/read/views.d.ts +35 -4
- package/dist/read/views.js +120 -18
- package/dist/read/views.js.map +1 -1
- package/dist/session-grant.d.ts +48 -0
- package/dist/session-grant.js +170 -0
- package/dist/session-grant.js.map +1 -0
- package/dist/surface-copy.d.ts +8 -0
- package/dist/surface-copy.js +8 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/sync-health.js +6 -2
- package/dist/sync-health.js.map +1 -1
- package/dist/wizard.d.ts +58 -0
- package/dist/wizard.js +167 -0
- package/dist/wizard.js.map +1 -0
- package/dist/write/accessibility-probe.js +4 -2
- package/dist/write/accessibility-probe.js.map +1 -1
- package/dist/write/automation-probe.js +5 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/availability.d.ts +1 -1
- package/dist/write/availability.js +10 -6
- package/dist/write/availability.js.map +1 -1
- package/dist/write/batch.d.ts +20 -0
- package/dist/write/batch.js +94 -15
- package/dist/write/batch.js.map +1 -1
- package/dist/write/capabilities.d.ts +9 -0
- package/dist/write/capabilities.js +2 -0
- package/dist/write/capabilities.js.map +1 -1
- package/dist/write/clear-reminder.js +1 -1
- package/dist/write/clear-reminder.js.map +1 -1
- package/dist/write/commands.d.ts +12 -1
- package/dist/write/commands.js +170 -237
- package/dist/write/commands.js.map +1 -1
- package/dist/write/experimental.d.ts +19 -0
- package/dist/write/experimental.js +50 -0
- package/dist/write/experimental.js.map +1 -1
- package/dist/write/failure-hints.d.ts +3 -1
- package/dist/write/failure-hints.js.map +1 -1
- package/dist/write/guards.js +19 -2
- package/dist/write/guards.js.map +1 -1
- package/dist/write/lock.d.ts +16 -13
- package/dist/write/lock.js +55 -0
- package/dist/write/lock.js.map +1 -1
- package/dist/write/make-repeating-project.js +9 -13
- package/dist/write/make-repeating-project.js.map +1 -1
- package/dist/write/move.js +14 -6
- package/dist/write/move.js.map +1 -1
- package/dist/write/operations.d.ts +64 -25
- package/dist/write/operations.js +7 -0
- package/dist/write/operations.js.map +1 -1
- package/dist/write/opid.d.ts +14 -0
- package/dist/write/opid.js +22 -0
- package/dist/write/opid.js.map +1 -1
- package/dist/write/param-schema.d.ts +124 -0
- package/dist/write/param-schema.js +747 -0
- package/dist/write/param-schema.js.map +1 -0
- package/dist/write/pipeline.d.ts +78 -2
- package/dist/write/pipeline.js +249 -4
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +8 -0
- package/dist/write/pre-state.js +62 -17
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/promote-clone.js +635 -367
- package/dist/write/promote-clone.js.map +1 -1
- package/dist/write/reorder.js +95 -29
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-anchor.d.ts +30 -0
- package/dist/write/repeat-anchor.js +166 -1
- package/dist/write/repeat-anchor.js.map +1 -1
- package/dist/write/repeat-rule.d.ts +28 -1
- package/dist/write/repeat-rule.js +64 -4
- package/dist/write/repeat-rule.js.map +1 -1
- package/dist/write/resolution-timestamps.d.ts +11 -0
- package/dist/write/resolution-timestamps.js +83 -38
- package/dist/write/resolution-timestamps.js.map +1 -1
- package/dist/write/reversibility.js +4 -0
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/template-mutation.d.ts +137 -0
- package/dist/write/template-mutation.js +451 -0
- package/dist/write/template-mutation.js.map +1 -0
- package/dist/write/undo.js +10 -20
- package/dist/write/undo.js.map +1 -1
- package/dist/write/update-fields.d.ts +151 -0
- package/dist/write/update-fields.js +427 -0
- package/dist/write/update-fields.js.map +1 -0
- package/dist/write/vectors/applescript.js +10 -16
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/registry.d.ts +1 -1
- package/dist/write/vectors/registry.js +10 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/session-reachability.d.ts +55 -0
- package/dist/write/vectors/session-reachability.js +56 -8
- package/dist/write/vectors/session-reachability.js.map +1 -1
- package/dist/write/vectors/shortcuts.js +7 -16
- package/dist/write/vectors/shortcuts.js.map +1 -1
- package/dist/write/vectors/simulator.d.ts +16 -1
- package/dist/write/vectors/simulator.js +68 -12
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/types.d.ts +124 -1
- package/dist/write/vectors/ui-recipes.d.ts +19 -0
- package/dist/write/vectors/ui-recipes.js +158 -38
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +89 -0
- package/dist/write/vectors/ui.js +504 -49
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/verify/delta.d.ts +21 -0
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.d.ts +13 -0
- package/dist/write/verify/poller.js +38 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +9 -1
- package/schema/envelope.schema.json +5 -3
- package/scripts/build-helpers.sh +109 -0
- package/scripts/deputy-cert-setup.sh +60 -0
- package/skills/things-cli/SKILL.md +12 -5
- package/skills/things-cli/references/contracts.md +1 -0
- package/skills/things-cli/references/data-model.md +3 -0
- package/skills/things-cli/references/errors.md +5 -1
- package/skills/things-cli/references/gui.md +1 -0
- package/skills/things-cli/references/repeating.md +75 -0
- package/skills/things-cli/references/tag-cleanup.md +64 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template-mutation composites — "Create Next Copy, then mutate the instance"
|
|
3
|
+
* (design of record: docs/lab/cnc1-template-mutations.md; ruling 2026-08-24).
|
|
4
|
+
*
|
|
5
|
+
* A repeating template refuses every schedule/status write we ship, and the
|
|
6
|
+
* app's own Make Exception chooser is not on the automation path. CNC1 measured
|
|
7
|
+
* the alternative and it is exact: `Items ▸ Repeat ▸ Create Next Copy` followed
|
|
8
|
+
* by an ordinary write to the row it mints reproduces Make Exception FIELD FOR
|
|
9
|
+
* FIELD on the template (weekly against REPX3 §1.2, daily against §2.1; the
|
|
10
|
+
* vacated slot then stays silent when the clock reaches it, against a live
|
|
11
|
+
* control that spawns normally). The composite differs from the chooser by one
|
|
12
|
+
* column on one row — the minted instance's modification stamp, which the
|
|
13
|
+
* chooser leaves unset — and that difference is the winning side of Things
|
|
14
|
+
* Cloud's merge arbitration (SYNCX1), so it is the safe direction.
|
|
15
|
+
*
|
|
16
|
+
* Two composites live here:
|
|
17
|
+
*
|
|
18
|
+
* - {@link runTemplateStatusWrite} — `complete`/`cancel` aimed at a series.
|
|
19
|
+
* UNFLAGGED, because there is exactly one sane reading of "check off this
|
|
20
|
+
* repeating to-do". If the series already has an OPEN materialized
|
|
21
|
+
* occurrence, that IS the current one and it is resolved directly (no
|
|
22
|
+
* mint); otherwise the pending occurrence is materialized first and
|
|
23
|
+
* resolved. Either way the template is left byte-unchanged and the series
|
|
24
|
+
* continues on schedule (CNC1 §6, measured for `cancel` as well as
|
|
25
|
+
* `complete`).
|
|
26
|
+
*
|
|
27
|
+
* - {@link runTemplateExceptionWrite} — `update --exception` aimed at a
|
|
28
|
+
* series: move/re-deadline/re-remind ONLY the next occurrence. Always mints,
|
|
29
|
+
* because the exception semantics belong to the PENDING occurrence — the one
|
|
30
|
+
* the rule has not spawned yet. (Re-dating an already-materialized
|
|
31
|
+
* occurrence is an ordinary update on that row and consumes no slot, so the
|
|
32
|
+
* rule would still spawn its own copy — REPX1 §3.2. Callers who want that
|
|
33
|
+
* can address the occurrence directly.)
|
|
34
|
+
*
|
|
35
|
+
* Three refusals, each measured, are enforced before anything is driven:
|
|
36
|
+
*
|
|
37
|
+
* 1. a target date that is a live slot of the same rule DOUBLE-BOOKS that day
|
|
38
|
+
* (CNC1 §2, inheriting oddities §17 whole);
|
|
39
|
+
* 2. a series with NO CURSOR has no pending occurrence to materialize, and
|
|
40
|
+
* `Create Next Copy` duplicates the current one onto the same day instead
|
|
41
|
+
* (CNC1 §5, CNCAC1 §6/§8, oddities §18);
|
|
42
|
+
* 3. repeating PROJECTS are out of scope: the menu that carries the command
|
|
43
|
+
* does not exist for a project selection (CNC1 §8).
|
|
44
|
+
*
|
|
45
|
+
* Refusal 2 used to be keyed on the rule KIND — "after-completion" — and that
|
|
46
|
+
* was fail-closed guesswork that fired on the wrong state. CNCAC1 measured the
|
|
47
|
+
* shape it was actually hitting: an after-completion series ANCHORS on its
|
|
48
|
+
* occurrence being resolved and DERIVES a real cursor from that anchor, so a
|
|
49
|
+
* series with a completed history renders a projection in Upcoming, the GUI
|
|
50
|
+
* checks that projection off, and `Create Next Copy` materializes it correctly
|
|
51
|
+
* with no duplicate — byte-equivalent to the GUI gesture modulo the minted row's
|
|
52
|
+
* modification stamp. The duplicate belongs to the CURSOR-LESS case alone (a
|
|
53
|
+
* never-resolved series, or a paused one), which is what this now refuses.
|
|
54
|
+
*
|
|
55
|
+
* Reversibility is HALF and is disclosed at op time: `things undo` restores the
|
|
56
|
+
* occurrence's own change perfectly, and can neither remove the materialized
|
|
57
|
+
* occurrence nor rewind the series (CNC1 §7).
|
|
58
|
+
*
|
|
59
|
+
* Two contract facts hold for BOTH composites (ruling 2026-08-25):
|
|
60
|
+
*
|
|
61
|
+
* - The result NAMES THE OCCURRENCE. Every success carries an
|
|
62
|
+
* {@link OccurrenceResolution}: the template the caller aimed at, the
|
|
63
|
+
* occurrence that was actually written, and whether that occurrence was
|
|
64
|
+
* minted for this call or already existed. The caller never has to infer
|
|
65
|
+
* which row moved — the same courtesy `add-repeating` does with its
|
|
66
|
+
* template/instance pair.
|
|
67
|
+
*
|
|
68
|
+
* - The composite is ONE op for idempotency. It records a single summary
|
|
69
|
+
* record (the legs stay legs, and the caller's `opId` rides the summary
|
|
70
|
+
* alone), so a caller that retries after an ambiguous failure — a killed
|
|
71
|
+
* process, a timed-out agent — replays the recorded result instead of
|
|
72
|
+
* minting a SECOND occurrence and advancing the series a second time. Every
|
|
73
|
+
* re-run without a key is a genuinely new action, which is exactly the
|
|
74
|
+
* add-repeating hazard class: pass `--op-id` on a retry.
|
|
75
|
+
*/
|
|
76
|
+
import { type IsoDate } from "../model/dates.ts";
|
|
77
|
+
import type { RepeatRule } from "../model/recurrence.ts";
|
|
78
|
+
import type { UpdateFields } from "./operations.ts";
|
|
79
|
+
import { type MutationResult, type WriteDeps, type WriteOptions } from "./pipeline.ts";
|
|
80
|
+
declare const PROJECT_REFUSAL = "this is a repeating project, and only repeating to-dos can have a single occurrence changed";
|
|
81
|
+
declare const PROJECT_REMEDIATION: string;
|
|
82
|
+
/** What the composite learned about the series before it drove anything. */
|
|
83
|
+
interface SeriesState {
|
|
84
|
+
templateUuid: string;
|
|
85
|
+
title: string;
|
|
86
|
+
/** The pending occurrence's date — the slot a mint would consume. */
|
|
87
|
+
cursor: IsoDate | null;
|
|
88
|
+
rule: RepeatRule | null;
|
|
89
|
+
deadlined: boolean;
|
|
90
|
+
afterCompletion: boolean;
|
|
91
|
+
/** The series is paused, which is WHY it has no cursor — a different remedy. */
|
|
92
|
+
paused: boolean;
|
|
93
|
+
/** An already-materialized OPEN occurrence of this series, if there is one. */
|
|
94
|
+
openInstance: {
|
|
95
|
+
uuid: string;
|
|
96
|
+
startDate: IsoDate | null;
|
|
97
|
+
} | null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Read everything the composite decides on, in one pass. Returns null when the
|
|
101
|
+
* target is not a repeating to-do template, which the caller has already
|
|
102
|
+
* established but which keeps this total.
|
|
103
|
+
*/
|
|
104
|
+
export declare function readSeriesState(deps: WriteDeps, uuid: string): SeriesState | null;
|
|
105
|
+
/**
|
|
106
|
+
* The dates this rule will still produce ON ITS OWN after the pending
|
|
107
|
+
* occurrence has been materialized — i.e. every slot from the one AFTER the
|
|
108
|
+
* cursor up to and including `until`. Landing an occurrence on one of these
|
|
109
|
+
* leaves that day holding two copies once the clock reaches it (CNC1 §2), which
|
|
110
|
+
* is the whole reason this list exists.
|
|
111
|
+
*
|
|
112
|
+
* Empty when the rule is undecodable or has no calendar: nothing can be proven
|
|
113
|
+
* about a rule we cannot read, and the caller refuses rather than guessing.
|
|
114
|
+
*/
|
|
115
|
+
export declare function liveSlotsAfterMint(state: SeriesState, until: IsoDate): IsoDate[];
|
|
116
|
+
/**
|
|
117
|
+
* `complete`/`cancel` aimed at a repeating to-do. Resolves the series' CURRENT
|
|
118
|
+
* occurrence: the open materialized one when it exists, otherwise the pending
|
|
119
|
+
* one, materialized first. The template itself is never written — CNC1 §6
|
|
120
|
+
* measured both status writes leaving it byte-unchanged, with the series
|
|
121
|
+
* continuing at its next date and exactly one occurrence per day.
|
|
122
|
+
*/
|
|
123
|
+
export declare function runTemplateStatusWrite(deps: WriteDeps, op: "todo.complete" | "todo.cancel", uuid: string,
|
|
124
|
+
/**
|
|
125
|
+
* The status write itself, applied to ONE ordinary occurrence. Injected
|
|
126
|
+
* rather than imported so the caller keeps ownership of its own vocabulary —
|
|
127
|
+
* `--completed-at` backdating, the children policy — and so this module does
|
|
128
|
+
* not have to import the resolution orchestrator that calls it.
|
|
129
|
+
*/
|
|
130
|
+
runStatusLeg: (occurrenceUuid: string, legOptions: WriteOptions) => Promise<MutationResult>, options?: WriteOptions): Promise<MutationResult>;
|
|
131
|
+
/**
|
|
132
|
+
* `update --exception` aimed at a repeating to-do: change ONLY the next
|
|
133
|
+
* occurrence. Materializes the pending occurrence and applies the patch to it,
|
|
134
|
+
* which is what the app's own "make a one-time exception" does.
|
|
135
|
+
*/
|
|
136
|
+
export declare function runTemplateExceptionWrite(deps: WriteDeps, uuid: string, patch: UpdateFields & Record<string, unknown>, options?: WriteOptions): Promise<MutationResult>;
|
|
137
|
+
export { PROJECT_REFUSAL, PROJECT_REMEDIATION };
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template-mutation composites — "Create Next Copy, then mutate the instance"
|
|
3
|
+
* (design of record: docs/lab/cnc1-template-mutations.md; ruling 2026-08-24).
|
|
4
|
+
*
|
|
5
|
+
* A repeating template refuses every schedule/status write we ship, and the
|
|
6
|
+
* app's own Make Exception chooser is not on the automation path. CNC1 measured
|
|
7
|
+
* the alternative and it is exact: `Items ▸ Repeat ▸ Create Next Copy` followed
|
|
8
|
+
* by an ordinary write to the row it mints reproduces Make Exception FIELD FOR
|
|
9
|
+
* FIELD on the template (weekly against REPX3 §1.2, daily against §2.1; the
|
|
10
|
+
* vacated slot then stays silent when the clock reaches it, against a live
|
|
11
|
+
* control that spawns normally). The composite differs from the chooser by one
|
|
12
|
+
* column on one row — the minted instance's modification stamp, which the
|
|
13
|
+
* chooser leaves unset — and that difference is the winning side of Things
|
|
14
|
+
* Cloud's merge arbitration (SYNCX1), so it is the safe direction.
|
|
15
|
+
*
|
|
16
|
+
* Two composites live here:
|
|
17
|
+
*
|
|
18
|
+
* - {@link runTemplateStatusWrite} — `complete`/`cancel` aimed at a series.
|
|
19
|
+
* UNFLAGGED, because there is exactly one sane reading of "check off this
|
|
20
|
+
* repeating to-do". If the series already has an OPEN materialized
|
|
21
|
+
* occurrence, that IS the current one and it is resolved directly (no
|
|
22
|
+
* mint); otherwise the pending occurrence is materialized first and
|
|
23
|
+
* resolved. Either way the template is left byte-unchanged and the series
|
|
24
|
+
* continues on schedule (CNC1 §6, measured for `cancel` as well as
|
|
25
|
+
* `complete`).
|
|
26
|
+
*
|
|
27
|
+
* - {@link runTemplateExceptionWrite} — `update --exception` aimed at a
|
|
28
|
+
* series: move/re-deadline/re-remind ONLY the next occurrence. Always mints,
|
|
29
|
+
* because the exception semantics belong to the PENDING occurrence — the one
|
|
30
|
+
* the rule has not spawned yet. (Re-dating an already-materialized
|
|
31
|
+
* occurrence is an ordinary update on that row and consumes no slot, so the
|
|
32
|
+
* rule would still spawn its own copy — REPX1 §3.2. Callers who want that
|
|
33
|
+
* can address the occurrence directly.)
|
|
34
|
+
*
|
|
35
|
+
* Three refusals, each measured, are enforced before anything is driven:
|
|
36
|
+
*
|
|
37
|
+
* 1. a target date that is a live slot of the same rule DOUBLE-BOOKS that day
|
|
38
|
+
* (CNC1 §2, inheriting oddities §17 whole);
|
|
39
|
+
* 2. a series with NO CURSOR has no pending occurrence to materialize, and
|
|
40
|
+
* `Create Next Copy` duplicates the current one onto the same day instead
|
|
41
|
+
* (CNC1 §5, CNCAC1 §6/§8, oddities §18);
|
|
42
|
+
* 3. repeating PROJECTS are out of scope: the menu that carries the command
|
|
43
|
+
* does not exist for a project selection (CNC1 §8).
|
|
44
|
+
*
|
|
45
|
+
* Refusal 2 used to be keyed on the rule KIND — "after-completion" — and that
|
|
46
|
+
* was fail-closed guesswork that fired on the wrong state. CNCAC1 measured the
|
|
47
|
+
* shape it was actually hitting: an after-completion series ANCHORS on its
|
|
48
|
+
* occurrence being resolved and DERIVES a real cursor from that anchor, so a
|
|
49
|
+
* series with a completed history renders a projection in Upcoming, the GUI
|
|
50
|
+
* checks that projection off, and `Create Next Copy` materializes it correctly
|
|
51
|
+
* with no duplicate — byte-equivalent to the GUI gesture modulo the minted row's
|
|
52
|
+
* modification stamp. The duplicate belongs to the CURSOR-LESS case alone (a
|
|
53
|
+
* never-resolved series, or a paused one), which is what this now refuses.
|
|
54
|
+
*
|
|
55
|
+
* Reversibility is HALF and is disclosed at op time: `things undo` restores the
|
|
56
|
+
* occurrence's own change perfectly, and can neither remove the materialized
|
|
57
|
+
* occurrence nor rewind the series (CNC1 §7).
|
|
58
|
+
*
|
|
59
|
+
* Two contract facts hold for BOTH composites (ruling 2026-08-25):
|
|
60
|
+
*
|
|
61
|
+
* - The result NAMES THE OCCURRENCE. Every success carries an
|
|
62
|
+
* {@link OccurrenceResolution}: the template the caller aimed at, the
|
|
63
|
+
* occurrence that was actually written, and whether that occurrence was
|
|
64
|
+
* minted for this call or already existed. The caller never has to infer
|
|
65
|
+
* which row moved — the same courtesy `add-repeating` does with its
|
|
66
|
+
* template/instance pair.
|
|
67
|
+
*
|
|
68
|
+
* - The composite is ONE op for idempotency. It records a single summary
|
|
69
|
+
* record (the legs stay legs, and the caller's `opId` rides the summary
|
|
70
|
+
* alone), so a caller that retries after an ambiguous failure — a killed
|
|
71
|
+
* process, a timed-out agent — replays the recorded result instead of
|
|
72
|
+
* minting a SECOND occurrence and advancing the series a second time. Every
|
|
73
|
+
* re-run without a key is a genuinely new action, which is exactly the
|
|
74
|
+
* add-repeating hazard class: pass `--op-id` on a retry.
|
|
75
|
+
*/
|
|
76
|
+
import { addDaysIso, decodePackedDate, localToday } from "../model/dates.js";
|
|
77
|
+
import { projectOccurrences } from "../model/occurrences.js";
|
|
78
|
+
import { byUuid } from "../read/detail.js";
|
|
79
|
+
import { replayIfApplied } from "./opid.js";
|
|
80
|
+
import { appendCompositeSummary, runComposite, runMutation, } from "./pipeline.js";
|
|
81
|
+
/** How far ahead the collision check will look for a live slot. */
|
|
82
|
+
const SLOT_LOOKAHEAD = 400;
|
|
83
|
+
const IRREVERSIBLE_NOTE = "`things undo` restores this occurrence's own change; it cannot remove the occurrence that was " +
|
|
84
|
+
"created for it, and the series has already moved on to its following date";
|
|
85
|
+
const PROJECT_REFUSAL = "this is a repeating project, and only repeating to-dos can have a single occurrence changed";
|
|
86
|
+
const PROJECT_REMEDIATION = "change the whole series with `things project reschedule-repeat <ref>`, or change one of its " +
|
|
87
|
+
"to-dos directly";
|
|
88
|
+
/**
|
|
89
|
+
* The precondition, stated as what it actually is. A composite can only work on
|
|
90
|
+
* an occurrence the series has NOT spawned yet, and the app tells us whether
|
|
91
|
+
* there is one: `rt1_nextInstanceStartDate`. When it is null there is no next
|
|
92
|
+
* date to bring forward, and `Create Next Copy` does not refuse — it DUPLICATES
|
|
93
|
+
* the current occurrence onto the same day (CNC1 §5 / CNCAC1 §6, oddities §18).
|
|
94
|
+
*
|
|
95
|
+
* This replaces a rule-KIND refusal that fired on the wrong state. It read "an
|
|
96
|
+
* after-completion series has no upcoming occurrence until the current one is
|
|
97
|
+
* done" — but CNCAC1 measured that an after-completion series acquires a real
|
|
98
|
+
* cursor the moment its occurrence is resolved (completed, canceled, OR
|
|
99
|
+
* trashed), which is precisely when the old branch fired: it refused the one
|
|
100
|
+
* shape whose projection the app renders in Upcoming and happily checks off.
|
|
101
|
+
*
|
|
102
|
+
* With that corrected, the only state that reaches this refusal is a PAUSED
|
|
103
|
+
* series whose occurrence is already resolved — and there the refusal earns its
|
|
104
|
+
* keep for a second measured reason. CNC on a paused series does not duplicate;
|
|
105
|
+
* it quietly materializes the occurrence the pause was suppressing, from the
|
|
106
|
+
* stale anchor, and clears that anchor while leaving the paused flag set
|
|
107
|
+
* (CNCAC1 §8). Producing an occurrence from a series the user deliberately
|
|
108
|
+
* paused is not a reading of "check this off" worth guessing at, so the refusal
|
|
109
|
+
* names `resume-repeat` instead.
|
|
110
|
+
*/
|
|
111
|
+
const NO_PENDING_REFUSAL = "this repeating to-do has no upcoming occurrence to work on — its schedule names no next date, " +
|
|
112
|
+
"so there is nothing to bring forward";
|
|
113
|
+
function blocked(op, detail, remediation) {
|
|
114
|
+
return { kind: "blocked", op, reason: "environment", detail, remediation };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read everything the composite decides on, in one pass. Returns null when the
|
|
118
|
+
* target is not a repeating to-do template, which the caller has already
|
|
119
|
+
* established but which keeps this total.
|
|
120
|
+
*/
|
|
121
|
+
export function readSeriesState(deps, uuid) {
|
|
122
|
+
const target = byUuid(deps.db, uuid);
|
|
123
|
+
if (target === null || target.type === "heading" || !target.repeating.isTemplate)
|
|
124
|
+
return null;
|
|
125
|
+
const rule = target.repeating.rule ?? null;
|
|
126
|
+
const rows = deps.db
|
|
127
|
+
.prepare("SELECT uuid, startDate FROM TMTask WHERE rt1_repeatingTemplate = ? AND trashed = 0 " +
|
|
128
|
+
"AND status = 0 ORDER BY startDate, creationDate")
|
|
129
|
+
.all(uuid);
|
|
130
|
+
const first = rows[0];
|
|
131
|
+
return {
|
|
132
|
+
templateUuid: uuid,
|
|
133
|
+
title: target.title,
|
|
134
|
+
cursor: target.repeating.nextOccurrence ?? null,
|
|
135
|
+
rule,
|
|
136
|
+
deadlined: target.repeating.deadlined === true,
|
|
137
|
+
afterCompletion: rule !== null && rule.type === "after-completion",
|
|
138
|
+
paused: target.repeating.paused === true,
|
|
139
|
+
openInstance: first === undefined
|
|
140
|
+
? null
|
|
141
|
+
: { uuid: first.uuid, startDate: decodePackedDate(first.startDate) },
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The dates this rule will still produce ON ITS OWN after the pending
|
|
146
|
+
* occurrence has been materialized — i.e. every slot from the one AFTER the
|
|
147
|
+
* cursor up to and including `until`. Landing an occurrence on one of these
|
|
148
|
+
* leaves that day holding two copies once the clock reaches it (CNC1 §2), which
|
|
149
|
+
* is the whole reason this list exists.
|
|
150
|
+
*
|
|
151
|
+
* Empty when the rule is undecodable or has no calendar: nothing can be proven
|
|
152
|
+
* about a rule we cannot read, and the caller refuses rather than guessing.
|
|
153
|
+
*/
|
|
154
|
+
export function liveSlotsAfterMint(state, until) {
|
|
155
|
+
const { rule, cursor } = state;
|
|
156
|
+
if (rule === null || rule.type !== "fixed" || cursor === null)
|
|
157
|
+
return [];
|
|
158
|
+
if (until <= cursor)
|
|
159
|
+
return [];
|
|
160
|
+
const projected = projectOccurrences(rule, cursor, { count: SLOT_LOOKAHEAD, until }, state.deadlined);
|
|
161
|
+
// The first projection IS the cursor — the slot the mint consumes, not a
|
|
162
|
+
// collision. Everything after it is a slot the rule will still fire.
|
|
163
|
+
return projected.slice(1).map((o) => o.startDate);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolve the calendar day a `when` value lands on, or null when it names no
|
|
167
|
+
* single day (`anytime`/`someday` are not dated, so they cannot collide with a
|
|
168
|
+
* slot).
|
|
169
|
+
*/
|
|
170
|
+
function whenTargetDay(when, todayIso) {
|
|
171
|
+
if (when === undefined)
|
|
172
|
+
return null;
|
|
173
|
+
if (when === "today" || when === "evening")
|
|
174
|
+
return todayIso;
|
|
175
|
+
if (when === "tomorrow")
|
|
176
|
+
return addDaysIso(todayIso, 1);
|
|
177
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(when) ? when : null;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The refusal for a series with nothing pending, with the remedy that fits WHY
|
|
181
|
+
* it is empty. A paused series is one command away from having a cursor again;
|
|
182
|
+
* anything else is the app's own business.
|
|
183
|
+
*/
|
|
184
|
+
function noPendingRefusal(op, state) {
|
|
185
|
+
if (state.paused) {
|
|
186
|
+
return blocked(op, "this repeating to-do is paused, so it has no upcoming occurrence to work on", "resume it first with `things todo resume-repeat <ref>`");
|
|
187
|
+
}
|
|
188
|
+
return blocked(op, NO_PENDING_REFUSAL, "work on one of its occurrences directly");
|
|
189
|
+
}
|
|
190
|
+
/** Human phrasing for "the occurrence dated X" when X may be unknown. */
|
|
191
|
+
function occurrenceLabel(date) {
|
|
192
|
+
return date === null ? "the current occurrence" : `the ${date} occurrence`;
|
|
193
|
+
}
|
|
194
|
+
/** Where the series goes next, read back AFTER the composite has run. */
|
|
195
|
+
function nextOccurrenceAfter(deps, templateUuid) {
|
|
196
|
+
const target = byUuid(deps.db, templateUuid);
|
|
197
|
+
if (target === null || target.type === "heading")
|
|
198
|
+
return null;
|
|
199
|
+
return target.repeating.nextOccurrence ?? null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Materialize the pending occurrence and hand back its uuid. Split out because
|
|
203
|
+
* both composites need exactly this, including the failure phrasing: a mint
|
|
204
|
+
* that did not land must never be reported as a mutation that did.
|
|
205
|
+
*/
|
|
206
|
+
async function mintPendingOccurrence(deps, op, templateUuid, options, txnId) {
|
|
207
|
+
const mint = await runMutation(deps, "todo.create-next-copy", { uuid: templateUuid }, {
|
|
208
|
+
...legOptions(options, txnId),
|
|
209
|
+
vector: "ui",
|
|
210
|
+
// The GUI-drive ACKNOWLEDGEMENT is made here, on the caller's behalf, and
|
|
211
|
+
// it is the one place in the package that does so. Rationale (ruling
|
|
212
|
+
// 2026-08-24): these verbs are UNFLAGGED — "check off this repeating
|
|
213
|
+
// to-do" and "move only the next occurrence" already name the effect the
|
|
214
|
+
// user asked for, and there is no other way to perform either, so a second
|
|
215
|
+
// flag would be ceremony with no decision behind it. Nothing else is
|
|
216
|
+
// relaxed: Article IV's capability gate still refuses a machine whose
|
|
217
|
+
// helpers have no GUI tier, and the disruption ceiling still refuses
|
|
218
|
+
// unless the caller allowed a change that visibly drives the app — which
|
|
219
|
+
// is the control that keeps this from being a surprise.
|
|
220
|
+
dangerouslyDriveGui: true,
|
|
221
|
+
});
|
|
222
|
+
if (mint.kind !== "ok" || mint.uuid === null) {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
result: {
|
|
226
|
+
...mint,
|
|
227
|
+
op,
|
|
228
|
+
...("detail" in mint
|
|
229
|
+
? { detail: `${mint.detail} — no occurrence was created, so nothing was changed` }
|
|
230
|
+
: {}),
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return { ok: true, uuid: mint.uuid };
|
|
235
|
+
}
|
|
236
|
+
function newTxnId(now) {
|
|
237
|
+
return `txn-${now.getTime().toString(36)}-${process.pid.toString(36)}`;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The options a LEG runs under: the caller's, grouped into the composite's txn,
|
|
241
|
+
* with the idempotency key REMOVED. The key identifies the whole composite, and
|
|
242
|
+
* the composite records exactly one record for it (the summary) — leaving it on
|
|
243
|
+
* the legs would put the same key on two or three records and let a resubmission
|
|
244
|
+
* match half a verb.
|
|
245
|
+
*/
|
|
246
|
+
function legOptions(options, txnId) {
|
|
247
|
+
const { opId: _opId, ...rest } = options;
|
|
248
|
+
return { ...rest, txn: { id: txnId, role: "leg" } };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Record the composite as ONE summary record and hand back its undo token.
|
|
252
|
+
*
|
|
253
|
+
* This is the record the whole verb is addressed by: `undo` targets it (a leg is
|
|
254
|
+
* never a direct undo target, so before this existed the composite left nothing
|
|
255
|
+
* for `things undo` to reach), and it is the single record the caller's `opId`
|
|
256
|
+
* keys, carrying the occurrence pair so a replay can answer with it.
|
|
257
|
+
*
|
|
258
|
+
* The summary's vector/tier report the composite's MOST disruptive leg: a mint
|
|
259
|
+
* drives the app window, so a minted occurrence is reported as such even though
|
|
260
|
+
* the status/update leg that followed it was an ordinary quiet write.
|
|
261
|
+
*/
|
|
262
|
+
function recordComposite(deps, args) {
|
|
263
|
+
return appendCompositeSummary(deps, {
|
|
264
|
+
startedAt: args.startedAt,
|
|
265
|
+
op: args.op,
|
|
266
|
+
uuid: args.occurrence.occurrenceUuid,
|
|
267
|
+
txnId: args.txnId,
|
|
268
|
+
invocation: `${args.op} on repeating series ${args.occurrence.templateUuid}: ` +
|
|
269
|
+
`${args.occurrence.minted ? "create next copy, then " : ""}write ${args.occurrence.occurrenceUuid}`,
|
|
270
|
+
requested: args.requested,
|
|
271
|
+
vector: args.occurrence.minted ? "ui" : args.legResult.vector,
|
|
272
|
+
disruption: args.occurrence.minted ? 3 : args.legResult.tier,
|
|
273
|
+
options: args.options,
|
|
274
|
+
occurrence: args.occurrence,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// ------------------------------------------------------------ status writes
|
|
278
|
+
/**
|
|
279
|
+
* `complete`/`cancel` aimed at a repeating to-do. Resolves the series' CURRENT
|
|
280
|
+
* occurrence: the open materialized one when it exists, otherwise the pending
|
|
281
|
+
* one, materialized first. The template itself is never written — CNC1 §6
|
|
282
|
+
* measured both status writes leaving it byte-unchanged, with the series
|
|
283
|
+
* continuing at its next date and exactly one occurrence per day.
|
|
284
|
+
*/
|
|
285
|
+
export async function runTemplateStatusWrite(deps, op, uuid,
|
|
286
|
+
/**
|
|
287
|
+
* The status write itself, applied to ONE ordinary occurrence. Injected
|
|
288
|
+
* rather than imported so the caller keeps ownership of its own vocabulary —
|
|
289
|
+
* `--completed-at` backdating, the children policy — and so this module does
|
|
290
|
+
* not have to import the resolution orchestrator that calls it.
|
|
291
|
+
*/
|
|
292
|
+
runStatusLeg, options = {}) {
|
|
293
|
+
const now = deps.now?.() ?? new Date();
|
|
294
|
+
return runComposite(deps, op, async () => {
|
|
295
|
+
const state = readSeriesState(deps, uuid);
|
|
296
|
+
if (state === null)
|
|
297
|
+
return blocked(op, "this to-do is no longer a repeating series", "retry");
|
|
298
|
+
const txnId = newTxnId(now);
|
|
299
|
+
let targetUuid;
|
|
300
|
+
let occurrenceDate;
|
|
301
|
+
let minted = false;
|
|
302
|
+
if (state.openInstance !== null) {
|
|
303
|
+
targetUuid = state.openInstance.uuid;
|
|
304
|
+
occurrenceDate = state.openInstance.startDate;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
if (state.cursor === null)
|
|
308
|
+
return noPendingRefusal(op, state);
|
|
309
|
+
const mint = await mintPendingOccurrence(deps, op, state.templateUuid, options, txnId);
|
|
310
|
+
if (!mint.ok)
|
|
311
|
+
return mint.result;
|
|
312
|
+
targetUuid = mint.uuid;
|
|
313
|
+
occurrenceDate = state.cursor;
|
|
314
|
+
minted = true;
|
|
315
|
+
}
|
|
316
|
+
const write = await runStatusLeg(targetUuid, legOptions(options, txnId));
|
|
317
|
+
if (write.kind !== "ok")
|
|
318
|
+
return { ...write, op };
|
|
319
|
+
const occurrence = {
|
|
320
|
+
templateUuid: state.templateUuid,
|
|
321
|
+
occurrenceUuid: targetUuid,
|
|
322
|
+
minted,
|
|
323
|
+
date: occurrenceDate,
|
|
324
|
+
};
|
|
325
|
+
const undoToken = recordComposite(deps, {
|
|
326
|
+
op,
|
|
327
|
+
startedAt: now,
|
|
328
|
+
txnId,
|
|
329
|
+
requested: { uuid: state.templateUuid },
|
|
330
|
+
occurrence,
|
|
331
|
+
legResult: write,
|
|
332
|
+
options,
|
|
333
|
+
});
|
|
334
|
+
const verb = op === "todo.complete" ? "checked off" : "canceled";
|
|
335
|
+
const nextDate = nextOccurrenceAfter(deps, state.templateUuid);
|
|
336
|
+
const disclosure = [
|
|
337
|
+
`${verb} ${occurrenceLabel(occurrenceDate)} of "${state.title}"` +
|
|
338
|
+
(minted ? " (created just now, because the series had no unfinished copy)" : ""),
|
|
339
|
+
nextDate === null
|
|
340
|
+
? "the series has no further scheduled occurrence"
|
|
341
|
+
: state.afterCompletion
|
|
342
|
+
? `the next occurrence is ${nextDate} — this series counts from each completion, so ` +
|
|
343
|
+
"resolving it now restarted the interval from today"
|
|
344
|
+
: `the next occurrence is ${nextDate}`,
|
|
345
|
+
];
|
|
346
|
+
if (minted)
|
|
347
|
+
disclosure.push(IRREVERSIBLE_NOTE);
|
|
348
|
+
return {
|
|
349
|
+
...write,
|
|
350
|
+
op,
|
|
351
|
+
occurrence,
|
|
352
|
+
undoToken,
|
|
353
|
+
warnings: [...(write.warnings ?? []), ...disclosure],
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
// --------------------------------------------------------- exception writes
|
|
358
|
+
/**
|
|
359
|
+
* `update --exception` aimed at a repeating to-do: change ONLY the next
|
|
360
|
+
* occurrence. Materializes the pending occurrence and applies the patch to it,
|
|
361
|
+
* which is what the app's own "make a one-time exception" does.
|
|
362
|
+
*/
|
|
363
|
+
export async function runTemplateExceptionWrite(deps, uuid, patch, options = {}) {
|
|
364
|
+
const op = "todo.update";
|
|
365
|
+
const now = deps.now?.() ?? new Date();
|
|
366
|
+
const todayIso = localToday(now, options.zone);
|
|
367
|
+
// Idempotency before the lock, like every refusal: a resubmitted key means the
|
|
368
|
+
// exception already landed, and re-running it would mint a SECOND occurrence
|
|
369
|
+
// (this composite always mints) and consume another of the series' slots.
|
|
370
|
+
const replay = replayIfApplied(deps, options);
|
|
371
|
+
if (replay !== null)
|
|
372
|
+
return replay;
|
|
373
|
+
return runComposite(deps, op, async () => {
|
|
374
|
+
const state = readSeriesState(deps, uuid);
|
|
375
|
+
if (state === null)
|
|
376
|
+
return blocked(op, "this to-do is no longer a repeating series", "retry");
|
|
377
|
+
if (state.cursor === null)
|
|
378
|
+
return noPendingRefusal(op, state);
|
|
379
|
+
// The collision refusal (CNC1 §2). A day the rule will still fire on ends up
|
|
380
|
+
// holding two copies of the series, and nothing in the app reconciles them.
|
|
381
|
+
const targetDay = whenTargetDay(typeof patch["when"] === "string" ? patch["when"] : undefined, todayIso);
|
|
382
|
+
if (targetDay !== null && !state.afterCompletion) {
|
|
383
|
+
// An after-completion series is exempt BY CONSTRUCTION, not by assumption:
|
|
384
|
+
// it has no calendar, so it owns exactly one future date — the cursor this
|
|
385
|
+
// mint is about to consume — and there is no second slot for the moved
|
|
386
|
+
// occurrence to collide with (CNCAC1 §7).
|
|
387
|
+
if (state.rule === null || state.rule.type !== "fixed") {
|
|
388
|
+
return blocked(op, "this series' schedule cannot be read, so there is no way to tell whether " +
|
|
389
|
+
`${targetDay} is a day it already lands on`, "move the occurrence in the Things app, or change the whole series with " +
|
|
390
|
+
"`things todo reschedule-repeat <ref>`");
|
|
391
|
+
}
|
|
392
|
+
if (liveSlotsAfterMint(state, targetDay).includes(targetDay)) {
|
|
393
|
+
return blocked(op, `"${state.title}" already lands on ${targetDay} — moving this occurrence there would ` +
|
|
394
|
+
"leave two copies on that day, and the app does not merge them", "pick a day the series does not already land on, or change the whole series with " +
|
|
395
|
+
"`things todo reschedule-repeat <ref>`");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const txnId = newTxnId(now);
|
|
399
|
+
const mint = await mintPendingOccurrence(deps, op, state.templateUuid, options, txnId);
|
|
400
|
+
if (!mint.ok)
|
|
401
|
+
return mint.result;
|
|
402
|
+
const write = await runMutation(deps, "todo.update", { uuid: mint.uuid, ...patch }, legOptions(options, txnId));
|
|
403
|
+
if (write.kind !== "ok") {
|
|
404
|
+
return {
|
|
405
|
+
...write,
|
|
406
|
+
op,
|
|
407
|
+
...("detail" in write
|
|
408
|
+
? {
|
|
409
|
+
detail: `${write.detail} — the ${occurrenceLabel(state.cursor)} was created but not ` +
|
|
410
|
+
`changed; it is now a plain to-do you can edit directly (${mint.uuid})`,
|
|
411
|
+
}
|
|
412
|
+
: {}),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
const occurrence = {
|
|
416
|
+
templateUuid: state.templateUuid,
|
|
417
|
+
occurrenceUuid: mint.uuid,
|
|
418
|
+
// An exception ALWAYS mints: the semantics belong to the occurrence the
|
|
419
|
+
// rule has not spawned yet (see the module header).
|
|
420
|
+
minted: true,
|
|
421
|
+
date: state.cursor,
|
|
422
|
+
};
|
|
423
|
+
const undoToken = recordComposite(deps, {
|
|
424
|
+
op,
|
|
425
|
+
startedAt: now,
|
|
426
|
+
txnId,
|
|
427
|
+
requested: { uuid: state.templateUuid, ...patch },
|
|
428
|
+
occurrence,
|
|
429
|
+
legResult: write,
|
|
430
|
+
options,
|
|
431
|
+
});
|
|
432
|
+
const nextDate = nextOccurrenceAfter(deps, state.templateUuid);
|
|
433
|
+
return {
|
|
434
|
+
...write,
|
|
435
|
+
op,
|
|
436
|
+
occurrence,
|
|
437
|
+
undoToken,
|
|
438
|
+
warnings: [
|
|
439
|
+
...(write.warnings ?? []),
|
|
440
|
+
`changed only ${occurrenceLabel(state.cursor)} of "${state.title}" — the series itself is ` +
|
|
441
|
+
"unchanged",
|
|
442
|
+
nextDate === null
|
|
443
|
+
? "the series has no further scheduled occurrence"
|
|
444
|
+
: `the next occurrence is ${nextDate}`,
|
|
445
|
+
IRREVERSIBLE_NOTE,
|
|
446
|
+
],
|
|
447
|
+
};
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
export { PROJECT_REFUSAL, PROJECT_REMEDIATION };
|
|
451
|
+
//# sourceMappingURL=template-mutation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-mutation.js","sourceRoot":"","sources":["../../src/write/template-mutation.ts"],"names":[],"mappings":"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"}
|