Collapse the fake user layer #53
Labels
No labels
bug
duplicate
enhancement
good first issue
help wanted
question
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lvl0/incr#53
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The app is single-user with no auth enforcement, yet carries a full user system that exists only to be bypassed.
Current state
User::default()(app/Models/User.php:40-48) invents a fakeuser@incr.localwith a random bcrypt password, and every controller opens withUser::default()->trackerplus a null-guard. Around it sitsAuthenticatable,Notifiable, hashed-password casts,users/sessions/password_reset_tokenstables,RegisteredUserController,routes/auth.php,LoginRequest,ProfileUpdateRequest— none of it enforcing anything.Scope
app/Http/Controllers/Auth/RegisteredUserController.php,routes/auth.phpand itsrequireinroutes/web.phpapp/Http/Requests/Auth/LoginRequest.php,app/Http/Requests/Settings/ProfileUpdateRequest.phpUserto nothing, or remove it entirely in favour of a singletonTrackerresolved byTracker::firstOrCreate()users,sessions,password_reset_tokensifUsergoesOpen question — resolve during planning
Whether
Userdisappears entirely or stays as a hollow owner record. Removing it is cleaner now; keeping it costs one table and makes multi-user a smaller change if that ever comes back. Recommend removing — it can be reintroduced properly if ever needed, and speculative retention is the habit this milestone is correcting.Depends on
#48 and #50 — those remove most
User::default()->trackercall sites, so this lands on a much smaller surface afterwards.Acceptance criteria
Live bug to fix here:
/registeris already brokenSurfaced by a
code-reviewerpass during #47. Not caused by #47 — traced to04fbda4("43 - Delete dead Breeze auth/settings boilerplate, slim auth routes to register-only"), which removed the page component but left the controller and route behind.Current state:
app/Http/Controllers/Auth/RegisteredUserController.php:28returnsInertia::render('auth/register')routes/auth.php:7registersGET /register;routes/web.php:54requires it, so the route is liveresources/js/pages/auth/register.tsxdoes not exist — confirmed absent since04fbda4Inertia resolves page components via
import.meta.glob('./pages/**/*.tsx')(resources/js/app.tsx:12), so this fails at runtime, not build time — which is why no gate caught it.Impact: on a fresh install
User::exists()is false, so/registeris the first-run setup path. It would fail for any new deployment.Resolution: this ticket already deletes
RegisteredUserController,routes/auth.php,LoginRequestandProfileUpdateRequest, which removes the dangling route entirely. No need to restore the page component — just make sure the route/controller removal actually lands rather than only trimming the model.Add to acceptance criteria:
GET /registerreturns 404 (route gone), not an Inertia resolution errorInertia::render()call anywhere references a non-existent page componentPHPStan level 8 finding in code this ticket deletes
While setting up #55 I tried level 8 to see what it would take. One real null-safety bug turned up in a file this ticket removes:
$this->user()->idassumes an authenticated user, but the app has no auth —user()is always null here. The file is unreachable (nothing routes to it), so it is latent rather than live, but it is a genuine defect and not a false positive.No action needed beyond deleting the file, which this ticket already scopes.
Consider raising to level 8 after this lands
The project is clean at level 7 with no baseline. Level 8 reports 8 errors across 4 files, and two of those files are deleted by this ticket and #52:
ProfileUpdateRequest.phpHandleInertiaRequests.phpCounterTest.php,TrackerTest.php,CountBackfillMigrationTest.phpModel::first()returning?Model— one-line fixesSo after this ticket and #52, level 8 costs roughly five
assertNotNull()calls in tests. Worth doing then rather than now, since fixing code scheduled for deletion is wasted effort.Done —
c34269d21 files, −292 lines.
Useris gone entirely.Decision on the open question: remove
Usercompletely rather than keep a hollow owner record. WithTrackerdown tolabel/unit/countthere was nothing meaningful being owned, and thebelongsTowas pure ceremony.Deleted:
User,UserFactory,RegisteredUserController,LoginRequest,ProfileUpdateRequest,routes/auth.php,config/auth.php.Trackeris standalone, resolved byTracker::current()(explicitorderBy('id')->first()).Migration
2026_08_15_000004_drop_users_and_sessions.php— dropstrackers.user_idFK and column, thensessions,password_reset_tokens,users.Verified live after migrating:
GET /trackerreturns{"exists":true,"tracker":{"id":1,"label":"Counter","unit":"units","count":1005,...}}— nouser_id, noasset_id, count intact.Required side effect: session driver
Changed
database→cookiein.env,.env.example, and theconfig/session.phpdefault.This was not optional. With no auth, sessions exist only to carry the CSRF token — dropping the
sessionstable on the database driver would have broken every POST. The config default was changed too, so a missing env var cannot fall back to a table that no longer exists.Tests run with
SESSION_DRIVER=arrayand would never have caught this, so it was verified against the running app:POST /incrementsucceeded with a real CSRF token (1005 → 1006),PATCH /countrestored it.config/auth.phpdeletedIt still referenced
User::classafter the model was gone — a latent fatal that only fires when the auth provider resolves. Confirmed safe: noAuth::,auth(),config('auth, or auth middleware anywhere in the codebase.Review found a real defect; testing the fix found the fix was wrong
Review correctly spotted that
down()restoredtrackers.user_idasnullable()while the original (2026_05_02_000001:14) is non-nullable. Removingnullable()and testing the round trip failed:A non-nullable FK cannot be restored here — existing trackers have no user to point at, so every row would get
user_id = 0and violate the constraint.nullable()is load-bearing. Reverted with a comment explaining why, so it does not get "corrected" again.This is the third
down()transcription mismatch in this milestone (#49, #48, #53). Practice changed: write the round-trip test alongside the migration rather than hand-checking the transcription.New tests
DropUsersMigrationTest(5 tests): migrated schema has no user tables,down()restores them, restoreduser_idis deliberately nullable,up()re-drops respecting FKs, and the counter survives the round trip.Also
BCRYPT_ROUNDSfrom.env.exampleusersrow for the old FK; removed2026_08_15_000003'sdown()used->after('user_id'), which no longer exists; positional hint droppedauth.userInertia shared propNoted, not fixed
TrackerController::store()has a check-then-create race on the singleton invariant. Pre-existing, negligible for a single-user app, and #52 rewrites that controller.Gates
PHPStan 0 errors (level 7) · Pint PASS 43 files · PHPUnit OK 36 tests, 93 assertions
Level 8 is now one file closer — only
HandleInertiaRequests.phpremains, which #52 deletes.