Haziran 2026 iyileştirme raporu

19 Haziran 2026’da PayCal, korumalı iş verisi ve uyumluluk denetimi olarak başlayan, ardından Redis veri onarımı, crypto/plaintext migration hazırlığı, güvenlik sertleştirme, ödeme dönemi doğruluğu ve ayarlar UX guardrail’lerine genişleyen kapsamlı bir iyileştirme tamamladı.

Yönetici özeti

Ana kaygı Korumalı iş verileri ve eski uyumluluk davranışı varsayımla güvenilir kabul edilemezdi. Mevcut sınırları kanıtlamamız, güvenli drift’i migrate etmemiz ve canlı veri hâlâ uyumluluk gerektiriyorsa guardrail bırakmamız gerekiyordu.
Bulunan başlıca hatalar DST’ye duyarlı iki haftalık ödeme dönemi hesaplaması doğru ilerlemeyebilirdi; eski uyumluluk yolları eski route ve kavramları canlı tutuyordu; Redis indekslerinde drift vardı; ve güvenlik bulguları kapatılmalıydı.
Başlıca düzeltmeler Korumalı business iş okumaları artık kanonik erişim gate’inden geçiyor; eski shim ve aliaslar kaldırıldı; Redis drift onarıldı; crypto/plaintext auditleri eklendi; ödeme dönemi DST navigasyonu düzeltildi; güvenlik bulguları kapatıldı; ve ayar kontrolleri testlerle yeniden kuruldu.
Doğrulama Private full PHPUnit 2.318 test ve 19.222 assertion ile geçti. Public quick gate 1.311 test ve 7.698 assertion ile geçti. Private ve public PHPStan Level 9 gate’leri temizdi.

Neden araştırdık

The work began with the protected business work-data lifecycle: identity, membership, consent, access, encrypted envelope state, exports, revocation, caching, and audit evidence all had to line up. The investigation widened because old compatibility paths, route shims, skipped tests, and Redis migration artifacts made it harder to prove the current product model.

The rule was simple: old compatibility should either be proven necessary and guarded, or proven unused and removed. For sensitive work data, assumptions were not enough.

Ödeme dönemi DST hatası

One real product bug found during the cleanup was in biweekly pay-period navigation. The old calculation converted timestamp seconds into days by dividing by 86400. Across a spring daylight-saving transition, that can miscount calendar days and cause the next period to resolve incorrectly.

  • Fixed PayPeriods::biweeklyStart() to use calendar-day differences instead of timestamp seconds divided by 86,400.
  • Added PayPeriodsTest::testBiweeklyNavigationAdvancesAcrossSpringDstBoundary, which verifies America/Edmonton navigation from 2024-02-26 to 2024-03-11 and then 2024-03-25.
  • Re-enabled EarningsCalendarParityIntegrationTest::testRenderSectionsLazyDoesNotFatalWhenEncryptedWorkRowOmitsGross, which had exposed the period-navigation stall.
  • Kept PayPeriodGeneratorTest in the remediation verification set so adjacent generation behavior remains covered.

Korumalı iş verileri

  • Added and enforced BusinessProtectedDataAccess for actor authority, target membership, consent, wrap state, envelope metadata, and business site visibility.
  • Routed member reports, financial summaries, workspace warmers, team earnings, daily member JSON, and member XLSX/PDF exports through the protected gate.
  • Locked generic PDF/XLSX endpoints to personal current-user payloads and added negative tests for forged business/member payloads.
  • Moved protected business XLSX/PDF export to server-side rebuilds from authorized rows.
  • Expanded audit events for requested, denied, started, completed, and failed protected read/export outcomes.
  • Added revocation tests proving stale reports, exports, summaries, team earnings, and caches are denied or purged after access changes.

Aliaslar, yollar, yönlendirmeler, metodlar ve sınıflar

  • Removed /earnings/ compatibility route and smoke references; personal earnings and reports use /reports/.
  • Removed /profile/ compatibility route and profile-page references; account profile settings now live under settings/account.
  • Removed /js/businesses/ asset shim after canonical asset references were verified.
  • Removed settings legacy hash redirect map, JSON payload, and browser redirect logic.
  • Removed /blog/article/?slug=... redirect entrypoint from active routing and accessibility inventory references.
  • Replaced /api/v1/profile/update/ and profile/settings route declarations with account-scoped profile routes.
  • Removed unused BusinessDiscovery method aliases, ShadowTalon::init(), old Settings account route aliases, and an internal EmailVerification rate-limit wrapper.
  • Removed runtime bare work.write organization mutation compatibility; runtime grants now require explicit self/org scope or manager authority.
  • Removed generated TODO: Document... comments and unused FPDF.php placeholder class so source scans and inventories reflect active work.

Redis ve veri drift’i

  • Created a Redis checkpoint before mutation: paycal-redis-20260619-091729.rdb, SHA-256 baff0c94361b57ef67a153b242669c537165a9b576a1cfb4953b10e95fa41215.
  • Migrated old relationship/metaphor Redis keys and values: 4,288 key migrations and 599 value updates, with 0 conflicts and 0 errors.
  • Repaired connection index drift across 1,698 connections and 1,447 businesses. The fixer repaired 378 drifted records and verified post-run drift at 0.
  • Extended scripts/connections-audit.php so report-only mode identifies bare work.write drift and --fix migrates records to explicit work.scope.self.
  • Replaced the stale SQL-era Redis field migration helper with a Redis-native dry-run/execute/verify flow for text_sizing -> text and density -> spacing.
  • Removed absent compatibility paths for no-colon email index keys, theme_mode, and legacy fixed-TTL lock-boundary keys after audit verification.
  • Marked 98 seeded @paycal.app fake personas so plaintext work-entry audits can ignore them by default while still reporting them with --include-ignored.

Crypto ve plaintext iş kayıtları

  • Added CryptoCompatibilityTelemetry for unwrap source selection and outcomes.
  • Added scripts/paycal crypto:compat:audit --json to report wrapper and fallback usage.
  • Added scripts/paycal work:plaintext:audit --json to report plaintext-only rows, invalid encrypted blobs, fake-user rows, and rows requiring user-session rewrite.
  • Retired the old plaintext migration execution path so it cannot strip intentional work-entry snapshot fields.
  • Backfilled earnings snapshot fields across 64,447 work entries, including 859 missing gross values, and verified final missing gross and wage counts at 0.
  • Kept plaintext/non-encrypted read compatibility because real-user rows still require user-session encrypted rewrite.
  • Kept work-entry short-alias read compatibility because encrypted blobs and fixtures can still contain historical alias-shaped payloads, even though active Redis hash fields showed zero short aliases.

Kapatılan güvenlik bulguları

  • Active request-IP call paths now use trusted-proxy-aware client IP resolution.
  • Invite code and audit-chain hash comparisons use constant-time comparison.
  • CORS validation evaluates request origins against an explicit allowlist.
  • The development security-disable flag is ignored outside approved dev-like environments.
  • Rate-limit IP key derivation uses SHA-256 instead of MD5.
  • The admin test runner returns summarized output by default instead of raw PHPUnit details.

Ayarlar ve görünüm iyileştirmesi

  • Added a persisted sidebar Trigger preference with a 400 ms default, 200 ms minimum, 3,000 ms maximum, and 200 ms step size.
  • Cancelled delayed sidebar timers on pointer exit, document leave, and window blur.
  • Grouped sidebar navigation into Personal, Business, and bottom Utility actions, with Settings, Help, and Sign Out anchored together.
  • Replaced the Accent dropdown with 16 spectrum swatches, hover popovers, shared accent tokens, and a live preview window.
  • Renamed Accent color to Accent, Density preset to Density, and Variant to Mode.
  • Rebuilt notification position selection as a spatial grid using canonical top/bottom values.
  • Fixed settings nav hover, active, and focus states after a global link hover rule produced a poor white hover block in the dark settings header.

İleriye dönük guardrail’ler

  • Protected business work rows must originate from the canonical protected access gate before any report, export, cache, warmer, or audit path can use them.
  • Old compatibility paths must be classified as required, migrated, or removed. PayCal will not keep old behavior alive only because it is convenient.
  • Data migrations require report-only audit, checkpoint, fix, report-only verification, and regression tests.
  • Compatibility that protects real user data stays until telemetry and audits prove removal is safe.
  • Pay-period and earnings code must use calendar-aware date math across timezone and DST boundaries.
  • Skipped tests must have a clear environment reason; deterministic product regressions should run in the normal gate.
  • Settings controls must be visibly connected to the preference they configure, bounded by persisted defaults, and protected by rendered-contract tests.