# Design — QA Template System (다랑이 runtime) > **처방 대상**: F3 (QA 자동 라우팅 누락), F2 (DoD 강제 실패 — QA 측면) ## 원칙 - 다랑이(Evaluator)는 **템플릿 체크리스트** 를 따라 검증한다. - 체크리스트의 모든 필수 항목을 확인하기 전에 `APPROVE` 를 낼 수 없다. - 체크 결과는 구조화된 artifact 로 저장되며 파이프라인이 기계적으로 읽는다. - 사용자 메모리 원칙: **"QA는 항상 철저하게, 작업 단위 작아도 QA 체크리스트는 제한 없음"** ## 스프린트 타입별 템플릿 스프린트 `type` (scaffold / feature / refactor / bugfix / migration / infra) 에 따라 다른 템플릿이 로드됨. ### scaffold 템플릿 ```yaml template: scaffold-v1 required_checks: - id: repo-structure description: 표준 디렉토리 구조(src/, tests/, .plans/) 존재 kind: file_exists - id: tsconfig-strict description: tsconfig.json strict 모드 kind: regex_in_file - id: package-manager-lockfile description: pnpm-lock.yaml 존재 (npm/yarn lock 없음) kind: file_exists - id: gitignore-basics description: .gitignore 에 node_modules, dist, *.env 등 포함 kind: regex_in_file - id: readme-minimum description: README 에 프로젝트명 + 요약 + 실행 방법 kind: manual - id: license-present description: LICENSE 파일 존재 kind: file_exists ``` ### feature 템플릿 ```yaml template: feature-v1 required_checks: - id: tests-added description: 새 기능에 대한 테스트 1개 이상 존재 kind: manual - id: tests-pass description: pnpm test 통과 kind: command_success - id: types-ok description: pnpm tsc --noEmit 통과 kind: command_success - id: no-console-log description: console.* 호출 없음 (pino 사용) kind: regex_absent - id: no-any description: any 타입 신규 도입 없음 (Zod 경계 밖) kind: manual - id: runtime-smoke description: 실제 기동해서 smoke 테스트 통과 kind: command_success - id: error-handling description: 주요 에러 경로에 Result / neverthrow 패턴 적용 kind: manual - id: docs-updated description: README / .plans 에 변경 반영 kind: manual ``` ### bugfix 템플릿 ```yaml template: bugfix-v1 required_checks: - id: regression-test description: 버그를 재현하는 테스트가 추가됨 (수정 전 fail → 수정 후 pass) kind: manual - id: root-cause-documented description: .plans/sprints/SPRINT-NNN.md 에 root cause 기록 kind: regex_in_file - id: no-scope-creep description: 버그 외 리팩터/기능 추가 없음 kind: manual - id: tests-pass description: 전체 테스트 pass kind: command_success ``` ### migration 템플릿 ```yaml template: migration-v1 required_checks: - id: migration-script description: 마이그레이션 스크립트 / SQL 존재 kind: file_exists - id: rollback-plan description: 롤백 계획 문서화됨 kind: regex_in_file - id: dry-run-tested description: dry-run 검증 완료 kind: command_success - id: data-loss-assessment description: 데이터 손실 가능성 평가 완료 kind: manual - id: backup-captured description: 운영 DB 백업 확인 kind: manual ``` ## 체크 종류 (kind) | kind | 자동 검증 | 설명 | |---|---|---| | `file_exists` | ✅ | 경로에 파일 존재 | | `regex_in_file` | ✅ | 파일 내 regex 매칭 | | `regex_absent` | ✅ | 파일/전체에 regex 없음 | | `command_success` | ✅ | 명령어 exit 0 | | `http_status` | ✅ | HTTP 엔드포인트 응답 | | `manual` | ❌ | 다랑이 LLM 이 코드를 읽고 판단 | `manual` 체크는 다랑이가 실제 코드를 읽고 체크박스를 수동으로 채운다. 단, 각 체크는 **근거 링크** (파일:라인) 를 반드시 첨부해야 한다. ## 체크 결과 Artifact ```json { "schemaVersion": "v1", "sprintId": "SPRINT-001", "templateId": "scaffold-v1", "reviewRound": 1, "reviewer": "darang", "startedAt": "2026-04-10T13:00:00Z", "completedAt": "2026-04-10T13:08:32Z", "checks": [ { "id": "repo-structure", "kind": "file_exists", "passed": true, "evidence": "src/, tests/, .plans/ 모두 존재", "duration_ms": 12 }, { "id": "tsconfig-strict", "kind": "regex_in_file", "passed": true, "evidence": "tsconfig.json:5 `\"strict\": true`", "duration_ms": 4 }, { "id": "no-any", "kind": "manual", "passed": false, "evidence": "src/handlers/request.ts:42 — `(req: any)` 발견", "reviewerNote": "Zod schema 를 추가하고 req 타입 구체화 필요", "severity": "major" } ], "verdict": "REQUEST_CHANGES", "unpassedRequired": 1, "summary": { "total": 6, "passed": 5, "failed": 1, "skipped": 0 } } ``` ## Verdict 판정 규칙 ``` verdict = | APPROVE if all required checks passed | REQUEST_CHANGES if any required check failed (severity major+) | APPROVE_WITH_NITS if only minor/nit issues remain | ABORT if prerequisite check failed ``` - **`minor` 이슈만으로는 REQUEST_CHANGES 불가** (기존 하네스 원칙 계승) - **`major` / `critical` 이 하나라도 있으면 REQUEST_CHANGES** - severity 는 다랑이 LLM 의 판단이지만 체크 설명의 강도가 가이드 ## 다랑이 runtime 흐름 ``` orchestrator → spawn darang actor │ ▼ ┌───────────────────┐ │ load contract │ (Sprint Contract 에서 type 확인) │ load template │ (type → qa-template) └─────────┬─────────┘ │ ▼ ┌───────────────────┐ │ automated checks │ (file_exists, command_success, etc.) └─────────┬─────────┘ │ ▼ ┌───────────────────┐ │ manual checks │ (다랑이 LLM 이 코드 읽고 판단) └─────────┬─────────┘ │ ▼ ┌───────────────────┐ │ verdict 집계 │ │ artifact 저장 │ └─────────┬─────────┘ │ ▼ structured HandoffMessage 반환 ``` ## 체크리스트 커스터마이즈 프로젝트별 추가 체크를 `qa-extra.yaml` 로 정의 가능: ```yaml # .rails/qa-extra.yaml extends: feature-v1 additional_checks: - id: korean-ui-strings description: 사용자 노출 문자열이 한국어인지 확인 kind: manual severity: major ``` orchestrator 가 `base + extra` 를 merge 해서 다랑이에게 전달. ## 참고 - `principles.md` 원칙 5 - `failure-audit.md` F3, F2 - 사용자 메모리: `feedback_qa_thorough.md` (철저한 QA) - `sprint-contract.md` — `manual_checklist` kind 와 연결