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

# Readability audit schema·expression·brace 경계 보정

## 이 파일을 읽는 구현 에이전트에게

> 아래 네 Required를 구현하고 검증 명령을 그대로 실행한 뒤 `CODE_REVIEW-local-G09.md`의 구현 에이전트 소유 섹션을 실제 메모와 stdout/stderr로 채운다. 판정, log rename, complete.log, archive 이동은 code-review 전용이다. 선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 review stub의 `사용자 리뷰 요청`에 연결 근거를 남기고 중단한다. 직접 사용자에게 묻거나 선택지를 제시하거나 request_user_input을 호출하지 않는다. 환경·secret·service, 일반 범위 조정, 후속으로 보완 가능한 증거 공백은 사용자 리뷰 사유가 아니다.

## 배경

108개 unit과 제출된 worktree/tracked audit은 재현됐지만 독립 deterministic probe에서 유효한 Kotlin, Shell, Go 구문이 함수 종료를 조기에 확정하는 문제가 확인됐다. read-set config도 root/task metadata의 필수 여부와 타입을 검증하지 않아 계획의 strict schema 계약을 만족하지 않는다. 계측 대상 언어의 LOC와 required config exit code를 실제 구문 경계에서 닫는다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 선택된 Milestone의 잠금 결정만 active review stub에 기록한다. 구현 중 직접 사용자 prompt는 금지하며, 타당성 검증과 USER_REVIEW.md 작성은 code-review가 소유한다.

## 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.
  - `plan_cloud_G07_3.log` / `code_review_cloud_G07_3.log`: FAIL, Required 4 / Suggested 0 / Nit 0.
- 현재 아카이브 작업: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_cloud_G07_3.log`
- 현재 아카이브 리뷰: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_cloud_G07_3.log`
- 판정: FAIL
- Required 4 / Suggested 0 / Nit 0
  - read-set `version`, `generated_at`, task `description`의 누락·잘못된 타입이 정상 config로 통과한다.
  - Kotlin multiline `=` expression body가 선언 줄에서 종료되어 5줄 함수가 `loc=1`로 축소된다.
  - Shell `:;# }` comment와 `echo \}` escaped literal이 실제 closing brace로 계측된다.
  - Go generic constraint와 anonymous result type의 type brace가 함수 body opener로 계측된다.
- 영향 파일: `scripts/readability_audit.py`, `scripts/readability_audit_test.py`, `scripts/readability_baseline.json`, `scripts/readability_read_sets.json`.
- 검증 증거: 공식 108 unit, worktree 2회 byte-identical JSON, tracked/worktree RATCHET OK, report/baseline 정확 일치, Make target, `git diff --check`는 PASS했다. 별도 in-memory probe는 위 네 오류를 결정적으로 재현했다.
- Roadmap carryover: `readability-baseline` Task는 PASS 전까지 미완료로 유지한다.
- 추가 상세가 필요하면 위 `plan_cloud_G07_3.log`와 `code_review_cloud_G07_3.log`만 좁게 재확인한다.

## 분석 결과

### SDD와 spec

선택 Milestone은 `SDD: 불필요`이며 구현 잠금이 해제되어 있다. runtime/API/wire/config 계약은 바꾸지 않는다. agent-spec index에 repository readability tooling과 매칭되는 living spec이 없으므로 Spec update not needed를 유지한다.

### 테스트 환경

`test_env=local`. Python stdlib unittest, 임시 git fixture, worktree/tracked CLI, 반복 JSON cmp, Make target과 whitespace 검증을 사용한다. production/runtime 경로는 바꾸지 않으므로 edge-node 진단, 보조 E2E, full-cycle, Docker/외부 서비스 검증은 대상이 아니다.

### 범위 결정 근거

`scripts/readability_audit.py`와 회귀 테스트를 수정하고, 실제 전체 projection이 바뀔 때만 baseline을 재생성한다. read-set JSON은 exact schema의 canonical fixture로 검증하되 내용 변경이 필요 없으면 유지한다. Makefile, runtime 소스, roadmap, agent-spec/contract는 수정하지 않는다.

### 빌드 등급

`local-G09`. 네 실패는 모두 두 Python 파일과 JSON 기준선 안에서 exact fixture와 subprocess exit code로 재현 가능하다. 외부 interactive 동작이나 live provider 판단이 없지만 다언어 balanced scanner와 baseline 재검증이 함께 필요해 높은 local 등급을 사용한다.

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] read-set root/task metadata를 exact schema로 검증하고 모든 schema 오류를 CLI exit 2로 고정한다.
- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2] Kotlin multiline expression body의 실제 종료까지 LOC를 측정한다.
- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-3] Shell token comment와 escaped literal의 brace를 cleaned source에서 제거한다.
- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-4] Go generic/result type brace를 건너뛰고 실제 function body opener를 찾는다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] exact read-set schema

- 문제: `scripts/readability_audit.py:1378-1406`은 unknown key와 tasks 존재만 확인한다. version/generated_at/description 누락과 잘못된 타입도 task total을 실행해 정상 config처럼 취급한다.
- 해결 방법:

~~~text
Before
tasks-only root and untyped metadata -> valid

After
exact required root keys + supported version + typed generated_at
exact required task keys + non-empty typed description
any schema error -> deterministic diagnostic and CLI exit 2
~~~

- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: root/task required-key, type, supported-version 검증을 추가한다.
  - [ ] `scripts/readability_audit_test.py`: missing/wrong-type/unsupported version과 missing/wrong description direct/CLI case를 추가한다.
  - [ ] `scripts/readability_read_sets.json`: exact schema를 만족하는 canonical config임을 회귀 테스트로 고정한다.
- 테스트 작성: 작성한다. `test_read_set_schema_requires_typed_metadata`와 `test_read_set_schema_error_exits_two`가 direct issue와 stderr/exit 2를 모두 검증한다.
- 중간 검증:

~~~bash
python3 -m unittest \
  scripts.readability_audit_test.TestTaskReadSetTotalsAndBudget.test_read_set_schema_requires_typed_metadata \
  scripts.readability_audit_test.TestCliExitCodes.test_read_set_schema_error_exits_two
~~~

### [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2] Kotlin multiline expression 종료

- 문제: `scripts/readability_audit.py:498-510`은 body 시작 줄 전체의 depth가 0이면 다음 줄을 읽지 않는다. `fun compute() =` 뒤에 expression이 내려가면 LOC가 1로 축소된다.
- 해결 방법:

~~~text
Before
declaration line balance 0 -> expression ends immediately

After
scan begins after the expression token
continue through the first complete expression across lines
one-line and multiline expression bodies remain deterministic
~~~

- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: expression token 위치와 multiline continuation을 보존하는 종료 scanner를 구현한다.
  - [ ] `scripts/readability_audit_test.py`: Kotlin one-line과 multiline list/call expression의 start/end/LOC를 assert한다.
  - [ ] `scripts/readability_baseline.json`: 실제 repository projection이 변할 때만 고정점으로 재생성한다.
- 테스트 작성: 작성한다. `test_kotlin_multiline_expression_body_spans_full_expression`이 5줄 body를 정확히 보고하는지 검증한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity.test_kotlin_multiline_expression_body_spans_full_expression
~~~

### [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-3] Shell token-aware brace blanking

- 문제: `scripts/readability_audit.py:202-215`은 whitespace 앞의 #만 comment로 처리하고 escaped character는 output에 남긴다. operator 뒤 comment와 escaped literal의 }가 함수 종료로 들어간다.
- 해결 방법:

~~~text
Before
:;# } and echo \} -> real closing braces

After
# begins a comment at valid shell token boundaries
escaped literals are blanked without removing newlines
parameter expansion and quoted content retain their own lexical meaning
~~~

- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: shell token boundary와 top-level escape blanking을 구현한다.
  - [ ] `scripts/readability_audit_test.py`: operator comment와 escaped opening/closing brace fixture의 name/start/end/LOC를 assert한다.
  - [ ] `scripts/readability_baseline.json`: 실제 projection이 변할 때만 재생성한다.
- 테스트 작성: 작성한다. `test_shell_escaped_and_operator_comment_braces_are_ignored`가 두 함수를 원래 종료 줄까지 측정하는지 검증한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity.test_shell_escaped_and_operator_comment_braces_are_ignored
~~~

### [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-4] Go type brace와 body brace 분리

- 문제: `scripts/readability_audit.py:441-467`은 parameter list가 열리기 전 또는 닫힌 뒤 처음 만난 {를 body로 확정한다. generic interface constraint와 anonymous struct/interface result type이 함수 본문으로 오인된다.
- 해결 방법:

~~~text
Before
first brace near declaration -> body opener

After
balance generic parameter and result-type literals
identify the declaration parameter list separately
select only the brace that opens the executable function body
~~~

- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: Go declaration header의 generic/parameter/result type 상태를 분리한다.
  - [ ] `scripts/readability_audit_test.py`: generic interface constraint와 anonymous struct/interface return의 name/signature/start/end/LOC를 assert한다.
  - [ ] `scripts/readability_baseline.json`: 전체 report identity/value가 바뀌면 단일 worktree projection으로 재생성한다.
- 테스트 작성: 작성한다. `test_go_type_braces_do_not_open_function_body`가 두 valid Go 선언을 실제 closing brace까지 측정하는지 검증한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity.test_go_type_braces_do_not_open_function_body
~~~

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `scripts/readability_audit.py` | REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1, 2, 3, 4 |
| `scripts/readability_audit_test.py` | REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1, 2, 3, 4 |
| `scripts/readability_baseline.json` | REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-2, 3, 4의 실제 projection 변경 시 |
| `scripts/readability_read_sets.json` | REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1 canonical schema 확인 |

## 최종 검증

~~~bash
python3 -m unittest scripts.readability_audit_test
rm -f /tmp/iop-readability-followup-g.json /tmp/iop-readability-followup-h.json /tmp/iop-readability-followup-tracked-4.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-g.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-h.json
cmp /tmp/iop-readability-followup-g.json /tmp/iop-readability-followup-h.json
python3 scripts/readability_audit.py --check --input-mode tracked --output /tmp/iop-readability-followup-tracked-4.json
python3 - <<'PY'
import collections
import json

with open('/tmp/iop-readability-followup-g.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)

identities = [
    (entry['path'], function['name'])
    for entry in report['files']
    for function in entry.get('functions', [])
]
assert not [key for key, count in collections.Counter(identities).items() if count > 1]

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
print('duplicate-function-identities=[]')
print('baseline-projection=exact')
PY
make readability-audit
git diff --check
~~~

기대 결과: 신규 targeted regression과 전체 unit PASS, worktree JSON byte-identical, tracked/worktree RATCHET OK, 함수 identity 중복 0, report/baseline violation과 task total 정확 일치, Make target PASS, whitespace 오류 없음. production/runtime 미변경으로 edge-node 진단·보조 E2E·full-cycle은 생략한다.

모든 코드 변경 완료 후 반드시 active CODE_REVIEW 파일의 구현 에이전트 소유 섹션을 채운다.
