@cat-factory/orchestration 0.239.0 → 0.241.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/container/dependencies.d.ts +24 -1
- package/dist/container/dependencies.d.ts.map +1 -1
- package/dist/container/module-shapes.d.ts +8 -0
- package/dist/container/module-shapes.d.ts.map +1 -1
- package/dist/container/modules.d.ts.map +1 -1
- package/dist/container/modules.js +9 -2
- package/dist/container/modules.js.map +1 -1
- package/dist/container/runtime.d.ts +1 -0
- package/dist/container/runtime.d.ts.map +1 -1
- package/dist/container/runtime.js +7 -1
- package/dist/container/runtime.js.map +1 -1
- package/dist/container.d.ts +9 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +2 -1
- package/dist/container.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/artifacts/artifactRetention.d.ts +11 -4
- package/dist/modules/artifacts/artifactRetention.d.ts.map +1 -1
- package/dist/modules/artifacts/artifactRetention.js +11 -4
- package/dist/modules/artifacts/artifactRetention.js.map +1 -1
- package/dist/modules/execution/AgentDispatchController.d.ts.map +1 -1
- package/dist/modules/execution/AgentDispatchController.js +11 -17
- package/dist/modules/execution/AgentDispatchController.js.map +1 -1
- package/dist/modules/execution/CompanionController.d.ts.map +1 -1
- package/dist/modules/execution/CompanionController.js +5 -22
- package/dist/modules/execution/CompanionController.js.map +1 -1
- package/dist/modules/execution/DeployerStepController.d.ts.map +1 -1
- package/dist/modules/execution/DeployerStepController.js +2 -4
- package/dist/modules/execution/DeployerStepController.js.map +1 -1
- package/dist/modules/execution/ExecutionService.d.ts.map +1 -1
- package/dist/modules/execution/ExecutionService.js +2 -1
- package/dist/modules/execution/ExecutionService.js.map +1 -1
- package/dist/modules/execution/FollowUpGateController.js +3 -3
- package/dist/modules/execution/FollowUpGateController.js.map +1 -1
- package/dist/modules/execution/ForkDecisionController.d.ts.map +1 -1
- package/dist/modules/execution/ForkDecisionController.js +1 -2
- package/dist/modules/execution/ForkDecisionController.js.map +1 -1
- package/dist/modules/execution/GateHelperDispatcher.d.ts.map +1 -1
- package/dist/modules/execution/GateHelperDispatcher.js +1 -2
- package/dist/modules/execution/GateHelperDispatcher.js.map +1 -1
- package/dist/modules/execution/GateStepController.d.ts.map +1 -1
- package/dist/modules/execution/GateStepController.js +1 -2
- package/dist/modules/execution/GateStepController.js.map +1 -1
- package/dist/modules/execution/HumanTestController.d.ts.map +1 -1
- package/dist/modules/execution/HumanTestController.js +7 -32
- package/dist/modules/execution/HumanTestController.js.map +1 -1
- package/dist/modules/execution/InterviewGateController.d.ts.map +1 -1
- package/dist/modules/execution/InterviewGateController.js +2 -21
- package/dist/modules/execution/InterviewGateController.js.map +1 -1
- package/dist/modules/execution/JudgeStepController.d.ts.map +1 -1
- package/dist/modules/execution/JudgeStepController.js +1 -2
- package/dist/modules/execution/JudgeStepController.js.map +1 -1
- package/dist/modules/execution/OneShotStepController.d.ts.map +1 -1
- package/dist/modules/execution/OneShotStepController.js +1 -2
- package/dist/modules/execution/OneShotStepController.js.map +1 -1
- package/dist/modules/execution/PollRunningController.d.ts.map +1 -1
- package/dist/modules/execution/PollRunningController.js +2 -4
- package/dist/modules/execution/PollRunningController.js.map +1 -1
- package/dist/modules/execution/RalphController.d.ts.map +1 -1
- package/dist/modules/execution/RalphController.js +5 -9
- package/dist/modules/execution/RalphController.js.map +1 -1
- package/dist/modules/execution/ReviewGateController.d.ts.map +1 -1
- package/dist/modules/execution/ReviewGateController.js +2 -20
- package/dist/modules/execution/ReviewGateController.js.map +1 -1
- package/dist/modules/execution/RunDispatcher.d.ts.map +1 -1
- package/dist/modules/execution/RunDispatcher.js +11 -54
- package/dist/modules/execution/RunDispatcher.js.map +1 -1
- package/dist/modules/execution/RunStateMachine.d.ts +49 -0
- package/dist/modules/execution/RunStateMachine.d.ts.map +1 -1
- package/dist/modules/execution/RunStateMachine.js +84 -0
- package/dist/modules/execution/RunStateMachine.js.map +1 -1
- package/dist/modules/execution/TesterController.d.ts.map +1 -1
- package/dist/modules/execution/TesterController.js +9 -21
- package/dist/modules/execution/TesterController.js.map +1 -1
- package/dist/modules/execution/VisualConfirmationController.d.ts +20 -4
- package/dist/modules/execution/VisualConfirmationController.d.ts.map +1 -1
- package/dist/modules/execution/VisualConfirmationController.js +83 -45
- package/dist/modules/execution/VisualConfirmationController.js.map +1 -1
- package/dist/modules/execution/gate-window-controllers.d.ts +2 -0
- package/dist/modules/execution/gate-window-controllers.d.ts.map +1 -1
- package/dist/modules/execution/gate-window-controllers.js +2 -1
- package/dist/modules/execution/gate-window-controllers.js.map +1 -1
- package/dist/modules/execution/step-fold.logic.d.ts +24 -0
- package/dist/modules/execution/step-fold.logic.d.ts.map +1 -1
- package/dist/modules/execution/step-fold.logic.js +29 -0
- package/dist/modules/execution/step-fold.logic.js.map +1 -1
- package/dist/modules/execution/stepPreamble.d.ts.map +1 -1
- package/dist/modules/execution/stepPreamble.js +2 -4
- package/dist/modules/execution/stepPreamble.js.map +1 -1
- package/dist/modules/execution/visual-confirm-design-references.d.ts +85 -0
- package/dist/modules/execution/visual-confirm-design-references.d.ts.map +1 -0
- package/dist/modules/execution/visual-confirm-design-references.js +229 -0
- package/dist/modules/execution/visual-confirm-design-references.js.map +1 -0
- package/dist/modules/notifications/NotificationService.d.ts.map +1 -1
- package/dist/modules/notifications/NotificationService.js +15 -9
- package/dist/modules/notifications/NotificationService.js.map +1 -1
- package/dist/modules/notifications/NotificationSettingsService.d.ts +28 -0
- package/dist/modules/notifications/NotificationSettingsService.d.ts.map +1 -0
- package/dist/modules/notifications/NotificationSettingsService.js +50 -0
- package/dist/modules/notifications/NotificationSettingsService.js.map +1 -0
- package/package.json +11 -11
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { BinaryArtifactRecord, BinaryArtifactStore, DocumentRecord, DocumentRepository } from '@cat-factory/kernel';
|
|
2
|
+
import type { DocumentRenderStatus, VisualConfirmDesignGapReason, VisualConfirmDesignReferences } from '@cat-factory/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* How many design-rendered views one gallery may carry, across every design a task links.
|
|
5
|
+
*
|
|
6
|
+
* A per-document render cap already bounds each design (Figma rasterises the first frames of a
|
|
7
|
+
* file, not all of them), and this bounds their SUM: a task linking four designs would otherwise
|
|
8
|
+
* push a couple of captured screenshots off the bottom of a page of mocks nobody asked to review
|
|
9
|
+
* here. Whatever it excludes is COUNTED and stated, never quietly trimmed.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_DESIGN_REFERENCE_VIEWS = 12;
|
|
12
|
+
/** One design-rendered frame, keyed by the view name it pairs on. */
|
|
13
|
+
export interface DesignReference {
|
|
14
|
+
view: string;
|
|
15
|
+
artifactId: string;
|
|
16
|
+
}
|
|
17
|
+
/** The fold's product: what to show, and what to say about what is missing. */
|
|
18
|
+
export interface DesignReferenceFold {
|
|
19
|
+
references: DesignReference[];
|
|
20
|
+
summary: VisualConfirmDesignReferences;
|
|
21
|
+
}
|
|
22
|
+
/** The document fields the fold reads; a `DocumentRecord` structurally satisfies it. */
|
|
23
|
+
export interface DesignDocumentSummary {
|
|
24
|
+
source: DocumentRecord['source'];
|
|
25
|
+
externalId: string;
|
|
26
|
+
title: string;
|
|
27
|
+
renderStatus: DocumentRenderStatus | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Why a linked design contributed less than its whole set of frames, or null when it contributed
|
|
31
|
+
* everything it has.
|
|
32
|
+
*
|
|
33
|
+
* Reads the stored `renderStatus` AND what the artifact store actually holds, because the two can
|
|
34
|
+
* disagree in the one direction that matters: a row CLAIMING retention over an empty shelf (an
|
|
35
|
+
* account that re-pointed its blob backend, a reclaim that outran its re-import) would otherwise
|
|
36
|
+
* describe a gallery that is not there. `partial` is such a claim just as `stored` is, and left to
|
|
37
|
+
* speak for itself it tells the reviewer "only part of its frames were retained" above nothing at
|
|
38
|
+
* all, which reads as a design that is merely short rather than one that is absent, and sends them
|
|
39
|
+
* looking for the frames that did survive.
|
|
40
|
+
*
|
|
41
|
+
* So the two claiming statuses are checked against what is held, and the three that already say
|
|
42
|
+
* nothing was kept (`failed` / `none` / `storage_unavailable`) stand as they are: each names a
|
|
43
|
+
* DIFFERENT cause of the same empty shelf, and collapsing them would throw the cause away.
|
|
44
|
+
*/
|
|
45
|
+
export declare function designGapReason(renderStatus: DocumentRenderStatus | null, heldImages: number): VisualConfirmDesignGapReason | null;
|
|
46
|
+
/**
|
|
47
|
+
* Fold a task's design documents and their retained renders into the gate's reference set.
|
|
48
|
+
*
|
|
49
|
+
* Four rules, each of which the obvious shape gets wrong:
|
|
50
|
+
*
|
|
51
|
+
* A view name claimed by TWO designs is qualified with its design's label on BOTH sides, never
|
|
52
|
+
* just the second. This is the same rule slice 1 applied to a frame name repeated across pages
|
|
53
|
+
* within one file: leaving the first occurrence bare hands the plain name to whichever document
|
|
54
|
+
* happened to be read first, so re-ordering the links would silently re-point a reviewed view at
|
|
55
|
+
* a different screen. The cost is that neither qualified name matches a screenshot captured as
|
|
56
|
+
* plain "Checkout", which is correct: nothing knows which of two designs that capture is of.
|
|
57
|
+
*
|
|
58
|
+
* Within ONE design, the NEWEST render for a view wins. The artifact reads are ordered oldest
|
|
59
|
+
* first, so a later assignment overriding an earlier one is the rule, not an accident.
|
|
60
|
+
*
|
|
61
|
+
* The cap counts VIEWS, not artifacts, and is applied after the per-view reduction: capping the
|
|
62
|
+
* raw rows first would spend the budget on frames a later revision had already replaced. It is
|
|
63
|
+
* SHARED across the linked designs rather than consumed in read order (see {@link allocate}), and
|
|
64
|
+
* what it cuts is named per design, not just totalled.
|
|
65
|
+
*
|
|
66
|
+
* Emission stays in document order with each design's own frames contiguous: the allocation
|
|
67
|
+
* decides how many slots each design gets, never how the gallery reads.
|
|
68
|
+
*/
|
|
69
|
+
export declare function foldDesignReferences(documents: readonly DesignDocumentSummary[], artifacts: readonly BinaryArtifactRecord[], cap?: number): DesignReferenceFold;
|
|
70
|
+
/**
|
|
71
|
+
* Read the task's linked DESIGN documents and their retained renders, then fold them.
|
|
72
|
+
*
|
|
73
|
+
* Returns null when the task links no design at all, which is a different fact from a design that
|
|
74
|
+
* gave nothing: the first has nothing to state and the second is exactly what the summary exists
|
|
75
|
+
* to say. Two reads regardless of how many designs are attached (the batched
|
|
76
|
+
* {@link BinaryArtifactStore.listByDocuments}), because this runs on the driver path of every run
|
|
77
|
+
* whose pipeline carries the gate.
|
|
78
|
+
*
|
|
79
|
+
* The documents are read LIVE at gather time rather than off the run's recorded context, for the
|
|
80
|
+
* same reason the hand-uploaded references are: `recapture` exists so a person can attach a
|
|
81
|
+
* missing reference mid-review and see it, and a design linked while the gate is parked is that
|
|
82
|
+
* same act.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveDesignReferences(documentRepository: DocumentRepository | undefined, store: BinaryArtifactStore | null, workspaceId: string, blockId: string): Promise<DesignReferenceFold | null>;
|
|
85
|
+
//# sourceMappingURL=visual-confirm-design-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-confirm-design-references.d.ts","sourceRoot":"","sources":["../../../src/modules/execution/visual-confirm-design-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EAEnB,cAAc,EACd,kBAAkB,EACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EACV,oBAAoB,EAEpB,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,wBAAwB,CAAA;AAc/B;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAA;AAE5C,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,OAAO,EAAE,6BAA6B,CAAA;CACvC;AAED,wFAAwF;AACxF,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAA;CAC1C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,oBAAoB,GAAG,IAAI,EACzC,UAAU,EAAE,MAAM,GACjB,4BAA4B,GAAG,IAAI,CAoBrC;AAiED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,qBAAqB,EAAE,EAC3C,SAAS,EAAE,SAAS,oBAAoB,EAAE,EAC1C,GAAG,SAA6B,GAC/B,mBAAmB,CAmErB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAC3C,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,EAClD,KAAK,EAAE,mBAAmB,GAAG,IAAI,EACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAiBrC"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { isDesignSource } from '@cat-factory/contracts';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// The visual-confirmation gate's DESIGN-ORIGIN reference half: the rendered frames an import
|
|
4
|
+
// retained for the designs a task links, folded into the gate's gallery beside the images a
|
|
5
|
+
// person uploaded by hand.
|
|
6
|
+
//
|
|
7
|
+
// Its own module rather than more methods on `VisualConfirmationController` because the fold is
|
|
8
|
+
// pure and the interesting part: which frame wins a view, what a name collision across two
|
|
9
|
+
// designs means, and which of five ways a design can contribute nothing the reviewer is looking
|
|
10
|
+
// at. The controller keeps the one impure step (two reads) and hands the rest here.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* How many design-rendered views one gallery may carry, across every design a task links.
|
|
14
|
+
*
|
|
15
|
+
* A per-document render cap already bounds each design (Figma rasterises the first frames of a
|
|
16
|
+
* file, not all of them), and this bounds their SUM: a task linking four designs would otherwise
|
|
17
|
+
* push a couple of captured screenshots off the bottom of a page of mocks nobody asked to review
|
|
18
|
+
* here. Whatever it excludes is COUNTED and stated, never quietly trimmed.
|
|
19
|
+
*/
|
|
20
|
+
export const MAX_DESIGN_REFERENCE_VIEWS = 12;
|
|
21
|
+
/**
|
|
22
|
+
* Why a linked design contributed less than its whole set of frames, or null when it contributed
|
|
23
|
+
* everything it has.
|
|
24
|
+
*
|
|
25
|
+
* Reads the stored `renderStatus` AND what the artifact store actually holds, because the two can
|
|
26
|
+
* disagree in the one direction that matters: a row CLAIMING retention over an empty shelf (an
|
|
27
|
+
* account that re-pointed its blob backend, a reclaim that outran its re-import) would otherwise
|
|
28
|
+
* describe a gallery that is not there. `partial` is such a claim just as `stored` is, and left to
|
|
29
|
+
* speak for itself it tells the reviewer "only part of its frames were retained" above nothing at
|
|
30
|
+
* all, which reads as a design that is merely short rather than one that is absent, and sends them
|
|
31
|
+
* looking for the frames that did survive.
|
|
32
|
+
*
|
|
33
|
+
* So the two claiming statuses are checked against what is held, and the three that already say
|
|
34
|
+
* nothing was kept (`failed` / `none` / `storage_unavailable`) stand as they are: each names a
|
|
35
|
+
* DIFFERENT cause of the same empty shelf, and collapsing them would throw the cause away.
|
|
36
|
+
*/
|
|
37
|
+
export function designGapReason(renderStatus, heldImages) {
|
|
38
|
+
switch (renderStatus) {
|
|
39
|
+
case 'stored':
|
|
40
|
+
return heldImages > 0 ? null : 'not_retained';
|
|
41
|
+
case 'partial':
|
|
42
|
+
return heldImages > 0 ? 'partial' : 'not_retained';
|
|
43
|
+
case 'failed':
|
|
44
|
+
return 'failed';
|
|
45
|
+
case 'none':
|
|
46
|
+
return 'none';
|
|
47
|
+
case 'storage_unavailable':
|
|
48
|
+
return 'storage_unavailable';
|
|
49
|
+
case null:
|
|
50
|
+
// No render outcome was ever recorded for this document. Two causes the gate cannot tell
|
|
51
|
+
// apart (a source that rasterises nothing, a document imported before renders were kept),
|
|
52
|
+
// and one answer covers both honestly; a re-import is what distinguishes them.
|
|
53
|
+
return heldImages > 0 ? null : 'not_retained';
|
|
54
|
+
default:
|
|
55
|
+
return exhaustiveRenderStatus(renderStatus);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The compile-time totality guard: a new {@link DocumentRenderStatus} member fails the build here
|
|
60
|
+
* rather than silently becoming "this design is complete".
|
|
61
|
+
*/
|
|
62
|
+
function exhaustiveRenderStatus(status) {
|
|
63
|
+
void status;
|
|
64
|
+
return 'not_retained';
|
|
65
|
+
}
|
|
66
|
+
/** The stable key both sides of the join use: a document's own source identity. */
|
|
67
|
+
function documentKey(ref) {
|
|
68
|
+
return `${ref.source}::${ref.externalId}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A per-document display label that is UNIQUE within this fold, used to qualify a view name two
|
|
72
|
+
* designs both claim. Titles alone are not unique (two boards can import files named "Checkout
|
|
73
|
+
* flow"), so a shared title falls back to carrying the source id.
|
|
74
|
+
*/
|
|
75
|
+
function documentLabels(documents) {
|
|
76
|
+
const byTitle = new Map();
|
|
77
|
+
for (const document of documents) {
|
|
78
|
+
byTitle.set(document.title, (byTitle.get(document.title) ?? 0) + 1);
|
|
79
|
+
}
|
|
80
|
+
const labels = new Map();
|
|
81
|
+
for (const document of documents) {
|
|
82
|
+
const shared = (byTitle.get(document.title) ?? 0) > 1;
|
|
83
|
+
labels.set(documentKey(document), shared ? `${document.title} ${document.externalId}` : document.title);
|
|
84
|
+
}
|
|
85
|
+
return labels;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Share a bounded budget of gallery slots across the designs competing for it, one slot at a time
|
|
89
|
+
* in turn, and answer how many each may take.
|
|
90
|
+
*
|
|
91
|
+
* The budget is shared, so how it is SPLIT is a decision, and taking the first `cap` views in read
|
|
92
|
+
* order is the one shape that answers it worst: reads come back oldest-first, so the design linked
|
|
93
|
+
* longest ago fills the gallery and a design linked this morning contributes nothing, its absence
|
|
94
|
+
* indistinguishable to the reviewer from a design with no frames. Round-robin instead: every
|
|
95
|
+
* design is represented before any design gets a second slot, a design with fewer frames than its
|
|
96
|
+
* share leaves the rest to the others, and the split does not move when the links are re-ordered.
|
|
97
|
+
*/
|
|
98
|
+
function allocate(counts, cap) {
|
|
99
|
+
const taken = counts.map(() => 0);
|
|
100
|
+
let remaining = Math.max(0, cap);
|
|
101
|
+
for (let round = 0; remaining > 0; round += 1) {
|
|
102
|
+
let servedThisRound = false;
|
|
103
|
+
for (let i = 0; i < counts.length && remaining > 0; i += 1) {
|
|
104
|
+
if (round >= counts[i])
|
|
105
|
+
continue;
|
|
106
|
+
taken[i] += 1;
|
|
107
|
+
remaining -= 1;
|
|
108
|
+
servedThisRound = true;
|
|
109
|
+
}
|
|
110
|
+
// Nobody had a frame left at this depth, so nobody will at any greater one.
|
|
111
|
+
if (!servedThisRound)
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
return taken;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Fold a task's design documents and their retained renders into the gate's reference set.
|
|
118
|
+
*
|
|
119
|
+
* Four rules, each of which the obvious shape gets wrong:
|
|
120
|
+
*
|
|
121
|
+
* A view name claimed by TWO designs is qualified with its design's label on BOTH sides, never
|
|
122
|
+
* just the second. This is the same rule slice 1 applied to a frame name repeated across pages
|
|
123
|
+
* within one file: leaving the first occurrence bare hands the plain name to whichever document
|
|
124
|
+
* happened to be read first, so re-ordering the links would silently re-point a reviewed view at
|
|
125
|
+
* a different screen. The cost is that neither qualified name matches a screenshot captured as
|
|
126
|
+
* plain "Checkout", which is correct: nothing knows which of two designs that capture is of.
|
|
127
|
+
*
|
|
128
|
+
* Within ONE design, the NEWEST render for a view wins. The artifact reads are ordered oldest
|
|
129
|
+
* first, so a later assignment overriding an earlier one is the rule, not an accident.
|
|
130
|
+
*
|
|
131
|
+
* The cap counts VIEWS, not artifacts, and is applied after the per-view reduction: capping the
|
|
132
|
+
* raw rows first would spend the budget on frames a later revision had already replaced. It is
|
|
133
|
+
* SHARED across the linked designs rather than consumed in read order (see {@link allocate}), and
|
|
134
|
+
* what it cuts is named per design, not just totalled.
|
|
135
|
+
*
|
|
136
|
+
* Emission stays in document order with each design's own frames contiguous: the allocation
|
|
137
|
+
* decides how many slots each design gets, never how the gallery reads.
|
|
138
|
+
*/
|
|
139
|
+
export function foldDesignReferences(documents, artifacts, cap = MAX_DESIGN_REFERENCE_VIEWS) {
|
|
140
|
+
// One entry per document IDENTITY, in first-linked order. A repeated link is one design: counted
|
|
141
|
+
// twice it would take two shares of the budget, state its gap twice, and make its own title look
|
|
142
|
+
// shared to the qualifier below, which would then qualify every one of its views against itself.
|
|
143
|
+
const known = new Map(documents.map((document) => [documentKey(document), document]));
|
|
144
|
+
const unique = [...known.values()];
|
|
145
|
+
const renders = artifacts.filter((record) => record.kind === 'reference' && record.document && known.has(documentKey(record.document)));
|
|
146
|
+
// Which documents claim each raw view name: the collision test the qualification below runs on.
|
|
147
|
+
const claimants = new Map();
|
|
148
|
+
for (const record of renders) {
|
|
149
|
+
const view = record.view;
|
|
150
|
+
if (!view)
|
|
151
|
+
continue;
|
|
152
|
+
const key = documentKey(record.document);
|
|
153
|
+
const held = claimants.get(view);
|
|
154
|
+
if (held)
|
|
155
|
+
held.add(key);
|
|
156
|
+
else
|
|
157
|
+
claimants.set(view, new Set([key]));
|
|
158
|
+
}
|
|
159
|
+
const labels = documentLabels(unique);
|
|
160
|
+
const byDocument = new Map(unique.map((document) => [documentKey(document), new Map()]));
|
|
161
|
+
const heldPerDocument = new Map();
|
|
162
|
+
for (const record of renders) {
|
|
163
|
+
// A render with no view has no pairing key, so it can only ever be shown against nothing.
|
|
164
|
+
// Counted for its document (it IS retained) but not rendered as a pair.
|
|
165
|
+
const key = documentKey(record.document);
|
|
166
|
+
heldPerDocument.set(key, (heldPerDocument.get(key) ?? 0) + 1);
|
|
167
|
+
if (!record.view)
|
|
168
|
+
continue;
|
|
169
|
+
const contested = (claimants.get(record.view)?.size ?? 0) > 1;
|
|
170
|
+
const view = contested ? `${record.view} (${labels.get(key) ?? key})` : record.view;
|
|
171
|
+
byDocument.get(key).set(view, record.id);
|
|
172
|
+
}
|
|
173
|
+
const offered = unique.map((document) => [...byDocument.get(documentKey(document)).entries()]);
|
|
174
|
+
const taken = allocate(offered.map((views) => views.length), cap);
|
|
175
|
+
const references = offered.flatMap((views, index) => views.slice(0, taken[index]).map(([view, artifactId]) => ({ view, artifactId })));
|
|
176
|
+
const gaps = [];
|
|
177
|
+
let dropped = 0;
|
|
178
|
+
unique.forEach((document, index) => {
|
|
179
|
+
const cut = offered[index].length - taken[index];
|
|
180
|
+
dropped += cut;
|
|
181
|
+
const reason = designGapReason(document.renderStatus, heldPerDocument.get(documentKey(document)) ?? 0);
|
|
182
|
+
// The two ways a design falls short are independent, so one entry carries both: a design can
|
|
183
|
+
// be short at its source, short at the gallery ceiling, or both at once.
|
|
184
|
+
if (reason || cut > 0) {
|
|
185
|
+
gaps.push({ title: document.title, reason, ...(cut > 0 ? { dropped: cut } : {}) });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
references,
|
|
190
|
+
summary: {
|
|
191
|
+
documents: unique.length,
|
|
192
|
+
images: references.length,
|
|
193
|
+
...(dropped > 0 ? { dropped } : {}),
|
|
194
|
+
...(gaps.length ? { gaps } : {}),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Read the task's linked DESIGN documents and their retained renders, then fold them.
|
|
200
|
+
*
|
|
201
|
+
* Returns null when the task links no design at all, which is a different fact from a design that
|
|
202
|
+
* gave nothing: the first has nothing to state and the second is exactly what the summary exists
|
|
203
|
+
* to say. Two reads regardless of how many designs are attached (the batched
|
|
204
|
+
* {@link BinaryArtifactStore.listByDocuments}), because this runs on the driver path of every run
|
|
205
|
+
* whose pipeline carries the gate.
|
|
206
|
+
*
|
|
207
|
+
* The documents are read LIVE at gather time rather than off the run's recorded context, for the
|
|
208
|
+
* same reason the hand-uploaded references are: `recapture` exists so a person can attach a
|
|
209
|
+
* missing reference mid-review and see it, and a design linked while the gate is parked is that
|
|
210
|
+
* same act.
|
|
211
|
+
*/
|
|
212
|
+
export async function resolveDesignReferences(documentRepository, store, workspaceId, blockId) {
|
|
213
|
+
if (!documentRepository || !store)
|
|
214
|
+
return null;
|
|
215
|
+
const linked = await documentRepository.listByBlock(workspaceId, blockId);
|
|
216
|
+
const designs = linked
|
|
217
|
+
.filter((document) => isDesignSource(document.source))
|
|
218
|
+
.map((document) => ({
|
|
219
|
+
source: document.source,
|
|
220
|
+
externalId: document.externalId,
|
|
221
|
+
title: document.title,
|
|
222
|
+
renderStatus: document.renderStatus,
|
|
223
|
+
}));
|
|
224
|
+
if (!designs.length)
|
|
225
|
+
return null;
|
|
226
|
+
const artifacts = await store.listByDocuments(workspaceId, designs.map((document) => ({ source: document.source, externalId: document.externalId })));
|
|
227
|
+
return foldDesignReferences(designs, artifacts);
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=visual-confirm-design-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-confirm-design-references.js","sourceRoot":"","sources":["../../../src/modules/execution/visual-confirm-design-references.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEvD,8EAA8E;AAC9E,6FAA6F;AAC7F,4FAA4F;AAC5F,2BAA2B;AAC3B,EAAE;AACF,gGAAgG;AAChG,2FAA2F;AAC3F,gGAAgG;AAChG,oFAAoF;AACpF,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAA;AAsB5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAC7B,YAAyC,EACzC,UAAkB;IAElB,QAAQ,YAAY,EAAE,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAA;QAC/C,KAAK,SAAS;YACZ,OAAO,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAA;QACpD,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,MAAM;YACT,OAAO,MAAM,CAAA;QACf,KAAK,qBAAqB;YACxB,OAAO,qBAAqB,CAAA;QAC9B,KAAK,IAAI;YACP,yFAAyF;YACzF,0FAA0F;YAC1F,+EAA+E;YAC/E,OAAO,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAA;QAC/C;YACE,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAA;IAC/C,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,sBAAsB,CAAC,MAAa;IAC3C,KAAK,MAAM,CAAA;IACX,OAAO,cAAc,CAAA;AACvB,CAAC;AAED,mFAAmF;AACnF,SAAS,WAAW,CAAC,GAAwB;IAC3C,OAAO,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,UAAU,EAAE,CAAA;AAC3C,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,SAA2C;IACjE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;IACzC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACrE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACrD,MAAM,CAAC,GAAG,CACR,WAAW,CAAC,QAAQ,CAAC,EACrB,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CACrE,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,QAAQ,CAAC,MAAyB,EAAE,GAAW;IACtD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAA;IACjC,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAChC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC9C,IAAI,eAAe,GAAG,KAAK,CAAA;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3D,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC,CAAE;gBAAE,SAAQ;YACjC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,CAAA;YACd,SAAS,IAAI,CAAC,CAAA;YACd,eAAe,GAAG,IAAI,CAAA;QACxB,CAAC;QACD,4EAA4E;QAC5E,IAAI,CAAC,eAAe;YAAE,MAAK;IAC7B,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAA2C,EAC3C,SAA0C,EAC1C,GAAG,GAAG,0BAA0B;IAEhC,iGAAiG;IACjG,iGAAiG;IACjG,iGAAiG;IACjG,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAU,CAAC,CAAC,CAAA;IAC9F,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;IAClC,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAC9B,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAC5F,CAAA;IACD,gGAAgG;IAChG,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAA;IAChD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;QACxB,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,QAAS,CAAC,CAAA;QACzC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,IAAI;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;;YAClB,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC1C,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;IACrC,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,IAAI,GAAG,EAAkB,CAAC,CAAC,CAC7E,CAAA;IACD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAA;IACjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,0FAA0F;QAC1F,wEAAwE;QACxE,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,QAAS,CAAC,CAAA;QACzC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7D,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,SAAQ;QAC1B,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QAC7D,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;QACnF,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;IAC3C,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC/F,MAAM,KAAK,GAAG,QAAQ,CACpB,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EACpC,GAAG,CACJ,CAAA;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAClD,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAClF,CAAA;IACD,MAAM,IAAI,GAA6B,EAAE,CAAA;IACzC,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QACjC,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAE,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAE,CAAA;QAClD,OAAO,IAAI,GAAG,CAAA;QACd,MAAM,MAAM,GAAG,eAAe,CAC5B,QAAQ,CAAC,YAAY,EACrB,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAChD,CAAA;QACD,6FAA6F;QAC7F,yEAAyE;QACzE,IAAI,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QACpF,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO;QACL,UAAU;QACV,OAAO,EAAE;YACP,SAAS,EAAE,MAAM,CAAC,MAAM;YACxB,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjC;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,kBAAkD,EAClD,KAAiC,EACjC,WAAmB,EACnB,OAAe;IAEf,IAAI,CAAC,kBAAkB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAC9C,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IACzE,MAAM,OAAO,GAA4B,MAAM;SAC5C,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;SACrD,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,YAAY,EAAE,QAAQ,CAAC,YAAY;KACpC,CAAC,CAAC,CAAA;IACL,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAChC,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,eAAe,CAC3C,WAAW,EACX,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAC1F,CAAA;IACD,OAAO,oBAAoB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;AACjD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationService.d.ts","sourceRoot":"","sources":["../../../src/modules/notifications/NotificationService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,WAAW,EACX,YAAY,EACZ,mBAAmB,
|
|
1
|
+
{"version":3,"file":"NotificationService.d.ts","sourceRoot":"","sources":["../../../src/modules/notifications/NotificationService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,WAAW,EACX,YAAY,EACZ,mBAAmB,EAEnB,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAE9D,MAAM,WAAW,+BAA+B;IAC9C,sBAAsB,EAAE,sBAAsB,CAAA;IAC9C,mBAAmB,EAAE,mBAAmB,CAAA;IACxC,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ;;;;;OAKG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAC9B;AAED;;;;;;;;GAQG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAqB;IAE9C,YAAY,IAAI,EAAE,+BAA+B,EAMhD;IAED;;;;;;;;;;OAUG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CA0CrF;IAED,yFAAyF;IACzF,OAAO,CAAC,cAAc;IAetB;;;;;;;;;;;OAWG;IACG,GAAG,CACP,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GACxD,OAAO,CAAC,YAAY,CAAC,CA6BvB;IAED,kFAAkF;IAC5E,OAAO,CACX,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,YAAY,CAAC,CAYvB;IAED,2EAA2E;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAE3D;IAED;;;;;OAKG;IACG,cAAc,CAClB,YAAY,EAAE,MAAM,EAAE,EACtB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAEpC;IAED;;;;OAIG;IACG,gBAAgB,CACpB,YAAY,EAAE,MAAM,EAAE,EACtB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAEpC;IAED;;;;;;;OAOG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAW3F;IAED;;;;;;;;;OASG;IACG,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9E;IAED;;;;;;;;OAQG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW1F;IAED,4CAA4C;IACtC,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAEvE;IAED,mFAAmF;YACrE,OAAO;CAYtB"}
|
|
@@ -72,7 +72,7 @@ export class NotificationService {
|
|
|
72
72
|
await this.notifications.upsert(workspaceId, notification);
|
|
73
73
|
}
|
|
74
74
|
if (!existing || this.contentChanged(existing, notification)) {
|
|
75
|
-
await this.deliver(workspaceId, persisted);
|
|
75
|
+
await this.deliver(workspaceId, persisted, 'raised');
|
|
76
76
|
}
|
|
77
77
|
return persisted;
|
|
78
78
|
}
|
|
@@ -122,10 +122,13 @@ export class NotificationService {
|
|
|
122
122
|
// it and this revert can't lose an escalation.
|
|
123
123
|
const reopened = { ...claimed, status: 'open', resolvedAt: null };
|
|
124
124
|
await this.notifications.upsert(workspaceId, reopened);
|
|
125
|
-
|
|
125
|
+
// `refreshed`, not `raised`: the human is at the screen they just acted from, and the card
|
|
126
|
+
// was already alerted on when it was raised. Re-interrupting the whole board because a
|
|
127
|
+
// merge call 500ed states a new decision that nobody has to make.
|
|
128
|
+
await this.deliver(workspaceId, reopened, 'refreshed');
|
|
126
129
|
throw err;
|
|
127
130
|
}
|
|
128
|
-
await this.deliver(workspaceId, claimed);
|
|
131
|
+
await this.deliver(workspaceId, claimed, 'settled');
|
|
129
132
|
return claimed;
|
|
130
133
|
}
|
|
131
134
|
/** Resolve a notification (the human acted on it or dismissed it). Idempotent. */
|
|
@@ -140,7 +143,7 @@ export class NotificationService {
|
|
|
140
143
|
resolvedAt: this.clock.now(),
|
|
141
144
|
};
|
|
142
145
|
await this.notifications.upsert(workspaceId, resolved);
|
|
143
|
-
await this.deliver(workspaceId, resolved);
|
|
146
|
+
await this.deliver(workspaceId, resolved, 'settled');
|
|
144
147
|
return resolved;
|
|
145
148
|
}
|
|
146
149
|
/** All open notifications for the workspace (for the inbox + snapshot). */
|
|
@@ -182,7 +185,7 @@ export class NotificationService {
|
|
|
182
185
|
resolvedAt: this.clock.now(),
|
|
183
186
|
};
|
|
184
187
|
await this.notifications.upsert(workspaceId, resolved);
|
|
185
|
-
await this.deliver(workspaceId, resolved);
|
|
188
|
+
await this.deliver(workspaceId, resolved, 'settled');
|
|
186
189
|
return resolved;
|
|
187
190
|
}
|
|
188
191
|
/**
|
|
@@ -211,7 +214,7 @@ export class NotificationService {
|
|
|
211
214
|
resolvedAt: this.clock.now(),
|
|
212
215
|
};
|
|
213
216
|
await this.notifications.upsert(workspaceId, resolved);
|
|
214
|
-
await this.deliver(workspaceId, resolved);
|
|
217
|
+
await this.deliver(workspaceId, resolved, 'settled');
|
|
215
218
|
}
|
|
216
219
|
}
|
|
217
220
|
/**
|
|
@@ -228,7 +231,10 @@ export class NotificationService {
|
|
|
228
231
|
// escalated row is then re-delivered so the inbox re-renders it red in real time.
|
|
229
232
|
const escalated = await this.notifications.escalateStaleOpen(workspaceId, now - thresholdMs);
|
|
230
233
|
for (const n of escalated) {
|
|
231
|
-
|
|
234
|
+
// `refreshed`: the colour change is for the boards already holding the card, not a second
|
|
235
|
+
// ask. It is also what keeps this loop free of per-card routing and email reads: an alert
|
|
236
|
+
// transport and the routing gate both stand down before touching a store.
|
|
237
|
+
await this.deliver(workspaceId, n, 'refreshed');
|
|
232
238
|
}
|
|
233
239
|
return escalated.length;
|
|
234
240
|
}
|
|
@@ -237,11 +243,11 @@ export class NotificationService {
|
|
|
237
243
|
return this.notifications.get(workspaceId, id);
|
|
238
244
|
}
|
|
239
245
|
/** Best-effort delivery to the channel — a failure must never break the caller. */
|
|
240
|
-
async deliver(workspaceId, notification) {
|
|
246
|
+
async deliver(workspaceId, notification, reason) {
|
|
241
247
|
if (!this.channel)
|
|
242
248
|
return;
|
|
243
249
|
try {
|
|
244
|
-
await this.channel.deliver(workspaceId, notification);
|
|
250
|
+
await this.channel.deliver(workspaceId, notification, reason);
|
|
245
251
|
}
|
|
246
252
|
catch {
|
|
247
253
|
// The row is persisted; delivery is an optimisation. Swallow channel errors.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationService.js","sourceRoot":"","sources":["../../../src/modules/notifications/NotificationService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NotificationService.js","sourceRoot":"","sources":["../../../src/modules/notifications/NotificationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type NotificationSettings } from '@cat-factory/contracts';
|
|
2
|
+
import type { Clock, NotificationDeliveryChannel, NotificationRouter, NotificationRoutingMatrix, NotificationSettingsRepository, NotificationType } from '@cat-factory/kernel';
|
|
3
|
+
export interface NotificationSettingsServiceDependencies {
|
|
4
|
+
notificationSettingsRepository: NotificationSettingsRepository;
|
|
5
|
+
clock: Clock;
|
|
6
|
+
}
|
|
7
|
+
export declare class NotificationSettingsService implements NotificationRouter {
|
|
8
|
+
private readonly deps;
|
|
9
|
+
constructor(deps: NotificationSettingsServiceDependencies);
|
|
10
|
+
/** A workspace's settings; the empty matrix (everything on its default) when unconfigured. */
|
|
11
|
+
get(workspaceId: string): Promise<NotificationSettings>;
|
|
12
|
+
/** Replace a workspace's routing overrides (a full replace, like the Slack routing write). */
|
|
13
|
+
update(workspaceId: string, matrix: NotificationRoutingMatrix): Promise<NotificationSettings>;
|
|
14
|
+
/**
|
|
15
|
+
* The routing decision, for one delivery. One point read per notification per routed
|
|
16
|
+
* channel: notifications are raised at human scale (a run parks, a gate gives up), so
|
|
17
|
+
* this is not on any hot path, and a cached copy would answer with a mute the operator
|
|
18
|
+
* had already lifted.
|
|
19
|
+
*/
|
|
20
|
+
isRouted(workspaceId: string, type: NotificationType, channel: NotificationDeliveryChannel): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Decode a stored matrix. The cell-by-cell tolerance (a retired type costs its own override
|
|
23
|
+
* and nothing else) lives in `@cat-factory/contracts` beside the resolution it feeds, so the
|
|
24
|
+
* settings UI decoding the same row reaches the same answer.
|
|
25
|
+
*/
|
|
26
|
+
private parseMatrix;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=NotificationSettingsService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationSettingsService.d.ts","sourceRoot":"","sources":["../../../src/modules/notifications/NotificationSettingsService.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EACV,KAAK,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,yBAAyB,EACzB,8BAA8B,EAC9B,gBAAgB,EACjB,MAAM,qBAAqB,CAAA;AAW5B,MAAM,WAAW,uCAAuC;IACtD,8BAA8B,EAAE,8BAA8B,CAAA;IAC9D,KAAK,EAAE,KAAK,CAAA;CACb;AAED,qBAAa,2BAA4B,YAAW,kBAAkB;IACxD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,uCAAuC,EAAI;IAE9E,8FAA8F;IACxF,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAI5D;IAED,8FAA8F;IACxF,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,oBAAoB,CAAC,CAQ/B;IAED;;;;;OAKG;IACG,QAAQ,CACZ,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,OAAO,CAAC,CAGlB;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAW;CASpB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { decodeNotificationRoutingMatrix, isNotificationRouted, } from '@cat-factory/contracts';
|
|
2
|
+
export class NotificationSettingsService {
|
|
3
|
+
deps;
|
|
4
|
+
constructor(deps) {
|
|
5
|
+
this.deps = deps;
|
|
6
|
+
}
|
|
7
|
+
/** A workspace's settings; the empty matrix (everything on its default) when unconfigured. */
|
|
8
|
+
async get(workspaceId) {
|
|
9
|
+
const record = await this.deps.notificationSettingsRepository.getByWorkspace(workspaceId);
|
|
10
|
+
if (!record)
|
|
11
|
+
return { matrix: {}, updatedAt: 0 };
|
|
12
|
+
return { matrix: this.parseMatrix(record.matrixJson), updatedAt: record.updatedAt };
|
|
13
|
+
}
|
|
14
|
+
/** Replace a workspace's routing overrides (a full replace, like the Slack routing write). */
|
|
15
|
+
async update(workspaceId, matrix) {
|
|
16
|
+
const updatedAt = this.deps.clock.now();
|
|
17
|
+
await this.deps.notificationSettingsRepository.upsert({
|
|
18
|
+
workspaceId,
|
|
19
|
+
matrixJson: JSON.stringify(matrix),
|
|
20
|
+
updatedAt,
|
|
21
|
+
});
|
|
22
|
+
return { matrix, updatedAt };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The routing decision, for one delivery. One point read per notification per routed
|
|
26
|
+
* channel: notifications are raised at human scale (a run parks, a gate gives up), so
|
|
27
|
+
* this is not on any hot path, and a cached copy would answer with a mute the operator
|
|
28
|
+
* had already lifted.
|
|
29
|
+
*/
|
|
30
|
+
async isRouted(workspaceId, type, channel) {
|
|
31
|
+
const { matrix } = await this.get(workspaceId);
|
|
32
|
+
return isNotificationRouted(matrix, type, channel);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Decode a stored matrix. The cell-by-cell tolerance (a retired type costs its own override
|
|
36
|
+
* and nothing else) lives in `@cat-factory/contracts` beside the resolution it feeds, so the
|
|
37
|
+
* settings UI decoding the same row reaches the same answer.
|
|
38
|
+
*/
|
|
39
|
+
parseMatrix(matrixJson) {
|
|
40
|
+
try {
|
|
41
|
+
return decodeNotificationRoutingMatrix(JSON.parse(matrixJson));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// A corrupt row is, for routing, the same state as an unconfigured one: every cell falls
|
|
45
|
+
// back to its shipped default. Throwing here would run on the delivery path.
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=NotificationSettingsService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationSettingsService.js","sourceRoot":"","sources":["../../../src/modules/notifications/NotificationSettingsService.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,+BAA+B,EAC/B,oBAAoB,GAErB,MAAM,wBAAwB,CAAA;AAwB/B,MAAM,OAAO,2BAA2B;IACT,IAAI;IAAjC,YAA6B,IAA6C;oBAA7C,IAAI;IAA4C,CAAC;IAE9E,8FAA8F;IAC9F,KAAK,CAAC,GAAG,CAAC,WAAmB;QAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,8BAA8B,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;QACzF,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAA;QAChD,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAA;IACrF,CAAC;IAED,8FAA8F;IAC9F,KAAK,CAAC,MAAM,CACV,WAAmB,EACnB,MAAiC;QAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QACvC,MAAM,IAAI,CAAC,IAAI,CAAC,8BAA8B,CAAC,MAAM,CAAC;YACpD,WAAW;YACX,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;YAClC,SAAS;SACV,CAAC,CAAA;QACF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IAC9B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CACZ,WAAmB,EACnB,IAAsB,EACtB,OAAoC;QAEpC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAC9C,OAAO,oBAAoB,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACpD,CAAC;IAED;;;;OAIG;IACK,WAAW,CAAC,UAAkB;QACpC,IAAI,CAAC;YACH,OAAO,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAA;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,yFAAyF;YACzF,6EAA6E;YAC7E,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/orchestration",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.241.0",
|
|
4
4
|
"description": "Delivery-workflow engine for the Agent Architecture Board (execution, bootstrap, pipelines, board, boardScan, requirements, and composition root).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,20 +25,20 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"ai": "^7.0.51",
|
|
28
|
-
"@cat-factory/agents": "0.
|
|
29
|
-
"@cat-factory/caching": "0.18.
|
|
30
|
-
"@cat-factory/contracts": "0.
|
|
31
|
-
"@cat-factory/integrations": "0.
|
|
32
|
-
"@cat-factory/kernel": "0.
|
|
33
|
-
"@cat-factory/prompt-fragments": "1.0.
|
|
34
|
-
"@cat-factory/sandbox": "0.11.
|
|
35
|
-
"@cat-factory/spend": "0.15.
|
|
36
|
-
"@cat-factory/workspaces": "0.26.
|
|
28
|
+
"@cat-factory/agents": "0.118.0",
|
|
29
|
+
"@cat-factory/caching": "0.18.14",
|
|
30
|
+
"@cat-factory/contracts": "0.275.0",
|
|
31
|
+
"@cat-factory/integrations": "0.151.0",
|
|
32
|
+
"@cat-factory/kernel": "0.273.0",
|
|
33
|
+
"@cat-factory/prompt-fragments": "1.0.23",
|
|
34
|
+
"@cat-factory/sandbox": "0.11.100",
|
|
35
|
+
"@cat-factory/spend": "0.15.41",
|
|
36
|
+
"@cat-factory/workspaces": "0.26.13"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"typescript": "7.0.2",
|
|
40
40
|
"vitest": "^4.1.10",
|
|
41
|
-
"@cat-factory/sandbox-fixtures": "0.7.
|
|
41
|
+
"@cat-factory/sandbox-fixtures": "0.7.310"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc -b tsconfig.build.json",
|