feat(#59): ticket-first gate for database migrations + /migration skill + migration AgDR template#72
Conversation
…ll + migration AgDR template
Ships Phase 1 of the migration-gate feature per the issue's own
rollout plan. Phase 2 (sam/terraform plan introspection) is deferred —
path-pattern heuristics cover 80% of the real cases and the remaining
20% can be handled by per-project `migration_paths` overrides.
What ships:
1. `.claude/hooks/require-migration-ticket.sh` — new PreToolUse hook on
Edit/Write/MultiEdit. If the target path matches a migration
pattern, runs three gates:
G1: active ticket marker exists (same #41 two-tier resolution —
per-project preferred, ops-level fallback)
G2: the referenced GitHub issue is OPEN and carries the
`migration` label (default, overridable via
`.claude/project-config.json` → `migration_label`)
G3: the issue body contains a reference to a migration AgDR
matching `docs/agdr/AgDR-\d+-.*migration.*\.md`
Non-migration paths exit silently; the normal require-active-ticket
hook then applies. Exempts .claude/, docs/, projects/*/docs/,
*.md, and *.example up front.
2. `.claude/skills/migration/SKILL.md` — guided flow that produces
both artefacts in one pass. Asks for migration type, affected
tables, rollback plan (required non-empty), downtime estimate,
cross-service consumers, data volume, testing plan, and
observability. Writes the AgDR first (reversible local write),
then creates the labelled GitHub issue, then back-fills the AgDR
with the issue reference. Tells the user to run /start-ticket as
the next step — doesn't do it automatically so the handoff stays
explicit.
3. `templates/agdr-migration.md` — AgDR template with migration-
specific sections: Rollback Plan (explicit, not buried under
Consequences), Cross-Service Consumers, Testing Plan, and
Observability. Built on the same shape as the base AgDR template
(Context / Options Considered / Decision) so existing AgDR
discipline carries over.
Wiring:
- `.claude/settings.json`: require-migration-ticket.sh registered
BEFORE require-active-ticket.sh in the PreToolUse chain so
migration-specific blocking messages surface first.
- `.claude/rules/workflow-gates.md`: new gate 3a row + dedicated
"Migration Gate" section with default path list and enforcement
note.
- `.claude/hooks/README.md`: new section 1a documenting the hook's
three gates. Hook #1 (require-active-ticket) description updated
to include the #41 two-tier resolution.
- `CLAUDE.md`: hook count 16 → 17, skill count 31 → 32, templates
table gains Migration AgDR row, skills table gains /migration row.
- `workflows/sdlc.md`: new "Sub-Workflow: Database Migrations"
section after Phase 3 Exit Criteria, showing the flow from
/migration → /start-ticket → edit → dev smoke → staging →
prod → monitor.
Smoke-tested the hook with 11 path-matching cases in an isolated
/tmp mock ops root (/tmp/smoke-59.sh). All pass — non-migration
paths pass through, every migration-pattern path blocks when no
marker exists. Gates 2 and 3 require live gh calls and are covered
by real-usage flow (documented in the test output).
Closes #59
atlas-apex
left a comment
There was a problem hiding this comment.
Code Review: PR #72
Commit: e8ca0372b62b6db483f685ca3e583d35f2e7ccfd
Summary
Phase 1 of the migration gate ships cleanly: new require-migration-ticket.sh PreToolUse hook (3 gates: marker → open+labelled issue → AgDR body ref), /migration skill that produces ticket + AgDR in one guided flow, and templates/agdr-migration.md extending base AgDR with rollback/consumers/testing/observability. Docs wiring is consistent (hook count 16→17, skill count 31→32, new gate row 3a, new SDLC sub-workflow block).
Checklist Results
- Architecture & Design: Pass — hook slots in BEFORE active-ticket, reuses #41 marker resolution cleanly, skill writes AgDR first (reversible) then ticket then back-fill.
- Code Quality: Pass —
is_migration_path()isolated, exemptions short-circuit before network, clear block messages point at/migrationwith exact invocation. - Testing: Pass for path-matching (11/11 smoke cases); Gates 2/3 verified via real-usage flow (noted in PR body).
- Security: N/A — no new surface;
ghauth reused, no shell-injection (all interpolations quoted). - Performance: Pass — exempt paths exit before any
ghcall. - PR Description & Glossary: Pass — 4 terms, all migration-specific.
- Technical Decisions (AgDR): N/A — this PR IS the AgDR mechanism; no separate decision record needed for shipping the gate itself.
Findings
-
Path-match false-positive risk — LOW, but one sharp edge. The generic fallback
*/migrations/*(line 156) is the widest pattern. It catchesconfig/migrations/notes.md(though.mdexempt short-circuits earlier) and would catch e.g.src/utils/migrations/helpers.tsif a project puts non-migration helpers under amigrations/subtree. Acceptable given exemptions cover docs/meta and projects can override viamigration_paths. The specific concern raised (migrations.ymlin a config dir): safe, because*/migrations/*requires/migrations/as a directory segment, not a filename prefix. -
Custom-paths override semantic (override, not union) — CORRECT. Lines 129–139 explicitly bail after custom patterns without falling through. This is right: a project setting
migration_pathsis declaring "here, and nowhere else, are my migrations." Union semantics would surprise — a user who just registeredsrc/db/**would not expectdb/migrate/*.rbto also gate. Consider documenting this explicitly in hooks/README.md (the skill already implies it). -
Gate ordering UX — CORRECT. Migration hook before active-ticket means migration files get the targeted message (pointing at
/migration), non-migration files get the generic active-ticket message. Pass-through when not a migration path is silent (exit 0) so no double-block. -
AgDR slug regex — SUFFICIENTLY STRICT.
AgDR-[0-9]+-[^[:space:]]*migration[^[:space:]]*\.mdrequires the word "migration" in the slug. Technically a user could game it by naming a non-migration AgDR with "migration" in the slug — but that's active evasion, not accidental bypass. Combined with the template forcing the shape, this is the right strictness level. Tightening toAgDR-\d+-migration-would be more rigid but would break the handful of legit slugs likeAgDR-0042-data-migration-split.md. -
Skill flow (AgDR-first, ticket-second, back-fill-third) — CLEAR. Rationale in rule 5 is well-articulated (ghost-ticket exposure minimised). The explicit "do NOT auto-run /start-ticket" handoff is the right UX call.
Suggestions (non-blocking)
- Consider logging the matched pattern in the block message (already done on gate 1 block — line 198 — nice touch).
- hooks/README.md could note the "override replaces, not unions" semantic explicitly.
Verdict
APPROVED
Rex (Code Reviewer Agent)
Reviewed commit: e8ca0372b62b6db483f685ca3e583d35f2e7ccfd
- require-migration-ticket.sh: remove subsumed case patterns (*/projects/*/docs/* under */docs/*, */migrations/*/*.sql under */migrations/*.sql) and disable SC2254 on the intentional glob-expansion in the custom-paths case. - agdr-migration.md: add blank line before a bulleted list under 'Deploy-order constraint'. All 11 smoke cases still pass.
atlas-apex
left a comment
There was a problem hiding this comment.
Code Review: PR #72 (re-review)
Commit: a2694e93239812938df18b25f20a1b2b419e0da0
Summary
Incremental follow-up addressing 4 CI-lint findings. Verified diff vs prior approved SHA e8ca037 is purely lint fixes — no logic changes.
Incremental Diff Verified
.claude/hooks/require-migration-ticket.sh:- Exempt-arm: removed subsumed
*/projects/*/docs/*(covered by*/docs/*since shell case*crosses/), added explanatory comment — correct. - Default migration-arm: removed subsumed
*/migrations/*/*.sql(covered by*/migrations/*.sql), added explanatory comment — correct. - Added
# shellcheck disable=SC2254before the intentional globcase "$path" in $pat)with clear justification — correct.
- Exempt-arm: removed subsumed
templates/agdr-migration.md: added blank line before bulleted list (MD032) — correct.
Checklist Results
- Architecture & Design: Pass (unchanged)
- Code Quality: Pass
- Testing: Pass (unchanged)
- Security: Pass (unchanged)
- Performance: Pass (unchanged)
- PR Description & Glossary: Pass (unchanged)
- Technical Decisions (AgDR):Pass (unchanged)
Verdict
APPROVED (via comment — cannot self-approve own PR) — lint-only delta, semantics preserved, CI green.
Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: a2694e93239812938df18b25f20a1b2b419e0da0
…ion skill + migration AgDR template (me2resh#72) * feat(me2resh#59): ticket-first gate for database migrations + /migration skill + migration AgDR template Ships Phase 1 of the migration-gate feature per the issue's own rollout plan. Phase 2 (sam/terraform plan introspection) is deferred — path-pattern heuristics cover 80% of the real cases and the remaining 20% can be handled by per-project `migration_paths` overrides. What ships: 1. `.claude/hooks/require-migration-ticket.sh` — new PreToolUse hook on Edit/Write/MultiEdit. If the target path matches a migration pattern, runs three gates: G1: active ticket marker exists (same me2resh#41 two-tier resolution — per-project preferred, ops-level fallback) G2: the referenced GitHub issue is OPEN and carries the `migration` label (default, overridable via `.claude/project-config.json` → `migration_label`) G3: the issue body contains a reference to a migration AgDR matching `docs/agdr/AgDR-\d+-.*migration.*\.md` Non-migration paths exit silently; the normal require-active-ticket hook then applies. Exempts .claude/, docs/, projects/*/docs/, *.md, and *.example up front. 2. `.claude/skills/migration/SKILL.md` — guided flow that produces both artefacts in one pass. Asks for migration type, affected tables, rollback plan (required non-empty), downtime estimate, cross-service consumers, data volume, testing plan, and observability. Writes the AgDR first (reversible local write), then creates the labelled GitHub issue, then back-fills the AgDR with the issue reference. Tells the user to run /start-ticket as the next step — doesn't do it automatically so the handoff stays explicit. 3. `templates/agdr-migration.md` — AgDR template with migration- specific sections: Rollback Plan (explicit, not buried under Consequences), Cross-Service Consumers, Testing Plan, and Observability. Built on the same shape as the base AgDR template (Context / Options Considered / Decision) so existing AgDR discipline carries over. Wiring: - `.claude/settings.json`: require-migration-ticket.sh registered BEFORE require-active-ticket.sh in the PreToolUse chain so migration-specific blocking messages surface first. - `.claude/rules/workflow-gates.md`: new gate 3a row + dedicated "Migration Gate" section with default path list and enforcement note. - `.claude/hooks/README.md`: new section 1a documenting the hook's three gates. Hook #1 (require-active-ticket) description updated to include the me2resh#41 two-tier resolution. - `CLAUDE.md`: hook count 16 → 17, skill count 31 → 32, templates table gains Migration AgDR row, skills table gains /migration row. - `workflows/sdlc.md`: new "Sub-Workflow: Database Migrations" section after Phase 3 Exit Criteria, showing the flow from /migration → /start-ticket → edit → dev smoke → staging → prod → monitor. Smoke-tested the hook with 11 path-matching cases in an isolated /tmp mock ops root (/tmp/smoke-59.sh). All pass — non-migration paths pass through, every migration-pattern path blocks when no marker exists. Gates 2 and 3 require live gh calls and are covered by real-usage flow (documented in the test output). Closes me2resh#59 * fix: shellcheck SC2221/SC2222/SC2254 + markdownlint MD032 on me2resh#59 - require-migration-ticket.sh: remove subsumed case patterns (*/projects/*/docs/* under */docs/*, */migrations/*/*.sql under */migrations/*.sql) and disable SC2254 on the intentional glob-expansion in the custom-paths case. - agdr-migration.md: add blank line before a bulleted list under 'Deploy-order constraint'. All 11 smoke cases still pass. --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
…ion skill + migration AgDR template (me2resh#72) * feat(me2resh#59): ticket-first gate for database migrations + /migration skill + migration AgDR template Ships Phase 1 of the migration-gate feature per the issue's own rollout plan. Phase 2 (sam/terraform plan introspection) is deferred — path-pattern heuristics cover 80% of the real cases and the remaining 20% can be handled by per-project `migration_paths` overrides. What ships: 1. `.claude/hooks/require-migration-ticket.sh` — new PreToolUse hook on Edit/Write/MultiEdit. If the target path matches a migration pattern, runs three gates: G1: active ticket marker exists (same me2resh#41 two-tier resolution — per-project preferred, ops-level fallback) G2: the referenced GitHub issue is OPEN and carries the `migration` label (default, overridable via `.claude/project-config.json` → `migration_label`) G3: the issue body contains a reference to a migration AgDR matching `docs/agdr/AgDR-\d+-.*migration.*\.md` Non-migration paths exit silently; the normal require-active-ticket hook then applies. Exempts .claude/, docs/, projects/*/docs/, *.md, and *.example up front. 2. `.claude/skills/migration/SKILL.md` — guided flow that produces both artefacts in one pass. Asks for migration type, affected tables, rollback plan (required non-empty), downtime estimate, cross-service consumers, data volume, testing plan, and observability. Writes the AgDR first (reversible local write), then creates the labelled GitHub issue, then back-fills the AgDR with the issue reference. Tells the user to run /start-ticket as the next step — doesn't do it automatically so the handoff stays explicit. 3. `templates/agdr-migration.md` — AgDR template with migration- specific sections: Rollback Plan (explicit, not buried under Consequences), Cross-Service Consumers, Testing Plan, and Observability. Built on the same shape as the base AgDR template (Context / Options Considered / Decision) so existing AgDR discipline carries over. Wiring: - `.claude/settings.json`: require-migration-ticket.sh registered BEFORE require-active-ticket.sh in the PreToolUse chain so migration-specific blocking messages surface first. - `.claude/rules/workflow-gates.md`: new gate 3a row + dedicated "Migration Gate" section with default path list and enforcement note. - `.claude/hooks/README.md`: new section 1a documenting the hook's three gates. Hook #1 (require-active-ticket) description updated to include the me2resh#41 two-tier resolution. - `CLAUDE.md`: hook count 16 → 17, skill count 31 → 32, templates table gains Migration AgDR row, skills table gains /migration row. - `workflows/sdlc.md`: new "Sub-Workflow: Database Migrations" section after Phase 3 Exit Criteria, showing the flow from /migration → /start-ticket → edit → dev smoke → staging → prod → monitor. Smoke-tested the hook with 11 path-matching cases in an isolated /tmp mock ops root (/tmp/smoke-59.sh). All pass — non-migration paths pass through, every migration-pattern path blocks when no marker exists. Gates 2 and 3 require live gh calls and are covered by real-usage flow (documented in the test output). Closes me2resh#59 * fix: shellcheck SC2221/SC2222/SC2254 + markdownlint MD032 on me2resh#59 - require-migration-ticket.sh: remove subsumed case patterns (*/projects/*/docs/* under */docs/*, */migrations/*/*.sql under */migrations/*.sql) and disable SC2254 on the intentional glob-expansion in the custom-paths case. - agdr-migration.md: add blank line before a bulleted list under 'Deploy-order constraint'. All 11 smoke cases still pass. --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
Summary
Ships Phase 1 of the migration-gate feature: a new hook that refuses edits to migration files without a labelled migration ticket + linked AgDR, plus the
/migrationskill that produces both artefacts in one guided flow, plus the migration AgDR template.Three layers:
Hook (
.claude/hooks/require-migration-ticket.sh) — PreToolUse on Edit/Write/MultiEdit. On migration-path match, runs three gates: active ticket marker exists, referenced issue is OPEN withmigrationlabel, issue body references a migration AgDR. Runs beforerequire-active-ticket.shso migration-specific messages surface first.Skill (
.claude/skills/migration/SKILL.md) — asks for type / affected tables / rollback plan / downtime / consumers / volume / testing / observability. Writes the AgDR first (reversible local write), then creates the labelled GitHub issue, then back-fills the AgDR with the ticket URL. Tells the user to run/start-ticketas the next step — doesn't do it automatically so the handoff stays explicit.Template (
templates/agdr-migration.md) — extends the base AgDR shape with migration-specific sections: explicit Rollback Plan (not buried under Consequences), Cross-Service Consumers, Testing Plan, Observability, plus frontmatter fields for migration type / affected tables / downtime / data volume / target environments.Default migration paths (overridable per project via
.claude/project-config.json→migration_paths)**/migrate-*.{ts,js,py,sql}— one-off migration scripts**/migrations/**— any file under a migrations/ directoryprisma/schema.prisma,prisma/migrations/**— Prismasrc/migrations/*.{ts,js}— TypeORM conventionalembic/versions/*.py— Alembicdb/migrate/*.rb— Rails*.sqlunder anymigrations/subtreeExempt regardless of pattern:
.claude/,docs/,projects/*/docs/, any*.md, any*.example.Docs wiring
.claude/rules/workflow-gates.md: new gate row 3a + dedicated "Migration Gate" section.claude/hooks/README.md: new section 1aCLAUDE.md: hook count 16→17, skill count 31→32, template + skill table rowsworkflows/sdlc.md: new "Sub-Workflow: Database Migrations" block after Phase 3 showing/migration→/start-ticket→ edit → dev smoke → staging → prod → monitorTesting
11 path-matching smoke cases against an isolated
/tmpmock ops root — all pass:Gates 2 (label check) and 3 (AgDR body ref) require live
ghcalls — verified in real-usage flow:/migration→ create ticket + AgDR →/start-ticket→ retry the migration-file edit → hook allows.Deferred — issue's Phase 2
sam validate/terraform planintrospection to auto-detectAWS::DynamoDB::Table/aws_rds_cluster/aws_dynamodb_tableadditions-or-modifications. Complex, and 80% of the value comes from the path-pattern heuristic + per-projectmigration_pathsoverrides.Glossary
docs/agdr/AgDR-NNNN-migration-<slug>.mdin the project's repoRelated
require-active-ticket.sh(runs after migration hook; handles non-migration ticket-first enforcement)