Fixed
Five defects an end-to-end pass over a freshly installed project turned up.
-
rebase db pushcould not finish against a database that had never booted.
It died at "Step 3/3: Applying RLS policies" withrelation "rebase.users" does not exist— which is the documented bring-your-own-Postgres first run:
setDATABASE_URL, then push. The step before it,ensureAuthTables, exists
precisely to create that table and had failed one line earlier with a warning
nobody would connect to the error: it loads the project's collections to find
the one flaggedauth, and could not resolve./authorsontoauthors.ts.Third copy of the same rule. Two spawns of the driver CLI were fixed to take
tsx; this one was missed, because a guard that asks "does a tsx lookup appear
earlier in this file" is satisfied by an unrelated one. There is now a single
spawnDriverCli, and the gate counts spawn sites rather than inspecting them
— one copy is the number that makes the question unnecessary. -
.env.examplenamed a port the project does not publish, with its
DATABASE_URLuncommented.rebase initderives a per-project database port
and rewritesdocker-compose.ymland.envwith it. It left.env.exampleat
the template'slocalhost:5432— and left itsDATABASE_URLset, which
contradicts the rule the rest of the scaffold states in three places: unset
means the managed database.Copying
.env.exampleto.envis a near-universal habit, and on a machine
already running Postgres on 5432 — most developer machines — the result is not
a connection error. It is a successful connection to a different database,
which boot then provisions. Found exactly that way: a test harness read the
example file and spent three runs talking to the wrong server.initnow comments the line out and writes this project's port into it, and
into thesslmodeillustration further down, so the file names one port. -
A dev database that fails to start now says why. The daemon is detached, so
its output goes topglite.lograther than to the terminal, and both failure
messages named that path instead of carrying its contents. That is a fair
trade on a laptop and none at all in CI, where the runner discards the
workspace: a canary run failed with "The development database failed to start
(exit 1) — see pglite.log", and by the time anyone read it the file was gone.
Both messages carry the tail of the log now, bounded, and say so distinctly
when the daemon wrote nothing or the log cannot be read. -
GET /api/data/:slug/aggregateanswered 501 on every deployment.
FetchService.aggregatehas existed for as long as the route has, and the
route reads it offrestFetchService— an adapter object that listed
fetchCollectionForRestandfetchOneForRestand nothing else. Both getters,
so both the service-key path and the authenticated one. The 501's own comment
says it describes "every non-Postgres driver", which is what its author
believed; the endpoint index documentscount(),sum(),avg(),min(),
max()andgroupBy, none of which had ever run over HTTP.Forwarded from both. The authenticated one goes through the same read-only
withTransactionas the other two, which is what sets the RLS GUCs and drops
to the restricted role — not an implementation detail here, because an
aggregate is an efficient way to learn about rows you cannot select. Verified:
an admin counts 2 users and a second account counts 1, matching what each can
list, and an unauthenticated aggregate is 401. -
rebase ejectproduced a project that compiled and could not boot. The
template'senv.tstookzfrom"zod"rather than from the runtime, under a
comment explaining that the version an ejected project pinned did not export
it and ending "Switch both lines back when the version bumps." 0.18.0 bumped
and publishedz; the workaround stayed, and became the thing it was written
to avoid.loadEnv({ extend })recognises a.default()by class identity, so
a schema built with a second copy of zod is rejected field by field: every
ejected project on 0.18.0, 0.18.1 and 0.19.0 died at boot on a rawZodError
namingSMTP_PORT,SMTP_SECUREandAPP_NAME— three variables with
defaults, none of which the operator had set.check:templatesnow refuses a template that importszoddirectly while
the runtime exportsz, which is the narrow condition that turns this
workaround into a defect rather than a necessity. -
A path naming a relation the collection does not declare was a 500.
GET /api/data/authors/1/postsansweredINTERNAL_ERROR— telling the caller
to retry or report an outage over a request that will never succeed, and
burying a message that already said exactly what was wrong. It is a
404 UNKNOWN_RELATIONnow, like an unknown collection: the URL names nothing.
Three throw sites, one innested-path.tsand two inRelationService. -
rebase schema stalechecked nothing on the stock scaffold. It loads the
project's collections in process, and the two places that spawn the driver CLI
each chose the interpreter from the driver's own file extension — tsx for a
.tsentry,nodeotherwise. Correct while the driver shippedsrc/; wrong
the moment it shippeddist/cli.js, because the files being loaded are the
project's TypeScript. Node cannot resolve./authorsontoauthors.ts, so
the command reported "⏭ Not checked" and exited 0 — on a scaffold whose own
collections import each other. Both spawns take tsx whenever it is installed,
and a gate refuses an interpreter chosen from the artifact's extension. -
defineCronrequired anamethe runtime does not. The loader has always
readdefinition.name ?? loaded.id, where the id is the file's own name — so a
cron without one registered happily underrebase devand then failed
rebase buildwith "Property 'name' is missing".nameis optional, and
documented as defaulting to the filename.