@anchrd/intel-contract 0.23.0 → 0.25.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.
@@ -1,4 +1,14 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * The id of the archive column, on every board (D68, #674).
4
+ *
5
+ * ⚠️ **It is derived, never stored.** A card sits in it because `nodes.archived_at` is set, and
6
+ * `board_tasks.status` keeps naming the working column it came from — that is what lets a card
7
+ * pulled back out land where it was. A configured column may not claim this id; `board_update`
8
+ * refuses it, because two columns with one id means every card in them lands in whichever the view
9
+ * draws first.
10
+ */
11
+ export declare const ARCHIVE_COLUMN_ID = "archived";
2
12
  export declare const BoardColumn: z.ZodObject<{
3
13
  id: z.ZodString;
4
14
  title: z.ZodString;
@@ -14,6 +24,7 @@ export declare const BoardTask: z.ZodObject<{
14
24
  startDate: z.ZodNullable<z.ZodISODateTime>;
15
25
  dueDate: z.ZodNullable<z.ZodISODateTime>;
16
26
  dependsOn: z.ZodNullable<z.ZodString>;
27
+ parentTaskId: z.ZodNullable<z.ZodString>;
17
28
  position: z.ZodNumber;
18
29
  archivedAt: z.ZodNullable<z.ZodISODateTime>;
19
30
  }, z.core.$strict>;
@@ -43,6 +54,7 @@ export declare const BoardView: z.ZodObject<{
43
54
  title: z.ZodString;
44
55
  terminal: z.ZodDefault<z.ZodBoolean>;
45
56
  }, z.core.$strict>>;
57
+ archiveVisible: z.ZodBoolean;
46
58
  tasks: z.ZodArray<z.ZodObject<{
47
59
  id: z.ZodString;
48
60
  title: z.ZodString;
@@ -52,6 +64,7 @@ export declare const BoardView: z.ZodObject<{
52
64
  startDate: z.ZodNullable<z.ZodISODateTime>;
53
65
  dueDate: z.ZodNullable<z.ZodISODateTime>;
54
66
  dependsOn: z.ZodNullable<z.ZodString>;
67
+ parentTaskId: z.ZodNullable<z.ZodString>;
55
68
  position: z.ZodNumber;
56
69
  archivedAt: z.ZodNullable<z.ZodISODateTime>;
57
70
  }, z.core.$strict>>;
@@ -64,6 +77,7 @@ export declare const BoardUpdateInput: z.ZodObject<{
64
77
  title: z.ZodString;
65
78
  terminal: z.ZodDefault<z.ZodBoolean>;
66
79
  }, z.core.$strict>>;
80
+ archiveVisible: z.ZodOptional<z.ZodBoolean>;
67
81
  idempotencyKey: z.ZodString;
68
82
  }, z.core.$strict>;
69
83
  export type BoardUpdateInput = z.infer<typeof BoardUpdateInput>;
@@ -76,6 +90,7 @@ export declare const BoardTaskCreateInput: z.ZodObject<{
76
90
  startDate: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
77
91
  dueDate: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
78
92
  dependsOn: z.ZodDefault<z.ZodNullable<z.ZodString>>;
93
+ parentTaskId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
79
94
  idempotencyKey: z.ZodString;
80
95
  }, z.core.$strict>;
81
96
  export type BoardTaskCreateInput = z.infer<typeof BoardTaskCreateInput>;
@@ -92,3 +107,28 @@ export declare const BoardTaskUpdateInput: z.ZodObject<{
92
107
  idempotencyKey: z.ZodString;
93
108
  }, z.core.$strict>;
94
109
  export type BoardTaskUpdateInput = z.infer<typeof BoardTaskUpdateInput>;
110
+ export declare const BoardAssignee: z.ZodObject<{
111
+ id: z.ZodString;
112
+ name: z.ZodString;
113
+ email: z.ZodEmail;
114
+ isMachine: z.ZodBoolean;
115
+ }, z.core.$strict>;
116
+ export type BoardAssignee = z.infer<typeof BoardAssignee>;
117
+ export declare const BoardAssigneeSearchInput: z.ZodObject<{
118
+ boardId: z.ZodString;
119
+ query: z.ZodString;
120
+ }, z.core.$strict>;
121
+ export type BoardAssigneeSearchInput = z.infer<typeof BoardAssigneeSearchInput>;
122
+ export declare const BoardAssigneeResolveInput: z.ZodObject<{
123
+ ids: z.ZodArray<z.ZodString>;
124
+ }, z.core.$strict>;
125
+ export type BoardAssigneeResolveInput = z.infer<typeof BoardAssigneeResolveInput>;
126
+ export declare const BoardAssigneeList: z.ZodObject<{
127
+ items: z.ZodArray<z.ZodObject<{
128
+ id: z.ZodString;
129
+ name: z.ZodString;
130
+ email: z.ZodEmail;
131
+ isMachine: z.ZodBoolean;
132
+ }, z.core.$strict>>;
133
+ }, z.core.$strict>;
134
+ export type BoardAssigneeList = z.infer<typeof BoardAssigneeList>;
@@ -3,6 +3,16 @@ import { IdempotencyKey, IntelId, IsoDateTime } from "./contract.js";
3
3
  // One column of a board. `terminal` is what "done" means here — a rule about the column rather than
4
4
  // a magic status name, so an installation may call it "Shipped" or "Abgerechnet" without anything
5
5
  // downstream having to know the word.
6
+ /**
7
+ * The id of the archive column, on every board (D68, #674).
8
+ *
9
+ * ⚠️ **It is derived, never stored.** A card sits in it because `nodes.archived_at` is set, and
10
+ * `board_tasks.status` keeps naming the working column it came from — that is what lets a card
11
+ * pulled back out land where it was. A configured column may not claim this id; `board_update`
12
+ * refuses it, because two columns with one id means every card in them lands in whichever the view
13
+ * draws first.
14
+ */
15
+ export const ARCHIVE_COLUMN_ID = "archived";
6
16
  export const BoardColumn = z.strictObject({
7
17
  id: z.string().min(1).max(60).describe("Stable key stored on every task in this column."),
8
18
  title: z.string().min(1).max(80).describe("What the column is called on screen."),
@@ -26,6 +36,18 @@ export const BoardTask = z.strictObject({
26
36
  startDate: IsoDateTime.nullable(),
27
37
  dueDate: IsoDateTime.nullable(),
28
38
  dependsOn: IntelId.nullable(),
39
+ /**
40
+ * The task this one sits under, or `null` when it sits directly on the board.
41
+ *
42
+ * ⚠️ **The hierarchy is the node tree** (`nodes.parent_id`), not a second column here — a task
43
+ * under a task IS the tree Intel already has, which is why the cycle guard that refuses moving a
44
+ * node into its own descendant covers this for free.
45
+ *
46
+ * ⚠️ **Under a filter this may name a task that is not in the same answer.** `board_get` returns
47
+ * what matches; a subtask can match while its parent does not. A reader that assumes the parent
48
+ * is present will lose the row — treat an unknown parent as top level.
49
+ */
50
+ parentTaskId: IntelId.nullable(),
29
51
  position: z.number(),
30
52
  archivedAt: IsoDateTime.nullable(),
31
53
  });
@@ -59,6 +81,14 @@ export const BoardView = z.strictObject({
59
81
  boardId: IntelId,
60
82
  title: z.string().min(1).max(240),
61
83
  columns: z.array(BoardColumn),
84
+ /**
85
+ * Whether the archive column is drawn (D68, #674).
86
+ *
87
+ * ⚠️ It travels even when it is `false`, because the settings dialog needs to draw the switch in
88
+ * both positions — and because "the column is not in `columns`" has two possible reasons, hidden
89
+ * and not-yet-supported, which a reader cannot tell apart from the list alone.
90
+ */
91
+ archiveVisible: z.boolean(),
62
92
  tasks: z.array(BoardTask),
63
93
  });
64
94
  export const BoardUpdateInput = z.strictObject({
@@ -66,7 +96,13 @@ export const BoardUpdateInput = z.strictObject({
66
96
  columns: z
67
97
  .array(BoardColumn)
68
98
  .min(1)
69
- .describe("The complete new column list, in order. Columns are replaced, not merged."),
99
+ .describe(`The complete new column list, in order. Columns are replaced, not merged. The archive column (\`${ARCHIVE_COLUMN_ID}\`) is not part of this list and cannot be named in it — it is on every board and is only shown or hidden.`),
100
+ // ⚠️ Shown or hidden, never removed. The archive is the same place on every board; a shelf that
101
+ // could be dragged between the working columns would be a shelf pretending to be a stage.
102
+ archiveVisible: z
103
+ .boolean()
104
+ .optional()
105
+ .describe("Whether the archive column is drawn. Omit to leave it as it is."),
70
106
  idempotencyKey: IdempotencyKey,
71
107
  });
72
108
  export const BoardTaskCreateInput = z.strictObject({
@@ -100,6 +136,12 @@ export const BoardTaskCreateInput = z.strictObject({
100
136
  dependsOn: IntelId.nullable()
101
137
  .default(null)
102
138
  .describe("A node this task waits for. Any node, not only another task."),
139
+ // ⚠️ Creating a subtask is ONE call; moving an existing task under another is `node_update`
140
+ // with a new `parentId`. Two paths for "where a task sits" would mean two cycle guards, and the
141
+ // one on the node path is the one that already exists.
142
+ parentTaskId: IntelId.nullable()
143
+ .default(null)
144
+ .describe("The task this one belongs under. It must be on the same board. Omit for a card that sits directly on the board."),
103
145
  idempotencyKey: IdempotencyKey,
104
146
  });
105
147
  // ⚠️ Moving a card IS this call: `status` and `position` together. There is deliberately no
@@ -117,7 +159,12 @@ export const BoardTaskUpdateInput = z.strictObject({
117
159
  .max(240)
118
160
  .optional()
119
161
  .describe("Refused on purpose: a card's title lives on the node. Rename it with node_update."),
120
- status: z.string().min(1).max(60).optional().describe("The column to move it to."),
162
+ status: z
163
+ .string()
164
+ .min(1)
165
+ .max(60)
166
+ .optional()
167
+ .describe(`The column to move it to. \`${ARCHIVE_COLUMN_ID}\` is not an ordinary column: it archives the card through the same path as \`node_archive\`, with the same refusals — a card that still has live subtasks is refused. Moving it to any other column while it is archived brings it back.`),
121
168
  position: z
122
169
  .number()
123
170
  .optional()
@@ -141,3 +188,39 @@ export const BoardTaskUpdateInput = z.strictObject({
141
188
  .describe("A node this card waits for. `null` clears the dependency."),
142
189
  idempotencyKey: IdempotencyKey,
143
190
  });
191
+ // ── Who a card can be given to ────────────────────────────────────────────────────────────────
192
+ // D70 (`~/Dev/anchrd/core/DECISIONS.md`): the people offered here are the ones who can actually open
193
+ // THIS board, never everyone who holds an account. The narrower answer is the point, and it is what
194
+ // makes the door safe to offer to anyone who can see the board.
195
+ //
196
+ // ⚠️ `email` travels because two colleagues share a first name more often than a picker can afford,
197
+ // and a list of indistinguishable "Anton" rows is not a picker. It is no wider than what gate hands
198
+ // any signed-in person at `POST /directory/search`; the narrowing this door adds is WHO appears,
199
+ // not WHAT is said about them.
200
+ //
201
+ // `isMachine` travels for the same reason gate carries it: whoever picks an assignee wants to know
202
+ // whether they are handing the card to a colleague or to an agent.
203
+ export const BoardAssignee = z.strictObject({
204
+ id: IntelId,
205
+ name: z.string(),
206
+ email: z.email(),
207
+ isMachine: z.boolean(),
208
+ });
209
+ export const BoardAssigneeSearchInput = z.strictObject({
210
+ boardId: IntelId.describe("The board whose people to offer. Only those who can open this board appear, so the same query against two boards can give two different answers."),
211
+ query: z
212
+ .string()
213
+ .describe("What was typed. Under two characters the answer is empty rather than everybody: a single letter is a listing under a different name."),
214
+ });
215
+ // ⚠️ A DIFFERENT question from the one above, and deliberately not board-scoped. This one names
216
+ // people who are ALREADY recorded on a card, so that a circle can carry initials instead of a raw
217
+ // id (`#258`). Someone whose access was withdrawn, or whose account was switched off, still has to
218
+ // be nameable: a card that reads as unassigned is a worse answer than the truth.
219
+ export const BoardAssigneeResolveInput = z.strictObject({
220
+ ids: z
221
+ .array(IntelId)
222
+ .min(1)
223
+ .max(100)
224
+ .describe("The ids to name. Ids that cannot be named are absent from the answer rather than reported, and more than a hundred is refused rather than silently shortened."),
225
+ });
226
+ export const BoardAssigneeList = z.strictObject({ items: z.array(BoardAssignee) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-contract",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {