immune-brain 2.8.2
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/README.md +239 -0
- package/README.zh-CN.md +239 -0
- package/package.json +84 -0
- package/plugins/immune-brain/.pi-extension/imm-canary-enroll.ts +666 -0
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +1622 -0
- package/plugins/immune-brain/.pi-extension/package.json +11 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-assurance-progression.ts +711 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-assurance.ts +154 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-interaction.ts +349 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-invocations.ts +90 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-native-review.ts +214 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-qa-findings.ts +50 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-review-bundle.ts +262 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-tool-failure.ts +28 -0
- package/plugins/immune-brain/.pi-extension/pi-canary-verification.ts +237 -0
- package/plugins/immune-brain/.pi-extension/runtime-stub.ts +414 -0
- package/plugins/immune-brain/.pi-extension/tsconfig.json +20 -0
- package/plugins/immune-brain/bin/imm-kernel +4 -0
- package/plugins/immune-brain/bin/imm-plan +4 -0
- package/plugins/immune-brain/bin/imm-pr-diag +230 -0
- package/plugins/immune-brain/bin/imm-retire-stale-wrapper +4 -0
- package/plugins/immune-brain/bin/imm-retired +4 -0
- package/plugins/immune-brain/bin/imm-tracker +4 -0
- package/plugins/immune-brain/dist/BASELINE.md +138 -0
- package/plugins/immune-brain/dist/docs/reference/HANDOFF-template.md +122 -0
- package/plugins/immune-brain/dist/docs/reference/design-contract-audit-rubric.md +149 -0
- package/plugins/immune-brain/dist/docs/reference/design-contract-review-checklist.md +55 -0
- package/plugins/immune-brain/dist/docs/reference/i18n-review-checklist.md +110 -0
- package/plugins/immune-brain/dist/docs/reference/immune-brain-config.md +52 -0
- package/plugins/immune-brain/dist/docs/reference/planning-artifact-retention.md +94 -0
- package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +44 -0
- package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +105 -0
- package/plugins/immune-brain/dist/docs/reference/ux-heuristic-checklist.md +131 -0
- package/plugins/immune-brain/dist/imm-brainstorm.md +140 -0
- package/plugins/immune-brain/dist/imm-doc-prune.md +137 -0
- package/plugins/immune-brain/dist/imm-loop.md +158 -0
- package/plugins/immune-brain/dist/imm-planner.md +387 -0
- package/plugins/immune-brain/dist/imm-pr-fix.md +71 -0
- package/plugins/immune-brain/dist/registry.yaml +49 -0
- package/plugins/immune-brain/dist/role-prompts/advisory-reviewer.md +16 -0
- package/plugins/immune-brain/dist/role-prompts/arch-explorer.md +14 -0
- package/plugins/immune-brain/dist/role-prompts/code-review.md +15 -0
- package/plugins/immune-brain/dist/role-prompts/compounder.md +20 -0
- package/plugins/immune-brain/dist/role-prompts/executor.md +13 -0
- package/plugins/immune-brain/dist/role-prompts/pr-fix.md +81 -0
- package/plugins/immune-brain/dist/role-prompts/qa.md +23 -0
- package/plugins/immune-brain/dist/role-prompts/test-fixer.md +3 -0
- package/plugins/immune-brain/dist/role-prompts/ui-review.md +14 -0
- package/plugins/immune-brain/runtime/authority_commit_receipts.ts +716 -0
- package/plugins/immune-brain/runtime/canonical_json.ts +19 -0
- package/plugins/immune-brain/runtime/commands/kernel.ts +1160 -0
- package/plugins/immune-brain/runtime/github_issue_tracker.ts +1009 -0
- package/plugins/immune-brain/runtime/kernel/application.ts +300 -0
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +284 -0
- package/plugins/immune-brain/runtime/kernel/authority_port.ts +208 -0
- package/plugins/immune-brain/runtime/kernel/automatic_observations.ts +451 -0
- package/plugins/immune-brain/runtime/kernel/backend_claim.ts +197 -0
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +507 -0
- package/plugins/immune-brain/runtime/kernel/canary_eligibility.ts +73 -0
- package/plugins/immune-brain/runtime/kernel/completion.ts +160 -0
- package/plugins/immune-brain/runtime/kernel/enrollment.ts +194 -0
- package/plugins/immune-brain/runtime/kernel/enrollment_authority.ts +123 -0
- package/plugins/immune-brain/runtime/kernel/index.ts +29 -0
- package/plugins/immune-brain/runtime/kernel/intent.ts +563 -0
- package/plugins/immune-brain/runtime/kernel/intent_token_registry.ts +80 -0
- package/plugins/immune-brain/runtime/kernel/legacy.ts +299 -0
- package/plugins/immune-brain/runtime/kernel/legacy_audit.ts +153 -0
- package/plugins/immune-brain/runtime/kernel/observation.ts +395 -0
- package/plugins/immune-brain/runtime/kernel/pi_canary_prepare.ts +169 -0
- package/plugins/immune-brain/runtime/kernel/readiness.ts +282 -0
- package/plugins/immune-brain/runtime/kernel/readiness_evidence.ts +132 -0
- package/plugins/immune-brain/runtime/kernel/reducer.ts +624 -0
- package/plugins/immune-brain/runtime/kernel/storage.ts +1780 -0
- package/plugins/immune-brain/runtime/kernel/storage_layout_migration.ts +791 -0
- package/plugins/immune-brain/runtime/kernel/storage_paths.ts +492 -0
- package/plugins/immune-brain/runtime/kernel/types.ts +295 -0
- package/plugins/immune-brain/runtime/kernel/validation.ts +963 -0
- package/plugins/immune-brain/runtime/loop_contract.ts +362 -0
- package/plugins/immune-brain/runtime/managed_task_routing_policy.ts +462 -0
- package/plugins/immune-brain/runtime/plan_core.ts +1053 -0
- package/plugins/immune-brain/runtime/prompts/advisory-reviewer.md +16 -0
- package/plugins/immune-brain/runtime/prompts/arch-explorer.md +14 -0
- package/plugins/immune-brain/runtime/prompts/code-review.md +15 -0
- package/plugins/immune-brain/runtime/prompts/compounder.md +20 -0
- package/plugins/immune-brain/runtime/prompts/executor.md +13 -0
- package/plugins/immune-brain/runtime/prompts/pr-fix.md +81 -0
- package/plugins/immune-brain/runtime/prompts/qa.md +23 -0
- package/plugins/immune-brain/runtime/prompts/test-fixer.md +3 -0
- package/plugins/immune-brain/runtime/prompts/ui-review.md +14 -0
- package/plugins/immune-brain/runtime/role_prompt_bridge.ts +160 -0
- package/plugins/immune-brain/runtime/v4_runtime.ts +295 -0
- package/plugins/immune-brain/runtime/verification_descriptor.ts +162 -0
- package/plugins/immune-brain/runtime/workspace_scope.ts +623 -0
- package/plugins/immune-brain/skills/.ignore +1 -0
- package/plugins/immune-brain/skills/BASELINE.md +138 -0
- package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +66 -0
- package/plugins/immune-brain/skills/imm-doc-prune/SKILL.md +11 -0
- package/plugins/immune-brain/skills/imm-loop/SKILL.md +52 -0
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +221 -0
- package/plugins/immune-brain/skills/imm-pr-fix/SKILL.md +10 -0
- package/plugins/immune-brain/skills/registry.yaml +49 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validation for `imm-loop` subagent output.
|
|
3
|
+
*
|
|
4
|
+
* The loop parent collects QA and reviewer decisions from isolated children.
|
|
5
|
+
* Those decisions are untrusted input: they arrive as free-form JSON produced
|
|
6
|
+
* by a model and are then converted into State Ledger writes. This module owns
|
|
7
|
+
* the machine-checkable half of that contract so the loop skill does not have
|
|
8
|
+
* to re-derive it in prose.
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
buildRoleDelegationPacket,
|
|
12
|
+
type RoleDelegationContext,
|
|
13
|
+
type RoleDelegationPacket,
|
|
14
|
+
type InternalRole,
|
|
15
|
+
} from "./role_prompt_bridge";
|
|
16
|
+
|
|
17
|
+
export type LoopRole = InternalRole;
|
|
18
|
+
|
|
19
|
+
export type LoopRoleContext = RoleDelegationPacket;
|
|
20
|
+
|
|
21
|
+
export type LoopRouteOwnership =
|
|
22
|
+
| "plan"
|
|
23
|
+
| "kernel"
|
|
24
|
+
| "brainstorm"
|
|
25
|
+
| "planner"
|
|
26
|
+
| "loop";
|
|
27
|
+
export type LoopRouteTarget =
|
|
28
|
+
| "step"
|
|
29
|
+
| "test-repair"
|
|
30
|
+
| "pr-repair"
|
|
31
|
+
| "architecture-exploration"
|
|
32
|
+
| "advisory-review"
|
|
33
|
+
| "compounder";
|
|
34
|
+
export type LoopRouteNext =
|
|
35
|
+
| "executor"
|
|
36
|
+
| "test-fixer"
|
|
37
|
+
| "pr-fix"
|
|
38
|
+
| "arch-explorer"
|
|
39
|
+
| "advisory-reviewer"
|
|
40
|
+
| "compounder"
|
|
41
|
+
| "imm_kernel_canary"
|
|
42
|
+
| "imm-planner";
|
|
43
|
+
|
|
44
|
+
export interface LoopRoute {
|
|
45
|
+
entry: "imm-loop";
|
|
46
|
+
next: LoopRouteNext;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Project the authoritative task owner and bounded target into Loop's next
|
|
51
|
+
* authority. This is routing only; State Ledger and Kernel remain authoritative.
|
|
52
|
+
*/
|
|
53
|
+
export function resolveLoopRoute(input: {
|
|
54
|
+
ownership: LoopRouteOwnership;
|
|
55
|
+
target: LoopRouteTarget;
|
|
56
|
+
scope_expansion?: boolean;
|
|
57
|
+
}): LoopRoute {
|
|
58
|
+
if (input.scope_expansion) return { entry: "imm-loop", next: "imm-planner" };
|
|
59
|
+
if (
|
|
60
|
+
input.target === "architecture-exploration" &&
|
|
61
|
+
(input.ownership === "brainstorm" || input.ownership === "planner")
|
|
62
|
+
) {
|
|
63
|
+
return { entry: "imm-loop", next: "arch-explorer" };
|
|
64
|
+
}
|
|
65
|
+
if (
|
|
66
|
+
input.target === "advisory-review" &&
|
|
67
|
+
(input.ownership === "brainstorm" || input.ownership === "planner")
|
|
68
|
+
) {
|
|
69
|
+
return { entry: "imm-loop", next: "advisory-reviewer" };
|
|
70
|
+
}
|
|
71
|
+
if (input.target === "compounder") {
|
|
72
|
+
if (input.ownership !== "loop") {
|
|
73
|
+
throw new Error("Compounder routing requires Loop ownership");
|
|
74
|
+
}
|
|
75
|
+
return { entry: "imm-loop", next: "compounder" };
|
|
76
|
+
}
|
|
77
|
+
if (input.ownership === "kernel") {
|
|
78
|
+
return { entry: "imm-loop", next: "imm_kernel_canary" };
|
|
79
|
+
}
|
|
80
|
+
const nextByTarget: Record<"step" | "test-repair" | "pr-repair", LoopRouteNext> = {
|
|
81
|
+
step: "executor",
|
|
82
|
+
"test-repair": "test-fixer",
|
|
83
|
+
"pr-repair": "pr-fix",
|
|
84
|
+
};
|
|
85
|
+
if (!(input.target in nextByTarget)) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`${input.ownership} ownership cannot route ${input.target} through Loop`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
entry: "imm-loop",
|
|
92
|
+
next: nextByTarget[
|
|
93
|
+
input.target as "step" | "test-repair" | "pr-repair"
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type LoopAction =
|
|
99
|
+
| { entry: "imm-loop"; next: "executor"; context: LoopRoleContext }
|
|
100
|
+
| {
|
|
101
|
+
entry: "imm-loop";
|
|
102
|
+
next: "test-fixer" | "pr-fix" | "arch-explorer" | "advisory-reviewer" | "compounder";
|
|
103
|
+
dispatch: LoopRoleDispatch;
|
|
104
|
+
}
|
|
105
|
+
| {
|
|
106
|
+
entry: "imm-loop";
|
|
107
|
+
next: "imm_kernel_canary";
|
|
108
|
+
tool: {
|
|
109
|
+
name: "imm_kernel_canary";
|
|
110
|
+
operation:
|
|
111
|
+
| "status"
|
|
112
|
+
| "record_evidence"
|
|
113
|
+
| "advance_assurance"
|
|
114
|
+
| "submit_review"
|
|
115
|
+
| "request_authorization"
|
|
116
|
+
| "complete";
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
| { entry: "imm-loop"; next: "imm-planner"; reason: "scope_expansion" }
|
|
120
|
+
| { entry: "imm-loop"; next: "none"; reason: "no_reusable_learning" };
|
|
121
|
+
|
|
122
|
+
function hasReusableLearningEvidence(context: RoleDelegationContext): boolean {
|
|
123
|
+
if (
|
|
124
|
+
context.workflow_phase !== "complete" ||
|
|
125
|
+
context.assurance_complete !== true ||
|
|
126
|
+
context.required_reviews_complete !== true
|
|
127
|
+
) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (!Array.isArray(context.closed_steps)) return false;
|
|
131
|
+
return context.closed_steps.some((rawStep) => {
|
|
132
|
+
const step = asRecord(rawStep);
|
|
133
|
+
if (step.state !== "closed") return false;
|
|
134
|
+
const evidence = asRecord(step.learning_evidence);
|
|
135
|
+
return (
|
|
136
|
+
evidence.reusable === true &&
|
|
137
|
+
nonEmptyString(evidence.summary) !== null &&
|
|
138
|
+
nonEmptyString(evidence.evidence_ref) !== null
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function buildLoopAction(input: {
|
|
144
|
+
ownership: LoopRouteOwnership;
|
|
145
|
+
target: LoopRouteTarget;
|
|
146
|
+
context?: RoleDelegationContext;
|
|
147
|
+
scope_expansion?: boolean;
|
|
148
|
+
kernel_operation?: Extract<LoopAction, { next: "imm_kernel_canary" }>["tool"]["operation"];
|
|
149
|
+
}): LoopAction {
|
|
150
|
+
const route = resolveLoopRoute(input);
|
|
151
|
+
if (route.next === "imm-planner") {
|
|
152
|
+
return { entry: "imm-loop", next: "imm-planner", reason: "scope_expansion" };
|
|
153
|
+
}
|
|
154
|
+
if (route.next === "compounder") {
|
|
155
|
+
if (!input.context || !hasReusableLearningEvidence(input.context)) {
|
|
156
|
+
return {
|
|
157
|
+
entry: "imm-loop",
|
|
158
|
+
next: "none",
|
|
159
|
+
reason: "no_reusable_learning",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
entry: "imm-loop",
|
|
164
|
+
next: "compounder",
|
|
165
|
+
dispatch: buildLoopRoleDispatch({ role: "compounder", context: input.context }),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (!input.context) throw new Error(`Loop ${route.next} action requires context`);
|
|
169
|
+
if (route.next === "executor") {
|
|
170
|
+
return {
|
|
171
|
+
entry: "imm-loop",
|
|
172
|
+
next: "executor",
|
|
173
|
+
context: buildRoleDelegationPacket({ role: "executor", context: input.context }),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (route.next === "test-fixer") {
|
|
177
|
+
return {
|
|
178
|
+
entry: "imm-loop",
|
|
179
|
+
next: "test-fixer",
|
|
180
|
+
dispatch: buildLoopRoleDispatch({ role: "test-fixer", context: input.context }),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (route.next === "pr-fix") {
|
|
184
|
+
return {
|
|
185
|
+
entry: "imm-loop",
|
|
186
|
+
next: "pr-fix",
|
|
187
|
+
dispatch: buildLoopRoleDispatch({ role: "pr-fix", context: input.context }),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (route.next === "arch-explorer") {
|
|
191
|
+
return {
|
|
192
|
+
entry: "imm-loop",
|
|
193
|
+
next: "arch-explorer",
|
|
194
|
+
dispatch: buildLoopRoleDispatch({ role: "arch-explorer", context: input.context }),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (route.next === "advisory-reviewer") {
|
|
198
|
+
if (!nonEmptyString(input.context.lens)) {
|
|
199
|
+
throw new Error("advisory review requires an explicit lens");
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
entry: "imm-loop",
|
|
203
|
+
next: "advisory-reviewer",
|
|
204
|
+
dispatch: buildLoopRoleDispatch({ role: "advisory-reviewer", context: input.context }),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
entry: "imm-loop",
|
|
209
|
+
next: "imm_kernel_canary",
|
|
210
|
+
tool: {
|
|
211
|
+
name: "imm_kernel_canary",
|
|
212
|
+
operation: input.kernel_operation ?? "status",
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface LoopRoleDispatch {
|
|
218
|
+
packet: RoleDelegationPacket;
|
|
219
|
+
call: {
|
|
220
|
+
subagent_type: "general-purpose";
|
|
221
|
+
description: string;
|
|
222
|
+
prompt: string;
|
|
223
|
+
inherit_context: false;
|
|
224
|
+
isolated: true;
|
|
225
|
+
run_in_background: false;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function buildLoopRoleDispatch(input: {
|
|
230
|
+
role: LoopRole;
|
|
231
|
+
context: RoleDelegationContext;
|
|
232
|
+
description?: string;
|
|
233
|
+
}): LoopRoleDispatch {
|
|
234
|
+
const packet = buildRoleDelegationPacket(input);
|
|
235
|
+
return {
|
|
236
|
+
packet,
|
|
237
|
+
call: {
|
|
238
|
+
subagent_type: "general-purpose",
|
|
239
|
+
description: input.description ?? `${input.role} internal role`,
|
|
240
|
+
prompt: packet.prompt,
|
|
241
|
+
inherit_context: false,
|
|
242
|
+
isolated: true,
|
|
243
|
+
run_in_background: false,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function asRecord(raw: unknown): Record<string, unknown> {
|
|
249
|
+
return raw && typeof raw === "object" && !Array.isArray(raw)
|
|
250
|
+
? (raw as Record<string, unknown>)
|
|
251
|
+
: {};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function nonEmptyString(value: unknown): string | null {
|
|
255
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Any field the child invents is treated as authority-widening: the loop parent
|
|
260
|
+
* turns this payload into a Ledger write, so silently ignoring extras would let
|
|
261
|
+
* a child smuggle in gate, successor, or scope intent the contract never grants.
|
|
262
|
+
*/
|
|
263
|
+
function collectUnknownFields(
|
|
264
|
+
record: Record<string, unknown>,
|
|
265
|
+
allowed: string[],
|
|
266
|
+
): string[] {
|
|
267
|
+
return Object.keys(record)
|
|
268
|
+
.filter((key) => !allowed.includes(key))
|
|
269
|
+
.map((key) => `unknown field: ${key}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface InternalRoleOutputRejection {
|
|
273
|
+
valid: false;
|
|
274
|
+
role: "arch-explorer" | "advisory-reviewer";
|
|
275
|
+
violations: string[];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export interface ArchitectureExplorerOutputAcceptance {
|
|
279
|
+
valid: true;
|
|
280
|
+
role: "arch-explorer";
|
|
281
|
+
candidates: unknown[];
|
|
282
|
+
evidence: unknown[];
|
|
283
|
+
risks: unknown[];
|
|
284
|
+
open_questions: unknown[];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface AdvisoryReviewerOutputAcceptance {
|
|
288
|
+
valid: true;
|
|
289
|
+
role: "advisory-reviewer";
|
|
290
|
+
recommendations: unknown[];
|
|
291
|
+
disagreements: unknown[];
|
|
292
|
+
open_questions: unknown[];
|
|
293
|
+
blockers: unknown[];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export type ArchitectureExplorerOutputValidation =
|
|
297
|
+
| ArchitectureExplorerOutputAcceptance
|
|
298
|
+
| InternalRoleOutputRejection;
|
|
299
|
+
export type AdvisoryReviewerOutputValidation =
|
|
300
|
+
| AdvisoryReviewerOutputAcceptance
|
|
301
|
+
| InternalRoleOutputRejection;
|
|
302
|
+
|
|
303
|
+
type StructuredRoleOutputResult =
|
|
304
|
+
| InternalRoleOutputRejection
|
|
305
|
+
| { valid: true; values: Record<string, unknown[]> };
|
|
306
|
+
|
|
307
|
+
function normalizeStructuredRoleOutput(
|
|
308
|
+
role: "arch-explorer" | "advisory-reviewer",
|
|
309
|
+
raw: unknown,
|
|
310
|
+
fields: string[],
|
|
311
|
+
): StructuredRoleOutputResult {
|
|
312
|
+
const record = asRecord(raw);
|
|
313
|
+
const violations = collectUnknownFields(record, fields);
|
|
314
|
+
const values: Record<string, unknown[]> = {};
|
|
315
|
+
for (const field of fields) {
|
|
316
|
+
if (!Array.isArray(record[field])) {
|
|
317
|
+
violations.push(`${field} must be an array`);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
values[field] = record[field] as unknown[];
|
|
321
|
+
}
|
|
322
|
+
if (violations.length > 0) return { valid: false, role, violations };
|
|
323
|
+
return { valid: true, values };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function normalizeArchitectureExplorerOutput(
|
|
327
|
+
raw: unknown,
|
|
328
|
+
): ArchitectureExplorerOutputValidation {
|
|
329
|
+
const values = normalizeStructuredRoleOutput(
|
|
330
|
+
"arch-explorer",
|
|
331
|
+
raw,
|
|
332
|
+
["candidates", "evidence", "risks", "open_questions"],
|
|
333
|
+
);
|
|
334
|
+
if (!values.valid) return values;
|
|
335
|
+
return {
|
|
336
|
+
valid: true,
|
|
337
|
+
role: "arch-explorer",
|
|
338
|
+
candidates: values.values.candidates,
|
|
339
|
+
evidence: values.values.evidence,
|
|
340
|
+
risks: values.values.risks,
|
|
341
|
+
open_questions: values.values.open_questions,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function normalizeAdvisoryReviewerOutput(
|
|
346
|
+
raw: unknown,
|
|
347
|
+
): AdvisoryReviewerOutputValidation {
|
|
348
|
+
const values = normalizeStructuredRoleOutput(
|
|
349
|
+
"advisory-reviewer",
|
|
350
|
+
raw,
|
|
351
|
+
["recommendations", "disagreements", "open_questions", "blockers"],
|
|
352
|
+
);
|
|
353
|
+
if (!values.valid) return values;
|
|
354
|
+
return {
|
|
355
|
+
valid: true,
|
|
356
|
+
role: "advisory-reviewer",
|
|
357
|
+
recommendations: values.values.recommendations,
|
|
358
|
+
disagreements: values.values.disagreements,
|
|
359
|
+
open_questions: values.values.open_questions,
|
|
360
|
+
blockers: values.values.blockers,
|
|
361
|
+
};
|
|
362
|
+
}
|