/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가 됩니다 — 반쪽 점수는 기준점이 될 수 없습니다. 이때는 항목별 점수와 문제 목록만 나오고 총점·등급은 나오지 않습니다.
안에서 무슨 일이 벌어지나요#
- 채점하기 전에 지난 기준점을 먼저 읽습니다 — 비교 대상을 손에 쥔 상태로 시작합니다.
- 지정한 기능을 5개 관점으로 채점합니다 — 기능 완성도, 화면/UX, 접근성, 성능, 다국어.
- 5개 항목이 전부 도착했는지 확인합니다. 빠진 항목이 있으면 그 항목은 확인 못 함(UNVERIFIED)으로 적고, 총점·등급 계산과 기준점 저장을 하지 않습니다 — 대신 "확인 못 함" 기록만 대장에 한 줄 남겨 빠진 사실이 묻히지 않게 합니다.
- 각 항목 점수에 비중을 곱해 더하고, 심각한 문제 1건당 10점씩 깎아 총점과 등급(A~F) 을 계산합니다.
--regression을 쓰면 1번에서 읽은 지난 기준점과 비교해 항목별 증감 리포트를 만들어 줍니다.-
비교를 끝낸 뒤에
.qa-baseline/{기능이름}.jsonl대장에 한 줄을 덧붙입니다 — 총점이 올랐으면 새 기준점, 내려갔으면 회귀 기록입니다. 부분 실행(--quick·--focus)이면 아무것도 쓰지 않습니다. -
--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-qaoverplugins/returns docs and index rows only). The blocking pre-PR gates live elsewhere —plugins/cc-dev/commands/run.mdStep 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 aGATEat that call site; a gate that only one document believes in is the failure modeplugins/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/ --parallelParameters#
| Parameter | Required | Description | Example |
|---|---|---|---|
feature_path | ✅ | Feature module path | feature/auth/ |
--regression | ❌ | Compare against previous baseline | |
--quick | ❌ | Quick check (functional completeness only). Partial run → implies --no-save, emits no total/grade | |
--focus | ❌ | Focus on specific axes. Partial run → implies --no-save, emits no total/grade | ui, a11y, perf, i18n, func |
--parallel | ❌ | 3-teammate evaluation of the 5 axes (requires Agent Teams; read-only teammates) | |
--save-baseline | ❌ | Save baseline (default: auto-save on a full 5-axis run) | |
--no-save | ❌ | Do 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%#
| Item | Points | Inspection Method |
|---|---|---|
| Acceptance Criteria met | 30 | Map issue AC to implementation code |
| Edge case handling | 25 | Check null/empty/boundary handling |
| Error handling | 25 | try-catch, ErrorState, user messages |
| Test coverage | 20 | UseCase 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%#
| Item | Points | Inspection Method |
|---|---|---|
| Correct CoUI component usage | 30 | Verify component API compliance |
| Layout consistency | 25 | Gap/Insets constants used, alignment |
| Color system compliance | 25 | appColors/colorScheme used |
| Typography compliance | 20 | context.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%#
| Item | Points | Inspection Method |
|---|---|---|
| Semantic Labels | 35 | Labels present on images, icons, buttons |
| Touch target size | 30 | Minimum 48x48 verified |
| Color contrast | 20 | WCAG AA criteria |
| Screen reader order | 15 | Logical 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%#
| Item | Points | Inspection Method |
|---|---|---|
| const widget usage | 30 | Check const-eligible widgets |
| BlocSignalBuilder optimization | 25 | buildWhen/listenWhen usage |
| Image optimization | 25 | cacheWidth/cacheHeight applied |
| Resource cleanup | 20 | dispose/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%#
| Item | Points | Inspection Method |
|---|---|---|
| Translation key usage | 40 | context.t.* pattern used |
| No hardcoded strings | 30 | UI text hardcoding detection |
| Pluralization handling | 15 | plural/ordinal applied |
| Dynamic value parameterization | 15 | Parameters 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 100Grading 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 state | Total / grade | Ledger |
|---|---|---|
| 5 axes reported | computed | one line appended (kind: baseline or kind: regression) |
any axis ⚠️ UNVERIFIED (dead teammate, tool absent, error) | not computed — print the per-axis table + the UNVERIFIED list | one kind: unverified audit line — never a baseline, never a regression |
partial by request (--quick, --focus) | not computed — axis scores only | nothing 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#
| Grade | Score Range | Meaning |
|---|---|---|
| A | 90-100 | Excellent — production ready |
| B | 80-89 | Good — minor improvements needed |
| C | 70-79 | Average — improvements recommended |
| D | 60-69 | Insufficient — improvements required |
| F | 0-59 | Poor — 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.
| # | Rule | Why |
|---|---|---|
| 1 | Load 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. |
| 2 | Never 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. |
| 3 | Monotone. 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. |
| 4 | No 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). |
| 5 | Append-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:
| Run | Current baseline total | Appended line | New baseline |
|---|---|---|---|
| full, 88 | 85 | kind: baseline, totalScore: 88 | 88 |
| full, 84 | 85 | kind: regression, totalScore: 84, vsBaseline: -1 | 85 (unchanged) |
| full, 85 | 85 | kind: baseline (tie refreshes the date, not the score) | 85 |
--quick, 91 | 85 | — (nothing) | 85 |
a11y ⚠️ UNVERIFIED | 85 | kind: unverified, unverified: ["accessibility"], no totalScore | 85 (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 ProfileImageOutput 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)andBaseline: not written (unverified axis · kind:unverified audit line appended) - partial by request →
Grade: — (partial run: --quick) · Axes: 1/5 reportedandBaseline: 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=1environment variable set- Availability is probed by
../../cc-product/references/helpers.md#Check-Agent-Teams-Available(the env-var check that returnsteams_available) — that helper, not this file, is the probe. The path must stay explicit and relative: a barehelpers.md#…resolves to nothing from this directory, which leftteams_availableundefined 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.
| Node | Assignment | Access | tier: | Why this tier |
|---|---|---|---|---|
| Teammate 1 | Functional Completeness (30%) | read-only ({feature_path}/**, issue AC) | standard | AC↔code mapping and coverage judgement are implementation-level reasoning |
| Teammate 2 | UI/UX (20%) + Accessibility (20%) | read-only ({feature_path}/**) | standard | contrast/layout/reading-order are judgement calls, not greps |
| Teammate 3 | Performance (15%) + i18n (15%) | read-only ({feature_path}/**) | frugal | pattern detection (const, buildWhen, cacheWidth, context.t.*) — a mechanical assignment |
| Lead | merge, weighting, grade, ledger append | write — .qa-baseline/{feature-name}.jsonl only (own:lead) | standard | composes 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-Available
→ Falls 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), advisoryFallback (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 0 → 5
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-modeRelated Commands#
/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, notfeature-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