Skip to content
This repository was archived by the owner on Aug 23, 2026. It is now read-only.

refactor(quality): Rector wave 6 — null-coalesce default gate, phantom TTL fallback removed (AID-542) - #193

Merged
abkrim merged 1 commit into
mainfrom
abdelkarim/aid-542-rector-null-coalesce-default
Jul 17, 2026
Merged

abkrim merged 1 commit into
mainfrom
abdelkarim/aid-542-rector-null-coalesce-default

Conversation

@abkrim

@abkrim abkrim commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Resolves the ApplyDefaultInsteadOfNullCoalesceRector split from AID-531 — the opposite way from mechanical application: the two flagged sites are fixed by hand, and the rule stays armed as the gate's guard.

What the rule actually does (ticket premise corrected)

Not the classic ??→?: trap: it rewrites helper('key') ?? $default into helper('key', $default). That form still has a non-equivalent corner — a key present with explicit null returns $default under ??, but null under the parameter form.

What the review found at the two sites

Both read config('unblock.hq.ttl') ?? 7200. The corner is unreachable by construction: config/unblock.php defines the key as (int) env('HQ_WHITELIST_TTL', 14000) — always present, always int. Which exposed the real defect:

  • the ?? 7200 was dead code, and
  • a phantom default: the code (and its // Default 2 hours comment) claimed 7200 while the real default is 14000, owned by the config file.

Both fallbacks removed → the default has a single owner. Sanity-checked: config('unblock.hq.ttl') = 14000 (int).

The suite caught what reachability analysis alone could not

One test pinned the phantom fallback by fabricating the impossible state: config()->set('unblock.hq.ttl', null) at runtime, then asserting 7200. "Not configured" really means the env var is absent — in which case config resolves 14000 at load time; null never occurs. The test was the fallback's only consumer, and it consumed a state that cannot exist (the recorded lesson: a test that constructs the state directly is testing its own setup). It now pins the real contract: config-owned 14000 flowing through to the mailable, with an explicit expect(config(...))->toBe(14000) guard.

The rule stays enabled as a guard

  • Zero-diff after the hand fix (refactor:dry exit 0, cleared cache)
  • Proven to bite: a new config('x') ?? $default → rc=2 naming app/ProbeCoalesce.php
  • Future config('x') ?? $d is always either dead or a duplicated default; if a genuinely present-but-null key ever appears, skip that call site — not the rule

Gates

composer check-full → exit 0, 1442 passed / 16 skipped (3585 assertions), coverage 89.8%.

…m TTL fallback removed (AID-542)

Resolve the ApplyDefaultInsteadOfNullCoalesceRector split (AID-531) the
opposite way from mechanical application: fix the two flagged sites by
hand and keep the rule armed as the gate's guard.

The ticket assumed the classic ??→?: trap; the rule actually rewrites
`helper('key') ?? $default` into `helper('key', $default)`. That form
still has a non-equivalent corner — a key present with an explicit null
returns $default under ??, null under the parameter form — but both
flagged sites read `config('unblock.hq.ttl') ?? 7200`, where that
corner is unreachable by construction: config/unblock.php defines the
key as `(int) env('HQ_WHITELIST_TTL', 14000)`, so it always exists as
an int. Which exposed the real defect: the ?? 7200 was dead code AND a
phantom default — the code (and its "Default 2 hours" comment) claimed
7200 while the actual default is 14000, owned by the config file. Both
fallbacks are removed; the default now has a single owner.

The suite caught what the reachability analysis alone could not: one
test pinned the phantom fallback by fabricating the impossible state
(`config()->set('unblock.hq.ttl', null)` at runtime, then asserting
7200). "Not configured" really means the env var is absent, in which
case config resolves 14000 at load time — null never occurs. The test
was the fallback's only consumer, and it consumed a state that cannot
exist; it now pins the real contract (config-owned 14000 flowing
through to the mailable) with an explicit guard assertion.

The rule stays enabled: zero-diff after the hand fix, proven to bite
(a new `config('x') ?? $default` fails refactor:dry naming the file),
guarding against future phantom or duplicated defaults. If a genuinely
present-but-null key ever appears, skip that call site — not the rule.

Gates: check-full green, 1442 passed / 16 skipped (3585 assertions),
coverage 89.8%, refactor:dry (cleared cache) exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@abkrim
abkrim merged commit b341c67 into main Jul 17, 2026
2 checks passed
@abkrim
abkrim deleted the abdelkarim/aid-542-rector-null-coalesce-default branch July 17, 2026 19:36
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant