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

refactor(quality): Rector wave 7 — Date facade and today() (AID-541) - #194

Merged
abkrim merged 2 commits into
mainfrom
abdelkarim/aid-541-rector-carbon-date-facade
Jul 18, 2026
Merged

abkrim merged 2 commits into
mainfrom
abdelkarim/aid-541-rector-carbon-date-facade

Conversation

@abkrim

@abkrim abkrim commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Seventh wave, split from AID-531 over temporal semantics. 6 app/ files (the ticket said 4+2: the two today() sites share one file, and two more Carbon sites surfaced on the full run — the initial dry-run listing had been truncated by a head cut when first sized).

The headline risk is refuted, not assumed away

The ticket feared "changes the runtime class". No Date::use()/DateFactory configuration exists anywhere, so the Date facade fabricates Carbon\Carbon — the concrete runtime class does not change at all. Remaining checks, all verified repo-wide:

  • zero instanceof Carbon
  • every converted value is consumed immediately (->toDateTimeString(), ->format(), ->greaterThan(), SQL comparison) — the object never escapes a boundary
  • today() ≡ now()->startOfDay() under the app's single timezone; both honour Carbon::setTestNow()
  • the facade keeps Date::use() available as a future seam and is the Laravel idiom

Hand finish: CommandOutputParserTrait kept a dead use Carbon\Carbon (its only remaining "use" was a comment) — Pint's preset here does not enforce no_unused_imports, so import and now-lying comment were removed manually.

First commit: deterministic midnight-window flake fixed (AID-552)

check-full went red at 00:57 local on a test untouched by this wave: CheckRecentBlockHistoryActionTest re-evaluated now()->subDay() and compared its app-timezone date (.env.testing runs Europe/Madrid) against the UTC ISO serialization. Between 00:00–02:00 CEST the calendars disagree by one day — red only in that window, latent since written.

Reproduced on both sides of the window with setTestNow: old assertion fails at 00:57 Madrid (searches 2026-07-17 inside 2026-07-16T22:57:14Z), passes at noon; the fixed one (single captured time origin + toBe($lastBlock->toISOString()), matching the sibling test's style) passes in both. Split into its own commit with its own ticket per the axis AID-499 pattern.

Gates

  • Both rules bite, attributably: Carbon::now() probe → rc=2 naming ProbeCarbon.php; now()->startOfDay() probe → rc=2 naming ProbeToday.php
  • Idempotent (second pass byte-level no-op) · refactor:dry (cleared cache) exit 0
  • composer check-full → exit 0, 1442 passed / 16 skipped, coverage 89.8%

abkrim and others added 2 commits July 18, 2026 06:45
…AID-552)

The last_blocked_at assertion re-evaluated now()->subDay() and compared
its app-timezone date against the model's UTC ISO serialization.
.env.testing runs Europe/Madrid, so between 00:00 and 02:00 local the
two calendars disagree by one day and the test goes red — exactly the
window where tonight's check-full ran (00:57). Latent since it was
written; outside the window it always passed.

Fix follows the sibling test in the same file: capture a single time
origin once ($lastBlock = now()->subDay()->startOfSecond()), feed it to
the factory AND the assertion, and pin the exact contract
(toBe($lastBlock->toISOString())) instead of a substring. startOfSecond
matches the database's second-precision storage. No clock freezing —
that would hide the symptom, not remove the double evaluation.

Reproduced on both sides of the window with setTestNow: the old
assertion fails at 00:57 Madrid (searches 2026-07-17 inside
2026-07-16T22:57:14Z) and passes at noon; the new one passes in both.

Unrelated to the Rector wave in the next commit — split out per the
axis AID-499 pattern: a pre-existing test fix that unblocks a gate goes
in its own commit with its own ticket.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Enable CarbonToDateFacadeRector and
NowFuncWithStartOfDayMethodCallToTodayFuncRector; apply with cleared
cache: 6 app/ files (the ticket said 4+2, but the 2 today() sites
shared one file and two more Carbon sites surfaced on the full run —
the dry-run listing had been truncated by a head cut when first sized).

The ticket's headline risk — "changes the runtime class" — is refuted,
not assumed away: no Date::use()/DateFactory configuration exists
anywhere, so the Date facade fabricates Carbon\Carbon and the concrete
runtime class does not change at all. Remaining checks: zero
`instanceof Carbon` repo-wide; every converted value is consumed
immediately (->toDateTimeString(), ->format(), ->greaterThan(), SQL
comparison) so the object never escapes a boundary; today() ≡
now()->startOfDay() under the app's single timezone; and both forms
honour Carbon::setTestNow(). The facade keeps Date::use() available as
a future seam and is the Laravel idiom (per the CLAUDE.md anchor).

Hand finish: CommandOutputParserTrait kept a dead `use Carbon\Carbon`
(its only remaining "use" was a comment) — Pint's preset here does not
enforce no_unused_imports, so the import and the now-lying comment were
removed manually.

Wave checklist:
- resized with --clear-cache
- both rules bite, attributably: Carbon::now() probe -> exit 2 naming
  ProbeCarbon.php (CarbonToDateFacadeRector); now()->startOfDay() probe
  -> exit 2 naming ProbeToday.php (NowFunc...TodayFuncRector)
- idempotence: second rector+pint pass is a byte-level no-op
- refactor:dry (cleared cache) exit 0 on the applied tree

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@abkrim
abkrim merged commit 1f55b82 into main Jul 18, 2026
2 checks passed
@abkrim
abkrim deleted the abdelkarim/aid-541-rector-carbon-date-facade branch July 18, 2026 04:52
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