@cat-factory/orchestration 0.63.0 → 0.65.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.d.ts +4 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +45 -12
- package/dist/container.js.map +1 -1
- package/dist/modules/execution/AgentContextBuilder.d.ts +11 -0
- package/dist/modules/execution/AgentContextBuilder.d.ts.map +1 -1
- package/dist/modules/execution/AgentContextBuilder.js +49 -3
- package/dist/modules/execution/AgentContextBuilder.js.map +1 -1
- package/dist/modules/execution/ExecutionService.d.ts +9 -1
- package/dist/modules/execution/ExecutionService.d.ts.map +1 -1
- package/dist/modules/execution/ExecutionService.js +9 -1
- package/dist/modules/execution/ExecutionService.js.map +1 -1
- package/dist/modules/execution/RunDispatcher.d.ts +62 -1
- package/dist/modules/execution/RunDispatcher.d.ts.map +1 -1
- package/dist/modules/execution/RunDispatcher.js +327 -90
- package/dist/modules/execution/RunDispatcher.js.map +1 -1
- package/dist/modules/execution/RunStateMachine.d.ts +7 -0
- package/dist/modules/execution/RunStateMachine.d.ts.map +1 -1
- package/dist/modules/execution/RunStateMachine.js +8 -0
- package/dist/modules/execution/RunStateMachine.js.map +1 -1
- package/dist/modules/execution/deployer.logic.d.ts +15 -1
- package/dist/modules/execution/deployer.logic.d.ts.map +1 -1
- package/dist/modules/execution/deployer.logic.js +41 -2
- package/dist/modules/execution/deployer.logic.js.map +1 -1
- package/dist/modules/execution/frame.logic.d.ts +20 -0
- package/dist/modules/execution/frame.logic.d.ts.map +1 -0
- package/dist/modules/execution/frame.logic.js +40 -0
- package/dist/modules/execution/frame.logic.js.map +1 -0
- package/dist/modules/initiative/InitiativeLoopService.d.ts +88 -0
- package/dist/modules/initiative/InitiativeLoopService.d.ts.map +1 -0
- package/dist/modules/initiative/InitiativeLoopService.js +356 -0
- package/dist/modules/initiative/InitiativeLoopService.js.map +1 -0
- package/dist/modules/initiative/InitiativeService.d.ts +31 -1
- package/dist/modules/initiative/InitiativeService.d.ts.map +1 -1
- package/dist/modules/initiative/InitiativeService.js +53 -0
- package/dist/modules/initiative/InitiativeService.js.map +1 -1
- package/dist/modules/initiative/initiative.logic.d.ts +58 -1
- package/dist/modules/initiative/initiative.logic.d.ts.map +1 -1
- package/dist/modules/initiative/initiative.logic.js +157 -0
- package/dist/modules/initiative/initiative.logic.js.map +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Block } from '@cat-factory/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The service-frame block enclosing a block, resolved from an already-loaded block map — a
|
|
4
|
+
* bounded (the tree is at most frame → module → task), cycle-guarded walk up the `parentId`
|
|
5
|
+
* chain. Returns the first ancestor that is a `frame` (or the topmost parent-less block, or the
|
|
6
|
+
* last block reached if the map is missing a link). Pure: the caller owns the load, so this does
|
|
7
|
+
* NO repository access — the point-read variant is `AgentContextBuilder.resolveServiceFrame`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function frameOf(byId: ReadonlyMap<string, Block>, blockId: string): Block | null;
|
|
10
|
+
/**
|
|
11
|
+
* The connected service frames "directly involved" in a task beyond its own — the shared read-time
|
|
12
|
+
* STALE FILTER over `block.involvedServiceIds` used by BOTH the deployer fan-out
|
|
13
|
+
* (`RunDispatcher.resolveDeployTargets`) and the agent-context resolution
|
|
14
|
+
* (`AgentContextBuilder.resolveInvolvedServices`), so the two can't drift on which peers are valid.
|
|
15
|
+
* Keeps only ids that (a) are not the own frame, (b) are STILL a connection neighbour of the own
|
|
16
|
+
* frame, (c) are unique, and (d) resolve to a `service` `frame` block. A stale/removed connection
|
|
17
|
+
* makes an id inert (dropped), never a run failure. Pure — the caller owns the block-list load.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validInvolvedServiceFrames(blocks: readonly Block[], block: Block, ownFrameId: string): Block[];
|
|
20
|
+
//# sourceMappingURL=frame.logic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/execution/frame.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAGhD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAOvF;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,GACjB,KAAK,EAAE,CAUT"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { connectionNeighborIds } from '@cat-factory/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The service-frame block enclosing a block, resolved from an already-loaded block map — a
|
|
4
|
+
* bounded (the tree is at most frame → module → task), cycle-guarded walk up the `parentId`
|
|
5
|
+
* chain. Returns the first ancestor that is a `frame` (or the topmost parent-less block, or the
|
|
6
|
+
* last block reached if the map is missing a link). Pure: the caller owns the load, so this does
|
|
7
|
+
* NO repository access — the point-read variant is `AgentContextBuilder.resolveServiceFrame`.
|
|
8
|
+
*/
|
|
9
|
+
export function frameOf(byId, blockId) {
|
|
10
|
+
let cursor = byId.get(blockId) ?? null;
|
|
11
|
+
for (let i = 0; cursor && i < 8; i++) {
|
|
12
|
+
if (cursor.level === 'frame' || !cursor.parentId)
|
|
13
|
+
return cursor;
|
|
14
|
+
cursor = byId.get(cursor.parentId) ?? null;
|
|
15
|
+
}
|
|
16
|
+
return cursor;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The connected service frames "directly involved" in a task beyond its own — the shared read-time
|
|
20
|
+
* STALE FILTER over `block.involvedServiceIds` used by BOTH the deployer fan-out
|
|
21
|
+
* (`RunDispatcher.resolveDeployTargets`) and the agent-context resolution
|
|
22
|
+
* (`AgentContextBuilder.resolveInvolvedServices`), so the two can't drift on which peers are valid.
|
|
23
|
+
* Keeps only ids that (a) are not the own frame, (b) are STILL a connection neighbour of the own
|
|
24
|
+
* frame, (c) are unique, and (d) resolve to a `service` `frame` block. A stale/removed connection
|
|
25
|
+
* makes an id inert (dropped), never a run failure. Pure — the caller owns the block-list load.
|
|
26
|
+
*/
|
|
27
|
+
export function validInvolvedServiceFrames(blocks, block, ownFrameId) {
|
|
28
|
+
if (block.level !== 'task')
|
|
29
|
+
return [];
|
|
30
|
+
const ids = block.involvedServiceIds ?? [];
|
|
31
|
+
if (ids.length === 0)
|
|
32
|
+
return [];
|
|
33
|
+
const byId = new Map(blocks.map((b) => [b.id, b]));
|
|
34
|
+
const neighbors = connectionNeighborIds(blocks, ownFrameId);
|
|
35
|
+
return ids
|
|
36
|
+
.filter((id, i) => id !== ownFrameId && neighbors.has(id) && ids.indexOf(id) === i)
|
|
37
|
+
.map((id) => byId.get(id))
|
|
38
|
+
.filter((b) => !!b && b.level === 'frame' && b.type === 'service');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=frame.logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.logic.js","sourceRoot":"","sources":["../../../src/modules/execution/frame.logic.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAE9D;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,IAAgC,EAAE,OAAe;IACvE,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,CAAA;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,OAAO,MAAM,CAAA;QAC/D,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAA;IAC5C,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAwB,EACxB,KAAY,EACZ,UAAkB;IAElB,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM;QAAE,OAAO,EAAE,CAAA;IACrC,MAAM,GAAG,GAAG,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAA;IAC1C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAClD,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC3D,OAAO,GAAG;SACP,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,UAAU,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;SAClF,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SACzB,MAAM,CAAC,CAAC,CAAC,EAAc,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;AAClF,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { BlockRepository, Clock, ExecutionEventPublisher, IdGenerator, InitiativeItem, InitiativeRepository, PipelineRepository, ResolveRunRepoContext, ServiceRepository, TaskEstimate } from '@cat-factory/kernel';
|
|
2
|
+
import type { ExecutionService } from '../execution/ExecutionService.js';
|
|
3
|
+
import type { NotificationService } from '../notifications/NotificationService.js';
|
|
4
|
+
import type { InitiativeService } from './InitiativeService.js';
|
|
5
|
+
export interface InitiativeLoopServiceDependencies {
|
|
6
|
+
initiativeRepository: InitiativeRepository;
|
|
7
|
+
initiativeService: InitiativeService;
|
|
8
|
+
blockRepository: BlockRepository;
|
|
9
|
+
pipelineRepository: PipelineRepository;
|
|
10
|
+
executionService: ExecutionService;
|
|
11
|
+
events: ExecutionEventPublisher;
|
|
12
|
+
clock: Clock;
|
|
13
|
+
idGenerator: IdGenerator;
|
|
14
|
+
/** Raises the `initiative` notification (blocked item / completion). Absent ⇒ no cards. */
|
|
15
|
+
notificationService?: NotificationService;
|
|
16
|
+
/** Resolves the initiative's repo for the best-effort tracker re-commit. Absent ⇒ mirror skipped. */
|
|
17
|
+
resolveRunRepoContext?: ResolveRunRepoContext;
|
|
18
|
+
/** Stamps a spawned task with the frame's service (in-org sharing). Absent ⇒ no service link. */
|
|
19
|
+
serviceRepository?: ServiceRepository;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The initiative EXECUTION LOOP (slice 3): the driver that turns an approved plan into a
|
|
23
|
+
* running body of work. The cron/interval sweepers call {@link runDue}; a settling child run
|
|
24
|
+
* pokes {@link pokeForInitiativeBlock} so the loop advances immediately instead of waiting for
|
|
25
|
+
* the next sweep. Each per-initiative {@link tick}:
|
|
26
|
+
*
|
|
27
|
+
* 1. RECONCILE — batch-reads ONLY the spawned task blocks (one chunked `findByIds`, indexed —
|
|
28
|
+
* no N+1, no full-workspace scan) and folds each block's status back onto its item (done +
|
|
29
|
+
* PR link / pr_open / blocked + deviation).
|
|
30
|
+
* 2. COMPLETE — when every item is settled, flip the initiative + its anchor block `done`,
|
|
31
|
+
* re-commit the tracker, and notify.
|
|
32
|
+
* 3. SPAWN — creates task blocks for the eligible `pending` items (current phase, deps met,
|
|
33
|
+
* phase not halted by a blocked sibling) up to the concurrency cap, picking each task's
|
|
34
|
+
* pipeline from the policy rules. Spawning is CLAIM-FIRST: a CAS write records the
|
|
35
|
+
* pre-generated block id BEFORE any side effect, so a concurrent ticker that lost the CAS
|
|
36
|
+
* created nothing (single-writer by construction, exactly like the rest of the entity).
|
|
37
|
+
*
|
|
38
|
+
* Every entity write goes through {@link InitiativeService}'s rev-guarded CAS, so a lost tick is
|
|
39
|
+
* simply abandoned; the next sweep retries. A per-service task-limit `ConflictError` leaves the
|
|
40
|
+
* item `pending` for the next sweep (never `blocked`); a missing pipeline (deleted after ingest)
|
|
41
|
+
* records a deviation + notification and blocks the item — the sweep NEVER throws.
|
|
42
|
+
*/
|
|
43
|
+
export declare class InitiativeLoopService {
|
|
44
|
+
private readonly deps;
|
|
45
|
+
/** In-process re-entrancy guard so a cron sweep + a terminal poke don't double-tick one initiative. */
|
|
46
|
+
private readonly ticking;
|
|
47
|
+
constructor(deps: InitiativeLoopServiceDependencies);
|
|
48
|
+
/**
|
|
49
|
+
* Tick every `executing` initiative across all workspaces. The cron (Worker) / interval
|
|
50
|
+
* (Node) sweepers call this. Never throws — a per-initiative failure is isolated so one bad
|
|
51
|
+
* initiative can't stall the others. Returns aggregate counts for logging.
|
|
52
|
+
*/
|
|
53
|
+
runDue(_now: number): Promise<{
|
|
54
|
+
ticked: number;
|
|
55
|
+
spawned: number;
|
|
56
|
+
completed: number;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Best-effort single tick for one initiative, triggered when a spawned child run settles (a
|
|
60
|
+
* terminal run / a merge). Fire-and-forget from the caller; swallow errors (the sweep is the
|
|
61
|
+
* backstop). `initiativeBlockId` is the block the settled child carries in its `initiativeId`.
|
|
62
|
+
*/
|
|
63
|
+
pokeForInitiativeBlock(workspaceId: string, initiativeBlockId: string): Promise<void>;
|
|
64
|
+
private tick;
|
|
65
|
+
/**
|
|
66
|
+
* Batch-read ONLY the blocks the initiative's items were spawned as (a single chunked `IN`
|
|
67
|
+
* via {@link BlockRepository.findByIds}), indexed by id for the reconcile lookup — never a
|
|
68
|
+
* full-workspace block scan. Empty when no item carries a block link yet.
|
|
69
|
+
*/
|
|
70
|
+
private readSpawnedBlocks;
|
|
71
|
+
private reconcile;
|
|
72
|
+
private complete;
|
|
73
|
+
private spawn;
|
|
74
|
+
private spawnItem;
|
|
75
|
+
/** Build the task block a spawned item runs as (item estimate stamped, `initiativeId` linked). */
|
|
76
|
+
private buildTaskBlock;
|
|
77
|
+
/**
|
|
78
|
+
* Block an item that could not be spawned (a missing pipeline / a start failure): mark it
|
|
79
|
+
* `blocked`, drop its dangling block link, record a deviation, and raise the notification.
|
|
80
|
+
* The blocked item halts its phase (see `phaseIsHalted`) until a human intervenes.
|
|
81
|
+
*/
|
|
82
|
+
private blockItem;
|
|
83
|
+
private recommitTracker;
|
|
84
|
+
private notify;
|
|
85
|
+
}
|
|
86
|
+
/** Build a {@link TaskEstimate} from an item's planner estimate, stamped for the spawned block. */
|
|
87
|
+
export declare function estimateForItem(item: InitiativeItem, now: number): TaskEstimate | undefined;
|
|
88
|
+
//# sourceMappingURL=InitiativeLoopService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InitiativeLoopService.d.ts","sourceRoot":"","sources":["../../../src/modules/initiative/InitiativeLoopService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EACf,KAAK,EACL,uBAAuB,EACvB,WAAW,EAEX,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACb,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAA;AAClF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAa/D,MAAM,WAAW,iCAAiC;IAChD,oBAAoB,EAAE,oBAAoB,CAAA;IAC1C,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,eAAe,EAAE,eAAe,CAAA;IAChC,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,MAAM,EAAE,uBAAuB,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAA;IACZ,WAAW,EAAE,WAAW,CAAA;IACxB,2FAA2F;IAC3F,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC,qGAAqG;IACrG,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACtC;AAcD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,qBAAqB;IAIpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,uGAAuG;IACvG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C,YAA6B,IAAI,EAAE,iCAAiC,EAAI;IAExE;;;;OAIG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAe1F;IAED;;;;OAIG;IACG,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU1F;YAEa,IAAI;IAqClB;;;;OAIG;YACW,iBAAiB;YAcjB,SAAS;YA0CT,QAAQ;YAqBR,KAAK;YAsCL,SAAS;IAwEvB,kGAAkG;IAClG,OAAO,CAAC,cAAc;IA2BtB;;;;OAIG;YACW,SAAS;YA+BT,eAAe;YA2Bf,MAAM;CAgCrB;AAED,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAU3F"}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { ConflictError, DomainError } from '@cat-factory/kernel';
|
|
2
|
+
import { commitInitiativeTracker } from '@cat-factory/agents';
|
|
3
|
+
import { activeItemCount, allItemsSettled, applyRevertClaim, applySpawnClaim, deriveCurrentPhase, effectiveMaxConcurrent, eligibleItemsToSpawn, reconcileItem, selectInitiativePipeline, } from './initiative.logic.js';
|
|
4
|
+
/**
|
|
5
|
+
* The initiative EXECUTION LOOP (slice 3): the driver that turns an approved plan into a
|
|
6
|
+
* running body of work. The cron/interval sweepers call {@link runDue}; a settling child run
|
|
7
|
+
* pokes {@link pokeForInitiativeBlock} so the loop advances immediately instead of waiting for
|
|
8
|
+
* the next sweep. Each per-initiative {@link tick}:
|
|
9
|
+
*
|
|
10
|
+
* 1. RECONCILE — batch-reads ONLY the spawned task blocks (one chunked `findByIds`, indexed —
|
|
11
|
+
* no N+1, no full-workspace scan) and folds each block's status back onto its item (done +
|
|
12
|
+
* PR link / pr_open / blocked + deviation).
|
|
13
|
+
* 2. COMPLETE — when every item is settled, flip the initiative + its anchor block `done`,
|
|
14
|
+
* re-commit the tracker, and notify.
|
|
15
|
+
* 3. SPAWN — creates task blocks for the eligible `pending` items (current phase, deps met,
|
|
16
|
+
* phase not halted by a blocked sibling) up to the concurrency cap, picking each task's
|
|
17
|
+
* pipeline from the policy rules. Spawning is CLAIM-FIRST: a CAS write records the
|
|
18
|
+
* pre-generated block id BEFORE any side effect, so a concurrent ticker that lost the CAS
|
|
19
|
+
* created nothing (single-writer by construction, exactly like the rest of the entity).
|
|
20
|
+
*
|
|
21
|
+
* Every entity write goes through {@link InitiativeService}'s rev-guarded CAS, so a lost tick is
|
|
22
|
+
* simply abandoned; the next sweep retries. A per-service task-limit `ConflictError` leaves the
|
|
23
|
+
* item `pending` for the next sweep (never `blocked`); a missing pipeline (deleted after ingest)
|
|
24
|
+
* records a deviation + notification and blocks the item — the sweep NEVER throws.
|
|
25
|
+
*/
|
|
26
|
+
export class InitiativeLoopService {
|
|
27
|
+
deps;
|
|
28
|
+
/** In-process re-entrancy guard so a cron sweep + a terminal poke don't double-tick one initiative. */
|
|
29
|
+
ticking = new Set();
|
|
30
|
+
constructor(deps) {
|
|
31
|
+
this.deps = deps;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Tick every `executing` initiative across all workspaces. The cron (Worker) / interval
|
|
35
|
+
* (Node) sweepers call this. Never throws — a per-initiative failure is isolated so one bad
|
|
36
|
+
* initiative can't stall the others. Returns aggregate counts for logging.
|
|
37
|
+
*/
|
|
38
|
+
async runDue(_now) {
|
|
39
|
+
const executing = await this.deps.initiativeRepository.listExecuting();
|
|
40
|
+
let spawned = 0;
|
|
41
|
+
let completed = 0;
|
|
42
|
+
for (const { workspaceId, initiative } of executing) {
|
|
43
|
+
try {
|
|
44
|
+
const result = await this.tick(workspaceId, initiative);
|
|
45
|
+
spawned += result.spawned;
|
|
46
|
+
if (result.completed)
|
|
47
|
+
completed++;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Isolate a bad initiative; the next sweep retries it. (Each entity write is already
|
|
51
|
+
// CAS-guarded, so a partial tick left the entity consistent.)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { ticked: executing.length, spawned, completed };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Best-effort single tick for one initiative, triggered when a spawned child run settles (a
|
|
58
|
+
* terminal run / a merge). Fire-and-forget from the caller; swallow errors (the sweep is the
|
|
59
|
+
* backstop). `initiativeBlockId` is the block the settled child carries in its `initiativeId`.
|
|
60
|
+
*/
|
|
61
|
+
async pokeForInitiativeBlock(workspaceId, initiativeBlockId) {
|
|
62
|
+
try {
|
|
63
|
+
const initiative = await this.deps.initiativeRepository.getByBlock(workspaceId, initiativeBlockId);
|
|
64
|
+
if (initiative && initiative.status === 'executing')
|
|
65
|
+
await this.tick(workspaceId, initiative);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Swallow — the periodic sweep will pick the initiative up.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async tick(workspaceId, seed) {
|
|
72
|
+
const key = seed.id;
|
|
73
|
+
if (this.ticking.has(key))
|
|
74
|
+
return { spawned: 0, completed: false };
|
|
75
|
+
this.ticking.add(key);
|
|
76
|
+
try {
|
|
77
|
+
// Re-read the freshest entity (the seed may be stale — from listExecuting or a poke).
|
|
78
|
+
let initiative = await this.deps.initiativeRepository.getByBlock(workspaceId, seed.blockId);
|
|
79
|
+
if (!initiative || initiative.status !== 'executing')
|
|
80
|
+
return { spawned: 0, completed: false };
|
|
81
|
+
const startRev = initiative.rev;
|
|
82
|
+
// 1. Reconcile spawned tasks from their blocks — a single batched point-read of ONLY the
|
|
83
|
+
// spawned task blocks (chunked `IN` via `findByIds`, not a full-workspace scan), indexed.
|
|
84
|
+
const blocksById = await this.readSpawnedBlocks(workspaceId, initiative);
|
|
85
|
+
initiative = (await this.reconcile(workspaceId, initiative, blocksById)) ?? initiative;
|
|
86
|
+
// 2. Complete when every item is settled.
|
|
87
|
+
if (allItemsSettled(initiative)) {
|
|
88
|
+
await this.complete(workspaceId, initiative);
|
|
89
|
+
return { spawned: 0, completed: true };
|
|
90
|
+
}
|
|
91
|
+
// 3. Spawn eligible items up to the effective concurrency cap.
|
|
92
|
+
const spawned = await this.spawn(workspaceId, initiative);
|
|
93
|
+
// 4. Best-effort mirror the tracker — ONLY when this tick actually mutated the entity (a
|
|
94
|
+
// reconcile folded a status, or a spawn/claim/block landed → the persisted `rev` moved).
|
|
95
|
+
// An idle tick (spawned tasks still running, nothing to fold) skips it entirely, so an
|
|
96
|
+
// executing initiative waiting on in-flight PRs doesn't hit GitHub every sweep. Never
|
|
97
|
+
// before a DB CAS wins; hash-short-circuited even when it does run.
|
|
98
|
+
await this.recommitTracker(workspaceId, initiative.blockId, startRev).catch(() => { });
|
|
99
|
+
return { spawned, completed: false };
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
this.ticking.delete(key);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Batch-read ONLY the blocks the initiative's items were spawned as (a single chunked `IN`
|
|
107
|
+
* via {@link BlockRepository.findByIds}), indexed by id for the reconcile lookup — never a
|
|
108
|
+
* full-workspace block scan. Empty when no item carries a block link yet.
|
|
109
|
+
*/
|
|
110
|
+
async readSpawnedBlocks(workspaceId, initiative) {
|
|
111
|
+
const ids = (initiative.items ?? []).map((i) => i.blockId).filter((id) => !!id);
|
|
112
|
+
if (ids.length === 0)
|
|
113
|
+
return new Map();
|
|
114
|
+
const found = await this.deps.blockRepository.findByIds(ids);
|
|
115
|
+
return new Map(found.filter((r) => r.workspaceId === workspaceId).map((r) => [r.block.id, r.block]));
|
|
116
|
+
}
|
|
117
|
+
// ---- Reconcile ----------------------------------------------------------
|
|
118
|
+
async reconcile(workspaceId, initiative, blocksById) {
|
|
119
|
+
// Nothing to reconcile unless an actively-spawned item exists.
|
|
120
|
+
const hasActive = (initiative.items ?? []).some((i) => i.blockId && (i.status === 'in_progress' || i.status === 'pr_open'));
|
|
121
|
+
if (!hasActive)
|
|
122
|
+
return null;
|
|
123
|
+
let newlyBlocked = false;
|
|
124
|
+
const updated = await this.deps.initiativeService.update(workspaceId, initiative.blockId, (current) => {
|
|
125
|
+
const items = (current.items ?? []).map((it) => it.blockId ? reconcileItem(it, blocksById.get(it.blockId)) : it);
|
|
126
|
+
const deviations = [...(current.deviations ?? [])];
|
|
127
|
+
for (let i = 0; i < items.length; i++) {
|
|
128
|
+
const before = current.items[i];
|
|
129
|
+
const after = items[i];
|
|
130
|
+
if (after.status === 'blocked' && before.status !== 'blocked') {
|
|
131
|
+
newlyBlocked = true;
|
|
132
|
+
deviations.push({
|
|
133
|
+
id: this.deps.idGenerator.next('idev'),
|
|
134
|
+
at: this.deps.clock.now(),
|
|
135
|
+
itemId: after.id,
|
|
136
|
+
description: `Task for "${after.title}" was blocked; the phase is halted until it is retried or skipped.`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { ...current, items, deviations };
|
|
141
|
+
});
|
|
142
|
+
if (newlyBlocked && updated)
|
|
143
|
+
await this.notify(workspaceId, updated, 'item_blocked');
|
|
144
|
+
return updated;
|
|
145
|
+
}
|
|
146
|
+
// ---- Complete -----------------------------------------------------------
|
|
147
|
+
async complete(workspaceId, initiative) {
|
|
148
|
+
const done = await this.deps.initiativeService.update(workspaceId, initiative.blockId, (current) => allItemsSettled(current) && current.status === 'executing'
|
|
149
|
+
? { ...current, status: 'done' }
|
|
150
|
+
: current);
|
|
151
|
+
if (!done || done.status !== 'done')
|
|
152
|
+
return;
|
|
153
|
+
await this.deps.blockRepository.update(workspaceId, done.blockId, {
|
|
154
|
+
status: 'done',
|
|
155
|
+
progress: 1,
|
|
156
|
+
});
|
|
157
|
+
await this.deps.events.boardChanged(workspaceId, 'initiative-complete', done.blockId);
|
|
158
|
+
await this.recommitTracker(workspaceId, done.blockId).catch(() => { });
|
|
159
|
+
await this.notify(workspaceId, done, 'complete');
|
|
160
|
+
}
|
|
161
|
+
// ---- Spawn --------------------------------------------------------------
|
|
162
|
+
async spawn(workspaceId, initiative) {
|
|
163
|
+
const phase = deriveCurrentPhase(initiative);
|
|
164
|
+
if (!phase)
|
|
165
|
+
return 0;
|
|
166
|
+
let slots = effectiveMaxConcurrent(initiative, phase) - activeItemCount(initiative);
|
|
167
|
+
if (slots <= 0)
|
|
168
|
+
return 0;
|
|
169
|
+
const eligible = eligibleItemsToSpawn(initiative);
|
|
170
|
+
if (eligible.length === 0)
|
|
171
|
+
return 0;
|
|
172
|
+
// Resolve the host frame ONCE (invariant across items): spawned tasks live under the
|
|
173
|
+
// initiative's parent service frame (structural containment), linked to the initiative via
|
|
174
|
+
// `initiativeId` (epic-style membership). No frame ⇒ nothing to host the work.
|
|
175
|
+
const anchor = await this.deps.blockRepository.get(workspaceId, initiative.blockId);
|
|
176
|
+
const frame = anchor?.parentId
|
|
177
|
+
? await this.deps.blockRepository.get(workspaceId, anchor.parentId)
|
|
178
|
+
: null;
|
|
179
|
+
if (!frame)
|
|
180
|
+
return 0;
|
|
181
|
+
const serviceId = this.deps.serviceRepository
|
|
182
|
+
? ((await this.deps.serviceRepository.getByFrameBlock(frame.id))?.id ?? null)
|
|
183
|
+
: null;
|
|
184
|
+
let spawned = 0;
|
|
185
|
+
let entity = initiative;
|
|
186
|
+
for (const item of eligible) {
|
|
187
|
+
if (slots <= 0)
|
|
188
|
+
break;
|
|
189
|
+
const result = await this.spawnItem(workspaceId, entity, item, frame, serviceId);
|
|
190
|
+
entity = result.entity;
|
|
191
|
+
if (result.outcome === 'spawned') {
|
|
192
|
+
spawned++;
|
|
193
|
+
slots--;
|
|
194
|
+
}
|
|
195
|
+
else if (result.outcome === 'conflict') {
|
|
196
|
+
// Lost a CAS, or the per-service task limit was hit — abandon the rest of this tick.
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
// 'skipped' (item no longer pending, or blocked on a config problem) — try the next item.
|
|
200
|
+
}
|
|
201
|
+
return spawned;
|
|
202
|
+
}
|
|
203
|
+
async spawnItem(workspaceId, entity, item, frame, serviceId) {
|
|
204
|
+
const policy = entity.policy;
|
|
205
|
+
if (!policy)
|
|
206
|
+
return { outcome: 'skipped', entity };
|
|
207
|
+
// Pick the pipeline first; a missing one is a config problem (a pipeline deleted after
|
|
208
|
+
// ingest) → block the item + deviation + notify, NEVER throw inside the sweep.
|
|
209
|
+
const pipelineId = selectInitiativePipeline(item, policy);
|
|
210
|
+
if (!(await this.deps.pipelineRepository.get(workspaceId, pipelineId))) {
|
|
211
|
+
const blocked = await this.blockItem(workspaceId, entity.blockId, item.id, `Pipeline "${pipelineId}" no longer exists — pick another in the initiative policy.`);
|
|
212
|
+
return { outcome: 'skipped', entity: blocked ?? entity };
|
|
213
|
+
}
|
|
214
|
+
// Claim the item BEFORE any side effect: a CAS write records the pre-generated block id, so
|
|
215
|
+
// a concurrent ticker whose CAS lost created nothing (the winner's claim short-circuits the
|
|
216
|
+
// loser's transform to a no-op).
|
|
217
|
+
const spawnedBlockId = this.deps.idGenerator.next('blk');
|
|
218
|
+
const claimed = await this.deps.initiativeService.update(workspaceId, entity.blockId, (current) => applySpawnClaim(current, item.id, spawnedBlockId));
|
|
219
|
+
if (!claimed)
|
|
220
|
+
return { outcome: 'skipped', entity };
|
|
221
|
+
const claimedItem = (claimed.items ?? []).find((i) => i.id === item.id);
|
|
222
|
+
if (!claimedItem || claimedItem.blockId !== spawnedBlockId) {
|
|
223
|
+
// Another ticker already claimed it (a different block id), or it moved on — skip.
|
|
224
|
+
return { outcome: 'skipped', entity: claimed };
|
|
225
|
+
}
|
|
226
|
+
// The claim won: insert the task block, then start its run.
|
|
227
|
+
const block = this.buildTaskBlock(spawnedBlockId, item, frame, entity.blockId);
|
|
228
|
+
try {
|
|
229
|
+
await this.deps.blockRepository.insert(workspaceId, block, serviceId);
|
|
230
|
+
await this.deps.events.boardChanged(workspaceId, 'block-added', block.id);
|
|
231
|
+
await this.deps.executionService.start(workspaceId, block.id, pipelineId);
|
|
232
|
+
return { outcome: 'spawned', entity: claimed };
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
// Roll back the block, then decide on the item. A per-service task-limit ConflictError is
|
|
236
|
+
// transient → revert the item to `pending` for the next sweep and stop spawning this tick.
|
|
237
|
+
// Any OTHER failure is (likely) a persistent config problem → block + notify so it isn't
|
|
238
|
+
// retried forever.
|
|
239
|
+
await this.deps.blockRepository.deleteMany(workspaceId, [block.id]).catch(() => { });
|
|
240
|
+
const reason = error instanceof DomainError ? error.details?.reason : undefined;
|
|
241
|
+
if (error instanceof ConflictError && reason === 'task_limit_reached') {
|
|
242
|
+
const reverted = await this.deps.initiativeService.update(workspaceId, entity.blockId, (current) => applyRevertClaim(current, item.id, spawnedBlockId));
|
|
243
|
+
return { outcome: 'conflict', entity: reverted ?? claimed };
|
|
244
|
+
}
|
|
245
|
+
const message = error instanceof Error ? error.message : 'Failed to start the task.';
|
|
246
|
+
const blocked = await this.blockItem(workspaceId, entity.blockId, item.id, `Could not start the task: ${message}`);
|
|
247
|
+
return { outcome: 'skipped', entity: blocked ?? claimed };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/** Build the task block a spawned item runs as (item estimate stamped, `initiativeId` linked). */
|
|
251
|
+
buildTaskBlock(blockId, item, frame, initiativeBlockId) {
|
|
252
|
+
const estimate = estimateForItem(item, this.deps.clock.now());
|
|
253
|
+
return {
|
|
254
|
+
id: blockId,
|
|
255
|
+
title: item.title,
|
|
256
|
+
// Inherit the host frame's (behavioural) repo type, like BoardService.addTask.
|
|
257
|
+
type: frame.type,
|
|
258
|
+
description: item.description,
|
|
259
|
+
position: { x: 24, y: 96 },
|
|
260
|
+
status: 'planned',
|
|
261
|
+
progress: 0,
|
|
262
|
+
dependsOn: [],
|
|
263
|
+
executionId: null,
|
|
264
|
+
level: 'task',
|
|
265
|
+
parentId: frame.id,
|
|
266
|
+
// Epic-style membership so the loop reconciles from the block and the board badges it. The
|
|
267
|
+
// loop OWNS sequencing, so NEVER opt into `autoStartDependents` on a spawned block.
|
|
268
|
+
initiativeId: initiativeBlockId,
|
|
269
|
+
...(estimate ? { estimate } : {}),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Block an item that could not be spawned (a missing pipeline / a start failure): mark it
|
|
274
|
+
* `blocked`, drop its dangling block link, record a deviation, and raise the notification.
|
|
275
|
+
* The blocked item halts its phase (see `phaseIsHalted`) until a human intervenes.
|
|
276
|
+
*/
|
|
277
|
+
async blockItem(workspaceId, initiativeBlockId, itemId, note) {
|
|
278
|
+
const updated = await this.deps.initiativeService.update(workspaceId, initiativeBlockId, (current) => {
|
|
279
|
+
const items = (current.items ?? []).map((i) => i.id === itemId ? { ...i, status: 'blocked', note, blockId: null } : i);
|
|
280
|
+
const deviations = [
|
|
281
|
+
...(current.deviations ?? []),
|
|
282
|
+
{
|
|
283
|
+
id: this.deps.idGenerator.next('idev'),
|
|
284
|
+
at: this.deps.clock.now(),
|
|
285
|
+
itemId,
|
|
286
|
+
description: note,
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
return { ...current, items, deviations };
|
|
290
|
+
});
|
|
291
|
+
if (updated)
|
|
292
|
+
await this.notify(workspaceId, updated, 'item_blocked');
|
|
293
|
+
return updated;
|
|
294
|
+
}
|
|
295
|
+
// ---- Tracker mirror + notifications -------------------------------------
|
|
296
|
+
async recommitTracker(workspaceId, initiativeBlockId, sinceRev) {
|
|
297
|
+
if (!this.deps.resolveRunRepoContext)
|
|
298
|
+
return;
|
|
299
|
+
const initiative = await this.deps.initiativeRepository.getByBlock(workspaceId, initiativeBlockId);
|
|
300
|
+
if (!initiative)
|
|
301
|
+
return;
|
|
302
|
+
// Nothing changed this tick (the persisted rev never moved) ⇒ skip the repo round-trip.
|
|
303
|
+
if (sinceRev !== undefined && initiative.rev === sinceRev)
|
|
304
|
+
return;
|
|
305
|
+
const runRepo = await this.deps.resolveRunRepoContext(workspaceId, initiativeBlockId);
|
|
306
|
+
if (!runRepo)
|
|
307
|
+
return;
|
|
308
|
+
const doc = await commitInitiativeTracker(runRepo.repo, runRepo.baseBranch, initiative, new Date(this.deps.clock.now()));
|
|
309
|
+
// Stamp the committed version/hash back (a content-unchanged tick commits nothing, so the
|
|
310
|
+
// no-change short-circuit means a replay skips this write too). `markExecuting` leaves an
|
|
311
|
+
// already-`executing` status untouched and only writes the `doc` bookkeeping.
|
|
312
|
+
if (doc)
|
|
313
|
+
await this.deps.initiativeService.markExecuting(workspaceId, initiativeBlockId, doc);
|
|
314
|
+
}
|
|
315
|
+
async notify(workspaceId, initiative, reason) {
|
|
316
|
+
if (!this.deps.notificationService)
|
|
317
|
+
return;
|
|
318
|
+
const items = initiative.items ?? [];
|
|
319
|
+
const blocked = items.filter((i) => i.status === 'blocked');
|
|
320
|
+
const input = reason === 'complete'
|
|
321
|
+
? {
|
|
322
|
+
title: `Initiative "${initiative.title}" complete`,
|
|
323
|
+
body: `Every planned task is resolved (${items.length} item${items.length === 1 ? '' : 's'}).`,
|
|
324
|
+
}
|
|
325
|
+
: {
|
|
326
|
+
title: `Initiative "${initiative.title}" needs attention`,
|
|
327
|
+
body: blocked.length === 1
|
|
328
|
+
? `A task was blocked (${blocked[0].title}). Retry or skip it to unblock the phase.`
|
|
329
|
+
: `${blocked.length} tasks are blocked. Retry or skip them to unblock the phase.`,
|
|
330
|
+
};
|
|
331
|
+
await this.deps.notificationService
|
|
332
|
+
.raise(workspaceId, {
|
|
333
|
+
type: 'initiative',
|
|
334
|
+
blockId: initiative.blockId,
|
|
335
|
+
executionId: null,
|
|
336
|
+
title: input.title,
|
|
337
|
+
body: input.body,
|
|
338
|
+
payload: { initiativeReason: reason },
|
|
339
|
+
})
|
|
340
|
+
.catch(() => { });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/** Build a {@link TaskEstimate} from an item's planner estimate, stamped for the spawned block. */
|
|
344
|
+
export function estimateForItem(item, now) {
|
|
345
|
+
if (!item.estimate)
|
|
346
|
+
return undefined;
|
|
347
|
+
return {
|
|
348
|
+
complexity: item.estimate.complexity,
|
|
349
|
+
risk: item.estimate.risk,
|
|
350
|
+
impact: item.estimate.impact,
|
|
351
|
+
rationale: item.estimate.rationale ?? '',
|
|
352
|
+
model: 'initiative-planner',
|
|
353
|
+
createdAt: now,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=InitiativeLoopService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InitiativeLoopService.js","sourceRoot":"","sources":["../../../src/modules/initiative/InitiativeLoopService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Block, BlockRepository, Clock, CreateInitiativeInput, ExecutionEventPublisher, IdGenerator, Initiative, InitiativeRepository, WorkspaceRepository } from '@cat-factory/kernel';
|
|
1
|
+
import type { Block, BlockRepository, Clock, CreateInitiativeInput, ExecutionEventPublisher, IdGenerator, Initiative, InitiativeRepository, PipelineRepository, WorkspaceRepository } from '@cat-factory/kernel';
|
|
2
2
|
export interface InitiativeServiceDependencies {
|
|
3
3
|
workspaceRepository: WorkspaceRepository;
|
|
4
4
|
blockRepository: BlockRepository;
|
|
@@ -6,6 +6,13 @@ export interface InitiativeServiceDependencies {
|
|
|
6
6
|
events: ExecutionEventPublisher;
|
|
7
7
|
clock: Clock;
|
|
8
8
|
idGenerator: IdGenerator;
|
|
9
|
+
/**
|
|
10
|
+
* Pipelines, used ONLY to validate the plan's `defaultPipelineId` at ingest (fail the
|
|
11
|
+
* planning run loudly on a plan that names a non-existent pipeline, rather than surfacing
|
|
12
|
+
* it later as a per-item spawn deviation). Optional so a deployment/test without pipelines
|
|
13
|
+
* wired still ingests. A pipeline deleted AFTER ingest is handled by the loop at spawn.
|
|
14
|
+
*/
|
|
15
|
+
pipelineRepository?: PipelineRepository;
|
|
9
16
|
}
|
|
10
17
|
/**
|
|
11
18
|
* The initiative entity's owner: creation (the board block + the entity in one
|
|
@@ -40,6 +47,13 @@ export declare class InitiativeService {
|
|
|
40
47
|
* initiative (nothing to ingest into).
|
|
41
48
|
*/
|
|
42
49
|
ingestPlan(workspaceId: string, blockId: string, rawDraft: unknown): Promise<Initiative | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Fail a plan ingest whose policy names a pipeline that doesn't exist (the default, or a
|
|
52
|
+
* rule/item override) — a plan the loop could never execute. No-op when no pipeline
|
|
53
|
+
* repository is wired (tests/conformance). The loop still guards a pipeline deleted AFTER
|
|
54
|
+
* ingest at spawn time (a deviation + notification, never a throw inside the sweep).
|
|
55
|
+
*/
|
|
56
|
+
private assertPipelinesExist;
|
|
43
57
|
/**
|
|
44
58
|
* Flip an initiative to `executing` (the committer step ran: the plan is
|
|
45
59
|
* approved and the tracker mirror committed), stamping the repo-doc
|
|
@@ -50,6 +64,22 @@ export declare class InitiativeService {
|
|
|
50
64
|
version: number;
|
|
51
65
|
hash: string;
|
|
52
66
|
} | null): Promise<Initiative | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Apply a CAS-guarded transform to the block's initiative — the execution loop's write
|
|
69
|
+
* path. A content-identical transform short-circuits to no write (replay/idempotency);
|
|
70
|
+
* a lost CAS reloads and retries a bounded number of times. Returns the persisted entity,
|
|
71
|
+
* or null when the block has no initiative.
|
|
72
|
+
*/
|
|
73
|
+
update(workspaceId: string, blockId: string, transform: (current: Initiative) => Initiative): Promise<Initiative | null>;
|
|
74
|
+
/** Pause an executing initiative (the sweep skips it; in-flight tasks finish naturally). */
|
|
75
|
+
pause(workspaceId: string, blockId: string): Promise<Initiative | null>;
|
|
76
|
+
/** Resume a paused initiative back to `executing` (the next sweep picks it up). */
|
|
77
|
+
resume(workspaceId: string, blockId: string): Promise<Initiative | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Cancel an initiative: stop spawning further work. Terminal from the loop's view. In-flight
|
|
80
|
+
* spawned tasks are left to finish on their own (cascading their teardown is slice 4).
|
|
81
|
+
*/
|
|
82
|
+
cancel(workspaceId: string, blockId: string): Promise<Initiative | null>;
|
|
53
83
|
/** Append a fresh round of pending interview questions (parks the interview `awaiting`). */
|
|
54
84
|
recordInterviewQuestions(workspaceId: string, blockId: string, questions: string[]): Promise<Initiative | null>;
|
|
55
85
|
/** Record the human's answer to one pending question (no run resume; the controller does that). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InitiativeService.d.ts","sourceRoot":"","sources":["../../../src/modules/initiative/InitiativeService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,eAAe,EACf,KAAK,EACL,qBAAqB,EACrB,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,qBAAqB,CAAA;AAe5B,MAAM,WAAW,6BAA6B;IAC5C,mBAAmB,EAAE,mBAAmB,CAAA;IACxC,eAAe,EAAE,eAAe,CAAA;IAChC,oBAAoB,EAAE,oBAAoB,CAAA;IAC1C,MAAM,EAAE,uBAAuB,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAA;IACZ,WAAW,EAAE,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"InitiativeService.d.ts","sourceRoot":"","sources":["../../../src/modules/initiative/InitiativeService.ts"],"names":[],"mappings":"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"}
|