things-api 0.19.3 → 0.20.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 (139) hide show
  1. package/README.md +1 -1
  2. package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
  3. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
  4. package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
  5. package/dist/audit/schema.d.ts +42 -10
  6. package/dist/audit/schema.js +21 -0
  7. package/dist/audit/schema.js.map +1 -1
  8. package/dist/cli/commands/doctor.js +3 -1
  9. package/dist/cli/commands/doctor.js.map +1 -1
  10. package/dist/cli/commands/op-result.js +25 -7
  11. package/dist/cli/commands/op-result.js.map +1 -1
  12. package/dist/cli/commands/repeat-flags.d.ts +19 -1
  13. package/dist/cli/commands/repeat-flags.js +71 -0
  14. package/dist/cli/commands/repeat-flags.js.map +1 -1
  15. package/dist/cli/commands/rescue.d.ts +12 -0
  16. package/dist/cli/commands/rescue.js +83 -0
  17. package/dist/cli/commands/rescue.js.map +1 -0
  18. package/dist/cli/commands/writes.js +86 -32
  19. package/dist/cli/commands/writes.js.map +1 -1
  20. package/dist/cli/help.js +25 -25
  21. package/dist/cli/help.js.map +1 -1
  22. package/dist/cli/main.js +12 -2
  23. package/dist/cli/main.js.map +1 -1
  24. package/dist/cli/resolve-invocation.d.ts +9 -1
  25. package/dist/cli/resolve-invocation.js +71 -0
  26. package/dist/cli/resolve-invocation.js.map +1 -1
  27. package/dist/cli/unknown-command.d.ts +26 -0
  28. package/dist/cli/unknown-command.js +135 -0
  29. package/dist/cli/unknown-command.js.map +1 -0
  30. package/dist/contracts.d.ts +4 -3
  31. package/dist/contracts.js +1 -1
  32. package/dist/index.d.ts +4 -2
  33. package/dist/index.js +1 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp/server.js +62 -10
  36. package/dist/mcp/server.js.map +1 -1
  37. package/dist/model/entities.d.ts +11 -0
  38. package/dist/model/entities.js.map +1 -1
  39. package/dist/model/mappers.js +15 -0
  40. package/dist/model/mappers.js.map +1 -1
  41. package/dist/op-result.d.ts +24 -2
  42. package/dist/op-result.js +77 -20
  43. package/dist/op-result.js.map +1 -1
  44. package/dist/process-instance.d.ts +41 -0
  45. package/dist/process-instance.js +82 -0
  46. package/dist/process-instance.js.map +1 -0
  47. package/dist/rescue.d.ts +247 -0
  48. package/dist/rescue.js +811 -0
  49. package/dist/rescue.js.map +1 -0
  50. package/dist/session-grant.js +4 -15
  51. package/dist/session-grant.js.map +1 -1
  52. package/dist/ui-state.d.ts +8 -4
  53. package/dist/ui-state.js +8 -4
  54. package/dist/ui-state.js.map +1 -1
  55. package/dist/write/batch.js +4 -1
  56. package/dist/write/batch.js.map +1 -1
  57. package/dist/write/clone.js +12 -6
  58. package/dist/write/clone.js.map +1 -1
  59. package/dist/write/commands.d.ts +9 -0
  60. package/dist/write/commands.js +135 -50
  61. package/dist/write/commands.js.map +1 -1
  62. package/dist/write/disclosures.d.ts +275 -0
  63. package/dist/write/disclosures.js +277 -0
  64. package/dist/write/disclosures.js.map +1 -0
  65. package/dist/write/experimental.d.ts +16 -0
  66. package/dist/write/experimental.js +19 -0
  67. package/dist/write/experimental.js.map +1 -1
  68. package/dist/write/failure-hints.js +3 -2
  69. package/dist/write/failure-hints.js.map +1 -1
  70. package/dist/write/guards.d.ts +1 -1
  71. package/dist/write/guards.js +102 -3
  72. package/dist/write/guards.js.map +1 -1
  73. package/dist/write/heading.js +4 -7
  74. package/dist/write/heading.js.map +1 -1
  75. package/dist/write/lock.d.ts +85 -1
  76. package/dist/write/lock.js +121 -6
  77. package/dist/write/lock.js.map +1 -1
  78. package/dist/write/make-repeating-project.js +2 -1
  79. package/dist/write/make-repeating-project.js.map +1 -1
  80. package/dist/write/operations.d.ts +52 -6
  81. package/dist/write/operations.js +22 -0
  82. package/dist/write/operations.js.map +1 -1
  83. package/dist/write/opid.d.ts +69 -8
  84. package/dist/write/opid.js +116 -20
  85. package/dist/write/opid.js.map +1 -1
  86. package/dist/write/param-schema.js +14 -2
  87. package/dist/write/param-schema.js.map +1 -1
  88. package/dist/write/pipeline.d.ts +82 -3
  89. package/dist/write/pipeline.js +204 -60
  90. package/dist/write/pipeline.js.map +1 -1
  91. package/dist/write/planner.d.ts +7 -0
  92. package/dist/write/planner.js +12 -0
  93. package/dist/write/planner.js.map +1 -1
  94. package/dist/write/preserve-modified.d.ts +17 -8
  95. package/dist/write/preserve-modified.js.map +1 -1
  96. package/dist/write/promote-clone.js +106 -53
  97. package/dist/write/promote-clone.js.map +1 -1
  98. package/dist/write/reorder.js +45 -39
  99. package/dist/write/reorder.js.map +1 -1
  100. package/dist/write/repeat-rule.d.ts +25 -1
  101. package/dist/write/repeat-rule.js +69 -0
  102. package/dist/write/repeat-rule.js.map +1 -1
  103. package/dist/write/resolution-timestamps.js +104 -78
  104. package/dist/write/resolution-timestamps.js.map +1 -1
  105. package/dist/write/reversibility.js +2 -2
  106. package/dist/write/reversibility.js.map +1 -1
  107. package/dist/write/spawn-expectation.d.ts +135 -0
  108. package/dist/write/spawn-expectation.js +237 -0
  109. package/dist/write/spawn-expectation.js.map +1 -0
  110. package/dist/write/template-mutation.js +34 -35
  111. package/dist/write/template-mutation.js.map +1 -1
  112. package/dist/write/undo.js +45 -5
  113. package/dist/write/undo.js.map +1 -1
  114. package/dist/write/vectors/simulator.js +48 -2
  115. package/dist/write/vectors/simulator.js.map +1 -1
  116. package/dist/write/vectors/types.d.ts +12 -0
  117. package/dist/write/vectors/ui-recipes.js +11 -1
  118. package/dist/write/vectors/ui-recipes.js.map +1 -1
  119. package/dist/write/vectors/ui-state.d.ts +64 -6
  120. package/dist/write/vectors/ui-state.js +91 -4
  121. package/dist/write/vectors/ui-state.js.map +1 -1
  122. package/dist/write/vectors/ui.d.ts +69 -3
  123. package/dist/write/vectors/ui.js +293 -146
  124. package/dist/write/vectors/ui.js.map +1 -1
  125. package/dist/write/vectors/url-scheme.js +26 -0
  126. package/dist/write/vectors/url-scheme.js.map +1 -1
  127. package/dist/write/verify/delta.d.ts +17 -2
  128. package/dist/write/verify/delta.js +46 -16
  129. package/dist/write/verify/delta.js.map +1 -1
  130. package/dist/write/verify/poller.d.ts +3 -2
  131. package/dist/write/verify/poller.js +3 -1
  132. package/dist/write/verify/poller.js.map +1 -1
  133. package/package.json +1 -1
  134. package/schema/envelope.schema.json +1 -2
  135. package/skills/things-cli/SKILL.md +4 -2
  136. package/skills/things-cli/references/repeating.md +1 -1
  137. package/dist/cli/commands/ui-state.d.ts +0 -11
  138. package/dist/cli/commands/ui-state.js +0 -27
  139. package/dist/cli/commands/ui-state.js.map +0 -1
@@ -3,11 +3,12 @@ import { byUuid } from "../read/detail.js";
3
3
  import { manualLogDateEpoch, pendingLogCount } from "../read/log-boundary.js";
4
4
  import { isLooseRef } from "../read/pseudo-area.js";
5
5
  import { RESOLUTION_TIMESTAMP_EXPECTED } from "../surface-copy.js";
6
+ import { isRepeatReanchor } from "./operations.js";
6
7
  import { areaMemberCounts, childTagTitles, classifyHeadingConvert, classifyHeadingDissolve, classifyHeadingMoveToProject, classifyProjectRepeat, computeHeadingMovePre, computeReorderPre, emptyPreState, loadTarget, projectChildren, projectStatus, projectSubtreeUuids, resolveArea, resolveHeading, resolveProject, resolveTag, sameTitleTaskUuids, seriesRowUuids, trashedCount, } from "./pre-state.js";
7
8
  import { assertNotesModesExclusive, normalizeReminder, updateAssertions, updateWireParams, whenAssertions, whenWithReminder, } from "./update-fields.js";
8
9
  import { resolveTagRefs } from "./tag-refs.js";
9
10
  import { PRIVATE_REORDER_COMMAND } from "./experimental.js";
10
- import { assertRepeatRule } from "./repeat-rule.js";
11
+ import { assertRepeatRule, assertRescheduleRule, asRepeatRuleParams, ruleToInverseParams, } from "./repeat-rule.js";
11
12
  import { assessOffRuleFirst, deadlineDriveNext, deriveFixedAnchor } from "./repeat-anchor.js";
12
13
  import { expectedRuleAssertions } from "./repeat-asserts.js";
13
14
  import { escapeAppleScript } from "./vectors/applescript.js";
@@ -1873,6 +1874,22 @@ function requestedRuleKeys(params) {
1873
1874
  }
1874
1875
  /** ui ops all guard existence/type + the H-UI-DRIVE acknowledgement. */
1875
1876
  const UI_HAZARDS = ["H-UNKNOWN-DESTINATION", "H-UI-DRIVE"];
1877
+ /**
1878
+ * reschedule-repeat additionally guards the RE-ANCHOR spelling's preconditions
1879
+ * (H-REPEAT-REANCHOR), which run BEFORE the GUI-drive gate — a re-anchor that
1880
+ * cannot be dispatched safely should say why, not ask for an acknowledgement it
1881
+ * does not need.
1882
+ */
1883
+ const RESCHEDULE_HAZARDS = ["H-UNKNOWN-DESTINATION", "H-REPEAT-REANCHOR", "H-UI-DRIVE"];
1884
+ /**
1885
+ * reschedule-repeat's two spellings ride two transports: a bare re-anchor is ONE
1886
+ * `things:///update?when=` dispatch (tier 0, no GUI), a rule restatement is the
1887
+ * Repeat dialog. Neither is compilable for the other's vector, so the planner is
1888
+ * told which one this call is.
1889
+ */
1890
+ function rescheduleVectors(params) {
1891
+ return isRepeatReanchor(params) ? ["url-scheme"] : ["ui"];
1892
+ }
1876
1893
  const todoMakeRepeating = {
1877
1894
  op: "todo.make-repeating",
1878
1895
  hazards: UI_HAZARDS,
@@ -1921,16 +1938,103 @@ const todoMakeRepeating = {
1921
1938
  return uiDrive(makeRepeatingRecipe(params.uuid, params.frequency, params.interval, makeRuleExtras(params)));
1922
1939
  },
1923
1940
  };
1941
+ /**
1942
+ * A series RE-ANCHOR as ONE url dispatch: `things:///update?id=<template>&when=<date>`
1943
+ * (`update-project` for a project template). BARE by measurement — REANCH2 cells
1944
+ * D6/E1/E3/E4 found that ANY `deadline=` riding the same url voids the whole
1945
+ * write, re-anchor included, so this compile carries the date and nothing else.
1946
+ */
1947
+ function reanchorUrl(urlCommand, params, ctx) {
1948
+ return thingsUrl(urlCommand, { id: params.uuid, when: params.next }, ctx.token);
1949
+ }
1950
+ /**
1951
+ * What a landed RE-ANCHOR looks like: the PRIOR rule, re-anchored to the target
1952
+ * date. The write rewrites both cursor columns, `todayIndexReferenceDate` and the
1953
+ * rule blob — whose calendar anchor is recomputed FROM that date (a weekly Sunday
1954
+ * rule moved to a Thursday becomes a Thursday rule; monthly day-of-month and
1955
+ * yearly month+day move likewise, REANCH1 §2/§7) — while frequency, interval,
1956
+ * rule type, ends bound and deadline mode all survive untouched.
1957
+ *
1958
+ * Expressed in the SAME vocabulary a dialog reschedule uses: the prior rule read
1959
+ * back as params, plus the anchor DERIVED from the target date, handed to the one
1960
+ * expected-rule builder ({@link expectedRuleAssertions}). So the re-anchor is
1961
+ * verified at the same fidelity as a rule rewrite — a landing that moved anything
1962
+ * else (or nothing at all: a refused write and the app's `target == cursor`
1963
+ * short-circuit both exit 0 at the transport, REANCH1 §2.4/§8) is a
1964
+ * verify-failed, never a silent ok. Returns null when the prior rule cannot be
1965
+ * expressed as params — the guard has already refused those shapes, so a null
1966
+ * here means only the cursor pair is asserted.
1967
+ */
1968
+ function reanchorAssertions(pre, next) {
1969
+ // BOTH cursor columns (REANCH1 §8): `nextOccurrence` is the projection the app
1970
+ // maintains, `spawnCursor` the raw materialization cursor beside it. A partial
1971
+ // landing moves one and not the other.
1972
+ const cursors = [
1973
+ { field: "repeating.nextOccurrence", equals: next },
1974
+ { field: "repeating.spawnCursor", equals: next },
1975
+ ];
1976
+ const target = pre.target;
1977
+ if (target === null || target.type === "heading")
1978
+ return cursors;
1979
+ const rule = target.repeating.rule;
1980
+ if (rule === undefined)
1981
+ return cursors;
1982
+ const prior = ruleToInverseParams(rule, target.repeating.deadlined === true);
1983
+ if (prior === null)
1984
+ return cursors;
1985
+ // The anchor the app recomputes from the target date — derived by the SAME law
1986
+ // the dialog drives (deriveFixedAnchor), with any EXPLICIT prior anchor dropped
1987
+ // first so the derivation is not short-circuited by the one being replaced.
1988
+ const { weekdays: _w, monthly: _m, yearly: _y, ...unanchored } = prior;
1989
+ const eff = { ...unanchored, next, ...deriveFixedAnchor(unanchored, next) };
1990
+ return [...cursors, ...expectedRuleAssertions(eff, { includeCursor: false })];
1991
+ }
1924
1992
  const todoRescheduleRepeat = {
1925
1993
  op: "todo.reschedule-repeat",
1926
- hazards: UI_HAZARDS,
1927
- preRead(db, params) {
1928
- assertRepeatRule(params);
1994
+ hazards: RESCHEDULE_HAZARDS,
1995
+ vectorsFor: rescheduleVectors,
1996
+ preRead(db, params, now, zone) {
1997
+ assertRescheduleRule(params);
1929
1998
  const pre = emptyPreState();
1930
- pre.target = loadTarget(db, params.uuid);
1999
+ // The device's current day — the re-anchor's strictly-future check runs
2000
+ // against it (H-REPEAT-REANCHOR), under the SAME response clock the verify
2001
+ // uses.
2002
+ pre.todayIso = localToday(now, zone);
2003
+ pre.target = loadTarget(db, params.uuid, now, zone);
1931
2004
  return pre;
1932
2005
  },
1933
- expectedDelta(_pre, params) {
2006
+ expectedDelta(pre, params) {
2007
+ return rescheduleDelta(pre, params);
2008
+ },
2009
+ compile(params, vector, _pre, ctx) {
2010
+ if (isRepeatReanchor(params)) {
2011
+ if (vector !== "url-scheme")
2012
+ unsupportedVector(this.op, vector);
2013
+ return reanchorUrl("update", params, ctx);
2014
+ }
2015
+ if (vector !== "ui")
2016
+ unsupportedVector(this.op, vector);
2017
+ const rule = asRepeatRuleParams(params);
2018
+ return uiDrive(rescheduleRepeatRecipe(rule.uuid, rule.frequency, rule.interval, reschedRuleExtras(rule)));
2019
+ },
2020
+ };
2021
+ /**
2022
+ * The reschedule's expected delta, both spellings (to-do and project share it —
2023
+ * the DB delta is identical, UIC2-a).
2024
+ */
2025
+ function rescheduleDelta(pre, params) {
2026
+ if (isRepeatReanchor(params)) {
2027
+ return {
2028
+ mode: "update",
2029
+ uuid: params.uuid,
2030
+ assert: reanchorAssertions(pre, params.next),
2031
+ // The prior rule + deadline flag, so the change record holds what the
2032
+ // series looked like before its anchor moved (there is no headless inverse
2033
+ // — see reversibility.ts — but the record is still the evidence).
2034
+ capture: [{ field: "repeating.rule" }, { field: "repeating.deadlined" }],
2035
+ };
2036
+ }
2037
+ {
1934
2038
  // Identity PRESERVED (UI2-b): the same template uuid, rule mutated in place.
1935
2039
  // Assert the FULL requested rule (#491) — type + unit + interval + calendar
1936
2040
  // anchor + ends bound + deadline offset + the ANCH2 first-occurrence cursor —
@@ -1942,7 +2046,8 @@ const todoRescheduleRepeat = {
1942
2046
  // so the undo can re-drive it faithfully. The DERIVED-anchor effective params
1943
2047
  // (reschedEffParams) are asserted — the SAME bag the compile drives — so a
1944
2048
  // `--when`-only reschedule verifies the derived anchor the drive lands (RSPA1).
1945
- const eff = reschedEffParams(params);
2049
+ const rule = asRepeatRuleParams(params);
2050
+ const eff = reschedEffParams(rule);
1946
2051
  return {
1947
2052
  mode: "update",
1948
2053
  uuid: params.uuid,
@@ -1961,15 +2066,10 @@ const todoRescheduleRepeat = {
1961
2066
  // rewrites an EXISTING rule, so there is a full pre-state to diff against —
1962
2067
  // which is why make-repeating / add-repeating do not carry it: they mint the
1963
2068
  // rule, so every field is new by construction and there is nothing to compare.
1964
- collateral: { requested: requestedRuleKeys(params) },
2069
+ collateral: { requested: requestedRuleKeys(rule) },
1965
2070
  };
1966
- },
1967
- compile(params, vector) {
1968
- if (vector !== "ui")
1969
- unsupportedVector(this.op, vector);
1970
- return uiDrive(rescheduleRepeatRecipe(params.uuid, params.frequency, params.interval, reschedRuleExtras(params)));
1971
- },
1972
- };
2071
+ }
2072
+ }
1973
2073
  const todoPauseRepeat = {
1974
2074
  op: "todo.pause-repeat",
1975
2075
  hazards: UI_HAZARDS,
@@ -2072,47 +2172,32 @@ const todoResumeRepeat = {
2072
2172
  // (CRASH1 / oddities §7 C5).
2073
2173
  const projectRescheduleRepeat = {
2074
2174
  op: "project.reschedule-repeat",
2075
- hazards: UI_HAZARDS,
2076
- preRead(db, params) {
2077
- assertRepeatRule(params);
2175
+ hazards: RESCHEDULE_HAZARDS,
2176
+ vectorsFor: rescheduleVectors,
2177
+ preRead(db, params, now, zone) {
2178
+ assertRescheduleRule(params);
2078
2179
  const pre = emptyPreState();
2079
- pre.target = loadTarget(db, params.uuid);
2180
+ pre.todayIso = localToday(now, zone);
2181
+ pre.target = loadTarget(db, params.uuid, now, zone);
2080
2182
  return pre;
2081
2183
  },
2082
- expectedDelta(_pre, params) {
2083
- // Identity PRESERVED (UIC2-a): same project uuid, rule mutated in place;
2084
- // capture the prior rule (+ deadline flag) for the faithful undo. Assert the
2085
- // FULL requested rule (#491) via expectedRuleAssertions — same completeness as
2086
- // todo.reschedule-repeat (a shallow unit+interval subset false-noop'd an
2087
- // anchor/deadline/ends-only reschedule). The DERIVED-anchor effective params
2088
- // (reschedEffParams) are asserted — the SAME bag the compile drives — so a
2089
- // `--when`-only reschedule verifies the derived anchor the drive lands (RSPA1).
2090
- const eff = reschedEffParams(params);
2091
- return {
2092
- mode: "update",
2093
- uuid: params.uuid,
2094
- // includeCursor asserts the first-occurrence cursor == --when. For an OFF-RULE
2095
- // first (explicit anchor ≠ --when) the cursor lands on the next RULE-ALIGNED
2096
- // occurrence, not --when (DACON1 cell DC4: --when Oct 16 with an Oct-16 due
2097
- // anchor cursors to the next aligned start, 2029-10-02), so asserting it would
2098
- // false-fail an honored off-rule reschedule — drop the cursor assertion there
2099
- // and verify the rule anchor only.
2100
- assert: expectedRuleAssertions(eff, {
2101
- includeCursor: assessOffRuleFirst(eff)?.kind !== "honored",
2102
- }),
2103
- capture: [{ field: "repeating.rule" }, { field: "repeating.deadlined" }],
2104
- // UNEXPLAINED-DELTA DETECTION (CGRD1 guard 3). The assertions above prove
2105
- // the requested rule landed; this proves nothing ELSE did. A reschedule
2106
- // rewrites an EXISTING rule, so there is a full pre-state to diff against —
2107
- // which is why make-repeating / add-repeating do not carry it: they mint the
2108
- // rule, so every field is new by construction and there is nothing to compare.
2109
- collateral: { requested: requestedRuleKeys(params) },
2110
- };
2184
+ expectedDelta(pre, params) {
2185
+ return rescheduleDelta(pre, params);
2111
2186
  },
2112
- compile(params, vector) {
2187
+ compile(params, vector, _pre, ctx) {
2188
+ // The route must match the ROW TYPE: `update` aimed at a project template is
2189
+ // a silent no-op and vice versa (REANCH1 §4.1). The op namespace carries the
2190
+ // type (H-UNKNOWN-DESTINATION refuses a mismatched target), so the route is
2191
+ // simply the project one here.
2192
+ if (isRepeatReanchor(params)) {
2193
+ if (vector !== "url-scheme")
2194
+ unsupportedVector(this.op, vector);
2195
+ return reanchorUrl("update-project", params, ctx);
2196
+ }
2113
2197
  if (vector !== "ui")
2114
2198
  unsupportedVector(this.op, vector);
2115
- return uiDrive(projectRescheduleRepeatRecipe(params.uuid, params.frequency, params.interval, reschedRuleExtras(params)));
2199
+ const rule = asRepeatRuleParams(params);
2200
+ return uiDrive(projectRescheduleRepeatRecipe(rule.uuid, rule.frequency, rule.interval, reschedRuleExtras(rule)));
2116
2201
  },
2117
2202
  };
2118
2203
  const projectPauseRepeat = {