<!-- task=m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline plan=3 tag=REVIEW_REVIEW_REVIEW_REFACTOR -->

# Code Review Reference - REVIEW_REVIEW_REVIEW_REFACTOR

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-16
task=m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline, plan=3, tag=REVIEW_REVIEW_REVIEW_REFACTOR

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md)
- Task ids:
  - `readability-baseline`: 추적 소스의 파일/함수/read-set 기준선과 재현 가능한 ratchet audit 도입
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- 루프 이력:
  - `plan_local_G07_0.log` / `code_review_local_G07_0.log`: FAIL, Required 4 / Suggested 0 / Nit 0.
  - `plan_local_G09_1.log` / `code_review_local_G09_1.log`: FAIL, Required 5 / Suggested 0 / Nit 0.
  - `plan_cloud_G07_2.log` / `code_review_cloud_G07_2.log`: FAIL, Required 5 / Suggested 0 / Nit 0.
- 현재 아카이브 작업: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_cloud_G07_2.log`
- 현재 아카이브 리뷰: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_cloud_G07_2.log`
- 판정: FAIL
- Required 5 / Suggested 0 / Nit 0
  - `scripts/e2e-*.sh`가 production으로 분류되어 Shell E2E test에 잘못된 LOC 정책이 적용된다.
  - missing/malformed `readability_read_sets.json`이 `None`으로 통과되어 task total ratchet 전체를 우회한다.
  - nested block comment와 Shell multiline quote가 조기 종료되어 함수 LOC와 현재 AWK 문자열의 함수 목록을 오염시킨다.
  - Dart callback `Function`, Shell call expression, multiline type declaration이 false positive 또는 scope 손실을 만든다.
  - Dart/Kotlin/Swift overload가 동일 `scope.name`으로 충돌해 strict baseline과 dedupe 신뢰성을 깨뜨린다.
- 영향 파일: `scripts/readability_audit.py`, `scripts/readability_audit_test.py`, `scripts/readability_baseline.json`, `scripts/readability_read_sets.json`.
- 검증 증거: 기존 unit 76개, worktree/tracked audit, 반복 JSON cmp, changed-existing probe, Make target, `git diff --check`는 PASS했다. 독립 probe는 read-set 우회, supported-language LOC 조기 종료, false callable, overload identity 충돌을 재현했다.
- Roadmap carryover: `readability-baseline` Task는 PASS 전까지 미완료로 유지한다.
- 추가 상세가 필요하면 위 `plan_cloud_G07_2.log`와 `code_review_cloud_G07_2.log`만 좁게 재확인한다.

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md` → `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` → `plan_cloud_G07_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 Milestone 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_REVIEW_REVIEW_REFACTOR-1] 실제 Shell test 진입점 분류 | [x] |
| [REVIEW_REVIEW_REVIEW_REFACTOR-2] required read-set config 우회 차단 | [x] |
| [REVIEW_REVIEW_REVIEW_REFACTOR-3] nested comment와 Shell multiline quote lexer | [x] |
| [REVIEW_REVIEW_REVIEW_REFACTOR-4] callable 선언 필터와 multiline type scope | [x] |
| [REVIEW_REVIEW_REVIEW_REFACTOR-5] overload-safe identity와 baseline 재생성 | [x] |

## 구현 체크리스트

- [x] [REVIEW_REVIEW_REVIEW_REFACTOR-1] 현재 저장소의 Shell E2E/smoke 진입점을 test policy로 분류하고 baseline regression을 없앤다.
- [x] [REVIEW_REVIEW_REVIEW_REFACTOR-2] required read-set JSON의 부재·파손·schema 오류가 task total ratchet을 우회하지 못하고 CLI exit 2로 고정한다.
- [x] [REVIEW_REVIEW_REVIEW_REFACTOR-3] nested block comment와 Shell multiline quote/AWK body를 newline 보존 lexer로 처리하고 함수 LOC 경계를 통과시킨다.
- [x] [REVIEW_REVIEW_REVIEW_REFACTOR-4] Dart callback·Shell call false positive를 제거하고 multiline type scope를 callable identity에 보존한다.
- [x] [REVIEW_REVIEW_REVIEW_REFACTOR-5] Dart/Kotlin/Swift overload에 결정적 signature identity를 부여하고 전체 report/baseline의 identity 중복 0을 검증한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

- 검증 명령은 계획에 명시된 그대로 전부 실행했다. 대체하거나 생략한 명령은 없다.
- REVIEW_REVIEW_REVIEW_REFACTOR-4에서 계획은 `_find_decl_body_start` 보정만 언급했지만, type 선언에는 함수와 다른 종료 규칙(`;` 종료, body 없는 타입 존재)이 필요해 `_find_type_body`를 별도로 분리했다. 함수용 `_find_decl_body`를 타입에 그대로 쓰면 `class Empty` 같은 body 없는 선언이 뒤따르는 다른 선언의 `{`를 자기 body로 오인한다.
- REVIEW_REVIEW_REVIEW_REFACTOR-4의 declaration/body gate를 구현하면서 body 없는 match를 전부 버리면 Dart `=>`, Kotlin `=` 같은 실제 expression body callable까지 사라지므로, `_classify_decl_tail`이 `brace` / `expression` / `none`을 구분하도록 했다. 계획의 "body/form for that language" 요구를 만족시키기 위한 구체화이며 범위 확대는 아니다.
- REVIEW_REVIEW_REVIEW_REFACTOR-5는 계획의 Dart/Kotlin/Swift overload에 더해 Python `@property`/`@x.setter` 쌍도 같은 `scope.name`으로 충돌하는 것을 확인해 동일한 disambiguation을 Python 추출기에도 적용했다. report 전체 identity 중복 0 게이트가 언어를 가리지 않기 때문이다.
- `scripts/readability_read_sets.json`은 schema와 파일 목록이 이미 최종 strict schema에 맞아 내용 변경이 필요 없었다. self read-set total 증가(4536 → 5297)는 계획대로 baseline `task_read_set_totals` 재생성으로 반영했고, budget 2209는 축소 목표값이므로 유지했다.

## 주요 설계 결정

- **Shell test 분류 범위(-1)**: `TEST_PATH_PATTERNS`에 `scripts/e2e-[\w.-]+\.sh`만 넣고 anchored `re.fullmatch`로 대조했다. project rule의 도메인 매핑(`scripts/e2e-*.sh` → testing)과 정확히 일치하는 최소 범위이며, `scripts/dev/**`나 `agent-ops/bin/*.sh` 같은 운영 script는 production을 유지한다.
- **required config 모델(-2)**: 기존 `load_json_file`(baseline 전용, 없으면 exit 3)은 그대로 두고 `load_required_json_file`을 추가해 missing/malformed/non-object를 각각 구분된 진단으로 반환한다. `audit_read_sets`도 `None` 입력을 config 실패로 바꿔, 어느 경로로 들어와도 `None`이 조용히 ratchet을 건너뛸 수 없게 했다.
- **Shell 문자열 상태(-3)**: shell만 newline을 넘는 전용 스캐너(`_blank_shell_string`)를 쓴다. single quote는 escape가 없고 double quote만 escape를 처리하는 shell 실제 규칙을 따랐다. 문자열 밖의 `\'` 같은 escape가 잘못된 문자열을 열지 않도록 top-level escape skip도 함께 넣었다. nested comment는 Dart/Kotlin/Swift에만 적용하고 Go는 첫 `*/`에서 닫히는 실제 언어 규칙을 유지했다(`test_go_block_comment_does_not_nest`로 고정).
- **선언 스캔 기준점(-4)**: `_find_decl_body`/`_declaration_signature` 모두 `match.end() - 1`(선언 자신의 파라미터 목록)에서 스캔을 시작한다. Go receiver clause를 header로 오인하면 multiline signature 함수가 통째로 사라지므로, receiver/generic 목록이 signature와 body 판정에 끼어들지 않게 했다.
- **identity 최소 변경 원칙(-5)**: 유일한 callable은 기존 `scope.name`을 그대로 유지하고, 실제로 충돌한 group만 정규화 signature를 받으며, signature까지 같은 경우에만 start 순서 기반 ordinal(`#1`, `#2`)을 붙인다. 충돌하지 않는 대다수 identity를 rename하지 않아 기존 baseline allowlist의 안정성을 지킨다. `signature`는 identity 계산용 내부 키라 report 방출 전에 제거해 JSON schema를 바꾸지 않는다(`test_extracted_functions_expose_no_internal_keys`).
- **baseline 고정점(-5)**: `scripts/readability_baseline.json`은 자신이 `readability-baseline` read-set의 구성 파일이라 재생성이 자신의 total을 다시 바꾼다. 재생성-측정을 drift가 사라질 때까지 반복해 고정점(5297)에서 확정했고, 이후 반복 실행에서 `RATCHET OK`와 byte-identical JSON을 확인했다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._

- 상태: 없음
- 사유 유형: 없음
- 연결 대상: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- 실제 `scripts/e2e-*.sh`가 test kind로 보고되고 일반 Shell 운영 파일은 production을 유지하는가.
- missing/malformed read-set config가 명시적 exit 2로 종료하고 task total ratchet을 우회하지 못하는가.
- nested comment, Shell multiline quote, heredoc/AWK 문자열이 brace/callable 계측을 오염시키지 않는가.
- Dart callback과 Shell call false positive가 없고 next-line type brace도 scope-qualified identity를 만드는가.
- overload identity, report duplicate gate, baseline violation/task total 정확 일치가 반복 JSON에서 재현되는가.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.

### REVIEW_REVIEW_REVIEW_REFACTOR-1 중간 검증
```
$ python3 -m unittest scripts.readability_audit_test.TestPolicyThresholdsByFileKind
.........
----------------------------------------------------------------------
Ran 9 tests in 0.036s

OK
```
신규 `test_classify_project_shell_test_entrypoints`가 실제 `scripts/e2e-smoke.sh`,
`scripts/e2e-long-context-admission-smoke.sh`의 kind=test와 `FILE_POLICY["test"]` threshold를,
대조 경로 `scripts/dev/edge.sh`, `agent-ops/bin/sync.sh`의 kind=production과
`FILE_POLICY["production"]` threshold를 함께 assert한다. 신규
`test_shell_entrypoint_uses_test_threshold_end_to_end`는 810줄 fixture로 e2e 진입점이
test `warning`, 일반 shell이 production `split_review`가 되는 것을 audit 경로로 확인한다.

### REVIEW_REVIEW_REVIEW_REFACTOR-2 중간 검증
```
$ python3 -m unittest scripts.readability_audit_test.TestTaskReadSetTotalsAndBudget scripts.readability_audit_test.TestCliExitCodes
............................
----------------------------------------------------------------------
Ran 28 tests in 1.069s

OK
```
config 오류는 exit 2, ratchet 위반은 exit 4, 정상 config는 exit 0으로 분리된다. 신규 CLI case:
`test_missing_read_set_config_exits_two`(삭제 → exit 2, `RATCHET OK` 미출력),
`test_malformed_read_set_config_exits_two`(파손 JSON → exit 2),
`test_non_object_read_set_config_root_exits_two`, `test_unknown_read_set_root_key_exits_two`,
그리고 대조군 `test_valid_read_set_config_reaches_total_ratchet`(정상 config → exit 4로 total ratchet 도달).

### REVIEW_REVIEW_REVIEW_REFACTOR-3 중간 검증
```
$ python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity
.....................................
----------------------------------------------------------------------
Ran 37 tests in 0.003s

OK
```
nested comment/multiline quote 내 brace와 callable text가 함수 LOC/name에 영향을 주지 않는다.
`test_kotlin_nested_block_comment_ignored`, `test_swift_nested_block_comment_ignored`는 조기 `}`를
넣은 nested comment에서 원본 end line(loc=4)을 지키고, `test_go_block_comment_does_not_nest`는 Go가
첫 `*/`에서 닫히는 실제 규칙을 고정한다. `test_shell_multiline_quote_holds_awk_body`는 awk 문자열
안의 `function reset()`과 `/^}/`가 name set(`{summarize, helper}`)과 LOC(9/3)를 오염시키지 않음을,
`test_shell_multiline_double_quote_preserves_lines`는 double quote가 newline을 보존함을 assert한다.

### REVIEW_REVIEW_REVIEW_REFACTOR-4 중간 검증
```
$ python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity scripts.readability_audit_test.TestFunctionExtraction
........................................
----------------------------------------------------------------------
Ran 40 tests in 0.003s

OK
```
false callable 0, multiline type scope-qualified identity PASS.
`test_dart_callback_type_is_not_a_function`(`void Function()` field/parameter가 name set에서 제외,
`{Button.register}`만 남음), `test_dart_body_less_declaration_is_not_a_function`,
`test_shell_body_less_call_is_not_a_function`, `test_shell_next_line_brace_body`,
`test_next_line_type_brace_keeps_scope_identity`(Dart/Kotlin/Swift 모두 next-line brace에서
`Repo.save`), `test_go_multiline_signature_with_receiver`(receiver clause를 header로 오인하지 않음),
`test_dart_expression_body_is_measured`(`=>` body가 종료 `;`까지 loc=5)를 포함한다.

### REVIEW_REVIEW_REVIEW_REFACTOR-5 중간 검증
```
$ python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity scripts.readability_audit_test.TestAllowlistIdentityAndReasonRatchet
...................................................
----------------------------------------------------------------------
Ran 51 tests in 0.005s

OK
```
overload identity 충돌 0, baseline schema/ratchet PASS. Dart/Kotlin/Swift overload는 각각
`C.run(int value)`/`C.run(String value)` 형태로 분리되고 유일한 `C.once`는 plain identity를 유지한다.
`test_overload_identity_is_deterministic`(3회 반복 동일 JSON),
`test_identical_signature_collision_gets_stable_ordinal`(`#1`/`#2` 순서 고정),
`test_report_function_identities_are_unique_per_path`(audit report 단위 identity 중복 0),
`test_extracted_functions_expose_no_internal_keys`(report schema는 `name/loc/start/end` 유지)를 추가했다.

### 최종 검증
```
$ python3 -m unittest scripts.readability_audit_test
$ rm -f /tmp/iop-readability-followup-e.json /tmp/iop-readability-followup-f.json /tmp/iop-readability-followup-tracked-3.json
$ python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-e.json
$ python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-f.json
$ cmp /tmp/iop-readability-followup-e.json /tmp/iop-readability-followup-f.json
$ python3 scripts/readability_audit.py --check --input-mode tracked --output /tmp/iop-readability-followup-tracked-3.json
$ python3 - <<'PY'
import collections
import json
import os
import subprocess

with open('/tmp/iop-readability-followup-e.json', encoding='utf-8') as handle:
    report = json.load(handle)
with open('scripts/readability_baseline.json', encoding='utf-8') as handle:
    baseline = json.load(handle)

files = {entry['path']: entry for entry in report['files']}
for path in ('scripts/e2e-smoke.sh', 'scripts/e2e-long-context-admission-smoke.sh'):
    assert files[path]['kind'] == 'test', (path, files[path]['kind'])

identities = [
    (entry['path'], function['name'])
    for entry in report['files']
    for function in entry.get('functions', [])
]
duplicates = sorted(key for key, count in collections.Counter(identities).items() if count > 1)
false_functions = sorted(key for key in identities if key[1].endswith('.Function'))
print(f'duplicate-function-identities={duplicates}')
print(f'false-dart-functions={false_functions}')
assert not duplicates and not false_functions

current_violations = sorted(
    (v['path'], v['metric'], v.get('function', ''), v['level'], v['value'])
    for v in report['violations']
)
baseline_violations = sorted(
    (v['path'], v['metric'], v.get('function', ''), v['level'], v['value'])
    for section in ('file_thresholds', 'function_thresholds')
    for v in baseline[section]
)
assert current_violations == baseline_violations

current_totals = sorted(
    (task['task_id'], task['total_loc'])
    for task in report['task_read_sets']
    if task['total_loc'] > task['budget']['max_total_loc']
)
baseline_totals = sorted((entry['task_id'], entry['value']) for entry in baseline['task_read_set_totals'])
assert current_totals == baseline_totals

reported = set(files)
changed_existing = {
    path
    for path in subprocess.run(
        ['git', 'diff', '--name-only', '-z', 'HEAD'],
        check=True,
        capture_output=True,
    ).stdout.decode().rstrip('\0').split('\0')
    if path and os.path.isfile(path) and os.path.splitext(path)[1] in {'.go', '.dart', '.sh', '.py', '.kt', '.swift'}
}
missing = sorted(changed_existing - reported)
print(f'changed-existing={len(changed_existing)} missing={missing}')
assert not missing
PY
$ make readability-audit
$ git diff --check
```
실제 출력:
```
$ python3 -m unittest scripts.readability_audit_test
............................................................................................................
----------------------------------------------------------------------
Ran 108 tests in 1.440s

OK

$ rm -f /tmp/iop-readability-followup-e.json /tmp/iop-readability-followup-f.json /tmp/iop-readability-followup-tracked-3.json

$ python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-e.json
RATCHET OK: no new or increased violations.
readability-audit: 336 files, 107236 LOC, 3142 functions, 208 violations
(exit=0)

$ python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-f.json
RATCHET OK: no new or increased violations.
readability-audit: 336 files, 107236 LOC, 3142 functions, 208 violations
(exit=0)

$ cmp /tmp/iop-readability-followup-e.json /tmp/iop-readability-followup-f.json
(no output, exit=0)

$ python3 scripts/readability_audit.py --check --input-mode tracked --output /tmp/iop-readability-followup-tracked-3.json
RATCHET OK: no new or increased violations.
readability-audit: 266 files, 73980 LOC, 2199 functions, 132 violations
(exit=0)

$ python3 - <<'PY' ... PY
duplicate-function-identities=[]
false-dart-functions=[]
changed-existing=8 missing=[]
(exit=0; e2e kind assert, report-baseline violation 일치, task total 일치 모두 통과)

$ make readability-audit
python3 scripts/readability_audit.py --check --input-mode worktree --output build/readability-audit.json
RATCHET OK: no new or increased violations.
readability-audit: 336 files, 107236 LOC, 3142 functions, 208 violations
(exit=0)

$ git diff --check
(no output, exit=0)
```

기존 76개 unit이 모두 통과한 상태에서 32개 regression을 추가해 108개가 PASS한다.
worktree JSON은 반복 실행에서 byte-identical(`cmp` exit 0)이고 tracked/worktree 모두 `RATCHET OK`다.
`scripts/e2e-*.sh` 두 진입점은 kind=test, report 함수 identity 중복 0, false `Function` 0,
report-baseline violation(41 file + 167 function)과 task total(9건) 정확 일치, 수정 tracked 누락 0이다.
계획대로 production/runtime 실행 경로를 바꾸지 않아 edge-node 진단, 보조 E2E smoke, full-cycle,
Docker/외부 서비스 검증은 대상이 아니며 실행하지 않았다.

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Agent UI Completion | Mixed | Present only for plan-required agent-ui code work; implementing agent fills actual evidence, review agent applies `구현됨` status/evidence update on PASS and copies the section into `complete.log` |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only; final checkbox is mandatory before saving |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected Milestone `구현 잠금 > 결정 필요` item blocks implementation; do not ask the user directly during implementation; environment/secret/service blockers, generic scope changes, and evidence gaps are not user-review requests |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Fail — 유효한 Kotlin/Shell/Go 구문에서 함수 종료를 조기에 확정해 LOC와 ratchet identity/value를 과소 계측한다.
  - Completeness: Fail — 계획이 요구한 strict read-set schema와 language-aware declaration/body gate가 일부 경계에서 구현되지 않았다.
  - Test coverage: Fail — 108개 unit은 통과하지만 아래 네 경계를 포함하지 않아 잘못된 결과를 정상으로 승인한다.
  - API contract: Pass — production API, wire, config schema와 runtime 호출 계약 변경은 없다.
  - Code quality: Pass — debug 출력, dead code, 신규 dependency나 대상 밖 소스 변경은 확인되지 않았다.
  - Implementation deviation: Fail — 구현 메모가 Kotlin `=` expression body와 strict schema를 지원한다고 기록했지만 실제 probe 결과가 일치하지 않는다.
  - Verification trust: Fail — 제출된 명령과 108-test 출력은 재현됐으나 누락된 deterministic probe에서 계측 오류가 재현되어 완료 증거로 충분하지 않다.
- 발견된 문제:
  - Required — `scripts/readability_audit.py:1378`: read-set validator는 unknown root key와 `tasks` 존재만 확인하고 `version`, `generated_at`, task `description`의 필수 여부·타입·지원 version을 검증하지 않는다. `{ "version": 2, "generated_at": [], "tasks": [...] }`와 metadata가 전혀 없는 config가 오류 0건으로 통과하므로 계획의 schema 오류 → exit 2 계약을 충족하지 않는다. exact required-key/type/version 검증과 missing/wrong-type CLI 회귀 테스트를 추가한다.
  - Required — `scripts/readability_audit.py:498`: Kotlin `fun compute() =`처럼 expression 본문이 다음 줄에서 계속되면 `_find_expression_end`가 선언 줄의 균형 깊이 0만 보고 즉시 종료해 5줄 함수를 `loc=1`로 보고한다. expression token 뒤부터 multiline expression의 실제 종료까지 추적하도록 바꾸고 one-line/multiline Kotlin 회귀를 함께 고정한다.
  - Required — `scripts/readability_audit.py:202`: Shell lexer가 공백 뒤의 `#`만 comment로 인식하고, `scripts/readability_audit.py:213`은 escaped character를 건너뛰기만 하고 cleaned source에서 지우지 않는다. 따라서 유효한 `:;# }` comment와 `echo \}`가 모두 함수의 조기 닫는 brace로 계측되어 4줄 함수를 `loc=2`로 보고한다. shell token boundary comment 처리와 escaped literal blanking을 구현하고 두 fixture의 원래 end/LOC를 assert한다.
  - Required — `scripts/readability_audit.py:441`: Go declaration scanner가 실제 parameter list가 열리기 전 만난 `{`도 함수 body로 확정한다. `func mapValue[T interface{ ... }](...)`와 `func build() struct{ ... } { ... }`가 모두 선언 줄에서 끝나는 `loc=1`로 보고된다. generic constraint와 result type의 balanced type literal을 건너뛴 뒤 실제 body opener를 찾고 signature identity를 유지하는 회귀 테스트를 추가한다.
- 다음 단계: user-review gate는 트리거되지 않는다. 위 Required 4건을 해결하는 후속 `PLAN-local-G09.md` / `CODE_REVIEW-local-G09.md` 루프를 생성한다.
