LogoSkills

pr:preflight

Pre-PR gate that runs unit + widget + integration tests on changed packages and blocks PR creation on failure.

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/pr:preflight
Aliases/preflight, /pr:check
Categorykobic-development
Complexitymoderate
MCP Serverspatrol

/pr:preflight โ€” PR ์˜ฌ๋ฆฌ๊ธฐ ์ „ ์ž๋™ ์ ๊ฒ€#

ํ•œ๋งˆ๋””๋กœ#

PR(์ฝ”๋“œ ํ•ฉ์น˜๊ธฐ ์š”์ฒญ)์„ ์˜ฌ๋ฆฌ๊ธฐ ์ „์— "๋ฐ”๋€ ๋ถ€๋ถ„์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€" ์ž๋™์œผ๋กœ ๊ฒ€์‚ฌํ•ด ์ฃผ๋Š” ๋งˆ์ง€๋ง‰ ๊ด€๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ณต์žฅ์—์„œ ์ œํ’ˆ์„ ์ถœ๊ณ ํ•˜๊ธฐ ์ „ ๋งˆ์ง€๋ง‰ ํ’ˆ์งˆ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•ด์•ผ๋งŒ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„์š”. ๊ฒ€์‚ฌ์— ์‹คํŒจํ•˜๋ฉด PR ์ƒ์„ฑ์ด ๋ง‰ํž™๋‹ˆ๋‹ค.

๋ˆ„๊ฐ€ยท์–ธ์ œ ์“ฐ๋‚˜์š”#

  • ๊ฐœ๋ฐœ์ž๊ฐ€ PR์„ ์˜ฌ๋ฆฌ๊ธฐ ์ง์ „์— ์ง์ ‘ ์‹คํ–‰ํ•  ๋•Œ
  • ์ž๋™ํ™”๋œ ๊ฐœ๋ฐœ ํ๋ฆ„(/dev:run)์ด ์ง„ํ–‰ ์ค‘ ์ž๋™์œผ๋กœ ์ด ์ ๊ฒ€์„ ๋ถ€๋ฅผ ๋•Œ
  • ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ์ž‘์—…(/e2e:kick-off)์„ ๋งˆ์นœ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ
  • ์ฝ”๋“œ๋ฅผ ์›๊ฒฉ์— ์˜ฌ๋ฆฌ๋Š” ์ˆœ๊ฐ„(push) ์ž๋™์œผ๋กœ ๊ฑธ๋ฆฌ๋„๋ก ์„ค์ •ํ•ด ๋‘” ๊ฒฝ์šฐ(์„ ํƒ ์‚ฌํ•ญ)

๋ฌด์—‡์„ ํ•ด์ฃผ๋‚˜์š”#

์ด๋ฒˆ์— ๋ฐ”๋€ ์ฝ”๋“œ ๋ฌถ์Œ(ํŒจํ‚ค์ง€)๋งŒ ์ž๋™์œผ๋กœ ์ฐพ์•„๋‚ด์–ด, 3๋‹จ๊ณ„ ๊ฒ€์‚ฌ๋ฅผ ์ฐจ๋ก€๋กœ ๋Œ๋ฆฝ๋‹ˆ๋‹ค.

  • 1๋‹จ๊ณ„ โ€” ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„์˜ ๋™์ž‘ ๊ฒ€์‚ฌ(unit)
  • 2๋‹จ๊ณ„ โ€” ํ™”๋ฉด ์กฐ๊ฐ์ด ์ œ๋Œ€๋กœ ๊ทธ๋ ค์ง€๋Š”์ง€ ๊ฒ€์‚ฌ(widget)
  • 3๋‹จ๊ณ„ โ€” ์‹ค์ œ ์‚ฌ์šฉ ํ๋ฆ„์ด ๋๊นŒ์ง€ ์ด์–ด์ง€๋Š”์ง€ ๊ฒ€์‚ฌ(integration)

๊ฒ€์‚ฌ ๊ฒฐ๊ณผ๋Š” ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์š”์•ฝ ๋ณด๊ณ ์„œ๋กœ ๋‚˜์˜ค๊ณ , ํ•„์š”ํ•˜๋ฉด ํŒŒ์ผ๋กœ๋„ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค(.claude/preflight-report-{์‹œ๊ฐ}.json). ์ „์ฒด ํ†ต๊ณผ(PASS)ํ•˜๋ฉด PR์„ ์ง„ํ–‰ํ•ด๋„ ์ข‹๋‹ค๋Š” ์‹ ํ˜ธ๋ฅผ, ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด PR์„ ๋ง‰๋Š” ์‹ ํ˜ธ๋ฅผ ๋Œ๋ ค์ค๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ์“ฐ๋‚˜์š”#

# ๊ธฐ๋ณธ โ€” ๋ฐ”๋€ ํŒจํ‚ค์ง€๋ฅผ ์ž๋™์œผ๋กœ ์ฐพ์•„ unit + widget ๊ฒ€์‚ฌ + ๊ฐ€๋ฒผ์šด integration(@smoke)
/pr:preflight

# ๋น„๊ต ๊ธฐ์ค€์ด ๋˜๋Š” base ๋ธŒ๋žœ์น˜๋ฅผ ์ง์ ‘ ์ง€์ •
/pr:preflight --base development

# ํŠน์ • ํŒจํ‚ค์ง€๋งŒ ๊ณจ๋ผ์„œ ๊ฒ€์‚ฌ
/pr:preflight --scope feature_common_auth,feature_application_store

# integration(์‹ค์ œ ์‚ฌ์šฉ ํ๋ฆ„) ๊ฒ€์‚ฌ ํฌํ•จ
/pr:preflight --with-integration           # ํ•ต์‹ฌ๋งŒ ๋น ๋ฅด๊ฒŒ(@smoke, ๊ธฐ๋ณธ)
/pr:preflight --with-integration=full      # ๋ฐ”๋€ ํŒจํ‚ค์ง€์˜ ์ „์ฒด ํ๋ฆ„ ๊ฒ€์‚ฌ

# ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํฌํŠธ ํŒŒ์ผ๋กœ ์ €์žฅ
/pr:preflight --save-report

# ์‹คํŒจํ•œ ๋ถ€๋ถ„์„ ์ž์„ธํžˆ ๋ณด๊ธฐ
/pr:preflight --verbose

# ์‹ค์ œ๋กœ ๋Œ๋ฆฌ์ง€ ์•Š๊ณ   " ๋ฌด์—‡์„ ๊ฒ€์‚ฌํ• ์ง€ "   ๊ณ„ํš๋งŒ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ
/pr:preflight --dry-run

# ์˜ˆ์™ธ โ€” ๊ธด๊ธ‰ ์ˆ˜์ •(hotfix)์ผ ๋•Œ๋งŒ, ์‚ฌ์œ ๋ฅผ ์ ๊ณ  ์ „์ฒด ๊ฑด๋„ˆ๋›ฐ๊ธฐ
/pr:preflight --skip-all --reason  " ํ”„๋กœ๋•์…˜ ๊ฒฐ์ œ ํฌ๋ž˜์‹œ ์‘๊ธ‰ ํŒจ์น˜ "

์ฐธ๊ณ ๋กœ ๊ฐ€์žฅ ๊ธฐ๋ณธ ๋‹จ์œ„์ธ unit/widget ๊ฒ€์‚ฌ๋Š” ์ž„์˜๋กœ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค(--skip-unit, --skip-widget์€ ์ •์ฑ…์ƒ ๊ฑฐ๋ถ€๋˜๋ฉฐ ๊ด€๋ฆฌ์ž ์Šน์ธ์ด ํ•„์š”). ์ „์ฒด๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋Š” --skip-all์€ ๊ธด๊ธ‰ ์ƒํ™ฉ ์ „์šฉ์œผ๋กœ, ๋ฐ˜๋“œ์‹œ ์‚ฌ์œ (--reason)๋ฅผ ์ ์–ด์•ผ ํ•˜๊ณ  ๊ธฐ๋ก์ด ๋‚จ์Šต๋‹ˆ๋‹ค.

์•ˆ์—์„œ ๋ฌด์Šจ ์ผ์ด ๋ฒŒ์–ด์ง€๋‚˜์š”#

  1. ๋ฌด์—‡๊ณผ ๋น„๊ตํ• ์ง€ ์ •ํ•˜๊ธฐ โ€” ๊ธฐ์ค€์ด ๋˜๋Š” base ๋ธŒ๋žœ์น˜๋ฅผ ์ž๋™์œผ๋กœ(๋˜๋Š” ์ง€์ •ํ•œ ๋Œ€๋กœ) ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ฐ”๋€ ๋ถ€๋ถ„ ์ฐพ์•„๋‚ด๊ธฐ โ€” ๊ธฐ์ค€ ๋Œ€๋น„ ์–ด๋–ค ํŒŒ์ผ์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ๋ฝ‘์•„, ๊ทธ ํŒŒ์ผ์ด ์†ํ•œ ์ฝ”๋“œ ๋ฌถ์Œ(ํŒจํ‚ค์ง€)์„ ์•Œ์•„๋ƒ…๋‹ˆ๋‹ค. ์ž๋™ ์ƒ์„ฑ๋œ ํŒŒ์ผ์ด๋‚˜ ๋ฐ”๋€ ์ฝ”๋“œ๊ฐ€ ์ „ํ˜€ ์—†์œผ๋ฉด ์ผ์ฐ ๋งˆ์นฉ๋‹ˆ๋‹ค.
  3. 3๋‹จ๊ณ„ ๊ฒ€์‚ฌ ์‹คํ–‰ โ€” ์ž‘์€ ๋‹จ์œ„ ๊ฒ€์‚ฌ(unit) โ†’ ํ™”๋ฉด ์กฐ๊ฐ ๊ฒ€์‚ฌ(widget) โ†’ ์‹ค์ œ ํ๋ฆ„ ๊ฒ€์‚ฌ(integration)๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋Œ๋ฆฝ๋‹ˆ๋‹ค. ์ค‘๊ฐ„์— ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ๊ฑฐ๊ธฐ์„œ ๋ฉˆ์ถ”๊ณ  ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธํ•  ๊ธฐ๊ธฐ๊ฐ€ ์—†์œผ๋ฉด ํ๋ฆ„ ๊ฒ€์‚ฌ๋Š” ๊ฑด๋„ˆ๋›ฐ๊ณ  ๊ฒฝ๊ณ ๋งŒ ๋‚จ๊น๋‹ˆ๋‹ค.
  4. ๊ฒฐ๊ณผ ์ •๋ฆฌยท์‹ ํ˜ธ ๋ฐ˜ํ™˜ โ€” ๋‹จ๊ณ„๋ณ„ ํ†ต๊ณผ/์‹คํŒจ, ๋ฐ”๋€ ํŒจํ‚ค์ง€ ๋ชฉ๋ก, ๋‹ค์Œ์— ํ•  ์ผ์„ ์š”์•ฝํ•ด ๋ณด์—ฌ์ฃผ๊ณ , PR์„ ์ง„ํ–‰ํ•ด๋„ ๋˜๋Š”์ง€(ํ—ˆ์šฉ/์ฐจ๋‹จ)๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

โš™๏ธ ์ƒ์„ธ ์˜ต์…˜ยท์‹คํ–‰ ๋ช…์„ธ (๊ฐœ๋ฐœ์ž / AI ์—์ด์ „ํŠธ์šฉ)

Triggers#

  • PR ์˜ฌ๋ฆฌ๊ธฐ ์ง์ „ ์ˆ˜๋™ ์‹คํ–‰
  • /dev:run Step 8 ์ž๋™ ํ˜ธ์ถœ
  • /e2e:kick-off ์™„๋ฃŒ ํ›„ ๋‹ค์Œ ๋‹จ๊ณ„
  • lefthook pre-push ํ›…์—์„œ ํ˜ธ์ถœ (์„ ํƒ)

์‚ฌ์šฉ๋ฒ•#

# ๊ธฐ๋ณธ โ€” ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ์ž๋™ ๊ฐ์ง€, unit + widget ํ•„์ˆ˜ + @smoke integration
/pr:preflight

# base ๋ธŒ๋žœ์น˜ ๋ช…์‹œ
/pr:preflight --base development

# ํŠน์ • ํŒจํ‚ค์ง€๋งŒ
/pr:preflight --scope feature_common_auth,feature_application_store

# integration ํฌํ•จ
/pr:preflight --with-integration           # @smoke ๋งŒ (๊ธฐ๋ณธ)
/pr:preflight --with-integration=full      # ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€์˜ ์ „์ฒด integration

# ๋ฆฌํฌํŠธ ์ €์žฅ
/pr:preflight --save-report

# ์‹คํŒจ ์ƒ์„ธ (verbose)
/pr:preflight --verbose

# Dry run โ€” ์‹คํ–‰ ๊ณ„ํš๋งŒ ์ถœ๋ ฅ
/pr:preflight --dry-run

# ์˜ˆ์™ธ โ€” hotfix
/pr:preflight --skip-all --reason  " ํ”„๋กœ๋•์…˜ ๊ฒฐ์ œ ํฌ๋ž˜์‹œ ์‘๊ธ‰ ํŒจ์น˜ "

ํŒŒ๋ผ๋ฏธํ„ฐ#

ํŒŒ๋ผ๋ฏธํ„ฐํ•„์ˆ˜์„ค๋ช…๊ธฐ๋ณธ
--baseโŒbase ๋ธŒ๋žœ์น˜auto (upstream โ†’ development)
--scopeโŒํŒจํ‚ค์ง€ scope ์ˆ˜๋™ ์ง€์ • (์ฝค๋งˆ)auto (git diff ๊ฐ์ง€)
--with-integration[=smoke|full]โŒintegration ๋ ˆ์ด์–ด ์‹คํ–‰smoke (๋กœ์ปฌ)
--skip-integrationโŒintegration ๋ช…์‹œ skipdefault ํ–‰๋™๊ณผ ๋™์ผ
--skip-unit / --skip-widgetโŒ์ •์ฑ…์ƒ ๊ฑฐ๋ถ€ โ€” ๊ด€๋ฆฌ์ž ์Šน์ธ ํ•„์š”false
--skip-allโŒ์ „๋Ÿ‰ skip (hotfix ์ „์šฉ)false
--reasonโš ๏ธ--skip-all ์‹œ ํ•„์ˆ˜ ์‚ฌ์œ โ€”
--save-reportโŒ๋ฆฌํฌํŠธ JSON ์ €์žฅfalse
--verboseโŒ์‹คํŒจ ์ƒ์„ธ ๋คํ”„false
--dry-runโŒ๊ณ„ํš๋งŒ ์ถœ๋ ฅfalse
--from-hookโŒlefthook ํ˜ธ์ถœ ํ‘œ์‹œ (๋ฆฌํฌํŠธ ๊ฐ„๊ฒฐ)false

์‹คํ–‰ ๋‹จ๊ณ„#

  1. Base ๋ธŒ๋žœ์น˜ ๊ฒฐ์ • โ€” ์šฐ์„ ์ˆœ์œ„: --base โ†’ upstream โ†’ development โ†’ main
  2. ๋ณ€๊ฒฝ ํŒŒ์ผ ์ถ”์ถœ โ€” git diff --name-only "$BASE"..HEAD
  3. ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ๋งคํ•‘
    • ํŒŒ์ผ ๊ฒฝ๋กœ โ†’ ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉฐ pubspec.yaml ์„ ๋งŒ๋‚œ ์ฒซ ๋””๋ ‰ํ† ๋ฆฌ
    • ์ž๋™ ์ƒ์„ฑ ํŒŒ์ผ(.g.dart, .freezed.dart, .widget_test.dart, .patrol_test.dart)์€ ๋ถ€์ˆ˜ ๋ณ€๊ฒฝ์œผ๋กœ ๋ถ„๋ฅ˜
    • ๋ณ€๊ฒฝ ์—†์Œ โ†’ ์กฐ๊ธฐ ์ข…๋ฃŒ (PR ์— ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์Œ)
  4. ๋ ˆ์ด์–ด 1: Unit ์‹คํ–‰ โ€” melos exec --scope=... -- flutter test --exclude-tags=integration
    • FAIL โ†’ ์ค‘๋‹จ, ๋ฆฌํฌํŠธ, exitโ‰ 0
  5. ๋ ˆ์ด์–ด 2: Widget ์‹คํ–‰ โ€” ์œ„ ๋ช…๋ น์— BDD .widget_test.dart ํฌํ•จ๋จ
    • FAIL โ†’ ์ค‘๋‹จ, ๋ฆฌํฌํŠธ, exitโ‰ 0
  6. ๋ ˆ์ด์–ด 3: Integration ์‹คํ–‰ (์กฐ๊ฑด๋ถ€)
    • --with-integration=smoke (๊ธฐ๋ณธ) โ†’ @smoke ํƒœ๊ทธ Patrol ์‹œ๋‚˜๋ฆฌ์˜ค
    • --with-integration=full โ†’ ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  integration
    • ๋””๋ฐ”์ด์Šค ์—†์Œ โ†’ skip + ๊ฒฝ๊ณ 
    • FAIL โ†’ ์ค‘๋‹จ, ๋ฆฌํฌํŠธ, exitโ‰ 0
  7. ์ง‘๊ณ„ ๋ฆฌํฌํŠธ โ€” ๋ ˆ์ด์–ด๋ณ„ PASS/FAIL, ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ๋ชฉ๋ก, ๋‹ค์Œ ๋‹จ๊ณ„ ์•ˆ๋‚ด
  8. PR ์ง„ํ–‰ ํ—ˆ์šฉ/์ฐจ๋‹จ ํ”Œ๋ž˜๊ทธ ๋ฐ˜ํ™˜

์ถœ๋ ฅ#

  • stdout ๋ฆฌํฌํŠธ (์‚ฌ๋žŒ ์ฝ๊ธฐ์šฉ)
  • (์„ ํƒ) .claude/preflight-report-{timestamp}.json
  • (skip ์‹œ) .claude/preflight-skip.log
  • exit code (0 PASS, 1 FAIL, 2 POLICY VIOLATION)

Skip ์ •์ฑ…#

--skip-unit, --skip-widget ์€ ํ•ญ์ƒ ์ •์ฑ… ์œ„๋ฐ˜ ์œผ๋กœ reject. --skip-all ์€ --reason ํ•„์ˆ˜ + ๊ฐ์‚ฌ ๋กœ๊ทธ ๊ธฐ๋ก + CI ๊ฐ€ ๊ฐ•ํ•œ ๊ฒŒ์ดํŠธ๋กœ ๋Œ€์ฒด ์‹คํ–‰.

์ •์ฑ… ์ƒ์„ธ: rules/pr-preflight-policy.md

์‹คํŒจ ์ผ€์ด์Šค#

์›์ธ๋Œ€์‘
Base ๋ธŒ๋žœ์น˜ ์ถ”์ • ์‹คํŒจ--base ๋ช…์‹œ ์•ˆ๋‚ด ํ›„ ์ค‘๋‹จ
melos ์—†์Œflutter test + ๋””๋ ‰ํ† ๋ฆฌ ์Šค์บ” ํด๋ฐฑ
bdd_test_gen ์ƒ์„ฑ๋ฌผ ๋ˆ„๋ฝdart run build_runner build ์ž๋™ ์‹คํ–‰ ํ›„ ์žฌ์‹œ๋„
๋””๋ฐ”์ด์Šค ์—†์Œ (integration ์š”์ฒญ ์‹œ)skip + ๊ฒฝ๊ณ  / ๋˜๋Š” --skip-integration ๊ถŒ์œ 
์ž๋™ ์ƒ์„ฑ ํŒŒ์ผ๋งŒ ๋ณ€๊ฒฝํŒจํ‚ค์ง€ scope ์—์„œ ์ œ์™ธ, ์•ˆ๋‚ด
--skip-all --reason ๋ฏธ์ œ๊ณต์ •์ฑ… ์œ„๋ฐ˜ exit

๊ด€๋ จ#

  • ์Šคํ‚ฌ ๋ณธ์ฒด: skills/pr/SKILL.md
  • ์ •์ฑ…: rules/pr-preflight-policy.md
  • ์—ฐ๊ณ„ ์ปค๋งจ๋“œ: /dev:run (Step 8 ์ž๋™ ํ˜ธ์ถœ), /e2e:kick-off
  • ์กฐ์ง ํ…Œ์ŠคํŠธ ํ‘œ์ค€: cc-uiux/skills/unit-testing, widget-testing, bdd-testing, integration-testing