diff --git a/docs/agents/task-logs/daily/2026-02-19_17-00-24_ci-health-agent_completed.md b/docs/agents/task-logs/daily/2026-02-19_17-00-24_ci-health-agent_completed.md new file mode 100644 index 00000000..dbd56725 --- /dev/null +++ b/docs/agents/task-logs/daily/2026-02-19_17-00-24_ci-health-agent_completed.md @@ -0,0 +1,19 @@ +# CI Health Agent - Daily Run (2026-02-19) + +## Summary +Investigated recent CI failures on the `unstable` branch and identified a recurring failure in the `deploy-cache-state` job, specifically in the `Verify deployed index.html version markup` step. + +## Findings +- **Local Unit Tests:** All passed (`npm run test:unit`). +- **CI Status:** + - Multiple recent runs on `unstable` failed at the deployment verification step. + - PR checks passed, indicating the issue is likely environment-related (CDN propagation latency) rather than a code defect. +- **Root Cause:** The `scripts/verify-deployed-index-html.mjs` script was fetching the URL once and failing immediately if the expected version markup was missing. This is brittle against Cloudflare's cache purge propagation times. + +## Actions Taken +- **Fix Implemented:** Updated `scripts/verify-deployed-index-html.mjs` to include a retry mechanism. + - Added `MAX_RETRIES = 5` and `RETRY_DELAY_MS = 5000` (5 seconds). + - The script now polls the URL up to 5 times before failing, significantly improving robustness against propagation delays. + +## Next Steps +- Monitor the next scheduled daily run or merge to `unstable` to verify the fix eliminates the flake. diff --git a/scripts/verify-deployed-index-html.mjs b/scripts/verify-deployed-index-html.mjs index 36bcf5b2..5d99dde0 100644 --- a/scripts/verify-deployed-index-html.mjs +++ b/scripts/verify-deployed-index-html.mjs @@ -31,26 +31,45 @@ function parseArgs(argv) { return args; } +const MAX_RETRIES = 5; +const RETRY_DELAY_MS = 5000; + +async function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + async function main() { const args = parseArgs(process.argv.slice(2)); - const response = await fetch(args.url, { - headers: { - Accept: 'text/html' + + for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { + try { + const response = await fetch(args.url, { + headers: { + Accept: 'text/html' + } + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + const html = await response.text(); + if (VERSION_MARKUP_PATTERN.test(html)) { + console.log(`Deployed index verification passed: ${args.url} includes slogan version markup.`); + return; + } + + throw new Error('Missing version markup'); + } catch (error) { + if (attempt === MAX_RETRIES) { + throw new Error( + `Failed to verify deployed index.html at ${args.url} after ${MAX_RETRIES} attempts. Last error: ${error.message}` + ); + } + console.log(`Attempt ${attempt} failed: ${error.message}. Retrying in ${RETRY_DELAY_MS / 1000}s...`); + await sleep(RETRY_DELAY_MS); } - }); - - if (!response.ok) { - throw new Error(`Unable to fetch deployed index.html (${args.url}): HTTP ${response.status}`); } - - const html = await response.text(); - if (!VERSION_MARKUP_PATTERN.test(html)) { - throw new Error( - `Deployed index.html is missing version markup under the slogan at ${args.url} (expected: v: <8-char-hash> • ).` - ); - } - - console.log(`Deployed index verification passed: ${args.url} includes slogan version markup.`); } main().catch((error) => {