pithy doctorThe first command to run when your model of the project and the project disagree. It writes nothing.
The rule it follows
pithy doctor is the command, exit codes is what a finding does to the exit, and reading your logs is where to go when the project is fine and the runtime is not.
Knowing this stops you fighting the report.
A finding fails the exit. A contradiction between your own config and your own wiring.
A step not yet taken passes. No domain yet, an environment not provisioned, a secret registry never provisioned. Every project is in those states on day one, and turning the report red for everyone on day one teaches people to ignore it.
A check that could not run gates nothing and says so — reported as skipped with the reason, never rendered as a pass. A check nobody ran established nothing.
The findings, and what each one means
bindings
A required binding is missing from a stanza — and it checks both halves. A Durable Object needs its config entry and an export from your Worker’s entry module, and wrangler refuses the deploy when the second is missing.
Remedy: pithy upgrade writes both.
migrations
Asked in both directions, and the second is why the line exists.
N pending means run migrate. A migration the database has applied that you no longer declare is a different fault with a different remedy — and invisible to a pending count.
Remedy: on dev, delete local state and re-run; on a deployed environment, restore the migration or remove its ledger row deliberately.
prereqs
A capability composed without its peers. The Worker will not start at all — this is caught before you find out the hard way.
Remedy: pithy add <capability> --with-prerequisites.
entitlements
Routes gating on an entitlement while nothing composed provides one. The Worker is not broken — it is silently paywalled shut, and the runtime cannot tell that from a legitimately unentitled user.
Remedy: compose a provider, or remove the gates. Report-only, because which is right is your decision.
workflows
The one whose entire symptom is that nothing happens.
A job declared and never synced ships with no binding and no cron trigger. No request fails, no log line appears, no probe goes red — the cron simply never fires.
Remedy: pithy worker sync.
origins
Three shapes: no origin at all (legitimate on day one, reported but not failing), an origin nothing routes, and the public subdomain left open beside a custom domain — where the same-origin gate then refuses exactly the requests that establish who somebody is.
The last two fail.
Project name:
Two states fail, and neither ever advises deleting anything.
Drifted — every declared name leads with one and the same other project and yours appears nowhere. The only shape a one-string rename can leave.
Orphaned — a database’s owner stamp proves Pithy created it under another project’s name.
A single foreign name, a mix, an unset name and an unreadable config all pass: none of them establishes anything, and <app>-<env>-<resource> is also the ordinary Cloudflare convention. A database you brought with you is not an orphan.
Cloudflare:
Configured and broken fails. Absent does not — a project that has not been provisioned yet is legitimate.
It names the file it resolved and where the credentials came from — the file, or the environment. That distinction matters: CI has no file and authenticates from variables, so a report naming a path with nothing at it was misleading about the thing you were about to go and check.
A pinned account the credentials do not match fails, names both, and is decided before anything reaches the network.
Settings:
The one tier that looks at a value rather than at presence. A sending address on a domain nobody onboarded, a base URL nothing serves.
Local findings fail. Account findings fail too — but only when the account was reached.
The locations block, which is not a finding
Config dir:, Secrets:, Ports:, Dev login:.
Every line reports a location, and each is a path nothing in your repository mentions. None can fail the exit.
The one line there you can act on is a ports row marked not on disk: pruning cannot tell a deleted checkout from a moved one, so that row means you renamed that directory.
Working offline
pithy doctor --offlineNo ambient credentials, no network call. The variable form says the same thing to every command and is inherited by anything spawned.
Setting the config directory does not imply it — that conflates read config from here with do not use the environment, and CI legitimately means the first without the second. Set both and nothing in the CLI has a credential to authenticate with.
When doctor is clean and it is still broken
Then it is a runtime problem rather than a wiring one. Filter your logs by version, then by request id.
And check the one thing doctor structurally cannot see: a route the SPA shell is answering instead of your Worker.
pithy ui sync --check --worker apiNo test suite sees that either, because tests call handlers directly and the asset router is never in the picture.