Skip to content

feat(auditors): add au6-parity auditor to enforce Preview-Driven Development (mockup↔implementation parity) #24

Description

@sgwannabe

TL;DR

Preview Forge의 핵심 가치 제안은 Preview-Driven Development (PDD) — advocate가 만든 mockup.html이 구현의 계약으로 작동하는 흐름. 그러나 현재 플러그인은 이 계약을 빌드 이후 어느 단계에서도 검증하지 않음. 5 Judges + 5 Auditors 전원이 artifact/liveness만 확인하고 mockup은 참조하지 않아, killer feature가 stub 상태여도 FROZEN 선언이 가능.

본 이슈는 6번째 Auditor au6-parity를 추가해 mockup↔implementation parity를 binary verdict(PASS/FAIL)로 강제. 기존 "1 auditor라도 FAIL이면 SCC 재수정" 루프에 자연스럽게 편입. Freeze 게이트: 5/5 → 6/6 Auditor PASS.


발견 계기 (증거)

테스트 run r-20260423-222713:

  • 아이디어: "당뇨환자를 위한 식단 관리 서비스"
  • Gate H1에서 P01 (snap-photo meal logger) 선택 — killer_feature: AI photo → carb/GI estimate
  • Engineering 완료 후 5 Judges (spec 88 / tests 78 / sec 82 / build 84 / demo 90) + 5 Auditors (license 95 / bundle 80 / perf 72 / sec 78 / a11y 85) 전원 PASSscore/report.json.freeze_eligible: true.frozen-hash 생성

하지만 apps/web/src/app/page.tsx:97:

photoBase64: note ? undefined : `stub-${Date.now()}`,

실제 카메라/파일 입력 UI 부재. 사용자가 UI 실행해보고 "사진 찍는게 있냐?" 한 번 물으니 즉시 붕괴.


근본 원인

  1. agents/judges/*.md 5개 전부 입력에 mockups/ 경로 없음 — j5-demo-readiness 채점 공식도 "health 200이면 30, seed data 존재하면 20, screenshot 성공이면 50"으로 liveness만 측정. killer feature 작동 여부 무관.
  2. agents/auditors/au1~5.md 5개 전부 mockup 미참조
  3. methodology/global.md Rule 9 (idea-drift-detector): SPEC.md + openapi.yaml 텍스트 토큰 containment만 검사. frontend stub은 openapi 스펙과 무관하게 통과 가능
  4. schemas/score-report.schema.json 하드코딩: required: [AU1..AU5], freeze_eligible description: "total>=499 AND all 5 auditors PASS"
  5. PDD가 Stage 13(ideation)에만 존재하고 Stage 57(engineering/test/freeze)엔 feedback loop 없음 → mockup이 심미적 장식으로 전락

설계 결정 (사용자 사전 승인)

질문 결정
일정 충분 — 정석 설계
parity 엄격도 의도 표현: killer_feature만 strict, 주변 요소 유연
디자인 검증 톤앤매너 + 컴포넌트 구조/레이아웃 "어느정도" (픽셀 diff 아님)
실패 처리 freeze 차단 + SCC 자동 재수정 (max_iter 초과 시 M3 escalate)
도구 Playwright, npx -y playwright@<pinned>

설계

au6-parity 동작 (Stage 7에서 au1~5와 병렬 dispatch)

입력

  • runs/<id>/chosen_preview.json
    • tuple.killer_feature, scope_v1.in, mockup_source
    • 신규 parity_contract 필드 — Gate H1 close 시 M3가 emit (아래 스키마 참조)
  • runs/<id>/mockups/<chosen>.html
  • runs/<id>/design-approved.jsoncolor_tokens, typography
  • 실행 중인 docker-compose web URL (agent가 docker compose ps로 탐지)

프로세스

1. 기능 검증 (strict — parity_contract.killer_interactions만)

각 interaction에 대해 Playwright page.route() 네트워크 인터셉트 + 실제 클릭/입력 실행:

  • ui.role + ui.text_hint로 locator 성공 (UI 요소 존재)
  • 인터랙션 후 assertion.network_request.path_glob 매칭 요청 발생 (same-origin, 2xx 응답, body >20 bytes)
  • 응답 body에 금지 문자열 부재: stub-, mock-, placeholder, TODO, LOREM
  • 응답 shape가 assertion.response.shape_required 키 포함

핵심: 문자열 스캔만으로는 약함. page.route() 네트워크 캡처가 "하드코딩된 클라이언트-사이드 가짜 데이터" 같은 은밀한 실패까지 잡음.

2. 디자인 검증 (lenient — 5차원 중 3 통과, accent 필수)

420×800 viewport로 mockup.html과 running app 둘 다 렌더 후 getComputedStyle 비교:

차원 추출 기준 가중치
Accent color :root --accent 또는 primary CTA bg design-approved.color_tokens.accent 일치 필수
Background body computed bg color_tokens.bg 일치 선택
Font family body computed font-family 첫 토큰 typography.family 첫 토큰 일치 선택
Section 수 main section, .card, article 카운트 mockup ±2 선택
Interactive density button + input + a 비율 0.5~2.0 선택

3/5 통과 (accent 필수 포함) → 디자인 PASS. (4/5는 Tailwind rgb() vs hex, 시스템 폰트 fallback 등 노이즈로 false FAIL 많음)

3. 판정

  • 기능 FAIL → au6 FAIL
  • 기능 PASS + 디자인 3/5 이상 (accent 포함) → PASS
  • 기능 PASS + 디자인 3/5 미만 → FAIL

4. 출력: runs/<id>/audit/au6-report.json (기존 auditor schema 준수)

{
  "auditor_id": "au6",
  "category": "Parity Auditor",
  "verdict": "FAIL",
  "findings": [
    {
      "severity": "critical",
      "path": "apps/web/src/app/page.tsx:97",
      "description": "killer_interaction 'take or upload meal photo': UI element not found (no <input type=file> or camera button matching text_hint)",
      "fix_hint": "Add file input bound to photo capture flow: <input type='file' accept='image/*' capture='environment'>"
    },
    {
      "severity": "high",
      "path": "network",
      "description": "killer_interaction response contained 'stub-' literal",
      "fix_hint": "Backend ML stub correct; frontend is sending placeholder. Replace page.tsx:97 photoBase64 placeholder with actual file content"
    }
  ],
  "evidence": [
    "runs/<id>/audit/au6-parity.mjs",
    "runs/<id>/audit/au6-mockup-screenshot.png",
    "runs/<id>/audit/au6-app-screenshot.png",
    "runs/<id>/audit/au6-network.har"
  ]
}

Playwright 실행 방식 (helper 스크립트 없음)

au6 agent가 자체 Bash tool로:

  1. runs/<id>/audit/au6-parity.mjs 임시 Playwright 스크립트 작성 (chosen_preview.parity_contract 기반 templating)
  2. npx -y playwright@<pinned-ver> install chromium
  3. node runs/<id>/audit/au6-parity.mjs
  4. 결과를 au6-report.json에 write

왜 helper 안 만드나: bin/엔 bash 스크립트만, scripts/ 디렉토리 없음, 기존 mjs/js 전례 없음. au6만 Node-specific 도구를 쓰므로 에이전트 자체 로직 포함이 플러그인 배포 단순성 유지.

중복 설치 방지: M3 Dev PM이 Stage 7 진입 직전 npx playwright@<ver> install chromium --with-deps 한 번 실행 → au3-performance · au6 · qa-e2e가 같은 npm 캐시 공유.

Open question: 핀할 Playwright 버전 결정 — 2026-04 시점 안정 버전 확인 후 고정 (예: 1.47.0 후보, 구현자가 재검토 권장)

parity_contract — Gate H1에서 M3가 생성 (결정론 확보)

핵심 이유: au6가 매 실행마다 killer_feature 산문으로부터 interactions를 LLM-추론하면 비결정적 — 같은 코드에 다른 FAIL 사유 발생. Gate H1 close 시점에 한 번 계산해 chosen_preview.json에 고정 → cache + replay 가능.

스키마:

{
  "parity_contract": {
    "version": 1,
    "generated_at": "2026-04-23",
    "killer_interactions": [
      {
        "name": "take or upload meal photo for carb estimate",
        "ui": {
          "role": "button|input[type=file]",
          "text_hint": "사진|카메라|photo|upload|capture"
        },
        "action": "filechooser|click",
        "assertion": {
          "network_request": {
            "origin": "same",
            "path_glob": "/api/meals*",
            "method": "POST"
          },
          "response": {
            "status_2xx": true,
            "shape_required": ["carbGrams|value", "confidence"]
          }
        }
      }
    ],
    "design_weights": {
      "accent_color_required": true,
      "pass_threshold": 3
    }
  }
}

M3가 chosen_preview.tuple.killer_feature + scope_v1.in + mockup DOM inspection 결과 결합해 생성. 기존 chosen_preview.json write 플로우 안에 한 블록 추가.

SCC 동작 (au6 FAIL 시)

agents/scc/scc-lead.md 분류 테이블에 새 행:

카테고리 트리거 Fixer Plateau 처리
parity_violation au6-parity FAIL scc-frontend + scc-backend (parity는 fetch + render 양쪽 걸침) 2-iter plateau → M3 즉시 escalate (일반 3-iter 아님), auto_extend: false

auto_extend: false: frontend가 stub 문자열만 제거하고 fetch 경로는 여전히 unreachable → au6 FAIL 사유가 "stub found"에서 "locator found but no network request"로 변형. 에러 "감소"로 보이지만 실은 다른 실패 패턴. 자동 연장 위험.

M3 escalation 시 AskUserQuestion 3옵션:

  1. "mockup 단순화해서 현재 구현에 맞추기" (사용자 기대치 조정)
  2. "killer_feature 범위 재정의" (Gate H1 /pf:design 재진입)
  3. "run abort + LESSONS 기록" (설계 수준 미스매치, 코드로 해결 불가)

변경되는 파일 (10개)

신규 (1)

  • agents/auditors/au6-parity.md
    • 기존 au1~5 패턴: ## Layer-0 / ## 역할 / ## 감사 범위 / ## 사용 도구 / ## 출력 / ## Freeze 결정 규칙 / ## 모델 설정 / ## allowed_scope / ## 보고선
    • YAML frontmatter: name: auditor-parity, tools: Read, Write, Bash, model: opus
    • 출력: runs/<id>/audit/au6-report.json
    • allowed_scope.Write: runs/<id>/audit/au6-*.{json,mjs,png,har} (기존 au<N>-report.json보다 확장 — Playwright 임시 스크립트 + screenshot 증거 + HAR 저장 필요)
    • 본문에 Playwright 실행 절차, parity_contract 읽기 로직, 판정 규칙 상세

수정 (9)

  • schemas/score-report.schema.json

    • auditors.required: [AU1..AU5][AU1..AU6]
    • auditors.propertiesAU6: {$ref: "#/definitions/auditorVerdict"} 추가
    • freeze_eligible.description: "all 5 auditors PASS""all 6 auditors PASS"
    • total은 변경 없음 (judge 수 5 유지, 500점 만점)
    • description 헤더에 "(+ 6 Auditors binary veto)" 보조 문구 추가
  • agents/meta/chief-engineer-pm.md — M3 Dev PM (실제 auditor dispatcher)

    • L27: "5 Judges + 5 Auditors""5 Judges + 6 Auditors"
    • L140: "Auditors: AU1–AU5""Auditors: AU1–AU6"
    • L119 Write scope: chosen_preview.jsonparity_contract 필드 write 권한 (실제론 /pf:design 슬래시커맨드의 supervisor role로 write)
    • 섹션 3 "Gate H1 관장" 절차 7에 step 추가: 7a. chosen_preview.jsonparity_contract emit — killer_feature + scope_v1.in + mockup DOM inspection 기반
    • "TestDD 시작 전" standup 직전: Playwright chromium 사전 설치 (npx playwright@<ver> install chromium --with-deps) — au3/au6/qa-e2e 중복 설치 방지
  • agents/scc/scc-lead.md ⚠️ 충돌 가능성 있음 (아래 Coordination 참조)

    • 분류 테이블에 parity_violation 행 추가
    • auto_extend 플래그 명시: parity_violation만 false
    • "모든 5명 Auditor PASS" → "모든 6명 Auditor PASS" (전체 치환)
    • Escalation 3옵션 AskUserQuestion 포맷 명시
  • methodology/global.md

    • 헤더 "7개 비협상 규칙" → "10개 비협상 규칙" (현재 stale, Rule 9까지 존재)
    • Rule 10 추가:
      ### Rule 10 — Mockup–Implementation Parity (v1.6.0+)
      
      `chosen_preview.json`의 `killer_feature`는 구현체에서 **실제로 동작해야**
      freeze 가능. 판정: `au6-parity` PASS 필수.
      
      Strict 대상: `parity_contract.killer_interactions` (1~3개).
        - UI element 존재 + 인터랙션 → 실제 same-origin 네트워크 요청 발생
        - 응답에 금지 문자열(`stub-|mock-|placeholder|TODO|LOREM`) 부재
        - 응답 shape가 `scope_v1.in` 기대 필드 포함
      
      Lenient 대상: 디자인 톤 (color/font/structure/density 5차원 중 3 통과,
      accent color 필수 포함).
      
      **강제**: au6-parity가 `runs/<id>/audit/au6-report.json`에 `verdict: FAIL`
      쓰면 `freeze_eligible: false` 자동. SCC `parity_violation` 재수정 루프,
      2-iter plateau 시 M3 즉시 escalate.
      
      **이유**: PDD(Preview-Driven Development)가 본 플러그인의 고유 가치.
      mockup이 구현과 괴리된 채 FROZEN 선언되면 플러그인의 신뢰 신호가 무너짐.
      
      **우회 경로**: 없음. killer_feature 범위 자체를 줄이려면 Gate H1 재진입
      (/pf:design) — 즉 사용자 명시적 동의만이 우회 경로.
      
  • memory/CLAUDE.md (plugin seed)

    • L27-29: "5 Judge(카테고리별) + 5 Auditor(독립 감사) 이중 검증""5 Judge + 6 Auditor 이중 검증 (au6-parity: PDD parity 강제)"
    • 다른 "5 Auditor" 언급 sweep
  • commands/freeze.md

    • "5/5 Auditor PASS""6/6 Auditor PASS" (전체 치환)
    • Freeze 불가 사유 목록에 "au6-parity FAIL" 명시 추가
  • agents/meta/run-supervisor.md (optional but recommended)

    • Blackboard key 열거에 audit.au6.* 이벤트 패턴 추가 (M1이 Stage 7 완료 모니터링 시)
    • Stage 7 병렬 dispatch 목록에 au6 포함
  • CHANGELOG.md + plugin.json version — 편집하지 않음

    • release-please가 commit message로 자동 처리 (관련 워크플로는 이미 설정됨: release-please--branches--main--components--preview-forge)
    • 커밋 메시지 컨벤션 (하단 참조)
  • Sweep updates (low priority, 누락해도 기능 영향 없음)

    • memory/LESSONS.md (plugin seed) — "5 Auditor" 산문 언급
    • agents/panels/tp-lead.md
    • agents/panels/tp-members/tp08-security-auditor.md
    • agents/engineering/frontend/fe-lead.md
    • agents/engineering/backend/be-lead.md

Coordination (⚠️ 중요)

feat/scc-build-config-fixer 브랜치와의 잠재 충돌

로컬에 feat/scc-build-config-fixer 브랜치가 존재하며 origin에도 push되어 있음. 이 브랜치가 agents/scc/scc-lead.md를 편집하는 경우, 본 이슈의 작업과 병렬 merge 시 충돌 유력.

질문:

  1. feat/scc-build-config-fixeragents/scc/scc-lead.md를 건드리나요?
  2. 건드린다면 — 그 브랜치가 먼저 merge된 후 본 이슈 진행하는 게 맞을지?
  3. 건드리지 않는다면 — 병렬 진행 가능.

편집 순서 권장

  1. 먼저 merge 대기: feat/scc-build-config-fixer + 기타 open 브랜치들 (fix/monitors-* 등)
  2. main 최신화 후 본 이슈의 브랜치 feat/au6-parity-auditor 분기
  3. Phase 1 (MVP) → PR → 리뷰 → merge → release-please 자동 release
  4. Phase 2/3 는 post-MVP 개별 PR

Phase 분할

  • Phase 1 (MVP, 해커톤 데모 가능):
    • 파일 1 (au6-parity.md)
    • 파일 2 (schema)
    • 파일 3 (chief-engineer-pm.md)
    • 파일 5 (Rule 10)
    • 파일 7 (freeze.md)
    • commit message로 release-please 작동
  • Phase 2 (SCC 자동 복구 시연용):
    • 파일 4 (scc-lead.md parity_violation 분류)
    • 파일 6 (memory/CLAUDE.md seed)
    • 파일 8 (run-supervisor.md)
  • Phase 3 (문서 일관성 polish):
    • 파일 10 (sweep updates)

재사용하는 기존 요소

  • Auditor 패턴 (agents/auditors/au1-license.md 등): frontmatter · 섹션 구조 · 출력 schema 그대로
  • Auditor 출력 schema: {auditor_id, category, verdict: PASS|FAIL, findings[{severity, path, description, fix_hint}], evidence} 준수
  • SCC escalation 패턴 (기존 scc-lead.md): 3-plateau → M3 escalate 로직. parity_violation은 2-plateau로 tighten
  • Factory-policy hook (hooks/factory-policy.py): runs/<id>/audit/**RUN_ARTIFACT_PATTERNS에 미포함 → hook 수정 불필요
  • hooks.json: auditor별 특수 hook 없음 — au6도 추가 hook 없이 factory-policy 공통 적용
  • Playwright: qa-e2e가 이미 사용 선언. M3 Stage 7 pre-step에서 한 번 설치, 캐시 공유

Verification

단위 검증

# 1) au6-parity.md YAML frontmatter 유효성
python3 -c "import yaml; yaml.safe_load(open('agents/auditors/au6-parity.md').read().split('---')[1])"

# 2) schema 유효성
python3 -c "import json, jsonschema; s=json.load(open('schemas/score-report.schema.json')); jsonschema.Draft7Validator.check_schema(s); print('OK')"

# 3) Rule 10 추가 확인
grep -c "^### Rule" methodology/global.md  # 10이 나와야 함

End-to-end 검증 (fresh run)

cd /tmp && mkdir pf-test-au6 && cd pf-test-au6
# Claude Code 세션 시작
/pf:new "당뇨환자를 위한 식단 관리 서비스" --profile=pro
# Gate H1에서 photo-heavy preview (예: P01) 선택
# → chosen_preview.json에 parity_contract 필드가 생성되었는지 확인:
cat runs/<new-id>/chosen_preview.json | jq '.parity_contract'

시나리오 A — RED (자연스러운 실패 감지):

  • Engineering에서 자연스럽게 frontend stub 발생
  • Stage 7 완료 후: cat runs/<new-id>/audit/au6-report.json | jq '.verdict'FAIL
  • 자동으로 SCC parity_violation 루프 진입 (runs/<new-id>/blackboard.db에서 scc-lead 호출 증거)

시나리오 B — GREEN (SCC 재수정 후 PASS):

  • SCC가 1~2 iter 내 frontend 고쳐 재검증 → au6 PASS
  • score/report.json.freeze_eligible: true
  • .frozen-hash 생성
  • docker compose up -d 이후 실제 /api/meals POST 네트워크 요청 확인

시나리오 C — Escalation (2-iter plateau):

  • 의도적으로 mockup과 전혀 다른 scope로 유도
  • SCC 2-iter 후 M3 escalate → AskUserQuestion 3옵션 노출 확인

해커톤 데모 시나리오 (1-minute)

동일 idea로 두 run 녹화:

  1. au6 없이 (v1.5.2): FROZEN 선언됐지만 devtools Network 탭 열면 stub- 보임 — false victory 시각화
  2. au6 있음 (v1.6.0): 초기엔 au6 FAIL로 freeze 차단 → SCC 자동 복구 → 2차 검증 PASS — true victory

"au6-parity found what 5 judges missed" 한 줄 카피로 PDD의 가치가 시각적으로 드러남.


Commit message template (release-please용)

feat(auditors): add au6-parity for Preview-Driven Development enforcement

New auditor verifies that `chosen_preview.killer_feature` is actually
wired in the running implementation. Strict functional check on killer
interactions (network-capture based via Playwright page.route()), lenient
design tone check (color/font/structure, 3-of-5 threshold, accent
required). Binary PASS/FAIL; freeze-gating.

Also adds methodology Rule 10 (Mockup–Implementation Parity), extends
SCC with `parity_violation` classification (2-iter plateau → M3
escalate, auto_extend: false), and adds `parity_contract` field to
chosen_preview.json for replay determinism.

BREAKING CHANGE: schemas/score-report.schema.json auditors.required
extended from AU1..AU5 to AU1..AU6. freeze_eligible now requires 6/6
auditor PASS.

Closes #<this-issue-number>

BREAKING CHANGE: footer로 release-please가 minor bump 자동 수행 (예상: 1.5.2 → 1.6.0).


Open questions (구현 중 결정)

  1. Playwright 버전 핀: 2026-04 시점 안정 버전 확인 후 npx playwright@<ver> 고정. @latest는 비결정성 유발.
  2. parity_contract LLM 생성의 안정성: M3가 killer_feature 산문 + scope_v1.in + mockup DOM으로 contract 생성 시, 다른 preview 카테고리 (P02 CGM, P03 clinician, P07 voice-first 등)에서도 robust한지 — 첫 구현 후 9개 preview 대표 샘플로 스모크 테스트 권장. 필요 시 preview 카테고리별 template 제공.
  3. au6 Write scope 확장hooks/factory-policy.py Rule 8 패턴과 충돌 없는지 — RUN_ARTIFACT_PATTERNSruns/<id>/audit/** 명시 없으므로 통과할 것으로 보이나 구현 시 실제 write 시도해 확인.

Labels 제안

  • enhancement — 신규 기능
  • breaking-change — schema break (AU1..AU5 → AU1..AU6)
  • methodology — Rule 10 추가
  • hackathon — 해커톤 데모 가치 직결

References

  • 실패 테스트 run: r-20260423-222713 (외부 user 환경, 재현 방법은 Verification 섹션)
  • 관련 기존 컴포넌트:
    • agents/auditors/au1-license.md (패턴 참조용)
    • agents/meta/chief-engineer-pm.md:27 (5 → 6 Auditors 변경 위치)
    • agents/meta/chief-engineer-pm.md:140 (AU1-AU5 → AU1-AU6)
    • schemas/score-report.schema.json (required 확장)
    • methodology/global.md (Rule 10 추가 위치 — Rule 9 뒤)
    • agents/scc/scc-lead.md (parity_violation 분류 추가 — ⚠️ 충돌 가능)
    • commands/freeze.md (5/5 → 6/6)
    • hooks/factory-policy.py (참고 — 수정 불필요)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions