@exaudeus/workrail 3.41.0 → 3.42.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 +40 -11
- package/dist/console-ui/assets/{index-CQt4UhPB.js → index-DwfWMKvv.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/context-assembly/deps.d.ts +8 -0
- package/dist/context-assembly/deps.js +2 -0
- package/dist/context-assembly/index.d.ts +6 -0
- package/dist/context-assembly/index.js +50 -0
- package/dist/context-assembly/infra.d.ts +3 -0
- package/dist/context-assembly/infra.js +154 -0
- package/dist/context-assembly/types.d.ts +30 -0
- package/dist/context-assembly/types.js +2 -0
- package/dist/coordinators/pr-review.d.ts +3 -1
- package/dist/coordinators/pr-review.js +25 -4
- package/dist/daemon/workflow-runner.js +11 -0
- package/dist/domain/execution/state.d.ts +6 -6
- package/dist/manifest.json +64 -32
- package/dist/mcp/handlers/v2-workflow.d.ts +2 -2
- package/dist/mcp/output-schemas.d.ts +234 -234
- package/dist/mcp/tools.d.ts +2 -2
- package/dist/mcp/v2/tools.d.ts +24 -24
- package/dist/v2/durable-core/schemas/artifacts/assessment.d.ts +2 -2
- package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.d.ts +2 -2
- package/dist/v2/durable-core/schemas/artifacts/loop-control.d.ts +6 -6
- package/dist/v2/durable-core/schemas/artifacts/review-verdict.d.ts +6 -6
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +56 -56
- package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.d.ts +83 -83
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +1024 -1024
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +2336 -2336
- package/dist/v2/durable-core/schemas/session/dag-topology.d.ts +6 -6
- package/dist/v2/durable-core/schemas/session/events.d.ts +339 -339
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +30 -30
- package/dist/v2/durable-core/schemas/session/manifest.d.ts +6 -6
- package/dist/v2/durable-core/schemas/session/outputs.d.ts +8 -8
- package/dist/v2/durable-core/schemas/session/validation-event.d.ts +3 -3
- package/docs/design/context-assembly-design-candidates.md +199 -0
- package/docs/design/context-assembly-implementation-plan.md +211 -0
- package/docs/design/context-assembly-review-findings.md +112 -0
- package/docs/ideas/backlog.md +64 -0
- package/package.json +1 -1
|
@@ -4,38 +4,38 @@ export declare const GapReasonSchema: z.ZodDiscriminatedUnion<"category", [z.Zod
|
|
|
4
4
|
category: z.ZodLiteral<"user_only_dependency">;
|
|
5
5
|
detail: z.ZodEnum<["needs_user_secret_or_token", "needs_user_account_access", "needs_user_artifact", "needs_user_choice", "needs_user_approval", "needs_user_environment_action"]>;
|
|
6
6
|
}, "strip", z.ZodTypeAny, {
|
|
7
|
-
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
8
7
|
category: "user_only_dependency";
|
|
9
|
-
}, {
|
|
10
8
|
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
9
|
+
}, {
|
|
11
10
|
category: "user_only_dependency";
|
|
11
|
+
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
12
12
|
}>, z.ZodObject<{
|
|
13
13
|
category: z.ZodLiteral<"contract_violation">;
|
|
14
14
|
detail: z.ZodEnum<["missing_required_output", "invalid_required_output", "missing_required_notes", "assessment_followup_required"]>;
|
|
15
15
|
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
detail: "invalid_required_output" | "missing_required_output" | "assessment_followup_required" | "missing_required_notes";
|
|
17
16
|
category: "contract_violation";
|
|
17
|
+
detail: "missing_required_output" | "invalid_required_output" | "missing_required_notes" | "assessment_followup_required";
|
|
18
18
|
}, {
|
|
19
|
-
detail: "invalid_required_output" | "missing_required_output" | "assessment_followup_required" | "missing_required_notes";
|
|
20
19
|
category: "contract_violation";
|
|
20
|
+
detail: "missing_required_output" | "invalid_required_output" | "missing_required_notes" | "assessment_followup_required";
|
|
21
21
|
}>, z.ZodObject<{
|
|
22
22
|
category: z.ZodLiteral<"capability_missing">;
|
|
23
23
|
detail: z.ZodEnum<["required_capability_unavailable", "required_capability_unknown"]>;
|
|
24
24
|
}, "strip", z.ZodTypeAny, {
|
|
25
|
-
detail: "required_capability_unknown" | "required_capability_unavailable";
|
|
26
25
|
category: "capability_missing";
|
|
26
|
+
detail: "required_capability_unavailable" | "required_capability_unknown";
|
|
27
27
|
}, {
|
|
28
|
-
detail: "required_capability_unknown" | "required_capability_unavailable";
|
|
29
28
|
category: "capability_missing";
|
|
29
|
+
detail: "required_capability_unavailable" | "required_capability_unknown";
|
|
30
30
|
}>, z.ZodObject<{
|
|
31
31
|
category: z.ZodLiteral<"unexpected">;
|
|
32
32
|
detail: z.ZodEnum<["invariant_violation", "storage_corruption_detected", "evaluation_error"]>;
|
|
33
33
|
}, "strip", z.ZodTypeAny, {
|
|
34
|
-
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
35
34
|
category: "unexpected";
|
|
36
|
-
}, {
|
|
37
35
|
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
36
|
+
}, {
|
|
38
37
|
category: "unexpected";
|
|
38
|
+
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
39
39
|
}>]>;
|
|
40
40
|
export declare const GapResolutionSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
41
41
|
kind: z.ZodLiteral<"unresolved">;
|
|
@@ -79,38 +79,38 @@ export declare const GapRecordedDataV1Schema: z.ZodObject<{
|
|
|
79
79
|
category: z.ZodLiteral<"user_only_dependency">;
|
|
80
80
|
detail: z.ZodEnum<["needs_user_secret_or_token", "needs_user_account_access", "needs_user_artifact", "needs_user_choice", "needs_user_approval", "needs_user_environment_action"]>;
|
|
81
81
|
}, "strip", z.ZodTypeAny, {
|
|
82
|
-
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
83
82
|
category: "user_only_dependency";
|
|
84
|
-
}, {
|
|
85
83
|
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
84
|
+
}, {
|
|
86
85
|
category: "user_only_dependency";
|
|
86
|
+
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
87
87
|
}>, z.ZodObject<{
|
|
88
88
|
category: z.ZodLiteral<"contract_violation">;
|
|
89
89
|
detail: z.ZodEnum<["missing_required_output", "invalid_required_output", "missing_required_notes", "assessment_followup_required"]>;
|
|
90
90
|
}, "strip", z.ZodTypeAny, {
|
|
91
|
-
detail: "invalid_required_output" | "missing_required_output" | "assessment_followup_required" | "missing_required_notes";
|
|
92
91
|
category: "contract_violation";
|
|
92
|
+
detail: "missing_required_output" | "invalid_required_output" | "missing_required_notes" | "assessment_followup_required";
|
|
93
93
|
}, {
|
|
94
|
-
detail: "invalid_required_output" | "missing_required_output" | "assessment_followup_required" | "missing_required_notes";
|
|
95
94
|
category: "contract_violation";
|
|
95
|
+
detail: "missing_required_output" | "invalid_required_output" | "missing_required_notes" | "assessment_followup_required";
|
|
96
96
|
}>, z.ZodObject<{
|
|
97
97
|
category: z.ZodLiteral<"capability_missing">;
|
|
98
98
|
detail: z.ZodEnum<["required_capability_unavailable", "required_capability_unknown"]>;
|
|
99
99
|
}, "strip", z.ZodTypeAny, {
|
|
100
|
-
detail: "required_capability_unknown" | "required_capability_unavailable";
|
|
101
100
|
category: "capability_missing";
|
|
101
|
+
detail: "required_capability_unavailable" | "required_capability_unknown";
|
|
102
102
|
}, {
|
|
103
|
-
detail: "required_capability_unknown" | "required_capability_unavailable";
|
|
104
103
|
category: "capability_missing";
|
|
104
|
+
detail: "required_capability_unavailable" | "required_capability_unknown";
|
|
105
105
|
}>, z.ZodObject<{
|
|
106
106
|
category: z.ZodLiteral<"unexpected">;
|
|
107
107
|
detail: z.ZodEnum<["invariant_violation", "storage_corruption_detected", "evaluation_error"]>;
|
|
108
108
|
}, "strip", z.ZodTypeAny, {
|
|
109
|
-
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
110
109
|
category: "unexpected";
|
|
111
|
-
}, {
|
|
112
110
|
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
111
|
+
}, {
|
|
113
112
|
category: "unexpected";
|
|
113
|
+
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
114
114
|
}>]>;
|
|
115
115
|
summary: z.ZodString;
|
|
116
116
|
resolution: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
@@ -149,28 +149,28 @@ export declare const GapRecordedDataV1Schema: z.ZodObject<{
|
|
|
149
149
|
outputId: string;
|
|
150
150
|
}>]>, "many">>;
|
|
151
151
|
}, "strip", z.ZodTypeAny, {
|
|
152
|
-
|
|
152
|
+
gapId: string;
|
|
153
|
+
severity: "info" | "warning" | "critical";
|
|
153
154
|
reason: {
|
|
154
|
-
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
155
155
|
category: "user_only_dependency";
|
|
156
|
+
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
156
157
|
} | {
|
|
157
|
-
detail: "invalid_required_output" | "missing_required_output" | "assessment_followup_required" | "missing_required_notes";
|
|
158
158
|
category: "contract_violation";
|
|
159
|
+
detail: "missing_required_output" | "invalid_required_output" | "missing_required_notes" | "assessment_followup_required";
|
|
159
160
|
} | {
|
|
160
|
-
detail: "required_capability_unknown" | "required_capability_unavailable";
|
|
161
161
|
category: "capability_missing";
|
|
162
|
+
detail: "required_capability_unavailable" | "required_capability_unknown";
|
|
162
163
|
} | {
|
|
163
|
-
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
164
164
|
category: "unexpected";
|
|
165
|
+
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
165
166
|
};
|
|
166
|
-
|
|
167
|
+
summary: string;
|
|
167
168
|
resolution: {
|
|
168
169
|
kind: "unresolved";
|
|
169
170
|
} | {
|
|
170
171
|
kind: "resolves";
|
|
171
172
|
resolvesGapId: string;
|
|
172
173
|
};
|
|
173
|
-
gapId: string;
|
|
174
174
|
evidenceRefs?: ({
|
|
175
175
|
kind: "event";
|
|
176
176
|
eventId: string;
|
|
@@ -179,28 +179,28 @@ export declare const GapRecordedDataV1Schema: z.ZodObject<{
|
|
|
179
179
|
outputId: string;
|
|
180
180
|
})[] | undefined;
|
|
181
181
|
}, {
|
|
182
|
-
|
|
182
|
+
gapId: string;
|
|
183
|
+
severity: "info" | "warning" | "critical";
|
|
183
184
|
reason: {
|
|
184
|
-
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
185
185
|
category: "user_only_dependency";
|
|
186
|
+
detail: "needs_user_secret_or_token" | "needs_user_account_access" | "needs_user_artifact" | "needs_user_choice" | "needs_user_approval" | "needs_user_environment_action";
|
|
186
187
|
} | {
|
|
187
|
-
detail: "invalid_required_output" | "missing_required_output" | "assessment_followup_required" | "missing_required_notes";
|
|
188
188
|
category: "contract_violation";
|
|
189
|
+
detail: "missing_required_output" | "invalid_required_output" | "missing_required_notes" | "assessment_followup_required";
|
|
189
190
|
} | {
|
|
190
|
-
detail: "required_capability_unknown" | "required_capability_unavailable";
|
|
191
191
|
category: "capability_missing";
|
|
192
|
+
detail: "required_capability_unavailable" | "required_capability_unknown";
|
|
192
193
|
} | {
|
|
193
|
-
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
194
194
|
category: "unexpected";
|
|
195
|
+
detail: "invariant_violation" | "storage_corruption_detected" | "evaluation_error";
|
|
195
196
|
};
|
|
196
|
-
|
|
197
|
+
summary: string;
|
|
197
198
|
resolution: {
|
|
198
199
|
kind: "unresolved";
|
|
199
200
|
} | {
|
|
200
201
|
kind: "resolves";
|
|
201
202
|
resolvesGapId: string;
|
|
202
203
|
};
|
|
203
|
-
gapId: string;
|
|
204
204
|
evidenceRefs?: ({
|
|
205
205
|
kind: "event";
|
|
206
206
|
eventId: string;
|
|
@@ -11,23 +11,23 @@ export declare const ManifestRecordV1Schema: z.ZodDiscriminatedUnion<"kind", [z.
|
|
|
11
11
|
bytes: z.ZodNumber;
|
|
12
12
|
}, "strip", z.ZodTypeAny, {
|
|
13
13
|
kind: "segment_closed";
|
|
14
|
-
sessionId: string;
|
|
15
14
|
v: 1;
|
|
16
|
-
sha256: string;
|
|
17
15
|
manifestIndex: number;
|
|
16
|
+
sessionId: string;
|
|
18
17
|
firstEventIndex: number;
|
|
19
18
|
lastEventIndex: number;
|
|
20
19
|
segmentRelPath: string;
|
|
20
|
+
sha256: string;
|
|
21
21
|
bytes: number;
|
|
22
22
|
}, {
|
|
23
23
|
kind: "segment_closed";
|
|
24
|
-
sessionId: string;
|
|
25
24
|
v: 1;
|
|
26
|
-
sha256: string;
|
|
27
25
|
manifestIndex: number;
|
|
26
|
+
sessionId: string;
|
|
28
27
|
firstEventIndex: number;
|
|
29
28
|
lastEventIndex: number;
|
|
30
29
|
segmentRelPath: string;
|
|
30
|
+
sha256: string;
|
|
31
31
|
bytes: number;
|
|
32
32
|
}>, z.ZodObject<{
|
|
33
33
|
v: z.ZodLiteral<1>;
|
|
@@ -39,17 +39,17 @@ export declare const ManifestRecordV1Schema: z.ZodDiscriminatedUnion<"kind", [z.
|
|
|
39
39
|
createdByEventId: z.ZodString;
|
|
40
40
|
}, "strip", z.ZodTypeAny, {
|
|
41
41
|
kind: "snapshot_pinned";
|
|
42
|
-
sessionId: string;
|
|
43
42
|
v: 1;
|
|
44
43
|
manifestIndex: number;
|
|
44
|
+
sessionId: string;
|
|
45
45
|
eventIndex: number;
|
|
46
46
|
snapshotRef: string;
|
|
47
47
|
createdByEventId: string;
|
|
48
48
|
}, {
|
|
49
49
|
kind: "snapshot_pinned";
|
|
50
|
-
sessionId: string;
|
|
51
50
|
v: 1;
|
|
52
51
|
manifestIndex: number;
|
|
52
|
+
sessionId: string;
|
|
53
53
|
eventIndex: number;
|
|
54
54
|
snapshotRef: string;
|
|
55
55
|
createdByEventId: string;
|
|
@@ -90,6 +90,8 @@ export declare const NodeOutputAppendedDataV1Schema: z.ZodEffects<z.ZodObject<{
|
|
|
90
90
|
content?: unknown;
|
|
91
91
|
}>]>;
|
|
92
92
|
}, "strip", z.ZodTypeAny, {
|
|
93
|
+
outputId: string;
|
|
94
|
+
outputChannel: "recap" | "artifact";
|
|
93
95
|
payload: {
|
|
94
96
|
payloadKind: "notes";
|
|
95
97
|
notesMarkdown: string;
|
|
@@ -100,10 +102,10 @@ export declare const NodeOutputAppendedDataV1Schema: z.ZodEffects<z.ZodObject<{
|
|
|
100
102
|
byteLength: number;
|
|
101
103
|
content?: unknown;
|
|
102
104
|
};
|
|
103
|
-
outputId: string;
|
|
104
|
-
outputChannel: "recap" | "artifact";
|
|
105
105
|
supersedesOutputId?: string | undefined;
|
|
106
106
|
}, {
|
|
107
|
+
outputId: string;
|
|
108
|
+
outputChannel: "recap" | "artifact";
|
|
107
109
|
payload: {
|
|
108
110
|
payloadKind: "notes";
|
|
109
111
|
notesMarkdown: string;
|
|
@@ -114,10 +116,10 @@ export declare const NodeOutputAppendedDataV1Schema: z.ZodEffects<z.ZodObject<{
|
|
|
114
116
|
byteLength: number;
|
|
115
117
|
content?: unknown;
|
|
116
118
|
};
|
|
117
|
-
outputId: string;
|
|
118
|
-
outputChannel: "recap" | "artifact";
|
|
119
119
|
supersedesOutputId?: string | undefined;
|
|
120
120
|
}>, {
|
|
121
|
+
outputId: string;
|
|
122
|
+
outputChannel: "recap" | "artifact";
|
|
121
123
|
payload: {
|
|
122
124
|
payloadKind: "notes";
|
|
123
125
|
notesMarkdown: string;
|
|
@@ -128,10 +130,10 @@ export declare const NodeOutputAppendedDataV1Schema: z.ZodEffects<z.ZodObject<{
|
|
|
128
130
|
byteLength: number;
|
|
129
131
|
content?: unknown;
|
|
130
132
|
};
|
|
131
|
-
outputId: string;
|
|
132
|
-
outputChannel: "recap" | "artifact";
|
|
133
133
|
supersedesOutputId?: string | undefined;
|
|
134
134
|
}, {
|
|
135
|
+
outputId: string;
|
|
136
|
+
outputChannel: "recap" | "artifact";
|
|
135
137
|
payload: {
|
|
136
138
|
payloadKind: "notes";
|
|
137
139
|
notesMarkdown: string;
|
|
@@ -142,7 +144,5 @@ export declare const NodeOutputAppendedDataV1Schema: z.ZodEffects<z.ZodObject<{
|
|
|
142
144
|
byteLength: number;
|
|
143
145
|
content?: unknown;
|
|
144
146
|
};
|
|
145
|
-
outputId: string;
|
|
146
|
-
outputChannel: "recap" | "artifact";
|
|
147
147
|
supersedesOutputId?: string | undefined;
|
|
148
148
|
}>;
|
|
@@ -46,23 +46,23 @@ export declare const ValidationPerformedDataV1Schema: z.ZodObject<{
|
|
|
46
46
|
suggestions: readonly string[];
|
|
47
47
|
}>;
|
|
48
48
|
}, "strict", z.ZodTypeAny, {
|
|
49
|
+
validationId: string;
|
|
50
|
+
attemptId: string;
|
|
49
51
|
contractRef: string;
|
|
50
52
|
result: {
|
|
51
53
|
issues: readonly string[];
|
|
52
54
|
valid: boolean;
|
|
53
55
|
suggestions: readonly string[];
|
|
54
56
|
};
|
|
57
|
+
}, {
|
|
55
58
|
validationId: string;
|
|
56
59
|
attemptId: string;
|
|
57
|
-
}, {
|
|
58
60
|
contractRef: string;
|
|
59
61
|
result: {
|
|
60
62
|
issues: readonly string[];
|
|
61
63
|
valid: boolean;
|
|
62
64
|
suggestions: readonly string[];
|
|
63
65
|
};
|
|
64
|
-
validationId: string;
|
|
65
|
-
attemptId: string;
|
|
66
66
|
}>;
|
|
67
67
|
export type ValidationPerformedDataV1 = z.infer<typeof ValidationPerformedDataV1Schema>;
|
|
68
68
|
export type ValidationPerformedResultV1 = z.infer<typeof ValidationPerformedResultV1Schema>;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Design Candidates: Context Assembly Layer v1
|
|
2
|
+
|
|
3
|
+
*Generated during coding-task-workflow-agentic session | 2026-04-19*
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Problem Understanding
|
|
8
|
+
|
|
9
|
+
### Core Tensions
|
|
10
|
+
|
|
11
|
+
1. **Clean DI vs. pragmatic inline construction**: `LocalSessionSummaryProviderV2` is
|
|
12
|
+
wired through the full DI container in the MCP server process. The coordinator runs
|
|
13
|
+
as a CLI in a separate process. We cannot share the DI container. The coordinator
|
|
14
|
+
composition root (`src/cli-worktrain.ts`) must construct the provider inline.
|
|
15
|
+
|
|
16
|
+
2. **Neverthrow ResultAsync vs. custom Result**: Storage layer uses neverthrow. New
|
|
17
|
+
`src/context-assembly/` uses the repo's custom `Result<T,E>` (`kind: 'ok'|'err'`).
|
|
18
|
+
The bridge must happen at the `infra.ts` boundary.
|
|
19
|
+
|
|
20
|
+
3. **Partial failure semantics**: Each source (gitDiff, priorNotes) must fail
|
|
21
|
+
independently without aborting context assembly. `Promise.all()` is safe because
|
|
22
|
+
each source always resolves to a `Result` (never throws) when implemented correctly.
|
|
23
|
+
|
|
24
|
+
4. **Backward compatibility vs. interface evolution**: Adding `contextAssembler?` as
|
|
25
|
+
optional to `CoordinatorDeps` and optional 4th arg to `spawnSession` keeps all
|
|
26
|
+
existing tests and callers working without modification.
|
|
27
|
+
|
|
28
|
+
### Likely Seam
|
|
29
|
+
|
|
30
|
+
The real seam is `CoordinatorDeps.spawnSession`. The coordinator calls this to dispatch
|
|
31
|
+
a session. Context bundle assembly belongs immediately before this call.
|
|
32
|
+
|
|
33
|
+
### What Makes This Hard
|
|
34
|
+
|
|
35
|
+
- `LocalSessionEventLogStoreV2` requires `FileSystemPortV2` (a composite of 5 sub-ports
|
|
36
|
+
including write/fd ops). But `load()` only calls `readFileUtf8`. All write methods
|
|
37
|
+
can be stubs for read-only use, but this is a partial lie against the interface contract.
|
|
38
|
+
- `LocalDirectoryListingV2` takes `DirectoryListingOpsPortV2` (separate, simpler) --
|
|
39
|
+
NOT the full `FileSystemPortV2`. These are separate ports.
|
|
40
|
+
- Two Result type systems (neverthrow vs custom) must coexist without leaking across
|
|
41
|
+
the `infra.ts` boundary.
|
|
42
|
+
- `Sha256PortV2` is injected into `LocalSessionEventLogStoreV2` but only called in
|
|
43
|
+
`append()`, not `load()`. Safe to stub.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Philosophy Constraints
|
|
48
|
+
|
|
49
|
+
From `CLAUDE.md` (system-wide agent instructions):
|
|
50
|
+
|
|
51
|
+
- **Errors are data**: Use `Result<T,E>` values. No throwing for expected failures.
|
|
52
|
+
- **DI for boundaries**: Inject I/O effects; keep core logic testable.
|
|
53
|
+
- **Prefer fakes over mocks**: Tests use realistic substitutes (no `vi.mock()`).
|
|
54
|
+
- **YAGNI with discipline**: No speculative abstractions.
|
|
55
|
+
- **Architectural fixes over patches**: Reuse canonical parsing logic.
|
|
56
|
+
- **Immutability by default**: `readonly` on all fields.
|
|
57
|
+
|
|
58
|
+
**Potential conflict**: Creating write stubs on `FileSystemPortV2` is pragmatic but
|
|
59
|
+
technically lies about the contract. The pitch (R1 mitigation) explicitly endorses this
|
|
60
|
+
approach. Acceptable given the inline-construction pattern is already acknowledged as
|
|
61
|
+
a special case.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Impact Surface
|
|
66
|
+
|
|
67
|
+
- `src/coordinators/pr-review.ts`: `CoordinatorDeps.spawnSession` (add optional 4th arg),
|
|
68
|
+
`contextAssembler?` optional field. Backward-compatible.
|
|
69
|
+
- `src/daemon/workflow-runner.ts`: `buildSystemPrompt()` reads `trigger.context['assembledContextSummary']`.
|
|
70
|
+
No signature change needed -- `trigger.context` already typed as `Readonly<Record<string,unknown>>`.
|
|
71
|
+
- `src/cli-worktrain.ts`: `spawnSession` impl forwards 4th arg; `contextAssembler` wired.
|
|
72
|
+
- All existing tests that construct `CoordinatorDeps` fakes: safe (optional fields).
|
|
73
|
+
- No changes to `src/mcp/` or `src/v2/durable-core/`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Candidates
|
|
78
|
+
|
|
79
|
+
### Candidate A: Real local infra classes with minimal read-only shims (SELECTED)
|
|
80
|
+
|
|
81
|
+
**Summary**: Construct `LocalDataDirV2`, `LocalDirectoryListingV2`, and
|
|
82
|
+
`LocalSessionEventLogStoreV2` directly inside `infra.ts`. Create inline adapters:
|
|
83
|
+
- `DirectoryListingOpsPortV2`: wraps `fs.promises.readdir` + `stat` with `okAsync/errAsync`
|
|
84
|
+
- `FileSystemPortV2`: only `readFileUtf8` is real; write/fd methods throw 'context-assembly: write ops not supported'
|
|
85
|
+
- `Sha256PortV2`: stub that throws (never called during `load()`)
|
|
86
|
+
|
|
87
|
+
**Tensions resolved**: R1 (DI wiring scope). Uses battle-tested session loading and
|
|
88
|
+
projection code. Neverthrow/custom Result bridge stays at `infra.ts` boundary only.
|
|
89
|
+
|
|
90
|
+
**Tensions accepted**: Write stubs on `FileSystemPortV2` are a partial lie.
|
|
91
|
+
|
|
92
|
+
**Boundary solved at**: `src/context-assembly/infra.ts` -- coordinator composition root.
|
|
93
|
+
|
|
94
|
+
**Why this boundary is best-fit**: The coordinator is already a composition root
|
|
95
|
+
(`src/cli-worktrain.ts` wires all real deps). `infra.ts` is a thin adapter layer that
|
|
96
|
+
belongs exactly here.
|
|
97
|
+
|
|
98
|
+
**Failure mode**: If `LocalSessionEventLogStoreV2.load()` ever internally calls a write
|
|
99
|
+
method (unlikely but possible in future refactors), `infra.ts` would throw at runtime
|
|
100
|
+
rather than returning `err()`. This would be caught immediately by tests.
|
|
101
|
+
|
|
102
|
+
**Repo-pattern relationship**: Follows existing local infra pattern exactly.
|
|
103
|
+
`LocalDataDirV2`, `LocalDirectoryListingV2`, `LocalSessionEventLogStoreV2` are already
|
|
104
|
+
the canonical local implementations.
|
|
105
|
+
|
|
106
|
+
**Gains**: Reuses battle-tested store loading and projection code. No risk of format
|
|
107
|
+
divergence if session file structure changes.
|
|
108
|
+
|
|
109
|
+
**Losses**: Write stubs feel slightly dishonest. Minor complexity in `infra.ts`.
|
|
110
|
+
|
|
111
|
+
**Scope judgment**: Best-fit. Stays within `src/context-assembly/` with no v2 changes.
|
|
112
|
+
|
|
113
|
+
**Philosophy fit**: Honors DI for boundaries (composition root pattern), errors-as-data
|
|
114
|
+
(Result bridge), architectural fixes over patches (reuses canonical code). Minor
|
|
115
|
+
tension with YAGNI (write stubs) and interface honesty.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### Candidate B: Read session files directly with fs.promises
|
|
120
|
+
|
|
121
|
+
**Summary**: Skip the port hierarchy. Read `manifest.jsonl` and `events/*.jsonl` directly
|
|
122
|
+
using `fs.promises.readFile`. Parse manually or reuse just the schema validators.
|
|
123
|
+
|
|
124
|
+
**Tensions resolved**: R1 completely -- no DI wiring at all.
|
|
125
|
+
|
|
126
|
+
**Tensions accepted**: Duplicates session reading logic. Any change to session file
|
|
127
|
+
format requires updates in two places.
|
|
128
|
+
|
|
129
|
+
**Failure mode**: Silent breakage if session file format changes.
|
|
130
|
+
|
|
131
|
+
**Repo-pattern relationship**: Departs from established pattern (re-implements existing logic).
|
|
132
|
+
|
|
133
|
+
**Scope judgment**: Too narrow -- creates a fragile parallel implementation.
|
|
134
|
+
|
|
135
|
+
**Philosophy fit**: Conflicts with 'architectural fixes over patches'.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### Candidate C: Add createLocalSessionSummaryProvider() factory to v2 module
|
|
140
|
+
|
|
141
|
+
**Summary**: Add a `createLocalSessionSummaryProvider()` factory function to
|
|
142
|
+
`src/v2/infra/local/session-summary-provider/index.ts` that wires real deps and
|
|
143
|
+
returns a `SessionSummaryProviderPortV2`. Import and call from `infra.ts`.
|
|
144
|
+
|
|
145
|
+
**Tensions resolved**: Cleanest DI wiring.
|
|
146
|
+
|
|
147
|
+
**Tensions accepted**: Modifies an existing v2 infra module.
|
|
148
|
+
|
|
149
|
+
**Failure mode**: Entangles CLI-coordinator concerns into the storage layer.
|
|
150
|
+
|
|
151
|
+
**Scope judgment**: Slightly too broad -- modifies v2 module with no functional benefit
|
|
152
|
+
over Candidate A.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Comparison and Recommendation
|
|
157
|
+
|
|
158
|
+
**Recommendation: Candidate A**
|
|
159
|
+
|
|
160
|
+
All candidates agree on the high-level approach (4 new files, 3 modified files, exact
|
|
161
|
+
pitch interfaces). The only real design decision is `infra.ts` implementation.
|
|
162
|
+
|
|
163
|
+
Candidate A wins because:
|
|
164
|
+
1. It reuses battle-tested code for session reading and health projection
|
|
165
|
+
2. It stays within `src/context-assembly/` -- no v2 module changes
|
|
166
|
+
3. The write stubs are safe: `load()` provably does not call write methods
|
|
167
|
+
4. The pitch explicitly endorses this approach as R1 mitigation
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Self-Critique
|
|
172
|
+
|
|
173
|
+
**Strongest counter-argument**: The write stubs on `FileSystemPortV2` create a silent
|
|
174
|
+
contract violation. If a future `load()` implementation adds a write call internally,
|
|
175
|
+
the error would surface at runtime (throw) rather than compile-time.
|
|
176
|
+
|
|
177
|
+
**Narrower option that lost**: Candidate B (raw fs.promises). Lost because it duplicates
|
|
178
|
+
canonical parsing logic and diverges from existing patterns.
|
|
179
|
+
|
|
180
|
+
**Broader option and evidence required**: Candidate C would be justified if multiple
|
|
181
|
+
other CLI commands needed access to session summaries -- adding a factory to the v2
|
|
182
|
+
module would be worth the scope increase. No evidence of this need in v1.
|
|
183
|
+
|
|
184
|
+
**Assumption that would invalidate this design**: If `LocalSessionEventLogStoreV2.load()`
|
|
185
|
+
internally calls any of the stubbed write methods (currently provably false by code
|
|
186
|
+
inspection of `loadImpl()`, `loadValidatedPrefixImpl()`, `readManifestOrEmpty()`).
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Open Questions for Main Agent
|
|
191
|
+
|
|
192
|
+
1. Verify: does `Sha256PortV2` have a simple interface that can be satisfied with a
|
|
193
|
+
1-line `node:crypto` implementation? (If so, use real impl rather than throw stub.)
|
|
194
|
+
2. Verify: does the `FileSystemPortV2` shim need anything beyond `readFileUtf8` for
|
|
195
|
+
the `load()` path? (Current analysis: no -- `readManifestOrEmpty` and segment loading
|
|
196
|
+
both use `readFileUtf8` only.)
|
|
197
|
+
3. The re-review spawn (line ~1265 in pr-review.ts) should forward `spawnContext` from
|
|
198
|
+
the outer scope per the pitch. Confirm this is the correct approach (vs. reassembling
|
|
199
|
+
context for re-reviews).
|