LogoSkills

/cc-quality:review — 코드 9개 항목 자동 점검

변경된 코드를 아키텍처·보안·성능·테스트 등 9개 항목으로 점검해 별점 요약표와 심각도별(치명/개선/제안) 문제 목록을 냅니다 — 흔한 오탐은 억제 규칙으로 걸러 따로 보여줍니다.

/cc-quality:review — 코드 9개 항목 자동 점검#

항목내용
실행 명령/cc-quality:review
분류개발
난이도●●○ 보통
MCP 서버serena, context7

한마디로#

작성한 코드를 PR(병합 요청)에 올리기 전, 숙련된 검토자가 9가지 기준으로 꼼꼼히 살펴보는 것과 같습니다. 출판 전 원고를 교정 보는 것처럼, 보안·성능·테스트 등 빠뜨리기 쉬운 부분을 자동으로 짚어줍니다.

누가·언제 쓰나요#

  • 동료의 PR을 검토(리뷰)해야 할 때
  • 코드 품질을 한번 점검하고 싶을 때
  • 커밋(코드 저장)하기 전에 스스로 먼저 살펴보고 싶을 때

무엇을 해주나요#

코드를 9개 항목으로 나누어 점검하고, 별점 요약표 + 문제 목록을 만들어 줍니다.

  • 9개 점검 항목: 아키텍처 / 상태 관리 / 보안 / 성능 / 테스트 / 가독성 / 다국어(i18n) / 접근성 / 구조적 결함
  • 마지막 "구조적 결함" 항목은 나머지 8개와 질문이 다릅니다 — "지금 이 코드가 맞는가"가 아니라 **"나중에 누가 한 줄 빠뜨려도 아무도 모르게 되지는 않는가"**를 봅니다. 같은 버그가 두세 번 반복되는 원인을 짚는 항목입니다.
  • 발견된 문제는 심각도별로 분류 — 🔴 치명적 / 🟡 개선 / 🟢 제안
  • 흔한 오탐(잘못된 지적)은 자동으로 걸러줌(억제 규칙). 걸러진 항목도 따로 보여줘서 빠짐없이 확인 가능
  • 점검하지 못한 항목은 "통과"로 세지 않습니다 — 도구가 안 돌았거나, MCP를 못 썼거나, 빠른 검토(--quick)로 못 본 항목은 ✅ 대신 ⚠️ 확인 못 함(UNVERIFIED) 으로 표시되고, 하나라도 있으면 관문은 막힙니다. --allow-unverified로 넘길 수는 있지만 그 목록이 PR 본문에 남습니다
  • 9개 항목은 언제나 9개 전부 판정합니다 — 여러 명이 나눠서 볼 때도 누가 어느 항목을 봤는지 표로 함께 출력하고, 아무도 맡지 않은 항목은 "확인 못 함"이 됩니다
  • --gate-mode에서는 "통과/차단" 형태의 관문(Gate) 결과표로 정리 — PR 생성을 막을 치명적 문제와 PR 본문에 넣을 참고 사항을 분리해서 보여줌. 통과는 치명적 0건 그리고 확인 못 함 0건일 때만입니다

어떻게 쓰나요#

# 특정 파일 검토
/cc-quality:review feature/auth/lib/src/presentation/bloc/login_bloc.dart

# 보안에 집중해서 검토
/cc-quality:review feature/auth/ --focus security

# 빠르게 훑어보기
/cc-quality:review . --quick
  • --focus : 한 항목에 집중 (예: security 보안, performance 성능)
  • --quick : 빠른 검토 모드 / --verbose : 더 자세한 검토 모드
  • --no-suppress : 오탐 걸러내기를 끄고 모든 지적을 다 보기
  • --gate-mode : 관문(Gate) 결과표 형태로 출력 (치명적 문제와 참고 사항 분리)
  • --allow-unverified : 확인 못 한 항목이 있어도 관문을 통과시킴 (대신 그 목록을 PR 본문에 반드시 남깁니다)

안에서 무슨 일이 벌어지나요#

  1. 오탐 걸러내기 준비 — 플러그인 기본 규칙과 프로젝트 자체 규칙(.code-review-suppress.md)을 불러와 잘못된 지적을 미리 거를 준비를 합니다.
  2. 9개 항목 점검 — 아키텍처부터 구조적 결함까지 9개 기준으로 코드를 하나씩 살펴봅니다. 항목마다 "무엇을 실제로 돌려서 그렇게 판단했는지"(명령어 출력 한 줄)를 함께 남깁니다. (필요하면 Serena로 코드 구조를, Context7로 모범 사례를 함께 확인)
  3. 합치기(한 번에 모아 판정) — 항목별로 나온 지적을 파일:줄 + 규칙 종류로 중복 제거하고, 오탐 걸러내기는 합친 뒤에 한 번만 적용합니다. 치명적 지적은 변경분 전체에 대해 한 번 더 확인해 살아남은 것만 관문을 막습니다 — 오탐 하나가 자동 수정 기회 2번을 태우고 파이프라인을 세우기 때문입니다.
  4. 결과 정리 — 항목별 별점 요약표와 함께 문제를 🔴치명적·🟡개선·🟢제안으로 나눠 보여주고, 확인 못 한 항목과 걸러낸 항목도 따로 표시합니다.

⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)

Triggers#

  • When a PR review is requested
  • When code quality inspection is needed
  • For self-review before committing

Context Trigger Pattern#

/cc-quality:review {target} [--options]

Parameters#

ParameterRequiredDescriptionExample
targetReview targetFile path, PR link
--focusFocus categoryarchitecture, state, security, performance, testing, readability, i18n, a11y, structural
--quickQuick review mode
--verboseDetailed review mode
--no-suppressDisable suppression
--gate-modeReview Gate mode (separates Critical/Informational output)
--allow-unverifiedDo not block on ⚠️ UNVERIFIED categories. Requires listing every unverified category + reason in the PR body (durable record — console is not durable)

Suppression List#

Automatically applies suppression rules during review to reduce false positives.

Loading Order#

  1. Plugin default: cc-quality/rules/suppression.md
  2. Project custom: project root .code-review-suppress.md (if exists)

Application Method#

  • Each review finding is compared against the suppression list
  • Matching items are excluded from review results
  • Can be disabled with --no-suppress option

Result Display#

## Suppressed (3 items excluded)
- [Readability] `*.g.dart` generated code pattern (suppression: code generation related)
- [Performance] const not applied (suppression: style/convention related)
- [Architecture] melos import path (suppression: project structure related)

Review Categories#

This section is the single SoT for the category list. The count is 9, not 8. Every other statement of the list or its count is a derived view (non-normative, plugins/cc-dev/rules/orchestration-graph.md §6) and must link here instead of restating a number. Known drift to repoint, not to copy: plugins/cc-dev/commands/run.md Step 8.7 ("8 review categories … 3 teams", plus its Fallback (순차) line) and plugins/cc-quality/commands/feature-qa.md ("8-category code review").

Enumeration obligation for any split (parallel or sequential). A run that distributes categories across reviewers/teammates MUST enumerate all 9 and print the Category → reviewer mapping in the gate output (see Gate Mode Output). A category that no reviewer owns is ⚠️ UNVERIFIED, never an implicit ✅ — the existing 3-teammate split drops §9 Structural Failure Modes (and §5 Testing) exactly this way, and §9 is the category that exists to catch repeat bugs. This obligation constrains the existing split; it does not add a new one.

1. Architecture#

  • Clean Architecture layer separation followed
  • Domain → Data → Presentation dependency direction
  • Business logic accessed through UseCases
  • Repository interface separation
  • Feature module independence

2. State Management#

  • BLoC/Cubit pattern followed
  • Event → BLoC → State flow
  • Immutable state via sealed class extends Equatable + copyWith (no @freezed)
  • Proper error handling
  • Loading state management

3. Security#

  • No hardcoded API keys or secrets
  • No sensitive information in logs
  • User input sanitization
  • Authentication/authorization properly applied

4. Performance#

  • const widgets utilized
  • BlocSignalBuilder buildWhen used
  • Image cacheWidth/cacheHeight applied
  • Resources released in dispose
  • Stream subscriptions cancelled

5. Testing#

  • UseCase unit tests
  • Repository tests (mocked)
  • BLoC tests
  • Edge cases covered
  • Arrange-Act-Assert pattern

6. Readability#

  • Clear and meaningful naming
  • Project convention followed
  • Appropriate file/class size
  • Single Responsibility Principle
  • Duplicate code removed

7. Internationalization (i18n)#

  • All UI text uses translation keys
  • context.t.* pattern used
  • Proper pluralization
  • Dynamic values parameterized

8. Accessibility#

  • Appropriate semantic labels
  • Minimum 48x48 touch target
  • WCAG color contrast criteria met

9. Structural Failure Modes#

Asks "could a future change break this without anyone noticing?" rather than "is this code correct now?". Targets the repeat-bug classes static analysis misses.

  • New public endpoint method reaches an authorization guard (and uses the same mechanism as its siblings)
  • on<Event>() handlers that await specify transformer: deliberately
  • New repository mutation wired to cache invalidation
  • Distinct identifier spaces / index bases / units have distinct types, not bare int/String
  • New optional parameter: sibling call sites checked, with the search command recorded
  • Dependency upgrade changing a default/signature: all call sites of that API audited, not just the reported one
  • Raw API that has a project wrapper: wrapper used rather than the raw form
  • Local widget state not duplicating a value already derivable from a source of truth
  • Anything deferred as "out of scope" exists as an issue or @Skip('#nnn') test
  • Generated/platform files the build needs are committed and clean-clone verified

Detailed criteria and real-case references: cc-quality/skills/code-review-checklist/REFERENCE.md §9.

Review Output Template#

Score legend (tri-state — pass / fail / undetermined, per plugins/cc-dev/rules/orchestration-graph.md §3): = evidence quoted and green · ⚠️ = findings, evidence present · = fail · ⚠️ UNVERIFIED = undetermined, which is not a pass (undet:fail). The Evidence cell is mandatory for every row including rows with no findings — "nothing found" and "nothing ran" are different results, and conflating them is the nothing-to-check pass / silent skip fail-open pair in §3.2 of that document. This mirrors the eval G5 convention in plugins/cc-quality/skills/code-review-checklist/SKILL.md.

## Summary

**Overall**: ⭐⭐⭐☆☆ (3/5)    < !-- any UNVERIFIED row caps Overall below 4/5 -- > 

 | Category | Score | Evidence (what actually ran) | Key Issues |
|----------|-------|------------------------------|------------|
| Architecture || `serena find_referencing_symbols LoginRepository` → 6 refs, layer order intact | - |
| State Management | ⚠️ | `melos run analyze` → 0 issues; 4 BlocSignalBuilder sites read | BlocSignalBuilder optimization needed |
| Security || `grep -rn  " apiKey\|secret "   --include= " *.dart "   lib/` → 0 hits outside Envied | - |
| Performance | ⚠️ | 3 Image widgets read in diff | Image cache size not specified |
| Testing || `melos run test` → 118 passed, 0 failed; 0 tests for LoginUseCase | UseCase tests missing |
| Readability || `melos run lint:check` → 0 issues | - |
| i18n | ⚠️ UNVERIFIED | Context7 unavailable — pluralization rules not checked | not judged |
| Accessibility | ⚠️ | 2 IconButton without semanticLabel | Some semantic labels missing |
| Structural Failure Modes | ⚠️ | Exhaustiveness block below (18/18 call sites) | New endpoint method has no authorization guard |

### Unverified ⚠️ (1 category — gate BLOCKED unless `--allow-unverified`)

| Category | Why undetermined | What would resolve it |
|----------|------------------|-----------------------|
| i18n | MCP (Context7) unavailable | re-run with Context7, or review pluralization manually |

### Critical Issues 🔴

1. **[Security]** API key hardcoded
   - File: `lib/core/config.dart:15`
   - Fix: Use environment variables via Envied

### Improvements 🟡

1. **[Performance]** Image cache size needs to be specified
   - File: `lib/presentation/widget/product_card.dart:42`
   - Recommended: Add `cacheWidth: 200`

### Suggestions 🟢

1. **[Readability]** Variable name improvement suggestion
   - `data` → `userProfile`

### Exhaustiveness (mandatory whenever §9 flags a call-site family)

| Search command (re-runnable verbatim) | Hits | Checked | Remainder → tracked as |
|---------------------------------------|------|---------|------------------------|
| `grep -rn  " allowMultiple "   --include= " *.dart "   .` | 18 | 18 | 0 |
| `grep -rn  " unawaited( "   --include= " *.dart "   .` | 11 | 9 | 2 → #7746 |

### Suppressed (N items excluded)
 >   Items excluded by suppression rules. Use `--no-suppress` to see all.

Exhaustiveness#

Mandatory block. Emit it whenever §9 flags a call-site family, a new optional parameter, or a dependency default/signature change. Four values, no prose substitutes:

FieldRule
Search commandThe exact command, re-runnable verbatim. "audited the call sites" is not a value.
HitsThe command's own count.
CheckedHow many were actually inspected. Checked < Hits with no remainder row is a defect.
RemainderEvery unchecked site is covered by a tracked issue id (#nnn or @Skip('#nnn')).
  • A missing Exhaustiveness block makes §9 ⚠️ UNVERIFIED, not ✅ — which blocks the gate.
  • Rationale is §9.2's own cited failure: kobic #9162 patched 1 of 18 call sites and left the rest unguarded, with "3 more remain" as prose in the PR body. The unit of work is the call-site family, not the reported instance. See plugins/cc-quality/skills/code-review-checklist/REFERENCE.md §9.2.
  • Loop contract for this sweep: L-review.sibling-callsite-sweep.

Gate Mode Output (--gate-mode)#

Structured output for workflow gate when running /cc-quality:review --gate-mode:

Verdict rule (tri-state, undet:fail). Status is ✅ PASS only when criticalCount === 0 && unverifiedCount === 0.

  • Status = ❌ BLOCKED if any category is ⚠️ UNVERIFIED — MCP unavailable, --quick did not reach it, no reviewer owned it in a split, or the teammate died — unless --allow-unverified, which must list every unverified category and its reason in the PR body (durable record; console output is not durable).
  • Critical: 0 items alone is not a pass. Zero findings from a category that never ran is the nothing-to-check pass form; a tool/MCP absence that quietly drops a category is the silent skip form — both in plugins/cc-dev/rules/orchestration-graph.md §3.2.
  • Degrading undetermined to warn is permitted only for the three pre-declared cases in §3.1 of that document (nothing left to block · structurally not applicable · tooling absent), with the reason printed in the Why undetermined cell.
## Code Review Gate Result

**Status**:PASS /BLOCKED  (PASS requires Critical 0 **and** Unverified 0)

### Category Coverage (9/9 — mapping is mandatory, also in single-reviewer runs)

| # | Category | Reviewer | Score | Evidence (what actually ran) |
|---|----------|----------|-------|------------------------------|
| 1 | Architecture | teammate-2 || `serena find_referencing_symbols` → 6 refs |
| 2 | State Management | teammate-2 | ⚠️ | 4 BlocSignalBuilder sites read |
| 3 | Security | teammate-1 || `grep -rn  " apiKey\|secret " ` → 0 hits |
| 4 | Performance | teammate-1 | ⚠️ | 3 Image widgets read |
| 5 | Testing | lead | ⚠️ UNVERIFIED | `melos run test` not run (`--quick`) |
| 6 | Readability | teammate-3 || `melos run lint:check` → 0 issues |
| 7 | i18n | teammate-3 || 0 hardcoded strings in diff |
| 8 | Accessibility | teammate-3 | ⚠️ | 2 IconButton without semanticLabel |
| 9 | Structural Failure Modes | lead | ⚠️ UNVERIFIED | no owner assigned in the split |

### Unverified (Pass 0) ⚠️
 >   Blocks the gate. `undetermined` is not a pass.

| # | Category | Why undetermined | Durable record |
|---|----------|------------------|----------------|
| 1 | Testing | `--quick` skipped `melos run test` | PR body (required if `--allow-unverified`) |
| 2 | Structural Failure Modes | no reviewer owned it in the 3-way split | PR body (required if `--allow-unverified`) |

### Critical Issues (Pass 1) 🔴
 >   Issues that block PR creation

| # | Category | Issue | File | Auto-fix |
|---|----------|-------|------|----------|
| 1 | Security | API key hardcoded | config.dart:15 |Possible |

### Informational (Pass 2) 📋
 >   Improvement suggestions to include in PR body

| # | Category | Issue | File |
|---|----------|-------|------|
| 1 | Performance | Image cache not specified | product_card.dart:42 |

### Summary
- Categories: 9/9 judged, 2 UNVERIFIED
- Critical: 0 items (after full-diff re-check)
- Unverified: 2 categories → ❌ Gate BLOCKED (`undet:fail`) — pass `--allow-unverified` to proceed
- Informational: 3 items → Included in PR body
- Suppressed: 2 items → Excluded (applied once, after merge)
- Verdict: Critical 0  & &   Unverified 2 → ❌ BLOCKED

Convergence#

Categories are reviewed as a pipeline, then merged in one barrier phase before the verdict. That barrier is the consolidation phase this command previously lacked: splitting the review across reviewers lowers recall precisely on cross-cutting Criticals, so the verdict is computed once, over the merged set — never per reviewer. The substrate is unchanged: this adds a join, not a fan-out. Notation, gate tri-state, barrier justification and loop-contract fields follow plugins/cc-dev/rules/orchestration-graph.md (§1, §3, §4.2, §2, §5) — §4.2 already lists "run.md Step 8.7 Critical 통합" among the confirmed-legitimate barriers, and this is that barrier's own site.

Normative declaration of this command's flow. The step lists, parameter tables and output templates above are derived views (§6) — where they disagree, this block wins.

RV0    ACT   load suppression list (plugin → project)   reads:cc-quality/rules/suppression.md,.code-review-suppress.md
RV1    LOOP  nine-category sweep (§1..§9)              contract:L-review.nine-category-sweep
RV1.9  LOOP  sibling call-site sweep (§9)              contract:L-review.sibling-callsite-sweep
RV2    JOIN  consolidation (dedup → suppress → recheck) mode:barrier  because:a cross-cutting Critical cannot be judged per category — judge once over the merged set
RV3    GATE  verdict                                   verdict:criticalCount===0  & &   unverifiedCount===0  undet:fail  fail:RVfix
RV4    ACT   emit report + gate tables                 writes:stdout,prBodyExtras.reviewFindings
RVfix  LOOP  caller autofix ↺ re-review                contract:L-review.gate-autofix-recheck  extern:true  tier:standard

RV0 -- >   RV1 -- >   RV1.9 -- >   RV2 -- >   RV3 -- >   RV4
RV3 ~~ >   RV4    on:--allow-unverified                  record:Unverified table in the PR body (console is not durable)
RV3 == >   RVfix  on:criticalCount > 0||unverifiedCount > 0  bound:2  invalidates:RV1,RV1.9,RV2

Merge Rules (RV2)#

RuleValue
Dedup keyfile:line + rule-class. Two reviewers reporting the same key are one finding; the highest severity wins.
Suppression timingApplied after the merge, never per reviewer, and counted once. The Suppressed (N) count is post-merge.
False-Critical guardEvery Critical must survive one re-check against the FULL diff before it blocks. A false Critical burns both autofix attempts and stops the pipeline.
Cross-category findingsA finding whose evidence spans two categories belongs to this phase, not to either reviewer — that is the because: of mode:barrier.
Unverified mergeunverifiedCount is computed here from the coverage map, so a category nobody owned surfaces at the join rather than disappearing.

L-review.nine-category-sweep#

inv:      each of §1..§9 holds exactly one summary row with BOTH Score and Evidence filled,
          checked on iteration entry and exit; a missing row is UNVERIFIED, never a pass
prog:     judged = categories carrying a Score, strictly increasing 09
          no-prog: never re-sweep a category without new evidence — mark it UNVERIFIED and move
          on instead of spending the remaining budget on the same category
term:     judged === 9   (9 is defined by `## Review Categories`; not redefined here)
budget:   9 category passes, 1 each. `--focus X` = 1 deep pass + the other 8 marked UNVERIFIED
          (never silently skipped); `--quick` reduces pass DEPTH, never the COUNT
exhaust:  any category still unjudged → `⚠️ UNVERIFIED` and RV3 blocks; promoting it to ✅ is
          prohibited
resume:   the summary table is the position record — rows already holding Score+Evidence are not
          re-run; re-running a category is idempotent (same `file:line + rule-class` merges at RV2)
log:      one line per category, e.g.  " §4 Performance: 2 findings (evidence: 3 Image widgets read) " ,
          plus the UNVERIFIED list; a 0 is printed together with the evidence that produced it

L-review.sibling-callsite-sweep#

Backs the Exhaustiveness block.

inv:      the recorded search command is re-runnable verbatim and its own count equals the
          reported Hits, on entry and exit
prog:     unchecked = Hits - Checked, strictly decreasing
          no-prog: never re-inspect a site without emitting a verdict for it → Rung 2
term:     unchecked === 0 || every remaining site is covered by a tracked issue id
budget:   1 sweep per changed API/parameter per review; Hits  >   20 → hand the family to
          `plugins/cc-dev/skills/discovery-audit/SKILL.md` instead of extending this sweep
exhaust:  a remainder with no tracked issue id keeps the §9 row 🔴 Critical — never downgraded to
          🟡, never closed as  " out of scope "   in prose
resume:   the previous report ' s Exhaustiveness block (command + Hits + Checked + remainder)
log:      one line per sweep:  " §9.2 allowMultiple → 18 hits / 18 checked / 0 left "

L-review.gate-autofix-recheck#

This is the SoT for the caller's autofix loop. Callers — plugins/cc-dev/commands/run.md Step 8.7's retry, and any auto_fix: true delegation — declare contract:L-review.gate-autofix-recheck at their call site and inherit these values instead of restating them. A delegated loop is still a loop (plugins/cc-dev/rules/orchestration-graph.md §2), and an interactive AskUserQuestion is not a valid exhaust: inside a dispatched worker.

inv:      every re-check re-runs the FULL nine-category sweep against the FULL diff — not only the
          files the autofix touched; suppression is applied once, after the merge, and counted once
prog:     blocking = criticalCount + unverifiedCount, strictly decreasing per attempt
          no-prog: if the same `file:line + rule-class` is still Critical after an attempt, stop
          autofixing that key and escalate (Rung 2 — `/cc-dev:unstuck`); do not spend the second
          attempt on the same key
term:     criticalCount === 0  & &   unverifiedCount === 0
          (or `--allow-unverified` with every unverified category listed in the PR body)
budget:   inner 2 autofix attempts per gate entry / outer 1 re-entry per push
          (how the number is chosen: `plugins/cc-dev/skills/job-timeout-budget/SKILL.md`)
exhaust:  BLOCKED — block PR creation and report the surviving keys.  " warn and continue "   is
          prohibited, as is downgrading a Critical to Informational to reach 0
resume:   the last gate output ' s Critical + Unverified tables, keyed by `file:line + rule-class`;
          re-running the review is idempotent
log:      one line per attempt,  " autofix #2: critical 3→1 unverified 0 " , plus a durable record of
          what was dropped and why (fixed / suppressed / deferred with issue id)

Automation Commands#

# Lint check
melos run analyze
melos run lint:check

# Test execution
melos run test
melos run test:with-html-coverage

# Formatting (kobic uses dcm format, not dart format)
melos run format

MCP Integration#

StepMCP ServerPurpose
Code analysisSerenaSymbol and reference search
Pattern verificationContext7Best practices validation

Examples#

File review#

/cc-quality:review feature/auth/lib/src/presentation/bloc/login_bloc.dart

Security-focused review#

/cc-quality:review feature/auth/ --focus security

Quick review#

/cc-quality:review . --quick

References#

  • Detailed checklist: cc-quality/skills/code-review-checklist/SKILL.md (per-category criteria: REFERENCE.md)
  • PR review guide: cc-quality/commands/checklist/pr-review.md
  • Suppression rules: cc-quality/rules/suppression.md
  • Flow notation · gate tri-state · loop contract: plugins/cc-dev/rules/orchestration-graph.md