Test audit
Audit tests as a product-risk portfolio. Run this before creating tests, for
every PR that changes tests or test infrastructure, and for every PR that
changes production behavior. Test infrastructure includes fixtures, test-only
routes, helpers, configuration, and harnesses.
The goal is tests that catch verified reachable future bugs and document
behavior the product still promises. The default answer is not "add a test"; the
default answer is "identify the product risk, then decide whether a test would
catch a real future bug."
There are four gates:
- Coverage drift: code changed, so related tests may need to change even if
no test file changed.
- Test value: tests changed, so each changed test must prove a real future
failure worth catching.
- Portfolio ownership: every retained risk has one executable owner at the
narrowest useful level, without equivalent duplicate coverage.
- Execution cost: code or tests changed, so discovered work, per-test
duration, runner cost, or parallel makespan may move even when nobody asked
for performance work.
Workflow
- Establish the behavior change.
- Read the PR summary, issue, diff, route/schema/API contract, UI flow, or
nearby docs.
- Write one sentence for the new product contract.
- If an API or UI contract is intentionally replaced, treat the old shape as
obsolete unless the code still promises compatibility.
- Inventory changed test files and related unmodified tests.
- Use
git diff --name-status <base>...HEAD for the committed range. When
local changes belong to the target, also inspect git diff --name-status,
git diff --cached --name-status, and
git ls-files --others --exclude-standard; deduplicate the resulting
paths before inspecting test hunks.
- Search around each changed route, component, hook, service, schema, helper,
package, or user flow.
- Include deleted tests and changed test infrastructure even when no test
file changed.
- Check execution-cost impact for every audit.
- When the audit explicitly aims to reduce test runtime, runner cost, or
full-workflow makespan, read
Suite execution cost and optimisation
and establish the applicable baseline even before a candidate change
exists.
- Otherwise inspect whether the change can alter test selection or count,
setup, retries, caching, fixtures, testability, test duration, sharding,
runner capacity, or scheduling.
- Trace a concrete mechanism from the current change to one of those
factors and judge whether the impact could be material. A suite already
known to be slow, costly, or sharded lowers the investigation threshold;
it is not sufficient by itself.
- When the traced mechanism could materially change total work, runner
cost, or full-workflow makespan, use the same workflow and establish the
applicable comparable baseline. Otherwise record
no material execution-cost impact and continue the portfolio audit.
- Map test ownership with Test portfolio.
- For each changed, deleted, or proposed test, and each nearby test used as
a replacement or overlap comparison, determine whether it owns a reachable
bug and at which boundary. For a retained or proposed owner, also name why
adjacent coverage misses it and derive the expected result independently.
- Inspect overlapping tests at other levels and any test infrastructure the
changed tests own.
- Classify each test or assertion using Classifications.
- Apply the usefulness bar and signal lists in
Usefulness bar.
- Defend proposed keepers and deletions adversarially.
- A keeper must catch a distinct bug that adjacent coverage would miss.
- A deletion of a test that owns a promised regression must name an
inspected executable replacement owner. Otherwise, name why the test owns
no current regression and needs no replacement.
- Recommend focused changes:
- Remove or rewrite tests that only prove old fields are gone, old callbacks
are absent, mocks were called in a specific order, tautological expected
values are recomputed from the same logic as the implementation, or
impossible data is ignored.
- Do not keep a test just because it guards against "the old design coming
back" when the old design is no longer reachable through current product
contracts.
- Add tests only for verified reachable change-relevant risks.
- Do not add tests for intentionally removed features, fields, callbacks,
routes, screens, or modes unless the current product contract promises
that absence for compatibility, privacy, migration, or security reasons.
- Do not add tests for a new feature or addition when the test would only
prove wiring, rendering, status codes, or mock calls without catching a
verified reachable product, API, data, security, or workflow regression.
- Do not add frontend tests that only assert arbitrary UI copy, button text,
headings, placeholder text, or marketing text. Use text selectors when
they are the best stable way to find an element, but assert the behavior
or state that matters after finding it.
- Prefer one test that exercises the user/API contract over several tests
that assert internals.
- Before recommending a test or test-infrastructure change, repeat the
execution-cost gate for the proposal. For a material proposal, use the
same workflow and report the cost mechanism, expected effect, and
available evidence or measurement limitation even when implementation is
not authorized. When editing is authorized, capture the applicable
baseline before editing.
- Prefer known-good literals, worked examples, specs, or fixtures for
expected values. Do not compute the expected value the same way production
code does.
- Edit tests only when implementation is authorized. After edits:
- Run the focused test file or package test script.
- Run typecheck/lint when test helpers, fixtures, route contracts, or shared
types changed.
- Repeat the execution-cost gate against the final diff. For every material
signal, compare the final candidate with its baseline and report the raw
values or measurement limitation.
- Report using the buckets in Output. Include each
execution-cost decision:
no material execution-cost impact, or the
material mechanism with observed or modelled values and limitations. For
an unimplemented proposal, report its expected cost direction and the
evidence or limitation supporting it.
Required judgment
- A real future bug means a failure path you can show from current code,
contracts, data, permissions, or user flows.
- A risk is change-relevant when the current work introduces, exposes, fixes,
or directly exercises it.
- Some feature work, cleanup, and intentional removals should leave no new tests
behind.
- Do not assume a changed test is useful just because it passes.
- If related tests exist but did not change, decide whether the PR should
update, add, delete, or leave them alone.
- If tests changed, audit every changed assertion.
- A tautological assertion gives no confidence: expected values must come from
an independent source of truth, not from re-running the implementation logic.
Test portfolio
Treat the suite as a portfolio of distinct reachable regressions, not a record
of every implementation change. Keep the smallest set of executable tests that
protects every behavior the product still promises.
Defend each owner
Assign one executable owner to each distinct regression. For every retained
test, record:
- the realistic future bug it catches;
- the public, integration, or domain boundary it proves;
- the nearest broader, narrower, or peer test and why that test would miss the
bug; and
- an expected result derived independently from the implementation under test.
Tests may cover the same feature at different levels only when they fail for
different bugs. A broader test replaces a smaller test only when both drive the
same branch with equivalent inputs, fail for the same regression, and assert
the same public outcome at an equivalent required cadence. Incidental traversal
is not coverage.
Require a replacement owner
Before deleting a test that owns a still-promised regression, name and inspect
the executable test that will fail for the same bug. Land and verify a required
replacement before or in the same change, and confirm it runs at an equivalent
required cadence. Manually
triggered or opt-in coverage does not replace routine automated coverage by
itself. A planned refactor, future test, manual check, development-only
assertion, type, or schema is not an executable owner.
A test for unreachable or retired behavior needs no replacement when the
current product promises no compatibility, privacy, migration, security, or
safety property that depends on its absence.
Preserve distinct owners for high-risk boundaries such as security, privacy,
permissions, accessibility, safety, time and expiry, races and concurrency,
offline behavior, migrations, transactions, data isolation, and external-call
failures. Do not infer that a happy path owns one of these branches without
driving the branch and asserting its outcome.
Choose the proof level
Place each owner at the narrowest level that proves the real contract:
| Risk |
Owning proof |
| Pure policy, state transition, parser, calculation, or boundary matrix |
Unit test |
| Real binding, persistence, authorization outcome, transaction, or data isolation |
Integration test |
| Visible workflow, native interaction, or cross-role handoff |
End-to-end test |
| Test parser, validator, selector, fixture helper, or serializer |
Focused test of that executable helper |
| Declarative test or build configuration |
Parser or linter plus real execution |
Do not start an application or render a component merely to test pure policy,
state, or calculation. Do not use a unit test of policy to claim that a route,
UI, serializer, or native adapter binds it correctly. Keep one complete golden
journey for a valuable workflow, then retain focused owners only for distinct
risks that journey cannot isolate.
Consolidate or remove
Consolidate distinct regression owners that repeat workflow setup or assertion
shape. Use a named object-shaped table while keeping one row for each distinct
failure mode or required side effect. Remove cases that differ only by
equivalent roles, statuses, invalid inputs, filters, or wording and drive the
same branch and public outcome.
Remove tests or assertions that own no current regression, including:
- branch history and retired or unreachable behavior;
- incidental or unpromised presentation, styling, geometry, token, or broad
snapshot assertions;
- incidental wording, waits, callbacks, mock counts, mock order, or helper
wiring;
- source or configuration mirrors that re-assert declarative files line by
line;
- success tests that assert only status, rendering, or response-key presence
while missing the returned values, state change, or persisted outcome;
- duplicate coverage that meets the replacement-equivalence test; and
- orphaned test-only routes, fixtures, environment switches, helpers, harnesses,
or duration metadata with no executable caller.
Keep appearance or layout coverage when it is an explicit stable product or
design contract. Keep exact wording, timing, geometry, lifecycle calls, or
presentation when it is itself a promised legal, accessibility, safety, domain,
or protocol contract. For authorization rejection, assert the denial and
absence of any forbidden side effect when one is possible.
Audit adversarially
Apply the five-question usefulness bar to each proposed
keeper. For each deletion, challenge whether the named replacement really
drives the same branch and outcome. Recheck every affected high-risk boundary
after the challenge.
For a portfolio-reduction audit, report before-and-after test file counts,
expanded test counts, and measured runtime when the environment can measure it
reliably. State limitations rather than estimating.
Classifications
Use these labels when auditing tests or assertions:
keeper: is the executable owner of a distinct reachable regression in
promised behavior.
consolidate: owns a distinct reachable regression but repeats the setup or
assertion shape of other distinct owners; retain the cases while sharing one
table or setup.
move: owns a real risk, but the same intended outcome should be proved at a
different boundary; relocate the test without changing that outcome.
rewrite: points at the right risk and boundary but uses a stale API shape,
brittle text, timing, fixture internals, implementation detail, or a
tautological expected value.
delete: covers removed behavior, impossible state, branch-local history,
equivalent duplicate coverage with another retained owner and no distinct
regression, orphaned test infrastructure, or "absence of old thing" with no
compatibility promise.
changed-but-useless: a changed or added test passes but would not catch a
verified reachable future bug.
missing: a verified reachable change-relevant regression risk has no test.
no-test-needed: changed behavior was inspected, and adding coverage would
only test branch-local history, removed behavior, duplicate coverage, or a
low-risk addition with no verified reachable future failure to catch.
no-change-needed: related tests exist, but they already cover the changed
contract and do not need edits.
dangerous-removal: deletes the last executable owner of a promised
regression without an equivalent, inspected replacement at the required
cadence.
Usefulness bar
A test earns its keep only when you can answer all five:
- What future code change could make this fail?
- Would that failure be a real product, API, data, security, or workflow bug?
- Is this the lowest practical level that proves the behavior?
- Does it assert stable behavior instead of private implementation details?
- Does the expected value come from an independent source of truth rather than
the same logic the implementation uses?
Treat "regression risk" as something demonstrated, not imagined. Good evidence
includes an existing caller, route, screen, permission path, data migration,
parser input, security boundary, or a real bug class already seen in the code.
Security vulnerabilities, auth bypasses, data loss, and migration corruption are
strong reasons to add regression tests when the failing path is real.
For frontend tests, prove behavior or state: navigation, submitted data,
enabled/disabled controls, permissions, validation, persisted state, loaded
records, or stable accessibility contracts. Do not add tests that only lock
incidental UI copy unless that exact copy is the current product contract, such
as a legal notice, required error message, or accessibility label.
Good test signals
- It would fail if a future refactor broke the new contract.
- It checks a real caller, route, screen, permission, state transition, data
conversion, validation rule, migration, or boundary.
- For frontend tests, it proves a user-visible behavior or stable app state,
such as a saved value appearing, an action becoming available, validation
blocking submission, navigation changing, or protected UI staying unavailable.
- It covers both sides of a meaningful branch when both are product behavior.
- It uses public API/UI output or a stable domain boundary.
- It asserts a known-good literal, worked example, fixture, or spec-derived
expected value.
- It prevents a regression that has happened before or is demonstrably reachable
from the changed code.
Waste signals
- It asserts a removed field is absent after the API was intentionally replaced.
- It tests branch-local history, such as "old behavior no longer happens."
- It proves an impossible internal state is handled after upstream validation
already rules it out.
- It checks mock call counts, exact call order, intermediate state, or private
helper output when the public result would catch the bug.
- It recomputes the expected value the same way the implementation does, so the
test passes by construction.
- It snapshots large payloads where only one field matters.
- It duplicates nearby coverage without protecting a different risk.
- It adds status-code-only or render-only tests that would pass while the real
contract is broken.
- It asserts random UI copy, headings, button text, placeholders, or marketing
text without proving the user flow, state change, permission, data rendering,
or accessibility contract that copy belongs to.
- It leaves nearby tests untouched even though their asserted contract changed.
- It changes a test only to prove that removed behavior is gone, without a
compatibility, privacy, migration, or security reason.
- It adds coverage for an intentional removal just to prevent the old feature
from returning, with no current contract that the old feature must stay
absent.
- It adds coverage for a new feature where the only possible failure is a
harmless implementation detail, duplicated nearby coverage, or branch-local
history.
- It says "this could regress" without showing a reachable path through current
code, contracts, data, permissions, or user flows.
Output
For a review, report:
Keep: tests that protect real behavior.
Consolidate: distinct regression owners that should retain separate cases
while sharing one table or setup.
Move: real risks proved at the wrong level.
Rewrite: tests that target a real risk but assert it poorly.
Delete: tests/assertions that do not earn their keep.
Changed but useless: changed tests that pass but should not exist.
Missing: specific verified reachable behavior risks that need coverage.
No test needed: changed behavior inspected where new coverage would not
catch a verified reachable future bug.
No change needed: related tests inspected and why they still fit.
Dangerous removals: removed tests or assertions that were the last
executable owner of promised behavior and have no equivalent, inspected
replacement at the required cadence.
Ownership: the named executable replacement for each removed test or
assertion that owned a still-promised regression, or why the removed coverage
owned no current regression and needs no replacement; also state why adjacent
coverage misses each keeper's or consolidated case's distinct regression.
Validation: commands run and result. For a portfolio-reduction audit, add
before-and-after file counts, expanded test counts, and measured runtime when
reliable.
For each Consolidate, Move, Rewrite, Delete, Missing, or Dangerous removal, include the smallest useful reason:
`path/to/test.ts`: delete the `systemAlerts` absence assertion. The API no
longer exposes that field, and no compatibility/privacy contract requires
testing its absence. Assert the new alert list shape instead.
Stale API assertion example
If a PR replaces:
expect(json.data.alerts.systemAlerts.length).toBe(2);
with:
expect(json.data.alerts).not.toHaveProperty("systemAlerts");
flag the new assertion for deletion unless the API explicitly promises that
systemAlerts must never appear for compatibility or privacy reasons. A better
test would assert the new alert contract that callers now consume.
1---2name: test-audit3description: Plan and audit test portfolios before creating, changing, or removing tests or test infrastructure; challenge missing, duplicate, brittle, wrongly placed, ownerless, and dangerous coverage.4---56# Test audit78Audit tests as a product-risk portfolio. Run this before creating tests, for9every PR that changes tests or test infrastructure, and for every PR that10changes production behavior. Test infrastructure includes fixtures, test-only11routes, helpers, configuration, and harnesses.1213The goal is tests that catch verified reachable future bugs and document14behavior the product still promises. The default answer is not "add a test"; the15default answer is "identify the product risk, then decide whether a test would16catch a real future bug."1718There are four gates:19201. **Coverage drift**: code changed, so related tests may need to change even if21 no test file changed.222. **Test value**: tests changed, so each changed test must prove a real future23 failure worth catching.243. **Portfolio ownership**: every retained risk has one executable owner at the25 narrowest useful level, without equivalent duplicate coverage.264. **Execution cost**: code or tests changed, so discovered work, per-test27 duration, runner cost, or parallel makespan may move even when nobody asked28 for performance work.2930## Workflow31321. Establish the behavior change.33 - Read the PR summary, issue, diff, route/schema/API contract, UI flow, or34 nearby docs.35 - Write one sentence for the new product contract.36 - If an API or UI contract is intentionally replaced, treat the old shape as37 obsolete unless the code still promises compatibility.382. Inventory changed test files and related unmodified tests.39 - Use `git diff --name-status <base>...HEAD` for the committed range. When40 local changes belong to the target, also inspect `git diff --name-status`,41 `git diff --cached --name-status`, and42 `git ls-files --others --exclude-standard`; deduplicate the resulting43 paths before inspecting test hunks.44 - Search around each changed route, component, hook, service, schema, helper,45 package, or user flow.46 - Include deleted tests and changed test infrastructure even when no test47 file changed.483. Check execution-cost impact for every audit.49 - When the audit explicitly aims to reduce test runtime, runner cost, or50 full-workflow makespan, read51 [Suite execution cost and optimisation](references/expensive-suites.md)52 and establish the applicable baseline even before a candidate change53 exists.54 - Otherwise inspect whether the change can alter test selection or count,55 setup, retries, caching, fixtures, testability, test duration, sharding,56 runner capacity, or scheduling.57 - Trace a concrete mechanism from the current change to one of those58 factors and judge whether the impact could be material. A suite already59 known to be slow, costly, or sharded lowers the investigation threshold;60 it is not sufficient by itself.61 - When the traced mechanism could materially change total work, runner62 cost, or full-workflow makespan, use the same workflow and establish the63 applicable comparable baseline. Otherwise record `no material64 execution-cost impact` and continue the portfolio audit.654. Map test ownership with [Test portfolio](#test-portfolio).66 - For each changed, deleted, or proposed test, and each nearby test used as67 a replacement or overlap comparison, determine whether it owns a reachable68 bug and at which boundary. For a retained or proposed owner, also name why69 adjacent coverage misses it and derive the expected result independently.70 - Inspect overlapping tests at other levels and any test infrastructure the71 changed tests own.725. Classify each test or assertion using [Classifications](#classifications).736. Apply the usefulness bar and signal lists in74 [Usefulness bar](#usefulness-bar).757. Defend proposed keepers and deletions adversarially.76 - A keeper must catch a distinct bug that adjacent coverage would miss.77 - A deletion of a test that owns a promised regression must name an78 inspected executable replacement owner. Otherwise, name why the test owns79 no current regression and needs no replacement.808. Recommend focused changes:81 - Remove or rewrite tests that only prove old fields are gone, old callbacks82 are absent, mocks were called in a specific order, tautological expected83 values are recomputed from the same logic as the implementation, or84 impossible data is ignored.85 - Do not keep a test just because it guards against "the old design coming86 back" when the old design is no longer reachable through current product87 contracts.88 - Add tests only for verified reachable change-relevant risks.89 - Do not add tests for intentionally removed features, fields, callbacks,90 routes, screens, or modes unless the current product contract promises91 that absence for compatibility, privacy, migration, or security reasons.92 - Do not add tests for a new feature or addition when the test would only93 prove wiring, rendering, status codes, or mock calls without catching a94 verified reachable product, API, data, security, or workflow regression.95 - Do not add frontend tests that only assert arbitrary UI copy, button text,96 headings, placeholder text, or marketing text. Use text selectors when97 they are the best stable way to find an element, but assert the behavior98 or state that matters after finding it.99 - Prefer one test that exercises the user/API contract over several tests100 that assert internals.101 - Before recommending a test or test-infrastructure change, repeat the102 execution-cost gate for the proposal. For a material proposal, use the103 same workflow and report the cost mechanism, expected effect, and104 available evidence or measurement limitation even when implementation is105 not authorized. When editing is authorized, capture the applicable106 baseline before editing.107 - Prefer known-good literals, worked examples, specs, or fixtures for108 expected values. Do not compute the expected value the same way production109 code does.1109. Edit tests only when implementation is authorized. After edits:111 - Run the focused test file or package test script.112 - Run typecheck/lint when test helpers, fixtures, route contracts, or shared113 types changed.114 - Repeat the execution-cost gate against the final diff. For every material115 signal, compare the final candidate with its baseline and report the raw116 values or measurement limitation.11710. Report using the buckets in [Output](#output). Include each118 execution-cost decision: `no material execution-cost impact`, or the119 material mechanism with observed or modelled values and limitations. For120 an unimplemented proposal, report its expected cost direction and the121 evidence or limitation supporting it.122123## Required judgment124125- A real future bug means a failure path you can show from current code,126 contracts, data, permissions, or user flows.127- A risk is change-relevant when the current work introduces, exposes, fixes,128 or directly exercises it.129- Some feature work, cleanup, and intentional removals should leave no new tests130 behind.131- Do not assume a changed test is useful just because it passes.132- If related tests exist but did not change, decide whether the PR should133 update, add, delete, or leave them alone.134- If tests changed, audit every changed assertion.135- A tautological assertion gives no confidence: expected values must come from136 an independent source of truth, not from re-running the implementation logic.137138## Test portfolio139140Treat the suite as a portfolio of distinct reachable regressions, not a record141of every implementation change. Keep the smallest set of executable tests that142protects every behavior the product still promises.143144### Defend each owner145146Assign one executable owner to each distinct regression. For every retained147test, record:148149- the realistic future bug it catches;150- the public, integration, or domain boundary it proves;151- the nearest broader, narrower, or peer test and why that test would miss the152 bug; and153- an expected result derived independently from the implementation under test.154155Tests may cover the same feature at different levels only when they fail for156different bugs. A broader test replaces a smaller test only when both drive the157same branch with equivalent inputs, fail for the same regression, and assert158the same public outcome at an equivalent required cadence. Incidental traversal159is not coverage.160161### Require a replacement owner162163Before deleting a test that owns a still-promised regression, name and inspect164the executable test that will fail for the same bug. Land and verify a required165replacement before or in the same change, and confirm it runs at an equivalent166required cadence. Manually167triggered or opt-in coverage does not replace routine automated coverage by168itself. A planned refactor, future test, manual check, development-only169assertion, type, or schema is not an executable owner.170171A test for unreachable or retired behavior needs no replacement when the172current product promises no compatibility, privacy, migration, security, or173safety property that depends on its absence.174175Preserve distinct owners for high-risk boundaries such as security, privacy,176permissions, accessibility, safety, time and expiry, races and concurrency,177offline behavior, migrations, transactions, data isolation, and external-call178failures. Do not infer that a happy path owns one of these branches without179driving the branch and asserting its outcome.180181### Choose the proof level182183Place each owner at the narrowest level that proves the real contract:184185| Risk | Owning proof |186| --- | --- |187| Pure policy, state transition, parser, calculation, or boundary matrix | Unit test |188| Real binding, persistence, authorization outcome, transaction, or data isolation | Integration test |189| Visible workflow, native interaction, or cross-role handoff | End-to-end test |190| Test parser, validator, selector, fixture helper, or serializer | Focused test of that executable helper |191| Declarative test or build configuration | Parser or linter plus real execution |192193Do not start an application or render a component merely to test pure policy,194state, or calculation. Do not use a unit test of policy to claim that a route,195UI, serializer, or native adapter binds it correctly. Keep one complete golden196journey for a valuable workflow, then retain focused owners only for distinct197risks that journey cannot isolate.198199### Consolidate or remove200201Consolidate distinct regression owners that repeat workflow setup or assertion202shape. Use a named object-shaped table while keeping one row for each distinct203failure mode or required side effect. Remove cases that differ only by204equivalent roles, statuses, invalid inputs, filters, or wording and drive the205same branch and public outcome.206207Remove tests or assertions that own no current regression, including:208209- branch history and retired or unreachable behavior;210- incidental or unpromised presentation, styling, geometry, token, or broad211 snapshot assertions;212- incidental wording, waits, callbacks, mock counts, mock order, or helper213 wiring;214- source or configuration mirrors that re-assert declarative files line by215 line;216- success tests that assert only status, rendering, or response-key presence217 while missing the returned values, state change, or persisted outcome;218- duplicate coverage that meets the replacement-equivalence test; and219- orphaned test-only routes, fixtures, environment switches, helpers, harnesses,220 or duration metadata with no executable caller.221222Keep appearance or layout coverage when it is an explicit stable product or223design contract. Keep exact wording, timing, geometry, lifecycle calls, or224presentation when it is itself a promised legal, accessibility, safety, domain,225or protocol contract. For authorization rejection, assert the denial and226absence of any forbidden side effect when one is possible.227228### Audit adversarially229230Apply the five-question [usefulness bar](#usefulness-bar) to each proposed231keeper. For each deletion, challenge whether the named replacement really232drives the same branch and outcome. Recheck every affected high-risk boundary233after the challenge.234235For a portfolio-reduction audit, report before-and-after test file counts,236expanded test counts, and measured runtime when the environment can measure it237reliably. State limitations rather than estimating.238239## Classifications240241Use these labels when auditing tests or assertions:242243- `keeper`: is the executable owner of a distinct reachable regression in244 promised behavior.245- `consolidate`: owns a distinct reachable regression but repeats the setup or246 assertion shape of other distinct owners; retain the cases while sharing one247 table or setup.248- `move`: owns a real risk, but the same intended outcome should be proved at a249 different boundary; relocate the test without changing that outcome.250- `rewrite`: points at the right risk and boundary but uses a stale API shape,251 brittle text, timing, fixture internals, implementation detail, or a252 tautological expected value.253- `delete`: covers removed behavior, impossible state, branch-local history,254 equivalent duplicate coverage with another retained owner and no distinct255 regression, orphaned test infrastructure, or "absence of old thing" with no256 compatibility promise.257- `changed-but-useless`: a changed or added test passes but would not catch a258 verified reachable future bug.259- `missing`: a verified reachable change-relevant regression risk has no test.260- `no-test-needed`: changed behavior was inspected, and adding coverage would261 only test branch-local history, removed behavior, duplicate coverage, or a262 low-risk addition with no verified reachable future failure to catch.263- `no-change-needed`: related tests exist, but they already cover the changed264 contract and do not need edits.265- `dangerous-removal`: deletes the last executable owner of a promised266 regression without an equivalent, inspected replacement at the required267 cadence.268269## Usefulness bar270271A test earns its keep only when you can answer all five:2722731. What future code change could make this fail?2742. Would that failure be a real product, API, data, security, or workflow bug?2753. Is this the lowest practical level that proves the behavior?2764. Does it assert stable behavior instead of private implementation details?2775. Does the expected value come from an independent source of truth rather than278 the same logic the implementation uses?279280Treat "regression risk" as something demonstrated, not imagined. Good evidence281includes an existing caller, route, screen, permission path, data migration,282parser input, security boundary, or a real bug class already seen in the code.283Security vulnerabilities, auth bypasses, data loss, and migration corruption are284strong reasons to add regression tests when the failing path is real.285286For frontend tests, prove behavior or state: navigation, submitted data,287enabled/disabled controls, permissions, validation, persisted state, loaded288records, or stable accessibility contracts. Do not add tests that only lock289incidental UI copy unless that exact copy is the current product contract, such290as a legal notice, required error message, or accessibility label.291292### Good test signals293294- It would fail if a future refactor broke the new contract.295- It checks a real caller, route, screen, permission, state transition, data296 conversion, validation rule, migration, or boundary.297- For frontend tests, it proves a user-visible behavior or stable app state,298 such as a saved value appearing, an action becoming available, validation299 blocking submission, navigation changing, or protected UI staying unavailable.300- It covers both sides of a meaningful branch when both are product behavior.301- It uses public API/UI output or a stable domain boundary.302- It asserts a known-good literal, worked example, fixture, or spec-derived303 expected value.304- It prevents a regression that has happened before or is demonstrably reachable305 from the changed code.306307### Waste signals308309- It asserts a removed field is absent after the API was intentionally replaced.310- It tests branch-local history, such as "old behavior no longer happens."311- It proves an impossible internal state is handled after upstream validation312 already rules it out.313- It checks mock call counts, exact call order, intermediate state, or private314 helper output when the public result would catch the bug.315- It recomputes the expected value the same way the implementation does, so the316 test passes by construction.317- It snapshots large payloads where only one field matters.318- It duplicates nearby coverage without protecting a different risk.319- It adds status-code-only or render-only tests that would pass while the real320 contract is broken.321- It asserts random UI copy, headings, button text, placeholders, or marketing322 text without proving the user flow, state change, permission, data rendering,323 or accessibility contract that copy belongs to.324- It leaves nearby tests untouched even though their asserted contract changed.325- It changes a test only to prove that removed behavior is gone, without a326 compatibility, privacy, migration, or security reason.327- It adds coverage for an intentional removal just to prevent the old feature328 from returning, with no current contract that the old feature must stay329 absent.330- It adds coverage for a new feature where the only possible failure is a331 harmless implementation detail, duplicated nearby coverage, or branch-local332 history.333- It says "this could regress" without showing a reachable path through current334 code, contracts, data, permissions, or user flows.335336## Output337338For a review, report:339340- `Keep`: tests that protect real behavior.341- `Consolidate`: distinct regression owners that should retain separate cases342 while sharing one table or setup.343- `Move`: real risks proved at the wrong level.344- `Rewrite`: tests that target a real risk but assert it poorly.345- `Delete`: tests/assertions that do not earn their keep.346- `Changed but useless`: changed tests that pass but should not exist.347- `Missing`: specific verified reachable behavior risks that need coverage.348- `No test needed`: changed behavior inspected where new coverage would not349 catch a verified reachable future bug.350- `No change needed`: related tests inspected and why they still fit.351- `Dangerous removals`: removed tests or assertions that were the last352 executable owner of promised behavior and have no equivalent, inspected353 replacement at the required cadence.354- `Ownership`: the named executable replacement for each removed test or355 assertion that owned a still-promised regression, or why the removed coverage356 owned no current regression and needs no replacement; also state why adjacent357 coverage misses each keeper's or consolidated case's distinct regression.358- `Validation`: commands run and result. For a portfolio-reduction audit, add359 before-and-after file counts, expanded test counts, and measured runtime when360 reliable.361362For each `Consolidate`, `Move`, `Rewrite`, `Delete`, `Missing`, or `Dangerous363removal`, include the smallest useful reason:364365```md366`path/to/test.ts`: delete the `systemAlerts` absence assertion. The API no367longer exposes that field, and no compatibility/privacy contract requires368testing its absence. Assert the new alert list shape instead.369```370371### Stale API assertion example372373If a PR replaces:374375```ts376expect(json.data.alerts.systemAlerts.length).toBe(2);377```378379with:380381```ts382expect(json.data.alerts).not.toHaveProperty("systemAlerts");383```384385flag the new assertion for deletion unless the API explicitly promises that386`systemAlerts` must never appear for compatibility or privacy reasons. A better387test would assert the new alert contract that callers now consume.