fix(db): reconcile prod-orphaned migrations blocking Supabase branching (#942)

* fix(db): reconcile prod-orphaned migrations blocking Supabase branching

Prod's schema_migrations carries three versions with no committed file on
main, leaving the default Supabase branch in MIGRATIONS_FAILED and stopping
preview branches from being created:

  20260707113729  add_transactions_enrichment    (adopted from #927)
  20260708120000  ledger_stats_committed_at_lag  (adopted from #935)
  20260708130000  ledger_deep_context            (adopted from #935)

Adopt the byte-identical SQL under the exact apply-time versions, plus the
matching pg-tests and fixtures for the two RPCs so pg-real stays green:
20260708120000 switches get_ledger_usage_stats' median_booking_lag_days to
committed_at, so the existing test now asserts the new behavior. Idempotent
(ADD COLUMN IF NOT EXISTS / CREATE OR REPLACE FUNCTION): no-op on prod,
clean on fresh replays, no-op on #927/#935's next rebase. The knowledge-page
UI/lib/i18n stay in #935.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(deps): pin @anthropic-ai/bedrock-sdk to 0.29.1

0.32.0 (grouped dependabot bump #884) broke Bedrock streaming in prod: empty stream / "request ended without sending any chunks", taking down the in-app AI assistant and invoice OCR. Local dev ran the stale 0.29.1 in node_modules, so it only failed on deploys built fresh from the lockfile. Revert to the six-week-stable 0.29.1; creds/region were never the cause (proven AKIA key + eu-west-1).

Guard against an accidental re-bump three ways: exact pin (no caret), a dependabot ignore, and a pinned-dep check in scripts/checks/no-new-antipatterns.mjs (check:guards). Unpin only once 0.32.x streaming is verified against Bedrock. See DECISIONS.md.
This commit is contained in:
Mattsson
2026-07-08 23:54:49 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent c068638d24
commit 8dde46ad96
11 changed files with 752 additions and 7 deletions
+55 -1
View File
@@ -22,6 +22,10 @@
* lineDimensionColumns() from the dimensions JSONB map
* (lib/bookkeeping/dimension-resolver.ts): a new direct insert site can
* silently diverge the mirror columns. Tracked as a file-set.
* 4. pinned-dep : a dependency pinned to an exact version (PINNED_DEPS)
* whose package.json spec or locked version drifted from the pin. Guards
* against a repeat of the @anthropic-ai/bedrock-sdk 0.32.0 prod outage
* (empty Bedrock stream). No baseline: any drift is a hard failure.
*
* Usage:
* node scripts/checks/no-new-antipatterns.mjs # check (CI)
@@ -130,10 +134,44 @@ function countNaiveRound() {
return count
}
// Dependencies pinned to an EXACT version on purpose, because a bump broke prod
// and must not silently return via `npm update`, a dependabot bump, or a manual
// install. Any drift (in package.json OR the lockfile) fails CI. See DECISIONS.md.
const PINNED_DEPS = [
{
name: '@anthropic-ai/bedrock-sdk',
version: '0.29.1',
reason:
'0.32.0 (grouped dependabot bump #884) broke Bedrock streaming in prod: empty stream, ' +
'"request ended without sending any chunks", taking down the AI assistant + invoice OCR. ' +
'Keep 0.29.1 until 0.32.x streaming is verified against Bedrock.',
},
]
/** Pinned deps whose package.json spec or locked version drifted from the pin. */
function findPinnedDepViolations() {
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'))
const lock = JSON.parse(fs.readFileSync(path.join(ROOT, 'package-lock.json'), 'utf8'))
const declared = { ...pkg.dependencies, ...pkg.devDependencies }
const out = []
for (const pin of PINNED_DEPS) {
const spec = declared[pin.name]
if (spec !== undefined && spec !== pin.version) {
out.push({ ...pin, where: 'package.json', actual: spec })
}
const locked = lock.packages?.[`node_modules/${pin.name}`]?.version
if (locked !== undefined && locked !== pin.version) {
out.push({ ...pin, where: 'package-lock.json', actual: locked })
}
}
return out
}
const current = {
rawRouteAuth: findRawRouteAuth(),
naiveOreRound: countNaiveRound(),
directJelInsert: findDirectJelInserts(),
pinnedDepViolations: findPinnedDepViolations(),
}
const isUpdate = process.argv.includes('--update')
@@ -190,6 +228,22 @@ if (current.directJelInsert.length) {
)
}
// 1c. pinned-dep: a version-pinned dependency must match its pin EXACTLY, in
// both package.json and the lockfile. No baseline: any drift is a hard failure.
if (current.pinnedDepViolations.length) {
failed = true
console.error(`\n✗ pinned-dep: ${current.pinnedDepViolations.length} version-pinned dependency change(s):`)
current.pinnedDepViolations.forEach((v) =>
console.error(
` ${v.name} in ${v.where}: found "${v.actual}", must be exactly "${v.version}".\n ${v.reason}`,
),
)
console.error(
' → restore the pin (npm install <name>@<version> --save-exact). Only change PINNED_DEPS in\n' +
' this script once the upstream regression is confirmed fixed.',
)
}
// 2. naive-ore-round: count may not increase.
if (current.naiveOreRound > baseline.naiveOreRound.count) {
failed = true
@@ -214,5 +268,5 @@ if (failed) {
process.exit(1)
}
console.log(
`\n✓ Antipattern guard passed (raw-route-auth: ${current.rawRouteAuth.length}, naive-ore-round: ${current.naiveOreRound}, direct-jel-insert: 0).`,
`\n✓ Antipattern guard passed (raw-route-auth: ${current.rawRouteAuth.length}, naive-ore-round: ${current.naiveOreRound}, direct-jel-insert: 0, pinned-dep: 0).`,
)