Skip to content

Commit 8c648cd

Browse files
committed
Gate runtime scenario retry attempts across demo policy and release checks
1 parent 24232a4 commit 8c648cd

9 files changed

Lines changed: 149 additions & 3 deletions

.kiro/specs/multimodal-agents/progress.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@
131131
100. Synced fast-retry workflow docs and alignment tests: updated `README.md` + `docs/challenge-demo-runbook.md`, extended `tests/unit/release-script-alias-alignment.test.ts`, and added `tests/unit/demo-fast-retry-docs-alignment.test.ts` to keep alias and documentation consistent.
132132
101. Expanded transient scenario retry coverage in `scripts/demo-e2e.ps1`: `runtime.lifecycle.endpoints` and `runtime.metrics.endpoints` now use bounded transient retries (`ScenarioRetryMaxAttempts`/`ScenarioRetryBackoffMs`) in addition to operator/visual flows, reducing false-negative demo failures from short-lived service/network blips.
133133
102. Updated alignment guard `tests/unit/demo-scenario-retry-alignment.test.ts` to enforce retry configuration across operator/ui/runtime flaky scenarios and prevent future drift in demo retry policy wiring.
134+
103. Added runtime scenario attempt KPIs to demo summary (`runtimeLifecycleScenarioAttempts`, `runtimeMetricsScenarioAttempts`) in `scripts/demo-e2e.ps1`, so runtime retry behavior is visible in judge-facing artifacts and release logs.
135+
104. Extended scenario retry gates across policy/release/docs/tests: `scripts/demo-e2e-policy-check.mjs` and `scripts/release-readiness.ps1` now enforce runtime attempt bounds (`1..options.scenarioRetryMaxAttempts`), runbook evidence was updated, and unit coverage expanded in `tests/unit/demo-e2e-policy-check.test.ts`, `tests/unit/release-readiness.test.ts`, `tests/unit/demo-scenario-retry-alignment.test.ts`, and `tests/unit/runbook-release-alignment.test.ts`.
134136

135137
## Current Focus Queue
136138

docs/challenge-demo-runbook.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ The release gate (`scripts/release-readiness.ps1`) hard-fails when these evidenc
135135
- strict final run (`npm run verify:release:strict`) enforces `kpi.scenarioRetriesUsedCount = 0`
136136
- `kpi.uiVisualTestingScenarioAttempts <= options.scenarioRetryMaxAttempts`
137137
- `kpi.operatorConsoleActionsScenarioAttempts <= options.scenarioRetryMaxAttempts`
138+
- `kpi.runtimeLifecycleScenarioAttempts <= options.scenarioRetryMaxAttempts`
139+
- `kpi.runtimeMetricsScenarioAttempts <= options.scenarioRetryMaxAttempts`
138140
- `kpi.scenarioRetryableFailuresTotal >= 0`
139141
- Perf-load anti-drift (from `artifacts/perf-load/policy-check.json`):
140142
- required check items include:

scripts/demo-e2e-policy-check.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,26 @@ async function main() {
832832
kpis.operatorConsoleActionsScenarioAttempts,
833833
"1..options.scenarioRetryMaxAttempts",
834834
);
835+
const runtimeLifecycleScenarioAttempts = toNumber(kpis.runtimeLifecycleScenarioAttempts);
836+
addCheck(
837+
"kpi.runtimeLifecycleScenarioAttempts",
838+
Number.isFinite(runtimeLifecycleScenarioAttempts) &&
839+
runtimeLifecycleScenarioAttempts >= 1 &&
840+
Number.isFinite(scenarioRetryMaxAttempts) &&
841+
runtimeLifecycleScenarioAttempts <= scenarioRetryMaxAttempts,
842+
kpis.runtimeLifecycleScenarioAttempts,
843+
"1..options.scenarioRetryMaxAttempts",
844+
);
845+
const runtimeMetricsScenarioAttempts = toNumber(kpis.runtimeMetricsScenarioAttempts);
846+
addCheck(
847+
"kpi.runtimeMetricsScenarioAttempts",
848+
Number.isFinite(runtimeMetricsScenarioAttempts) &&
849+
runtimeMetricsScenarioAttempts >= 1 &&
850+
Number.isFinite(scenarioRetryMaxAttempts) &&
851+
runtimeMetricsScenarioAttempts <= scenarioRetryMaxAttempts,
852+
kpis.runtimeMetricsScenarioAttempts,
853+
"1..options.scenarioRetryMaxAttempts",
854+
);
835855
const scenarioRetryableFailuresTotal = toNumber(kpis.scenarioRetryableFailuresTotal);
836856
addCheck(
837857
"kpi.scenarioRetryableFailuresTotal",

scripts/demo-e2e.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,8 @@ $runtimeLifecycleData = Get-ScenarioData -Name "runtime.lifecycle.endpoints"
26932693
$runtimeMetricsData = Get-ScenarioData -Name "runtime.metrics.endpoints"
26942694
$uiVisualTestingScenario = @($script:ScenarioResults | Where-Object { $_.name -eq "ui.visual_testing" } | Select-Object -First 1)
26952695
$operatorActionsScenario = @($script:ScenarioResults | Where-Object { $_.name -eq "operator.console.actions" } | Select-Object -First 1)
2696+
$runtimeLifecycleScenario = @($script:ScenarioResults | Where-Object { $_.name -eq "runtime.lifecycle.endpoints" } | Select-Object -First 1)
2697+
$runtimeMetricsScenario = @($script:ScenarioResults | Where-Object { $_.name -eq "runtime.metrics.endpoints" } | Select-Object -First 1)
26962698
$scenarioRetriedSet = @($script:ScenarioResults | Where-Object { [bool]$_.retried })
26972699
$scenarioRetryableFailuresTotal = @($script:ScenarioResults | ForEach-Object { [int]$_.retryableFailureCount } | Measure-Object -Sum).Sum
26982700
$uiExecutorService = $script:ServiceStatuses | Where-Object { $_.name -eq "ui-executor" } | Select-Object -First 1
@@ -2846,6 +2848,8 @@ $summary = [ordered]@{
28462848
scenarioRetryableFailuresTotal = [int]$scenarioRetryableFailuresTotal
28472849
uiVisualTestingScenarioAttempts = if ($uiVisualTestingScenario.Count -gt 0) { [int]$uiVisualTestingScenario[0].attempts } else { $null }
28482850
operatorConsoleActionsScenarioAttempts = if ($operatorActionsScenario.Count -gt 0) { [int]$operatorActionsScenario[0].attempts } else { $null }
2851+
runtimeLifecycleScenarioAttempts = if ($runtimeLifecycleScenario.Count -gt 0) { [int]$runtimeLifecycleScenario[0].attempts } else { $null }
2852+
runtimeMetricsScenarioAttempts = if ($runtimeMetricsScenario.Count -gt 0) { [int]$runtimeMetricsScenario[0].attempts } else { $null }
28492853
delegatedRoute = if ($null -ne $delegationData) { $delegationData.delegatedRoute } else { $null }
28502854
assistiveRouterMode = if ($null -ne $delegationData) { $delegationData.routingMode } else { $null }
28512855
assistiveRouterReason = if ($null -ne $delegationData) { $delegationData.routingReason } else { $null }

scripts/release-readiness.ps1

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,34 @@ if ((-not $SkipDemoE2E) -and (Test-Path $SummaryPath)) {
450450
)
451451
}
452452

453+
$runtimeLifecycleScenarioAttempts = To-NumberOrNaN $summary.kpis.runtimeLifecycleScenarioAttempts
454+
if (
455+
[double]::IsNaN($runtimeLifecycleScenarioAttempts) -or
456+
$runtimeLifecycleScenarioAttempts -lt 1 -or
457+
$runtimeLifecycleScenarioAttempts -gt $scenarioRetryMaxAttempts
458+
) {
459+
Fail (
460+
"Critical KPI check failed: kpi.runtimeLifecycleScenarioAttempts expected 1.." +
461+
$summary.options.scenarioRetryMaxAttempts +
462+
", actual " +
463+
$summary.kpis.runtimeLifecycleScenarioAttempts
464+
)
465+
}
466+
467+
$runtimeMetricsScenarioAttempts = To-NumberOrNaN $summary.kpis.runtimeMetricsScenarioAttempts
468+
if (
469+
[double]::IsNaN($runtimeMetricsScenarioAttempts) -or
470+
$runtimeMetricsScenarioAttempts -lt 1 -or
471+
$runtimeMetricsScenarioAttempts -gt $scenarioRetryMaxAttempts
472+
) {
473+
Fail (
474+
"Critical KPI check failed: kpi.runtimeMetricsScenarioAttempts expected 1.." +
475+
$summary.options.scenarioRetryMaxAttempts +
476+
", actual " +
477+
$summary.kpis.runtimeMetricsScenarioAttempts
478+
)
479+
}
480+
453481
$scenarioRetryableFailuresTotal = To-NumberOrNaN $summary.kpis.scenarioRetryableFailuresTotal
454482
if ([double]::IsNaN($scenarioRetryableFailuresTotal) -or $scenarioRetryableFailuresTotal -lt 0) {
455483
Fail ("Critical KPI check failed: kpi.scenarioRetryableFailuresTotal expected >= 0, actual " + $summary.kpis.scenarioRetryableFailuresTotal)
@@ -687,20 +715,26 @@ if ((-not $SkipDemoE2E) -and (Test-Path $SummaryPath)) {
687715
$scenarioRetryableFailuresTotal = $summary.kpis.scenarioRetryableFailuresTotal
688716
$uiVisualAttempts = $summary.kpis.uiVisualTestingScenarioAttempts
689717
$operatorActionsAttempts = $summary.kpis.operatorConsoleActionsScenarioAttempts
718+
$runtimeLifecycleAttempts = $summary.kpis.runtimeLifecycleScenarioAttempts
719+
$runtimeMetricsAttempts = $summary.kpis.runtimeMetricsScenarioAttempts
690720
if (
691721
$null -ne $scenarioRetryAttempts -or
692722
$null -ne $scenarioRetryBackoff -or
693723
$null -ne $scenarioRetriesUsedCount -or
694724
$null -ne $uiVisualAttempts -or
695-
$null -ne $operatorActionsAttempts
725+
$null -ne $operatorActionsAttempts -or
726+
$null -ne $runtimeLifecycleAttempts -or
727+
$null -ne $runtimeMetricsAttempts
696728
) {
697729
Write-Host (
698730
"demo.scenario.retry: max_attempts=" + $scenarioRetryAttempts +
699731
", backoff_ms=" + $scenarioRetryBackoff +
700732
", retries_used=" + $scenarioRetriesUsedCount +
701733
", retryable_failures=" + $scenarioRetryableFailuresTotal +
702734
", ui.visual_testing_attempts=" + $uiVisualAttempts +
703-
", operator.console.actions_attempts=" + $operatorActionsAttempts
735+
", operator.console.actions_attempts=" + $operatorActionsAttempts +
736+
", runtime.lifecycle.endpoints_attempts=" + $runtimeLifecycleAttempts +
737+
", runtime.metrics.endpoints_attempts=" + $runtimeMetricsAttempts
704738
)
705739
}
706740
$gatewayRoundTrip = $summary.kpis.gatewayWsRoundTripMs

tests/unit/demo-e2e-policy-check.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ function createPassingSummary(overrides?: {
147147
scenarioRetriesUsedCount: 0,
148148
uiVisualTestingScenarioAttempts: 1,
149149
operatorConsoleActionsScenarioAttempts: 1,
150+
runtimeLifecycleScenarioAttempts: 1,
151+
runtimeMetricsScenarioAttempts: 1,
150152
scenarioRetryableFailuresTotal: 0,
151153
sandboxPolicyValidated: true,
152154
visualTestingStatus: "passed",
@@ -240,7 +242,7 @@ test("demo-e2e policy check passes with baseline passing summary", () => {
240242
const result = runPolicyCheck(createPassingSummary());
241243
assert.equal(result.exitCode, 0, JSON.stringify(result.payload));
242244
assert.equal(result.payload.ok, true);
243-
assert.equal(result.payload.checks, 160);
245+
assert.equal(result.payload.checks, 162);
244246
});
245247

246248
test("demo-e2e policy check fails when assistant activity lifecycle KPI is missing", () => {
@@ -455,6 +457,44 @@ test("demo-e2e policy check fails when ui visual scenario attempts exceed config
455457
assert.ok(violations.some((item) => item.includes("kpi.uiVisualTestingScenarioAttempts")));
456458
});
457459

460+
test("demo-e2e policy check fails when runtime lifecycle scenario attempts exceed configured retry max", () => {
461+
const result = runPolicyCheck(
462+
createPassingSummary({
463+
kpis: {
464+
runtimeLifecycleScenarioAttempts: 3,
465+
},
466+
options: {
467+
scenarioRetryMaxAttempts: 2,
468+
},
469+
}),
470+
);
471+
assert.equal(result.exitCode, 1);
472+
assert.equal(result.payload.ok, false);
473+
const details = result.payload.details as Record<string, unknown>;
474+
assert.ok(Array.isArray(details?.violations));
475+
const violations = details.violations as string[];
476+
assert.ok(violations.some((item) => item.includes("kpi.runtimeLifecycleScenarioAttempts")));
477+
});
478+
479+
test("demo-e2e policy check fails when runtime metrics scenario attempts exceed configured retry max", () => {
480+
const result = runPolicyCheck(
481+
createPassingSummary({
482+
kpis: {
483+
runtimeMetricsScenarioAttempts: 3,
484+
},
485+
options: {
486+
scenarioRetryMaxAttempts: 2,
487+
},
488+
}),
489+
);
490+
assert.equal(result.exitCode, 1);
491+
assert.equal(result.payload.ok, false);
492+
const details = result.payload.details as Record<string, unknown>;
493+
assert.ok(Array.isArray(details?.violations));
494+
const violations = details.violations as string[];
495+
assert.ok(violations.some((item) => item.includes("kpi.runtimeMetricsScenarioAttempts")));
496+
});
497+
458498
test("demo-e2e policy check fails when operator.device_nodes.lifecycle scenario is missing", () => {
459499
const summary = createPassingSummary();
460500
summary.scenarios = (summary.scenarios as Array<Record<string, unknown>>).filter(

tests/unit/demo-scenario-retry-alignment.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,6 @@ test("demo-e2e applies transient retry to flaky operator/ui/runtime scenarios",
5454
assert.match(source, /scenarioRetriesUsedCount/);
5555
assert.match(source, /uiVisualTestingScenarioAttempts/);
5656
assert.match(source, /operatorConsoleActionsScenarioAttempts/);
57+
assert.match(source, /runtimeLifecycleScenarioAttempts/);
58+
assert.match(source, /runtimeMetricsScenarioAttempts/);
5759
});

tests/unit/release-readiness.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ function createPassingSummary(
4141
scenarioRetriesUsedCount: number | string;
4242
uiVisualTestingScenarioAttempts: number | string;
4343
operatorConsoleActionsScenarioAttempts: number | string;
44+
runtimeLifecycleScenarioAttempts: number | string;
45+
runtimeMetricsScenarioAttempts: number | string;
4446
scenarioRetryableFailuresTotal: number | string;
4547
analyticsSplitTargetsValidated: boolean | string;
4648
analyticsBigQueryConfigValidated: boolean | string;
@@ -173,6 +175,12 @@ function createPassingSummary(
173175
operatorConsoleActionsScenarioAttempts: hasOverride("operatorConsoleActionsScenarioAttempts")
174176
? overrides.operatorConsoleActionsScenarioAttempts
175177
: 1,
178+
runtimeLifecycleScenarioAttempts: hasOverride("runtimeLifecycleScenarioAttempts")
179+
? overrides.runtimeLifecycleScenarioAttempts
180+
: 1,
181+
runtimeMetricsScenarioAttempts: hasOverride("runtimeMetricsScenarioAttempts")
182+
? overrides.runtimeMetricsScenarioAttempts
183+
: 1,
176184
scenarioRetryableFailuresTotal: hasOverride("scenarioRetryableFailuresTotal")
177185
? overrides.scenarioRetryableFailuresTotal
178186
: 0,
@@ -680,6 +688,38 @@ test(
680688
},
681689
);
682690

691+
test(
692+
"release-readiness fails when runtime lifecycle scenario attempts exceed configured retry max",
693+
{ skip: skipIfNoPowerShell },
694+
() => {
695+
const result = runReleaseReadiness(
696+
createPassingSummary({
697+
scenarioRetryMaxAttempts: "2",
698+
runtimeLifecycleScenarioAttempts: "3",
699+
}),
700+
);
701+
assert.equal(result.exitCode, 1);
702+
const output = `${result.stderr}\n${result.stdout}`;
703+
assert.match(output, /kpi\.runtimeLifecycleScenarioAttempts expected 1\.\.2, actual 3/i);
704+
},
705+
);
706+
707+
test(
708+
"release-readiness fails when runtime metrics scenario attempts exceed configured retry max",
709+
{ skip: skipIfNoPowerShell },
710+
() => {
711+
const result = runReleaseReadiness(
712+
createPassingSummary({
713+
scenarioRetryMaxAttempts: "2",
714+
runtimeMetricsScenarioAttempts: "3",
715+
}),
716+
);
717+
assert.equal(result.exitCode, 1);
718+
const output = `${result.stderr}\n${result.stdout}`;
719+
assert.match(output, /kpi\.runtimeMetricsScenarioAttempts expected 1\.\.2, actual 3/i);
720+
},
721+
);
722+
683723
test(
684724
"release-readiness fails when analytics split targets KPI is not validated",
685725
{ skip: skipIfNoPowerShell },

tests/unit/runbook-release-alignment.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ test("runbook documents release perf artifact-only mode and critical evidence ke
6666
"`kpi.scenarioRetriesUsedCount = 0`",
6767
"`kpi.uiVisualTestingScenarioAttempts <= options.scenarioRetryMaxAttempts`",
6868
"`kpi.operatorConsoleActionsScenarioAttempts <= options.scenarioRetryMaxAttempts`",
69+
"`kpi.runtimeLifecycleScenarioAttempts <= options.scenarioRetryMaxAttempts`",
70+
"`kpi.runtimeMetricsScenarioAttempts <= options.scenarioRetryMaxAttempts`",
6971
"`kpi.scenarioRetryableFailuresTotal >= 0`",
7072
"`workload.live.p95`",
7173
"`workload.ui.p95`",

0 commit comments

Comments
 (0)