Discovery Audit & Fix-Fanout#
ํ๋ง๋๋ก (๋น๊ฐ๋ฐ์์ฉ)#
ํ ๊ณณ์์ ๋ฒ๊ทธ๋ฅผ ํ๋ ๊ณ ์ณค๋๋ฐ, "ํน์ ๊ฐ์ ์ค์๊ฐ ์ฝ๋ ์ ์ฒด์ ๋ ์์ง ์์๊น?"๊ฐ ๊ถ๊ธํ ๋ ์ฐ๋ ์ ์ฐจ์ ๋๋ค. ๊ฑด๋ฌผ ํ ๊ณณ์์ ํ์ฌ ์์ธ์ ํ๋ ์ฐพ์๋ค๊ณ ๋ฐ๋ก ์ ์ฒด๋ฅผ ๋ค ๊ณ ์น๋ ๊ฒ ์๋๋ผ, ๋จผ์ ๋น์ทํ ๊ณณ์ ๋๊ฒ ํ์ด "์์ฌ๋๋ ๊ณณ ๋ชฉ๋ก"์ ๋ง๋ค๊ณ (Triage), ๊ทธ ๋ชฉ๋ก์ ๋ค๋ฅธ ์ ๊ฒ๋ฐ์ด ์ฒ์๋ถํฐ ๋ค์ ํ์ธํด "์ง์ง ์ํํ ๊ณณ"๋ง ์ถ๋ ค๋ธ ๋ค(Verify), ํ์ ๋ ๊ณณ๋ค๋ง ์ฌ๋ฌ ํ์ด ๋์์ ๋๋ ๊ณ ์น๋(Fix-Fanout) 3๋จ๊ณ ์ ์ฐจ์ ๋๋ค. ๋๊ฒ ์ฐพ๋ ์ฌ๋๊ณผ ํ์ ํ๋ ์ฌ๋์ ๋ถ๋ฆฌํด์ผ ์คํ(๊ฐ์ง ๊ฒฝ๋ณด)์ด ๊ทธ๋๋ก ์์ ์ง์๋ก ์ด์ด์ง๋ ์ฌ๊ณ ๋ฅผ ๋ง์ ์ ์์ต๋๋ค.
๋ฌด์์ยท์ธ์ (๋น๊ฐ๋ฐ์์ฉ)#
-
๋ฌด์์ ํด์ฃผ๋์
- 1๋จ๊ณ(Triage): "์ด๋ฐ ํจํด์ ๋ฒ๊ทธ"๋ผ๋ ์ค๋ช ์ ์ฌ๋ฌ ์กฐ์์๊ฒ ๋๋ ์ฃผ๊ณ , ์ฝ๋ ์ ์ฒด์์ ์์ฌ ํ๋ณด ๋ชฉ๋ก์ ๋น ๋ฅด๊ฒ ๋ฝ์ต๋๋ค.
- 2๋จ๊ณ(Verify): ํ๋ณด ํ๋ํ๋๋ฅผ Triage์ ๋ค๋ฅธ ์กฐ์๊ฐ ์๋ณธ ์ฝ๋๋ฅผ ์ฒ์๋ถํฐ ๋ค์ ์ฝ๊ณ "์ง์ง ๋ฌธ์ ์ธ์ง" ์ฌํ์ธํฉ๋๋ค. ์ด๋ฏธ ์ ์ฐ๋ ์ฃฝ์ ์ฝ๋์ธ์ง, ์ ๋ง ๋ฉ์ถ๋์ง ์๋๋ฉด ๊ทธ๋ฅ ์กฐ๊ธ ๋ฆ๊ฒ ๊ฐฑ์ ๋๋ ์ ๋์ธ์ง๊น์ง ๋ฐ์ง๋๋ค.
- 3๋จ๊ณ(Fix-Fanout): ํ์ ๋ ๋ชฉ๋ก๋ง ๊ฐ๊ฐ ๋ ๋ฆฝ๋ ์์ ๊ณต๊ฐ(git worktree)์์ ๋์์ ๊ณ ์น๊ณ , ๊ฒ์ฌโ์ปค๋ฐโํฉ์น๊ธฐ ์์ฒญ(PR)๊น์ง ๊ฐ์ ๋๋ ๋๋ค.
-
์ธ์ ์ฐ๋์
- "์ด ๋ฒ๊ทธ, ๋ค๋ฅธ ๋ฐ๋ ์์ ๊ฒ ๊ฐ์๋ฐ" ์ถ์ ๋ โ ์ฆ ํ ๋ฒ ์ฐพ์ ๊ฒฐํจ ํจํด์ด ์ฝ๋๋ฒ ์ด์ค ๋ค๋ฅธ ๊ณณ์๋ ๋ฐ๋ณต๋์ ๊ฐ๋ฅ์ฑ์ด ์์ ๋.
- ํ์ ๋ ๋ฌธ์ ๊ฐ ์ฌ๋ฌ ๊ฑด์ด๋ผ ํ ๋ช ์ด ์์๋๋ก ๊ณ ์น๋ฉด ์ค๋ ๊ฑธ๋ฆด ๋.
- ์ฃผ์ํ ์ : ๋๊ฒ ์ฐพ๋ ์ฌ๋์ด ์ค์ค๋ก "์ด๊ฑด ์ง์ง ๋ฌธ์ ๋ค"๊น์ง ํ์ ํด๋ฒ๋ฆฌ๋ฉด ์ ๋ฉ๋๋ค. ๋ฐ๋์ ๋ณ๋์ ํ์ธ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ์ผ ๊ฐ์ง ๊ฒฝ๋ณด๊ฐ ์ค์ ์์ ์ผ๋ก ์ด์ด์ง๋ ๊ฒ์ ๋ง์ต๋๋ค.
ํต์ฌ ์ฉ์ด (๋น๊ฐ๋ฐ์์ฉ)#
| ์ฉ์ด | ์ฌ์ด ์ค๋ช |
|---|---|
| Triage(ํธ๋ฆฌ์์ง) | ๋๊ณ ๋น ๋ฅด๊ฒ ํ์ด "์์ฌ ํ๋ณด"๋ง ๊ณจ๋ผ๋ด๋ 1์ฐจ ์ ๋ณ ๋จ๊ณ |
| Verify(๊ฒ์ฆ) | ํ๋ณด๋ฅผ ๋ณ๋ ์ธ๋ ฅ์ด ์ฒ์๋ถํฐ ๋ค์ ํ์ธํด ์ง์ง/๊ฐ์ง๋ฅผ ๊ฐ๋ฆฌ๋ 2์ฐจ ํ์ธ ๋จ๊ณ |
| ์ ๋์ ๊ฒ์ฆ | "์ด๊ฒ ์ง์ง ๋ง๋?"๋ฅผ ์์ฌํ๋ ํ๋๋ก ์ฌํ์ธํ๋ ๊ฒ โ ์ฐพ์ ์ฌ๋์ด ์ค์ค๋ก ํ์ ํ์ง ์๊ฒ ํ๋ ์์ ์ฅ์น |
| Fix-Fanout | ํ์ ๋ ์ฌ๋ฌ ๊ฑด์ ๋์์ ๋๋ ๊ณ ์น๋ ๋ณ๋ ฌ ์์ ๋จ๊ณ |
| git worktree | ๊ฐ์ ํ๋ก์ ํธ๋ฅผ ์์ ํ ๋ ๋ฆฝ๋ ์์ ๊ณต๊ฐ์ผ๋ก ๋ถ๋ฆฌํด ์๋ก ๊ฐ์ญ ์์ด ๋ณ๋ ฌ ์์ ํ๊ฒ ํ๋ ๋ฐฉ๋ฒ |
| ์ฃฝ์ ์ฝ๋(dead code) | ์ด์ ์๋ฌด๋ ํธ์ถํ์ง ์๋, ๊ณ ์ณ๋ ์๋ฏธ ์๋ ์ฝ๋ |
| Self-healing | ์ ๊น ๋ฌธ์ ๊ฐ ์๊ฒจ๋ ๋ค์ ๋์(์: ๋ค์ ํ์ด์ง ์ด๋)์์ ์ ์ ๋ก ๋ณต๊ตฌ๋๋ ๊ฒฝ๋ก |
| Rate limit | ํ๊บผ๋ฒ์ ๋๋ฌด ๋ง์ ์์ฒญ์ ๋ณด๋ด๋ฉด ์๋ฒ๊ฐ "์ ์ ์ฒ์ฒํ ๋ณด๋ด๋ผ"๊ณ ์ ํํ๋ ๊ฒ |
์ธ์ ์ด ํจํด์ ์ฐ๋๊ฐ#
๋ฒ๊ทธ๋ฅผ ํ๋ ๊ณ ์น๊ณ ๋์ "์ด ํจํด์ด ์ฝ๋๋ฒ ์ด์ค ๋ค๋ฅธ ๊ณณ์๋ ์์ ์ ์๋ค"๋ ํ์ ์ด ๋ค ๋. ์๋ฅผ ๋ค์ด:
-
ํน์ ์ธ์ด/ํ๋ ์์ํฌ ํจ์ (์: [cc-flutter-dev:swr-pattern]์
unawaited() + Error์นจ๋ฌต ์คํจ ํจ์ )์ ํ๋ ๋ฐ๊ฒฌํ๊ณ , grep์ผ๋ก ํ๋ณด๋ฅผ ๋๊ฒ ์ฐพ์ ์ ์๋ ๊ฒฝ์ฐ - ๊ฐ์ ์ํฐํจํด(์: ํน์ API์ ์๋ชป๋ ์ฌ์ฉ๋ฒ)์ด ์ฌ๋ฌ ๋ชจ๋์ ๋ฐ๋ณต๋์ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ฒฝ์ฐ
์ด ํจํด์ ํ ์์ด์ ํธ๊ฐ "์ฐพ๊ณ โ ํ์ ํ๊ณ โ ๊ณ ์น๋" ๊ฒ์ ์ ๋ถ ํผ์ ํ์ง ์๋๋ค๋ ์์น ์์ ์ ์์ต๋๋ค. ์ฐพ๊ธฐ(๋๊ฒ, ๋น ๋ฅด๊ฒ)์ ํ์ (์ข๊ฒ, ๋๋ฆฌ๊ฒ, ์ ๋์ )์ ๋ถ๋ฆฌํด์ผ, ํ๋ณด ๋จ๊ณ์ ์คํ์ด ๊ทธ๋๋ก ์์ PR๋ก ์ด์ด์ง๋ ์ฌ๊ณ ๋ฅผ ๋ง์ ์ ์์ต๋๋ค.
3๋จ๊ณ ๊ตฌ์กฐ#
Phase 1: Triage (๋๊ฒ, ๋น ๋ฅด๊ฒ, ํ๋ณด ์์ฃผ)
โ gray-list ์์ฑ โ ์ฒญํฌ ๋ถํ โ ๋ณ๋ ฌ ์์ด์ ํธ์ ๋ฐฐ๋ถ
Phase 2: Verify (์ข๊ฒ, ๋๋ฆฌ๊ฒ, ์ ๋์ /๋
๋ฆฝ์ )
โ ํ๋ณด๋ง๋ค ๋ณ๋ ์์ด์ ํธ๊ฐ ์๋ณธ ์์ค๋ฅผ ์ฒ์๋ถํฐ ์ฌํ์ธ
โ dead code / self-healing / ์ค์ง๋จ ์ฌ๋ถ๊น์ง ์ฌํ๊ฐ
Phase 3: Fix-Fanout (worktree ๊ฒฉ๋ฆฌ ๋ณ๋ ฌ ์์ )
โ ํ์ ๋ ํญ๋ชฉ๋ง ๊ฐ์ ๋
๋ฆฝ worktree์์ ๋ธ๋์นโ์์ โ๊ฒ์ฆโPR
ํ ์ธ์ ์์ Phase 1โ2โ3์ ์์๋๋ก(Verify๋ฅผ ํต๊ณผํ ๊ฒ๋ง Phase 3๋ก) ์งํํ๋ ๊ฒ์ด ์์น์ด๋ฉฐ, Phase 2๊ฐ Phase 1์ ๊ฒฐ๊ณผ(ํนํ severity)๋ฅผ ๊ทธ๋๋ก ๋ฏฟ์ง ์๊ณ ์ฌํ๊ฐํ๋ค๋ ์ ์ด ํต์ฌ์ ๋๋ค.
Phase 1 โ Triage (๋๊ฒ, ๋น ๋ฅด๊ฒ, ํ๋ณด ์์ฃผ)#
๋ชฉํ#
์ ํ๋๋ณด๋ค **์ฌํ์จ(recall)**์ ์ฐ์ ํฉ๋๋ค. ์ด ๋จ๊ณ์ ๋ชฉ์ ์ ํ์ ์ด ์๋๋ผ "๋ค์ ๋จ๊ณ์์ ๊ฒํ ํ ๊ฐ์น๊ฐ ์๋ ํ๋ณด ๋ชฉ๋ก"์ ๋ง๋๋ ๊ฒ์ ๋๋ค.
์ ์ฐจ#
-
grep ๋ฑ์ผ๋ก ๊ฒฐํจ ํจํด์ด ์์ ๋งํ ํ์ผ์ gray-list(์ ์ ๋ชฉ๋ก)๋ฅผ ๋ง๋ญ๋๋ค.
- ์:
grep -rl "unawaited(" --include="*.dart" feature/ package/ app/ | grep -v _test.dart
- ์:
- gray-list๋ฅผ ํฐ ์ฒญํฌ(์: ํ์ผ 18~40๊ฐ์ฉ)๋ก ๋๋ ๋ณ๋ ฌ ์์ด์ ํธ์ ๋ฐฐ๋ถํฉ๋๋ค.
-
๊ฐ ์์ด์ ํธ ํ๋กฌํํธ์๋ ์ ํํ ์ด๋ค ํจํด์ ์ฐพ๋์ง์ ์ค์ฝํ ์ถ์ ๋ฌธ๊ตฌ๋ฅผ ๋ฐ๋์ ํฌํจํฉ๋๋ค.
- "
unawaited()ํธ์ถ ์์ฒด๊ฐ ๋์๋ค๋ ๊ฒ ์๋๋ค โ ๊ทธ ์์ ํธ์ถ ์ฒด์ธ์ดError๋ฅผ ๋์น๋ catch๋ง ์๋ ๊ฒฝ์ฐ๋ง ํ๋ณด๋ก ํ์ํ๋ผ"์ฒ๋ผ, ๋ฌด์์ด ์๋์ง๋ ๋ช ์ํด์ผ ์คํ์ด ์ค์ด๋ญ๋๋ค.
- "
Workflow ์คํฌ๋ฆฝํธ ์์ (Triage)#
export const meta = {
name: ' unawaited-error-triage ' ,
description: ' unawaited() ํธ์ถ ์ฒด์ธ์ Error ๋๋ฝ ํ๋ณด ๊ฐ์ฌ ' ,
phases: [{ title: ' Triage ' }],
}
// gray-list๋ ์คํฌ๋ฆฝํธ ๋ณธ๋ฌธ์ ๋ฆฌํฐ๋ด๋ก ์ง์ ๋ฐ์๋ฃ๋๋ค (์๋ " ์ด์์ ์ฃผ์์ " ์ฐธ์กฐ)
const GRAY_LIST = [/* 274๊ฐ ํ์ผ ๊ฒฝ๋ก */]
const CHUNK_SIZE = 30 // rate limit ์ ๋๋ ค์ ๋์์ฑ์ ์ค์ธ๋ค
const chunks = []
for (let i = 0; i < GRAY_LIST.length; i += CHUNK_SIZE) {
chunks.push(GRAY_LIST.slice(i, i + CHUNK_SIZE))
}
const CANDIDATES_SCHEMA = { /* { file, function, line, callChain, reason } ๋ฐฐ์ด */ }
const results = await parallel(chunks.map(chunk = > () = >
agent(
`๋ค์ ํ์ผ๋ค์์ unawaited(fn()) ํจํด์ ์ฐพ์๋ผ: ${chunk.join( ' , ' )}\n` +
`์ฐพ์ ๊ฒ: fn()์ ์ ์ฒด ํธ์ถ ์ฒด์ธ์์ ๋ชจ๋ catch ์ ์ด on Exception๋ง ์ก๊ณ Error(TypeError, ` +
`StateError, NoSuchMethodError, JsonUnsupportedObjectError ๋ฑ)๋ฅผ ๋์น๋ ๊ฒฝ์ฐ.\n` +
`์ฃผ์: unawaited() ์์ฒด๊ฐ ๋์ ๊ฒ ์๋๋ค โ ์ ์กฐ๊ฑด์ ํด๋นํ๋ ๊ฒ๋ง ํ๋ณด๋ก ๋ณด๊ณ ํ๋ผ.`,
{ phase: ' Triage ' , schema: CANDIDATES_SCHEMA }
)
))
const candidates = results.filter(Boolean).flatMap(r = > r.candidates)
Phase 2 โ Verify (์ข๊ฒ, ๋๋ฆฌ๊ฒ, ์ ๋์ /๋ ๋ฆฝ์ )#
์์น: ์ฐพ์ ์์ด์ ํธ๊ฐ ์ค์ค๋ก ํ์ ํ์ง ์๋๋ค#
Triage์์ ๋์จ ๊ฐ ํ๋ณด๋ Triage์ ๋ณ๋์, ๋ ๋ฆฝ์ ์ธ ์์ด์ ํธ๊ฐ ์ค์ ์์ค์ฝ๋๋ฅผ ์ฒ์๋ถํฐ ๋ค์ ์ฝ๊ณ ์ฌํ์ธํฉ๋๋ค. ๊ฐ์ ์์ด์ ํธ๊ฐ ์ฐพ๊ณ ์ค์ค๋ก ํ์ ๊น์ง ํ๋ฉด, ์คํ(false positive)์ด ๊ทธ๋๋ก ํ์ ๋ ๊ฒฐํจ์ผ๋ก ๊ตณ์ด์ง๋๋ค.
๋ฐ๋์ ํ์ธํด์ผ ํ๋ 3๊ฐ์ง#
| ํ์ธ ํญ๋ชฉ | ๋ฐฉ๋ฒ | ์ค์ธก ์ |
|---|---|---|
| ์ฃฝ์ ์ฝ๋/๋๋ฌ ๋ถ๊ฐ๋ฅํ ๊ฒฝ๋ก์ธ๊ฐ | grep์ผ๋ก ์ค์ฌ์ฉ ํธ์ถ๋ถ ํ์ธ | "high" ๋ฑ๊ธ ํ๋ณด 2๊ฑด์ด @Deprecated/๊ณ ์ ์ฝ๋๋ก ๊ธฐ๊ฐ |
| ์๋น์๊ฐ ์ ๋ง "์์ํ ๋ฉ์ถ๋"๊ฐ, ์๋๋ฉด ๊ฐ๋ง staleํด์ง๋๊ฐ | ์ปจ์๋จธ(BLoC/Cubit/ValueNotifier/StreamController)์ ์ค์ ๋๊ธฐ ๋ฐฉ์ ์ถ์ | BehaviorSubject.seeded()๋ ๋ฉ์ถ์ง ์๊ณ ๊ฐฑ์ ๋ง ๋ฆ์ด์ง โ ๊ธฐ๊ฐ |
| self-healing ๊ฒฝ๋ก๊ฐ ์๋๊ฐ | ๋ค์ ์ด๋ฒคํธ/๋ค๋น๊ฒ์ด์ ์์ ์ฌ์๋๋๋์ง ํ์ธ | ๋ค์ ํ์ด์ง ์ด๋ ์ ์ฌ์๋๋๋ ๊ฒฝ๋ก๋ severity ํํฅ ๊ทผ๊ฑฐ |
Triage์ ์ฃผ์ฅ(ํนํ severity)์ ๊ทธ๋๋ก ๋ฏฟ์ง ๋ง๊ณ , ์ 3๊ฐ์ง๋ฅผ ๋ฐ๋์ ์ฌํ๊ฐํ ๋ค confirmed/rejected๋ฅผ ํ์ ํฉ๋๋ค.
Workflow ์คํฌ๋ฆฝํธ ์์ (Verify)#
const VERDICT_SCHEMA = { /* { confirmed: bool, severity, reason, isDead, isSelfHealing } */ }
const verified = await parallel(candidates.map(c = > () = >
agent(
`๋ค์ ํ๋ณด๋ฅผ ์ฒ์๋ถํฐ ์ฌํ์ธํ๋ผ(Triage ๊ฒฐ๊ณผ๋ฅผ ์ ๋ขฐํ์ง ๋ง ๊ฒ): ` +
`${c.file}:${c.line} (${c.function})\n` +
`1) grep์ผ๋ก ์ค์ฌ์ฉ ํธ์ถ๋ถ๊ฐ ์๋์ง ํ์ธ โ ์์ผ๋ฉด dead code๋ก ๊ธฐ๊ฐ\n` +
`2) ์ปจ์๋จธ๊ฐ ์ ๋ง ์์ํ ๋ฉ์ถ๋์ง, ์๋๋ฉด ๊ฐ๋ง staleํด์ง๋์ง ํ์ธ\n` +
`3) ๋ค์ ์ด๋ฒคํธ/๋ค๋น๊ฒ์ด์
์์ ์ฌ์๋๋๋ self-healing ๊ฒฝ๋ก๊ฐ ์๋์ง ํ์ธ\n` +
`Triage severity๋ฅผ ๊ทธ๋๋ก ์ฐ์ง ๋ง๊ณ ์ฌํ๊ฐํ๋ผ.`,
{ phase: ' Verify ' , schema: VERDICT_SCHEMA }
).then(v = > ({ ...c, verdict: v }))
))
const confirmed = verified.filter(Boolean).filter(v = > v.verdict.confirmed)
Phase 3 โ Fix-Fanout (git worktree ๊ฒฉ๋ฆฌ ๋ณ๋ ฌ ์์ )#
์ธ์ worktree ๊ฒฉ๋ฆฌ๊ฐ ํ์ํ๊ฐ#
ํ์ ๋ ํญ๋ชฉ์ด ์ฌ๋ฌ ๊ฑด์ด๊ณ , ์๋ก ๋ค๋ฅธ ํ์ผ/๋ชจ๋์ ๊ฐ๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ๊ณ ์ณ์ผ ํ ๋. agent()์ isolation: 'worktree'
์ต์
์ ์ฐ๋ฉด ํญ๋ชฉ๋น ๋ณ๋ git worktree๊ฐ ์๋ ์์ฑ๋์ด, N๊ฐ ์์ด์ ํธ๊ฐ ๋์์ ๋ธ๋์น ์์ฑ โ ์ฝ๋ ์์ โ ๊ฒ์ฆ โ ์ปค๋ฐ โ ํธ์ โ PR ์์ฑ๊น์ง ๊ฐ์ ๋
๋ฆฝ์ ์ผ๋ก ์ํํด๋ ๊ฐ์ working tree๋ฅผ ๊ณต์ ํ์ง ์์ผ๋ฏ๋ก ์๋ก ์ถฉ๋ํ์ง ์์ต๋๋ค.
Serverpod 4 ํ์คํ์ worktree๋ง๋ค ๋์ฐ๋ ํฌํธยทMCP ๊ฒฉ๋ฆฌ๋ [cc-serverpod:serverpod-worktree-parallel] ์ฐธ์กฐ. Agent Teams(ํ์ผ ์์ ๊ถ ๋ถ๋ฆฌ๋ก ๋ณ๋ ฌํ)์์ ์ฐจ์ด๋ [cc-dev-cycle:agent-teams]์ "ํ์ผ์์คํ ๊ฒฉ๋ฆฌ๊ฐ ํ์ํ๋ฉด git worktree์ ์ํธ๋ณด์" ์ฐธ์กฐ.
๊ฐ ์์ด์ ํธ ํ๋กฌํํธ์ ๋ฐ๋์ ํฌํจํ ๊ฒ#
ํ๋กฌํํธ์ ์๋๋ฅผ ๋ช ์์ ์ผ๋ก ๋ฃ์์๋ก ์ ํ๋๊ฐ ๋์์ต๋๋ค:
- ์ ํํ ๊ฒฐํจ ์ค๋ช โ ํ์ผ/ํจ์/๋ผ์ธ/ํธ์ถ ์ฒด์ธ. Verify ๋จ๊ณ์์ ์ด๋ฏธ ํ๋ณดํ ์ ๋ณด๋ฅผ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ๊ณ , ์์ด์ ํธ๊ฐ ์ฒ์๋ถํฐ ์ฌ์กฐ์ฌํ์ง ์๊ฒ ํ๋ค.
- ์ ํํ ์์ ๋ฐฉํฅ โ ๊ฐ๋ฅํ๋ฉด "์ด ํ ์ค์ ์ด๋ ๊ฒ ๋ฐ๊ฟ๋ผ" ์์ค๊น์ง ๊ตฌ์ฒด์ ์ผ๋ก.
-
git/PR ํ์ค ์์
์์ โ ๋ธ๋์น๋ช
๊ท์น, ์ปค๋ฐ ๋ฉ์์ง ์ปจ๋ฒค์
,
dart analyze --fatal-infos/dcm analyzeํต๊ณผ ํ์, ๊ธฐ์กด ํ ์คํธ ํ์ผ์ด ์์ผ๋ฉด ํ๊ท ํ ์คํธ 1๊ฐ ์ถ๊ฐ,gh pr create, ๋จธ์ง๋ ํ์ง ๋ง ๊ฒ. -
pre-push hook(lefthook ๋ฑ) ์คํจ ์ ์ฐํ ๊ธ์ง โ ์คํจํ๋ฉด ์์ธ์ ์์ ํ๋ผ๊ณ ๋ช
์ (
LEFTHOOK=0๊ธ์ง๋ [cc-dev-cycle:dev] ์คํฌ์ ๊ณตํต ๊ท์น๊ณผ ๋์ผ).
Workflow ์คํฌ๋ฆฝํธ ์์ (Fix-Fanout)#
phase( ' Fix ' )
const fixResults = await parallel(confirmed.map(c = > () = >
agent(
`๋ค์ ํ์ ๋ ๊ฒฐํจ์ ์์ ํ๋ผ: ${c.file}:${c.line} (${c.function})\n` +
`๊ฒฐํจ: ${c.verdict.reason}\n` +
`์์ ๋ฐฉํฅ: unawaited(fn()) ๋์ fn()์ catch ์ ์ on Object catch (๋๋ ` +
`on Exception catch + on Error catch ๋ณํ)๋ก ๋ํ Error๋ ๋ก๊น
/์ฒ๋ฆฌํ๋ผ.\n` +
`์์
์์: fix/{issue}-{slug} ๋ธ๋์น ์์ฑ โ ์์ โ ํ๊ท ํ
์คํธ 1๊ฐ ์ถ๊ฐ โ ` +
`dart analyze --fatal-infos & & dcm analyze ํต๊ณผ โ ์ปค๋ฐ โ ํธ์ โ gh pr create.\n` +
`๋จธ์ง๋ ํ์ง ๋ง๋ผ. pre-push hook ์คํจ ์ ์ฐํํ์ง ๋ง๊ณ ์์ธ์ ๊ณ ์ณ๋ผ.`,
{ isolation: ' worktree ' , label: `fix:${c.file}` }
)
))
์ด์์ ์ฃผ์์ (์ค์ธก)#
-
argsํ๋ผ๋ฏธํฐ๋ก ํฐ ๋ฐฐ์ด(270+ ํญ๋ชฉ)์ ๋๊ธฐ๋ฉด ํ์ฑ์ด ์คํจํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค โ ๋ฐ์ดํฐ๋ฅผ ์คํฌ๋ฆฝํธ ๋ณธ๋ฌธ์ JS ๋ฆฌํฐ๋ด๋ก ์ง์ ๋ฐ์๋ฃ๋ ๋ฐฉ์์ผ๋ก ์ฐํํ๋ค (์ Triage ์์์GRAY_LIST์ฐธ์กฐ). -
"Server is temporarily limiting requests" rate limit โ ์ฒญํฌ ํฌ๊ธฐ๋ฅผ ํค์ ๋์ ์์ด์ ํธ ์ ์์ฒด๋ฅผ ์ค์ฌ์
์ฌ์คํํ๋ฉด ํด๊ฒฐ๋๋ค (์: 16๊ฐ ๋์ โ 5๊ฐ ๋์). ๋์์ฑ์ ๋ฎ์ถ๋ ค๋ฉด
CHUNK_SIZE๋ฅผ ๋๋ ค ์ฒญํฌ(=์์ด์ ํธ) ๊ฐ์๋ฅผ ์ค์ธ๋ค. -
Verify phase๋ ๋์ผํ๊ฒ rate limit์ ๊ฑธ๋ฆด ์ ์๋ค (ํ๋ณด ์๋งํผ ๋์ ์คํ๋๋ฏ๋ก) โ ์ ์ฒด๋ฅผ ์ฌ์คํํ์ง ๋ง๊ณ ์คํจํ ๊ฒ๋ง ๊ณจ๋ผ ๋ณ๋๋ก ์ฌ์คํํ๋ค.
Workflow์resumeFromRunId๋ฅผ ์ฐ๋ฉด ์ด๋ฏธ ์ฑ๊ณตํ ํธ์ถ์ ์บ์์์ ์ฆ์ ๋ฐํ๋๊ณ ์คํจ/์ ๊ท ํธ์ถ๋ง ์ฌ์คํ๋๋ค.
โ ๏ธ Fix-Fanout ํ ๋จธ์ง ์ถฉ๋ ํจ์ โ ์๋ณธ fix PR์ ๋จผ์ ๋จธ์งํ๋ผ#
๊ฐ์ ๊ทผ๋ณธ ์์ธ์ ์ฌ๋ฌ ์ธ์คํด์ค๋ฅผ ๊ฐ์ฌ๋ก ์ฐพ์ ํ์ fix๋ค์ ๋ณ๋ ฌ ๋ธ๋์น๋ก ๋ธ ๋, ์๋ณธ ๋ฌธ์ ์ ์ต์ด ์์ PR์ด ์์ง ๋จธ์ง๋์ง ์์ ์ํ์์ ํ์ fix ๋ธ๋์น๋ค์ ๋ง๋ค๋ฉด, ์๋ณธ PR์ด ๋จผ์ ๋จธ์ง๋ ๋ค ํ์ ๋ธ๋์น๋ค์ด ๊ฐ์ ํ์ผ์ ๋ค๋ฅธ ๋ถ๋ถ์ ๊ฑด๋๋ฆฌ๊ณ ์์ด ๋จธ์ง ์ ์ถฉ๋์ด ๋ ์ ์์ต๋๋ค.
์ค์ธก ์ฌ๋ก: swr_strategy_impl.dart์ ์ค์ฒฉ๋ unawaited(saveToCache) ์์ PR 1๊ฑด์ด, ์ด๋ฏธ ๋จธ์ง๋ ์๋ณธ ์์ PR(#7746/#7748)๊ณผ ๋ณ๋๋ก ๊ฐ์ ํ์ผ์ ๋ค๋ฅธ ํจ์๋ฅผ ๊ณ ์น๊ณ ์์๊ณ , ์์ชฝ์ด ํ๊ท ํ
์คํธ๋ฅผ ๊ฐ๊ฐ ์ถ๊ฐํด ๋ณํฉ ์
ํ
์คํธ ํ์ผ์์๋ง ์ถฉ๋์ด ๋ฐ์(๋ณธ์ฒด ์ฝ๋๋ ์๋ ๋ณํฉ๋จ). ํด๊ฒฐ: ํด๋น ๋ธ๋์น์ ์ต์ origin/development๋ฅผ merge โ lib ์ฝ๋ ์ถฉ๋ ์์ ํ์ธ โ test ํ์ผ๋ง "๋ ํ
์คํธ ์ผ์ด์ค ๋ชจ๋ ์ ์ง"๋ก ์๋ ์ ๋ฆฌ โ
dart analyze/ํ
์คํธ ์ฌํ์ธ โ merge commit push. ์์ธ ํด์ ์ ์ฐจ๋ [cc-dev-cycle:merge-conflict-resolution] ์ฐธ์กฐ.
๊ตํ (์ฒดํฌ๋ฆฌ์คํธํ):
- Fix-Fanout์ ์์ํ๊ธฐ ์ ์, ๊ฐ์ ํ์ผ/ํจ์๋ฅผ ๊ฑด๋๋ฆฌ๋ ์๋ณธ ๊ฒฐํจ์ ์ต์ด ์์ PR์ด ์ด๋ฏธ ๋จธ์ง๋๋์ง ํ์ธํ๋ผ. ์์ง open ์ํ๋ผ๋ฉด, ๊ฐ๋ฅํ๋ฉด ์๋ณธ PR์ ๋จผ์ ๋จธ์งํ ๋ค ํ์ fix ๋ธ๋์น๋ค์ ๋ฐ๋ผ.
-
์ด๋ฏธ ํ์ ๋ธ๋์น๋ค์ ๋ค ๋ง๋ ์ํ์์ ์๋ณธ PR์ด ๋จธ์ง๋๋ค๋ฉด, ๊ฐ ํ์ ๋ธ๋์น์
origin/development(๋๋ base) merge๋ฅผ ์ ํํ๊ณ ์ถฉ๋(๋๊ฐ ํ๊ท ํ ์คํธ ํ์ผ)์ ์ ๋ฆฌํ ๋ค PR์ ์ฌ๋ ค๋ผ. - ๋ณ๋ ฌ ์์ PR๋ค์ด ์ค์ ๋ก ์๋ก ๋ค๋ฅธ ํ์ผ๋ง ๊ฑด๋๋ฆฌ๋์ง Fix-Fanout ์ ์ ํ ๋ฒ ํ์ธํ๋ฉด, ์ด๋ฐ ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ ๋ฏธ๋ฆฌ ์ค์ผ ์ ์๋ค.
์ฒดํฌ๋ฆฌ์คํธ#
Triage#
- gray-list๊ฐ grep ๋ฑ์ผ๋ก ์ ์ ์์ฑ๋๋๊ฐ (์์ ์ํ๋ง ์๋)
- ๊ฐ ์์ด์ ํธ ํ๋กฌํํธ์ "์ด ํจํด์ด ์๋ ๊ฒ"์ด ๋ช ์๋๋๊ฐ (์ค์ฝํ ์ถ์)
- ์ฒญํฌ ํฌ๊ธฐ๊ฐ rate limit์ ๊ณ ๋ คํด ์ ์ ํ๊ฐ
Verify#
- Triage์ ๋ณ๋์ ์์ด์ ํธ๊ฐ ๊ฒ์ฆํ๋๊ฐ (๋์ผ ์์ด์ ํธ ์์ฒด ํ์ ๊ธ์ง)
- dead code / self-healing / stale-only(๋ฉ์ถ์ง ์์) 3๊ฐ์ง๋ฅผ ์ฌํ์ธํ๋๊ฐ
- Triage์ severity๋ฅผ ๊ทธ๋๋ก ์ ๋ขฐํ์ง ์๊ณ ์ฌํ๊ฐํ๋๊ฐ
Fix-Fanout#
- ๊ฒฐํจ ์ค๋ช (ํ์ผ/ํจ์/๋ผ์ธ/ํธ์ถ์ฒด์ธ)๊ณผ ์์ ๋ฐฉํฅ์ด ํ๋กฌํํธ์ ๊ตฌ์ฒด์ ์ผ๋ก ํฌํจ๋๋๊ฐ
isolation: 'worktree'๋ก ํญ๋ชฉ๋ณ ๊ฒฉ๋ฆฌ๊ฐ ์ ์ฉ๋๋๊ฐ- ์๋ณธ root-cause fix PR์ด ๋จผ์ ๋จธ์ง๋๋์ง(๋๋ ๋จธ์ง ํ ํ์ ๋ธ๋์น์ rebase/mergeํ ๊ณํ์ธ์ง) ํ์ธํ๋๊ฐ
- ๊ฐ fix ์์ด์ ํธ๊ฐ ๋จธ์ง๊น์ง๋ ํ์ง ์๊ณ PR ์์ฑ์์ ๋ฉ์ถ๋๊ฐ
์ค์ ์ฌ๋ก โ kobic #7746#
kobic(Flutter/Dart melos ๋ชจ๋
ธ๋ ํฌ) ํ๋ก์ ํธ์์ SwrStrategyImpl._revalidate์ ๋ฌดํ ๋ก๋ฉ ๋ฒ๊ทธ(#7746, ์์ธ๋ [cc-flutter-dev:swr-pattern] ์ฐธ์กฐ)๋ฅผ ๊ณ ์น ๋ค, ๊ฐ์
unawaited() + on Exception catch(Error ๋๋ฝ) ํจํด์ด ๋ค๋ฅธ ๊ณณ์๋ ์๋์ง ์ด ์ ์ฐจ๋ก ๊ฐ์ฌํ์ต๋๋ค.
- Triage:
unawaited(์ ํฌํจํ๋ ํ๋ก๋์ ํ์ผ 274๊ฐ๋ฅผ gray-list๋ก ๋ง๋ค์ด 30๊ฐ์ฉ ์ฒญํฌ ๋ถํ , ๋ณ๋ ฌ ์์ด์ ํธ ๋ฐฐ๋ถ -
Verify: ํ๋ณด ์ค severity "high" 2๊ฑด์ด ์ฃฝ์ ์ฝ๋(
@Deprecated)๋ก ๊ธฐ๊ฐ, 1๊ฑด์ดBehaviorSubject.seeded()๋ผ ๋ฉ์ถ์ง ์๊ณ stale๋ง ๋ฐ์ํ๋ ๊ฒ์ผ๋ก ๊ธฐ๊ฐ -
Fix-Fanout: ํ์ 11๊ฑด + ๋ถ์ ๋ฐ๊ฒฌ ์ฃฝ์ ์ฝ๋ ์ ๋ฆฌ 1๊ฑด, ์ด 12๊ฑด์
isolation: 'worktree'๋ก ๋์ ์์ (EPUB ๋ฆฌ๋ ์ ์ฒด ๋ฉ์ถค ๋ฑ high severity ํฌํจ) - ๋จธ์ง ํ: 12๊ฑด ์ค 1๊ฑด์์ ์ "๋จธ์ง ์ถฉ๋ ํจ์ "์ด ์ค์ ๋ก ๋ฐ์ โ origin/development merge + ํ ์คํธ ํ์ผ ์๋ ์ ๋ฆฌ๋ก ํด๊ฒฐ
๊ด๋ จ ์คํฌ#
- [cc-flutter-dev:swr-pattern] โ ์ด ์ฌ๋ก์ ๊ทผ๋ณธ ์์ธ์ด ๋ Dart
unawaited() + Error์นจ๋ฌต ์คํจ ํจ์ ์์ธ - [cc-dev-cycle:merge-conflict-resolution] โ Fix-Fanout ํ ๋ฐ์ํ๋ ๋จธ์ง ์ถฉ๋์ ํ์ผ ์ ํ๋ณ ํด์ ์ ๋ต
- [cc-dev-cycle:agent-teams] โ Agent Teams(ํ์ผ ์์ ๊ถ ๋ถ๋ฆฌ) vs git worktree(ํ์ผ์์คํ ๊ฒฉ๋ฆฌ) ๋ณ๋ ฌํ ๋น๊ต
- [cc-serverpod:serverpod-worktree-parallel] โ worktree๋ณ Serverpod ํ์คํ ํฌํธ/MCP ๊ฒฉ๋ฆฌ (ํ์คํ ๊ฒ์ฆ์ด ํ์ํ fix์ ์ ์ฉ)