fix(profile,nav): Home reached the Cat, banners rendered black, and the profile published needs as offers - #843
Merged
Conversation
…rd was unreachable
/dashboard's page component router.replace()d every visitor to /dashboard/cat
("Cat-first", 2026-07-13). But /dashboard is what the sidebar's "Home" row, the
mobile tab bar, the breadcrumb "Dashboard" crumb, the 404 page's "Go to
Dashboard" and every RouteError recovery link point at — so all of them landed
on the Cat hub, the destination the "Cat" row already owned. The dashboard only
rendered when the timeline fetch *errored*, which is why nobody noticed.
/dashboard renders the dashboard again. Cat-first is kept where it belongs:
sign-in still lands on CAT_WELCOME (auth/callback + auth/confirm, untouched),
and "/" — which the brand mark points at — now redirects to the Cat rather than
bouncing through /dashboard.
The existing mobile-tab-bar test compared hrefs as strings, so two rows that
resolved to one surface through a client redirect read as distinct. New gate
follows the redirect: an authenticated nav destination may not redirect to
another authenticated nav destination. Proven by mutation — reinstating the
router.replace turns it red with "/dashboard → /dashboard/cat".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sh5aTRjzkcZkTyiu9D5RCM
… header, and needs published as offers
Five defects reported from one pass over a live profile. All of them share a
shape: the code says the right thing and the screen shows something else.
**1. Every banner and avatar scrim rendered solid black.** Tailwind v4 deleted
the `*-opacity-*` utilities. They do not warn — they stop existing, so
`bg-black bg-opacity-20` keeps `bg-black` and drops the 20%. The profile
banner's scrim became an opaque rectangle over every maker's banner image (the
image itself was never the problem: sampling its pixels in the live page gives
an average of rgb(182,142,95)), and the avatar's hover scrim in the profile
editor became a black square. Rewritten to the v4 slash modifier. A new gate
scans src for any surviving `*-opacity-N`, proven by mutation.
**2. The sidebar context switcher listed "Audit WF009 …" groups** — throwaway
teams minted by workflow audits, which enrol the account they run as.
config/public-directory already classifies these and keeps them off Discover
and People; the switcher was a second reader that never imported the rule. It
does now, so "fixture group" still has exactly one definition. The rows are
hidden, not deleted.
**3. In that dropdown the user's own row showed a generic person glyph** while
every group below it showed its real picture — the one row a person can
recognise at a glance was the anonymous one.
**4. The profile header read as three unrelated boxes.** The gap between banner
and identity card was set in two components that could not see each other
(`mb-4 sm:mb-6 lg:mb-8` in ProfileBannerSection, `mt-12 sm:mt-16 md:mt-20` in
ProfileLayout) while a third value — the avatar's overhang — decided how much
clearance was actually needed. The avatar floated in a band of background
belonging to neither box, and its left edge missed the name's by 8px because it
used `left-3 sm:left-6 lg:left-8` against the card's `p-4 sm:p-6`.
The seam now has one owner: the banner sets no bottom margin, and the identity
card carries top padding sized to the overhang, so the avatar overlaps the card
the way it overlaps the banner. Overhang is half the avatar at every breakpoint
(it was 1/2, 3/5, 2/3 and 1/2), and its inset matches the card's padding.
Measured on a running build, logged out:
1280px — avatar l40 b480, card top 432 (48px overlap), h1 l41;
previously a 41px dead gap with a 7px offset. scrollWidth 1265/1280.
390px — avatar l28 b232, card top 212 (20px overlap), h1 l29.
scrollWidth 375/390 — no horizontal scroll on a phone.
**5. "What I can offer" published the user's needs as things they supply.** The
extraction prompt defined `asked_for` as "what people come to them for" and
stopped there. It runs over a chat with an assistant, where the most common
"ask" by far is the user asking the Cat for something — so it recorded their
own requests and needs as things they OFFER. A live profile listed "suggestions
on what to offer", "helpful assistance", "vet care in Zürich" and "funding for
dog surgery" under "People come to me for". The field now states the direction,
names the trap, says to leave it empty when in doubt, and the worked example
contrasts the two readings.
The card also told the owner "Only you see these create shortcuts" — true of the
+ buttons, and easy to read as covering the entries. It does not: a logged-out
visitor sees every skill, asset and asked-for. The footer now says "Visible to
everyone", keeps the shortcut hint separate, and links to the Cat, which is the
only thing that can correct this data (removeFromEconomicProfile). When the
section is empty the owner gets that invitation instead of a component that
rendered nothing.
The extraction prompt moves to its own module (economic-profile-prompt.ts): it
is prose on its own cadence, reviewing a wording change should not mean
scrolling past merge/upsert code, and the expanded note pushed the service past
the 500-line limit.
Verified with `npm run verify`. Worth knowing: `core.hooksPath` is the relative
`.husky/_`, which is gitignored and generated by npm install, so it exists only
in the main checkout — inside a worktree git finds no pre-commit hook and a
commit prints nothing but the commit line. The size failure above was caught
only because verify was run by hand.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sh5aTRjzkcZkTyiu9D5RCM
catomean
force-pushed
the
worktree-fix-home-dashboard
branch
from
August 29, 2026 10:11
81d9c52 to
437f441
Compare
catomean
added a commit
that referenced
this pull request
Aug 29, 2026
…s two siblings (#845) Verifying #843 on the live site showed the context switcher had gone from three junk groups to one: "Audit WF009 …" and "Audit WF009b …" were correctly hidden, and "Audit Group 1783191071580" was still sitting there. `FIXTURE_GROUP_TITLE` is an allow-list of wordings we happened to have seen — `audit wf`, `ephemeral verify`, `workflow audit`. Audits keep inventing new ones, so the list is always one fixture behind, and the failure is silent: the row simply looks like a team the user joined. So match the *generating* signature instead of the wording: an audit-ish first word followed by the millisecond epoch a fixture appends to keep names unique. Keying on the timestamp is what makes widening safe — a real group is named by a person, and people do not append an epoch. That matters more here than in most products, because OrangeCat is heading for governance, where "Audit Committee" is a group somebody will genuinely create. Tests pin both directions, and the predicate is proven by mutation: drop the new clause and "Audit Group 1783191071580" comes back. Because this is the shared SSOT, it fixes Discover and People too, not just the switcher. Claude-Session: https://claude.ai/code/session_01Sh5aTRjzkcZkTyiu9D5RCM Co-authored-by: Georgy Butaev <41178744+g-but@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <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.
Six defects reported from one pass over a live profile. They share a shape: the code says the right thing and the screen shows something else, and in five of the six nothing was red.
1. "Home" and "Cat" were the same destination, and the dashboard was unreachable
/dashboard's page componentrouter.replace'd every visitor to/dashboard/cat("Cat-first", 2026-07-13). But/dashboardis what the sidebar's Home row, the mobile tab bar, the breadcrumb "Dashboard" crumb, the 404 page's "Go to Dashboard" and everyRouteErrorrecovery link point at — so all of them landed on the Cat hub, the destination the Cat row already owned. The dashboard rendered only when the timeline fetch errored, which is why nobody noticed it was gone./dashboardrenders the dashboard again. Cat-first is kept where it belongs: sign-in still lands onCAT_WELCOME(auth/callback+auth/confirm, untouched), and/— which the brand mark points at — redirects to the Cat rather than bouncing through/dashboard.2. Every banner and avatar scrim rendered solid black
Tailwind v4 deleted the
*-opacity-*utilities. They do not warn; they stop existing. Sobg-black bg-opacity-20keepsbg-blackand drops the 20% — an opaque rectangle over every maker's banner image, and an opaque square over the avatar's hover scrim in the profile editor.The images were never the problem: sampling the banner's pixels in the live page gives an average of
rgb(182,142,95).3. The context switcher listed "Audit WF009 …" groups
Throwaway teams minted by workflow audits, which enrol the account they run as.
config/public-directoryalready classifies these and keeps them off Discover and People — the switcher was a second reader that never imported the rule. It does now, so "fixture group" still has exactly one definition. The rows are hidden, not deleted.4. …and drew the user's own row anonymously
A generic person glyph, while every group below it showed its real picture — the one row a person can recognise at a glance was the anonymous one.
5. The profile header read as three unrelated boxes
The gap between banner and identity card was set in two components that could not see each other (
mb-4 sm:mb-6 lg:mb-8inProfileBannerSection,mt-12 sm:mt-16 md:mt-20inProfileLayout) while a third value — the avatar's overhang — decided how much clearance was actually needed. The avatar floated in a band of background belonging to neither box, and its left edge missed the name's by 8px (left-3 sm:left-6 lg:left-8against the card'sp-4 sm:p-6).The seam now has one owner: the banner sets no bottom margin, the identity card carries top padding sized to the overhang, and the avatar overlaps the card the way it overlaps the banner. Overhang is half the avatar at every breakpoint (it was 1/2, 3/5, 2/3 and 1/2); its inset matches the card's padding.
The loading skeleton had guessed its own geometry — a 192px banner where the real one is 128px on a phone, a 96px avatar where the real one is 64px — so the header visibly jumped when content arrived. It mirrors the real measurements now.
Measured on a running build, logged out:
Previously 1280px had a 41px dead gap and a 7px offset. No horizontal scroll at 390px.
6. "What I can offer" published people's needs as things they supply
The extraction prompt defined
asked_foras "what people come to them for" and stopped there. It runs over a chat with an assistant, where the most frequent ask by far is the user asking the Cat for something — so it recorded their own requests and needs as things they OFFER, on a public profile. A live account listed "suggestions on what to offer", "helpful assistance", "vet care in Zürich" and "funding for dog surgery" under People come to me for.The field now states the direction, names the trap, says to leave it empty when in doubt, and the worked example contrasts the two readings. The prompt moves to its own module — it is prose on its own cadence, and reviewing a wording change should not mean scrolling past merge/upsert code.
The card also told the owner "Only you see these create shortcuts" — true of the
+buttons, and easy to read as covering the entries. It does not: a logged-out visitor sees every skill, asset and asked-for. The footer now says "Visible to everyone", keeps the shortcut hint separate, and links to the Cat — the only thing that can correct this data (removeFromEconomicProfile). When the section is empty the owner gets that invitation instead of a component that rendered nothing.Gates
Three, each proven by mutation (reinstate the bug → red; revert → green):
nav-destinations-do-not-bounce— an authenticated nav destination may not redirect to another one. The existingmobile-tab-bartest compared hrefs as strings, so two rows resolving to one surface through a client redirect read as distinct.tailwind-v4-dead-utilities— no*-opacity-Nanywhere insrc.useNavigationContext-hides-fixture-groups— the switcher lists only real groups.Verification
npm run verifygreen over the final tree.Worth knowing:
core.hooksPathis the relative.husky/_, which is gitignored and generated bynpm install, so it exists only in the main checkout. Inside a worktree git finds no pre-commit hook and a commit prints nothing but the commit line. An ungated commit here shipped a file over the 500-line limit; only runningverifyby hand caught it.Not done here
The two
Audit WF009 …rows and the incorrect economic-profile entries still exist in production data — this PR stops them being created and shown, but does not delete rows.🤖 Generated with Claude Code
https://claude.ai/code/session_01Sh5aTRjzkcZkTyiu9D5RCM