@anchrd/intel-contract 0.10.0 → 0.11.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.
@@ -471,6 +471,7 @@ export declare const BoardStatus: z.ZodObject<{
471
471
  id: z.ZodString;
472
472
  label: z.ZodString;
473
473
  order: z.ZodNumber;
474
+ terminal: z.ZodBoolean;
474
475
  }, z.core.$strict>;
475
476
  export type BoardStatus = z.infer<typeof BoardStatus>;
476
477
  export declare const BoardDefaultStatuses: readonly BoardStatus[];
@@ -502,6 +503,45 @@ export declare const BoardTaskId: z.ZodString;
502
503
  export declare const BoardTaskLabel: z.ZodString;
503
504
  export declare const BoardTaskDate: z.ZodISODate;
504
505
  export declare const BoardTaskDescription: z.ZodString;
506
+ /**
507
+ * The tasks one task waits for (#285), each of them at most once (anchrd/intel#318).
508
+ *
509
+ * ⚠️ Board-internal only, enforced on the write path: a dependency on a task in another board would
510
+ * hang this node on a file that can change without anyone here noticing. Across boards the link is
511
+ * `references`, which points at the board NODE and lands in the link graph.
512
+ *
513
+ * ⚠️ Refused rather than folded together, the same shape as the status ids in `ConfigureBoardInput`.
514
+ * A repeat carries no information — but that is a fact about the value, not about the answer: a
515
+ * caller handed back a shorter list than it sent is told nothing, and composes the same one again.
516
+ * The refusal names the mistake once, and the caller is holding the list it has to fix. (The one
517
+ * place a repeat is folded instead is `upgradeStoredBoard`, where there is no caller to tell.)
518
+ *
519
+ * ⚠️ It sits on the STORED task as well as on the two inputs, so a consumer may rely on it rather
520
+ * than defend against it — `createBoardGraph` mints one edge key per pair, and a second one threw
521
+ * the whole graph view off the screen for everybody looking at that board.
522
+ */
523
+ export declare const BoardTaskDependsOn: z.ZodArray<z.ZodString>;
524
+ /**
525
+ * A task's labels, each of them at most once (anchrd/intel#318).
526
+ *
527
+ * ⚠️ The same rule the detail panel has always applied to what a person types — it refuses to add a
528
+ * label the task already carries — stated where every surface meets it, because the MCP write path
529
+ * did not. A repeated label draws the same chip twice on the card, with two remove buttons of which
530
+ * either takes both away, and weights that word higher in the search text (`indexing.ts`).
531
+ */
532
+ export declare const BoardTaskLabels: z.ZodArray<z.ZodString>;
533
+ /**
534
+ * The Intel nodes a task points at, each of them at most once (anchrd/intel#318).
535
+ *
536
+ * They land in the link graph as `text` links, the same way a document's inline links do, so what a
537
+ * board points at is visible from the other side too.
538
+ *
539
+ * ⚠️ Distinct for the same reason as `labels`: the picker in the detail panel already refuses one
540
+ * the task holds, and the link graph counts a repeat once anyway (`ON CONFLICT DO NOTHING`), so a
541
+ * duplicate is a second row in the panel and nothing else — which is exactly the kind of value that
542
+ * has no reading and should not be storable.
543
+ */
544
+ export declare const BoardTaskReferences: z.ZodArray<z.ZodString>;
505
545
  export declare const BoardTask: z.ZodObject<{
506
546
  id: z.ZodString;
507
547
  title: z.ZodString;
@@ -528,6 +568,7 @@ export declare const BoardDocument: z.ZodObject<{
528
568
  id: z.ZodString;
529
569
  label: z.ZodString;
530
570
  order: z.ZodNumber;
571
+ terminal: z.ZodBoolean;
531
572
  }, z.core.$strict>>;
532
573
  tasks: z.ZodArray<z.ZodObject<{
533
574
  id: z.ZodString;
@@ -581,6 +622,7 @@ export declare const NodeBoard: z.ZodObject<{
581
622
  id: z.ZodString;
582
623
  label: z.ZodString;
583
624
  order: z.ZodNumber;
625
+ terminal: z.ZodBoolean;
584
626
  }, z.core.$strict>>;
585
627
  tasks: z.ZodArray<z.ZodObject<{
586
628
  id: z.ZodString;
@@ -609,6 +651,7 @@ export type NodeBoard = z.infer<typeof NodeBoard>;
609
651
  export declare const BoardStatusInput: z.ZodObject<{
610
652
  id: z.ZodString;
611
653
  label: z.ZodString;
654
+ terminal: z.ZodOptional<z.ZodBoolean>;
612
655
  }, z.core.$strict>;
613
656
  export type BoardStatusInput = z.infer<typeof BoardStatusInput>;
614
657
  export declare const ConfigureBoardInput: z.ZodObject<{
@@ -616,6 +659,7 @@ export declare const ConfigureBoardInput: z.ZodObject<{
616
659
  statuses: z.ZodArray<z.ZodObject<{
617
660
  id: z.ZodString;
618
661
  label: z.ZodString;
662
+ terminal: z.ZodOptional<z.ZodBoolean>;
619
663
  }, z.core.$strict>>;
620
664
  idempotencyKey: z.ZodString;
621
665
  }, z.core.$strict>;
@@ -829,6 +873,7 @@ export declare const ConfigureBoardResult: z.ZodObject<{
829
873
  id: z.ZodString;
830
874
  label: z.ZodString;
831
875
  order: z.ZodNumber;
876
+ terminal: z.ZodBoolean;
832
877
  }, z.core.$strict>>;
833
878
  }, z.core.$strict>;
834
879
  export type ConfigureBoardResult = z.infer<typeof ConfigureBoardResult>;
@@ -1761,6 +1806,7 @@ export declare const ToolCatalog: z.ZodObject<{
1761
1806
  }, z.core.$strict>;
1762
1807
  fingerprint: z.ZodString;
1763
1808
  }, z.core.$strict>>;
1809
+ reached: z.ZodOptional<z.ZodArray<z.ZodString>>;
1764
1810
  }, z.core.$strict>;
1765
1811
  export type ToolCatalog = z.infer<typeof ToolCatalog>;
1766
1812
  /**
@@ -291,14 +291,32 @@ export const BoardStatus = z.strictObject({
291
291
  // both claiming position 3 is a board no surface could draw, and it is a state nobody has to be
292
292
  // able to reason about if it cannot be expressed.
293
293
  order: z.number().int().nonnegative(),
294
+ /**
295
+ * Whether standing in this column means the work is finished (anchrd/intel#311).
296
+ *
297
+ * It answers the one question #285 left open — when a `dependsOn` is satisfied — and it is the
298
+ * ONLY answer to it. There is deliberately no second reading anywhere: a surface that decided
299
+ * "done" for itself would decide it differently the first time somebody reconfigured a board.
300
+ *
301
+ * ⚠️ It is a property of the status, not a position in the list, and that distinction is the
302
+ * whole ticket. #286 first read "the last column before `archived`" as done, reasoning by symmetry
303
+ * with the server's rule that a new task lands in the first column that is not `archived`. But a
304
+ * status list is configurable on purpose, so `… done → blocked → archived` makes "blocked" mean
305
+ * finished — silently, with a wrong blocked marker as the only symptom.
306
+ *
307
+ * ⚠️ Several columns may carry it. "Done" and a cancelled-like column are both ends of the work,
308
+ * and nothing waiting on a cancelled task is still blocked by it.
309
+ */
310
+ terminal: z.boolean(),
294
311
  });
295
312
  // What a board starts out with. Five columns, of which the last one is the fixed `archived` shelf.
313
+ // `done` and the shelf are where work ends; the three before them are not (anchrd/intel#311).
296
314
  export const BoardDefaultStatuses = [
297
- { id: "backlog", label: "Backlog", order: 0 },
298
- { id: "in_progress", label: "In progress", order: 1 },
299
- { id: "review", label: "Review", order: 2 },
300
- { id: "done", label: "Done", order: 3 },
301
- { id: ArchivedBoardStatusId, label: "Archived", order: 4 },
315
+ { id: "backlog", label: "Backlog", order: 0, terminal: false },
316
+ { id: "in_progress", label: "In progress", order: 1, terminal: false },
317
+ { id: "review", label: "Review", order: 2, terminal: false },
318
+ { id: "done", label: "Done", order: 3, terminal: true },
319
+ { id: ArchivedBoardStatusId, label: "Archived", order: 4, terminal: true },
302
320
  ];
303
321
  /**
304
322
  * Who a task is on: a person Gate knows, or an agent node in this installation (#285).
@@ -328,27 +346,76 @@ export const BoardTaskDate = z.iso.date();
328
346
  // Markdown, and capped: a task's description is a card, and what needs more than this is a document
329
347
  // the task can point at through `references`.
330
348
  export const BoardTaskDescription = z.string().max(20_000);
349
+ /**
350
+ * The tasks one task waits for (#285), each of them at most once (anchrd/intel#318).
351
+ *
352
+ * ⚠️ Board-internal only, enforced on the write path: a dependency on a task in another board would
353
+ * hang this node on a file that can change without anyone here noticing. Across boards the link is
354
+ * `references`, which points at the board NODE and lands in the link graph.
355
+ *
356
+ * ⚠️ Refused rather than folded together, the same shape as the status ids in `ConfigureBoardInput`.
357
+ * A repeat carries no information — but that is a fact about the value, not about the answer: a
358
+ * caller handed back a shorter list than it sent is told nothing, and composes the same one again.
359
+ * The refusal names the mistake once, and the caller is holding the list it has to fix. (The one
360
+ * place a repeat is folded instead is `upgradeStoredBoard`, where there is no caller to tell.)
361
+ *
362
+ * ⚠️ It sits on the STORED task as well as on the two inputs, so a consumer may rely on it rather
363
+ * than defend against it — `createBoardGraph` mints one edge key per pair, and a second one threw
364
+ * the whole graph view off the screen for everybody looking at that board.
365
+ */
366
+ export const BoardTaskDependsOn = z
367
+ .array(BoardTaskId)
368
+ .max(64)
369
+ .refine((ids) => new Set(ids).size === ids.length, {
370
+ error: "A task can be named only once in dependsOn",
371
+ });
372
+ /**
373
+ * A task's labels, each of them at most once (anchrd/intel#318).
374
+ *
375
+ * ⚠️ The same rule the detail panel has always applied to what a person types — it refuses to add a
376
+ * label the task already carries — stated where every surface meets it, because the MCP write path
377
+ * did not. A repeated label draws the same chip twice on the card, with two remove buttons of which
378
+ * either takes both away, and weights that word higher in the search text (`indexing.ts`).
379
+ */
380
+ export const BoardTaskLabels = z
381
+ .array(BoardTaskLabel)
382
+ .max(32)
383
+ .refine((labels) => new Set(labels).size === labels.length, {
384
+ error: "A label can be named only once",
385
+ });
386
+ /**
387
+ * The Intel nodes a task points at, each of them at most once (anchrd/intel#318).
388
+ *
389
+ * They land in the link graph as `text` links, the same way a document's inline links do, so what a
390
+ * board points at is visible from the other side too.
391
+ *
392
+ * ⚠️ Distinct for the same reason as `labels`: the picker in the detail panel already refuses one
393
+ * the task holds, and the link graph counts a repeat once anyway (`ON CONFLICT DO NOTHING`), so a
394
+ * duplicate is a second row in the panel and nothing else — which is exactly the kind of value that
395
+ * has no reading and should not be storable.
396
+ */
397
+ export const BoardTaskReferences = z
398
+ .array(IntelId)
399
+ .max(64)
400
+ .refine((ids) => new Set(ids).size === ids.length, {
401
+ error: "A node can be referenced only once",
402
+ });
331
403
  export const BoardTask = z.strictObject({
332
404
  id: BoardTaskId,
333
405
  title: z.string().trim().min(1).max(240),
334
406
  status: BoardStatusId,
335
407
  assignee: BoardAssignee.nullable(),
336
- labels: z.array(BoardTaskLabel).max(32),
408
+ labels: BoardTaskLabels,
337
409
  startDate: BoardTaskDate.nullable(),
338
410
  dueDate: BoardTaskDate.nullable(),
339
411
  // ⚠️ The whole hierarchy in one field, deliberately: epic, task and subtask are a DEPTH and not a
340
412
  // type (#285). A `kind` beside it would allow a subtask under nothing and an epic under an epic,
341
413
  // and every surface would then need its own opinion about which combinations mean anything.
342
414
  parentId: BoardTaskId.nullable(),
343
- // ⚠️ Board-internal only, enforced on the write path: a dependency on a task in another board
344
- // would hang this node on a file that can change without anyone here noticing. Across boards the
345
- // link is `references`, which points at the board NODE and lands in the link graph.
346
- dependsOn: z.array(BoardTaskId).max(64),
415
+ dependsOn: BoardTaskDependsOn,
347
416
  order: BoardTaskOrder,
348
417
  description: BoardTaskDescription,
349
- // Node ids. They land in the link graph as `text` links, the same way a document's inline links
350
- // do, so what a board points at is visible from the other side too.
351
- references: z.array(IntelId).max(64),
418
+ references: BoardTaskReferences,
352
419
  });
353
420
  // The whole board, as it is stored and as it is read. There is no second representation to keep in
354
421
  // step with it — this document is the file.
@@ -370,6 +437,16 @@ export const NodeBoard = z.strictObject({
370
437
  export const BoardStatusInput = z.strictObject({
371
438
  id: BoardStatusId,
372
439
  label: z.string().trim().min(1).max(60),
440
+ /**
441
+ * Whether this column means finished (anchrd/intel#311).
442
+ *
443
+ * ⚠️ Optional, and the absence is not the same as `false`. A caller who says nothing gets the
444
+ * server's answer — `false` for an ordinary column, `true` for the shelf, which cannot be
445
+ * anything else. Making it a required boolean would force every caller that only wanted to rename
446
+ * a column to restate the whole board's notion of done, and getting one entry wrong there is a
447
+ * silent change to what counts as blocked.
448
+ */
449
+ terminal: z.boolean().optional(),
373
450
  });
374
451
  // The status list, written whole and in the order it should be drawn — never a patch. Adding,
375
452
  // renaming and reordering are all this one call, and `archived` has to be in what it is given.
@@ -384,7 +461,12 @@ export const ConfigureBoardInput = z.strictObject({
384
461
  })
385
462
  .refine((statuses) => statuses.some((status) => status.id === ArchivedBoardStatusId), {
386
463
  error: `The "${ArchivedBoardStatusId}" status cannot be removed`,
387
- }),
464
+ })
465
+ // ⚠️ Refused rather than corrected, the same way removing the shelf is refused. A task swept
466
+ // onto `archived` is finished with, and a board that could declare the shelf non-terminal would
467
+ // hold every archived task open as a blocker forever. Only an EXPLICIT `false` is refused —
468
+ // saying nothing is fine and means the server's `true` (anchrd/intel#311).
469
+ .refine((statuses) => statuses.find((status) => status.id === ArchivedBoardStatusId)?.terminal !== false, { error: `The "${ArchivedBoardStatusId}" status is always terminal` }),
388
470
  idempotencyKey: z.string().min(8).max(200),
389
471
  });
390
472
  /**
@@ -402,13 +484,13 @@ export const AddBoardTaskInput = z.strictObject({
402
484
  // the shelf.
403
485
  status: BoardStatusId.optional(),
404
486
  assignee: BoardAssignee.nullable().default(null),
405
- labels: z.array(BoardTaskLabel).max(32).default([]),
487
+ labels: BoardTaskLabels.default([]),
406
488
  startDate: BoardTaskDate.nullable().default(null),
407
489
  dueDate: BoardTaskDate.nullable().default(null),
408
490
  parentId: BoardTaskId.nullable().default(null),
409
- dependsOn: z.array(BoardTaskId).max(64).default([]),
491
+ dependsOn: BoardTaskDependsOn.default([]),
410
492
  description: BoardTaskDescription.default(""),
411
- references: z.array(IntelId).max(64).default([]),
493
+ references: BoardTaskReferences.default([]),
412
494
  // Where among its neighbours it goes. Both absent puts it last in its column.
413
495
  afterTaskId: BoardTaskId.nullable().default(null),
414
496
  beforeTaskId: BoardTaskId.nullable().default(null),
@@ -428,12 +510,12 @@ export const UpdateBoardTaskInput = z
428
510
  taskId: BoardTaskId,
429
511
  title: z.string().trim().min(1).max(240).optional(),
430
512
  assignee: BoardAssignee.nullable().optional(),
431
- labels: z.array(BoardTaskLabel).max(32).optional(),
513
+ labels: BoardTaskLabels.optional(),
432
514
  startDate: BoardTaskDate.nullable().optional(),
433
515
  dueDate: BoardTaskDate.nullable().optional(),
434
- dependsOn: z.array(BoardTaskId).max(64).optional(),
516
+ dependsOn: BoardTaskDependsOn.optional(),
435
517
  description: BoardTaskDescription.optional(),
436
- references: z.array(IntelId).max(64).optional(),
518
+ references: BoardTaskReferences.optional(),
437
519
  idempotencyKey: z.string().min(8).max(200),
438
520
  })
439
521
  .refine((input) => input.title !== undefined ||
@@ -1030,6 +1112,24 @@ export const ToolCapability = z.strictObject({
1030
1112
  export const ToolCatalog = z.strictObject({
1031
1113
  portalConnected: z.boolean(),
1032
1114
  items: z.array(ToolCapability),
1115
+ /**
1116
+ * Which delegated servers actually contributed a tool to this catalog (#289).
1117
+ *
1118
+ * ⚠️ Present only where the attribution was actually made — a delegated caller whose catalog was
1119
+ * read. It is absent for an ordinary user, and absent as well when the answer comes from one of
1120
+ * the short paths that never reach the portal (nothing delegated, no portal sign-in, connection
1121
+ * dropped). Absent therefore means "not stated", never "nothing arrived"; `[]` is the second one.
1122
+ *
1123
+ * That it is missing rather than empty on those paths is deliberate rather than half-finished:
1124
+ * the attribution already happens for a delegation — `capabilities` has to make it to cut the
1125
+ * list — so naming it costs nothing there, while computing the same thing for an ordinary user
1126
+ * would mean a second portal request per call, for a question their screen does not ask.
1127
+ *
1128
+ * ⚠️ It is the answer to "what arrived", never to "what was granted". A server missing here has
1129
+ * been switched off, revoked, or is failing right now; the delegation in the definition is
1130
+ * unchanged. Reading it the other way round would turn an outage into a permission change.
1131
+ */
1132
+ reached: z.array(ToolServerHandle).optional(),
1033
1133
  });
1034
1134
  /**
1035
1135
  * One MCP server the asking user reaches right now, as the portal itself names it (D30).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-contract",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {