( ctx: CliFailureContext, requestId: string, opts?: AssertContextIntegrityOptions, )
| 134 | * trace the bad envelope back. |
| 135 | */ |
| 136 | export function assertContextIntegrity( |
| 137 | ctx: CliFailureContext, |
| 138 | requestId: string, |
| 139 | opts?: AssertContextIntegrityOptions, |
| 140 | ): void { |
| 141 | if (opts?.requireRunId && !ctx.result.runIdIfAvailable) { |
| 142 | throw bundleIntegrityError( |
| 143 | 'run_id_missing', |
| 144 | 'meta.runId (result.runIdIfAvailable) is required for run-scoped bundles but is null or missing', |
| 145 | requestId, |
| 146 | ); |
| 147 | } |
| 148 | if (ctx.snapshotId !== ctx.result.snapshotId) { |
| 149 | throw bundleIntegrityError( |
| 150 | 'snapshot_id_mismatch', |
| 151 | `Bundle integrity check failed: expected snapshotId=${ctx.snapshotId} got snapshotId=${ctx.result.snapshotId}`, |
| 152 | requestId, |
| 153 | { expectedSnapshotId: ctx.snapshotId, actualSnapshotId: ctx.result.snapshotId }, |
| 154 | ); |
| 155 | } |
| 156 | // Per codex round-1 P2: every embedded testId must equal ctx.testId. |
| 157 | // §6.X duplicates `testId` in `result`, `code`, and each step so a |
| 158 | // bundle stitched together from rows of two different tests is |
| 159 | // detectable without external state. Without this gate, an agent |
| 160 | // could open `meta.json` for `test_A`, edit the file the bundle |
| 161 | // claims is its code, and have the edit actually target `test_B`'s |
| 162 | // source — exactly the cross-test contamination the failure bundle |
| 163 | // exists to prevent. |
| 164 | if (ctx.result.testId !== ctx.testId) { |
| 165 | throw bundleIntegrityError( |
| 166 | 'test_id_mismatch', |
| 167 | `Bundle integrity check failed: expected testId=${ctx.testId} got testId=${ctx.result.testId} (in result)`, |
| 168 | requestId, |
| 169 | { expectedTestId: ctx.testId, actualTestId: ctx.result.testId }, |
| 170 | ); |
| 171 | } |
| 172 | if (ctx.code.testId !== ctx.testId) { |
| 173 | throw bundleIntegrityError( |
| 174 | 'test_id_mismatch', |
| 175 | `Bundle integrity check failed: expected testId=${ctx.testId} got testId=${ctx.code.testId} (in code)`, |
| 176 | requestId, |
| 177 | { expectedTestId: ctx.testId, actualTestId: ctx.code.testId }, |
| 178 | ); |
| 179 | } |
| 180 | for (const step of ctx.steps) { |
| 181 | if (step.testId !== ctx.testId) { |
| 182 | throw bundleIntegrityError( |
| 183 | 'test_id_mismatch', |
| 184 | `Bundle integrity check failed: expected testId=${ctx.testId} got testId=${step.testId} (in step[${step.stepIndex}])`, |
| 185 | requestId, |
| 186 | { expectedTestId: ctx.testId, actualTestId: step.testId }, |
| 187 | ); |
| 188 | } |
| 189 | } |
| 190 | // §6.7: code is "version pinned to result.codeVersion." If both |
| 191 | // sides are non-null and disagree, the bundle stitched code from |
| 192 | // one version with a result from another — exactly the drift case |
| 193 | // the failure bundle exists to prevent. Both-null is fine (the M2 |
no test coverage detected