@cat-factory/orchestration 0.221.0 → 0.223.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/modules/board/BoardService.d.ts +24 -31
- package/dist/modules/board/BoardService.d.ts.map +1 -1
- package/dist/modules/board/BoardService.js +107 -89
- package/dist/modules/board/BoardService.js.map +1 -1
- package/dist/modules/board/internalAnchors.d.ts +17 -0
- package/dist/modules/board/internalAnchors.d.ts.map +1 -0
- package/dist/modules/board/internalAnchors.js +55 -0
- package/dist/modules/board/internalAnchors.js.map +1 -0
- package/dist/modules/board/layoutWrites.d.ts +4 -2
- package/dist/modules/board/layoutWrites.d.ts.map +1 -1
- package/dist/modules/board/layoutWrites.js +24 -9
- package/dist/modules/board/layoutWrites.js.map +1 -1
- package/dist/modules/bootstrap/BootstrapService.d.ts +13 -1
- package/dist/modules/bootstrap/BootstrapService.d.ts.map +1 -1
- package/dist/modules/bootstrap/BootstrapService.js +21 -3
- package/dist/modules/bootstrap/BootstrapService.js.map +1 -1
- package/dist/modules/execution/PostMergeBoardController.js +4 -4
- package/dist/modules/execution/PostMergeBoardController.js.map +1 -1
- package/dist/modules/execution/RunAdmission.d.ts.map +1 -1
- package/dist/modules/execution/RunAdmission.js +3 -2
- package/dist/modules/execution/RunAdmission.js.map +1 -1
- package/dist/modules/execution/RunDispatcher.d.ts.map +1 -1
- package/dist/modules/execution/RunDispatcher.js +8 -6
- package/dist/modules/execution/RunDispatcher.js.map +1 -1
- package/dist/modules/execution/RunLifecycleController.d.ts.map +1 -1
- package/dist/modules/execution/RunLifecycleController.js +9 -5
- package/dist/modules/execution/RunLifecycleController.js.map +1 -1
- package/dist/modules/execution/linked-context.d.ts +12 -10
- package/dist/modules/execution/linked-context.d.ts.map +1 -1
- package/dist/modules/execution/linked-context.js +14 -15
- package/dist/modules/execution/linked-context.js.map +1 -1
- package/dist/modules/initiative/InitiativeLoopService.d.ts.map +1 -1
- package/dist/modules/initiative/InitiativeLoopService.js +16 -2
- package/dist/modules/initiative/InitiativeLoopService.js.map +1 -1
- package/dist/modules/initiative/InitiativeService.js +1 -1
- package/dist/modules/initiative/InitiativeService.js.map +1 -1
- package/dist/modules/recurring/RecurringPipelineService.js +1 -1
- package/dist/modules/recurring/RecurringPipelineService.js.map +1 -1
- package/package.json +11 -11
|
@@ -165,6 +165,11 @@ export declare class BoardService {
|
|
|
165
165
|
private readonly reviewFrictionGuard;
|
|
166
166
|
/** The external `/api/v1` board surface (see publicBoardReads.ts). */
|
|
167
167
|
private readonly publicReads;
|
|
168
|
+
/**
|
|
169
|
+
* The headless `internal` anchor blocks a public-API run hangs off (see internalAnchors.ts).
|
|
170
|
+
* Never board state: no event announces one, and every projection filters it out.
|
|
171
|
+
*/
|
|
172
|
+
private readonly internalAnchors;
|
|
168
173
|
/**
|
|
169
174
|
* The board's LAYOUT writes — drag a container to a new spot, drag its border to new bounds
|
|
170
175
|
* (see layoutWrites.ts). Both split their write between a frame's per-board mount override and
|
|
@@ -191,23 +196,31 @@ export declare class BoardService {
|
|
|
191
196
|
private readonly riskPolicySelection;
|
|
192
197
|
constructor({ workspaceRepository, blockRepository, executionRepository, idGenerator, clock, repoProjectionRepository, repoProjectionCache, serviceRepository, workspaceMountRepository, serviceFragmentDefaultsRepository, initiativeRepository, documentRepository, taskRepository, executionEventPublisher, taskTypeRegistry, taskTypeSuppressionRepository, promptFragmentSource, workspaceSettings, reviewFrictionNotifications, resolveRunRepoContext, riskPolicyRepository, logger, }: BoardServiceDependencies);
|
|
193
198
|
/**
|
|
194
|
-
* Push a
|
|
199
|
+
* Push a board-changed signal for a successful mutation. `originWorkspaceId` MUST be
|
|
195
200
|
* the workspace that physically HOMES the affected block (its `homeWorkspaceId`), not
|
|
196
201
|
* necessarily the acting workspace: {@link FanOutEventPublisher} resolves the block's service
|
|
197
|
-
*
|
|
202
|
+
* (and thus every workspace that mounts it) by looking the block up under this origin, so
|
|
198
203
|
* passing a mounter's id for a block homed elsewhere would find nothing and collapse the
|
|
199
|
-
* fan-out to that one board. Naming a block lets the change reach every mount;
|
|
204
|
+
* fan-out to that one board. Naming a block lets the change reach every mount; name none for
|
|
200
205
|
* a signal that should reach the origin workspace only (e.g. a per-workspace frame-layout
|
|
201
206
|
* move). Best-effort: swallow any failure so a missed push never fails the already-persisted
|
|
202
|
-
* mutation
|
|
207
|
+
* mutation, since the client reconciles by re-fetching its snapshot.
|
|
208
|
+
*
|
|
209
|
+
* Pass `block` (rather than only `blockId`) when the change is FULLY described by that one
|
|
210
|
+
* block, so subscribers patch it in place instead of re-reading the whole board. That is the
|
|
211
|
+
* difference between a spawned task costing one small payload and costing a snapshot on every
|
|
212
|
+
* open board. Withhold it for a structural change whose new shape a single block cannot state:
|
|
213
|
+
* a removal or a reparent moves a block BETWEEN parents, and a cascade touches rows the event
|
|
214
|
+
* never names. A frame payload is dropped at the wire by `deliverableBoardBlock`, so naming one
|
|
215
|
+
* here is safe but pointless.
|
|
203
216
|
*
|
|
204
|
-
* When `originConnectionId` is given (a user-driven mutation
|
|
217
|
+
* When `originConnectionId` is given (a user-driven mutation: move / reparent / field edit,
|
|
205
218
|
* carrying the acting tab's connection id), the realtime transport SKIPS delivering this
|
|
206
219
|
* echo back to that connection: its REST response already carried the authoritative result,
|
|
207
220
|
* so refreshing off its own event would only race an in-flight drag (snapping a block back to
|
|
208
221
|
* a stale position) or trigger a redundant board-wide re-hydrate on every inspector edit. Every
|
|
209
|
-
* OTHER subscriber still receives the
|
|
210
|
-
*
|
|
222
|
+
* OTHER subscriber still receives the signal. Engine-driven board changes pass no origin id, so
|
|
223
|
+
* they fan out to everyone as before.
|
|
211
224
|
*/
|
|
212
225
|
private emitBoardChanged;
|
|
213
226
|
/**
|
|
@@ -297,36 +310,16 @@ export declare class BoardService {
|
|
|
297
310
|
* Pass `UNATTRIBUTED_BLOCK_EDITOR` for a caller with no workspace tier (see its doc).
|
|
298
311
|
*/
|
|
299
312
|
addTask(workspaceId: string, containerId: string, input: AddTaskInput, editor: BlockEditActor, createdBy?: string | null): Promise<Block>;
|
|
300
|
-
/**
|
|
301
|
-
* Create a HEADLESS internal task — a top-level, `internal: true` block used purely to anchor
|
|
302
|
-
* a public-API run (an external "initiative breakdown"). It is EXCLUDED from every board
|
|
303
|
-
* projection (see the snapshot/board reads), so it never renders in the UI; deliberately does
|
|
304
|
-
* NOT emit a `block-added` event (nothing should flash onto a live board). Returns the block so
|
|
305
|
-
* the caller can start an execution on it. The engine writes status onto it like any block.
|
|
306
|
-
*/
|
|
313
|
+
/** Public-API: create the anchor block a public-API run hangs off. */
|
|
307
314
|
createInternalTask(workspaceId: string, input: {
|
|
308
315
|
title: string;
|
|
309
316
|
description: string;
|
|
310
317
|
}): Promise<Block>;
|
|
311
|
-
/**
|
|
312
|
-
* Fetch a HEADLESS internal anchor block by id, or null when no block with that id exists in
|
|
313
|
-
* the workspace OR it is not `internal`. The public-API job reads use this to confine an
|
|
314
|
-
* external key to the runs IT created (an `internal` block) — never an arbitrary board
|
|
315
|
-
* execution that merely shares the key's workspace. See PublicApiController.
|
|
316
|
-
*/
|
|
318
|
+
/** Public-API: an anchor by id, or null when the block is absent or not `internal`. */
|
|
317
319
|
getInternalTask(workspaceId: string, blockId: string): Promise<Block | null>;
|
|
318
|
-
/**
|
|
319
|
-
* Delete a HEADLESS internal anchor block. Used by the public API to roll back the anchor when
|
|
320
|
-
* the run it was created for fails to start, so a failed dispatch never leaves an orphan
|
|
321
|
-
* `internal` block behind (it renders nowhere and is invisible to the cap, so it would just
|
|
322
|
-
* accumulate). A headless anchor has no children/service subtree, so a direct delete is enough.
|
|
323
|
-
*/
|
|
320
|
+
/** Public-API: roll an anchor back when the run it was created for fails to start. */
|
|
324
321
|
deleteInternalTask(workspaceId: string, blockId: string): Promise<void>;
|
|
325
|
-
/**
|
|
326
|
-
* How many of the workspace's headless internal "initiative" runs are still in flight — the
|
|
327
|
-
* concurrency backstop the public API checks before starting another, so a single (possibly
|
|
328
|
-
* leaked) key can't spin up unbounded LLM runs. A SQL `COUNT`, not a load-and-count.
|
|
329
|
-
*/
|
|
322
|
+
/** Public-API: how many anchored runs are in flight — the concurrency backstop. */
|
|
330
323
|
countActiveInternalTasks(workspaceId: string): Promise<number>;
|
|
331
324
|
/** Public-API: the workspace's board services (visible service frames). */
|
|
332
325
|
listServices(workspaceId: string): Promise<Block[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BoardService.d.ts","sourceRoot":"","sources":["../../../src/modules/board/BoardService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"BoardService.d.ts","sourceRoot":"","sources":["../../../src/modules/board/BoardService.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import { pruneDanglingEdges, reclaimDoomedEntities } from './removal-cascade.js'
|
|
|
7
7
|
import { canReparent, descendantIds, gridSlot, serviceOf, tasksOf, unfinishedTasksUnder, wouldCreateCycle, } from './board.logic.js';
|
|
8
8
|
import { ReviewFrictionGuard } from './reviewFrictionGuard.js';
|
|
9
9
|
import { resolveNewServiceFrameDefaults } from './newServiceFrameDefaults.js';
|
|
10
|
+
import { createInternalAnchors } from './internalAnchors.js';
|
|
10
11
|
import { PublicBoardReads } from './publicBoardReads.js';
|
|
11
12
|
import { buildReviewDescription, resolveReviewTaskTarget } from './reviewTaskTarget.js';
|
|
12
13
|
import { createBlockPatchNarrowing } from './blockPatchNarrowing.js';
|
|
@@ -40,6 +41,11 @@ export class BoardService {
|
|
|
40
41
|
reviewFrictionGuard;
|
|
41
42
|
/** The external `/api/v1` board surface (see publicBoardReads.ts). */
|
|
42
43
|
publicReads;
|
|
44
|
+
/**
|
|
45
|
+
* The headless `internal` anchor blocks a public-API run hangs off (see internalAnchors.ts).
|
|
46
|
+
* Never board state: no event announces one, and every projection filters it out.
|
|
47
|
+
*/
|
|
48
|
+
internalAnchors;
|
|
43
49
|
/**
|
|
44
50
|
* The board's LAYOUT writes — drag a container to a new spot, drag its border to new bounds
|
|
45
51
|
* (see layoutWrites.ts). Both split their write between a frame's per-board mount override and
|
|
@@ -116,36 +122,49 @@ export class BoardService {
|
|
|
116
122
|
resolveBlock: (workspaceId, id) => this.resolveBlock(workspaceId, id),
|
|
117
123
|
frameMount: (workspaceId, block) => this.frameMount(workspaceId, block),
|
|
118
124
|
projectForWorkspace: (workspaceId, block) => this.projectForWorkspace(workspaceId, block),
|
|
119
|
-
emitBoardChanged: (originWorkspaceId,
|
|
125
|
+
emitBoardChanged: (originWorkspaceId, change) => this.emitBoardChanged(originWorkspaceId, change),
|
|
120
126
|
});
|
|
121
127
|
this.publicReads = new PublicBoardReads({
|
|
122
128
|
blockRepository,
|
|
123
129
|
requireWorkspace: (workspaceId) => this.requireWorkspace(workspaceId),
|
|
124
130
|
addTask: (workspaceId, containerId, input, editor, createdBy) => this.addTask(workspaceId, containerId, input, editor, createdBy),
|
|
125
131
|
});
|
|
132
|
+
this.internalAnchors = createInternalAnchors({
|
|
133
|
+
blockRepository,
|
|
134
|
+
idGenerator,
|
|
135
|
+
requireWorkspace: (workspaceId) => this.requireWorkspace(workspaceId),
|
|
136
|
+
});
|
|
126
137
|
}
|
|
127
138
|
/**
|
|
128
|
-
* Push a
|
|
139
|
+
* Push a board-changed signal for a successful mutation. `originWorkspaceId` MUST be
|
|
129
140
|
* the workspace that physically HOMES the affected block (its `homeWorkspaceId`), not
|
|
130
141
|
* necessarily the acting workspace: {@link FanOutEventPublisher} resolves the block's service
|
|
131
|
-
*
|
|
142
|
+
* (and thus every workspace that mounts it) by looking the block up under this origin, so
|
|
132
143
|
* passing a mounter's id for a block homed elsewhere would find nothing and collapse the
|
|
133
|
-
* fan-out to that one board. Naming a block lets the change reach every mount;
|
|
144
|
+
* fan-out to that one board. Naming a block lets the change reach every mount; name none for
|
|
134
145
|
* a signal that should reach the origin workspace only (e.g. a per-workspace frame-layout
|
|
135
146
|
* move). Best-effort: swallow any failure so a missed push never fails the already-persisted
|
|
136
|
-
* mutation
|
|
147
|
+
* mutation, since the client reconciles by re-fetching its snapshot.
|
|
137
148
|
*
|
|
138
|
-
*
|
|
149
|
+
* Pass `block` (rather than only `blockId`) when the change is FULLY described by that one
|
|
150
|
+
* block, so subscribers patch it in place instead of re-reading the whole board. That is the
|
|
151
|
+
* difference between a spawned task costing one small payload and costing a snapshot on every
|
|
152
|
+
* open board. Withhold it for a structural change whose new shape a single block cannot state:
|
|
153
|
+
* a removal or a reparent moves a block BETWEEN parents, and a cascade touches rows the event
|
|
154
|
+
* never names. A frame payload is dropped at the wire by `deliverableBoardBlock`, so naming one
|
|
155
|
+
* here is safe but pointless.
|
|
156
|
+
*
|
|
157
|
+
* When `originConnectionId` is given (a user-driven mutation: move / reparent / field edit,
|
|
139
158
|
* carrying the acting tab's connection id), the realtime transport SKIPS delivering this
|
|
140
159
|
* echo back to that connection: its REST response already carried the authoritative result,
|
|
141
160
|
* so refreshing off its own event would only race an in-flight drag (snapping a block back to
|
|
142
161
|
* a stale position) or trigger a redundant board-wide re-hydrate on every inspector edit. Every
|
|
143
|
-
* OTHER subscriber still receives the
|
|
144
|
-
*
|
|
162
|
+
* OTHER subscriber still receives the signal. Engine-driven board changes pass no origin id, so
|
|
163
|
+
* they fan out to everyone as before.
|
|
145
164
|
*/
|
|
146
|
-
async emitBoardChanged(originWorkspaceId,
|
|
165
|
+
async emitBoardChanged(originWorkspaceId, change) {
|
|
147
166
|
try {
|
|
148
|
-
await this.events?.boardChanged(originWorkspaceId,
|
|
167
|
+
await this.events?.boardChanged(originWorkspaceId, change);
|
|
149
168
|
}
|
|
150
169
|
catch {
|
|
151
170
|
// best-effort; the REST response already carried the mutation
|
|
@@ -273,7 +292,8 @@ export class BoardService {
|
|
|
273
292
|
};
|
|
274
293
|
const serviceId = await this.registerService(workspaceId, block);
|
|
275
294
|
await this.blockRepository.insert(workspaceId, block, serviceId);
|
|
276
|
-
|
|
295
|
+
// A service FRAME, so no payload: its position is the per-board mount, not this row.
|
|
296
|
+
await this.emitBoardChanged(workspaceId, { reason: 'block-added', blockId: block.id });
|
|
277
297
|
return block;
|
|
278
298
|
}
|
|
279
299
|
/**
|
|
@@ -364,7 +384,8 @@ export class BoardService {
|
|
|
364
384
|
directory: directory ?? null,
|
|
365
385
|
});
|
|
366
386
|
await this.blockRepository.insert(workspaceId, block, serviceId);
|
|
367
|
-
|
|
387
|
+
// A service FRAME, so no payload (see `addBlock`).
|
|
388
|
+
await this.emitBoardChanged(workspaceId, { reason: 'block-added', blockId: block.id });
|
|
368
389
|
return block;
|
|
369
390
|
}
|
|
370
391
|
/**
|
|
@@ -414,8 +435,12 @@ export class BoardService {
|
|
|
414
435
|
createdAt: this.clock.now(),
|
|
415
436
|
};
|
|
416
437
|
await this.workspaceMountRepository.upsert(mount);
|
|
417
|
-
// Fan out from the frame's HOME so every board mounting the shared service refreshes.
|
|
418
|
-
|
|
438
|
+
// Fan out from the frame's HOME so every board mounting the shared service refreshes. A
|
|
439
|
+
// FRAME, and each target board reads its own mount, so there is no payload to carry.
|
|
440
|
+
await this.emitBoardChanged(home.workspaceId, {
|
|
441
|
+
reason: 'block-added',
|
|
442
|
+
blockId: home.block.id,
|
|
443
|
+
});
|
|
419
444
|
}
|
|
420
445
|
// The frame block is the one homed on ANOTHER board, carrying that board's coordinates —
|
|
421
446
|
// return it placed where THIS board just mounted it, or the SPA drops the imported service
|
|
@@ -567,66 +592,30 @@ export class BoardService {
|
|
|
567
592
|
block.technical = input.technical;
|
|
568
593
|
await this.blockRepository.insert(homeWorkspaceId, block, await this.serviceForContainer(blocks, container));
|
|
569
594
|
// Origin = the block's HOME (the mounted service's home when added to a shared board), so
|
|
570
|
-
// the fan-out reaches every workspace mounting the service, not just the acting one.
|
|
571
|
-
|
|
595
|
+
// the fan-out reaches every workspace mounting the service, not just the acting one. A new
|
|
596
|
+
// task is fully described by itself, so it rides along and every board patches it in place
|
|
597
|
+
// rather than re-reading a snapshot: this is the event an initiative loop fires per spawn.
|
|
598
|
+
await this.emitBoardChanged(homeWorkspaceId, { reason: 'block-added', block });
|
|
572
599
|
return block;
|
|
573
600
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
*/
|
|
581
|
-
async createInternalTask(workspaceId, input) {
|
|
582
|
-
await this.requireWorkspace(workspaceId);
|
|
583
|
-
const block = {
|
|
584
|
-
id: this.idGenerator.next('task'),
|
|
585
|
-
title: input.title.trim() || 'Initiative',
|
|
586
|
-
// `type` is the service/repo CLASSIFICATION (frontend/service/library/…), orthogonal to the
|
|
587
|
-
// `level` hierarchy; there is no task-specific BlockType. This anchor is a standalone,
|
|
588
|
-
// never-rendered, repo-less `level:'task'` block, so `type` is irrelevant to it — 'service'
|
|
589
|
-
// is just the neutral default (a normal task inherits its parent service's type instead).
|
|
590
|
-
type: 'service',
|
|
591
|
-
description: input.description ?? '',
|
|
592
|
-
position: { x: 0, y: 0 },
|
|
593
|
-
status: 'planned',
|
|
594
|
-
progress: 0,
|
|
595
|
-
dependsOn: [],
|
|
596
|
-
executionId: null,
|
|
597
|
-
level: 'task',
|
|
598
|
-
parentId: null,
|
|
599
|
-
internal: true,
|
|
600
|
-
};
|
|
601
|
-
await this.blockRepository.insert(workspaceId, block);
|
|
602
|
-
return block;
|
|
601
|
+
// --- Headless internal anchors ---------------------------------------------
|
|
602
|
+
// Delegated to {@link createInternalAnchors}: the top-level `internal: true` blocks that anchor
|
|
603
|
+
// a public-API run and render on no board, ever. See that file for why the four belong together.
|
|
604
|
+
/** Public-API: create the anchor block a public-API run hangs off. */
|
|
605
|
+
createInternalTask(workspaceId, input) {
|
|
606
|
+
return this.internalAnchors.createInternalTask(workspaceId, input);
|
|
603
607
|
}
|
|
604
|
-
/**
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
* external key to the runs IT created (an `internal` block) — never an arbitrary board
|
|
608
|
-
* execution that merely shares the key's workspace. See PublicApiController.
|
|
609
|
-
*/
|
|
610
|
-
async getInternalTask(workspaceId, blockId) {
|
|
611
|
-
const block = await this.blockRepository.get(workspaceId, blockId);
|
|
612
|
-
return block?.internal ? block : null;
|
|
608
|
+
/** Public-API: an anchor by id, or null when the block is absent or not `internal`. */
|
|
609
|
+
getInternalTask(workspaceId, blockId) {
|
|
610
|
+
return this.internalAnchors.getInternalTask(workspaceId, blockId);
|
|
613
611
|
}
|
|
614
|
-
/**
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
* `internal` block behind (it renders nowhere and is invisible to the cap, so it would just
|
|
618
|
-
* accumulate). A headless anchor has no children/service subtree, so a direct delete is enough.
|
|
619
|
-
*/
|
|
620
|
-
async deleteInternalTask(workspaceId, blockId) {
|
|
621
|
-
await this.blockRepository.deleteMany(workspaceId, [blockId]);
|
|
612
|
+
/** Public-API: roll an anchor back when the run it was created for fails to start. */
|
|
613
|
+
deleteInternalTask(workspaceId, blockId) {
|
|
614
|
+
return this.internalAnchors.deleteInternalTask(workspaceId, blockId);
|
|
622
615
|
}
|
|
623
|
-
/**
|
|
624
|
-
* How many of the workspace's headless internal "initiative" runs are still in flight — the
|
|
625
|
-
* concurrency backstop the public API checks before starting another, so a single (possibly
|
|
626
|
-
* leaked) key can't spin up unbounded LLM runs. A SQL `COUNT`, not a load-and-count.
|
|
627
|
-
*/
|
|
616
|
+
/** Public-API: how many anchored runs are in flight — the concurrency backstop. */
|
|
628
617
|
countActiveInternalTasks(workspaceId) {
|
|
629
|
-
return this.
|
|
618
|
+
return this.internalAnchors.countActiveInternalTasks(workspaceId);
|
|
630
619
|
}
|
|
631
620
|
// --- Public-API board reads/writes -----------------------------------------
|
|
632
621
|
// Delegated to {@link PublicBoardReads}: the external `/api/v1` surface is its own cohesive
|
|
@@ -694,7 +683,8 @@ export class BoardService {
|
|
|
694
683
|
};
|
|
695
684
|
await this.blockRepository.insert(homeWorkspaceId, block, containerServiceId);
|
|
696
685
|
// Origin = the block's HOME so a module added to a mounted service fans out to all mounts.
|
|
697
|
-
|
|
686
|
+
// A module is a sub-frame with no mount of its own, so its payload is correct everywhere.
|
|
687
|
+
await this.emitBoardChanged(homeWorkspaceId, { reason: 'block-added', block });
|
|
698
688
|
created.push(block);
|
|
699
689
|
n += 1;
|
|
700
690
|
}
|
|
@@ -732,7 +722,7 @@ export class BoardService {
|
|
|
732
722
|
parentId,
|
|
733
723
|
};
|
|
734
724
|
await this.blockRepository.insert(workspaceId, block);
|
|
735
|
-
await this.emitBoardChanged(workspaceId, 'block-added', block
|
|
725
|
+
await this.emitBoardChanged(workspaceId, { reason: 'block-added', block });
|
|
736
726
|
return block;
|
|
737
727
|
}
|
|
738
728
|
/**
|
|
@@ -754,9 +744,12 @@ export class BoardService {
|
|
|
754
744
|
}
|
|
755
745
|
}
|
|
756
746
|
await this.blockRepository.update(homeWorkspaceId, taskId, { epicId });
|
|
747
|
+
// Re-read BEFORE emitting so the event carries the task it just changed: membership is a
|
|
748
|
+
// field ON the task, so the one block states the whole change and subscribers patch it.
|
|
757
749
|
// Origin = the task's HOME so the fan-out resolves the (possibly mounted) service's boards.
|
|
758
|
-
await this.
|
|
759
|
-
|
|
750
|
+
const updated = assertFound(await this.blockRepository.get(homeWorkspaceId, taskId), 'Block', taskId);
|
|
751
|
+
await this.emitBoardChanged(homeWorkspaceId, { reason: 'epic-assigned', block: updated });
|
|
752
|
+
return updated;
|
|
760
753
|
}
|
|
761
754
|
/** Move a block to a new spot on the board (a frame's position is its per-board override). */
|
|
762
755
|
async moveBlock(workspaceId, id, position, originConnectionId) {
|
|
@@ -805,17 +798,24 @@ export class BoardService {
|
|
|
805
798
|
// `customTaskTypeFields` (the half that may be patched) and the row stores the whole
|
|
806
799
|
// `taskTypeFields`, so this is where the request type becomes the repository's.
|
|
807
800
|
await this.blockRepository.update(homeWorkspaceId, id, narrow.customTaskTypeFields(effective, block));
|
|
801
|
+
const updated = assertFound(await this.blockRepository.get(homeWorkspaceId, id), 'Block', id);
|
|
808
802
|
// Origin = the block's HOME so editing a shared block fans out to every board mounting it.
|
|
809
|
-
// Forward the acting tab's connection id so the realtime transport SKIPS echoing this
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
|
|
815
|
-
//
|
|
803
|
+
// Forward the acting tab's connection id so the realtime transport SKIPS echoing this back to
|
|
804
|
+
// it: the REST response already carried the authoritative block (the SPA upserts it), so a
|
|
805
|
+
// self-echo would only trigger a redundant board-wide re-hydrate, the same "don't refresh off
|
|
806
|
+
// your own mutation" contract move/reparent already follow. Every OTHER subscriber receives
|
|
807
|
+
// the change, carrying the edited block. The UNPROJECTED row is what rides: the projection
|
|
808
|
+
// below is for THIS board, and a fan-out reaches boards with different mounts. That only ever
|
|
809
|
+
// matters for a frame, whose payload `deliverableBoardBlock` drops at the wire anyway.
|
|
810
|
+
await this.emitBoardChanged(homeWorkspaceId, {
|
|
811
|
+
reason: 'block-updated',
|
|
812
|
+
block: updated,
|
|
813
|
+
originConnectionId,
|
|
814
|
+
});
|
|
815
|
+
// A frame's position/size come from THIS board's mount, not the row we just re-read, so a
|
|
816
816
|
// frame edit (rename, threshold, and above all a RESIZE) must not hand the SPA the block's
|
|
817
817
|
// own, never-updated coordinates to upsert.
|
|
818
|
-
return this.projectForWorkspace(workspaceId,
|
|
818
|
+
return this.projectForWorkspace(workspaceId, updated);
|
|
819
819
|
}
|
|
820
820
|
/** Move a block into a new container at a new local position. */
|
|
821
821
|
async reparent(workspaceId, id, input, originConnectionId) {
|
|
@@ -853,8 +853,14 @@ export class BoardService {
|
|
|
853
853
|
const destService = await this.serviceForContainer(destBlocks, parent);
|
|
854
854
|
await this.blockRepository.setService(blockHome, [...descendantIds(destBlocks, id)], destService ?? null);
|
|
855
855
|
}
|
|
856
|
-
// Origin = the block's HOME so the re-stamped subtree fans out to every mounting board.
|
|
857
|
-
|
|
856
|
+
// Origin = the block's HOME so the re-stamped subtree fans out to every mounting board. No
|
|
857
|
+
// payload: a reparent moves the whole SUBTREE between parents, and the moved block alone
|
|
858
|
+
// cannot state what its descendants' service stamps became.
|
|
859
|
+
await this.emitBoardChanged(blockHome, {
|
|
860
|
+
reason: 'block-reparented',
|
|
861
|
+
blockId: id,
|
|
862
|
+
originConnectionId,
|
|
863
|
+
});
|
|
858
864
|
return assertFound(await this.blockRepository.get(blockHome, id), 'Block', id);
|
|
859
865
|
}
|
|
860
866
|
// Cross-home: the block and its new parent belong to two services homed in different
|
|
@@ -895,10 +901,14 @@ export class BoardService {
|
|
|
895
901
|
// service's mounts (and that board). Source side: the block is gone from its old service, so
|
|
896
902
|
// the block→service join can't resolve it anymore — notify the captured source boards
|
|
897
903
|
// directly (origin-only) so they refresh the subtree away.
|
|
898
|
-
await this.emitBoardChanged(parentHome,
|
|
904
|
+
await this.emitBoardChanged(parentHome, {
|
|
905
|
+
reason: 'block-reparented',
|
|
906
|
+
blockId: id,
|
|
907
|
+
originConnectionId,
|
|
908
|
+
});
|
|
899
909
|
for (const ws of sourceFanout) {
|
|
900
910
|
if (ws !== parentHome) {
|
|
901
|
-
await this.emitBoardChanged(ws, 'block-reparented',
|
|
911
|
+
await this.emitBoardChanged(ws, { reason: 'block-reparented', originConnectionId });
|
|
902
912
|
}
|
|
903
913
|
}
|
|
904
914
|
return assertFound(await this.blockRepository.get(parentHome, id), 'Block', id);
|
|
@@ -965,9 +975,10 @@ export class BoardService {
|
|
|
965
975
|
await this.blockRepository.deleteMany(homeWorkspaceId, [...doomed]);
|
|
966
976
|
await pruneDanglingEdges(this.blockRepository, homeWorkspaceId, blocks, doomed);
|
|
967
977
|
// The block + any shared service are now gone, so fan out per captured target (blockId is
|
|
968
|
-
// unresolvable post-delete)
|
|
978
|
+
// unresolvable post-delete): every board that showed the block refreshes it away. There is
|
|
979
|
+
// nothing to carry, and a delete CASCADES, so the refresh is the point.
|
|
969
980
|
for (const ws of fanoutTargets) {
|
|
970
|
-
await this.emitBoardChanged(ws, 'block-removed'
|
|
981
|
+
await this.emitBoardChanged(ws, { reason: 'block-removed' });
|
|
971
982
|
}
|
|
972
983
|
}
|
|
973
984
|
/**
|
|
@@ -992,7 +1003,11 @@ export class BoardService {
|
|
|
992
1003
|
}
|
|
993
1004
|
await this.blockRepository.update(homeWorkspaceId, id, { archived });
|
|
994
1005
|
// Origin = the block's HOME so archiving a shared service fans out to every board mounting it.
|
|
995
|
-
|
|
1006
|
+
// Always a FRAME (asserted above) and archiving hides its whole subtree, so no payload.
|
|
1007
|
+
await this.emitBoardChanged(homeWorkspaceId, {
|
|
1008
|
+
reason: archived ? 'block-archived' : 'block-restored',
|
|
1009
|
+
blockId: id,
|
|
1010
|
+
});
|
|
996
1011
|
// Always a frame (asserted above), so it owes the caller this board's layout override.
|
|
997
1012
|
return this.projectForWorkspace(workspaceId, assertFound(await this.blockRepository.get(homeWorkspaceId, id), 'Block', id));
|
|
998
1013
|
}
|
|
@@ -1037,8 +1052,11 @@ export class BoardService {
|
|
|
1037
1052
|
}
|
|
1038
1053
|
}
|
|
1039
1054
|
// Origin = the target's HOME so toggling an edge on a shared task fans out to all mounts.
|
|
1040
|
-
|
|
1041
|
-
|
|
1055
|
+
// Re-read first so the event carries the target: `dependsOn` lives on it, so one block
|
|
1056
|
+
// states the whole change (the SOURCE block is untouched).
|
|
1057
|
+
const updated = assertFound(await this.blockRepository.get(homeWorkspaceId, targetId), 'Block', targetId);
|
|
1058
|
+
await this.emitBoardChanged(homeWorkspaceId, { reason: 'dependency-toggled', block: updated });
|
|
1059
|
+
return updated;
|
|
1042
1060
|
}
|
|
1043
1061
|
}
|
|
1044
1062
|
/**
|