Chapter 36. How to Run an Estate-Wide Pass
A pass is one question asked of every surface inside a bounded
window, with the answers written down beside the date they were taken. I
have run three: Lighthouse scores across the estate starting 2026-07-17,
a 2026-07-26 sweep asking whether each filename in every Vercel repo’s
bare api/ directory resolves to a live public route, and a
rollout checking every public repo for the committed marketplace
registration in .claude/settings.json.
The shape holds across all three. Define one check that produces a value. Enumerate the surfaces from the platform, not from memory. Sweep. Record per-surface results with dates. Fix the worst first. Then write the sweep date into the standing rule so the next operator knows how stale the answer is.
Define one check that produces a value
“Is this page fast” is not a check. “What does Lighthouse report for
Performance, and under which throttling method” is a check, because it
lands in a cell. The api/ sweep’s check is a command:
curl -s -o /dev/null -w '%{http_code}' https://<domain>/api/<basename>Expect 404 for anything that is not a real endpoint. That check has a
property the file tree does not: it interrogates production. On
2026-07-26 the file tree in suede-geo looked reasonable and
the deployed surface was not.
GET https://scan.suedeai.ai/api/check.test returned 200
with a plain-text body of partial, which was a fixture from
a mock server inside a test file. Requesting the URL executed the test
suite inside a production function, booted that test’s HTTP server, and
hung past 25 seconds against the 300-second function ceiling. Anyone
could have looped it. The remediation went in as
site/.vercelignore, chosen over moving the file because it
keeps the test in git and runnable on my machine.
Verify against production rather than the repo, on every check where
production is the thing you care about. A liveness sweep uses the same
discipline. hub.suedeai.ai returns a final HTTP 200 in
0.221s with no redirect, measured with
curl -L --max-time 20, and that number means something
because it came off the wire.
Enumerate surfaces from the platform, not from your repo docs
The repo list is not the surface list. On 2026-07-16 I audited every
app in the jasoncola1@gmail.com App Store Connect account
for a stale-name bug and found Suede Cinematic: AI Video,
ASC app id 6780266335, bundle
xyz.suedeai.cinematic. Its only submission, v1.0, had been
sitting REJECTED since 2026-06-14 with no resubmission. No
project note, map, or repo doc tracked it. It corresponded to an
untracked Suede-AI-App/ios-cinematic/ directory with no
README, AGENTS, or CLAUDE.md inside. My call on 2026-07-16 was to leave
it alone and not resubmit, which is a decision I could only make once
the sweep put it in front of me.
Repo documentation is the least reliable inventory I own, because it
gets written once and never re-checked against the platform. On
2026-07-10 I confirmed that suede-map (map.suedeai.ai) does
not auto-deploy on push while its own repo CLAUDE.md claimed push went
to production. It ships with npx vercel --prod --yes from
~/code/suede-map followed by a curl. At least four surfaces
in this estate are manual-deploy while the rest auto-deploy, and the
deploy mechanism is a per-project fact you verify rather than
inherit.
The same trap sits one layer up, at the account level. On 2026-07-17
suedeai.xyz did not appear in Search Console for
jasoncola1@gmail.com. Filtering the property switcher
showed suedeai.ai, suedeai.org, and unrelated
suedeai*.com domains, with no .xyz anywhere. A
second Google account under the Johnny Suede identity holds verified
ownership of that domain and is also a listed owner on
suedeai.ai, where Settings then Users and permissions shows
both JasonCola1 and Johnny Suede. The Change
of Address from .xyz to .ai had been submitted
from that second account. On a solo estate accumulated over years, a
property that does not exist usually means you are signed in as the
wrong identity. Check the second account before you re-verify anything
from scratch.
The same check across unrelated repos finds house patterns
Running the same check across unrelated repos on the same day is what turns a bug into a pattern. The performance pass produced four families, and three of them appeared in more than one repo.
| Family | Where it surfaced | Fix |
|---|---|---|
| Wallet/Web3 SDK mounted globally | Suede-AI-App/frontend studio/play/distro/fretpulse/acp
routes; 6 marketing routes on ip.suedeai.ai |
PR #655 and PR #656 |
| React #418 hydration mismatch after static rendering | suede-home; frontend studio and social |
PR #657 and PR #658 |
NO_LCP from a non-composited or animated LCP
element |
studio.suedeai.ai; acp.suedeai.ai |
PR #662, PR #669, and PR #5 in
JasonColapietro/suede-acp-x402-business |
| Merged work not reaching production | muse.suedeai.ai; acp.suedeai.ai |
Manual vercel --prod plus alias pin |
The wallet family is the clearest case of a house pattern. Pure
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 that was the dominant TBT
and bundle-size cost on both sets of routes. In both repos the provider
stack was mounted unconditionally in the root layout.
landing-site also had its own Connect Wallet button inside
Header.tsx, doing the same thing a second time from shared
chrome rather than from a page. Route-level scoping alone would have
missed that. Grep transitively for the hooks, not for
src/app. PR #656 also migrated the homepage’s single
on-chain read to a viem createPublicClient rather than
keeping the whole SDK around to serve one call.
The hydration family arrived out of a change I made on purpose. PR
#653 removed a root-layout headers() call that had been
forcing dynamic rendering across the estate, which flipped studio,
social, play, and distro to static. Every usePathname() or
typeof window conditional living in shared chrome had been
correct under per-request rendering and became wrong under prerendering,
because the check now bakes in a build-time value that disagrees with
the client at runtime. React #418 showed up independently in three repos
the same day. The fix is a mounted flag backed by
useSyncExternalStore, so the first client render matches
server output and self-corrects after mount. A plain
useEffect plus useState mount flag trips this
codebase’s react-hooks/set-state-in-effect lint rule, which
takes the obvious shape off the table. PR #658 handled
page-padding.tsx and mobile-menu-button.tsx;
PR #657 handled FounderSignature.tsx and
MusicianCreateFooterCta.tsx in suede-home.
One symptom hitting three repos inside a single day is the signal that pays for the whole pass.
Same symptom does not mean same cause
studio.suedeai.ai returned a NO_LCP error
and a hard 0 Performance score under Lighthouse’s default Lantern
simulated-throttling mode, while real devtools-throttling measurement
gave a normal number. The cause was an “ember-breathe” live-indicator
dot animating box-shadow, a non-composited property, with
infinite iteration. Continuous non-compositor repaint left
Lantern’s simulator unable to model a valid LCP candidate graph. The
diagnostic recipe that got me there: bisect by page, since
/studio failed and /studio/about did not, then
run lighthouse --only-audits=non-composited-animations,
then confirm against a raw Chrome trace with --save-assets
looking for largestContentfulPaint::Candidate events, or a
PerformanceObserver check in a real browser. That root
cause took most of a day. PR #662 fixed it alongside font preload and
subsetting on studio and a real LCP-image plus render-blocking-CSS fix
on social, verified locally at Performance 0.55 to 0.97, LCP 5.1s to
2.0s, TBT 590ms to 70ms. PR #669 finished the job the next day by
exempting the LCP element from the animation outright.
Then acp.suedeai.ai produced the identical
NO_LCP symptom with a clean
non-composited-animations audit. Tightening the animation
timing by 3x, the move that had worked on ip.suedeai.ai and
studio.suedeai.ai, left NO_LCP in place. The
LCP element itself, the H1, was opacity:0-animated with an
entrance stagger, and an animated LCP candidate defeats Lantern no
matter how fast the animation runs. Full exemption of the H1, staggered
reveal kept on secondary hero elements, shipped as PR #5 in
JasonColapietro/suede-acp-x402-business, which is on my
personal account and not the Suede-AI org. That surface also turned out
not to auto-deploy on push, with a 6-day-stale build live at the time. I
deployed manually with vercel --prod plus an alias pin and
live-verified 97/100/100/100.
Carrying a fix forward across surfaces is how a pass gets fast. Carrying an assumption forward is how it produces false negatives.
Check load average before you trust a Lighthouse number
The measurement instrument in the performance pass was this Mac, and
the Mac was under load from work that had nothing to do with the pass.
Multiple parallel Claude Code sessions and Codex CLI processes belonging
to other sessions were running throughout. Host CPU contention corrupts
--throttling-method=devtools in proportion to the
contention, since that mode applies a real CPU multiplier. Above a
threshold it corrupts default Lantern mode too.
I have the proof rather than the theory. An agent reproduced the
identical local production build passing Lantern cleanly twice, then
failing NO_LCP on the third run once machine load rose.
Nothing about the build changed. Load average sat at 8 to 16 for hours
during the pass, confirmed with uptime, and oscillated
between 5 and 838 through the following night.
the standing rule is a command before a number:
uptimeAbove load average roughly 5, do not trust an absolute Lighthouse
score. Trust before-and-after deltas taken on the same machine state, or
get a real PageSpeed Insights reading or a real-device reading. The
keyless default quota on pagespeedonline.googleapis.com
does not qualify as a fallback channel: it is a shared per-project daily
quota, it was already exhausted before the pass started, and it never
reset inside the session. A real API key is required for that path to be
worth anything.
The rule cost me a merge and I let it. PR #675, a framer-motion code-split on studio, was left open and unverified because the machine hit load 600 to 838 during verification. At that load “still broken” and “fixed” are both unprovable, and merging on either claim would have been guessing.
Resource contention from parallel work shows up in other costumes. On
2026-07-23 three separate agents, on fretpulse, Suede Social, and the
music-generator TWA task, all hit 99 to 100 percent full disk mid-build
on the same day. Each self-recovered, so I flagged it for a
df -h check rather than treating it as blocking. The root
condition is the same one that corrupted the Lighthouse numbers a week
earlier, and it presents as unrelated per-repo build failures.
A green build is not a result
PR #660 lazy-loaded the wallet-connect button in
Header.tsx. It passed build, tsc, and lint,
merged, deployed, and then threw a client-side exception on click across
6 marketing routes in production. ssr:false skips server
rendering and does nothing to make wagmi hooks provider-optional, so
once the layout dropped WalletProviders and the component
mounted client-side with no WagmiProvider ancestor,
useConfig() threw synchronously. Manual click-through on an
isolated browser tab caught it, and PR #661 reverted inside about 10
minutes. PR #663 redid it: HeaderWalletConnect brings its
own scoped EvmProvider and is standalone by default, and
routes that already have an ancestor provider opt out through a
walletProvidersMounted prop instead of relying on ancestor
detection. I click-tested all 8 wallet-touching routes before and after
that merge.
Merged is not deployed either. On 2026-07-16
muse.suedeai.ai was still serving the pre-change build over
an hour and two pushes after the manifest work merged to
origin/main at commit f3f3558, and
https://app.suedeai.ai/.well-known/assetlinks.json was
still 404 after 14 minutes of polling despite commit
bcde5f2f being on main. The Vercel CLI on this machine had
{} in
~/Library/Application Support/com.vercel.cli/auth.json,
zero stored credentials, so every vercel command blocked on
an interactive OAuth device flow that the agent declined to complete
unprompted. Only a live curl surfaced any of it.
Two more things a long pass will take from you. Production broke for
roughly 18 minutes mid-pass on 2026-07-17 because an unrelated session
landed a bad rebase on Suede-AI-App main, fixed in PRs #672
and #673, with no connection to the performance work at all. And on
2026-07-18 the worktrees holding in-progress work for the two hardest
remaining surfaces, ip.suedeai.ai and
social.suedeai.ai at Performance around 56 to 57, were
orphaned by a process restart with nothing salvageable. That work
carried forward through the vault handoff at
05_handoffs/2026-07-18-claude-suede-lighthouse-pass-handoff.md.
Commit early on a multi-surface pass, because uncommitted worktree state
is one restart from zero.
Write the sweep date into the rule
A pass produces two artifacts. The first is the per-surface record
with dates attached, which for the 2026-07-17 pass lives in the vault
handoff above , which recorded 18 to 20 of 22 surfaces confirmed at 90
or better. The second is a standing rule in CLAUDE.md
carrying the date it was last enforced, so the next operator reads
“Swept 2026-07-26” and knows exactly how much of the estate that claim
covers and how stale it has become.
Rate limits set the pace, not your throughput. The 19-domain Search Console sweep on 2026-07-10 hit “Quota Exceeded, try tomorrow” after 11 indexing requests, leaving 8 batch-2 domains unindexed, and a scheduled retry the same day hit the quota on the first domain and completed 0 of 8. That is a per-account daily limit and it makes a 19-domain sweep a multi-day operation by design. Plan the pass around the platform’s ceiling.
Two smaller disciplines close the loop. Before you delete anything a
sweep marks as dead, prove nothing references it: when I pruned the
music skill cluster from 14 skills to 3 on 2026-07-12, I ran
lint_skill_estate.py with --external-root
pointed at the public pack to confirm no surviving skill referenced the
11 deletions. And treat verification leftovers as access-control debt.
On 2026-07-18 suedeai.ai showed 1 stale “Domain name
provider” ownership token owned by johnny@suedeai.ai, a
leftover DNS TXT record that would have let anyone controlling that
mailbox re-verify ownership and take back Search Console access. I
removed the record from GoDaddy DNS and confirmed through Search
Console’s Verify Removal flow, which now reports 0 unused tokens.