fix(#60): surface db:push failures instead of swallowing the exit code#200
Open
agjs wants to merge 1 commit into
Open
fix(#60): surface db:push failures instead of swallowing the exit code#200agjs wants to merge 1 commit into
agjs wants to merge 1 commit into
Conversation
boringstackCommandStage ran `bun run db:push -- --force` every cycle but DISCARDED its result. A failed push — a broken Drizzle schema, or the DB not reachable — was silent, and the gate then ran against a stale/out-of-sync DB, surfacing confusing downstream errors instead of the real cause. Now a non-zero push short-circuits to a gate failure carrying the raw output, WITHOUT running the gate. It does NOT auto-classify schema-vs-infra from the text. Extended review proved that unwinnable: Postgres echoes ARBITRARY user identifiers AND unquoted data in error detail (`column "epipe"`, `DETAIL: Key (name)=(socket hang up) …`, `Failing row contains (1, EPIPE)`), so any token matcher — even quote-blanked or word-bounded — can match a genuine schema/data failure and mis-steer the model to "bring the stack up / do NOT edit the schema" (the stuck-inducing direction). Instead the error carries the raw output + a decision rule naming BOTH levers (fix apps/api/.../app.schema.ts for a column/table/type/constraint/data problem; dev.sh up for a connection/auth/TLS/does-not-exist/too-many-clients problem) and lets the model decide. No `file` is set — guessing app.schema.ts would be wrong for a real infra failure. (A clean infra HARD-abort belongs to the settleGate seam — #47.) The error KEY hashes the NUL-joined stdout/stderr verbatim (no lowercasing/whitespace/digit normalization, which is not semantics-preserving for Postgres output), so distinct failures get distinct keys while the same deterministic failure stays stable across cycles — stuck detection isn't fooled by a changed failure, nor a changed byte sliding between streams. Tests: db:push failure is surfaced (not swallowed) and PROVES the gate never runs afterward (db:push is the last command); the error is NOT auto-classified (raw output + a both-levers decision rule, no file) even for an adversarial `socket hang up` DETAIL value; distinct keys for preamble-tail / digit-only / case-only differences and a NUL-delimiter forge attempt, with the same failure stable. Full suite 0 fail; typecheck + lint clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
boringstackCommandStageranbun run db:push -- --forceevery cycle but discarded its result — a failed schema push (bad Drizzle schema, or the DB unreachable) was silent, and the gate then ran against a stale/out-of-sync DB, surfacing confusing downstream errors instead of the real cause.Now a non-zero push short-circuits to a gate failure carrying the raw output, without running the gate.
It does not auto-classify schema-vs-infra from the text — extended review proved that unwinnable (Postgres echoes arbitrary user identifiers and unquoted data in error detail:
column "epipe",DETAIL: Key (name)=(socket hang up),Failing row contains (1, EPIPE), so any matcher mis-steers real schema failures). Instead the error carries a decision rule naming both levers (fixapp.schema.tsfor a column/table/type/constraint/data problem;dev.sh upfor a connection/auth/TLS/does-not-exist/too-many-clients problem) and lets the model decide, with no guessedfile.The error key hashes
JSON.stringify([stdout, stderr])— an unforgeable serialization (escapes every byte incl. NUL, encodes the array boundary) with no cwd stripping — so distinct failures get distinct keys (stuck-detection isn't fooled) while the same deterministic failure stays stable.Panel: PASS (clean). Full suite green; typecheck + lint clean. Infra HARD-abort at the settleGate seam is a separate task (#47).