Repository navigation
A wrong /app URL dropped you onto the marketing page - #2
Merged
Merged
Conversation
HANDOFF said unknown `/app/*` paths render the shell empty. They do not, and the real behaviour is worse: with no child matching, `/app` does not match either, so the top-level `*` caught them and redirected to `/`. A signed-in user who mistyped a URL was dropped out of the app onto the landing page - which is indistinguishable from having been signed out, and on a screen holding clinical data that is the one thing it must not look like. Measured both ways against the real route table: before, `/app/typo` ended at `/` showing the hero and no shell; after, it stays at `/app/typo`, inside the shell, and says the address is wrong and the session is fine. `/app/reports/nonsense` is caught too, and the real children still resolve - `/app` and `/app/review` unchanged. The catch-all is a child of `/app` and stays inside the guard on purpose. Redirecting anywhere would reproduce the bug it fixes. NotFound is imported statically rather than lazily like the other screens: it is a heading and a link, and making it a chunk means a network round trip to tell somebody their URL is wrong, on the one path where the network may be why they are here. Also the last empty state without a door. /app/review/learned explained itself well and offered nothing to do; it now links to the queue the rules come from. And the polish list itself, which was sending the next session after finished work. Re-read against the code: the upload screen already says what happens next, prefers-reduced-motion is already handled in CascadeScene and tokens.css, and the 404 entry had the wrong diagnosis. Three of five entries were stale. A to-do list that advertises done work costs the next reader the same time it cost this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second pass over LabLedger — this time the auth core, the access choke point,
the sharing flow, the security headers and the routing. I found no hole in the
security model; what I found was in the routing, and a to-do list that was
sending readers after finished work.
The bug, and the diagnosis that was wrong
HANDOFF said:
They do not. With no child matching,
/appdoes not match either — so thetop-level
*caught them and redirected to/. A signed-in user whomistyped a URL was dropped out of the app onto the marketing landing page.
That is indistinguishable from having been signed out, and on a screen holding
clinical data it is the one thing a typo must not look like.
Measured both ways, against the real route table (real
App, real guard, fakeaxios adapter so no Mongo is needed):
The catch-all is a child of
/appand stays inside the guard — redirectinganywhere would reproduce the bug it fixes.
NotFoundis imported staticallyrather than lazily like the other screens: it is a heading and a link, and
making it a chunk means a network round trip to tell somebody their URL is
wrong, on the one path where the network may be why they are here.
The last empty state without a door
/app/review/learnedexplained itself well and offered nothing to do. It nowlinks to the queue the rules come from.
The polish list was mostly stale
Re-read against the code, three of five entries were already done:
prefers-reduced-motionunhandledCascadeScene.jsxreadsmatchMediabefore animating;tokens.csscarries the global rule/app/*renders shell emptyHANDOFF is corrected, including keeping the wrong diagnosis on the record. A
to-do list that advertises finished work costs the next reader exactly the time
it cost this one.
What I audited and did not change
that ends the session, idle timeout enforced in
deps._live_sessionandagain in
/refresh— I went looking specifically for a refresh that couldwalk around the idle timeout, and it is explicitly closed with a comment
saying why.
checked only after the password verifies, per-account cooldown on codes,
failures audited.
forged, and accepting can never downgrade access already held.
live()filter for revocation and expiry, 404-never-403throughout.
no-storeon every response,default-src 'none', HSTS in prod.One genuinely dead thing worth a later tidy:
models/session.pyexports alive()helper that nothing imports —auth.pybuilds the same filter inlinefrom
idle_cutoff(). Two ways to express one rule is how they drift, but it isnot a bug today and did not belong in this change.
Verification
vite buildclean,oxlintclean on the touched files,vitest18/18. Theroute behaviour above was driven in a browser against the real
App, with athrowaway fake-adapter harness that was deleted before commit.