Skip to content

fix(profile,nav): Home reached the Cat, banners rendered black, and the profile published needs as offers - #843

Merged
github-actions[bot] merged 2 commits into
mainfrom
worktree-fix-home-dashboard
Aug 29, 2026
Merged

fix(profile,nav): Home reached the Cat, banners rendered black, and the profile published needs as offers#843
github-actions[bot] merged 2 commits into
mainfrom
worktree-fix-home-dashboard

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

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 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 rendered only when the timeline fetch errored, which is why nobody noticed it was gone.

/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 — 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. So bg-black bg-opacity-20 keeps bg-black and 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-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.

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-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 (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, 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:

viewport avatar card top overlap h1 left page scrollWidth
1280px l40 b480 432 48px 41 1265 / 1280
390px l28 b232 212 20px 29 375 / 390

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_for as "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 existing mobile-tab-bar test compared hrefs as strings, so two rows resolving to one surface through a client redirect read as distinct.
  • tailwind-v4-dead-utilities — no *-opacity-N anywhere in src.
  • useNavigationContext-hides-fixture-groups — the switcher lists only real groups.

Verification

npm run verify green over the final tree.

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. An ungated commit here shipped a file over the 500-line limit; only running verify by 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

catomean and others added 2 commits August 29, 2026 12:10
…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
catomean force-pushed the worktree-fix-home-dashboard branch from 81d9c52 to 437f441 Compare August 29, 2026 10:11
@github-actions
github-actions Bot merged commit 2bc49b9 into main Aug 29, 2026
6 checks passed
@github-actions
github-actions Bot deleted the worktree-fix-home-dashboard branch August 29, 2026 10:17
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant