wicked-crew 0.7.9 → 0.7.11
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/api/group-index.d.ts +59 -0
- package/dist/api/group-index.d.ts.map +1 -0
- package/dist/api/group-index.js +100 -0
- package/dist/api/group-index.js.map +1 -0
- package/dist/api/retry-index.d.ts +6 -0
- package/dist/api/retry-index.d.ts.map +1 -1
- package/dist/api/retry-index.js +13 -6
- package/dist/api/retry-index.js.map +1 -1
- package/dist/api/routes.d.ts +50 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +64 -5
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +14 -4
- package/dist/api/server.js.map +1 -1
- package/dist/api/steering-landing.d.ts +1 -1
- package/dist/api/steering-landing.d.ts.map +1 -1
- package/dist/api/steering-landing.js +59 -3
- package/dist/api/steering-landing.js.map +1 -1
- package/dist/campaigns/rollup.d.ts +39 -0
- package/dist/campaigns/rollup.d.ts.map +1 -0
- package/dist/campaigns/rollup.js +70 -0
- package/dist/campaigns/rollup.js.map +1 -0
- package/dist/campaigns/routes.d.ts +10 -3
- package/dist/campaigns/routes.d.ts.map +1 -1
- package/dist/campaigns/routes.js +27 -5
- package/dist/campaigns/routes.js.map +1 -1
- package/dist/core/adapter.js +1 -1
- package/dist/core/adapter.js.map +1 -1
- package/dist/studio/assets/{index-gtE6scX0.js → index-DuU8RZg9.js} +95 -95
- package/dist/studio/index.html +1 -1
- package/dist/studio/testid-inventory.json +47 -17
- package/endpoint-manifest.json +4 -3
- package/package.json +3 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run→ad-hoc-group index (wicked-studio#27; api-types 0.19.0).
|
|
3
|
+
*
|
|
4
|
+
* Ad-hoc sibling runs launched via `POST /runs` can be filed onto a grouping surface at launch:
|
|
5
|
+
* `campaignId` attaches the run to an EXISTING engine campaign (validated loudly — an unknown id
|
|
6
|
+
* fails the launch), `groupLabel` puts it in a label group created on first use. Either way the
|
|
7
|
+
* attach is daemon-side PROVENANCE only: the engine's run record carries no such field (the run
|
|
8
|
+
* executes byte-identically to an ungrouped launch, and the campaign scheduler never learns of
|
|
9
|
+
* it), so — exactly like retry lineage (CREW-UX-3) and the actor trail (task #88) — the durable
|
|
10
|
+
* record is the `run.launched` audit entry (`detail.campaignId` / `detail.groupLabel`), and this
|
|
11
|
+
* map is the read-side latency layer that lets `GET /runs` echo `campaign_id`/`group_label` and
|
|
12
|
+
* `GET /campaigns` serve `attached_runs` + the `groups` rows without a trail scan per request.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors `RetryIndex`'s posture: hydrated once at server start (best-effort — a missing or
|
|
15
|
+
* unreadable trail leaves prior runs ungrouped, the pre-0.19 behavior, not an error) and updated
|
|
16
|
+
* at the same post-commit point that writes the audit entry, so the map can only lag by a failed
|
|
17
|
+
* hydrate, never diverge silently.
|
|
18
|
+
*
|
|
19
|
+
* SCAN BUDGET: this index consumes the SAME `run.launched` entries `RetryIndex` already scans,
|
|
20
|
+
* so it exposes {@link GroupIndex.hydrateFromLaunchEntries} and `createServer` feeds both
|
|
21
|
+
* indexes from ONE `readAll({ action: 'run.launched' })` — boot stays at three full-file trail
|
|
22
|
+
* scans, not four (the delivery-index consolidation note, crew#321).
|
|
23
|
+
*/
|
|
24
|
+
import type { AuditLog } from './audit.js';
|
|
25
|
+
import type { AuditEntry } from '../core/types.js';
|
|
26
|
+
/** One launch's group attach: exactly one of the two forms (the launch route enforces it). */
|
|
27
|
+
export type GroupAttach = {
|
|
28
|
+
campaignId: string;
|
|
29
|
+
} | {
|
|
30
|
+
label: string;
|
|
31
|
+
};
|
|
32
|
+
export declare class GroupIndex {
|
|
33
|
+
private readonly runToAttach;
|
|
34
|
+
/** campaignId → attached run ids, launch order. */
|
|
35
|
+
private readonly campaignRuns;
|
|
36
|
+
/** label → member run ids, launch order. */
|
|
37
|
+
private readonly labelRuns;
|
|
38
|
+
/**
|
|
39
|
+
* Consume pre-read `run.launched` entries (NEWEST FIRST, `AuditLog.readAll`'s order —
|
|
40
|
+
* exhaustive, per BRIEF-UX-002 C5). The first entry seen per run wins, like the sibling
|
|
41
|
+
* indexes; member lists come out in LAUNCH order because insertion happens on a reversed
|
|
42
|
+
* walk. Idempotent per boot (called once).
|
|
43
|
+
*/
|
|
44
|
+
hydrateFromLaunchEntries(entries: AuditEntry[]): void;
|
|
45
|
+
/** Standalone hydrate (tests / direct use) — production shares the scan via `createServer`. */
|
|
46
|
+
hydrate(audit: AuditLog, log?: (msg: string) => void): Promise<void>;
|
|
47
|
+
/** Record one launch's attach (post-commit, beside the audit write). */
|
|
48
|
+
set(runId: string, attach: GroupAttach): void;
|
|
49
|
+
/** This run's attach, or `undefined` (the DTO spells that as ABSENT fields). */
|
|
50
|
+
attachOf(runId: string): GroupAttach | undefined;
|
|
51
|
+
/** Run ids attached to this campaign, launch order (`[]` when none). */
|
|
52
|
+
attachedTo(campaignId: string): string[];
|
|
53
|
+
/** Every label group: `{ label, runIds }` in first-use order, members in launch order. */
|
|
54
|
+
labelGroups(): Array<{
|
|
55
|
+
label: string;
|
|
56
|
+
runIds: string[];
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=group-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group-index.d.ts","sourceRoot":"","sources":["../../src/api/group-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IAEzD;;;;;OAKG;IACH,wBAAwB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;IAoBrD,+FAA+F;IACzF,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1E,wEAAwE;IACxE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAc7C,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIhD,wEAAwE;IACxE,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC,0FAA0F;IAC1F,WAAW,IAAI,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAM1D"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run→ad-hoc-group index (wicked-studio#27; api-types 0.19.0).
|
|
3
|
+
*
|
|
4
|
+
* Ad-hoc sibling runs launched via `POST /runs` can be filed onto a grouping surface at launch:
|
|
5
|
+
* `campaignId` attaches the run to an EXISTING engine campaign (validated loudly — an unknown id
|
|
6
|
+
* fails the launch), `groupLabel` puts it in a label group created on first use. Either way the
|
|
7
|
+
* attach is daemon-side PROVENANCE only: the engine's run record carries no such field (the run
|
|
8
|
+
* executes byte-identically to an ungrouped launch, and the campaign scheduler never learns of
|
|
9
|
+
* it), so — exactly like retry lineage (CREW-UX-3) and the actor trail (task #88) — the durable
|
|
10
|
+
* record is the `run.launched` audit entry (`detail.campaignId` / `detail.groupLabel`), and this
|
|
11
|
+
* map is the read-side latency layer that lets `GET /runs` echo `campaign_id`/`group_label` and
|
|
12
|
+
* `GET /campaigns` serve `attached_runs` + the `groups` rows without a trail scan per request.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors `RetryIndex`'s posture: hydrated once at server start (best-effort — a missing or
|
|
15
|
+
* unreadable trail leaves prior runs ungrouped, the pre-0.19 behavior, not an error) and updated
|
|
16
|
+
* at the same post-commit point that writes the audit entry, so the map can only lag by a failed
|
|
17
|
+
* hydrate, never diverge silently.
|
|
18
|
+
*
|
|
19
|
+
* SCAN BUDGET: this index consumes the SAME `run.launched` entries `RetryIndex` already scans,
|
|
20
|
+
* so it exposes {@link GroupIndex.hydrateFromLaunchEntries} and `createServer` feeds both
|
|
21
|
+
* indexes from ONE `readAll({ action: 'run.launched' })` — boot stays at three full-file trail
|
|
22
|
+
* scans, not four (the delivery-index consolidation note, crew#321).
|
|
23
|
+
*/
|
|
24
|
+
export class GroupIndex {
|
|
25
|
+
runToAttach = new Map();
|
|
26
|
+
/** campaignId → attached run ids, launch order. */
|
|
27
|
+
campaignRuns = new Map();
|
|
28
|
+
/** label → member run ids, launch order. */
|
|
29
|
+
labelRuns = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Consume pre-read `run.launched` entries (NEWEST FIRST, `AuditLog.readAll`'s order —
|
|
32
|
+
* exhaustive, per BRIEF-UX-002 C5). The first entry seen per run wins, like the sibling
|
|
33
|
+
* indexes; member lists come out in LAUNCH order because insertion happens on a reversed
|
|
34
|
+
* walk. Idempotent per boot (called once).
|
|
35
|
+
*/
|
|
36
|
+
hydrateFromLaunchEntries(entries) {
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
const oldestFirst = [];
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (typeof entry.runId !== 'string')
|
|
41
|
+
continue;
|
|
42
|
+
if (seen.has(entry.runId))
|
|
43
|
+
continue;
|
|
44
|
+
seen.add(entry.runId);
|
|
45
|
+
const campaignId = entry.detail?.['campaignId'];
|
|
46
|
+
const label = entry.detail?.['groupLabel'];
|
|
47
|
+
if (typeof campaignId === 'string' && campaignId !== '') {
|
|
48
|
+
oldestFirst.push({ runId: entry.runId, attach: { campaignId } });
|
|
49
|
+
}
|
|
50
|
+
else if (typeof label === 'string' && label !== '') {
|
|
51
|
+
oldestFirst.push({ runId: entry.runId, attach: { label } });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// The trail reads newest-first; one reverse restores launch order (unshift-in-loop is O(n²)).
|
|
55
|
+
oldestFirst.reverse();
|
|
56
|
+
for (const { runId, attach } of oldestFirst)
|
|
57
|
+
this.set(runId, attach);
|
|
58
|
+
}
|
|
59
|
+
/** Standalone hydrate (tests / direct use) — production shares the scan via `createServer`. */
|
|
60
|
+
async hydrate(audit, log) {
|
|
61
|
+
try {
|
|
62
|
+
this.hydrateFromLaunchEntries(await audit.readAll({ action: 'run.launched' }));
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
log?.(`[runs] group-index hydrate failed (prior runs read as ungrouped until restart): ${err instanceof Error ? err.message : String(err)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Record one launch's attach (post-commit, beside the audit write). */
|
|
69
|
+
set(runId, attach) {
|
|
70
|
+
if (this.runToAttach.has(runId))
|
|
71
|
+
return; // launch-time fact — first write wins
|
|
72
|
+
this.runToAttach.set(runId, attach);
|
|
73
|
+
if ('campaignId' in attach) {
|
|
74
|
+
const list = this.campaignRuns.get(attach.campaignId) ?? [];
|
|
75
|
+
list.push(runId);
|
|
76
|
+
this.campaignRuns.set(attach.campaignId, list);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const list = this.labelRuns.get(attach.label) ?? [];
|
|
80
|
+
list.push(runId);
|
|
81
|
+
this.labelRuns.set(attach.label, list);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** This run's attach, or `undefined` (the DTO spells that as ABSENT fields). */
|
|
85
|
+
attachOf(runId) {
|
|
86
|
+
return this.runToAttach.get(runId);
|
|
87
|
+
}
|
|
88
|
+
/** Run ids attached to this campaign, launch order (`[]` when none). */
|
|
89
|
+
attachedTo(campaignId) {
|
|
90
|
+
return [...(this.campaignRuns.get(campaignId) ?? [])];
|
|
91
|
+
}
|
|
92
|
+
/** Every label group: `{ label, runIds }` in first-use order, members in launch order. */
|
|
93
|
+
labelGroups() {
|
|
94
|
+
return [...this.labelRuns.entries()].map(([label, runIds]) => ({
|
|
95
|
+
label,
|
|
96
|
+
runIds: [...runIds],
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=group-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group-index.js","sourceRoot":"","sources":["../../src/api/group-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,8 +13,14 @@
|
|
|
13
13
|
* the audit entry, so the map can only lag by a failed hydrate, never diverge silently.
|
|
14
14
|
*/
|
|
15
15
|
import type { AuditLog } from './audit.js';
|
|
16
|
+
import type { AuditEntry } from '../core/types.js';
|
|
16
17
|
export declare class RetryIndex {
|
|
17
18
|
private readonly runToRetryOf;
|
|
19
|
+
/**
|
|
20
|
+
* Consume pre-read `run.launched` entries — the seam that lets `createServer` feed this index
|
|
21
|
+
* and `GroupIndex` from ONE trail scan (the delivery-index consolidation note, crew#321).
|
|
22
|
+
*/
|
|
23
|
+
hydrateFromLaunchEntries(entries: AuditEntry[]): void;
|
|
18
24
|
/**
|
|
19
25
|
* Load lineage from EVERY `run.launched` entry in the trail — exhaustively, not capped
|
|
20
26
|
* (BRIEF-UX-002 C5: a lineage fact 2,678 launches deep must survive a restart; the newest-1000
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-index.d.ts","sourceRoot":"","sources":["../../src/api/retry-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"retry-index.d.ts","sourceRoot":"","sources":["../../src/api/retry-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAE1D;;;OAGG;IACH,wBAAwB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;IASrD;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1E,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIzC,4FAA4F;IAC5F,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAG9C"}
|
package/dist/api/retry-index.js
CHANGED
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export class RetryIndex {
|
|
16
16
|
runToRetryOf = new Map();
|
|
17
|
+
/**
|
|
18
|
+
* Consume pre-read `run.launched` entries — the seam that lets `createServer` feed this index
|
|
19
|
+
* and `GroupIndex` from ONE trail scan (the delivery-index consolidation note, crew#321).
|
|
20
|
+
*/
|
|
21
|
+
hydrateFromLaunchEntries(entries) {
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const retryOf = entry.detail?.['retryOf'];
|
|
24
|
+
if (typeof entry.runId === 'string' && typeof retryOf === 'string') {
|
|
25
|
+
this.runToRetryOf.set(entry.runId, retryOf);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
17
29
|
/**
|
|
18
30
|
* Load lineage from EVERY `run.launched` entry in the trail — exhaustively, not capped
|
|
19
31
|
* (BRIEF-UX-002 C5: a lineage fact 2,678 launches deep must survive a restart; the newest-1000
|
|
@@ -23,12 +35,7 @@ export class RetryIndex {
|
|
|
23
35
|
*/
|
|
24
36
|
async hydrate(audit, log) {
|
|
25
37
|
try {
|
|
26
|
-
|
|
27
|
-
const retryOf = entry.detail?.['retryOf'];
|
|
28
|
-
if (typeof entry.runId === 'string' && typeof retryOf === 'string') {
|
|
29
|
-
this.runToRetryOf.set(entry.runId, retryOf);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
38
|
+
this.hydrateFromLaunchEntries(await audit.readAll({ action: 'run.launched' }));
|
|
32
39
|
}
|
|
33
40
|
catch (err) {
|
|
34
41
|
log?.(`[runs] retry-index hydrate failed (prior runs read as not-a-retry until restart): ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-index.js","sourceRoot":"","sources":["../../src/api/retry-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"retry-index.js","sourceRoot":"","sources":["../../src/api/retry-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,MAAM,OAAO,UAAU;IACJ,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1D;;;OAGG;IACH,wBAAwB,CAAC,OAAqB;QAC5C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC;YAC1C,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACnE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,KAAe,EAAE,GAA2B;QACxD,IAAI,CAAC;YACH,IAAI,CAAC,wBAAwB,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,EAAE,CACH,qFACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,GAAG,CAAC,KAAa,EAAE,OAAe;QAChC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,4FAA4F;IAC5F,UAAU,CAAC,KAAa;QACtB,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;CACF"}
|
package/dist/api/routes.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { DocLedgerSweep } from '../interactive/doc-ledger-sweep.js';
|
|
|
11
11
|
import { AuditLog } from './audit.js';
|
|
12
12
|
import { type ErrorRing } from './diagnostics.js';
|
|
13
13
|
import { RetryIndex } from './retry-index.js';
|
|
14
|
+
import { GroupIndex } from './group-index.js';
|
|
14
15
|
import { GuidanceIndex } from './guidance-index.js';
|
|
15
16
|
import { DeliveryIndex } from './delivery-index.js';
|
|
16
17
|
import { type DeliverExec } from './post-hoc-deliver.js';
|
|
@@ -29,7 +30,7 @@ export { API_PREFIX } from './api-prefix.js';
|
|
|
29
30
|
* schema does not accept — so `.strict()` is what turns that trip into a 400 instead of an
|
|
30
31
|
* unworkflowed run reported as `201`.
|
|
31
32
|
*/
|
|
32
|
-
export declare const LaunchSchema: z.ZodEffects<z.ZodObject<{
|
|
33
|
+
export declare const LaunchSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
33
34
|
problem: z.ZodString;
|
|
34
35
|
sessionId: z.ZodOptional<z.ZodString>;
|
|
35
36
|
clisJson: z.ZodOptional<z.ZodString>;
|
|
@@ -52,10 +53,22 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodObject<{
|
|
|
52
53
|
* (the route checks the store and 400s with a named error otherwise); persisted via the
|
|
53
54
|
* `run.launched` audit entry + retry index and echoed as `AgentSession.retry_of`. */
|
|
54
55
|
retryOf: z.ZodOptional<z.ZodString>;
|
|
56
|
+
/** wicked-studio#27 (api-types 0.19.0) — attach this AD-HOC run to an EXISTING campaign's
|
|
57
|
+
* surface. Validated against the campaign store before anything launches (unknown ⇒ 404,
|
|
58
|
+
* engine without campaign bindings ⇒ 501 — never a silently dropped attach); persisted via
|
|
59
|
+
* the `run.launched` audit entry + group index, echoed as `AgentSession.campaign_id`, and
|
|
60
|
+
* served under `Campaign.attached_runs`. Provenance only: the run is NOT a DAG node. */
|
|
61
|
+
campaignId: z.ZodOptional<z.ZodString>;
|
|
62
|
+
/** wicked-studio#27 (api-types 0.19.0) — ad-hoc label group, created on first use: runs
|
|
63
|
+
* sharing a label form one `RunGroup` on `GET /campaigns`. Persisted/echoed like
|
|
64
|
+
* `campaignId` (as `AgentSession.group_label`). */
|
|
65
|
+
groupLabel: z.ZodOptional<z.ZodString>;
|
|
55
66
|
}, "strict", z.ZodTypeAny, {
|
|
56
67
|
problem: string;
|
|
57
68
|
deliver?: "pr" | "none" | undefined;
|
|
58
69
|
projectId?: string | undefined;
|
|
70
|
+
campaignId?: string | undefined;
|
|
71
|
+
groupLabel?: string | undefined;
|
|
59
72
|
workflow?: string | undefined;
|
|
60
73
|
repoRef?: string | undefined;
|
|
61
74
|
clisJson?: string | undefined;
|
|
@@ -67,6 +80,8 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodObject<{
|
|
|
67
80
|
problem: string;
|
|
68
81
|
deliver?: "pr" | "none" | undefined;
|
|
69
82
|
projectId?: string | undefined;
|
|
83
|
+
campaignId?: string | undefined;
|
|
84
|
+
groupLabel?: string | undefined;
|
|
70
85
|
workflow?: string | undefined;
|
|
71
86
|
repoRef?: string | undefined;
|
|
72
87
|
clisJson?: string | undefined;
|
|
@@ -78,6 +93,8 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodObject<{
|
|
|
78
93
|
problem: string;
|
|
79
94
|
deliver?: "pr" | "none" | undefined;
|
|
80
95
|
projectId?: string | undefined;
|
|
96
|
+
campaignId?: string | undefined;
|
|
97
|
+
groupLabel?: string | undefined;
|
|
81
98
|
workflow?: string | undefined;
|
|
82
99
|
repoRef?: string | undefined;
|
|
83
100
|
clisJson?: string | undefined;
|
|
@@ -89,6 +106,34 @@ export declare const LaunchSchema: z.ZodEffects<z.ZodObject<{
|
|
|
89
106
|
problem: string;
|
|
90
107
|
deliver?: "pr" | "none" | undefined;
|
|
91
108
|
projectId?: string | undefined;
|
|
109
|
+
campaignId?: string | undefined;
|
|
110
|
+
groupLabel?: string | undefined;
|
|
111
|
+
workflow?: string | undefined;
|
|
112
|
+
repoRef?: string | undefined;
|
|
113
|
+
clisJson?: string | undefined;
|
|
114
|
+
sessionId?: string | undefined;
|
|
115
|
+
retryOf?: string | undefined;
|
|
116
|
+
entityMode?: "shared" | "isolated" | undefined;
|
|
117
|
+
humanConfirm?: string | undefined;
|
|
118
|
+
}>, {
|
|
119
|
+
problem: string;
|
|
120
|
+
deliver?: "pr" | "none" | undefined;
|
|
121
|
+
projectId?: string | undefined;
|
|
122
|
+
campaignId?: string | undefined;
|
|
123
|
+
groupLabel?: string | undefined;
|
|
124
|
+
workflow?: string | undefined;
|
|
125
|
+
repoRef?: string | undefined;
|
|
126
|
+
clisJson?: string | undefined;
|
|
127
|
+
sessionId?: string | undefined;
|
|
128
|
+
retryOf?: string | undefined;
|
|
129
|
+
entityMode?: "shared" | "isolated" | undefined;
|
|
130
|
+
humanConfirm?: string | undefined;
|
|
131
|
+
}, {
|
|
132
|
+
problem: string;
|
|
133
|
+
deliver?: "pr" | "none" | undefined;
|
|
134
|
+
projectId?: string | undefined;
|
|
135
|
+
campaignId?: string | undefined;
|
|
136
|
+
groupLabel?: string | undefined;
|
|
92
137
|
workflow?: string | undefined;
|
|
93
138
|
repoRef?: string | undefined;
|
|
94
139
|
clisJson?: string | undefined;
|
|
@@ -162,6 +207,10 @@ export interface RuntimeDeps {
|
|
|
162
207
|
/** Run→retry-lineage index (CREW-UX-3) — `createServer` hydrates one from the audit trail so
|
|
163
208
|
* a restarted daemon still echoes `retry_of`; a directly-driven route set gets a fresh one. */
|
|
164
209
|
retryIndex?: RetryIndex;
|
|
210
|
+
/** Run→ad-hoc-group index (wicked-studio#27) — `createServer` hydrates one from the audit
|
|
211
|
+
* trail (the SAME `run.launched` scan as `retryIndex`) so attaches survive a restart; a
|
|
212
|
+
* directly-driven route set gets a fresh one. */
|
|
213
|
+
groupIndex?: GroupIndex;
|
|
165
214
|
/** Run→operator-guidance index (CREW-UX-7) — `createServer` hydrates one from the audit trail
|
|
166
215
|
* so a restarted daemon still echoes `guidance`; a directly-driven route set gets a fresh one. */
|
|
167
216
|
guidanceIndex?: GuidanceIndex;
|
package/dist/api/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,OAAO,
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"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"}
|
package/dist/api/routes.js
CHANGED
|
@@ -5,7 +5,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
5
5
|
import { promises as fsp } from 'node:fs';
|
|
6
6
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { ChatUnsupportedError, CoreAdapter, ElicitationUnsupportedError, SteeringUnsupportedError, humanGatePhaseIds } from '../core/adapter.js';
|
|
8
|
+
import { CampaignsUnsupportedError, ChatUnsupportedError, CoreAdapter, ElicitationUnsupportedError, SteeringUnsupportedError, humanGatePhaseIds } from '../core/adapter.js';
|
|
9
9
|
import { codeGraphDb, requirementsGraph } from '../core/repoPaths.js';
|
|
10
10
|
import { QeGateCache } from '../qe/gate-events.js';
|
|
11
11
|
import { buildAcceptanceView, resolveRunWorkflow } from '../qe/acceptance.js';
|
|
@@ -32,6 +32,7 @@ import { MEMBERSHIP_ATTACHED, membershipAttachedKey } from '../projects/events.j
|
|
|
32
32
|
import { AuditLog } from './audit.js';
|
|
33
33
|
import { AcpFoldCache, EngineVersionCache, eventsDirOf, installedPackageVersion, listStoreFiles, readStudioBundleVersion, } from './diagnostics.js';
|
|
34
34
|
import { RetryIndex } from './retry-index.js';
|
|
35
|
+
import { GroupIndex } from './group-index.js';
|
|
35
36
|
import { GuidanceIndex } from './guidance-index.js';
|
|
36
37
|
import { DeliveryIndex, deliveryStateWithVacuity, gitRunBranchIsEmpty, gitWorktreeIsClean, prUrlFrom, } from './delivery-index.js';
|
|
37
38
|
import { runDeliverScript } from './post-hoc-deliver.js';
|
|
@@ -151,9 +152,22 @@ export const LaunchSchema = z.object({
|
|
|
151
152
|
* (the route checks the store and 400s with a named error otherwise); persisted via the
|
|
152
153
|
* `run.launched` audit entry + retry index and echoed as `AgentSession.retry_of`. */
|
|
153
154
|
retryOf: z.string().min(1).optional(),
|
|
155
|
+
/** wicked-studio#27 (api-types 0.19.0) — attach this AD-HOC run to an EXISTING campaign's
|
|
156
|
+
* surface. Validated against the campaign store before anything launches (unknown ⇒ 404,
|
|
157
|
+
* engine without campaign bindings ⇒ 501 — never a silently dropped attach); persisted via
|
|
158
|
+
* the `run.launched` audit entry + group index, echoed as `AgentSession.campaign_id`, and
|
|
159
|
+
* served under `Campaign.attached_runs`. Provenance only: the run is NOT a DAG node. */
|
|
160
|
+
campaignId: z.string().min(1).optional(),
|
|
161
|
+
/** wicked-studio#27 (api-types 0.19.0) — ad-hoc label group, created on first use: runs
|
|
162
|
+
* sharing a label form one `RunGroup` on `GET /campaigns`. Persisted/echoed like
|
|
163
|
+
* `campaignId` (as `AgentSession.group_label`). */
|
|
164
|
+
groupLabel: z.string().min(1).max(200).optional(),
|
|
154
165
|
}).strict().refine((b) => b.deliver !== 'pr' || b.workflow !== undefined, {
|
|
155
166
|
message: 'deliver: "pr" requires a workflow — a free-text run has no def to append the deliver phase to',
|
|
156
167
|
path: ['deliver'],
|
|
168
|
+
}).refine((b) => b.campaignId === undefined || b.groupLabel === undefined, {
|
|
169
|
+
message: 'campaignId and groupLabel are mutually exclusive — a run files onto ONE grouping surface (an existing campaign, or a label group)',
|
|
170
|
+
path: ['groupLabel'],
|
|
157
171
|
});
|
|
158
172
|
export const GateSchema = z.object({
|
|
159
173
|
approve: z.boolean(),
|
|
@@ -235,6 +249,7 @@ runtime = {}) {
|
|
|
235
249
|
const openWithOs = runtime.openWithOs ?? openWithSystemDefault;
|
|
236
250
|
const signedIn = runtime.signedIn ?? signedInHeuristic;
|
|
237
251
|
const retryIndex = runtime.retryIndex ?? new RetryIndex();
|
|
252
|
+
const groupIndex = runtime.groupIndex ?? new GroupIndex();
|
|
238
253
|
const guidanceIndex = runtime.guidanceIndex ?? new GuidanceIndex();
|
|
239
254
|
const deliveryIndex = runtime.deliveryIndex ?? new DeliveryIndex();
|
|
240
255
|
const worktreeExists = runtime.worktreeExists ?? ((p) => existsSync(p));
|
|
@@ -265,6 +280,15 @@ runtime = {}) {
|
|
|
265
280
|
const retryOf = retryIndex.retryOfFor(view.session.id);
|
|
266
281
|
if (retryOf !== undefined)
|
|
267
282
|
view.session.retry_of = retryOf;
|
|
283
|
+
// wicked-studio#27 (api-types 0.19.0): the launch-time group attach, from the same durable
|
|
284
|
+
// trail record as lineage. ABSENT when ungrouped — never null.
|
|
285
|
+
const attach = groupIndex.attachOf(view.session.id);
|
|
286
|
+
if (attach !== undefined) {
|
|
287
|
+
if ('campaignId' in attach)
|
|
288
|
+
view.session.campaign_id = attach.campaignId;
|
|
289
|
+
else
|
|
290
|
+
view.session.group_label = attach.label;
|
|
291
|
+
}
|
|
268
292
|
const guidance = guidanceIndex.guidanceFor(view.session.id);
|
|
269
293
|
if (guidance !== undefined)
|
|
270
294
|
view.session.guidance = guidance;
|
|
@@ -628,9 +652,11 @@ runtime = {}) {
|
|
|
628
652
|
manifest: {
|
|
629
653
|
requestType: 'LaunchRunBody',
|
|
630
654
|
responseType: '{ runId: string }',
|
|
631
|
-
// 404/409: unknown project /
|
|
632
|
-
// catch below); 400: zod
|
|
633
|
-
|
|
655
|
+
// 404/409: unknown project / unknown campaignId (wicked-studio#27) /
|
|
656
|
+
// archived-or-synthesized project + busy engine (see the catch below); 400: zod
|
|
657
|
+
// reject or a retryOf naming no existing run; 501: campaignId attach on an engine
|
|
658
|
+
// addon without the campaign bindings ("upgrade the engine").
|
|
659
|
+
statusCodes: [201, 400, 404, 409, 501],
|
|
634
660
|
},
|
|
635
661
|
},
|
|
636
662
|
}, async (req, reply) => {
|
|
@@ -716,6 +742,26 @@ runtime = {}) {
|
|
|
716
742
|
});
|
|
717
743
|
}
|
|
718
744
|
}
|
|
745
|
+
// Ad-hoc campaign attach (wicked-studio#27): `campaignId` must name an EXISTING campaign,
|
|
746
|
+
// checked BEFORE anything launches — an unknown id is a 404 with nothing committed, never a
|
|
747
|
+
// run silently filed onto a surface that does not exist. An engine addon without the
|
|
748
|
+
// campaign bindings is a 501 ("upgrade the engine"), the campaigns-surface doctrine. A
|
|
749
|
+
// `groupLabel` needs no check: a label group is created by its first use.
|
|
750
|
+
if (b.campaignId !== undefined) {
|
|
751
|
+
try {
|
|
752
|
+
if ((await adapter.campaignDetail(b.campaignId)) === null) {
|
|
753
|
+
return reply.code(404).send({
|
|
754
|
+
error: `campaignId names an unknown campaign: ${b.campaignId} — an ad-hoc run can only attach to an existing campaign (launch one via POST /campaigns, or use groupLabel for a create-on-first-use group)`,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
catch (err) {
|
|
759
|
+
if (err instanceof CampaignsUnsupportedError) {
|
|
760
|
+
return reply.code(501).send({ error: message(err) });
|
|
761
|
+
}
|
|
762
|
+
throw err;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
719
765
|
try {
|
|
720
766
|
const runId = await adapter.launchRun(input);
|
|
721
767
|
// Who launched it — the engine's LaunchOptions carries no actor field
|
|
@@ -734,10 +780,18 @@ runtime = {}) {
|
|
|
734
780
|
// CREW-UX-3: the trail is the durable record of lineage — the retry index (and a
|
|
735
781
|
// restarted daemon's hydrate) reads it back from exactly this entry.
|
|
736
782
|
...(b.retryOf !== undefined ? { retryOf: b.retryOf } : {}),
|
|
783
|
+
// wicked-studio#27: the trail is likewise the durable record of the group attach —
|
|
784
|
+
// the group index (and a restarted daemon's hydrate) reads it back from here.
|
|
785
|
+
...(b.campaignId !== undefined ? { campaignId: b.campaignId } : {}),
|
|
786
|
+
...(b.groupLabel !== undefined ? { groupLabel: b.groupLabel } : {}),
|
|
737
787
|
},
|
|
738
788
|
});
|
|
739
789
|
if (b.retryOf !== undefined)
|
|
740
790
|
retryIndex.set(runId, b.retryOf);
|
|
791
|
+
if (b.campaignId !== undefined)
|
|
792
|
+
groupIndex.set(runId, { campaignId: b.campaignId });
|
|
793
|
+
else if (b.groupLabel !== undefined)
|
|
794
|
+
groupIndex.set(runId, { label: b.groupLabel });
|
|
741
795
|
if (b.projectId !== undefined) {
|
|
742
796
|
// The engine attached the crew.run membership ATOMICALLY with the launch record
|
|
743
797
|
// (DES-PROJECT-001 §2.2) — this is the post-commit half: tag future /ws frames and
|
|
@@ -2380,11 +2434,16 @@ runtime = {}) {
|
|
|
2380
2434
|
registerProjectRoutes(app, adapter, { ...projects, settings: projectSettings }, security);
|
|
2381
2435
|
// ── Campaigns (crew#342 + TH-9) — the engine's durable Run-DAG scheduler over REST ──────────
|
|
2382
2436
|
// Progress streams as campaign* CoreEvents on the existing allowlist-free /ws relay; these
|
|
2383
|
-
// routes are launch/resume/cancel + the reads a scoreboard builds from.
|
|
2437
|
+
// routes are launch/resume/cancel + the reads a scoreboard builds from. The GET routes join
|
|
2438
|
+
// per-member delivery (wicked-studio#27) with the SAME machinery the run DTOs use — one
|
|
2439
|
+
// sessionsDetail() per request, the shared TTL-memoized vacuity probes, never a per-node fetch.
|
|
2384
2440
|
registerCampaignRoutes(app, adapter, {
|
|
2385
2441
|
audit,
|
|
2386
2442
|
actorOf,
|
|
2387
2443
|
roster: () => CoreAdapter.roster(),
|
|
2444
|
+
groupIndex,
|
|
2445
|
+
deliveryUrlFor: (runId) => deliveryIndex.urlFor(runId),
|
|
2446
|
+
vacuity: vacuityProbes,
|
|
2388
2447
|
});
|
|
2389
2448
|
// ── Governance wiki management (wiki-mgmt) — scoreboard + honest empty-state meta ──────────
|
|
2390
2449
|
// Browse (`GET /governance/rules` + facets) and retire (`DELETE /governance/rules/:id`) stay
|