Pre-push checklist¶
Run these gates before every git push on a module build. Each one is fast to
check locally and catches a class of failure that has burned CI cycles and produced
wiki-sync churn across multiple modules in practice.
The gates below were identified after MOD-118 required 5 push cycles to reach Built+Deployed — each cycle due to a failure category in this list. Catching them locally saves ~30–60 min of CI compute and avoids the handoff collisions that failed pipelines generate.
This checklist is separate from the acceptance criteria (which covers what must be true when the module is complete) and the module build prompt (which covers what to read before writing code). Run this checklist immediately before pushing.
Gate 1 — Lockfile drift on a new workspace package¶
What fails: pnpm install --frozen-lockfile errors with ERR_PNPM_OUTDATED_LOCKFILE
in migrate-all or any CI job that runs install. Happens when a new
package.json is added to the workspace without re-running pnpm install at
the repo root.
Check:
git diff --name-only main HEAD | grep -q "package\.json$" && \
pnpm install --no-frozen-lockfile && \
git diff --quiet pnpm-lock.yaml || \
echo "❌ pnpm-lock.yaml has uncommitted changes — stage and commit before pushing"
If the check prints the error line, stage the updated lockfile and amend or add a commit before pushing.
Also affects: any commit that adds a shared package (e.g. @bank-core/idempotency,
@bank-core/mod-NNN-contracts) — the lockfile must be updated in the same push.
Gate 2 — changes: rule scope on lockfile-only or infra-only commits¶
What fails: GitLab creates no pipeline at all. The push sits on main silently.
If the intent was to re-fire a module's deploy after a previously-failed pipeline,
the deploy never runs.
Why it happens: Module-deploy changes: rules require MOD-NNN-.../** or
.gitlab/ci/mod-NNN.gitlab-ci.yml. A repo-root file change (lockfile fix, shared
infra update, workspace config) does not match any per-module rule and triggers
nothing.
Check:
# Does this push touch at least one file inside the target module's directory?
git diff --name-only main HEAD | grep -q "^MOD-${MODULE_ID}-" || \
echo "❌ No module-dir file changed — GitLab may not trigger the deploy job"
Fix: if the push's only purpose is to re-fire a specific module deploy, touch any file inside the module directory in the same commit. A comment addition in any source file is sufficient.
Gate 3 — Cleanup + immutability collision in test fixtures¶
What fails: afterAll or afterEach in integration tests panics with a foreign
key violation when attempting to DELETE from a parent table that has a Cat 1
immutable child table (ADR-048) referencing it.
Why it happens: ADR-048 Cat 1 immutable tables carry a
BEFORE UPDATE OR DELETE OR TRUNCATE trigger that rejects all mutations. A
cleanupFoo() helper that does DELETE FROM parent will fail once a child row
exists, because the child's FK constraint prevents the parent delete.
Check: For any module with a Cat 1 immutable table that has a FK into a parent in the same schema — confirm the test fixture cleanup does NOT delete the parent directly.
Fix options:
- Use randomUUID() per test run and accept dev-DB leakage (preferred for immutable
tables — the leakage is harmless and isolated per environment)
- Use a savepoint / rollback pattern instead of explicit deletes
- Clean child rows first if the child is not itself immutable, then delete the parent
Gate 4 — Reserved-concurrency budget on the dev account¶
What fails: sst deploy errors with:
InvalidParameterValueException: Specified ReservedConcurrentExecutions for
function decreases account's UnreservedConcurrentExecution below its minimum
value of [100].
Why it happens: The AWS dev account has a per-region unreserved concurrency floor
of 100. Each Lambda with reservedConcurrentExecutions set consumes from the account
pool. When multiple modules each allocate reserved concurrency, the pool depletes.
Rule: New modules must not set reservedConcurrentExecutions in their Lambda
config. Leave it undefined and rely on unreserved concurrency for dev/staging.
Tune reserved concurrency for production only, after a capacity review that sums
existing reservations across all deployed modules.
Check:
grep -r "reservedConcurrentExecutions" MOD-${MODULE_ID}-*/infra/ && \
echo "❌ reservedConcurrentExecutions is set — remove for dev; only set in prod after capacity review" || \
echo "✓ no reserved concurrency set"
Gate 5 — Column-name typos in integration test SQL¶
What fails: Integration tests error at runtime with:
Why it happens: tsc --noEmit type-checks TypeScript but does not validate raw
SQL string literals inside template expressions. A column referenced in an ORDER BY,
WHERE, or SELECT can be misspelled without the type checker catching it.
Check: Before pushing, cross-reference any raw SQL in test files against the migration that created the table.
# List column references in test SQL strings and compare against the V### migration
grep -rh "ORDER BY\|WHERE\|SELECT" MOD-${MODULE_ID}-*/tests/ --include="*.ts" | \
grep -oP '(?<=\b(ORDER BY|WHERE|AND|OR) )\w+' | sort -u
Then verify each listed name appears in the corresponding migrations/V*.sql file.
A lightweight permanent fix: use a thin query-builder helper that introspects the schema at first call, making typos a type error rather than a runtime failure.
Running the full set¶
Before any push on a new or in-progress module:
MODULE_ID="NNN" # e.g. 135
# Gate 1 — lockfile
git diff --name-only main HEAD | grep -q "package\.json$" && \
pnpm install --no-frozen-lockfile && \
git diff --quiet pnpm-lock.yaml || echo "❌ Gate 1: lockfile drift"
# Gate 2 — changes: rule scope
git diff --name-only main HEAD | grep -q "^MOD-${MODULE_ID}-" || \
echo "⚠️ Gate 2: no module-dir file changed — check deploy trigger"
# Gate 3 — immutability / fixture cleanup
# (manual: review test helpers for DELETE on Cat 1 parent tables)
# Gate 4 — reserved concurrency
grep -r "reservedConcurrentExecutions" MOD-${MODULE_ID}-*/infra/ 2>/dev/null && \
echo "❌ Gate 4: reserved concurrency set" || echo "✓ Gate 4: ok"
# Gate 5 — column name typos
# (manual: cross-check SQL literals in tests against V### migrations)
Extending this list¶
When a new recurring failure category is identified in CI, add it here with: - What the error looks like (exact message or symptom) - Why it happens (root cause, one paragraph) - The pre-push check command or manual step - Any permanent fix that would make the gate unnecessary
File the addition as a wiki edit — no ADR required.