Chapter 28. 2026-07-17 to 07-18: The Twelve-Hour Estate-Wide Pass

On 2026-07-17 I ran a Lighthouse pass across the estate with parallel agents: 22 live web surfaces audited, 18 PRs merged across nine repos in one day (one of them reverted and redone), and most of the estate confirmed at 90-plus. My own notes had claimed the estate held “perfect Lighthouse” scores, and the pass confirmed that line was aspirational before anyone opened a terminal. The pass record supersedes it.

The structure was the one that lets a single operator cover that much ground: parallel background agents, one git worktree per lane, each agent merging and deploying under standing autonomy while I made the judgment calls. The same structure supplied two of the day’s worst problems. The machine running the measurements also ran every other concurrent session on this Mac, and one of those sessions broke production in the middle of my pass.

The per-surface scores, and the count I can prove

The clean wins first: jcig-site (jcinvestmentgroup.ventures) reached a perfect 100/100/100/100 through PR #13, which fixed color contrast, malformed <dl>/<dt>/<dd> markup, and an invalid robots.txt directive. acp.suedeai.ai live-verified at 97/100/100/100. studio.suedeai.ai went from Performance 0.55 to 0.97, LCP 5.1s to 2.0s, and TBT 590ms to 70ms, verified on my machine rather than against the live deploy. ip.suedeai.ai and social.suedeai.ai closed the pass still sitting around Performance 56-57, with real bundle-weight work remaining.

The pass file is the primary tally: at least 15 of 22 audited surfaces were confirmed in the top range, with all four categories at 90 or better. A later index summarized the result as 18 to 20, but it did not preserve the surface-level evidence needed to support that higher count, so this book does not use it. The stricter, traceable number wins.

What landed: 18 PRs across nine repos

Repo PR(s) What landed
Suede-AI-App frontend + landing-site #653-#663 static rendering, wallet-SDK scoping, hydration fixes, NO_LCP root cause, one revert and redo
suede-cosmos #6 perpetual requestAnimationFrame canvas drift loop, unscoped zustand store subscriptions, sub-12px fonts
suede-empire #5 a --dim color token too light against 3 backgrounds, aria-label mismatches on the logo and the split-flap board
fretpulse #29 two setInterval polls for the Pulse-by-Anthony-J metronome running forever regardless of active tab, favicon/analytics 404s, missing <select> labels
jcig-site #13 contrast, definition-list markup, robots.txt
suede-geo #10 contrast, link-in-text-block, missing favicon
suedeai-org #29 render-blocking Google Fonts stylesheet, link-in-text-block
suede-acp-x402-business #5 exempting an animated H1 from its entrance stagger

Two notes on that table. First, the per-repo arithmetic in my own pass file does not add up: it labels the frontend lane “×6” while enumerating eight PR numbers, counts PR #663 under both frontend and landing-site, and describes PR #657 both as a landing-site PR shared with suede-home and as suede-home’s own. The totals (18 PRs, nine repos) are self-consistent; the per-repo split is not, and I am not going to pretend otherwise. Second, look at what the small-repo rows contain. A Lighthouse pass sells itself as performance work, and then half of what it surfaces is contrast failures, missing labels, broken markup, and timers nobody cleared. fretpulse had two polls driving a metronome whether or not the tab was active. suede-cosmos had a canvas loop that never stopped. Those are functional bugs wearing a performance costume.

One headers() call held eight hosts dynamic

The opening move was PR #653. The shared root layout in Suede-AI-App/frontend wraps eight hosts: app, social, studio, play, agents, ip, fretpulse, distro. That layout called headers() with no condition, to answer one question (“is this app.suedeai.ai?”) that is true for one host and false for the other seven, each of which already injects its own JSON-LD. One dynamic API call in shared chrome forced dynamic rendering estate-wide and pinned Cache-Control: private, no-cache, no-store on every request.

PR #653 moved the app-host-exclusive routes into an (app-host) route group with their own layout and deleted the headers() call from the shared root. studio, social, play, and distro now render as static pages and serve Cache-Control: public, max-age=0, must-revalidate, confirmed live. An earlier attempt to fix this from middleware (PR #612) had been proven dead by live curl (Vercel’s origin-function Cache-Control wins over anything middleware sets on a rewrite() response; they don’t merge) and reverted in PR #614 rather than left as dead code under a confident comment.

That single merge did more for the estate than any other PR in the pass, and it triggered the next bug family the same day.

The wallet SDK loaded on every route, marketing pages included

Marketing pages that never touch a wallet were loading wagmi plus WalletConnect/Reown AppKit plus the Solana wallet-adapter, with autoConnect, on every route, and two repos that share no code had the same dominant cost. On Suede-AI-App/frontend that stack dominated TBT and bundle size across the studio, play, distro, fretpulse, and acp routes; on ip.suedeai.ai it did the same across six marketing routes. The mechanism was identical in both repos: the provider stack sat in the root layout, mounted for all comers. landing-site went one further. Its Header.tsx carried its own Connect Wallet button, so the cost lived in shared chrome, not in any page you could audit route by route.

The fix shape: extract the provider stack into its own component and mount it through route-group layouts only where pages call wallet hooks. PR #655 scoped the SDK away from studio/play/distro/fretpulse/acp. PR #656 did the same for the landing-site homepage and went further, migrating the homepage’s single on-chain read to a viem createPublicClient call instead of keeping the whole SDK for one read.

The lesson generalizes past wallets. When you scope a heavy dependency to the routes that need it, grep transitively for hook usage, not just src/app: the shared nav or header is the usual hidden culprit. And when the same anti-pattern shows up in two unrelated repos on the same day, stop treating it as a bug. It is a house pattern, and it will be waiting in the next repo too.

PR #660: ssr:false skips server rendering and nothing else

PR #660 lazy-loaded Header.tsx’s wallet-connect button as part of the same scoping effort. Build passed, tsc passed, lint passed. It merged and deployed under the same standing autonomy as everything else, and then the live Connect Wallet button threw a client-side exception on click across six marketing routes in production. The mechanism: ssr:false skips server rendering and nothing else. It does not make wagmi hooks provider-optional. Once the layout dropped WalletProviders and the component mounted client-side with zero WagmiProvider ancestor, wagmi’s useConfig() threw.

A manual click-through on an isolated browser tab caught it about 10 minutes in, and PR #661 reverted it. The redo, PR #663, inverted the ownership model: HeaderWalletConnect now brings its own scoped EvmProvider by default, and routes that already have an ancestor provider (the dashboard, ip/[id]) opt out through an explicit walletProvidersMounted prop instead of any ancestor detection. The v2 got click-tested across all eight wallet-touching routes before and after merge, by the implementing agent against a local build and then against the live deployed site.

Build success said the change was fine, and build success was wrong. If a change touches something a user clicks, someone has to click it, on the deployment users get.

React hydration error #418 after switching routes to static

React error #418, the hydration mismatch, showed up the same day in three places: suede-home, and the studio and social surfaces of Suede-AI-App/frontend. My pass record counts those as three repos. Nothing new had been written. PR #653 had changed the ground under old code. Components in shared chrome (a mobile nav button, a page-padding wrapper, FounderSignature.tsx, MusicianCreateFooterCta.tsx) branched on usePathname() or typeof window. While every route rendered per request, those checks resolved against a real request and matched. Once the routes were prerendered at build time, the build baked in one value, the client computed another, and React noticed.

The fix is a mounted flag built on useSyncExternalStore, so the first client render always matches server output and corrects itself after mount. PR #658 fixed page-padding.tsx and mobile-menu-button.tsx; PR #657 fixed FounderSignature.tsx and MusicianCreateFooterCta.tsx in suede-home. The obvious alternative, a plain useEffect plus useState mount flag, trips this codebase’s react-hooks/set-state-in-effect lint rule, so useSyncExternalStore is the required shape here.

The portable rule: flipping a route from dynamic to static changes the correctness of every runtime conditional in its shared chrome. Audit them as part of the same change, in every repo that shares the pattern, or the hydration errors will file in one repo at a time until you do.

Two animation causes of a NO_LCP error under Lantern

Lighthouse’s default mode, Lantern, simulates throttling rather than applying it. Two animation patterns broke its LCP detection during the pass, with the same symptom and different causes, and the difference cost me most of a day.

studio.suedeai.ai returned a NO_LCP error and a hard 0 Performance score under Lantern while a real devtools-throttled run measured normal. The cause was an “ember-breathe” live-indicator dot animating box-shadow, a non-composited property, on infinite iteration. Continuous non-compositor repaint left Lantern unable to model a valid LCP candidate graph. The diagnostic recipe that isolated it: bisect by page (/studio failed, /studio/about did not), then

lighthouse --only-audits=non-composited-animations

then confirm with a raw Chrome trace (--save-assets, looking for largestContentfulPaint::Candidate events) or a PerformanceObserver check in a real browser. PR #662 root-caused it and cleaned up font preload and subsetting on studio plus an LCP-image and render-blocking-CSS fix on social. The fix that held came the next day: PR #669 exempted the LCP element from the animation outright.

Then acp.suedeai.ai produced the identical NO_LCP, and its non-composited-animations audit came back clean. I applied the fix that had worked on ip.suedeai.ai and studio.suedeai.ai, tightening the entrance-animation timing 3x, and NO_LCP persisted. The cause was different. The LCP element itself, the H1, was opacity:0-animated inside an entrance stagger, and an animated LCP candidate defeats Lantern no matter how fast the animation runs. The fix exempted the H1 and kept the staggered reveal on secondary hero elements, shipped as PR #5 in JasonColapietro/suede-acp-x402-business (that repo lives under my personal account, not the Suede-AI org, which matters when you go looking for it). Shipping it surfaced a second problem: that Vercel project does not auto-deploy on push, and a 6-day-stale deploy was live. I shipped by hand with vercel --prod plus an alias pin, then live-verified 97/100/100/100.

Two rules came out of this pair. Animate transform and opacity, never box-shadow or width, and treat infinite iteration as its own red flag. And do not delete entrance animations to buy LCP: scale durations and delays down, divided by about 2 to 3, which preserved the visual stagger on ip’s .doc-rise and studio’s .luxe-rise tiers (5 delay tiers each). When NO_LCP survives the tightening, exempt the LCP element alone rather than tightening again. Same symptom does not mean same cause, and the fix that worked on the last surface does not transfer on its own authority.

Check uptime before trusting a Lighthouse number

One agent ran the identical local production build through Lantern three times: clean pass, clean pass, then NO_LCP once machine load rose, and that finding reframed the whole pass. The code had not changed between runs; the machine had. This Mac carries concurrent Claude Code sessions and Codex CLI processes belonging to other work, and that contention corrupts --throttling-method=devtools in proportion to host load. Above a threshold it corrupts even Lantern’s simulated mode, which I had assumed was contention-proof. Load average ran 8-16 for hours during the pass and oscillated between 5 and 838 through the following night. The fallback I reached for was no fallback at all: the keyless default quota on pagespeedonline.googleapis.com is a shared per-project daily quota, already exhausted before the pass began, and it never reset within the session.

The rule that survives: run

uptime

before treating any Lighthouse number from this machine as ground truth. Above a load average of about 5, trust only before/after deltas taken on the same machine state, or get a real PageSpeed Insights or real-device reading. I enforced the rule against my own work the same night. PR #675, a framer-motion code-split for studio, stayed open and unverified because load hit 600-838 mid-verification. At that load, “fixed” and “still broken” are both unprovable, and merging on an unprovable claim is how a performance pass ships a regression.

The shared machine sent one more reminder. Mid-pass, an unrelated session landed a bad rebase on Suede-AI-App main. PRs #672 and #673 repaired a duplicate articlePath declaration that caused production builds to fail. The more specific deployment record says the live site remained on its prior deployment because Vercel did not promote the failed build, so I classify this as an 18-minute production-pipeline failure, not a confirmed live outage. On an estate where parallel agents push to one default branch, failures arrive from outside your own change set. Attribute the incident to the branch, not to whatever you happened to be doing when the alarm went off.

What I deferred, and the uncommitted worktree work I lost

A pass this wide ends with three lists: fixed, deferred, lost.

Four items stayed broken by decision. The Twitter ads pixel fired on every page of play and studio, dragging third-party-cookie and inspector flags into the reports; that is a marketing attribution call, not a perf bug, and on 2026-07-18 PR #674 host-scoped the pixel to app.suedeai.ai, picked from three options: gate it behind interaction, scope it by host, or accept the score. scan.suedeai.ai’s Vercel Web Analytics toggle sat disabled at project-settings level; account-level and project-settings toggles are not something I let agents flip, so it stayed put pending my own sign-off. social.suedeai.ai’s Reown/WalletConnect allowed-origins gap, found while verifying PR #658 as a console error plus two 403s from the relay, needs a dashboard session at cloud.reown.com, not a code change; nothing in the repo, the build, or the test suite can represent it. And acp.suedeai.ai’s .dashed-bar::after still animates width where transform:scaleX() belongs, flagged and left.

The lost list is the one that stings. ip.suedeai.ai and social.suedeai.ai were the two hardest remaining surfaces, both still around Performance 56-57 with real bundle-weight gaps. The in-progress work on both lived as uncommitted state in worktrees, and a process restart orphaned those worktrees with nothing salvageable. The work carried forward as a written handoff (05_handoffs/2026-07-18-claude-suede-lighthouse-pass-handoff.md in the vault) rather than as code. Uncommitted worktree state is one restart away from zero. On a long multi-surface pass, commit early and keep committing; a handoff document is a consolation prize.

The order to fix in: shared chrome, then per-surface

The order the day imposed is the order I would choose again. Shared chrome first: PR #653 and the wallet scoping in PRs #655 and #656 each moved multiple surfaces per merge, and they flushed out the downstream failures (the hydration mismatches, the broken header button) while the pass still had hours left to absorb them. Per-surface cleanup second: the small-repo PRs held audit-level items, cheap to fix in parallel lanes because each lane owned its own worktree and merged without waiting on the others. Verification ran throughout: click-tests for anything interactive, live curls for anything cached, and uptime next to every score.

The denominator matters more than the wins. 22 surfaces went in. Between 15 and 20 came out confirmed at 90-plus, two closed with honest open gaps and a handoff, four items were deferred with the reasons written down, and one production incident passed through from outside the pass. The old “perfect Lighthouse” line died on 2026-07-17, and what replaced it is worth more, because every number in it comes with the machine state it was measured under.