@anchrd/intel-contract 0.10.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.
- package/dist/contract/contract.d.ts +151 -0
- package/dist/contract/contract.js +178 -21
- package/package.json +1 -1
|
@@ -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[];
|
|
@@ -496,12 +497,66 @@ export type BoardAssignee = z.infer<typeof BoardAssignee>;
|
|
|
496
497
|
* mints a key between theirs, so moving one task writes one task and renumbers nothing — the whole
|
|
497
498
|
* reason a board is not addressed by position the way a table's rows are (`TableRowPosition`).
|
|
498
499
|
* A hand-written key could collide, and two tasks sharing a key have no defined order at all.
|
|
500
|
+
*
|
|
501
|
+
* ⚠️ This CHARACTER CLASS is the truth about a stored key, not `fractional-indexing`, and the two
|
|
502
|
+
* are deliberately not the same set (anchrd/intel#359). A board that arrived through a bundle may
|
|
503
|
+
* spell `"0"`, `"a00"`, `"zzz"` or `"A"` — all of them legal here, none of them readable by that
|
|
504
|
+
* library. Narrowing the regex to what it reads would be a rule on the STORED document, so a board
|
|
505
|
+
* carrying one would stop parsing in all four places a board body is read (the board read, the
|
|
506
|
+
* indexer, the link reader, the bundle import) — unreadable, unsearchable and unmovable at once,
|
|
507
|
+
* over a value its owner never wrote. That is the trap #311, #318 and #321 each walked into from a
|
|
508
|
+
* different side.
|
|
509
|
+
*
|
|
510
|
+
* ⚠️ What every consumer may therefore rely on is exactly what stands here: keys are non-empty
|
|
511
|
+
* strings over `[0-9A-Za-z]` and are ORDERED BY STRING COMPARISON. Nothing may assume more — and
|
|
512
|
+
* `packages/api`'s `orderBetween` is the one place that asks `fractional-indexing` for a key and
|
|
513
|
+
* carries on without it when it refuses a bound.
|
|
499
514
|
*/
|
|
515
|
+
export declare const MaxBoardTaskOrderLength = 64;
|
|
500
516
|
export declare const BoardTaskOrder: z.ZodString;
|
|
501
517
|
export declare const BoardTaskId: z.ZodString;
|
|
502
518
|
export declare const BoardTaskLabel: z.ZodString;
|
|
503
519
|
export declare const BoardTaskDate: z.ZodISODate;
|
|
504
520
|
export declare const BoardTaskDescription: z.ZodString;
|
|
521
|
+
/**
|
|
522
|
+
* The tasks one task waits for (#285), each of them at most once (anchrd/intel#318).
|
|
523
|
+
*
|
|
524
|
+
* ⚠️ Board-internal only, enforced on the write path: a dependency on a task in another board would
|
|
525
|
+
* hang this node on a file that can change without anyone here noticing. Across boards the link is
|
|
526
|
+
* `references`, which points at the board NODE and lands in the link graph.
|
|
527
|
+
*
|
|
528
|
+
* ⚠️ Refused rather than folded together, the same shape as the status ids in `ConfigureBoardInput`.
|
|
529
|
+
* A repeat carries no information — but that is a fact about the value, not about the answer: a
|
|
530
|
+
* caller handed back a shorter list than it sent is told nothing, and composes the same one again.
|
|
531
|
+
* The refusal names the mistake once, and the caller is holding the list it has to fix. (The one
|
|
532
|
+
* place a repeat is folded instead is `upgradeStoredBoard`, where there is no caller to tell.)
|
|
533
|
+
*
|
|
534
|
+
* ⚠️ It sits on the STORED task as well as on the two inputs, so a consumer may rely on it rather
|
|
535
|
+
* than defend against it — `createBoardGraph` mints one edge key per pair, and a second one threw
|
|
536
|
+
* the whole graph view off the screen for everybody looking at that board.
|
|
537
|
+
*/
|
|
538
|
+
export declare const BoardTaskDependsOn: z.ZodArray<z.ZodString>;
|
|
539
|
+
/**
|
|
540
|
+
* A task's labels, each of them at most once (anchrd/intel#318).
|
|
541
|
+
*
|
|
542
|
+
* ⚠️ The same rule the detail panel has always applied to what a person types — it refuses to add a
|
|
543
|
+
* label the task already carries — stated where every surface meets it, because the MCP write path
|
|
544
|
+
* did not. A repeated label draws the same chip twice on the card, with two remove buttons of which
|
|
545
|
+
* either takes both away, and weights that word higher in the search text (`indexing.ts`).
|
|
546
|
+
*/
|
|
547
|
+
export declare const BoardTaskLabels: z.ZodArray<z.ZodString>;
|
|
548
|
+
/**
|
|
549
|
+
* The Intel nodes a task points at, each of them at most once (anchrd/intel#318).
|
|
550
|
+
*
|
|
551
|
+
* They land in the link graph as `text` links, the same way a document's inline links do, so what a
|
|
552
|
+
* board points at is visible from the other side too.
|
|
553
|
+
*
|
|
554
|
+
* ⚠️ Distinct for the same reason as `labels`: the picker in the detail panel already refuses one
|
|
555
|
+
* the task holds, and the link graph counts a repeat once anyway (`ON CONFLICT DO NOTHING`), so a
|
|
556
|
+
* duplicate is a second row in the panel and nothing else — which is exactly the kind of value that
|
|
557
|
+
* has no reading and should not be storable.
|
|
558
|
+
*/
|
|
559
|
+
export declare const BoardTaskReferences: z.ZodArray<z.ZodString>;
|
|
505
560
|
export declare const BoardTask: z.ZodObject<{
|
|
506
561
|
id: z.ZodString;
|
|
507
562
|
title: z.ZodString;
|
|
@@ -528,6 +583,7 @@ export declare const BoardDocument: z.ZodObject<{
|
|
|
528
583
|
id: z.ZodString;
|
|
529
584
|
label: z.ZodString;
|
|
530
585
|
order: z.ZodNumber;
|
|
586
|
+
terminal: z.ZodBoolean;
|
|
531
587
|
}, z.core.$strict>>;
|
|
532
588
|
tasks: z.ZodArray<z.ZodObject<{
|
|
533
589
|
id: z.ZodString;
|
|
@@ -581,6 +637,7 @@ export declare const NodeBoard: z.ZodObject<{
|
|
|
581
637
|
id: z.ZodString;
|
|
582
638
|
label: z.ZodString;
|
|
583
639
|
order: z.ZodNumber;
|
|
640
|
+
terminal: z.ZodBoolean;
|
|
584
641
|
}, z.core.$strict>>;
|
|
585
642
|
tasks: z.ZodArray<z.ZodObject<{
|
|
586
643
|
id: z.ZodString;
|
|
@@ -609,6 +666,7 @@ export type NodeBoard = z.infer<typeof NodeBoard>;
|
|
|
609
666
|
export declare const BoardStatusInput: z.ZodObject<{
|
|
610
667
|
id: z.ZodString;
|
|
611
668
|
label: z.ZodString;
|
|
669
|
+
terminal: z.ZodOptional<z.ZodBoolean>;
|
|
612
670
|
}, z.core.$strict>;
|
|
613
671
|
export type BoardStatusInput = z.infer<typeof BoardStatusInput>;
|
|
614
672
|
export declare const ConfigureBoardInput: z.ZodObject<{
|
|
@@ -616,6 +674,7 @@ export declare const ConfigureBoardInput: z.ZodObject<{
|
|
|
616
674
|
statuses: z.ZodArray<z.ZodObject<{
|
|
617
675
|
id: z.ZodString;
|
|
618
676
|
label: z.ZodString;
|
|
677
|
+
terminal: z.ZodOptional<z.ZodBoolean>;
|
|
619
678
|
}, z.core.$strict>>;
|
|
620
679
|
idempotencyKey: z.ZodString;
|
|
621
680
|
}, z.core.$strict>;
|
|
@@ -695,6 +754,24 @@ export declare const DeleteBoardTaskInput: z.ZodObject<{
|
|
|
695
754
|
idempotencyKey: z.ZodString;
|
|
696
755
|
}, z.core.$strict>;
|
|
697
756
|
export type DeleteBoardTaskInput = z.infer<typeof DeleteBoardTaskInput>;
|
|
757
|
+
/**
|
|
758
|
+
* The way out of a board whose `tasks[]` names one id twice (anchrd/intel#341).
|
|
759
|
+
*
|
|
760
|
+
* ⚠️ It names no task, and that is not an oversight. Such a board holds a pair the caller cannot
|
|
761
|
+
* tell apart — every other operation here addresses a task BY id (#285), so the one thing nobody
|
|
762
|
+
* can say is "the second of the two". The board is what is named, and the server does the one thing
|
|
763
|
+
* that removes the ambiguity: the first entry under an id keeps it, every later one gets a freshly
|
|
764
|
+
* minted id and keeps everything else it carries.
|
|
765
|
+
*
|
|
766
|
+
* ⚠️ It exists because the pair is deliberately NOT folded away on read (`upgradeStoredBoard`,
|
|
767
|
+
* `repeatedBoardId`): two tasks under one id are two whole tasks, and a fold on the read is written
|
|
768
|
+
* back by the next save of any kind. So somebody has to ask for the repair, and this is the asking.
|
|
769
|
+
*/
|
|
770
|
+
export declare const RepairBoardTaskIdsInput: z.ZodObject<{
|
|
771
|
+
nodeId: z.ZodString;
|
|
772
|
+
idempotencyKey: z.ZodString;
|
|
773
|
+
}, z.core.$strict>;
|
|
774
|
+
export type RepairBoardTaskIdsInput = z.infer<typeof RepairBoardTaskIdsInput>;
|
|
698
775
|
export declare const BoardTaskResult: z.ZodObject<{
|
|
699
776
|
node: z.ZodObject<{
|
|
700
777
|
id: z.ZodString;
|
|
@@ -829,9 +906,82 @@ export declare const ConfigureBoardResult: z.ZodObject<{
|
|
|
829
906
|
id: z.ZodString;
|
|
830
907
|
label: z.ZodString;
|
|
831
908
|
order: z.ZodNumber;
|
|
909
|
+
terminal: z.ZodBoolean;
|
|
832
910
|
}, z.core.$strict>>;
|
|
833
911
|
}, z.core.$strict>;
|
|
834
912
|
export type ConfigureBoardResult = z.infer<typeof ConfigureBoardResult>;
|
|
913
|
+
/**
|
|
914
|
+
* What the repair did, task by task (anchrd/intel#341).
|
|
915
|
+
*
|
|
916
|
+
* ⚠️ `previousId` is the id the entry shared, and it still names a task on this board — the FIRST
|
|
917
|
+
* entry under it, the one that kept it. That is what makes the repair readable: nothing that
|
|
918
|
+
* pointed at that id moved, so a caller can see which of the two the board's `parentId` and
|
|
919
|
+
* `dependsOn` edges have been meaning all along, and move them with `board_task_move` and
|
|
920
|
+
* `board_task_update` if they meant the other one.
|
|
921
|
+
*
|
|
922
|
+
* ⚠️ At least one entry, because a board with nothing to repair is refused rather than answered
|
|
923
|
+
* with an empty list and a new version that changed nothing.
|
|
924
|
+
*/
|
|
925
|
+
export declare const RepairBoardTaskIdsResult: z.ZodObject<{
|
|
926
|
+
node: z.ZodObject<{
|
|
927
|
+
id: z.ZodString;
|
|
928
|
+
parentId: z.ZodNullable<z.ZodString>;
|
|
929
|
+
kind: z.ZodEnum<{
|
|
930
|
+
folder: "folder";
|
|
931
|
+
document: "document";
|
|
932
|
+
attachment: "attachment";
|
|
933
|
+
table: "table";
|
|
934
|
+
agent: "agent";
|
|
935
|
+
board: "board";
|
|
936
|
+
}>;
|
|
937
|
+
title: z.ZodString;
|
|
938
|
+
description: z.ZodNullable<z.ZodString>;
|
|
939
|
+
ownerId: z.ZodString;
|
|
940
|
+
currentVersionId: z.ZodNullable<z.ZodString>;
|
|
941
|
+
createdAt: z.ZodISODateTime;
|
|
942
|
+
updatedAt: z.ZodISODateTime;
|
|
943
|
+
archivedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
944
|
+
}, z.core.$strict>;
|
|
945
|
+
version: z.ZodObject<{
|
|
946
|
+
id: z.ZodString;
|
|
947
|
+
nodeId: z.ZodString;
|
|
948
|
+
sequence: z.ZodNumber;
|
|
949
|
+
contentKey: z.ZodString;
|
|
950
|
+
mediaType: z.ZodString;
|
|
951
|
+
contentHash: z.ZodString;
|
|
952
|
+
size: z.ZodNumber;
|
|
953
|
+
segment: z.ZodNullable<z.ZodEnum<{
|
|
954
|
+
append: "append";
|
|
955
|
+
snapshot: "snapshot";
|
|
956
|
+
}>>;
|
|
957
|
+
createdBy: z.ZodString;
|
|
958
|
+
createdAt: z.ZodISODateTime;
|
|
959
|
+
}, z.core.$strict>;
|
|
960
|
+
renumbered: z.ZodArray<z.ZodObject<{
|
|
961
|
+
previousId: z.ZodString;
|
|
962
|
+
task: z.ZodObject<{
|
|
963
|
+
id: z.ZodString;
|
|
964
|
+
title: z.ZodString;
|
|
965
|
+
status: z.ZodString;
|
|
966
|
+
assignee: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
967
|
+
type: z.ZodLiteral<"user">;
|
|
968
|
+
id: z.ZodString;
|
|
969
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
970
|
+
type: z.ZodLiteral<"agent">;
|
|
971
|
+
nodeId: z.ZodString;
|
|
972
|
+
}, z.core.$strict>], "type">>;
|
|
973
|
+
labels: z.ZodArray<z.ZodString>;
|
|
974
|
+
startDate: z.ZodNullable<z.ZodISODate>;
|
|
975
|
+
dueDate: z.ZodNullable<z.ZodISODate>;
|
|
976
|
+
parentId: z.ZodNullable<z.ZodString>;
|
|
977
|
+
dependsOn: z.ZodArray<z.ZodString>;
|
|
978
|
+
order: z.ZodString;
|
|
979
|
+
description: z.ZodString;
|
|
980
|
+
references: z.ZodArray<z.ZodString>;
|
|
981
|
+
}, z.core.$strict>;
|
|
982
|
+
}, z.core.$strict>>;
|
|
983
|
+
}, z.core.$strict>;
|
|
984
|
+
export type RepairBoardTaskIdsResult = z.infer<typeof RepairBoardTaskIdsResult>;
|
|
835
985
|
export declare const AgentMediaType = "application/vnd.anchrd.agent+json";
|
|
836
986
|
export declare const AgentReferenceRole: z.ZodEnum<{
|
|
837
987
|
"system-message": "system-message";
|
|
@@ -1761,6 +1911,7 @@ export declare const ToolCatalog: z.ZodObject<{
|
|
|
1761
1911
|
}, z.core.$strict>;
|
|
1762
1912
|
fingerprint: z.ZodString;
|
|
1763
1913
|
}, z.core.$strict>>;
|
|
1914
|
+
reached: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1764
1915
|
}, z.core.$strict>;
|
|
1765
1916
|
export type ToolCatalog = z.infer<typeof ToolCatalog>;
|
|
1766
1917
|
/**
|
|
@@ -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).
|
|
@@ -318,8 +336,28 @@ export const BoardAssignee = z.discriminatedUnion("type", [
|
|
|
318
336
|
* mints a key between theirs, so moving one task writes one task and renumbers nothing — the whole
|
|
319
337
|
* reason a board is not addressed by position the way a table's rows are (`TableRowPosition`).
|
|
320
338
|
* A hand-written key could collide, and two tasks sharing a key have no defined order at all.
|
|
339
|
+
*
|
|
340
|
+
* ⚠️ This CHARACTER CLASS is the truth about a stored key, not `fractional-indexing`, and the two
|
|
341
|
+
* are deliberately not the same set (anchrd/intel#359). A board that arrived through a bundle may
|
|
342
|
+
* spell `"0"`, `"a00"`, `"zzz"` or `"A"` — all of them legal here, none of them readable by that
|
|
343
|
+
* library. Narrowing the regex to what it reads would be a rule on the STORED document, so a board
|
|
344
|
+
* carrying one would stop parsing in all four places a board body is read (the board read, the
|
|
345
|
+
* indexer, the link reader, the bundle import) — unreadable, unsearchable and unmovable at once,
|
|
346
|
+
* over a value its owner never wrote. That is the trap #311, #318 and #321 each walked into from a
|
|
347
|
+
* different side.
|
|
348
|
+
*
|
|
349
|
+
* ⚠️ What every consumer may therefore rely on is exactly what stands here: keys are non-empty
|
|
350
|
+
* strings over `[0-9A-Za-z]` and are ORDERED BY STRING COMPARISON. Nothing may assume more — and
|
|
351
|
+
* `packages/api`'s `orderBetween` is the one place that asks `fractional-indexing` for a key and
|
|
352
|
+
* carries on without it when it refuses a bound.
|
|
321
353
|
*/
|
|
322
|
-
|
|
354
|
+
// ⚠️ Named because a consumer has to be able to stay inside it. The repair of a repeated task id
|
|
355
|
+
// (anchrd/intel#341) mints a key beside an existing one, and a key one character too long would be
|
|
356
|
+
// written and then refused by the very next read — the whole board lost over a repair.
|
|
357
|
+
export const MaxBoardTaskOrderLength = 64;
|
|
358
|
+
export const BoardTaskOrder = z
|
|
359
|
+
.string()
|
|
360
|
+
.regex(new RegExp(`^[0-9A-Za-z]{1,${MaxBoardTaskOrderLength}}$`));
|
|
323
361
|
export const BoardTaskId = IntelId;
|
|
324
362
|
export const BoardTaskLabel = z.string().trim().min(1).max(60);
|
|
325
363
|
// A day, not an instant. A task is due on a date; giving it a time zone would make the same task
|
|
@@ -328,27 +366,76 @@ export const BoardTaskDate = z.iso.date();
|
|
|
328
366
|
// Markdown, and capped: a task's description is a card, and what needs more than this is a document
|
|
329
367
|
// the task can point at through `references`.
|
|
330
368
|
export const BoardTaskDescription = z.string().max(20_000);
|
|
369
|
+
/**
|
|
370
|
+
* The tasks one task waits for (#285), each of them at most once (anchrd/intel#318).
|
|
371
|
+
*
|
|
372
|
+
* ⚠️ Board-internal only, enforced on the write path: a dependency on a task in another board would
|
|
373
|
+
* hang this node on a file that can change without anyone here noticing. Across boards the link is
|
|
374
|
+
* `references`, which points at the board NODE and lands in the link graph.
|
|
375
|
+
*
|
|
376
|
+
* ⚠️ Refused rather than folded together, the same shape as the status ids in `ConfigureBoardInput`.
|
|
377
|
+
* A repeat carries no information — but that is a fact about the value, not about the answer: a
|
|
378
|
+
* caller handed back a shorter list than it sent is told nothing, and composes the same one again.
|
|
379
|
+
* The refusal names the mistake once, and the caller is holding the list it has to fix. (The one
|
|
380
|
+
* place a repeat is folded instead is `upgradeStoredBoard`, where there is no caller to tell.)
|
|
381
|
+
*
|
|
382
|
+
* ⚠️ It sits on the STORED task as well as on the two inputs, so a consumer may rely on it rather
|
|
383
|
+
* than defend against it — `createBoardGraph` mints one edge key per pair, and a second one threw
|
|
384
|
+
* the whole graph view off the screen for everybody looking at that board.
|
|
385
|
+
*/
|
|
386
|
+
export const BoardTaskDependsOn = z
|
|
387
|
+
.array(BoardTaskId)
|
|
388
|
+
.max(64)
|
|
389
|
+
.refine((ids) => new Set(ids).size === ids.length, {
|
|
390
|
+
error: "A task can be named only once in dependsOn",
|
|
391
|
+
});
|
|
392
|
+
/**
|
|
393
|
+
* A task's labels, each of them at most once (anchrd/intel#318).
|
|
394
|
+
*
|
|
395
|
+
* ⚠️ The same rule the detail panel has always applied to what a person types — it refuses to add a
|
|
396
|
+
* label the task already carries — stated where every surface meets it, because the MCP write path
|
|
397
|
+
* did not. A repeated label draws the same chip twice on the card, with two remove buttons of which
|
|
398
|
+
* either takes both away, and weights that word higher in the search text (`indexing.ts`).
|
|
399
|
+
*/
|
|
400
|
+
export const BoardTaskLabels = z
|
|
401
|
+
.array(BoardTaskLabel)
|
|
402
|
+
.max(32)
|
|
403
|
+
.refine((labels) => new Set(labels).size === labels.length, {
|
|
404
|
+
error: "A label can be named only once",
|
|
405
|
+
});
|
|
406
|
+
/**
|
|
407
|
+
* The Intel nodes a task points at, each of them at most once (anchrd/intel#318).
|
|
408
|
+
*
|
|
409
|
+
* They land in the link graph as `text` links, the same way a document's inline links do, so what a
|
|
410
|
+
* board points at is visible from the other side too.
|
|
411
|
+
*
|
|
412
|
+
* ⚠️ Distinct for the same reason as `labels`: the picker in the detail panel already refuses one
|
|
413
|
+
* the task holds, and the link graph counts a repeat once anyway (`ON CONFLICT DO NOTHING`), so a
|
|
414
|
+
* duplicate is a second row in the panel and nothing else — which is exactly the kind of value that
|
|
415
|
+
* has no reading and should not be storable.
|
|
416
|
+
*/
|
|
417
|
+
export const BoardTaskReferences = z
|
|
418
|
+
.array(IntelId)
|
|
419
|
+
.max(64)
|
|
420
|
+
.refine((ids) => new Set(ids).size === ids.length, {
|
|
421
|
+
error: "A node can be referenced only once",
|
|
422
|
+
});
|
|
331
423
|
export const BoardTask = z.strictObject({
|
|
332
424
|
id: BoardTaskId,
|
|
333
425
|
title: z.string().trim().min(1).max(240),
|
|
334
426
|
status: BoardStatusId,
|
|
335
427
|
assignee: BoardAssignee.nullable(),
|
|
336
|
-
labels:
|
|
428
|
+
labels: BoardTaskLabels,
|
|
337
429
|
startDate: BoardTaskDate.nullable(),
|
|
338
430
|
dueDate: BoardTaskDate.nullable(),
|
|
339
431
|
// ⚠️ The whole hierarchy in one field, deliberately: epic, task and subtask are a DEPTH and not a
|
|
340
432
|
// type (#285). A `kind` beside it would allow a subtask under nothing and an epic under an epic,
|
|
341
433
|
// and every surface would then need its own opinion about which combinations mean anything.
|
|
342
434
|
parentId: BoardTaskId.nullable(),
|
|
343
|
-
|
|
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),
|
|
435
|
+
dependsOn: BoardTaskDependsOn,
|
|
347
436
|
order: BoardTaskOrder,
|
|
348
437
|
description: BoardTaskDescription,
|
|
349
|
-
|
|
350
|
-
// do, so what a board points at is visible from the other side too.
|
|
351
|
-
references: z.array(IntelId).max(64),
|
|
438
|
+
references: BoardTaskReferences,
|
|
352
439
|
});
|
|
353
440
|
// The whole board, as it is stored and as it is read. There is no second representation to keep in
|
|
354
441
|
// step with it — this document is the file.
|
|
@@ -370,6 +457,16 @@ export const NodeBoard = z.strictObject({
|
|
|
370
457
|
export const BoardStatusInput = z.strictObject({
|
|
371
458
|
id: BoardStatusId,
|
|
372
459
|
label: z.string().trim().min(1).max(60),
|
|
460
|
+
/**
|
|
461
|
+
* Whether this column means finished (anchrd/intel#311).
|
|
462
|
+
*
|
|
463
|
+
* ⚠️ Optional, and the absence is not the same as `false`. A caller who says nothing gets the
|
|
464
|
+
* server's answer — `false` for an ordinary column, `true` for the shelf, which cannot be
|
|
465
|
+
* anything else. Making it a required boolean would force every caller that only wanted to rename
|
|
466
|
+
* a column to restate the whole board's notion of done, and getting one entry wrong there is a
|
|
467
|
+
* silent change to what counts as blocked.
|
|
468
|
+
*/
|
|
469
|
+
terminal: z.boolean().optional(),
|
|
373
470
|
});
|
|
374
471
|
// The status list, written whole and in the order it should be drawn — never a patch. Adding,
|
|
375
472
|
// renaming and reordering are all this one call, and `archived` has to be in what it is given.
|
|
@@ -384,7 +481,12 @@ export const ConfigureBoardInput = z.strictObject({
|
|
|
384
481
|
})
|
|
385
482
|
.refine((statuses) => statuses.some((status) => status.id === ArchivedBoardStatusId), {
|
|
386
483
|
error: `The "${ArchivedBoardStatusId}" status cannot be removed`,
|
|
387
|
-
})
|
|
484
|
+
})
|
|
485
|
+
// ⚠️ Refused rather than corrected, the same way removing the shelf is refused. A task swept
|
|
486
|
+
// onto `archived` is finished with, and a board that could declare the shelf non-terminal would
|
|
487
|
+
// hold every archived task open as a blocker forever. Only an EXPLICIT `false` is refused —
|
|
488
|
+
// saying nothing is fine and means the server's `true` (anchrd/intel#311).
|
|
489
|
+
.refine((statuses) => statuses.find((status) => status.id === ArchivedBoardStatusId)?.terminal !== false, { error: `The "${ArchivedBoardStatusId}" status is always terminal` }),
|
|
388
490
|
idempotencyKey: z.string().min(8).max(200),
|
|
389
491
|
});
|
|
390
492
|
/**
|
|
@@ -402,13 +504,13 @@ export const AddBoardTaskInput = z.strictObject({
|
|
|
402
504
|
// the shelf.
|
|
403
505
|
status: BoardStatusId.optional(),
|
|
404
506
|
assignee: BoardAssignee.nullable().default(null),
|
|
405
|
-
labels:
|
|
507
|
+
labels: BoardTaskLabels.default([]),
|
|
406
508
|
startDate: BoardTaskDate.nullable().default(null),
|
|
407
509
|
dueDate: BoardTaskDate.nullable().default(null),
|
|
408
510
|
parentId: BoardTaskId.nullable().default(null),
|
|
409
|
-
dependsOn:
|
|
511
|
+
dependsOn: BoardTaskDependsOn.default([]),
|
|
410
512
|
description: BoardTaskDescription.default(""),
|
|
411
|
-
references:
|
|
513
|
+
references: BoardTaskReferences.default([]),
|
|
412
514
|
// Where among its neighbours it goes. Both absent puts it last in its column.
|
|
413
515
|
afterTaskId: BoardTaskId.nullable().default(null),
|
|
414
516
|
beforeTaskId: BoardTaskId.nullable().default(null),
|
|
@@ -428,12 +530,12 @@ export const UpdateBoardTaskInput = z
|
|
|
428
530
|
taskId: BoardTaskId,
|
|
429
531
|
title: z.string().trim().min(1).max(240).optional(),
|
|
430
532
|
assignee: BoardAssignee.nullable().optional(),
|
|
431
|
-
labels:
|
|
533
|
+
labels: BoardTaskLabels.optional(),
|
|
432
534
|
startDate: BoardTaskDate.nullable().optional(),
|
|
433
535
|
dueDate: BoardTaskDate.nullable().optional(),
|
|
434
|
-
dependsOn:
|
|
536
|
+
dependsOn: BoardTaskDependsOn.optional(),
|
|
435
537
|
description: BoardTaskDescription.optional(),
|
|
436
|
-
references:
|
|
538
|
+
references: BoardTaskReferences.optional(),
|
|
437
539
|
idempotencyKey: z.string().min(8).max(200),
|
|
438
540
|
})
|
|
439
541
|
.refine((input) => input.title !== undefined ||
|
|
@@ -467,6 +569,23 @@ export const DeleteBoardTaskInput = z.strictObject({
|
|
|
467
569
|
taskId: BoardTaskId,
|
|
468
570
|
idempotencyKey: z.string().min(8).max(200),
|
|
469
571
|
});
|
|
572
|
+
/**
|
|
573
|
+
* The way out of a board whose `tasks[]` names one id twice (anchrd/intel#341).
|
|
574
|
+
*
|
|
575
|
+
* ⚠️ It names no task, and that is not an oversight. Such a board holds a pair the caller cannot
|
|
576
|
+
* tell apart — every other operation here addresses a task BY id (#285), so the one thing nobody
|
|
577
|
+
* can say is "the second of the two". The board is what is named, and the server does the one thing
|
|
578
|
+
* that removes the ambiguity: the first entry under an id keeps it, every later one gets a freshly
|
|
579
|
+
* minted id and keeps everything else it carries.
|
|
580
|
+
*
|
|
581
|
+
* ⚠️ It exists because the pair is deliberately NOT folded away on read (`upgradeStoredBoard`,
|
|
582
|
+
* `repeatedBoardId`): two tasks under one id are two whole tasks, and a fold on the read is written
|
|
583
|
+
* back by the next save of any kind. So somebody has to ask for the repair, and this is the asking.
|
|
584
|
+
*/
|
|
585
|
+
export const RepairBoardTaskIdsInput = z.strictObject({
|
|
586
|
+
nodeId: IntelId,
|
|
587
|
+
idempotencyKey: z.string().min(8).max(200),
|
|
588
|
+
});
|
|
470
589
|
// The one task that was written, not the whole board: a board can hold thousands of tasks, and
|
|
471
590
|
// answering a one-card edit with all of them would make every write pay for the read.
|
|
472
591
|
export const BoardTaskResult = z.strictObject({
|
|
@@ -486,6 +605,26 @@ export const ConfigureBoardResult = z.strictObject({
|
|
|
486
605
|
version: NodeVersion,
|
|
487
606
|
statuses: z.array(BoardStatus),
|
|
488
607
|
});
|
|
608
|
+
/**
|
|
609
|
+
* What the repair did, task by task (anchrd/intel#341).
|
|
610
|
+
*
|
|
611
|
+
* ⚠️ `previousId` is the id the entry shared, and it still names a task on this board — the FIRST
|
|
612
|
+
* entry under it, the one that kept it. That is what makes the repair readable: nothing that
|
|
613
|
+
* pointed at that id moved, so a caller can see which of the two the board's `parentId` and
|
|
614
|
+
* `dependsOn` edges have been meaning all along, and move them with `board_task_move` and
|
|
615
|
+
* `board_task_update` if they meant the other one.
|
|
616
|
+
*
|
|
617
|
+
* ⚠️ At least one entry, because a board with nothing to repair is refused rather than answered
|
|
618
|
+
* with an empty list and a new version that changed nothing.
|
|
619
|
+
*/
|
|
620
|
+
export const RepairBoardTaskIdsResult = z.strictObject({
|
|
621
|
+
node: Node,
|
|
622
|
+
version: NodeVersion,
|
|
623
|
+
renumbered: z
|
|
624
|
+
.array(z.strictObject({ previousId: BoardTaskId, task: BoardTask }))
|
|
625
|
+
.min(1)
|
|
626
|
+
.max(5_000),
|
|
627
|
+
});
|
|
489
628
|
// ── The agent definition (#139, ADR-0005 §4) ─────────────────────────────────────────────────────
|
|
490
629
|
//
|
|
491
630
|
// An agent's body is a definition, stored as an immutable version in R2 exactly like a document's.
|
|
@@ -1030,6 +1169,24 @@ export const ToolCapability = z.strictObject({
|
|
|
1030
1169
|
export const ToolCatalog = z.strictObject({
|
|
1031
1170
|
portalConnected: z.boolean(),
|
|
1032
1171
|
items: z.array(ToolCapability),
|
|
1172
|
+
/**
|
|
1173
|
+
* Which delegated servers actually contributed a tool to this catalog (#289).
|
|
1174
|
+
*
|
|
1175
|
+
* ⚠️ Present only where the attribution was actually made — a delegated caller whose catalog was
|
|
1176
|
+
* read. It is absent for an ordinary user, and absent as well when the answer comes from one of
|
|
1177
|
+
* the short paths that never reach the portal (nothing delegated, no portal sign-in, connection
|
|
1178
|
+
* dropped). Absent therefore means "not stated", never "nothing arrived"; `[]` is the second one.
|
|
1179
|
+
*
|
|
1180
|
+
* That it is missing rather than empty on those paths is deliberate rather than half-finished:
|
|
1181
|
+
* the attribution already happens for a delegation — `capabilities` has to make it to cut the
|
|
1182
|
+
* list — so naming it costs nothing there, while computing the same thing for an ordinary user
|
|
1183
|
+
* would mean a second portal request per call, for a question their screen does not ask.
|
|
1184
|
+
*
|
|
1185
|
+
* ⚠️ It is the answer to "what arrived", never to "what was granted". A server missing here has
|
|
1186
|
+
* been switched off, revoked, or is failing right now; the delegation in the definition is
|
|
1187
|
+
* unchanged. Reading it the other way round would turn an outage into a permission change.
|
|
1188
|
+
*/
|
|
1189
|
+
reached: z.array(ToolServerHandle).optional(),
|
|
1033
1190
|
});
|
|
1034
1191
|
/**
|
|
1035
1192
|
* One MCP server the asking user reaches right now, as the portal itself names it (D30).
|