The Weekly and Monthly Maintenance Loop: Selectors, Data, Dependencies
A suite that nobody maintains does not fail suddenly — it decays. Selectors break one by one, fixtures outlive the data they seed, and eighteen months later the team reruns tests until green and trusts none of them. The countermeasure is not effort, it is a calendar: recurring maintenance items with owners, cadence, and visible evidence.
Run the loop on two clocks. Weekly, a 30-minute triage: every red build from the last seven days is classified as product bug, test bug, flaky, or infrastructure, and each class gets an owner before the meeting ends. The service level is simple enough to remember — every red build has a named owner within one business day — because an unowned red build teaches the team that red means nothing.
The taxonomy needs evidence or it becomes storytelling. Classify from what the pipeline already stored: the failing assertion in test-results/, the trace for whether the DOM or the API drifted, the nightly quarantine report for repeats. Signatures are worth writing down in the team wiki: TimeoutError: locator.resolve on a rock-solid test-id usually means the page never rendered because a dependency call failed — an API problem wearing a UI costume — while a sudden 403 on an API spec that passed yesterday points at the account fixture or an expired token, not the endpoint. Ten minutes with a trace beats an hour of theorizing, and the classifications you collect here feed the escaped-defect reviews later.
Monthly, a dependency and debt pass driven by automation rather than heroics:
// .github/renovate.json
{
"schedule": ["before 6am on monday"],
"packageRules": [
{ "matchPackagePatterns": ["playwright"], "groupName": "playwright stack",
"automerge": false },
{ "matchUpdateTypes": ["minor", "patch"], "automerge": true,
"automergeType": "pr", "requiredStatusChecks": ["capstone-smoke"] }
]
}
Minor and patch bumps automerge only after the smoke lane proves them — the gate you built now protects the framework itself. Majors (Playwright 1.4x → 1.5y) get a manual PR, a read of the changelog for locator and config deprecations, and a full-suite run before merge.
Each volatile asset in the framework has a maintenance item with the right frequency:
| Asset | Symptom of neglect | Cadence | Evidence of health |
|---|---|---|---|
| Locators | UI specs fail on every UI change | Weekly triage | % of failures classified selector-drift |
| Test data/accounts | 403 storms, orphaned rows |
Every run (fixtures create + delete) | Row count diff before/after run |
| Dependencies | Old browsers, patched CVEs | Weekly PRs (Renovate) | Time since last green upgrade |
| Quota/money | Staging blocked mid-suite | Monthly | 0 quota-related failures |
| Docs | New hire blocked > 15 min | Per merged change | Onboarding stopwatch, quarterly |
Selector churn gets special attention because it is the decay vector you can measure. The page objects from Designing the Capstone Framework localize it; you go further and track its size:
grep -rn "getByTestId\|getByRole" src/ui | wc -l # the surface area
grep -rln "css=.*div\." src/ui # the stragglers to purge
When the second number is non-zero, a refactor is scheduled — brittle chains are debt with interest, and the interest is paid in triage hours.
Budget the whole loop honestly: reserve about 10% of sprint capacity for framework maintenance, and defend it in the same review where you defend features. The argument is arithmetic, not sentiment: a 38-minute suite that rots gains retry time and loses trust, and rebuilding trust costs far more than the 10% ever did. A routine proves itself by what it prevents — and by how cleanly it lets you remove things, which is the discipline the next chapter covers.
Retiring Tests on Purpose: Quarantine, Criteria, and the Coverage Record
There is a test named legacy-cart-2019.spec.ts that has failed on 60% of runs since March. Someone reruns the job until it passes. Nobody remembers what it protects. This is how suites die — not by deletion, but by learned helplessness around tests nobody is allowed to touch. The alternative is deliberate retirement: every removal is a decision with criteria, an owner, and a written record.
Decide with an explicit policy, applied at the monthly pass:
| Situation | Signal | Action |
|---|---|---|
| Intermittent failure | Fails ≥ 20% of runs over 30 days | Fix within the sprint or quarantine |
| Covers removed feature | Feature flag or route deleted | Retire with a record |
| Duplicated coverage | Same journey proven at a cheaper layer | Retire UI version, keep API/data version |
| Business rule gone | PM confirms rule retired | Retire + update the test-strategy matrix |
Quarantine is a state, not a .skip someone forgot. In Playwright it is a tag plus two pipelines:
# the gate — quarantined tests are excluded from the merge verdict
npx playwright test --grep-invert @quarantine
# the nightly — quarantined tests still run, and their failures are reported
npx playwright test --grep @quarantine --reporter=github
The quarantine tag requires a ticket in the test's annotation (test.info().annotations.push({ type: 'ticket', description: 'QAI-1183' })), and the nightly quarantine report appears in the weekly digest with the oldest ticket's age. A quarantined test with no ticket and no age is just deleted already — the ticket is what stops quarantine from becoming a graveyard.
Track the quarantine count itself on that weekly digest; it is the honest version of the pass rate. A quarantine that grows every month is a suite being pruned of its hard problems, while one that empties is a team paying its debts down. Put a ceiling on the state — no more than 2% of the suite quarantined at once — so it stays temporary by construction instead of by good intentions, and make the ceiling a gate you actually look at in the retrospective.
Retirement itself is a commit that touches three things: the test, the changelog, and nothing else — no sweeping refactors in the same PR, so the removal stays reviewable:
## TEST-CHANGELOG
### 2026-09-14 — retired `legacy-cart-2019.spec.ts` (14 tests)
Reason: cart-v1 removed in release v4.2 (flag CART_V2=100%)
Replace: none — journey superseded by `cart-v2.spec.ts` (API-seeded)
Coverage: 1,842 → 1,828 declared tests; spec matrix row "Cart v1" removed
Ref: commit 9f3c1ab · QAI-1402
That file is the answer to the auditor's inevitable question — "why did declared coverage drop last month?" — answered in 30 seconds without archaeology. It is also the answer interviewers probe when they ask how you handle obsolete tests: delete only with a record, keep the strategy matrix and the tests in the same commit, and never let a coverage number move without a written cause.
The trust argument is the deeper one. Teams stop believing coverage percentages the first time a number drops silently; they stop believing the suite the first time a critical test is deleted "because it was flaky". Deliberate retirement — criteria applied uniformly, a quarantine that expires into a decision, a changelog auditors can follow — is what makes the remaining 1,828 tests mean something. Maintenance keeps the suite alive; retirement keeps it true. What is left is to prove both to the people who fund them, which is the retrospective and demo in the final chapter of the roadmap.
Retrospective to Demo: Proving the Framework Still Earns Its Keep
The framework is built, wired, reported, and maintained. Two obligations remain, and they are the ones that decide whether the work is remembered: an honest retrospective on the framework itself, and a demo that proves its value to people who will never read its code.
Feed the retrospective with data, not impressions. Pull the last quarter's numbers straight from the pipeline artifacts behind the dashboards — the shared dataset from Reporting for Stakeholders:
suite duration 38m → 12m30s wall-clock (4 shards)
flaky rate 2.1% → 0.7%
PR feedback p95 31m → 12m40s
escaped defects/qtr 9 → 4 (escape rate 15% → 8%)
maintenance hours/wk 6.0 (selector churn 41%, env issues 22%)
Then run a 60-minute session with the three questions that produce output instead of catharsis: what is working that we must not break, where does the framework actively cost us, and what is the next phase? Each gap becomes a ranked proposal with an estimate — "replace 140 UI-clicked setup steps with API fixtures: −6 minutes suite duration, ~2 days of work" beats "reduce flakiness" as a backlog item because it can be accepted or rejected on the spot. The output is three to five committed proposals with owners and dates; a retrospective whose actions live in someone's notebook did not happen.
The demo is an engineering exercise with one success criterion: the audience sees a red test produce evidence and a green test prove a journey, live. Structure it in five beats, about eight minutes total:
# 1 — repo + README, then first green, from a clean clone (pre-warmed)
npm ci && npm run test:smoke # 12 passed (6.4s)
# 2 — the full run against the demo tag, 4 shards in the CI UI
npx playwright test --shard=1/4
# 3 — the deliberate failure: flip the seeded bug, rerun one test
npx playwright test checkout --grep "applies coupon once"
# → expect(409).received(200) + trace.zip in the artifact
# 4 — evidence on screen
npx playwright show-trace test-results/checkout/trace.zip
# 5 — the reports: dashboard trend line, then the Slack readiness post
The seeded bug is the keystone: a known defect (your double-coupon regression from the escaped-defect process makes a perfect subject) that the suite catches on camera. Nothing else you say about reliability lands like a red build turning into a named assertion, a stack, and a downloadable trace in 40 seconds.
Live demos fail in predictable ways, so engineer them: run against a pinned demo tag, never main, so a teammate's merge cannot sabotage you; pre-warm node_modules and browsers on the demo machine; and have the pipeline's video artifact queued as the fallback. State the reliability numbers up front — 0.7% flaky over 30 days — so the audience knows what they are watching is typical, not lucky.
Prepare for the two questions that always follow. "What happens when the framework itself breaks?" — answer with the maintenance loop: Renovate PRs gated by the smoke lane, weekly triage with a one-business-day owner SLA, quarantine ceiling at 2%. "What would you do next?" — answer with the retrospective's ranked proposals, by name and estimate, because they were committed before the demo, not invented during it.
Expect the question that ties the whole capstone together: "walk me through your framework." Answer it in the roadmap's own order — three layers and their boundary rule, one config entry point, two CI triggers and the gates they enforce, one dataset rendered for three audiences, a maintenance loop with a retirement policy — then hand over one metric, such as PR feedback p95 dropping from 31 minutes to 12:40. Layers, gates, evidence, and upkeep, in one breath: that is the difference between a folder of tests and a framework.
Quiz
1. A test has failed on 8 of its last 30 nightly runs and passed on retry each time. What does the maintenance policy require?
2. Why must the quarantine tag carry a ticket annotation and appear in the weekly digest?
3. Declared coverage drops from 1,842 to 1,828 tests and an auditor asks what happened. What is the correct answer?
4. How should the capstone demo be prepared so it proves reliability instead of merely claiming it?
Flashcards
Question
The weekly and monthly maintenance cadence, and the one SLA inside it.
Click to reveal answer
Answer
Weekly: 30-minute triage classifying every red build (product bug / test bug / flaky / infra) from stored evidence — traces, test-results, quarantine report. Monthly: dependency and debt pass via Renovate PRs gated by the smoke lane. SLA: every red build has a named owner within one business day.
Question
The four-signal test-retirement policy.
Click to reveal answer
Answer
Fails ≥ 20% of runs over 30 days → fix this sprint or quarantine · covers a removed feature → retire with a record · duplicated at a cheaper layer → retire the expensive one · business rule retired by the PM → retire and update the test-strategy matrix.
Question
What three things does every retirement commit touch, and why the third rule?
Click to reveal answer
Answer
The test (deleted), the TEST-CHANGELOG (date, spec, reason, replacement or 'none', coverage delta, commit + ticket refs), and nothing else. No sweeping refactors in the same PR, so the removal stays reviewable and the coverage change always has a written cause.
Question
What goes into the framework retrospective, and what must it produce?
Click to reveal answer
Answer
Quarter-over-quarter metrics pulled from pipeline artifacts — duration, flaky rate, PR feedback p95, escapes, maintenance hours by cause — discussed as strengths, gaps, and ranked proposals with estimates. Output: 3–5 proposals with owners and dates; actions kept in a notebook mean the retro did not happen.
Question
The five-beat demo script and its success criterion.
Click to reveal answer
Answer
Clean clone to first green → full run sharded in the CI UI → deliberate seeded failure (e.g., the double-coupon regression) → open the trace with show-trace → dashboard trend plus the Slack readiness post. Success: the audience watches a red build become a named assertion, a stack, and a downloadable trace in ~40 seconds.
Revision Notes
Key Takeaways
- 1. Suites decay one unowned red build at a time — the calendar, not heroics, is the countermeasure: weekly triage, monthly upgrades, a one-business-day owner SLA
- 2. Quarantine is a ticketed state with a ceiling, not a .skip someone forgot; nightly runs keep quarantined tests visible so decisions actually happen
- 3. Retirement is a reviewable commit: the test, the TEST-CHANGELOG entry, and the strategy-matrix update — never a silent deletion, never a mixed refactor
- 4. The retrospective runs on pipeline metrics and must emit ranked proposals with estimates; anything less is conversation, not process
- 5. The demo proves reliability by showing a seeded bug fail live and produce evidence in seconds — pinned tag, warmed machine, rehearsed beats
Interview Tips
- • "How do you keep an automation framework healthy?" — cadence and SLA: weekly triage with owners, monthly dependency pass gated by smoke, 10% capacity budget defended like feature work
- • "What do you do with a flaky test?" — evidence first (30-run window), then fix-or-quarantine with a ticket, nightly visibility, and a quarantine ceiling; explicitly reject retry-until-green
- • "How do you handle obsolete tests?" — explicit criteria, a TEST-CHANGELOG answering 'why did coverage drop', and retirement kept in its own PR for auditability
- • "Show me your framework" (the capstone question) — answer in system order: layers and boundary rule, config entry point, CI triggers and gates, one dataset for three audiences, maintenance loop; close with one metric like PR feedback p95 from 31 min to 12:40
Cheat Sheet
Maintenance, Review, and Demo
Weekly (30 min): triage every red build → product bug / test bug / flaky / infra, classified from stored evidence. SLA: named owner within 1 business day.
Monthly: Renovate pass — minors automerge only after capstone-smoke is green; majors get a changelog read and a full-suite run. Plus debt items: brittle locator purge, fixture/data audit, docs check.
Retention policy:
npx playwright test --grep-invert @quarantine # the gate
npx playwright test --grep @quarantine # nightly, reported
Quarantine requires a ticket annotation; digest shows count + oldest age; ceiling ~2% of suite.
Retirement record (in the same PR as the deletion):
## TEST-CHANGELOG
2026-09-14 — retired legacy-cart-2019.spec.ts (14 tests)
Reason: cart-v1 removed in v4.2 · Replace: cart-v2.spec.ts (API-seeded)
Coverage: 1,842 → 1,828 · Ref: commit 9f3c1ab · QAI-1402
Retro inputs: duration, flaky %, PR feedback p95, escape rate, maintenance hours by cause → 3–5 ranked proposals with estimates, owners, dates.
Demo: pinned demo-v1.4.0 tag, warmed machine, seeded bug, trace on stage, video artifact as fallback.