Chapter 37. What Done Means, and Who Decides
Done is a claim about the world, and every claim in this estate has to survive being checked. I hold the result to four conditions: checked against the live surface, not the config meant to produce it; carrying a human name where a human name belongs; written somewhere the next agent finds it before it repeats the mistake; and reported without asking anyone for permission to care.
Nothing about that list is philosophy. Each condition exists because I skipped it once and paid.
Checked against the live surface, not the config
Until 2026-06-11, Suede Social served an empty server-rendered body
on every route. Crawlers saw nothing. Every build passed, every deploy
succeeded, every page looked correct in a browser, because the client
rendered it after load. The cause was one line in
frontend/src/components/theme-provider.tsx:
if (!mounted) return null;
A mount gate wrapped the entire app body. PR #290
(f6d2dd7) made children always SSR, moved the dark-default
localStorage migration into a blocking inline script that runs before
next-themes’ pre-paint script, and put
suppressHydrationWarning on <html>. The
standing rule I wrote afterward is a one-liner I now run against each
domain after production deploys:
curl -s <domain> | grep '<h1'The check costs a second. The condition it catches lasted months.
A month later, on 2026-07-16, agents.suedeai.ai gave me the same
lesson in a different register. Vercel Production env vars
DB_DRIVER and SUPABASE_* were pulling as empty
strings. The code read them, found nothing, and fell back to a
non-durable SQLite repository inside a serverless function without
raising an error. Agent Studio’s Supabase runtime had never been
implemented, though Phase 9 assumed DB_DRIVER=supabase in
production. A same-day multi-lane audit found a false save/recovery
conflict that auto-persisted a draft on page view, a disabled Run
button, and live /api/v2/* endpoints returning 503. PR #71
covered visual and UX, PR #72 covered database security. Supabase config
was restored, RLS hardened, the service-role key removed from Vercel,
atomic RPCs added, and an end-to-end canary of create, bind, version,
and run passed. PR #73 followed.
An env var that reads as an empty string does more damage than one
that is missing, because the missing one throws. Dev runs SQLite, prod
runs Supabase, and there is no local .env, so production
behavior can only be established by hitting production. That is the
whole rule: verify the datastore identity from the datastore, not from
the intent recorded in a dashboard.
I also wrote a caveat into that memory the same day, and I stand by it. Given how much churn happened in one day, the state is worth an independent spot-check before anything downstream depends on it. Recording your own uncertainty is part of finishing.
Verification takes a specific shape when the thing being verified is
access rather than output. Suede-AI/strumly is private and
owned by the Suede-AI account. The proof that the four-step
invitation flow worked is not the fact that the invitation was
accepted:
gh repo view Suede-AI/strumly --json viewerPermission
# "WRITE"Attributed to a person, not a bot account
In May 2026 a comment on
solana-foundation/awesome-solana-ai#155 went out under
Suede-AI, a deprecated bot account, because gh
had that identity active in the keyring and nothing checked before the
mutating command ran. The comment is permanent and public. On an
external repository, in an ecosystem where the people reading are the
people I want to work with.
The rule now: verify gh auth status shows the personal
account before gh pr create, gh pr comment,
gh issue create, gh issue comment,
gh pr review, gh pr merge, or
gh release create. Read-only commands can run under
whichever account is active, because they stamp nothing. The asymmetry
is the point. Mutating commands write an identity into a record I cannot
retract, and the check costs one line.
For a solo founder, attribution is a distribution asset. Investors,
hiring partners, and collaborators look at founder GitHub activity and
read package metadata. A bot byline does not compound anything. So the
split is explicit: Suede-AI appears in repository paths,
and contributors, maintainers, and founders resolve to the human. The
one place the org account is correct is the Org entity’s
sameAs field, which is the inverse of the attribution rule
and catches people who learned the first half.
Two traps sit inside this. The account was renamed from
jasoncola1 to JasonColapietro on 2026-05-11,
and gh’s local keyring kept displaying the old identifier
afterward, which looks exactly like the wrong account being active. The
OAuth token is bound to the account and resolves correctly; the cached
display name lags. gh auth refresh -h github.com clears it,
and until then gh auth switch --user jasoncola1 targets the
same human account. Know which layer is authoritative or you will chase
a non-bug for an hour. The second trap: when sweeping old-username
references out of repos and docs, replace the username and leave
jasoncola1@gmail.com alone. It is a Gmail address, a
separate system from GitHub usernames, still the verified email on the
renamed account, and rewriting it breaks commit attribution.
Attribution extends into the machine-readable layer, where it fails
without any visible symptom. On agentix, founder SEO shipped with a
freshly minted Person @id of
/#person-jason-colapietro. The page rendered fine. It would
not merge with the live entity graph, because graph consolidation keys
on identical @id strings. Canonical values live in
suede-home/src/lib/seo-entity.ts in
Suede-AI/Suede-AI-App: Person
https://suedeai.ai/founder#person, Org
https://suedeai.ai/#organization, Website
https://suedeai.ai/#website, JCIG
https://jcinvestmentgroup.ventures/#organization.
Satellites mirror those strings verbatim. The canon was backported into
seo-entity.ts on 2026-06-11 (Suede-AI-App main
756f1205), and agentix now matches.
The same surface carried @suedeai in its social
metadata, a handle that does not exist. Corrected on 2026-06-11 to site
@AISUEDE and creator @johnnysuede. Next.js
replaces the whole metadata object per segment, so a page-level
twitter block overrides a correct root layout without
warning. Verify the rendered metadata per route.
Built and dark does not count as shipped
A feature sitting behind a disabled flag is code, not a product. It
has no users, no error rate, and no evidence. When I write it up as
shipped, I have corrupted the only record I have of what the estate
does, and the corruption compounds: the next agent reads the memory,
believes the capability exists, and builds on top of nothing. Agent
Studio is the clean case. Phase 9 assumed
DB_DRIVER=supabase in production and the runtime behind
that assumption had never been implemented. The plan said durable
Postgres. Production ran ephemeral SQLite. Both statements were true at
once for as long as nobody checked.
The census is what keeps the distinction honest. It is a flat list of
every surface and what each one does right now, rebuilt by hitting the
surfaces rather than reading the repository. Anything built and dark
gets recorded with the flag name and the reason it is off, in the same
list, so the gap between intent and behavior stays visible instead of
resolving itself into optimism. The census entry I point at when
explaining this is paid memory sync on muse.suedeai.ai: built, merged,
and sitting behind MEMORY_SYNC_ENABLED=false. It is in the
codebase and it is not in production, and the census says so in those
words rather than listing the feature as shipped.
The census also caps the estate’s claims. The public claim can be no larger than the census entry that backs it.
The voice standard for outward-facing copy
On 2026-05-14 a re-engagement email draft came back to me opening with “just wanted to flag what’s new in case any of it matters.” What I said in response is the whole standard in one line: “its suede of course it fucking matters.”
The line asked the reader to grant the update permission to matter. Suede claims best-in-category positioning, and copy that pre-apologizes tells the reader the claim is soft. The rule covers outward-facing artifacts: emails, marketing, social, READMEs, app store listings, blog posts, outreach, slide decks. Approved openers instead: “Here’s what shipped since:”, “Quick update on what’s new:”, “Three things worth knowing about:”. Warmth stays legal. “Hey {name}” and “reply if you bounced” are fine, because confidence is about whether the copy claims its own value and has nothing to do with tone temperature. The rule bites hardest on dormant re-engagement, where apologetic copy lowers reply rates at the exact moment reply rate is the metric.
Commit messages, code comments, and debugging notes are exempt. Private working text does not need a brand voice.
Vocabulary sits under the same standard. On 2026-05-15, mid-write, a spec draft called the Suede Social audience “players” and I stopped it. The shipped codebase had already standardized on “members” in discover page metadata and OG cards, so the spec had drifted from live copy. Substitutes: “members” for the collective, “guitarists” when the context is gear, “musicians” when it is broader, or direct pronouns. If the sentence reads without a collective noun, drop the noun. The correction applies upstream, to specs, design proposals, PR descriptions, and brainstorming summaries, because wording that survives a spec propagates into the build and then has to be fixed at every layer it reached.
Claims the estate refuses to make
On 2026-06-10, during the master registry rebuild, the first pass kept the existing section structure and swapped the accent color and the copy. I cut in mid-pass: “this is the same old site, i want it to look so much better… much cleaner and more interactive.” The corrective includes a claim boundary. Prefer live on-chain and honest numbers over static mock numbers on any surface a visitor can touch pre-signup. A mock number on a live page is a small lie that gets screenshotted.
On 2026-07-21, scoping a metered external-tool node for Agent Studio, a session proposed Monid, a peer tool-marketplace aggregator, as the backend supplier. It had 1 GitHub star, a release five days old, and no README. I killed it: “no we dont integrate any third partys yet.” Every external-call surface in the estate is first-party, including the 12 Business Skills templates and all Suede Tools canvas nodes, each calling its provider directly. The written rationale, that a reseller stacks Suede’s margin on the aggregator’s and adds dependency risk, is flagged in the memory as a working theory rather than a reason I gave, and it stays flagged that way. “Yet” makes it a maturity bar rather than a ban.
The claim case now has a winner. On 2026-07-17, X and Substack bios
still carried unverified “Forbes contributor” wording after it had been
removed from suedeai.ai, and two memory files disagreed about whether
the phrase belonged in my canonical description. For this editorial
revision I checked the live founder record. On 2026-07-29,
suedeai.ai/founder identifies me as a published author and
does not make a Forbes claim. That live record wins: do not describe me
as a Forbes contributor unless a primary publication record later
verifies it. Claim hygiene still needs a surface inventory covering the
site, GitHub, X, Substack, LinkedIn, and store listings, because a
retired claim can survive wherever the pass did not reach.
The same 2026-07-17 pass shows what recording looks like when it goes
well. The profile README went from 245 lines and 1,816 words to 38 lines
and 320 words across PR #5 and PR #6, COMBINED.md from 725
lines to 17, topics from 20 to 14. Before any of it, the original was
archived byte-for-byte with hashes and git bundle verify,
so the prune is reversible and unique material can be routed to its
canonical surface instead of republished as duplicate markdown. Two
items from that pass remain open: the pinned
suede-sdk-python card carries a stale description that my
personal account cannot edit, since pin cards render the owning repo’s
description and the repo lives under Suede-AI, which leaves
a Suede-AI-admin lane or dropping the pin. And
Suede-AI/suede-docs’s repo manifest and skill count still
need a cleanup pass.
The loop: instruct, verify, record, hand off
Instruct, verify, record, hand off. The instruction names the surface
and the acceptance check. Verification hits the live thing:
curl the domain, read viewerPermission, run
the canary. Recording writes what happened, what remains open, and where
the disagreement is, in the place the next session will read before it
starts. The handoff assumes the reader has no memory of any of this,
because in the strict sense that is true.
The Reown/WalletConnect allowlist gap on social.suedeai.ai closes the
shape. Found and fixed live on 2026-07-17. Right after verifying it I
called off further investment in that surface: “dont worry about crypto
or wallets with suede social right now.” The fix stays deployed and
correct. EvmProvider, WalletProviders, the
Solana wallet-adapter, WalletConnect/Reown config, and wallet-bind UI on
Suede Social are off the initiative list until I say otherwise, with
(app-host)/app.suedeai.ai excluded from that boundary
because wallets are core there. Fixing a surface is not a mandate to
keep spending attention on it, and that decision only holds if someone
writes it down.
I run this alone with agents. The estate has surfaces I have verified this month and surfaces I have not, two memory files that contradict each other about a bio line, and a pinned card I lack the permission to edit from where it appears. That list is the honest position. Done means the record and the live surface say the same thing, and I keep the list short enough to check.