@exaudeus/workrail 3.73.1 → 3.74.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/cli-worktrain.js +126 -1
- package/dist/console-ui/assets/{index-txIYXGHx.js → index-CfU3va8H.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/coordinators/pr-review.d.ts +11 -1
- package/dist/coordinators/types.d.ts +15 -0
- package/dist/coordinators/types.js +2 -0
- package/dist/manifest.json +81 -57
- package/dist/mcp/handlers/v2-advance-core/index.d.ts +1 -0
- package/dist/mcp/handlers/v2-advance-core/index.js +3 -3
- package/dist/mcp/handlers/v2-advance-core/outcome-success.js +4 -18
- package/dist/mcp/handlers/v2-advance-events.d.ts +1 -1
- package/dist/mcp/handlers/v2-advance-events.js +1 -1
- package/dist/mcp/handlers/v2-execution/advance.d.ts +1 -0
- package/dist/mcp/handlers/v2-execution/advance.js +3 -3
- package/dist/mcp/handlers/v2-execution/continue-advance.d.ts +1 -0
- package/dist/mcp/handlers/v2-execution/continue-advance.js +2 -1
- package/dist/mcp/handlers/v2-execution/index.js +3 -1
- package/dist/mcp/server.js +6 -4
- package/dist/mcp/types.d.ts +2 -0
- package/dist/trigger/coordinator-deps.js +203 -36
- package/dist/trigger/delivery-action.d.ts +1 -0
- package/dist/trigger/delivery-action.js +1 -1
- package/dist/trigger/delivery-pipeline.d.ts +13 -2
- package/dist/trigger/delivery-pipeline.js +58 -3
- package/dist/trigger/trigger-router.js +6 -3
- package/dist/v2/durable-core/constants.d.ts +1 -0
- package/dist/v2/durable-core/constants.js +1 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +202 -0
- package/dist/v2/durable-core/schemas/session/events.d.ts +56 -0
- package/dist/v2/durable-core/schemas/session/events.js +8 -0
- package/dist/v2/infra/local/git-snapshot/index.d.ts +6 -0
- package/dist/v2/infra/local/git-snapshot/index.js +39 -0
- package/dist/v2/ports/git-snapshot.port.d.ts +10 -0
- package/dist/v2/ports/git-snapshot.port.js +9 -0
- package/dist/v2/projections/session-metrics.js +17 -2
- package/docs/authoring.md +23 -0
- package/docs/design/engine-boundary-discovery.md +123 -0
- package/docs/design/engine-boundary-review-findings.md +72 -0
- package/docs/ideas/backlog.md +129 -48
- package/package.json +1 -1
- package/spec/authoring-spec.json +36 -1
|
@@ -2567,6 +2567,62 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
|
|
|
2567
2567
|
durationMs?: number | undefined;
|
|
2568
2568
|
};
|
|
2569
2569
|
timestampMs: number;
|
|
2570
|
+
}>, z.ZodObject<{
|
|
2571
|
+
v: z.ZodLiteral<1>;
|
|
2572
|
+
eventId: z.ZodString;
|
|
2573
|
+
eventIndex: z.ZodNumber;
|
|
2574
|
+
sessionId: z.ZodString;
|
|
2575
|
+
dedupeKey: z.ZodString;
|
|
2576
|
+
timestampMs: z.ZodNumber;
|
|
2577
|
+
} & {
|
|
2578
|
+
kind: z.ZodLiteral<"delivery_recorded">;
|
|
2579
|
+
scope: z.ZodObject<{
|
|
2580
|
+
runId: z.ZodString;
|
|
2581
|
+
}, "strip", z.ZodTypeAny, {
|
|
2582
|
+
runId: string;
|
|
2583
|
+
}, {
|
|
2584
|
+
runId: string;
|
|
2585
|
+
}>;
|
|
2586
|
+
data: z.ZodObject<{
|
|
2587
|
+
shas: z.ZodArray<z.ZodString, "many">;
|
|
2588
|
+
prUrl: z.ZodOptional<z.ZodString>;
|
|
2589
|
+
}, "strip", z.ZodTypeAny, {
|
|
2590
|
+
shas: string[];
|
|
2591
|
+
prUrl?: string | undefined;
|
|
2592
|
+
}, {
|
|
2593
|
+
shas: string[];
|
|
2594
|
+
prUrl?: string | undefined;
|
|
2595
|
+
}>;
|
|
2596
|
+
}, "strip", z.ZodTypeAny, {
|
|
2597
|
+
kind: "delivery_recorded";
|
|
2598
|
+
v: 1;
|
|
2599
|
+
sessionId: string;
|
|
2600
|
+
eventIndex: number;
|
|
2601
|
+
eventId: string;
|
|
2602
|
+
dedupeKey: string;
|
|
2603
|
+
scope: {
|
|
2604
|
+
runId: string;
|
|
2605
|
+
};
|
|
2606
|
+
data: {
|
|
2607
|
+
shas: string[];
|
|
2608
|
+
prUrl?: string | undefined;
|
|
2609
|
+
};
|
|
2610
|
+
timestampMs: number;
|
|
2611
|
+
}, {
|
|
2612
|
+
kind: "delivery_recorded";
|
|
2613
|
+
v: 1;
|
|
2614
|
+
sessionId: string;
|
|
2615
|
+
eventIndex: number;
|
|
2616
|
+
eventId: string;
|
|
2617
|
+
dedupeKey: string;
|
|
2618
|
+
scope: {
|
|
2619
|
+
runId: string;
|
|
2620
|
+
};
|
|
2621
|
+
data: {
|
|
2622
|
+
shas: string[];
|
|
2623
|
+
prUrl?: string | undefined;
|
|
2624
|
+
};
|
|
2625
|
+
timestampMs: number;
|
|
2570
2626
|
}>]>, "many">;
|
|
2571
2627
|
manifest: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
2572
2628
|
v: z.ZodLiteral<1>;
|
|
@@ -5311,6 +5367,21 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
|
|
|
5311
5367
|
durationMs?: number | undefined;
|
|
5312
5368
|
};
|
|
5313
5369
|
timestampMs: number;
|
|
5370
|
+
} | {
|
|
5371
|
+
kind: "delivery_recorded";
|
|
5372
|
+
v: 1;
|
|
5373
|
+
sessionId: string;
|
|
5374
|
+
eventIndex: number;
|
|
5375
|
+
eventId: string;
|
|
5376
|
+
dedupeKey: string;
|
|
5377
|
+
scope: {
|
|
5378
|
+
runId: string;
|
|
5379
|
+
};
|
|
5380
|
+
data: {
|
|
5381
|
+
shas: string[];
|
|
5382
|
+
prUrl?: string | undefined;
|
|
5383
|
+
};
|
|
5384
|
+
timestampMs: number;
|
|
5314
5385
|
})[];
|
|
5315
5386
|
manifest: ({
|
|
5316
5387
|
kind: "segment_closed";
|
|
@@ -5925,6 +5996,21 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
|
|
|
5925
5996
|
durationMs?: number | undefined;
|
|
5926
5997
|
};
|
|
5927
5998
|
timestampMs: number;
|
|
5999
|
+
} | {
|
|
6000
|
+
kind: "delivery_recorded";
|
|
6001
|
+
v: 1;
|
|
6002
|
+
sessionId: string;
|
|
6003
|
+
eventIndex: number;
|
|
6004
|
+
eventId: string;
|
|
6005
|
+
dedupeKey: string;
|
|
6006
|
+
scope: {
|
|
6007
|
+
runId: string;
|
|
6008
|
+
};
|
|
6009
|
+
data: {
|
|
6010
|
+
shas: string[];
|
|
6011
|
+
prUrl?: string | undefined;
|
|
6012
|
+
};
|
|
6013
|
+
timestampMs: number;
|
|
5928
6014
|
})[];
|
|
5929
6015
|
manifest: ({
|
|
5930
6016
|
kind: "segment_closed";
|
|
@@ -8504,6 +8590,62 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
|
|
|
8504
8590
|
durationMs?: number | undefined;
|
|
8505
8591
|
};
|
|
8506
8592
|
timestampMs: number;
|
|
8593
|
+
}>, z.ZodObject<{
|
|
8594
|
+
v: z.ZodLiteral<1>;
|
|
8595
|
+
eventId: z.ZodString;
|
|
8596
|
+
eventIndex: z.ZodNumber;
|
|
8597
|
+
sessionId: z.ZodString;
|
|
8598
|
+
dedupeKey: z.ZodString;
|
|
8599
|
+
timestampMs: z.ZodNumber;
|
|
8600
|
+
} & {
|
|
8601
|
+
kind: z.ZodLiteral<"delivery_recorded">;
|
|
8602
|
+
scope: z.ZodObject<{
|
|
8603
|
+
runId: z.ZodString;
|
|
8604
|
+
}, "strip", z.ZodTypeAny, {
|
|
8605
|
+
runId: string;
|
|
8606
|
+
}, {
|
|
8607
|
+
runId: string;
|
|
8608
|
+
}>;
|
|
8609
|
+
data: z.ZodObject<{
|
|
8610
|
+
shas: z.ZodArray<z.ZodString, "many">;
|
|
8611
|
+
prUrl: z.ZodOptional<z.ZodString>;
|
|
8612
|
+
}, "strip", z.ZodTypeAny, {
|
|
8613
|
+
shas: string[];
|
|
8614
|
+
prUrl?: string | undefined;
|
|
8615
|
+
}, {
|
|
8616
|
+
shas: string[];
|
|
8617
|
+
prUrl?: string | undefined;
|
|
8618
|
+
}>;
|
|
8619
|
+
}, "strip", z.ZodTypeAny, {
|
|
8620
|
+
kind: "delivery_recorded";
|
|
8621
|
+
v: 1;
|
|
8622
|
+
sessionId: string;
|
|
8623
|
+
eventIndex: number;
|
|
8624
|
+
eventId: string;
|
|
8625
|
+
dedupeKey: string;
|
|
8626
|
+
scope: {
|
|
8627
|
+
runId: string;
|
|
8628
|
+
};
|
|
8629
|
+
data: {
|
|
8630
|
+
shas: string[];
|
|
8631
|
+
prUrl?: string | undefined;
|
|
8632
|
+
};
|
|
8633
|
+
timestampMs: number;
|
|
8634
|
+
}, {
|
|
8635
|
+
kind: "delivery_recorded";
|
|
8636
|
+
v: 1;
|
|
8637
|
+
sessionId: string;
|
|
8638
|
+
eventIndex: number;
|
|
8639
|
+
eventId: string;
|
|
8640
|
+
dedupeKey: string;
|
|
8641
|
+
scope: {
|
|
8642
|
+
runId: string;
|
|
8643
|
+
};
|
|
8644
|
+
data: {
|
|
8645
|
+
shas: string[];
|
|
8646
|
+
prUrl?: string | undefined;
|
|
8647
|
+
};
|
|
8648
|
+
timestampMs: number;
|
|
8507
8649
|
}>]>, "many">;
|
|
8508
8650
|
manifest: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
8509
8651
|
v: z.ZodLiteral<1>;
|
|
@@ -11248,6 +11390,21 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
|
|
|
11248
11390
|
durationMs?: number | undefined;
|
|
11249
11391
|
};
|
|
11250
11392
|
timestampMs: number;
|
|
11393
|
+
} | {
|
|
11394
|
+
kind: "delivery_recorded";
|
|
11395
|
+
v: 1;
|
|
11396
|
+
sessionId: string;
|
|
11397
|
+
eventIndex: number;
|
|
11398
|
+
eventId: string;
|
|
11399
|
+
dedupeKey: string;
|
|
11400
|
+
scope: {
|
|
11401
|
+
runId: string;
|
|
11402
|
+
};
|
|
11403
|
+
data: {
|
|
11404
|
+
shas: string[];
|
|
11405
|
+
prUrl?: string | undefined;
|
|
11406
|
+
};
|
|
11407
|
+
timestampMs: number;
|
|
11251
11408
|
})[];
|
|
11252
11409
|
manifest: ({
|
|
11253
11410
|
kind: "segment_closed";
|
|
@@ -11862,6 +12019,21 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
|
|
|
11862
12019
|
durationMs?: number | undefined;
|
|
11863
12020
|
};
|
|
11864
12021
|
timestampMs: number;
|
|
12022
|
+
} | {
|
|
12023
|
+
kind: "delivery_recorded";
|
|
12024
|
+
v: 1;
|
|
12025
|
+
sessionId: string;
|
|
12026
|
+
eventIndex: number;
|
|
12027
|
+
eventId: string;
|
|
12028
|
+
dedupeKey: string;
|
|
12029
|
+
scope: {
|
|
12030
|
+
runId: string;
|
|
12031
|
+
};
|
|
12032
|
+
data: {
|
|
12033
|
+
shas: string[];
|
|
12034
|
+
prUrl?: string | undefined;
|
|
12035
|
+
};
|
|
12036
|
+
timestampMs: number;
|
|
11865
12037
|
})[];
|
|
11866
12038
|
manifest: ({
|
|
11867
12039
|
kind: "segment_closed";
|
|
@@ -12490,6 +12662,21 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
|
|
|
12490
12662
|
durationMs?: number | undefined;
|
|
12491
12663
|
};
|
|
12492
12664
|
timestampMs: number;
|
|
12665
|
+
} | {
|
|
12666
|
+
kind: "delivery_recorded";
|
|
12667
|
+
v: 1;
|
|
12668
|
+
sessionId: string;
|
|
12669
|
+
eventIndex: number;
|
|
12670
|
+
eventId: string;
|
|
12671
|
+
dedupeKey: string;
|
|
12672
|
+
scope: {
|
|
12673
|
+
runId: string;
|
|
12674
|
+
};
|
|
12675
|
+
data: {
|
|
12676
|
+
shas: string[];
|
|
12677
|
+
prUrl?: string | undefined;
|
|
12678
|
+
};
|
|
12679
|
+
timestampMs: number;
|
|
12493
12680
|
})[];
|
|
12494
12681
|
manifest: ({
|
|
12495
12682
|
kind: "segment_closed";
|
|
@@ -13121,6 +13308,21 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
|
|
|
13121
13308
|
durationMs?: number | undefined;
|
|
13122
13309
|
};
|
|
13123
13310
|
timestampMs: number;
|
|
13311
|
+
} | {
|
|
13312
|
+
kind: "delivery_recorded";
|
|
13313
|
+
v: 1;
|
|
13314
|
+
sessionId: string;
|
|
13315
|
+
eventIndex: number;
|
|
13316
|
+
eventId: string;
|
|
13317
|
+
dedupeKey: string;
|
|
13318
|
+
scope: {
|
|
13319
|
+
runId: string;
|
|
13320
|
+
};
|
|
13321
|
+
data: {
|
|
13322
|
+
shas: string[];
|
|
13323
|
+
prUrl?: string | undefined;
|
|
13324
|
+
};
|
|
13325
|
+
timestampMs: number;
|
|
13124
13326
|
})[];
|
|
13125
13327
|
manifest: ({
|
|
13126
13328
|
kind: "segment_closed";
|
|
@@ -2555,5 +2555,61 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
|
|
|
2555
2555
|
durationMs?: number | undefined;
|
|
2556
2556
|
};
|
|
2557
2557
|
timestampMs: number;
|
|
2558
|
+
}>, z.ZodObject<{
|
|
2559
|
+
v: z.ZodLiteral<1>;
|
|
2560
|
+
eventId: z.ZodString;
|
|
2561
|
+
eventIndex: z.ZodNumber;
|
|
2562
|
+
sessionId: z.ZodString;
|
|
2563
|
+
dedupeKey: z.ZodString;
|
|
2564
|
+
timestampMs: z.ZodNumber;
|
|
2565
|
+
} & {
|
|
2566
|
+
kind: z.ZodLiteral<"delivery_recorded">;
|
|
2567
|
+
scope: z.ZodObject<{
|
|
2568
|
+
runId: z.ZodString;
|
|
2569
|
+
}, "strip", z.ZodTypeAny, {
|
|
2570
|
+
runId: string;
|
|
2571
|
+
}, {
|
|
2572
|
+
runId: string;
|
|
2573
|
+
}>;
|
|
2574
|
+
data: z.ZodObject<{
|
|
2575
|
+
shas: z.ZodArray<z.ZodString, "many">;
|
|
2576
|
+
prUrl: z.ZodOptional<z.ZodString>;
|
|
2577
|
+
}, "strip", z.ZodTypeAny, {
|
|
2578
|
+
shas: string[];
|
|
2579
|
+
prUrl?: string | undefined;
|
|
2580
|
+
}, {
|
|
2581
|
+
shas: string[];
|
|
2582
|
+
prUrl?: string | undefined;
|
|
2583
|
+
}>;
|
|
2584
|
+
}, "strip", z.ZodTypeAny, {
|
|
2585
|
+
kind: "delivery_recorded";
|
|
2586
|
+
v: 1;
|
|
2587
|
+
sessionId: string;
|
|
2588
|
+
eventIndex: number;
|
|
2589
|
+
eventId: string;
|
|
2590
|
+
dedupeKey: string;
|
|
2591
|
+
scope: {
|
|
2592
|
+
runId: string;
|
|
2593
|
+
};
|
|
2594
|
+
data: {
|
|
2595
|
+
shas: string[];
|
|
2596
|
+
prUrl?: string | undefined;
|
|
2597
|
+
};
|
|
2598
|
+
timestampMs: number;
|
|
2599
|
+
}, {
|
|
2600
|
+
kind: "delivery_recorded";
|
|
2601
|
+
v: 1;
|
|
2602
|
+
sessionId: string;
|
|
2603
|
+
eventIndex: number;
|
|
2604
|
+
eventId: string;
|
|
2605
|
+
dedupeKey: string;
|
|
2606
|
+
scope: {
|
|
2607
|
+
runId: string;
|
|
2608
|
+
};
|
|
2609
|
+
data: {
|
|
2610
|
+
shas: string[];
|
|
2611
|
+
prUrl?: string | undefined;
|
|
2612
|
+
};
|
|
2613
|
+
timestampMs: number;
|
|
2558
2614
|
}>]>;
|
|
2559
2615
|
export type DomainEventV1 = z.infer<typeof DomainEventV1Schema>;
|
|
@@ -274,4 +274,12 @@ exports.DomainEventV1Schema = zod_1.z.discriminatedUnion('kind', [
|
|
|
274
274
|
}
|
|
275
275
|
}),
|
|
276
276
|
}),
|
|
277
|
+
exports.DomainEventEnvelopeV1Schema.extend({
|
|
278
|
+
kind: zod_1.z.literal('delivery_recorded'),
|
|
279
|
+
scope: zod_1.z.object({ runId: zod_1.z.string().min(1) }),
|
|
280
|
+
data: zod_1.z.object({
|
|
281
|
+
shas: zod_1.z.array(zod_1.z.string()),
|
|
282
|
+
prUrl: zod_1.z.string().optional(),
|
|
283
|
+
}),
|
|
284
|
+
}),
|
|
277
285
|
]);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GitEndSnapshot, GitSnapshotPortV2 } from '../../../ports/git-snapshot.port.js';
|
|
2
|
+
export declare class LocalGitSnapshotV2 implements GitSnapshotPortV2 {
|
|
3
|
+
resolveEndSnapshot(repoRoot: string | null, startSha: string | null): Promise<GitEndSnapshot>;
|
|
4
|
+
private resolveEndSha;
|
|
5
|
+
private resolveCommitRange;
|
|
6
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalGitSnapshotV2 = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
7
|
+
const GIT_TIMEOUT_MS = 2000;
|
|
8
|
+
class LocalGitSnapshotV2 {
|
|
9
|
+
async resolveEndSnapshot(repoRoot, startSha) {
|
|
10
|
+
if (!repoRoot)
|
|
11
|
+
return { endSha: null, commitShas: [] };
|
|
12
|
+
const [endSha, commitShas] = await Promise.all([
|
|
13
|
+
this.resolveEndSha(repoRoot),
|
|
14
|
+
this.resolveCommitRange(repoRoot, startSha),
|
|
15
|
+
]);
|
|
16
|
+
return { endSha, commitShas };
|
|
17
|
+
}
|
|
18
|
+
async resolveEndSha(repoRoot) {
|
|
19
|
+
try {
|
|
20
|
+
const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: repoRoot, timeout: GIT_TIMEOUT_MS });
|
|
21
|
+
return stdout.trim() || null;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async resolveCommitRange(repoRoot, startSha) {
|
|
28
|
+
if (!startSha)
|
|
29
|
+
return [];
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execFileAsync('git', ['log', '--no-merges', '--first-parent', `${startSha}..HEAD`, '--format=%H'], { cwd: repoRoot, timeout: GIT_TIMEOUT_MS });
|
|
32
|
+
return stdout.trim().split('\n').filter(s => s.length > 0);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.LocalGitSnapshotV2 = LocalGitSnapshotV2;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface GitEndSnapshot {
|
|
2
|
+
readonly endSha: string | null;
|
|
3
|
+
readonly commitShas: readonly string[];
|
|
4
|
+
}
|
|
5
|
+
export interface GitSnapshotPortV2 {
|
|
6
|
+
resolveEndSnapshot(repoRoot: string | null, startSha: string | null): Promise<GitEndSnapshot>;
|
|
7
|
+
}
|
|
8
|
+
export declare class NullGitSnapshotV2 implements GitSnapshotPortV2 {
|
|
9
|
+
resolveEndSnapshot(): Promise<GitEndSnapshot>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NullGitSnapshotV2 = void 0;
|
|
4
|
+
class NullGitSnapshotV2 {
|
|
5
|
+
async resolveEndSnapshot() {
|
|
6
|
+
return { endSha: null, commitShas: [] };
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.NullGitSnapshotV2 = NullGitSnapshotV2;
|
|
@@ -52,6 +52,18 @@ function projectSessionMetricsV2(events) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
let deliveryShas = [];
|
|
56
|
+
for (const e of events) {
|
|
57
|
+
if (e.kind !== constants_js_1.EVENT_KIND.DELIVERY_RECORDED)
|
|
58
|
+
continue;
|
|
59
|
+
if (e.scope?.runId !== runCompletedRunId)
|
|
60
|
+
continue;
|
|
61
|
+
const shasRaw = e.data.shas;
|
|
62
|
+
if (Array.isArray(shasRaw)) {
|
|
63
|
+
deliveryShas = shasRaw.filter((s) => typeof s === 'string');
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
55
67
|
const commitShasRaw = metricsContext['metrics_commit_shas'];
|
|
56
68
|
const metricCommitShas = [];
|
|
57
69
|
if (Array.isArray(commitShasRaw)) {
|
|
@@ -60,19 +72,22 @@ function projectSessionMetricsV2(events) {
|
|
|
60
72
|
metricCommitShas.push(sha);
|
|
61
73
|
}
|
|
62
74
|
}
|
|
63
|
-
const finalAgentCommitShas =
|
|
75
|
+
const finalAgentCommitShas = deliveryShas.length > 0 ? deliveryShas :
|
|
76
|
+
metricCommitShas.length > 0 ? metricCommitShas :
|
|
77
|
+
agentCommitShas;
|
|
64
78
|
const filesChangedRaw = metricsContext['metrics_files_changed'];
|
|
65
79
|
const filesChanged = typeof filesChangedRaw === 'number' && Number.isFinite(filesChangedRaw) ? filesChangedRaw : null;
|
|
66
80
|
const linesAddedRaw = metricsContext['metrics_lines_added'];
|
|
67
81
|
const linesAdded = typeof linesAddedRaw === 'number' && Number.isFinite(linesAddedRaw) ? linesAddedRaw : null;
|
|
68
82
|
const linesRemovedRaw = metricsContext['metrics_lines_removed'];
|
|
69
83
|
const linesRemoved = typeof linesRemovedRaw === 'number' && Number.isFinite(linesRemovedRaw) ? linesRemovedRaw : null;
|
|
84
|
+
const finalCaptureConfidence = deliveryShas.length > 0 ? 'high' : captureConfidence;
|
|
70
85
|
return {
|
|
71
86
|
startGitSha,
|
|
72
87
|
endGitSha,
|
|
73
88
|
gitBranch,
|
|
74
89
|
agentCommitShas: finalAgentCommitShas,
|
|
75
|
-
captureConfidence,
|
|
90
|
+
captureConfidence: finalCaptureConfidence,
|
|
76
91
|
durationMs,
|
|
77
92
|
outcome,
|
|
78
93
|
prNumbers,
|
package/docs/authoring.md
CHANGED
|
@@ -761,6 +761,28 @@ Canonical current rules for authoring good WorkRail workflows. workflow.schema.j
|
|
|
761
761
|
|
|
762
762
|
|
|
763
763
|
## Artifacts and planning surfaces
|
|
764
|
+
### coordinator-result-artifact-schema
|
|
765
|
+
- **Level**: required
|
|
766
|
+
- **Status**: active
|
|
767
|
+
- **Scope**: artifact.coordinator-result
|
|
768
|
+
- **Rule**: When a workflow step signals coordinator phase completion, emit a `wr.coordinator_result` artifact with exactly 4 fields: `outcome` (enum: success|failed|timed_out|await_degraded), `summary` (string), `sessionId` (string), `error` (string|null). No additional fields allowed.
|
|
769
|
+
- **Why**: Coordinators read this artifact to determine whether to proceed, retry, or escalate. Extra fields pollute the schema boundary and break forward compatibility. The 4-field constraint is a hard limit, not a guideline.
|
|
770
|
+
- **Enforced by**: advisory
|
|
771
|
+
|
|
772
|
+
**Checks**
|
|
773
|
+
- Exactly 4 fields present: outcome, summary, sessionId, error.
|
|
774
|
+
- outcome is one of: success, failed, timed_out, await_degraded.
|
|
775
|
+
- error is string|null -- null when outcome is success, non-null string when outcome is failed.
|
|
776
|
+
- No workflow-specific fields (prUrl, branchName, commitSha, etc.) in wr.coordinator_result. Those belong in workflow-specific artifacts.
|
|
777
|
+
|
|
778
|
+
**Anti-patterns**
|
|
779
|
+
- Adding prUrl, branchName, or commitSha to wr.coordinator_result
|
|
780
|
+
- Using a free-form notes string instead of the typed outcome enum
|
|
781
|
+
- Omitting sessionId (required for coordinator tracing and console parent-child display)
|
|
782
|
+
|
|
783
|
+
**Source refs**
|
|
784
|
+
- `src/coordinators/types.ts` (runtime) — ChildSessionResult discriminated union -- the runtime type that wr.coordinator_result maps to.
|
|
785
|
+
|
|
764
786
|
### artifact-canonicality
|
|
765
787
|
- **Level**: recommended
|
|
766
788
|
- **Status**: active
|
|
@@ -913,6 +935,7 @@ Canonical current rules for authoring good WorkRail workflows. workflow.schema.j
|
|
|
913
935
|
- `artifact.plan`: Implementation-planning artifacts
|
|
914
936
|
- `artifact.spec`: Behavior/specification artifacts
|
|
915
937
|
- `artifact.verification`: Verification or handoff artifacts
|
|
938
|
+
- `artifact.coordinator-result`: wr.coordinator_result artifact emitted by coordinator-phase workflows to signal phase completion to the coordinator
|
|
916
939
|
- `delegation.context-packet`: Structured context passed to subagents
|
|
917
940
|
- `delegation.result-envelope`: Structured result shape returned by subagents
|
|
918
941
|
- `legacy.patterns`: Older authoring patterns that should now be discouraged or avoided
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Engine Boundary Discovery: SHA Tracking and Layer Architecture
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-30
|
|
4
|
+
**Status:** Discovery complete -- recommendation: A now (PR #903), C next
|
|
5
|
+
**Goal:** Determine the right architecture for commit SHA tracking and clarify engine/coordinator/delivery layer boundaries
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem Understanding
|
|
10
|
+
|
|
11
|
+
### The Real Problem
|
|
12
|
+
The engine emits `run_completed` at step T. The delivery pipeline commits to git at step T+1. There is no mechanism to communicate back from T+1 to T. Agent self-reporting of `metrics_commit_shas` was the broken workaround.
|
|
13
|
+
|
|
14
|
+
### Core Tensions
|
|
15
|
+
1. **Information completeness vs engine purity**: commits only exist after delivery, which is after `run_completed`
|
|
16
|
+
2. **YAGNI vs architectural completeness**: MCP sessions may not need SHA tracking right now
|
|
17
|
+
3. **Port purity vs pragmatism**: `outcome-success.ts` already calls `execFile` directly for `endGitSha` -- known violation
|
|
18
|
+
4. **Atomic delivery vs distributed write-back**: appending after `run_completed` requires the session gate to remain open
|
|
19
|
+
|
|
20
|
+
### What Makes This Hard
|
|
21
|
+
The gap is temporal. Information that belongs to session T becomes available at T+1. The event log is append-only but NOT closed after `run_completed` -- `ExecutionSessionGateV2` gates on `healthy` vs `corrupt`, not on completion state. Post-completion appends ARE valid.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Architecture Contract (what exists, what's missing)
|
|
26
|
+
|
|
27
|
+
### Engine owns (src/v2/durable-core/ + src/mcp/handlers/):
|
|
28
|
+
- Session lifecycle (start, advance, checkpoint, resume)
|
|
29
|
+
- HMAC token protocol -- cryptographic enforcement
|
|
30
|
+
- Append-only event log with crash-safe writes
|
|
31
|
+
- Port abstractions for all I/O (no direct fs/git/network calls in engine)
|
|
32
|
+
- Projections: reads event log, produces typed views
|
|
33
|
+
- **Records observations it can make directly**: git_branch, git_head_sha (via WorkspaceContextResolverPortV2 at START); endGitSha (via execFile direct call at END -- known port violation)
|
|
34
|
+
|
|
35
|
+
### Coordinator/delivery layer owns (src/trigger/ + src/daemon/):
|
|
36
|
+
- Git commits, pushes, PR creation
|
|
37
|
+
- Delivery pipeline stages
|
|
38
|
+
- SHA extraction from `git commit` output
|
|
39
|
+
- Proof records, pipeline orchestration (future)
|
|
40
|
+
- Agent loop, context injection, crash recovery
|
|
41
|
+
|
|
42
|
+
### The gap:
|
|
43
|
+
No mechanism for the delivery layer to record its observations (what it committed) back into the session event log.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Philosophy Constraints
|
|
48
|
+
|
|
49
|
+
- **Architectural fixes over patches**: always-empty is a patch; extending the port is the fix
|
|
50
|
+
- **Dependency injection for boundaries**: git I/O must be behind a port in the engine layer
|
|
51
|
+
- **Make illegal states unrepresentable**: `captureConfidence: 'high'` with `agentCommitShas: []` is already schema-enforced as invalid
|
|
52
|
+
- **Validate at boundaries, trust inside**: SHA derivation must happen at the git boundary, never from agent memory
|
|
53
|
+
- **YAGNI with discipline**: don't build write-back infrastructure until coordinators actually consume it
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Candidates
|
|
58
|
+
|
|
59
|
+
### A: Always-empty (current PR #903)
|
|
60
|
+
Engine emits `agentCommitShas: []` and `captureConfidence: 'none'` always. Console shows no SHAs. Coordinator scripts derive SHAs from `git log startSha..endSha` themselves.
|
|
61
|
+
|
|
62
|
+
- **Tensions resolved**: engine purity, illegal states, YAGNI
|
|
63
|
+
- **Tensions accepted**: information completeness, worktree-after-cleanup risk
|
|
64
|
+
- **Failure mode**: worktree cleaned up before coordinator script runs `git log`
|
|
65
|
+
- **Scope**: best-fit immediate fix; patch not architectural fix
|
|
66
|
+
- **Philosophy**: honors YAGNI, make-illegal-states-unrepresentable. Conflicts with architectural-fixes-over-patches.
|
|
67
|
+
|
|
68
|
+
### B: Delivery write-back via DeliveryStage
|
|
69
|
+
New 5th DeliveryStage appends `observation_recorded` event with `git_commit_shas` to session event log after successful `git commit`. Requires injecting `SessionEventLogAppendStorePortV2` into `DeliveryPipeline`.
|
|
70
|
+
|
|
71
|
+
- **Tensions resolved**: information completeness (daemon sessions), crash safety (stage before sidecar delete)
|
|
72
|
+
- **Tensions accepted**: delivery-action API change (new session store dependency); MCP sessions still get no SHAs
|
|
73
|
+
- **Failure mode**: append fails silently if session health degraded (must be best-effort, never abort delivery)
|
|
74
|
+
- **Scope**: best-fit for daemon/autoCommit sessions; accepted gap for MCP
|
|
75
|
+
- **Philosophy**: honors architectural-fixes-over-patches, dependency-injection. Mild YAGNI tension.
|
|
76
|
+
|
|
77
|
+
### C: Extend WorkspaceContextResolverPortV2 with end-state (port-correct fix)
|
|
78
|
+
Add `resolveEndState(repoRoot, startSha) -> { endSha, commitShas }` to the workspace anchor port (or create `GitSnapshotPortV2`). Inject into `outcome-success.ts`. Runs `git rev-parse HEAD` + `git log startSha..HEAD --format=%H` in parallel. `run_completed` event gets `commitShas: string[]` field.
|
|
79
|
+
|
|
80
|
+
- **Tensions resolved**: engine purity (eliminates execFile violation), information completeness (all session types), MCP + daemon covered
|
|
81
|
+
- **Tensions accepted**: run_completed schema gets new field; old sessions project `commitShas: []` (non-breaking)
|
|
82
|
+
- **Failure mode**: `git log startSha..HEAD` may include merge commits or upstream advances; use `--no-merges --first-parent` to scope
|
|
83
|
+
- **Scope**: slightly broader than needed (touches port contract) but correct -- each piece is load-bearing
|
|
84
|
+
- **Philosophy**: honors architectural-fixes-over-patches, dependency-injection-for-boundaries, make-illegal-states-unrepresentable.
|
|
85
|
+
|
|
86
|
+
### D: Delivery sidecar store (reframe -- REJECTED)
|
|
87
|
+
Delivery writes `delivery-result-<sessionId>.json` sidecar. Session-metrics reads from both event log and sidecar. REJECTED: violates single-source-of-truth principle for the event log. Two truth sources for SHA data is architecturally unsound for this codebase.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Comparison and Recommendation
|
|
92
|
+
|
|
93
|
+
**Recommendation: A now (PR #903 as-is), C next.**
|
|
94
|
+
|
|
95
|
+
C is the architectural ideal because:
|
|
96
|
+
1. Solves both problems simultaneously: SHA gap AND the existing execFile violation in outcome-success.ts
|
|
97
|
+
2. Works for ALL session types (MCP + daemon) without special-casing
|
|
98
|
+
3. Follows the port pattern the codebase is designed around
|
|
99
|
+
4. run_completed is the correct home -- startGitSha is already there, endGitSha is already there, commitShas belongs alongside them
|
|
100
|
+
|
|
101
|
+
B is viable but inferior: adds a session store dependency to the delivery layer (which currently has none), only covers daemon/autoCommit sessions, and has a semantic question (delivery artifacts vs session truth).
|
|
102
|
+
|
|
103
|
+
A is correct as the IMMEDIATE state. PR #903 should merge. It makes the illegal state (high confidence + empty SHAs) impossible, and clears the field for C.
|
|
104
|
+
|
|
105
|
+
**The original question -- do we need to split the engine? -- is NO.** The current architecture is sound. The fix is a port extension, not a structural split.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Self-Critique
|
|
110
|
+
|
|
111
|
+
**Strongest counter-argument against C**: run_completed schema migration. Existing stored events lack `commitShas`. Rebuttal: session-metrics projection already handles absent fields (defaults to []). Adding `commitShas?: string[]` is non-breaking additive.
|
|
112
|
+
|
|
113
|
+
**What would tip to B**: if daemon sessions with autoCommit dominate and MCP sessions never need SHAs. Currently autoCommit is daemon-only, so B's coverage gap is acceptable. But if MCP sessions start running in worktrees with autoCommit, B becomes wrong.
|
|
114
|
+
|
|
115
|
+
**Invalidating assumption**: if `git log startSha..HEAD` includes upstream merge commits, SHAs from unrelated PRs land in the session's record. Mitigation: `--no-merges --first-parent` in the port implementation.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Open Questions
|
|
120
|
+
|
|
121
|
+
1. Should `GitSnapshotPortV2` be a new port or an extension of `WorkspaceContextResolverPortV2`? New port has cleaner separation; extension avoids proliferating ports.
|
|
122
|
+
2. What is the right git log filter for commit SHAs: `--no-merges`, `--first-parent`, both? Depends on whether merge commits from squash-merge PRs should be included.
|
|
123
|
+
3. Should Candidate C be implemented now (before PR #903 merges) or after? After is safer -- PR #903 is already in CI, touching outcome-success.ts again would create a dirty merge.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Engine Boundary Discovery: Design Review Findings
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-30
|
|
4
|
+
**Decision:** A now (PR #903), C next (GitSnapshotPortV2)
|
|
5
|
+
**Confidence:** HIGH
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Tradeoff Review
|
|
10
|
+
|
|
11
|
+
| Tradeoff | Acceptable? | Invalidating condition |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Console shows empty SHAs until C is built | Yes -- empty is honest, no current consumer needs populated SHAs | Proof records feature built before C |
|
|
14
|
+
| MCP sessions get no SHAs under A-only | Yes -- human can check git; MCP is not the autonomous use case | MCP sessions start requiring attribution for proof records |
|
|
15
|
+
| run_completed schema gets optional field | Yes -- non-breaking additive change; old sessions project [] | Schema versioning feature enforces strict field presence |
|
|
16
|
+
|
|
17
|
+
All tradeoffs acceptable under current conditions.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Failure Mode Review
|
|
22
|
+
|
|
23
|
+
| Failure Mode | Coverage | Risk |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| git log includes upstream merge commits | Mitigate with `--no-merges --first-parent` in port implementation | MEDIUM -- must be in the implementation spec |
|
|
26
|
+
| outcome-success.ts partial failure (endGitSha or commitShas fails) | Promise.all + best-effort; both degrade to null/[] independently | LOW -- same behavior as current resolveEndGitSha |
|
|
27
|
+
|
|
28
|
+
**Highest-risk**: FM1. Must specify `--no-merges --first-parent` explicitly in Candidate C implementation.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Runner-up / Simpler Alternative Review
|
|
33
|
+
|
|
34
|
+
- B (delivery write-back): no elements worth borrowing. Its coupling (session store injected into delivery layer) is the problem C avoids.
|
|
35
|
+
- Simpler C variant (fix execFile violation only, skip commitShas): would require a second port extension later. Marginal cost of doing both at once is low. Not simpler enough to justify the incompleteness.
|
|
36
|
+
- No hybrid warranted. A-then-C sequencing is correct.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Philosophy Alignment
|
|
41
|
+
|
|
42
|
+
**Satisfied**: make-illegal-states-unrepresentable, validate-at-boundaries, errors-as-data, dependency-injection-for-boundaries, architectural-fixes-over-patches.
|
|
43
|
+
|
|
44
|
+
**Under acceptable tension**: YAGNI (building port before proof records consume it -- justified by dual purpose: also fixes execFile violation). Immutability (appending to completed session -- design-correct, session gate confirms).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Findings
|
|
49
|
+
|
|
50
|
+
### YELLOW: git log filter not specified
|
|
51
|
+
The implementation of Candidate C's `resolveCommitShaRange` must use `--no-merges --first-parent` to avoid including upstream merge commits. This is not currently specified anywhere. Without it, sessions on branches with merge commits from main would record incorrect SHAs.
|
|
52
|
+
**Action**: add explicit filter spec to the Candidate C implementation ticket.
|
|
53
|
+
|
|
54
|
+
### YELLOW: No forcing function to build C after A merges
|
|
55
|
+
PR #903 will merge. C is the architectural fix. Without a ticket, C may never get built and the console SHA display stays empty indefinitely.
|
|
56
|
+
**Action**: create a GitHub issue for Candidate C immediately after PR #903 merges.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Recommended Revisions to Selected Design
|
|
61
|
+
|
|
62
|
+
1. Before implementing C: specify `--no-merges --first-parent` as the required git log filter in the issue description
|
|
63
|
+
2. New port name: `GitSnapshotPortV2` is cleaner than extending `WorkspaceContextResolverPortV2` -- keeps the end-state capture concern separate from the start-state anchor concern
|
|
64
|
+
3. New method signature: `resolveEndSnapshot(repoRoot: string, startSha: string): Promise<{ endSha: string | null, commitShas: string[] }>`
|
|
65
|
+
4. In `outcome-success.ts`: replace `resolveEndGitSha` with `resolveEndSnapshot` -- single port call that returns both `endGitSha` and `commitShas` in parallel
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Residual Concerns
|
|
70
|
+
|
|
71
|
+
- **Architecture contract not written**: the meta-fix (documenting what the engine layer owns vs coordinator/delivery) has not been done. This is the most important long-term outcome from this discovery. Without it, future contributors will make the same boundary violations. Should be added to `docs/design/v2-core-design-locks.md`.
|
|
72
|
+
- **C has open design question**: `GitSnapshotPortV2` vs extension of `WorkspaceContextResolverPortV2`. Both work. `GitSnapshotPortV2` is slightly cleaner (separate port, separate concern). Decision can be made at implementation time.
|