* Type-narrow + validate a parsed `{ planSteps: [...] }` envelope. * The file is expected to be a single JSON object with a `planSteps` * array property; we tolerate the bare array form too (some agents * may emit `[...]` directly) so the surface forgives a common * mistake without surprising ca
(parsed: unknown)
| 1134 | * mistake without surprising callers. |
| 1135 | */ |
| 1136 | function assertPlanStepsShape(parsed: unknown): CliPlanStep[] { |
| 1137 | let stepsRaw: unknown; |
| 1138 | if (Array.isArray(parsed)) { |
| 1139 | stepsRaw = parsed; |
| 1140 | } else if (typeof parsed === 'object' && parsed !== null) { |
| 1141 | stepsRaw = (parsed as Record<string, unknown>).planSteps; |
| 1142 | } else { |
| 1143 | throw localValidationError('steps', 'must be a JSON object with a `planSteps` array'); |
| 1144 | } |
| 1145 | |
| 1146 | requireArrayLength('planSteps', stepsRaw, { min: 1, max: MAX_PLAN_STEPS, itemNoun: 'step' }); |
| 1147 | |
| 1148 | for (let i = 0; i < stepsRaw.length; i += 1) { |
| 1149 | const step = stepsRaw[i]; |
| 1150 | if (typeof step !== 'object' || step === null || Array.isArray(step)) { |
| 1151 | throw localValidationError(`planSteps[${i}]`, 'must be an object', undefined, 'field'); |
| 1152 | } |
| 1153 | const s = step as Record<string, unknown>; |
| 1154 | requireEnum(`planSteps[${i}].type`, s.type, PLAN_STEP_TYPES); |
| 1155 | requireString(`planSteps[${i}].description`, s.description); |
| 1156 | } |
| 1157 | |
| 1158 | return stepsRaw as CliPlanStep[]; |
| 1159 | } |
| 1160 | |
| 1161 | /** |
| 1162 | * §6.X / M3.2 piece-3 `UpdateTestResponse` shape. `updatedFields` is |
no test coverage detected