LogoSkills

/cc-quality:feature-qa — 기능 품질 건강검진

기능 단위 QA Health Score 평가와 baseline 회귀 추적

/cc-quality:feature-qa — 기능 품질 건강검진#

항목내용
실행 명령/cc-quality:feature-qa
분류개발
난이도●●○ 보통

한마디로#

완성한 기능을 건강검진하듯 5개 항목으로 점수 매기고 A~F 등급을 매기는 도구입니다. 지난번 점수와 비교해서 "예전보다 나빠진 곳"이 있는지도 알려줍니다.

누가·언제 쓰나요#

  • 개발자가 기능을 다 만든 직후 품질이 괜찮은지 확인하고 싶을 때
  • PR(코드 제출)을 올리기 전 참고용 점검으로 — 이 커맨드는 아무것도 막지 않습니다(권고용). PR 을 실제로 막는 관문은 /cc-quality:review --gate-mode(코드 리뷰 게이트)입니다.
  • 이미 있던 기능이 예전보다 나빠지지 않았는지 다시 확인하고 싶을 때

무엇을 해주나요#

  • 기능을 5가지 관점으로 채점해 100점 만점 점수와 A~F 등급을 줍니다.
    • 기능 완성도(30%), 화면/UX(20%), 접근성(20%), 성능(15%), 다국어(15%)
    • 5가지가 전부 도착해야 등급이 나옵니다. 하나라도 결과가 없으면 그 항목은 확인 못 함(UNVERIFIED) 이고, 0점으로 채점하지 않고 등급 계산 자체를 멈춥니다.
  • 결과를 한눈에 보이는 점수판(막대그래프) 으로 보여주고, 발견된 문제를 표로 정리합니다.
  • 채점 결과를 .qa-baseline/{기능이름}.jsonl 대장에 한 줄씩 덧붙여(append-only) 기준점(baseline) 으로 삼습니다 — 지난 줄을 덮어쓰거나 지우지 않습니다.
  • 총점이 지난 기준점보다 낮으면 기준점을 갈아치우지 않고 회귀 기록으로만 남깁니다(기준점은 내려가지 않습니다).
  • 다음번 검사 때 이 기준점과 비교해 좋아졌는지·나빠졌는지(회귀) 리포트를 만들어 줍니다.
  • 등급은 참고용입니다 — 점수가 F 라도 이 커맨드가 PR 을 막지는 않습니다.

어떻게 쓰나요#

# 기본: 기능 폴더 경로를 지정해 검사
/cc-quality:feature-qa feature/auth/

# 지난번 기준점과 비교 (좋아졌나 나빠졌나)
/cc-quality:feature-qa feature/auth/ --regression

# 빠른 점검 (기능 완성도만)
/cc-quality:feature-qa feature/auth/ --quick

# 특정 항목만 집중 검사
/cc-quality:feature-qa feature/auth/ --focus ui,a11y

# 5개 항목 동시 검사 (Agent Teams 필요, 더 빠름)
/cc-quality:feature-qa feature/auth/ --parallel
  • 맨 앞의 기능 경로(feature/auth/ 등)는 반드시 필요합니다.
  • --focus에 넣을 수 있는 항목: ui(화면), a11y(접근성), perf(성능), i18n(다국어), func(기능).
  • 기준점 저장은 기본적으로 자동입니다. 저장하지 않으려면 --no-save를 붙이세요.
  • --quick·--focus는 일부 항목만 보므로 자동으로 --no-save 가 됩니다 — 반쪽 점수는 기준점이 될 수 없습니다. 이때는 항목별 점수와 문제 목록만 나오고 총점·등급은 나오지 않습니다.

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

  1. 채점하기 전에 지난 기준점을 먼저 읽습니다 — 비교 대상을 손에 쥔 상태로 시작합니다.
  2. 지정한 기능을 5개 관점으로 채점합니다 — 기능 완성도, 화면/UX, 접근성, 성능, 다국어.
  3. 5개 항목이 전부 도착했는지 확인합니다. 빠진 항목이 있으면 그 항목은 확인 못 함(UNVERIFIED)으로 적고, 총점·등급 계산과 기준점 저장을 하지 않습니다 — 대신 "확인 못 함" 기록만 대장에 한 줄 남겨 빠진 사실이 묻히지 않게 합니다.
  4. 각 항목 점수에 비중을 곱해 더하고, 심각한 문제 1건당 10점씩 깎아 총점과 등급(A~F) 을 계산합니다.
  5. --regression을 쓰면 1번에서 읽은 지난 기준점과 비교해 항목별 증감 리포트를 만들어 줍니다.
  6. 비교를 끝낸 뒤에 .qa-baseline/{기능이름}.jsonl 대장에 한 줄을 덧붙입니다 — 총점이 올랐으면 새 기준점, 내려갔으면 회귀 기록입니다. 부분 실행(--quick·--focus)이면 아무것도 쓰지 않습니다.
  7. --parallel을 쓰면 항목들을 3개 팀이 동시에(읽기만) 검사해 더 빠르게 끝냅니다. (불가능하면 자동으로 순차 검사로 전환)

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

Triggers#

  • After feature implementation for quality verification
  • Advisory pre-PR read-out — this command scores and reports; it never blocks
  • To check quality regression of existing features

This command is advisory, not a gate. No grade, no score and no regression entry blocks anything: there is no failing threshold here and no caller reads the grade (grep -rn feature-qa over plugins/ returns docs and index rows only). The blocking pre-PR gates live elsewhere — plugins/cc-dev/commands/run.md Step 8.5 (runPrePushGate) and Step 8.7 (Code Review Gate, Critical 0, via /cc-quality:review --gate-mode). Do not wire a threshold here without also declaring it as a GATE at that call site; a gate that only one document believes in is the failure mode plugins/cc-dev/rules/orchestration-graph.md §3 exists to prevent.

Usage#

# Basic usage: QA by feature path
/cc-quality:feature-qa feature/auth/

# Regression comparison (against previous baseline)
/cc-quality:feature-qa feature/auth/ --regression

# Quick check (functional completeness only)
/cc-quality:feature-qa feature/auth/ --quick

# Specific axis only
/cc-quality:feature-qa feature/auth/ --focus ui,a11y

# Parallel evaluation (Agent Teams)
/cc-quality:feature-qa feature/auth/ --parallel

Parameters#

ParameterRequiredDescriptionExample
feature_pathFeature module pathfeature/auth/
--regressionCompare against previous baseline
--quickQuick check (functional completeness only). Partial run → implies --no-save, emits no total/grade
--focusFocus on specific axes. Partial run → implies --no-save, emits no total/gradeui, a11y, perf, i18n, func
--parallel3-teammate evaluation of the 5 axes (requires Agent Teams; read-only teammates)
--save-baselineSave baseline (default: auto-save on a full 5-axis run)
--no-saveDo not append a ledger entry. Forced on by --quick / --focus

--quick and --focus are not overridable by --save-baseline: a partial score can never become a baseline, so the two flags win and the run reports Baseline: not written (partial run).


5-Axis Evaluation System#

Functional Completeness — Weight 30%#

ItemPointsInspection Method
Acceptance Criteria met30Map issue AC to implementation code
Edge case handling25Check null/empty/boundary handling
Error handling25try-catch, ErrorState, user messages
Test coverage20UseCase 100%, BLoC 80%+ target

Inspection Items:

  • Are all ACs mapped to implementation code
  • Is null safety handled properly
  • Is there user feedback on network errors
  • Is empty list state (EmptyState) handled
  • Is loading state displayed

UI/UX (Visual) — Weight 20%#

ItemPointsInspection Method
Correct CoUI component usage30Verify component API compliance
Layout consistency25Gap/Insets constants used, alignment
Color system compliance25appColors/colorScheme used
Typography compliance20context.textStyles used

Inspection Items:

  • Are CoUI component APIs used correctly (features parameter, etc.)
  • Are Gap/Insets constants used (no hardcoded values)
  • Are colors accessed via context.appColors / context.colorScheme
  • Are text styles accessed via context.textStyles
  • Are non-existent APIs like ButtonSize.medium not used

Accessibility — Weight 20%#

ItemPointsInspection Method
Semantic Labels35Labels present on images, icons, buttons
Touch target size30Minimum 48x48 verified
Color contrast20WCAG AA criteria
Screen reader order15Logical tab order

Inspection Items:

  • Do Image/Icon have semanticLabel
  • Are touchable elements at least 48x48
  • Is text-background color contrast 4.5:1 or above
  • Are widgets arranged in a meaningful order

Performance — Weight 15%#

ItemPointsInspection Method
const widget usage30Check const-eligible widgets
BlocSignalBuilder optimization25buildWhen/listenWhen usage
Image optimization25cacheWidth/cacheHeight applied
Resource cleanup20dispose/cancel verified

Inspection Items:

  • Are const-eligible widgets declared as const
  • Is buildWhen applied to BlocSignalBuilder
  • Do network images have cacheWidth/cacheHeight
  • Are Stream subscriptions cancelled in dispose
  • Is BLoC isClosed checked in async handlers

Internationalization (i18n) — Weight 15%#

ItemPointsInspection Method
Translation key usage40context.t.* pattern used
No hardcoded strings30UI text hardcoding detection
Pluralization handling15plural/ordinal applied
Dynamic value parameterization15Parameters instead of string interpolation

Inspection Items:

  • Do all UI texts use context.t.*
  • Are there no hardcoded Korean/English strings
  • Is pluralization handled for texts containing numbers
  • Are dynamic values passed as parameters

Health Score Calculation#

Score Computation#

Total = Sum(each axis score x weight) - (Critical issue count x 10)

Each axis score = Sum(item scores) / 100 x 100

Grading Precondition — all 5 axes must have reported#

A grade is computed only when all 5 axes reported a score. A missing axis is ⚠️ UNVERIFIED; it is never scored 0 and never treated as "no findings".

Axis stateTotal / gradeLedger
5 axes reportedcomputedone line appended (kind: baseline or kind: regression)
any axis ⚠️ UNVERIFIED (dead teammate, tool absent, error)not computed — print the per-axis table + the UNVERIFIED listone kind: unverified audit line — never a baseline, never a regression
partial by request (--quick, --focus)not computed — axis scores onlynothing written (a requested partial run is not an anomaly)

Scoring a missing axis 0 is the nothing-to-check pass / null-as-pass pair from plugins/cc-dev/rules/orchestration-graph.md §3.2 read backwards: a dead teammate would otherwise produce a low-but-plausible D/F that looks like a real measurement. 0 and "not measured" are printed as different things, and only measured zeros carry evidence.

Grade Criteria#

GradeScore RangeMeaning
A90-100Excellent — production ready
B80-89Good — minor improvements needed
C70-79Average — improvements recommended
D60-69Insufficient — improvements required
F0-59Poor — fixing before PR is strongly recommended (advisory; nothing here blocks)

Every row above is a recommendation. No row is a threshold: see the advisory note under Triggers for where the blocking gates actually sit.


Baseline Storage and Regression Comparison#

Baseline Storage#

The store is the append-only ledger .qa-baseline/{feature-name}.jsonl — one JSON object per line, same shape of record as this plugin's own TMR ledger (plugins/cc-quality/skills/qa-environment-hygiene/SKILL.md.claude/qa/{scope}/mutations.jsonl). A run appends; it never rewrites or truncates a previous line. The current baseline is the last line with "kind": "baseline".

One ledger entry (pretty-printed here; on disk it is one line):

{
   " kind " :  " baseline " ,
   " feature " :  " auth " ,
   " date " :  " 2026-03-13T10:30:00Z " ,
   " substrate " :  " agent-teams " ,
   " partial " : false,
   " unverified " : [],
   " grade " :  " B " ,
   " totalScore " : 85,
   " scores " : {
     " functional " : {  " score " : 90,  " weight " : 0.30,  " weighted " : 27.0 },
     " visual " : {  " score " : 80,  " weight " : 0.20,  " weighted " : 16.0 },
     " accessibility " : {  " score " : 75,  " weight " : 0.20,  " weighted " : 15.0 },
     " performance " : {  " score " : 85,  " weight " : 0.15,  " weighted " : 12.75 },
     " i18n " : {  " score " : 90,  " weight " : 0.15,  " weighted " : 13.5 }
  },
   " criticalIssues " : 0,
   " penalty " : 0,
   " issues " : [
    {
       " axis " :  " accessibility " ,
       " severity " :  " warning " ,
       " message " :  " semantic label missing " ,
       " file " :  " lib/src/presentation/widget/author_card.dart " ,
       " line " : 42
    }
  ]
}

Baseline Ratchet — read before scoring, compare before writing#

Five rules. The first two exist because the previous flow saved at step 3 and compared at step 4, so every run compared the current score against itself and reported a 0 delta.

#RuleWhy
1Load first. The previous baseline is read at the start of the run, before any axis is scored.A comparison target read after the write is the current run.
2Never write before comparing. The append happens after the regression report is emitted, never before.Ordering is what makes the delta real; see the Q1 → … → Q8 → Q9 → Q10 chain in Convergence.
3Monotone. A total lower than the current baseline does not overwrite it — it is appended as "kind": "regression" and the baseline line stays where it is.The baseline is a high-water mark. A ratchet that slides down ratifies every regression it records.
4No partial baselines. partial: true (--quick / --focus) or a non-empty unverified list ⇒ no baseline/regression line. A requested partial run appends nothing; an unverified run appends one kind: "unverified" audit line (the gap must be durable — the console is not).A 1-axis score is not comparable to a 5-axis one; storing it would silently redefine the baseline. A gap that leaves no trace is the silent skip form (§3.2).
5Append-only. Existing lines are immutable; a correction is a new line, and --no-save means "append no score line", never "edit" (--no-save cannot suppress the unverified audit line — that line is a gap record, not a score).Same contract as the TMR ledger — an overwritten history cannot show when quality turned.

Ratchet outcomes:

RunCurrent baseline totalAppended lineNew baseline
full, 8885kind: baseline, totalScore: 8888
full, 8485kind: regression, totalScore: 84, vsBaseline: -185 (unchanged)
full, 8585kind: baseline (tie refreshes the date, not the score)85
--quick, 9185— (nothing)85
a11y ⚠️ UNVERIFIED85kind: unverified, unverified: ["accessibility"], no totalScore85 (unchanged)

This ratchet is not a loop — it neither iterates nor rolls anything back, so it carries no 7-field loop contract (plugins/cc-dev/rules/orchestration-graph.md §2). One run appends at most one line.

Regression Comparison#

When using the --regression option, compares against the baseline loaded before scoring (Ratchet rule 1) — not against a line this run wrote (rule 2). If the ledger has no kind: baseline line yet, the report prints no baseline yet per axis; it does not treat a missing baseline as 0 (that would report every axis as a large improvement):

## Regression Report: auth

| Axis | Previous | Current | Change |
|------|----------|---------|--------|
| Functional Completeness | 90 | 92 |+2 |
| UI/UX | 80 | 80 | ➡️ 0 |
| Accessibility | 75 | 70 | ⚠️ -5 |
| Performance | 85 | 88 |+3 |
| i18n | 90 | 90 | ➡️ 0 |
| **Total** | **85 (B)** | **84 (B)** | ⚠️ -1 |

### Regression Items (score decrease)
-Accessibility -5 points: semanticLabel missing on newly added ProfileImage

Output Format#

╔════════════════════════════════════════════════════════════════╗
║  Feature QA Health Score: auth                                ║
╠════════════════════════════════════════════════════════════════╣
║                                                                ║
║  Grade: B (85/100) — advisory (blocks nothing)                 ║
║                                                                ║
║  🧪 Functional     ████████████████████░░  90/100 (×0.30 = 27.0) ║
║  🎨 UI/UX          ████████████████░░░░░░  80/100 (×0.20 = 16.0) ║
║  ♿ Accessibility   ███████████████░░░░░░░  75/100 (×0.20 = 15.0) ║
║  ⚡ Performance     ████████████████░░░░░░  85/100 (×0.15 = 12.8) ║
║  🌐 i18n           ████████████████████░░  90/100 (×0.15 = 13.5) ║
║                                                                ║
║  Critical: 0 items | Penalty: 0 points                         ║
║  Axes: 5/5 reported (UNVERIFIED: none)                         ║
║  Baseline: appended (.qa-baseline/auth.jsonl · kind:baseline)  ║
║                                                                ║
╚════════════════════════════════════════════════════════════════╝

### Issues Found (5 items)

| # | Axis | Severity | Issue | File |
|---|------|----------|-------|------|
| 1 |Accessibility | ⚠️ | semantic label missing | author_card.dart:42 |
| 2 |Performance | ⚠️ | cacheWidth not specified | author_image.dart:15 |
| 3 | 🌐 i18n | 💡 | hardcoded string | author_list_page.dart:28 |
| 4 | 🎨 UI/UX | 💡 | SizedBox used instead of Gap | author_form.dart:55 |
| 5 | 🧪 Functional | 💡 | EmptyState not handled | author_list_bloc.dart:30 |

The Axes: and Baseline: lines are mandatory — they are how a reader tells a measurement from a gap. The two non-grading states print instead of a grade:

  • missing axis → Grade: — (not computed) · Axes: 4/5 reported (UNVERIFIED: performance) and Baseline: not written (unverified axis · kind:unverified audit line appended)
  • partial by request → Grade: — (partial run: --quick) · Axes: 1/5 reported and Baseline: not written (partial run)

Per-axis rows still print in both cases, with the unverified axis shown as ⚠️ UNVERIFIED, never as 0/100.


Parallel Evaluation (--parallel, Agent Teams)#

Prerequisites#

  • CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 environment variable set
  • Availability is probed by ../../cc-product/references/helpers.md#Check-Agent-Teams-Available (the env-var check that returns teams_available) — that helper, not this file, is the probe. The path must stay explicit and relative: a bare helpers.md#… resolves to nothing from this directory, which left teams_available undefined and the fallback trigger with it — the exact cross-plugin debt recorded in ../../cc-dev/skills/agent-teams/SKILL.md
  • If Agent Teams unavailable, automatically falls back to sequential evaluation. Same 5 axes, same scoring rules, only slower — the Fallback Principle in ../../cc-dev/skills/agent-teams/SKILL.md

Access and Effort Tier (3 teammates)#

All three teammates are read-only: they read {feature_path}/** plus the issue AC and return scores + findings. None of them writes a file, and none runs git checkout/commit (Agent Teams share one worktree — plugins/cc-dev/rules/orchestration-graph.md §4). Every write in this command is the Lead's, so the ledger has exactly one writer.

NodeAssignmentAccesstier:Why this tier
Teammate 1Functional Completeness (30%)read-only ({feature_path}/**, issue AC)standardAC↔code mapping and coverage judgement are implementation-level reasoning
Teammate 2UI/UX (20%) + Accessibility (20%)read-only ({feature_path}/**)standardcontrast/layout/reading-order are judgement calls, not greps
Teammate 3Performance (15%) + i18n (15%)read-only ({feature_path}/**)frugalpattern detection (const, buildWhen, cacheWidth, context.t.*) — a mechanical assignment
Leadmerge, weighting, grade, ledger appendwrite.qa-baseline/{feature-name}.jsonl only (own:lead)standardcomposes the verdict; the shared/generated file is Lead-only

frontier is deliberately unused: no teammate's own verdict gates anything here (this command is advisory), which is the stated condition for frontier in ../../cc-product/references/helpers.md#Spawn-BMAD-Teammate. tier: is never left unset — at a degree-3 fan-out an unset tier is a 3× multiplier, not a default (plugins/cc-dev/rules/orchestration-graph.md §5).

Parallel Execution Flow#

Derived view (non-normative, plugins/cc-dev/rules/orchestration-graph.md §6) — Convergence wins where they disagree.

When --parallel option is used:

1. Check Agent Teams availability
   ../../cc-product/references/helpers.md#Check-Agent-Teams-AvailableFalls back to sequential evaluation if teams_available !== true

2. 3-team parallel evaluation (3 Teammates, read-only)

   ├─ Teammate 1: Functional Completeness (30%)
   │   ├─ Acceptance Criteria met
   │   ├─ Edge case handling
   │   ├─ Error handling
   │   └─ Test coverage
   │
   ├─ Teammate 2: UI/UX (20%) + Accessibility (20%)
   │   ├─ Correct CoUI component usage
   │   ├─ Layout/color/typography consistency
   │   ├─ Semantic Labels
   │   ├─ Touch target size
   │   └─ Color contrast
   │
   └─ Teammate 3: Performance (15%) + i18n (15%)
       ├─ const widgets / BlocSignalBuilder optimization
       ├─ Image optimization / resource cleanup
       ├─ Translation key usage
       └─ Hardcoded string detection

3. Lead merges results (barrier — all 3 must have reported)
   ├─ ../../cc-product/references/helpers.md#Collect-Team-Results
   ├─ Axis roll-call: 5/5 reported? a missing axis is ⚠️ UNVERIFIED, never 0
   │   → if any axis is missing: no total, no grade, no ledger append
   ├─ Sum each axis score and apply weights
   ├─ Critical issue count x 10 point penalty
   └─ Final grade calculation (A~F), advisory

Fallback (Sequential Evaluation)#

If Agent Teams unavailable, runs 5-axis sequential evaluation as before — the correct behaviour, and the same one every other substrate degrades to. The fallback is recorded durably (substrate field of the ledger entry + the report header), so a run that scored sequentially is never mistaken for a 3-teammate run. Sequential runs are subject to the identical roll-call: 5 axes or no grade.


Convergence#

The substrate is unchanged: Agent Teams, 3 read-only teammates, width 3, with the existing sequential fallback. Nothing here adds fan-out — it names the ownership, the tiers and the two places the run must stop. The barrier before grading is genuinely required: the total is a weighted sum over all 5 axes, so it cannot be judged axis-by-axis and must be computed once over the merged set (plugins/cc-dev/rules/orchestration-graph.md §4.2 lists this command's 5-axis weighted grade as a justified barrier). It is a barrier, not a pipeline, and it stays one.

Normative declaration of this command's flow. The step lists, parameter tables, the ASCII execution flow and the output templates above are derived views (§6) — where they disagree, this block wins. Notation, gate tri-state, barrier justification, loop contract and fan-out obligations follow plugins/cc-dev/rules/orchestration-graph.md (§1, §3, §4.2, §2, §5).

Q1     ACT   arg normalize — --quick|--focus ⇒ partial=true--no-save
Q2     ACT   load previous baseline BEFORE scoring    reads:.qa-baseline/{feature}.jsonl (last kind:baseline)
Q3     ACT   Agent Teams probe → teams_available      reads:../../cc-product/references/helpers.md#Check-Agent-Teams-Available
Q4     FORK  3-teammate axis split (--parallel)       width:3  own:none  # all 3 read-only; every write is the Lead ' s
Q4.1   ACT   T1 Functional (30%)                      reads:{feature_path}/**  tier:standard
Q4.2   ACT   T2 UI/UX + Accessibility (20+20%)        reads:{feature_path}/**  tier:standard
Q4.3   ACT   T3 Performance + i18n (15+15%)           reads:{feature_path}/**  tier:frugal
Q4s    ACT   sequential 5-axis evaluation (fallback)  reads:{feature_path}/**  tier:standard
Q5     LOOP  axis roll-call (collect 5 axes)          contract:L-feature-qa.five-axis-rollcall
Q6     JOIN  weighted sum                             mode:barrier  because:the total is a weighted sum over all 5 axes — no axis can be judged alone (§4.2)
Q7     GATE  all 5 axes reported                      verdict:reportedAxes===5  & &   unverifiedAxes===0  undet:fail  fail:Q5
Q8     ACT   total + grade — withheld if unverifiedAxes > 0  writes:stdout
Q9     ACT   regression report vs the Q2 entry (--regression)  writes:stdout
Q10    GATE  baseline/regression append eligibility   verdict:!partial  & &   unverifiedAxes===0  & &   !--no-save  undet:fail  fail:HALT
Q11    ACT   append 1 line (kind:baseline|regression) own:lead  writes:.qa-baseline/{feature}.jsonl

Q1 -- >   Q2 -- >   Q3 -- >   Q4
Q4 -- >   Q4.1
Q4 -- >   Q4.2
Q4 -- >   Q4.3
Q4.1 -- >   Q5
Q4.2 -- >   Q5
Q4.3 -- >   Q5
Q4s  -- >   Q5
Q5 -- >   Q6 -- >   Q7 -- >   Q8 -- >   Q9 -- >   Q10 -- >   Q11
Q4 ~~ >   Q4s  on:teams_available!==true  record:report header  " Substrate: sequential "   + ledger substrate: " sequential " 
 Q7 == >   Q5   on:unverifiedAxes > 0        bound:1  invalidates:Q6
Q7 ~~ >   Q8   on:roll-call exhausted (unverifiedAxes > 0 after the one re-request)  record:.qa-baseline/{feature}.jsonl 1 line kind: " unverified "   + the UNVERIFIED axis list (console is not durable)

Read it as: Q7 is the only gate that can send work back, it sends it back exactly once, and undet:fail means an axis whose state cannot be determined counts as unverified — the tri-state's unknown, never a pass (§3). When the re-request is spent the run does not invent a grade: it takes the ~~> edge, and the gap is written to the ledger as a kind:"unverified" line so a dead teammate leaves a trace instead of a plausible D. Q10 failing is then the ordinary outcome of a partial or unverified run — HALT means "no baseline/regression line is appended", not "the report is lost"; Q8/Q9 have already printed. Q9 standing before Q10/Q11 is Ratchet rule 2: the comparison strictly precedes the write.

L-feature-qa.five-axis-rollcall#

inv:      each of the 5 axes holds exactly one row with BOTH a score and its evidence, checked on
          iteration entry and exit; a row with no evidence is ⚠️ UNVERIFIED, never a 0 and never a pass
prog:     reported = axes carrying a score, strictly increasing 05
          no-prog: never re-request an axis whose teammate returned nothing twice — mark it
          UNVERIFIED and stop spending the budget on it
term:     reported === 5 || every remaining axis is marked ⚠️ UNVERIFIED
budget:   1 collection pass + at most 1 re-request per missing axis (= the bound:1 on Q7 == >   Q5).
          `--quick`/`--focus` reduce the axis COUNT by request → the unrun axes are UNVERIFIED and
          partial=true, they are not re-requested
exhaust:  remaining axes stay ⚠️ UNVERIFIED → no total, no grade, no baseline/regression line
          (Q10 fails); one `kind: " unverified " ` audit line records the gap. Promoting an unverified
          axis to 0 or to ✅ is prohibited. No interactive AskUserQuestion — a teammate session has
          nobody to ask (§2)
resume:   the axis roll-call table is the position record; axes already holding score+evidence are not
          re-requested, and re-running an axis is idempotent (read-only, same findings merge by
          `file:line + axis`)
log:      one line per axis, e.g.  " a11y: 75/100 (4 items, evidence: 12 widgets read) " , plus the
          UNVERIFIED list; a 0 is printed with the evidence that produced it (claimed 0 ≠ verified 0)

Automation#

# Run tests (for coverage verification)
melos run test:with-html-coverage -- --scope= " *auth* " 

 # Static analysis
melos run analyze -- --scope= " *auth* " 

 # Code review (detailed)
/cc-quality:review feature/auth/ --gate-mode

  • /cc-quality:review - 9-category code review (count and list SoT: review.md## Review Categories; do not restate the number here) — this is the blocking pre-PR gate in --gate-mode, not feature-qa
  • /cc-quality:checklist:feature-complete - Feature complete checklist
  • /cc-dev:run - Full development cycle. Its Step 8.7 gate runs /cc-quality:review; it does not read this command's grade