things-api 0.11.0 → 0.12.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 (192) hide show
  1. package/README.md +16 -5
  2. package/dist/audit/schema.d.ts +7 -0
  3. package/dist/audit/schema.js.map +1 -1
  4. package/dist/cli/commands/area.d.ts +18 -11
  5. package/dist/cli/commands/area.js +55 -67
  6. package/dist/cli/commands/area.js.map +1 -1
  7. package/dist/cli/commands/doctor.d.ts +1 -2
  8. package/dist/cli/commands/doctor.js +11 -1
  9. package/dist/cli/commands/doctor.js.map +1 -1
  10. package/dist/cli/commands/install-skill.d.ts +19 -4
  11. package/dist/cli/commands/install-skill.js +101 -52
  12. package/dist/cli/commands/install-skill.js.map +1 -1
  13. package/dist/cli/commands/mcp.js +9 -0
  14. package/dist/cli/commands/mcp.js.map +1 -1
  15. package/dist/cli/commands/project.d.ts +1 -0
  16. package/dist/cli/commands/project.js +20 -28
  17. package/dist/cli/commands/project.js.map +1 -1
  18. package/dist/cli/commands/reads.js +100 -28
  19. package/dist/cli/commands/reads.js.map +1 -1
  20. package/dist/cli/commands/show.js +32 -8
  21. package/dist/cli/commands/show.js.map +1 -1
  22. package/dist/cli/commands/todo.js +1 -1
  23. package/dist/cli/commands/todo.js.map +1 -1
  24. package/dist/cli/commands/writes.js +769 -143
  25. package/dist/cli/commands/writes.js.map +1 -1
  26. package/dist/cli/did-you-mean.d.ts +14 -3
  27. package/dist/cli/did-you-mean.js +26 -3
  28. package/dist/cli/did-you-mean.js.map +1 -1
  29. package/dist/cli/glyphs.d.ts +16 -2
  30. package/dist/cli/glyphs.js +60 -12
  31. package/dist/cli/glyphs.js.map +1 -1
  32. package/dist/cli/help.js +67 -12
  33. package/dist/cli/help.js.map +1 -1
  34. package/dist/cli/main.js +1 -1
  35. package/dist/cli/main.js.map +1 -1
  36. package/dist/cli/move-hint.d.ts +3 -2
  37. package/dist/cli/move-hint.js +2 -2
  38. package/dist/cli/move-hint.js.map +1 -1
  39. package/dist/cli/read-driver.d.ts +43 -5
  40. package/dist/cli/read-driver.js +81 -15
  41. package/dist/cli/read-driver.js.map +1 -1
  42. package/dist/cli/render.d.ts +19 -8
  43. package/dist/cli/render.js +59 -25
  44. package/dist/cli/render.js.map +1 -1
  45. package/dist/cli/resolve-invocation.d.ts +2 -2
  46. package/dist/cli/resolve-invocation.js +3 -3
  47. package/dist/cli/resolve-invocation.js.map +1 -1
  48. package/dist/cli/skill-check.d.ts +12 -6
  49. package/dist/cli/skill-check.js +31 -14
  50. package/dist/cli/skill-check.js.map +1 -1
  51. package/dist/cli/skill.d.ts +8 -0
  52. package/dist/cli/skill.js +10 -0
  53. package/dist/cli/skill.js.map +1 -1
  54. package/dist/cli/verb-hint.js +1 -1
  55. package/dist/cli/verb-hint.js.map +1 -1
  56. package/dist/client.d.ts +140 -19
  57. package/dist/client.js +253 -38
  58. package/dist/client.js.map +1 -1
  59. package/dist/config.d.ts +66 -2
  60. package/dist/config.js +120 -12
  61. package/dist/config.js.map +1 -1
  62. package/dist/contracts.d.ts +176 -28
  63. package/dist/contracts.js +22 -1
  64. package/dist/contracts.js.map +1 -1
  65. package/dist/diagnose.d.ts +34 -2
  66. package/dist/diagnose.js +36 -1
  67. package/dist/diagnose.js.map +1 -1
  68. package/dist/index.d.ts +19 -8
  69. package/dist/index.js +19 -3
  70. package/dist/index.js.map +1 -1
  71. package/dist/mcp/server.d.ts +9 -0
  72. package/dist/mcp/server.js +733 -304
  73. package/dist/mcp/server.js.map +1 -1
  74. package/dist/model/entities.d.ts +46 -3
  75. package/dist/model/entities.js.map +1 -1
  76. package/dist/model/mappers.d.ts +2 -0
  77. package/dist/model/mappers.js +41 -2
  78. package/dist/model/mappers.js.map +1 -1
  79. package/dist/model/recurrence.d.ts +8 -1
  80. package/dist/model/recurrence.js.map +1 -1
  81. package/dist/read/area-filter.d.ts +76 -0
  82. package/dist/read/area-filter.js +59 -0
  83. package/dist/read/area-filter.js.map +1 -0
  84. package/dist/read/area-view.d.ts +11 -11
  85. package/dist/read/area-view.js +50 -28
  86. package/dist/read/area-view.js.map +1 -1
  87. package/dist/read/detail.js +15 -8
  88. package/dist/read/detail.js.map +1 -1
  89. package/dist/read/predicates.d.ts +18 -0
  90. package/dist/read/predicates.js +19 -0
  91. package/dist/read/predicates.js.map +1 -1
  92. package/dist/read/project-view.d.ts +31 -15
  93. package/dist/read/project-view.js +72 -39
  94. package/dist/read/project-view.js.map +1 -1
  95. package/dist/read/pseudo-area.d.ts +29 -0
  96. package/dist/read/pseudo-area.js +27 -0
  97. package/dist/read/pseudo-area.js.map +1 -0
  98. package/dist/read/queries.d.ts +105 -18
  99. package/dist/read/queries.js +185 -30
  100. package/dist/read/queries.js.map +1 -1
  101. package/dist/read/scope.d.ts +126 -0
  102. package/dist/read/scope.js +162 -0
  103. package/dist/read/scope.js.map +1 -0
  104. package/dist/read/search-rank.d.ts +2 -5
  105. package/dist/read/shape.d.ts +162 -0
  106. package/dist/read/shape.js +686 -0
  107. package/dist/read/shape.js.map +1 -0
  108. package/dist/read/show-target.d.ts +9 -1
  109. package/dist/read/show-target.js +45 -4
  110. package/dist/read/show-target.js.map +1 -1
  111. package/dist/read/stage.d.ts +199 -0
  112. package/dist/read/stage.js +125 -0
  113. package/dist/read/stage.js.map +1 -0
  114. package/dist/read/truncation.d.ts +7 -5
  115. package/dist/read/truncation.js +33 -8
  116. package/dist/read/truncation.js.map +1 -1
  117. package/dist/read/views.d.ts +49 -9
  118. package/dist/read/views.js +193 -40
  119. package/dist/read/views.js.map +1 -1
  120. package/dist/surface-copy.d.ts +9 -0
  121. package/dist/surface-copy.js +9 -0
  122. package/dist/surface-copy.js.map +1 -1
  123. package/dist/write/batch.d.ts +49 -10
  124. package/dist/write/batch.js +423 -71
  125. package/dist/write/batch.js.map +1 -1
  126. package/dist/write/commands.js +266 -55
  127. package/dist/write/commands.js.map +1 -1
  128. package/dist/write/guards.d.ts +1 -1
  129. package/dist/write/guards.js +71 -10
  130. package/dist/write/guards.js.map +1 -1
  131. package/dist/write/heading.d.ts +10 -1
  132. package/dist/write/heading.js +35 -5
  133. package/dist/write/heading.js.map +1 -1
  134. package/dist/write/make-repeating-project.d.ts +2 -2
  135. package/dist/write/make-repeating-project.js +9 -9
  136. package/dist/write/make-repeating-project.js.map +1 -1
  137. package/dist/write/move.d.ts +130 -0
  138. package/dist/write/move.js +1533 -0
  139. package/dist/write/move.js.map +1 -0
  140. package/dist/write/operations.d.ts +105 -19
  141. package/dist/write/operations.js +56 -7
  142. package/dist/write/operations.js.map +1 -1
  143. package/dist/write/opid.d.ts +31 -0
  144. package/dist/write/opid.js +30 -0
  145. package/dist/write/opid.js.map +1 -0
  146. package/dist/write/pipeline.d.ts +70 -3
  147. package/dist/write/pipeline.js +198 -33
  148. package/dist/write/pipeline.js.map +1 -1
  149. package/dist/write/pre-state.d.ts +162 -8
  150. package/dist/write/pre-state.js +385 -20
  151. package/dist/write/pre-state.js.map +1 -1
  152. package/dist/write/reorder.d.ts +23 -3
  153. package/dist/write/reorder.js +1871 -85
  154. package/dist/write/reorder.js.map +1 -1
  155. package/dist/write/reversibility.js +19 -7
  156. package/dist/write/reversibility.js.map +1 -1
  157. package/dist/write/scope-guard.d.ts +31 -0
  158. package/dist/write/scope-guard.js +162 -0
  159. package/dist/write/scope-guard.js.map +1 -0
  160. package/dist/write/undo.js +120 -13
  161. package/dist/write/undo.js.map +1 -1
  162. package/dist/write/vectors/applescript.js +16 -7
  163. package/dist/write/vectors/applescript.js.map +1 -1
  164. package/dist/write/vectors/shortcuts.js +1 -1
  165. package/dist/write/vectors/shortcuts.js.map +1 -1
  166. package/dist/write/vectors/simulator.js +83 -27
  167. package/dist/write/vectors/simulator.js.map +1 -1
  168. package/dist/write/vectors/types.d.ts +11 -0
  169. package/dist/write/vectors/ui-certification.d.ts +3 -3
  170. package/dist/write/vectors/ui-certification.js +41 -5
  171. package/dist/write/vectors/ui-certification.js.map +1 -1
  172. package/dist/write/vectors/ui-drag.d.ts +21 -1
  173. package/dist/write/vectors/ui-drag.js +138 -27
  174. package/dist/write/vectors/ui-drag.js.map +1 -1
  175. package/dist/write/vectors/ui-recipes.d.ts +21 -1
  176. package/dist/write/vectors/ui-recipes.js +166 -7
  177. package/dist/write/vectors/ui-recipes.js.map +1 -1
  178. package/dist/write/vectors/ui.d.ts +25 -1
  179. package/dist/write/vectors/ui.js +142 -33
  180. package/dist/write/vectors/ui.js.map +1 -1
  181. package/dist/write/verify/delta.d.ts +17 -1
  182. package/dist/write/verify/delta.js +106 -29
  183. package/dist/write/verify/delta.js.map +1 -1
  184. package/package.json +7 -1
  185. package/schema/envelope.schema.json +383 -0
  186. package/skills/things-cli/SKILL.md +34 -17
  187. package/skills/things-cli/references/banner.md +35 -0
  188. package/skills/things-cli/references/contracts.md +30 -9
  189. package/skills/things-cli/references/errors.md +49 -0
  190. package/skills/things-cli/references/gui.md +1 -1
  191. package/skills/things-cli/references/model.md +15 -6
  192. package/skills/things-cli/references/ordering.md +71 -0
@@ -0,0 +1,162 @@
1
+ import { EFFECTIVE_AREA, ReferenceResolutionError, resolveAreaUuid, resolveProjectUuid, } from "./queries.js";
2
+ /**
3
+ * Fail-closed if a requested scope is unresolvable — `things mcp --scope <bogus>`
4
+ * refuses to start, a CLI invocation errors as usage — so the daemon never runs
5
+ * unscoped when a scope was asked for. A subclass of {@link ReferenceResolutionError}
6
+ * so existing usage-class handlers treat it uniformly.
7
+ */
8
+ export class ScopeResolutionError extends ReferenceResolutionError {
9
+ constructor(message, ref) {
10
+ super(message, { code: "not-found", ref });
11
+ this.name = "ScopeResolutionError";
12
+ }
13
+ }
14
+ /**
15
+ * Resolve a scope ref (uuid, uuid-prefix, or unique area/project name) to a
16
+ * pinned {@link ResolvedScope}. Areas win over same-named projects (mirroring
17
+ * `classifyShowTarget`). A to-do / heading / tag ref is a usage error — only an
18
+ * area or a project can be a container. Fail-closed: an unresolvable ref throws
19
+ * {@link ScopeResolutionError}.
20
+ */
21
+ export function resolveScope(db, ref, source) {
22
+ // Area first (an area outranks a same-named project, like the show router).
23
+ try {
24
+ const uuid = resolveAreaUuid(db, ref, { prefixTier: false });
25
+ const row = db.prepare("SELECT title FROM TMArea WHERE uuid = ?").get(uuid);
26
+ return { kind: "area", uuid, title: row?.title ?? "", source, areaUuid: uuid };
27
+ }
28
+ catch (err) {
29
+ // An ambiguous area name is a real conflict — surface it. A plain not-found
30
+ // falls through to project resolution.
31
+ if (err instanceof RangeError && err.message.includes("matches")) {
32
+ throw new ScopeResolutionError(`scope "${ref}" is ambiguous — ${err.message}`, ref);
33
+ }
34
+ }
35
+ try {
36
+ const uuid = resolveProjectUuid(db, ref, { prefixTier: false });
37
+ const row = db.prepare("SELECT title, area FROM TMTask WHERE uuid = ?").get(uuid);
38
+ return {
39
+ kind: "project",
40
+ uuid,
41
+ title: row?.title ?? "",
42
+ source,
43
+ areaUuid: row?.area ?? null,
44
+ };
45
+ }
46
+ catch (err) {
47
+ if (err instanceof RangeError && err.message.includes("matches")) {
48
+ throw new ScopeResolutionError(`scope "${ref}" is ambiguous — ${err.message}`, ref);
49
+ }
50
+ }
51
+ throw new ScopeResolutionError(`scope "${ref}" matches no area or project — a scope must name a container (only areas and ` +
52
+ "projects can be a container; to-dos, headings, and tags cannot)", ref);
53
+ }
54
+ /** The `meta.scope` projection of a resolved scope (drops the internal `areaUuid`). */
55
+ export function scopeMeta(scope) {
56
+ return { kind: scope.kind, uuid: scope.uuid, title: scope.title, source: scope.source };
57
+ }
58
+ /**
59
+ * The entity-level membership predicate — the SAME relation as
60
+ * {@link scopeMembershipSql}, for an already-shaped view row. Area scope: the
61
+ * row's EFFECTIVE area is the scope (its `area` Ref, which queries.ts already
62
+ * resolves transitively). Project scope: the row IS the project, is a direct
63
+ * child, or is nested under one of the project's headings.
64
+ */
65
+ export function inScopeItem(item, scope) {
66
+ if (scope.kind === "area") {
67
+ const area = "area" in item ? item.area : null;
68
+ return area != null && area.uuid === scope.uuid;
69
+ }
70
+ const p = scope.uuid;
71
+ if (item.uuid === p)
72
+ return true;
73
+ const project = "project" in item ? item.project : null;
74
+ if (project != null && project.uuid === p)
75
+ return true;
76
+ const hp = "headingProject" in item ? item.headingProject : undefined;
77
+ if (hp != null && hp.uuid === p)
78
+ return true;
79
+ return false;
80
+ }
81
+ /**
82
+ * The SQL fragment on alias `t` — the single membership source the leak-critical
83
+ * query paths AND the write ancestry check share. Area: the effective-area
84
+ * COALESCE equals the scope. Project: the row is the project, its direct child,
85
+ * or a heading-nested child.
86
+ */
87
+ export function scopeMembershipSql(scope) {
88
+ if (scope.kind === "area") {
89
+ return { where: `${EFFECTIVE_AREA} = ?`, binds: [scope.uuid] };
90
+ }
91
+ return {
92
+ where: "(t.uuid = ? OR t.project = ? OR t.heading IN " +
93
+ "(SELECT uuid FROM TMTask WHERE type = 2 AND project = ?))",
94
+ binds: [scope.uuid, scope.uuid, scope.uuid],
95
+ };
96
+ }
97
+ /** True when the given uuid is an in-scope TMTask row (single-row leak paths, write parity). */
98
+ export function isUuidInScope(db, uuid, scope) {
99
+ const mem = scopeMembershipSql(scope);
100
+ const row = db
101
+ .prepare(`SELECT 1 AS ok FROM TMTask t WHERE t.uuid = ? AND ${mem.where} LIMIT 1`)
102
+ .get(uuid, ...mem.binds);
103
+ return row !== undefined;
104
+ }
105
+ /**
106
+ * The membership clause for {@link resolveTaskUuidPrefix} (alias `t`): the same
107
+ * fragment as {@link scopeMembershipSql}, so an out-of-scope uuid resolves to
108
+ * "not found" through the identical code path a nonexistent one does.
109
+ */
110
+ export function taskMembershipClause(scope) {
111
+ return scopeMembershipSql(scope);
112
+ }
113
+ /**
114
+ * The membership clause for {@link resolveNamedRef} over TMTask projects
115
+ * (UNqualified columns — that resolver aliases nothing). A project carries its
116
+ * area directly (no inheritance), so area scope compares `area`; project scope
117
+ * pins the one project uuid.
118
+ */
119
+ export function namedProjectClause(scope) {
120
+ if (scope.kind === "area")
121
+ return { where: "area = ?", binds: [scope.uuid] };
122
+ return { where: "uuid = ?", binds: [scope.uuid] };
123
+ }
124
+ /**
125
+ * The membership clause for {@link resolveNamedRef} over TMArea (UNqualified).
126
+ * Area scope: the one scope area. Project scope: the project's own area (an
127
+ * area-less project has none, so nothing resolves — `0`).
128
+ */
129
+ export function namedAreaClause(scope) {
130
+ if (scope.kind === "area")
131
+ return { where: "uuid = ?", binds: [scope.uuid] };
132
+ return scope.areaUuid === null
133
+ ? { where: "0", binds: [] }
134
+ : { where: "uuid = ?", binds: [scope.areaUuid] };
135
+ }
136
+ /** Filter a flat view (upcoming/logbook/trash/search/changes) to in-scope rows. */
137
+ export function filterListByScope(items, scope) {
138
+ return items.filter((i) => inScopeItem(i, scope));
139
+ }
140
+ /**
141
+ * Filter the grouped sidebar catalogue (anytime/someday) to in-scope rows: each
142
+ * section's items are filtered and empty sections drop, so the per-block caps
143
+ * size the survivors.
144
+ */
145
+ export function filterSectionsByScope(sections, scope) {
146
+ return sections
147
+ .map((s) => ({ area: s.area, items: s.items.filter((i) => inScopeItem(i, scope)) }))
148
+ .filter((s) => s.items.length > 0);
149
+ }
150
+ /**
151
+ * Filter the Today split to in-scope rows, RECOMPUTING the badge over the
152
+ * surviving OPEN members so it never counts a dropped (out-of-scope) row — the
153
+ * same treatment `filterTodayByArea` gives the area filter.
154
+ */
155
+ export function filterTodayByScope(view, scope, todayIso) {
156
+ const today = view.today.filter((i) => inScopeItem(i, scope));
157
+ const evening = view.evening.filter((i) => inScopeItem(i, scope));
158
+ const open = [...today, ...evening].filter((i) => i.status === "open");
159
+ const dueOrOverdue = open.filter((i) => i.deadline !== null && i.deadline <= todayIso).length;
160
+ return { today, evening, badge: { dueOrOverdue, other: open.length - dueOrOverdue } };
161
+ }
162
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/read/scope.ts"],"names":[],"mappings":"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"}
@@ -18,12 +18,9 @@ import type { ListItem } from "./views.ts";
18
18
  export type MatchField = "title" | "notes" | "heading" | "checklist";
19
19
  export interface SearchMatch {
20
20
  item: ListItem;
21
+ /** The RANK field (strongest match by rank order); the displayed provenance
22
+ * annotation rides the item as `match` and follows its own precedence. */
21
23
  field: MatchField;
22
- /** Present when `field === "heading"`: the heading whose title matched. */
23
- matchedVia?: {
24
- kind: "heading";
25
- title: string;
26
- };
27
24
  }
28
25
  export declare function fieldRank(field: MatchField): number;
29
26
  /** Containers (projects, areas) sort above to-dos. */
@@ -0,0 +1,162 @@
1
+ /**
2
+ * The read-payload SHAPING transform: the token-economy rules R6 and R7, the
3
+ * universal item-DTO reshapes (R9), and the R10 lifecycle taxonomy — applied at
4
+ * the JSON emit boundary of the read surfaces (the CLI `--json` read envelope,
5
+ * src/cli/read-driver.ts, and the MCP read tool results, src/mcp/server.ts), the
6
+ * same boundaries omit-empty runs at. Shaping runs BEFORE omit-empty. The
7
+ * human-render path keeps the full, unshaped entities, so this is JSON-only.
8
+ *
9
+ * Both R6/R7 are deterministic BY VIEW KIND / SECTION — the emitter knows whether
10
+ * it is inside a single-container view, a lifecycle bucket, or a mixed list —
11
+ * never a per-item heuristic.
12
+ *
13
+ * ## R10 — the `stage` lifecycle taxonomy (every tier, every kind incl. detail)
14
+ * The three former wire fields `start` / `logged` / `trashed` are DELETED from
15
+ * every item and replaced by ONE derived `stage` ∈ `inbox | upcoming | anytime |
16
+ * someday | logbook | trash` (src/read/stage.ts `deriveStage`, the single pure
17
+ * derivation reused by the card bucketing so `stage` can never disagree with the
18
+ * bucket a view puts an item in). Today/evening membership is a SEPARATE
19
+ * presence-keyed axis — `today: true` / `evening: true` (evening implies today) —
20
+ * derived in the mapper with the Today view's own two-arm predicate.
21
+ * - `stage` is DROPPED only where the enclosing node PROVABLY states it — the R6
22
+ * rule (drop only what the node provably says). That is the stage-PURE flat
23
+ * views (inbox, `anytime`, someday, logbook, trash) and the stage-named card
24
+ * sub-buckets (anytime/upcoming/someday/logbook/trash, which the bucketer splits
25
+ * BY stage). The `anytime` catalogue is stage-PURE (R10.2): every member is an
26
+ * Anytime-view row (ANYTIME_SELF) — undated-active, arrived-active, or arrived
27
+ * someday-scheduled — and an ARRIVED dated row derives `anytime` (Upcoming is
28
+ * STRICTLY FUTURE, UPC1), so the field is redundant there.
29
+ * `stage` is KEPT on the stage-MIXED or derived surfaces: the `upcoming` view
30
+ * carries deadline-forecast stage-`anytime`/`someday` rows alongside its
31
+ * future-dated stage-`upcoming` ones — dropping `stage` there would delete
32
+ * non-redundant information — plus `today` (mixes upcoming + anytime-deadline
33
+ * rows), search, changes, the projects/areas listings, the card NODE, and
34
+ * detail.
35
+ * - the former `todaySection` field is RETIRED from the wire entirely (R10.1):
36
+ * `todaySection: "evening"` merely duplicated the `evening: true` marker. It
37
+ * remains an internal entity field (the human render and the write-verify delta
38
+ * still read it); shaping deletes it from the JSON copy.
39
+ *
40
+ * ## R12 — `when`, the derived TIME-AXIS position (replaces startDate + markers)
41
+ * Today/evening membership and the scheduled/projected date collapse onto ONE
42
+ * derived, presence-keyed field `when` (src/read/stage.ts `deriveWhen`): `"today"`
43
+ * / `"evening"` (Today-view membership, from the SAME `today`/`evening` markers the
44
+ * mapper stamps — never re-derived, so `when` can never disagree with the star), a
45
+ * FUTURE ISO date (a strictly-future scheduled row, or a template's projected next
46
+ * occurrence), or absent (unscheduled + not in Today; also an unprojected template
47
+ * and every logged/trashed row). The doctrine line: **`stage` enumerates the
48
+ * sidebar BUCKETS an item lives in; `when` enumerates its TIME POSITIONS (today |
49
+ * evening | a future date).** Someday is deliberately NOT a `when` value (it is a
50
+ * bucket → stage). Tier/drop rules:
51
+ * - the former `today`/`evening` marker KEYS are DELETED from the wire on EVERY
52
+ * tier — `when` carries the fact; the markers stay internal (they feed `when`).
53
+ * - the raw `startDate` is DELETED in COMPACT (a list needs the position, not the
54
+ * substrate) and KEPT in FULL/DETAIL beside `when` — different facts: `startDate`
55
+ * = what is stored, `when` = where it sits.
56
+ * - a template's `repeating.nextOccurrence` is GONE from the wire — `when` replaces
57
+ * it (same fact, one word); the resting-templates `{date: null}` group is
58
+ * unchanged (an unprojected template has no `when`).
59
+ * - `when` is DROPPED inside the `today` view's own sections (the section key states
60
+ * today/evening) and inside any card/heading `upcoming` DATE-GROUP for a member
61
+ * whose `when` equals the group's date (the group states it). KEPT everywhere
62
+ * else it is present — including the flat `upcoming`/`anytime`/`inbox`/`someday`
63
+ * catalogues, search, changes (a deadline-pulled row reads `when: "today"` in the
64
+ * mixed search/changes surfaces, informatively; note R13 re-files it to stage
65
+ * `anytime` and the flat inbox/someday views now EXCLUDE it — it appears in the
66
+ * `anytime` catalogue instead, `when: "today"` kept, stage dropped as pure).
67
+ *
68
+ * ## R13 — provisional Today members + GUI-faithful pulled-row membership
69
+ * BANNER1 / BANNER1b (docs/lab/banner1-research.md). Two coupled facts:
70
+ * - **`provisional: true`** — a presence-keyed marker on every Today member the GUI
71
+ * pips / counts in the "You have N new to-dos" banner: a Today member NOT yet
72
+ * materialized (`start != active OR startDate IS NULL`, BANNER1 L1). Derived from
73
+ * the SAME today/evening markers + fields the stage/`when` axes use (never
74
+ * re-derived). Emitted on EVERY tier, NEVER dropped (the banner is not a section,
75
+ * so no node implies it). Absent on non-Today rows and on materialized ones.
76
+ * Read-only: the app clears it by materializing the row on banner-OK, a GUI-only
77
+ * side effect our read cannot perform (watchers beware).
78
+ * - **stage `anytime` for a deadline pull** — a due-deadline pull re-files an undated
79
+ * Inbox/Someday row into Anytime (deriveStage step 2½, L-A). So EVERY Today member
80
+ * derives stage `anytime`, and the `today` view's own sections become stage-PURE →
81
+ * `stage` is DROPPED there (TODAY_SECTION_DROP), alongside the section-implied
82
+ * `when`. The flat someday/inbox views EXCLUDE pulled rows and the anytime view
83
+ * INCLUDES them (src/read/views.ts + predicates.ts DEADLINE_PULLED) — GUI fidelity.
84
+ *
85
+ * ## Universal item-DTO reshapes (R9 — EVERY tier, EVERY read kind incl. detail)
86
+ * - **checklist nesting** — flat counts → presence-keyed `checklist: {open,total}`.
87
+ * - **todos counts** — a project's flat leaf-action counts → presence-keyed
88
+ * `todos: {open, total}` (omit when total 0).
89
+ * - **repeating template/instance split (R11)** — the wire drops the
90
+ * `isTemplate`/`isInstance` discriminators; key presence carries the fact. A
91
+ * TEMPLATE keeps a nested `repeating: {paused?, deadlined?, rule?,
92
+ * latestInstance?}` — the series object (rule config + backward pointer +
93
+ * state flags); presence MEANS template. The forward pointer `nextOccurrence`
94
+ * moved to the top-level `when` (R12 — a template's projected date IS its time
95
+ * position); `latestInstance` is detail-only (SL1). An INSTANCE keeps a flat
96
+ * `instanceOf: <templateUuid>` and no `repeating`. A plain row keeps neither.
97
+ * See {@link reshapeRepeatingWire}.
98
+ * - **string tags** — `tags`/`inheritedTags` become plain arrays of names.
99
+ * - **one project key** — a headed item's owning project (formerly
100
+ * `headingProject`) is merged into `project`; `headingProject` never appears.
101
+ *
102
+ * ## R6 — no-redundant-ancestry (both tiers)
103
+ * project-view children drop `project`+`area` (heading-group members also drop
104
+ * `heading`); area-view children/project-cards drop `area`; anytime/someday
105
+ * section items drop `area`. Mixed lists keep every ref. (In the COMPACT tier the
106
+ * `heading` ref is additionally dropped everywhere — R7.)
107
+ *
108
+ * ## R7 — named detail tiers (compact | full)
109
+ * List contexts default to COMPACT; `detail`/`show` and `--full` / `full:true`
110
+ * use FULL. Compact drops `created`/`modified`, the full `notes` string (a
111
+ * presence-keyed `hasNotes: true` marks a row with notes), and the `heading` ref;
112
+ * `status` is omitted when `open`. FULL keeps them but still applies R6, the
113
+ * universal reshapes, and R10.
114
+ */
115
+ import { type Stage, type When } from "./stage.ts";
116
+ /**
117
+ * Apply the universal reshapes + R6 + R7 + R10 to a read payload for one view
118
+ * `kind`. `full` forces the FULL tier (R7 default-pruning off, everything else
119
+ * applied); an unrecognized kind passes through unchanged. The input is never
120
+ * mutated (shallow copies throughout), so the human-render path keeps the full
121
+ * entities.
122
+ */
123
+ export declare function shapeReadPayload(kind: string, data: unknown, full: boolean): unknown;
124
+ /** The disambiguation-candidate kinds an error may list (a subset of the entity kinds). */
125
+ export type CandidateType = "to-do" | "project" | "heading" | "area" | "tag";
126
+ /**
127
+ * The ONE fixed error-candidate shape — the presence-keyed disambiguation DTO a
128
+ * not-found / ambiguous resolution lists under `error.detail.candidates` (and the
129
+ * did-you-mean fallback under the same key). Deliberately minimal and INVARIANT
130
+ * across request flags: `--full` / `--all` / etc. NEVER widen it, because an
131
+ * error payload is the most determinism-critical surface. A candidate carries
132
+ * ONLY material that helps a caller pick the right entity, drawn from the SAME
133
+ * single-source derivations the read wire uses — never the raw internal entity
134
+ * (no counts, no notes, no dates, no null-stuffed keys):
135
+ * - `uuid` / `title` / `type` — always present (`type` names the kind).
136
+ * - `area` / `project` — container hint as a TITLE string, present only when set.
137
+ * - `stage` / `when` — the R10/R12 lifecycle words, present only for a to-do /
138
+ * project candidate whose source row carries the materialized lifecycle fields
139
+ * (a thin uuid+title resolver row carries neither — presence-keyed, so absent).
140
+ * A trashed / logged candidate needs no boolean: `stage` already reads `"trash"`
141
+ * / `"logbook"` for it — the same vocabulary the wire uses.
142
+ */
143
+ export interface CandidateRef {
144
+ uuid: string;
145
+ title: string;
146
+ type: CandidateType;
147
+ area?: string;
148
+ project?: string;
149
+ stage?: Stage;
150
+ when?: When;
151
+ }
152
+ /** The fixed cap on a listed candidate array; the error `message` states the total when it overflows. */
153
+ export declare const CANDIDATE_CAP = 8;
154
+ /**
155
+ * Project ONE entity — a materialized to-do/project/area/heading, or a thin
156
+ * `{uuid, title}` resolver row — to the fixed {@link CandidateRef}. The SINGLE
157
+ * source every error-candidate emit flows through (the did-you-mean fallback and
158
+ * every not-found/ambiguous resolver), so the candidate shape can never vary by
159
+ * site. Reuses the wire's own {@link stageOf}/{@link whenOf} derivations — the
160
+ * lifecycle words are never re-derived here.
161
+ */
162
+ export declare function candidateRef(type: CandidateType, src: unknown): CandidateRef;