diff --git a/.github/workflows/migration_version_check.yml b/.github/workflows/migration_version_check.yml new file mode 100644 index 00000000000..db43ab0425b --- /dev/null +++ b/.github/workflows/migration_version_check.yml @@ -0,0 +1,41 @@ +name: Migration Version Check + +on: + pull_request: + paths: + - 'db/migrate/**' + +concurrency: + group: pr-${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + migration_version_check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Reject hand-written migration versions + run: | + rounded=$(git diff --name-only --diff-filter=AR HEAD^1 HEAD -- db/migrate | grep -E '^db/migrate/[0-9]{10}0000_' || true) + if [ -n "$rounded" ]; then + echo "::error::Rounded migration versions collide across branches. Regenerate with 'bundle exec rails g migration':" + echo "$rounded" + exit 1 + fi + + - name: Reject duplicate migration versions + run: | + duplicates=$(ls db/migrate | cut -d_ -f1 | sort | uniq -d) + if [ -n "$duplicates" ]; then + echo "::error::Duplicate migration versions break db:migrate:" + echo "$duplicates" + exit 1 + fi diff --git a/AGENTS.md b/AGENTS.md index 3bc730befaf..4c987b8e3e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,6 +44,7 @@ ## General Guidelines - **Database foreign keys**: Prefer Rails associations and callbacks over database foreign-key constraints. Add a database foreign key only when there is a clear need and after checking deletion behavior and production migration/locking risks. Existing foreign keys alone are not a reason to add new ones. +- **Migrations**: Create migrations with `bundle exec rails g migration ` so the version is a real timestamp. Never hand-write or round the version (e.g. `20260925000000`); rounded versions collide across branches and break `db:migrate`. - Prefer the smallest production-ready change that solves the current problem. - Build for the expected production path first. Do not add speculative guards, fallbacks, retries, or edge-case handling unless the caller can actually hit that case or production has proven it necessary. - Enforce eligibility and exclusivity rules at the earliest shared entry point. Do not repeat backup guards across downstream jobs, callbacks, services, or writes unless a proven independent path bypasses that point.