Skip to content

fix: maintenance banner never on home, below the nav header elsewhere (TASK-22206) - #2946

Merged
kushagrasarathe merged 13 commits into
devfrom
fix/TASK-22206-maintenance-banner
Sep 3, 2026
Merged

kushagrasarathe merged 13 commits into
devfrom
fix/TASK-22206-maintenance-banner

Conversation

@kushagrasarathe

@kushagrasarathe kushagrasarathe commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Note (park time): the Chip re-review of the final head is pending — the review bot was down when this PR was parked. All findings from every completed round (r1-r4, 8 threads) are fixed, replied, and resolved; CI is fully green on the final head.

Summary

Designer ruling (2026-09-03): a maintenance banner on the home screen reads as "funds at risk" and reduces trust, and feature maintenance banners belong BELOW the page's nav header (reference: the card page mock, figma 17994:21117) — not above it in the shell's top slot.

  • NavHeader now mounts the maintenance Banner directly below the header row. Every page with a nav header shows the banner in the ruled spot, with no per-page wiring.

  • Banner hard-excludes /home — home never shows a maintenance banner, whatever underMaintenance.config says.

  • The AppShell top-slot mounts in (mobile-ui)/layout.tsx and the guest-payment layout wrapper are removed. Home has no NavHeader, so the exclusion also holds structurally.

  • The setup-ribbon and landing mounts stay: those surfaces have no NavHeader, and full maintenance must still announce there.

  • Marketing pages and /shhhhh show NO maintenance banner at all (Kush ruling): NavHeader gained a hideMaintenanceBanner opt-out, HeroBackNav (the only marketing NavHeader caller) passes it, and an opted-out header does not suppress the shell fallback.

  • The fallback is visibility-aware: useRegisterNavHeader registers only while the header's root passes checkVisibility() (re-synced on resize), so md:hidden headers (receipt chrome, DevPageShell, PaymentSuccessView) don't silence the fallback at desktop. jsdom/older-Safari fall back to mount-equals-visible.

  • Full maintenance forces the global one-liner on every mount — the feature copy's "everything else works as usual" only appears in scoped banner mode.

  • Chip rounds 1-2: one rule now covers every state — NavHeader carries the banner below the header row when a header is on screen; a shell-level fallback (ShellBannerFallback, driven by a NavHeader presence context) renders it at the top of the shell on headerless states (loading, error screens, guest views). The underMaintenance.config docblock now states the /home exclusion.

  • Unrelated ride-along (own commit 6e54c70e9, Kush's ask): in the activity feed, PENDING CARD-PAYMENT amounts stay full-strength (foreground-primary) instead of the opacity-40 grey — an authorized card spend is committed money, the grey read as "amount in doubt". Every other kind keeps the grey; the pending chip stays. Exemption limited to committed (non-refund) card spends in TransactionCard.tsx; pending refund CREDITS keep the grey. Unit tests for both.

DS KR ratchet (folded in per Kush's call)

Three ride-along commits against the KR1 ds-lint-counts metrics:

  • Allowlist ruling (approved): marketing surfaces and dev tooling are out of DS scope (DS 17 Notion page), so the four KR1 metrics now share the legacy-palette metric's LEGACY_ALLOW set (71898dadd). Baselines rebased so the ratchet keeps biting.
  • Mechanical renames (51e84273c): min-h-[inherit] → new @utility min-h-inherit (43 sites); h-[100dvh]/min-h-[100dvh] → native v4 h-dvh/min-h-dvh (26 sites). No visual change.
  • Stock-text small hits (4c1a6c31b): two comment rewordings (the regex counts prose) + CardLaunchCTABanner headline text-4xl → text-h2 (identical 2.25rem). The PIN screen's deliberate 6xl digits stay.
metric before after
rawHex 61 27
inlineStyle 51 20
stockTextSize 275 8
nonDsClassesInViews 121 16

Task

TASK-22206 — https://app.notion.com/p/3d083811757981289c6fec84d7dc27c5

Risks / breaking changes

  • NavHeader wraps its row in a new div.w-full; outside maintenance mode the banner renders null, so the visual output of every page is unchanged (verified: full jest suite + build).
  • NavHeader now reads usePathname (via Banner) — two test suites' next/navigation mocks gained a usePathname stub.
  • Headerless app states get the shell fallback at the top; home stays excluded in Banner itself, so the fallback can never resurface it there (verified live).
  • Modals/drawers that embed NavHeader show the banner under their header while maintenance targets that path — consistent with the ruling, noting it for review.

Design notes / accepted trade-offs

  • Header-to-banner gap: section gap XL/24 (mt-6), per the spacing board 17291:2772 anatomy roles ("section gap XL/24 between major blocks") — the banner is a block in the page stack, matching PageStack's gap-6 rhythm.

  • Copy (approved): feature banner title "Under maintenance" + body "This feature is temporarily unavailable while we work on it. Your funds are safe and everything else works as usual."; global one-liner global.maintenanceBanner = "Maintenance mode — some features are temporarily unavailable. Your funds are safe." Both in en, es-419, and pt-BR. The old "maintainance" spelling only existed in the mock, not in code.

  • Banner mounted inside NavHeader couples the announcement surface to the navigation component. Accepted: it buys one mount point (banner below the header on every feature page) instead of edits at ~65 NavHeader call sites, and pages without a NavHeader (home) are excluded structurally.

  • Follow-up (mono, not this PR): design/design.md row "maintenance / connectivity announcement" still says the banner renders at the top of the shell — needs a doc edit to record the new placement ruling.

QA

  • npm test (435 suites / 5575 tests green), npm run typecheck, npm run build, prettier check.
  • New tests: Banner never renders on /home (scoped + full maintenance); NavHeader renders the banner after the header row; ShellBannerFallback shows the banner on headerless states, yields (exactly one banner) when a NavHeader is on screen, and stays quiet outside maintenance.
  • Screenshots below were captured with enableMaintenanceBanner toggled on locally (sandbox only, not committed).
  • Card-amount ride-along: ⚠️ no screenshot — a pending card-payment row needs a live Rain authorization, which the sandbox and the dev fixtures cannot fake; the behavior is pinned by the TransactionCard unit test ('keeps the amount full-strength for a pending card payment').

Screenshots

Home — no banner (maintenance ON) History — banner below the nav header Card — banner below the nav header Withdraw — banner below the nav header
Guest public profile — shell fallback (no NavHeader there)

Captured at 375x667 against the local sandbox with enableMaintenanceBanner: true (all paths), on the final head (ruled spacing + approved copy). Assets branch pr-assets-2946 — delete it after merge.

…ader

A maintenance warning on the home screen reads as 'funds at risk' and
reduces trust (designer ruling 2026-09-03), and the top-of-shell banner
slot rendered it above every page's nav header. NavHeader now mounts the
Banner directly below the header row, so each feature page carries it in
the ruled spot, and Banner hard-excludes /home. The (mobile-ui) and
guest-payment top-slot mounts are gone; the setup ribbon and landing
mounts stay (no NavHeader there).

TASK-22206
@notion-workspace

Copy link
Copy Markdown

@vercel

vercel Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
peanut-wallet Ready Ready Preview Sep 3, 2026 3:28pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 80d26159-8ad1-408b-939d-45304fdbb946

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7052.83 → 7062.75 (+9.92)
Findings: +6 net (+30 new, -24 resolved)

🆕 New findings (30)

  • critical complexity — src/components/TransactionDetails/TransactionCard.tsx — CC 67, MI 51.73, SLOC 142
  • critical method-complexity — src/components/TransactionDetails/TransactionCard.tsx:79 — CC 58 SLOC 106
  • high hotspot — src/app/(mobile-ui)/qr-pay/page.tsx — 143 commits, +1956/-1653 lines since 6 months ago
  • high hotspot — src/app/(mobile-ui)/withdraw/manteca/page.tsx — 84 commits, +804/-498 lines since 6 months ago
  • high hotspot — src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx — 66 commits, +599/-319 lines since 6 months ago
  • high hotspot — src/app/(mobile-ui)/withdraw/crypto/page.tsx — 54 commits, +568/-293 lines since 6 months ago
  • high hotspot — src/components/AddWithdraw/AddWithdrawCountriesList.tsx — 54 commits, +739/-508 lines since 6 months ago
  • high hotspot — src/components/TransactionDetails/TransactionCard.tsx — 52 commits, +505/-421 lines since 6 months ago
  • high hotspot — src/app/(mobile-ui)/card/page.tsx — 51 commits, +1148/-498 lines since 6 months ago
  • high hotspot — src/components/Claim/Link/Initial.view.tsx — 43 commits, +343/-205 lines since 6 months ago
  • high hotspot — src/components/Invites/JoinWaitlistPage.tsx — 42 commits, +551/-292 lines since 6 months ago
  • high hotspot — src/components/AddMoney/components/MantecaAddMoney.tsx — 41 commits, +327/-144 lines since 6 months ago
  • high hotspot — src/app/(mobile-ui)/layout.tsx — 40 commits, +291/-283 lines since 6 months ago
  • high hotspot — src/components/Invites/InvitesPage.tsx — 39 commits, +581/-384 lines since 6 months ago
  • high method-complexity — src/app/(mobile-ui)/layout.tsx:44 — CC 38 SLOC 68
  • medium high-mdd — src/app/(mobile-ui)/layout.tsx:44 — Layout: MDD 72.9 (uses across many lines from declarations)
  • medium high-mdd — src/components/TransactionDetails/TransactionCard.tsx:79 — TransactionCard: MDD 71.2 (uses across many lines from declarations)
  • medium high-mdd — src/components/Global/NavHeader/index.tsx:45 — NavHeader: MDD 33.9 (uses across many lines from declarations)
  • medium high-mdd — src/components/Global/Banner/index.tsx:38 — Banner: MDD 21.0 (uses across many lines from declarations)
  • medium complexity — src/components/Global/Banner/navHeaderPresence.tsx — CC 20, MI 69.98, SLOC 54

…and 10 more.

✅ Resolved (24)

  • src/components/TransactionDetails/TransactionCard.tsx — CC 65, MI 51.95, SLOC 140
  • src/components/TransactionDetails/TransactionCard.tsx:79 — CC 56 SLOC 104
  • src/app/(mobile-ui)/qr-pay/page.tsx — 142 commits, +1952/-1649 lines since 6 months ago
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx — 83 commits, +801/-495 lines since 6 months ago
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx — 65 commits, +598/-318 lines since 6 months ago
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx — 53 commits, +567/-292 lines since 6 months ago
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx — 53 commits, +738/-507 lines since 6 months ago
  • src/app/(mobile-ui)/card/page.tsx — 50 commits, +1145/-495 lines since 6 months ago
  • src/components/TransactionDetails/TransactionCard.tsx — 50 commits, +491/-415 lines since 6 months ago
  • src/components/Claim/Link/Initial.view.tsx — 42 commits, +342/-204 lines since 6 months ago
  • src/components/Invites/JoinWaitlistPage.tsx — 41 commits, +550/-291 lines since 6 months ago
  • src/components/AddMoney/components/MantecaAddMoney.tsx — 40 commits, +326/-143 lines since 6 months ago
  • src/hooks/useHomeCarouselCTAs.tsx — 39 commits, +474/-270 lines since 6 months ago
  • src/app/(mobile-ui)/layout.tsx:43 — CC 38 SLOC 68
  • src/components/Invites/InvitesPage.tsx — 38 commits, +579/-382 lines since 6 months ago
  • src/app/(mobile-ui)/layout.tsx:43 — Layout: MDD 72.5 (uses across many lines from declarations)
  • src/components/TransactionDetails/TransactionCard.tsx:79 — TransactionCard: MDD 68.9 (uses across many lines from declarations)
  • src/components/Global/NavHeader/index.tsx:37 — NavHeader: MDD 36.0 (uses across many lines from declarations)
  • src/components/Global/NavHeader/index.tsx — CC 13, MI 61.42, SLOC 26
  • src/app/(mobile-ui)/layout.tsx:70 — useEffect with empty deps + setState — derived state anti-pattern

…and 4 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Global/Banner/navHeaderPresence.tsx 0.0 4.3 +4.3
src/components/Global/Banner/ShellBannerFallback.tsx 0.0 3.3 +3.3
src/components/Global/Banner/index.tsx 5.8 6.8 +0.9
src/components/Global/NavHeader/index.tsx 8.2 8.9 +0.7

@github-actions

github-actions Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • ✅ unit: 5609 ran, 0 failed, 0 skipped, 1.8m

📊 Coverage (unit)

metric %
statements 74.0%
branches 59.4%
functions 67.5%
lines 74.9%
⏱ 10 slowest test cases
time test
🐢 9.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Network failure keeps loading while retries remain, then shows the generic error
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_VOLUME_NEAR_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_SOURCE_OVER_MONTHLY_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › User KYC not approved fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_RECENT_REFUND fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_USER_NOT_PROVISIONED fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › a refused idempotency key tells the user to scan again, not to contact support
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › routes the KYC rejection on its wire code, and does not retry it
3.1s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Scan that recovers on the retry lands on the payment screen, not an error
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@github-actions

github-actions Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

🖼 Visual diff — no screen moved

62 shots, all identical. baseline 45e11ba → head c14cb62.

job summary

Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data.

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review — no blocking findings — this is not an approval

The new placement works on NavHeader pages, but deleting the catch-all shell mount removes maintenance notices from logged-out public profiles.

Findings

  • MAJOR · src/app/[...recipient]/payment-layout-wrapper.tsx:19 · Keep a maintenance banner on guest profiles
    With enableMaintenanceBanner=true and maintenanceBannerPaths=[], a logged-out visit to a one-segment public profile now renders PaymentLayoutWrapper without Banner, while PublicProfile's guest branch renders only the logo and never mounts NavHeader. The maintenance warning therefore disappears from a reachable public surface that previously received the shell banner. Add an explicit Banner fallback to the no-NavHeader guest-profile branch (or otherwise retain exactly one fallback mount for no-header catch-all views), and cover that route with a regression test.

  • MINOR · src/config/underMaintenance.config.ts:63 · [claude-opus] underMaintenance.config docs still promise the banner shows everywhere
    The config file is the ops control panel for outages and its header comment now misstates what the switches do. Lines 8, 11 and 63 say the banner "shows on all pages (including landing and support)" and "if either mode is enabled, the maintenance banner will show everywhere". After this change src/components/Global/Banner/index.tsx:45-47 returns null unconditionally on /home and /home/*, ahead of both the enableFullMaintenance and enableMaintenanceBanner checks — the PR's own new test 'never shows on home, even under full maintenance' asserts exactly that. The code is the intended behaviour (designer ruling); the comment is what is wrong. An on-call engineer flipping enableMaintenanceBanner during an incident reads this block and expects home to be covered. Fix: amend the three comment lines to state that /home is unconditionally excluded, matching the new docblock in Banner/index.tsx.

Inline anchors unavailable for 1 finding(s); the findings remain in this summary.

Checked clean

  • Exact detached HEAD, supplied base SHA, PR author, base ref, and GitHub head SHA all match.
  • Home exclusion and maintenance path-prefix matching are explicit and covered by focused tests.
  • NavHeader keeps its existing controls and mounts one banner directly after the header row.
  • No new secret exposure, injection path, authorization change, or unsafe workflow behavior was found.
  • At the exact head, unit, typecheck, eslint, format, design-system lint, provenance, and ownership checks passed; visual snapshots, native export, preview deploy, and aggregate report were still running when checked.

Second opinion by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only, so treat its findings as advice.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: c928cb1adf0e · Context: repo · Took 11m

Comment thread src/app/[...recipient]/payment-layout-wrapper.tsx
…onfig docs

Chip review on #2946: the guest public profile is the one reachable public
surface with no NavHeader, so dropping the shell mount silenced maintenance
notices there — the guest branch now mounts the Banner itself, with a
regression test. The underMaintenance.config docblock also claimed the
banner shows everywhere; it now states the /home exclusion so an on-call
engineer is not misled during an incident.

TASK-22206

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review — no blocking findings — this is not an approval

The guest-profile regression is fixed, but moving the app-wide banner entirely into NavHeader leaves headerless app error and loading states without the configured maintenance notice.

Findings

  • MAJOR · src/app/(mobile-ui)/layout.tsx:179 · Keep maintenance visible in headerless app states
    With enableMaintenanceBanner=true and maintenanceBannerPaths=['/card'], a /card API failure takes the pioneerError/overviewError branch in CardPage, which renders only the error text and Retry button and has no NavHeader. Removing this shell-level Banner therefore hides the maintenance explanation precisely during the provider/API failure it is meant to explain; loading-only and other headerless states have the same gap. Give those states a banner mount before removing the fallback (or centralize placement with an explicit way for pages that render a NavHeader to suppress the shell copy), and add a route-state regression test.

  • MAJOR · src/app/[...recipient]/payment-layout-wrapper.tsx:19 · [moonshotai/kimi-k3] Keep a maintenance banner on guest profiles — still open
    payment-layout-wrapper.tsx drops banner={<Banner />} and adds no replacement. The new PublicProfile mount only covers the profile view's guest branch; the other pages wrapped by this layout (the [...recipient] payment/request flow) render no NavHeader and now no Banner at all. These are public routes that stay reachable during enableFullMaintenance (public guest links bypass the redirect), so a guest mid-payment during a full outage sees no maintenance notice. Fix: keep a mount in PaymentLayoutWrapper itself (e.g. above {children}) rather than relying on individual pages.

Checked clean

  • Exact head and supplied dev merge base match; detached worktree is clean and PR author/base metadata match the trusted input.
  • Prior P1 is fixed: guest PublicProfile now mounts Banner when it has no NavHeader, with maintenance-on and maintenance-off coverage.
  • Banner path matching, unconditional /home exclusion, NavHeader placement, and setup/landing banner mounts were reviewed.
  • No new secret exposure, injection surface, authorization change, or privileged workflow behavior was introduced.
  • CI at the exact head reports unit, typecheck, eslint, format, native-export, ownership, and aggregate ci-success checks successful; screenshot and preview jobs were still running when checked.

Second opinion by moonshotai/kimi-k3: 1 finding(s), marked with the model name. It reads the diff only, so treat its findings as advice.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: c3733888d1f0 · Context: repo · Took 12m (queued 2m)

Comment thread src/app/(mobile-ui)/layout.tsx
Comment thread src/app/[...recipient]/payment-layout-wrapper.tsx Outdated
Chip round 2 on #2946: loading and error screens (and any state with no
NavHeader, like a /card API failure) lost the maintenance notice exactly
when it explains the outage. One rule now covers everything: NavHeader
carries the banner below the header row when a header is on screen; a
shell-level fallback renders it at the top otherwise, driven by a
NavHeader presence context. The PublicProfile special-case mount from the
previous round is replaced by the same fallback.

TASK-22206
…lobal one-liner)

Kush's review: the header-to-banner gap is now the section gap XL/24
(mt-6, spacing board 17291:2772) — the banner is a block in the page
stack, same rhythm as PageStack's gap-6, not an eyeballed 8px. The
feature banner below the nav header now carries the approved title +
body copy; the top-of-shell mounts keep a shorter one-liner. All three
locales updated; the old 'Funds safe' grammar is gone.

TASK-22206

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review — no blocking findings — this is not an approval

Two major maintenance-banner regressions remain: CSS-hidden app headers suppress the shell fallback, and absolute marketing back-navs still inherit the feature banner.

Findings

  • MAJOR · src/components/Global/NavHeader/index.tsx:142 · Keep the banner out of absolute marketing navs
    HeroBackNav mounts NavHeader inside an absolute top-left overlay on every localized marketing page and /shhhhh. When enableMaintenanceBanner targets all pages, this child now places the full feature notification inside that floating back-button overlay, covering the hero instead of occupying a normal announcement slot. Keep banner mounting out of overlay/marketing NavHeader callers (for example via an explicit opt-out) and mount any marketing maintenance notice in the marketing layout.

  • MINOR · src/components/Global/Banner/index.tsx:64 · [claude-opus] Feature-scoped maintenance copy is shown during global/full maintenance
    Banner picks its copy from the variant prop, which reflects the mount point (NavHeader = 'feature', shell = 'global'), not the scope of the maintenance. NavHeader/index.tsx:120 always passes variant="feature", so whenever the banner is on, every page with a nav header renders maintenanceTitle + maintenanceBody: "This feature is temporarily unavailable while we work on it. Your funds are safe and everything else works as usual."

That statement is false in the two modes the config documents as global. With the default maintenanceBannerPaths: [] and enableMaintenanceBanner: true — which src/config/underMaintenance.config.ts:11 defines as "shows a banner on ALL pages", i.e. the ops switch used to warn about an app-wide outage — a user on /card, /add-money or /history is told exactly one feature is down and everything else is fine. The enableFullMaintenance branch at src/components/Global/Banner/index.tsx:63 falls into the same variant === 'feature' path, where the app is in fact fully blocked (proxy.ts:24 only leaves /, /maintenance, /support reachable) yet the copy still promises "everything else works as usual". The accurate string for those cases, maintenanceBanner ("some features are temporarily unavailable"), is suppressed precisely when a nav header is present. This matters because in-product outage status is a documented open money-blocking problem (/home/chip/mono/product/feedback/problems/latam-provider-outages-no-status.md) — the banner is the only status channel users get, so understating the blast radius sends them back into flows that are down.

Fix: derive the variant from scope rather than mount point — e.g. compute const isGlobal = maintenanceConfig.enableFullMaintenance || maintenanceConfig.maintenanceBannerPaths.length === 0 and use the feature title/body only when the path matched an explicitly configured entry in maintenanceBannerPaths, falling back to the maintenanceBanner one-liner otherwise. The approved feature copy stays as-is; only its selection condition changes. Worth a case in MaintenanceBannerPaths.test.tsx asserting the global one-liner under an empty maintenanceBannerPaths even when rendered as variant="feature".

Answered by a human, not raised again

These are still present as far as this review can tell. You resolved the thread or deferred the fix, so Chip files them instead of repeating them.

  • MAJOR · src/app/(mobile-ui)/layout.tsx:179 · Keep the fallback when a NavHeader is CSS-hidden — task

Checked clean

  • Confirmed the detached worktree HEAD, merge base, trusted author, dev base ref, and base SHA match the supplied values.
  • Exact-head CI reports successful unit, typecheck, eslint, format, design-system, native-export, analysis, and preview checks; only intentionally skipped checks are skipped.
  • Verified the payment-layout ShellBannerFallback now keeps the notice on guest public profiles and yields to logged-in NavHeader pages.
  • Verified /home is excluded under both scoped and full maintenance, while path-prefix targeting remains intact elsewhere.
  • Checked responsive-hidden and absolute-overlay NavHeader call sites for placement and fallback behavior.
  • Checked the English, Spanish, and Portuguese maintenance keys and the config documentation for consistency.
  • Reviewed the diff for authorization, secret exposure, injection, shared-state mutation, dead code, and unnecessary duplication; no additional issue found.
  • Focused Jest could not be rerun in the detached worktree because Jest is not installed there; the exact-head unit check is green.

Second opinion by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only, so treat its findings as advice.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 9048972e1c53 · Context: repo · Took 9m (queued 7m)

Comment thread src/components/Global/NavHeader/index.tsx Outdated
…feed

An authorized card spend is money already committed, so the pending grey
(opacity-40) read as 'amount in doubt'. Card payments are now exempt from
the greyed-amount treatment only — the pending chip stays, every other
kind keeps the grey. Block-based isCardPaymentEntry on purpose: card
refunds arrive with legacy kinds (same reasoning as isFxBearingFlow).

TASK-22206

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review — no blocking findings — this is not an approval

The guest and ordinary headerless-state regressions are fixed, but maintenance is still misplaced on marketing pages and disappears behind CSS-hidden headers. Full-maintenance copy and pending-refund styling also remain incorrect.

Findings

  • MAJOR · src/components/Global/NavHeader/index.tsx:142 · Keep maintenance out of absolute marketing navs — also flagged by moonshotai/kimi-k3
    Every localized marketing page mounts HeroBackNav, which places NavHeader in an absolute top-left back-button container and outside the app-shell presence provider. When maintenance is enabled for one of those paths, this unconditional mount puts the feature Banner inside that narrow overlay; the marketing catalog also has no global namespace, so the notice falls back to missing-message keys instead of approved copy. Give marketing NavHeaders an explicit banner opt-out (or make the maintenance-bearing header app-specific) and mount any marketing notice in a full-width surface with the required messages.

  • MAJOR · src/components/Global/NavHeader/index.tsx:70 · Keep the fallback when a header is CSS-hidden — also flagged by moonshotai/kimi-k3
    Presence is registered from component mount, not visual presence. For example, the mobile-ui receipt keeps NavHeader mounted inside md:hidden; at a desktop viewport line 70 still increments the provider count, ShellBannerFallback returns null, and the header's own Banner is hidden with its ancestor, so maintenance has no visible notice. The same pattern exists in claim states. Register only a header that is actually visible, or avoid mounting the responsive-hidden header at that breakpoint, and cover an md+ receipt/headerless case.

  • MINOR · src/components/Global/Banner/index.tsx:64 · Use global copy during full maintenance — also flagged by moonshotai/kimi-k3
    The feature branch wins even when enableFullMaintenance is true. A route with NavHeader that is outside the proxy matcher, such as /card, therefore renders “This feature is temporarily unavailable” and “everything else works as usual” during a global outage. Choose the global copy before applying the feature variant (or derive the variant from the active mode) and add a full-maintenance NavHeader regression test.

  • MINOR · src/components/TransactionDetails/TransactionCard.tsx:221 · Keep pending card refunds visually pending
    isCardPaymentEntry intentionally matches both spends and refunds, so this exemption also removes opacity from pending refund credits. The transformer already maps a negative CARD_SPEND_AUTH to status: pending, direction: receive, and cardPayment.isRefund: true; that row now shows its incoming amount at full strength even though the credit has not settled. Limit the exemption to non-refund card spends and add the negative-authorization refund case beside the new pending-spend test.

Checked clean

  • Confirmed the detached head, supplied base SHA, merge base, trusted author, base ref, and pull-request metadata exactly match the review request.
  • Rechecked P1 and P3: PaymentLayoutWrapper now provides NavHeader presence and mounts ShellBannerFallback, so guest profiles without NavHeader retain the maintenance notice.
  • Rechecked P2: the mobile AppShell now has the same provider/fallback, covering child loading and API-error branches that render no NavHeader while yielding to visible headers.
  • Reviewed all 17 changed files, maintenance flag/path behavior, proxy coverage, marketing catalog boundaries, responsive NavHeader call sites, localization changes, and the transaction-card predicate/test change.
  • Correctness, security, adversarial, and slop passes found no new trust-boundary, secret-exposure, injection, authorization, or shared-state mutation issue beyond the findings above.
  • All exact-head GitHub checks completed successfully, including unit, typecheck, lint, format, design-system checks, native export, analysis, preview deployment, provenance, and the aggregate CI gate.

Second opinion by moonshotai/kimi-k3: 3 finding(s), marked with the model name. It reads the diff only, so treat its findings as advice.

Third opinion: did not run — claude-api_error. This review is one reviewer short.

Exact head: 6e54c70e9006 · Context: repo, ci · Took 13m (queued 5m)

Comment thread src/components/Global/NavHeader/index.tsx Outdated
Comment thread src/components/Global/NavHeader/index.tsx Outdated
Comment thread src/components/Global/Banner/index.tsx Outdated
Comment thread src/components/TransactionDetails/TransactionCard.tsx Outdated
@kushagrasarathe
kushagrasarathe marked this pull request as ready for review September 3, 2026 14:02
…ull-maintenance copy, refund carve-out

Chip round on 6e54c70 + Kush ruling:
- marketing pages and /shhhhh show NO maintenance banner at all —
  HeroBackNav opts its NavHeader out (hideMaintenanceBanner), and an
  opted-out header no longer suppresses the shell fallback it does not
  carry a banner for.
- registration now follows VISUAL presence via checkVisibility(): a
  header inside a responsive-hidden wrapper (md:hidden receipt chrome)
  no longer silences the fallback at the breakpoint where it is
  display:none. jsdom/older-Safari fall back to mount-equals-visible.
- full maintenance forces the global one-liner even on feature mounts —
  'everything else works as usual' would be a lie during a global outage.
- pending card REFUND credits keep the grey: only committed card spends
  are exempt from the pending amount treatment.

TASK-22206
@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

/chip review

…TODO

Advisory review nits on #2946: the presence hook's constraint (state-driven
class toggles would stay mis-registered — no such caller exists) is now
named in a comment instead of observer machinery, and Global/Layout's
'temp, remove banner later' note is gone — this PR's docblock blesses that
mount as the landing surface's permanent one.

TASK-22206
DS 17 ruling (approved by Kush 2026-09-03): marketing surfaces are out of
DS scope and dev tooling is not product UI. The legacy-palette metric
already excluded exactly this set (LEGACY_ALLOW); rawHex, inlineStyle,
stockTextSize and nonDsClassesInViews now share it. Baselines rebased to
the lower counts so the ratchet keeps biting: rawHex 61->27,
inlineStyle 51->20, stockTextSize 275->11, nonDsClassesInViews 121->64.

TASK-22206
…ry variants

Pure mechanical renames, no visual change: min-h-[inherit] (43 sites)
becomes a named @Utility min-h-inherit in globals.css, and
h-[100dvh]/min-h-[100dvh] (26 sites) become tailwind v4's native
h-dvh/min-h-dvh. Kills the two biggest arbitrary-value classes the
nonDsClassesInViews metric counts (64 -> 16).

TASK-22206
…Banner on the ramp

The lint regex counts comments too: CardPinScreen and UserHeader prose
mentioned stock size names, reworded without changing meaning (the PIN's
deliberate 6xl digits stay). CardLaunchCTABanner's headline moves from
stock text-4xl to text-h2 — identical 2.25rem, the display face keeps its
own weight and leading. stockTextSize 11 -> 8, baseline rebased.

TASK-22206
Same class as the earlier mock fixes: NavHeader now mounts the
maintenance Banner (reads the pathname), and the residence suites that
merged into dev mid-PR mock next/navigation without usePathname.

TASK-22206
@kushagrasarathe
kushagrasarathe merged commit 367da93 into dev Sep 3, 2026
21 of 22 checks passed

This branch was successfully deployed

1 active deployment
Preview — c14cb62c Deployed Sep 3, 2026 by vercel[bot]
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