Real engineering failure shapes

The code can be valid and the change can still be wrong.

These are not lint problems. They are the kinds of cross-tool, cross-repo, rollout, and compatibility constraints senior engineers catch because they remember the history.

Database migration

NOT NULL before the backfill

The task says “make email required.” The agent changes the schema immediately. The migration playbook requires adding the column/constraint safely, backfilling existing rows, validating, and only then enforcing it.

PR: SET NOT NULL now
team rule: backfill → validate → enforce
Public API

Backend passes. Clients break.

An API field is renamed in the service and OpenAPI file. Service tests pass, but an older mobile release and generated SDK still expect the previous field for the deprecation window.

PR: slug → handle
consumer: still reads workspace.slug
Generated code

Schema changed, SDK did not

The agent edits the API implementation and contract but misses the repository that generates the TypeScript and Python clients from that contract.

openapi.yaml: changed
sdk-typescript: stale
sdk-python: stale
Async jobs

A retry creates duplicate side effects

A queue worker uses at-least-once delivery. The agent adds an email, webhook, or external write directly in the handler without the job's required idempotency key.

worker: may retry
PR: sendWebhook() every attempt
Auth

A read path quietly gains write scope

The agent reuses an existing internal client because it is convenient. That client carries broader OAuth scopes than the read-only flow is supposed to have.

required: records:read
new client: records:read + records:write
Monorepo

Shared type changes. One consumer is missed.

A shared package changes an event or type. The web app is updated in the same PR, but a background worker imports the same package through a different workspace path and is not touched.

packages/events: v4 shape
web: updated
worker: still destructures v3 field
Feature flag

The flag is removed before old clients age out

The agent sees a flag that has been “100% on” for weeks and deletes the fallback. The mobile support policy says the old app version still needs that branch until the minimum supported version moves.

dashboard: 100% enabled
mobile policy: keep fallback through v8.14
Events

An event field is renamed like a local refactor

The producer and its tests move from account_id to workspace_id. The event contract says consumers must receive both during a compatibility window.

producer: workspace_id only
contract: dual-publish for 2 releases
Infrastructure

A new storage resource violates residency

The agent provisions a bucket in the default region because the Terraform module permits it. The customer/environment policy requires EU workloads to keep customer data in-region.

terraform default: us-east-1
environment rule: eu-west-1 only
Transactions

An event moves before the commit

The agent publishes an integration event immediately after a write call. The service's outbox pattern requires publishing only from committed rows so consumers never observe data that later rolls back.

PR: save() → publish()
service rule: commit → outbox → publish
Permissions

A service-role key bypasses row-level security

The agent fixes a failing query by swapping in a privileged server client. It works, but bypasses the tenant isolation rule the normal user-scoped client enforces.

before: tenant-scoped client
PR: service-role client for convenience
Rollout

Cleanup happens in the same deploy as the cutover

The task says “finish migration.” The agent removes old reads, old writes, and the fallback immediately. The rollout plan requires one release of observation before destructive cleanup.

PR: cutover + cleanup together
plan: cutover → observe → cleanup
What HoldUp is looking for

Not “is this code good?” — “what did this task need to preserve?”

For each task, HoldUp finds the relevant constraints before implementation, makes contradictions explicit, and carries that same approved packet into PR review.