Skip to content

Release browse-gate triage runbook

When a Test→Prod (or Dev→Test) promotion shows a red Browse smoke / admin / payment gate (AzDO release def 31 → browse-ui-tests.yml), do not assume a product regression and do not silently re-run until green. Triage it: a browse failure is either a product bug (block the promotion) or a test-harness bug (fix the test, then promote). This runbook is the procedure for telling them apart and is the companion to release-test-visibility.md (which covers where the results show up, not how to act on a red one).

Rule of thumb from the Jun-2026 Release-95 gate: both failing tests were test-harness bugs (a seed payload missing a field that the real UI always sends, and a helper relying on a debug-only AngularJS API). Neither blocked prod. Always run the failing test locally before concluding "the product is broken."

1. Pull the exact failures from the release

The consolidated sticky comment on the Test→Prod PR links the GitHub Browse UI Tests run; open the failing suite's job log, or query AzDO directly:

# Failed cases for a release test run (runId from the release Tests tab / link)
az devops invoke --org https://dev.azure.com/raklet-git --area test --resource results \
  --route-parameters project=raklet-git runId=<RUNID> \
  --query-parameters outcomes=Failed --api-version 7.1 -o json

Record, per failure: the test basename (e.g. 52-events-manual-ticket-registration), the first-line error + any errorMessage/stackTrace, and the org it ran against (single-leg runs use the fallback general-test-account; sharded admin-regression legs use browse-ci-admin-a/b/c — see resolve-browse-shard-org.ps1).

2. Reproduce locally against the same environment

The release runs against .raklet.net. Run the same test from a fresh worktree off master, using the raklet-net profile in browse-tests/local.settings.json:

# Acquire the browse lock first (shared daemon)
pwsh scripts/dev/browse-lock.ps1 acquire -Owner triage -Intent "release gate" -Wait

# Load the raklet-net profile into the env the suite reads, then run just the failing test(s)
$p = (Get-Content browse-tests/local.settings.json -Raw | ConvertFrom-Json).profiles.'raklet-net'
$env:BROWSE_TEST_PERMALINK = $p.permalink; $env:BROWSE_TEST_EMAIL = $p.email
$env:BROWSE_TEST_PASSWORD  = $p.password;  $env:BROWSE_TEST_ADMIN_URL = $p.adminUrl
$env:BROWSE_LOGIN_MANAGER_TIMEOUT = '180000'   # the deployed SPA is cold on first hit
./browse-tests/run.ps1 -Suite admin-regression -TestDomain .raklet.net -TestNameFilter '52-events-manual-ticket-registration'

Notes: - run.ps1 restarts the daemon + retries once, so a single about:blank nav drop on a dev box self-heals. If your local daemon is wedged, kill browse procs and browse restart before relying on a local red. - For a clean, authoritative re-run, dispatch on your branch instead of trusting a flaky local box: Actions → Browse UI Tests → Run workflow, suite=admin-regression, test_name=<basename or wildcard>. This runs on a fresh CI daemon.

3. Classify: test bug or product bug

Drive the failing operation directly and capture the real error the harness hides. Browse logs collapse promise rejections to Object; re-issue the call with a rejection handler that stringifies status/data:

api.createEvent({organisationId: orgId}, payload).$promise.then(
  function(r){ return 'OK:' + JSON.stringify(r); },
  function(e){ return 'REJECT:' + JSON.stringify({status: e.status, data: e.data, url: e.config && e.config.url}); });

Then decide:

Signal Likely product bug (block) Likely test bug (fix test, promote)
API rejection 500 reproducible with the same payload the real UI sends 500/400 only with a payload the UI never sends (missing/garbled field)
AngularJS .scope() / .controller() returns undefined n/a Bundle runs debugInfoEnabled(false) on test/prod — use the $rootScope injector-walk instead (see Set-DirectoryDisplayFields)
Selector/timeout only Element genuinely absent for users Selector drift, missing wait, or a read-after-write/index lag
Recent diff on the promoted branch touches the path Inspect that PR closely
Sentry shows the same exception on environment:test from real traffic Product If only your synthetic call appears, suspect the test

Cross-check git log origin/prod..origin/test -- <area> for what's new in this promotion, and Sentry (environment:test, release:Raklet.Api*) for whether real users hit the same error.

4. Fix the right layer, re-verify, record

  • Test bug → fix the helper/test so it exercises the real path (send the field the UI sends; reach the model the way that works on the deployed bundle).
  • Product bug → it blocks the promotion. Open a fix PR through the normal Dev→Test→Prod chain; do not promote until it lands on test.
  • Re-verify by dispatching browse-ui-tests.yml (test_name=<basename>) on the fix branch; confirm green on a clean CI daemon.
  • If the cause is a chronic, non-product flake unrelated to the change, quarantine it in browse-tests/known-failures.json (run-once, non-gating) with a tracking ticket — never just re-run to green.

5. Then re-read the go/no-go

A green browse gate is necessary but not sufficient. The promotion also gates on the promotion-todo-gate (every rolled-up PR's (verify-in-test) todos checked) and net-new Sentry issues. Clear those before merging the Test→Prod PR — see reference: master→test→prod promotion and the promotion checklist.