@anchrd/intel-contract 0.27.0 → 0.29.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.
@@ -9,10 +9,23 @@ import { z } from "zod";
9
9
  * draws first.
10
10
  */
11
11
  export declare const ARCHIVE_COLUMN_ID = "archived";
12
+ /**
13
+ * What a colour picker starts on for a column that has none (#725).
14
+ *
15
+ * ⚠️ **Here rather than in the browser package, and that is not tidiness.** The token linter refuses
16
+ * a hard-coded hex in `packages/ui`, and it is right to: a colour written into a component bypasses
17
+ * the theme. This one is not a theme colour, it is DATA — the value a person is about to choose
18
+ * from and the api writes as a default — so it belongs where both sides may read it, which is here.
19
+ *
20
+ * ⚠️ **It is the widget's starting point, never a written value.** `color` stays absent until
21
+ * somebody picks one, so opening the dialog does not repaint a board that predates colours.
22
+ */
23
+ export declare const DEFAULT_COLUMN_COLOR = "#9ca3af";
12
24
  export declare const BoardColumn: z.ZodObject<{
13
25
  id: z.ZodString;
14
26
  title: z.ZodString;
15
27
  terminal: z.ZodDefault<z.ZodBoolean>;
28
+ color: z.ZodOptional<z.ZodString>;
16
29
  }, z.core.$strict>;
17
30
  export type BoardColumn = z.infer<typeof BoardColumn>;
18
31
  export declare const BoardTask: z.ZodObject<{
@@ -53,6 +66,7 @@ export declare const BoardView: z.ZodObject<{
53
66
  id: z.ZodString;
54
67
  title: z.ZodString;
55
68
  terminal: z.ZodDefault<z.ZodBoolean>;
69
+ color: z.ZodOptional<z.ZodString>;
56
70
  }, z.core.$strict>>;
57
71
  archiveVisible: z.ZodBoolean;
58
72
  tasks: z.ZodArray<z.ZodObject<{
@@ -76,6 +90,7 @@ export declare const BoardUpdateInput: z.ZodObject<{
76
90
  id: z.ZodString;
77
91
  title: z.ZodString;
78
92
  terminal: z.ZodDefault<z.ZodBoolean>;
93
+ color: z.ZodOptional<z.ZodString>;
79
94
  }, z.core.$strict>>;
80
95
  archiveVisible: z.ZodOptional<z.ZodBoolean>;
81
96
  idempotencyKey: z.ZodString;
@@ -130,5 +145,6 @@ export declare const BoardAssigneeList: z.ZodObject<{
130
145
  email: z.ZodEmail;
131
146
  isMachine: z.ZodBoolean;
132
147
  }, z.core.$strict>>;
148
+ more: z.ZodDefault<z.ZodNumber>;
133
149
  }, z.core.$strict>;
134
150
  export type BoardAssigneeList = z.infer<typeof BoardAssigneeList>;
@@ -13,6 +13,18 @@ import { IdempotencyKey, IntelId, IsoDateTime } from "./contract.js";
13
13
  * draws first.
14
14
  */
15
15
  export const ARCHIVE_COLUMN_ID = "archived";
16
+ /**
17
+ * What a colour picker starts on for a column that has none (#725).
18
+ *
19
+ * ⚠️ **Here rather than in the browser package, and that is not tidiness.** The token linter refuses
20
+ * a hard-coded hex in `packages/ui`, and it is right to: a colour written into a component bypasses
21
+ * the theme. This one is not a theme colour, it is DATA — the value a person is about to choose
22
+ * from and the api writes as a default — so it belongs where both sides may read it, which is here.
23
+ *
24
+ * ⚠️ **It is the widget's starting point, never a written value.** `color` stays absent until
25
+ * somebody picks one, so opening the dialog does not repaint a board that predates colours.
26
+ */
27
+ export const DEFAULT_COLUMN_COLOR = "#9ca3af";
16
28
  export const BoardColumn = z.strictObject({
17
29
  id: z.string().min(1).max(60).describe("Stable key stored on every task in this column."),
18
30
  title: z.string().min(1).max(80).describe("What the column is called on screen."),
@@ -20,6 +32,20 @@ export const BoardColumn = z.strictObject({
20
32
  .boolean()
21
33
  .default(false)
22
34
  .describe("Whether a task in this column counts as finished. More than one column may be."),
35
+ /**
36
+ * ⚠️ **The colour belongs to the COLUMN, never to the card** (#725, and #365 took the status dot
37
+ * off the card on purpose). A card is in a column; drawing the column's colour on it would be the
38
+ * same fact twice, and the second copy is the one that goes stale.
39
+ *
40
+ * A free value rather than a fixed palette (Jack's decision 2026-08-22). It only ever fills a
41
+ * DOT, so a badly chosen one is hard to see but makes nothing unreadable — which is what makes a
42
+ * free choice affordable here and would not make it affordable behind text.
43
+ */
44
+ color: z
45
+ .string()
46
+ .regex(/^#[0-9a-fA-F]{6}$/, "A colour is six hex digits behind a hash, like #6b7280.")
47
+ .optional()
48
+ .describe("The dot in front of this column's title, and in front of a linked task that sits in it. Six hex digits behind a hash. Absent means the reader's own default, so an older board is not repainted by this field appearing."),
23
49
  });
24
50
  // One card, as the board view needs it: everything to draw and sort by, and NOTHING from R2. The
25
51
  // body is fetched per task through `node_version_get` when somebody opens one.
@@ -210,7 +236,7 @@ export const BoardAssigneeSearchInput = z.strictObject({
210
236
  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
237
  query: z
212
238
  .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."),
239
+ .describe("What was typed. EMPTY names up to three people who reach this board, so a picker can open with suggestions instead of nothing. One character answers empty rather than everybody: a single letter is a listing under a different name, while an empty query asks for a bounded set this board already knows."),
214
240
  });
215
241
  // ⚠️ A DIFFERENT question from the one above, and deliberately not board-scoped. This one names
216
242
  // people who are ALREADY recorded on a card, so that a circle can carry initials instead of a raw
@@ -223,4 +249,14 @@ export const BoardAssigneeResolveInput = z.strictObject({
223
249
  .max(100)
224
250
  .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
251
  });
226
- export const BoardAssigneeList = z.strictObject({ items: z.array(BoardAssignee) });
252
+ export const BoardAssigneeList = z.strictObject({
253
+ items: z.array(BoardAssignee),
254
+ /**
255
+ * ⚠️ **How many are left over, so the cap is visible rather than silent** (#757). Three suggestions
256
+ * that look like the whole list are worse than three that say "and 4 more": the first teaches the
257
+ * reader that nobody else exists.
258
+ *
259
+ * `0` on a search answer, where the cap is gate's own and this door does not own it.
260
+ */
261
+ more: z.number().int().min(0).default(0),
262
+ });
@@ -5,10 +5,26 @@ export declare const FeedAction: z.ZodEnum<{
5
5
  "node.update": "node.update";
6
6
  "node.append": "node.append";
7
7
  "node.archive": "node.archive";
8
+ "node.purge": "node.purge";
8
9
  "node.share": "node.share";
9
10
  "node.revoke": "node.revoke";
11
+ "flows.create": "flows.create";
12
+ "flows.save": "flows.save";
13
+ "flows.update": "flows.update";
14
+ "flows.publish": "flows.publish";
15
+ "flows.unpublish": "flows.unpublish";
16
+ "flows.archive": "flows.archive";
17
+ "flows.share": "flows.share";
18
+ "flows.revoke": "flows.revoke";
19
+ "flows.purge": "flows.purge";
10
20
  }>;
11
21
  export type FeedAction = z.infer<typeof FeedAction>;
22
+ /**
23
+ * What an entry is about. Derived from the action rather than carried separately: the two can then
24
+ * never disagree, and a reader that has to draw a flow differently from a document does not depend
25
+ * on a second field being filled in correctly.
26
+ */
27
+ export declare function feedKindOf(action: FeedAction): "node" | "flow";
12
28
  export declare const FeedPathSegment: z.ZodObject<{
13
29
  id: z.ZodString;
14
30
  title: z.ZodString;
@@ -23,11 +39,21 @@ export declare const FeedEvent: z.ZodObject<{
23
39
  "node.update": "node.update";
24
40
  "node.append": "node.append";
25
41
  "node.archive": "node.archive";
42
+ "node.purge": "node.purge";
26
43
  "node.share": "node.share";
27
44
  "node.revoke": "node.revoke";
45
+ "flows.create": "flows.create";
46
+ "flows.save": "flows.save";
47
+ "flows.update": "flows.update";
48
+ "flows.publish": "flows.publish";
49
+ "flows.unpublish": "flows.unpublish";
50
+ "flows.archive": "flows.archive";
51
+ "flows.share": "flows.share";
52
+ "flows.revoke": "flows.revoke";
53
+ "flows.purge": "flows.purge";
28
54
  }>;
29
- nodeId: z.ZodString;
30
- nodeTitle: z.ZodString;
55
+ resourceId: z.ZodString;
56
+ resourceTitle: z.ZodString;
31
57
  path: z.ZodArray<z.ZodObject<{
32
58
  id: z.ZodString;
33
59
  title: z.ZodString;
@@ -53,11 +79,21 @@ export declare const FeedListResponse: z.ZodObject<{
53
79
  "node.update": "node.update";
54
80
  "node.append": "node.append";
55
81
  "node.archive": "node.archive";
82
+ "node.purge": "node.purge";
56
83
  "node.share": "node.share";
57
84
  "node.revoke": "node.revoke";
85
+ "flows.create": "flows.create";
86
+ "flows.save": "flows.save";
87
+ "flows.update": "flows.update";
88
+ "flows.publish": "flows.publish";
89
+ "flows.unpublish": "flows.unpublish";
90
+ "flows.archive": "flows.archive";
91
+ "flows.share": "flows.share";
92
+ "flows.revoke": "flows.revoke";
93
+ "flows.purge": "flows.purge";
58
94
  }>;
59
- nodeId: z.ZodString;
60
- nodeTitle: z.ZodString;
95
+ resourceId: z.ZodString;
96
+ resourceTitle: z.ZodString;
61
97
  path: z.ZodArray<z.ZodObject<{
62
98
  id: z.ZodString;
63
99
  title: z.ZodString;
@@ -16,9 +16,45 @@ export const FeedAction = z.enum([
16
16
  "node.update",
17
17
  "node.append",
18
18
  "node.archive",
19
+ // ⚠️ The one event whose node no longer exists when it is read. Its visibility runs over the
20
+ // surviving folder, from `parentId` in the metadata, and its title comes from there too — the row
21
+ // it would have been read from is gone (#645).
22
+ "node.purge",
19
23
  "node.share",
20
24
  "node.revoke",
25
+ // The flow half (#763). Twelve kinds are written; these are the ones a person did rather than a
26
+ // machine bookkeeping step.
27
+ //
28
+ // ⚠️ The three run kinds — `flows.run`, `flows.complete`, `flows.cancel` — are NOT here, and the
29
+ // reason is not that the list refuses them: they are written against `resource_type = 'flow-run'`
30
+ // with the RUN's id, which is a third kind this query does not read at all. Admitting them here
31
+ // would change nothing on the surface and quietly suggest they had been considered.
32
+ //
33
+ // They need their own everything — a third visibility walk (run → flow → folder), a title read
34
+ // from the flow rather than the run, a third link target — and one decision first: an agent
35
+ // writes three of them per run, so without any summarising a busy morning buries every human
36
+ // entry under identical cards. That is #767, and it is a real question rather than a leftover.
37
+ "flows.create",
38
+ "flows.save",
39
+ "flows.update",
40
+ "flows.publish",
41
+ "flows.unpublish",
42
+ "flows.archive",
43
+ "flows.share",
44
+ "flows.revoke",
45
+ // ⚠️ The flow-side twin of `node.purge`, and it carries the same two consequences: its row is
46
+ // gone when the card is read, so both its visibility and its title come from the metadata of the
47
+ // event itself (#763, D73).
48
+ "flows.purge",
21
49
  ]);
50
+ /**
51
+ * What an entry is about. Derived from the action rather than carried separately: the two can then
52
+ * never disagree, and a reader that has to draw a flow differently from a document does not depend
53
+ * on a second field being filled in correctly.
54
+ */
55
+ export function feedKindOf(action) {
56
+ return action.startsWith("flows.") ? "flow" : "node";
57
+ }
22
58
  // One step on the way down to the node, from the root. It carries the id as well as the title
23
59
  // because the surface links each step, and two folders may share a name.
24
60
  export const FeedPathSegment = z.strictObject({
@@ -27,10 +63,14 @@ export const FeedPathSegment = z.strictObject({
27
63
  });
28
64
  // One card's worth of journal.
29
65
  //
30
- // ⚠️ `nodeTitle` and `path` are read from `nodes` at query time, so they are TODAY's title and
31
- // today's place — not the ones the node had when the event happened. That is the right answer for a
32
- // feed (the reader wants to find the thing now) and the wrong one for an audit trail, which is one
33
- // more reason these are two doors and not one.
66
+ // ⚠️ `resourceTitle` and `path` are read from `nodes` or `flows` at query time, so they are TODAY's
67
+ // title and today's place — not the ones the thing had when the event happened. That is the right
68
+ // answer for a feed (the reader wants to find it now) and the wrong one for an audit trail, which is
69
+ // one more reason these are two doors and not one.
70
+ //
71
+ // ⚠️ The fields say `resource` and not `node` because half of them are not nodes (#763). Which half
72
+ // is answered by `feedKindOf(action)` and by nothing on the card — a second field saying "flow"
73
+ // beside an action that already says `flows.publish` is a second truth, and two truths disagree.
34
74
  //
35
75
  // `metadata` is whatever the write site recorded, untyped on purpose: a schema per action would
36
76
  // have to be kept in step with eleven write sites and would go stale in silence. What the surface
@@ -40,8 +80,8 @@ export const FeedEvent = z.strictObject({
40
80
  id: IntelId,
41
81
  actorId: z.string().min(1),
42
82
  action: FeedAction,
43
- nodeId: IntelId,
44
- nodeTitle: z.string().min(1),
83
+ resourceId: IntelId,
84
+ resourceTitle: z.string().min(1),
45
85
  path: z.array(FeedPathSegment),
46
86
  metadata: z.record(z.string(), z.unknown()),
47
87
  occurredAt: IsoDateTime,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-contract",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {