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

# Kotlin generic type closer와 no-space comparison 경계 보정

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

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

## 배경

118개 unit과 공식 ratchet은 통과하지만 trailing bare `>`를 무조건 comparison continuation으로 처리해, Kotlin grammar상 완결된 `value as List<Int>` / `value is List<Int>`의 generic type closer도 다음 줄로 이어진다고 오판한다. extractor는 첫 함수의 end를 뒤 sibling까지 확장하고 스캔 인덱스를 건너뛰므로 sibling function identity 자체가 누락된다. spacing-independent comparison 양성 케이스를 유지하면서 type RHS의 generic closer를 구분한다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 선택된 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.
  - `plan_local_G09_4.log` / `code_review_local_G09_4.log`: FAIL, Required 2 / Suggested 0 / Nit 0.
  - `plan_local_G09_5.log` / `code_review_local_G09_5.log`: FAIL, Required 1 / Suggested 0 / Nit 0.
  - `plan_local_G09_6.log` / `code_review_local_G09_6.log`: FAIL, Required 1 / Suggested 0 / Nit 0.
  - `plan_local_G09_7.log` / `code_review_local_G09_7.log`: FAIL, Required 1 / Suggested 0 / Nit 0.
- 현재 아카이브 작업: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_local_G09_7.log`
- 현재 아카이브 리뷰: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_local_G09_7.log`
- 판정: FAIL
- Required 1 / Suggested 0 / Nit 0
  - spacing-independent `KOTLIN_BARE_COMPARISON_RE`가 `as`/`is` RHS의 완결 generic type closer `>`까지 continuation으로 처리해 sibling function identity를 누락한다.
- 영향 파일: `scripts/readability_audit.py`, `scripts/readability_audit_test.py`, `scripts/readability_baseline.json`.
- 검증 증거: 제출 및 독립 재실행에서 118 unit, worktree 2회 byte-identical JSON, tracked/worktree RATCHET, baseline exact projection, Make target, `git diff --check`는 PASS했다. 독립 negative probe에서는 `C.cast`가 2~5번 줄(4 LOC)을 삼키고 `C.typed`와 `C.greater` identity가 누락됐다.
- Roadmap carryover: `readability-baseline` Task는 PASS 전까지 미완료로 유지한다.
- 추가 상세가 필요하면 위 `plan_local_G09_7.log`와 `code_review_local_G09_7.log`만 좁게 재확인한다.

## 분석 결과

### 읽은 파일

- `scripts/readability_audit.py`
- `scripts/readability_audit_test.py`
- `scripts/readability_baseline.json`
- `scripts/readability_read_sets.json`
- `Makefile`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
- `agent-spec/index.md`
- `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_local_G09_7.log`
- `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_local_G09_7.log`

### 문법 근거

Kotlin 공식 문법에서 comparison은 `genericCallLikeComparison {comparisonOperator NL* genericCallLikeComparison}`이고, cast와 type check의 RHS는 각각 `type`이다. 따라서 줄 끝 `>`는 comparison operator일 수도 있지만 `as List<Int>`, `is Map<String, Int>`처럼 완결 type의 generic closer일 수도 있다. lexical 마지막 문자만으로 두 경우를 구분할 수 없다.

### SDD 기준

선택 Milestone은 `SDD: 불필요`다. 외부 API/proto/config schema나 runtime 책임 경계를 바꾸지 않는 repository readability tooling 보정이라는 기존 사유를 유지한다.

### 테스트 환경 규칙

`test_env=local`. Python stdlib unittest, 임시 `/tmp` JSON, worktree/tracked audit, 반복 `cmp`, Make target, whitespace 검증을 적용한다. 사용자 실행/runtime 동작은 바꾸지 않으므로 edge-node 진단, 보조 E2E, full-cycle, 외부 provider/Docker 검증은 대상이 아니다.

### 테스트 커버리지 공백

- 기존 fixture는 no-space `1<`/`2>` 양성 continuation만 검증한다.
- `as`/`is` RHS의 단일·중첩 generic type이 줄 끝 `>`로 완결되는 음성 케이스가 없다.
- generic type 함수 뒤 sibling의 identity/start/end/LOC가 모두 보존되는지 확인하지 않는다.

### 심볼 참조

- `KOTLIN_BARE_COMPARISON_RE`는 `_kotlin_trailing_continuation` 한 곳에서만 참조된다.
- continuation 결과는 `_kotlin_expression_continues` → `_find_expression_end` → `_extract_functions_brace`로 전달되며, 잘못된 end는 `i = end + 1` 때문에 중간 sibling 추출 누락으로 이어진다.
- 보정은 Kotlin 분기 안에서만 수행하고 Dart/다른 brace language의 expression 종료 계약을 바꾸지 않는다.

### 분할 판단

같은 `12+03,06,10,11_readability_baseline` subtask의 단일 후속 plan을 유지한다. 한 Kotlin continuation classifier, 한 regression fixture, 같은 self-referential baseline projection을 함께 바꾸는 단일 ownership/risk 경계다.

### 범위 결정 근거

`scripts/readability_audit.py`의 trailing bare comparison 판정, 대응 unit, 실제 projection 값만 수정한다. literal infix 보정, Shell lexer, `scripts/readability_read_sets.json`, Makefile, 다른 언어 extractor, runtime 소스, roadmap, spec/contract는 수정하지 않는다. 신규 helper violation은 baseline에 넣지 않는다.

### 빌드 등급

`local-G09`. 현재 Required는 공식 grammar와 7줄 probe로 재현되며 두 Python 파일 및 JSON 고정점에서 결정적으로 검증 가능하다. 반복 루프이지만 현재 finding은 1개의 좁은 parser 경계이고 외부 runtime/evidence-trust 판단이 필요 없어 local 조건을 유지한다.

### Spec 판단

`agent-spec/index.md`에 repository readability tooling과 매칭되는 living spec이 없다. Spec update not needed: runtime/API 동작이나 기존 spec evidence를 바꾸지 않는다.

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] Kotlin trailing `>`가 완결 generic cast/type-test인지 comparison continuation인지 구분하고 모든 sibling function identity와 span을 보존한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] Kotlin generic closer와 comparison 구분

- 문제: `scripts/readability_audit.py:510-515`의 `r"[<>]$"`는 `value>` comparison과 `value as List<Int>` type closer를 동일하게 처리한다. 후자는 완결 표현식인데도 다음 줄까지 이어져 `_extract_functions_brace`가 sibling 선언을 건너뛴다.
- 해결 방법:

~~~text
Before
줄 끝 bare < 또는 > = 항상 comparison continuation

After
줄 끝 bare < 또는 > = Kotlin token/context로 comparison 여부 판정
as/is RHS에서 완결된 단일·중첩 generic type closer = expression 종료
공백 유무와 무관한 1< / 2> comparison = RHS까지 continuation
custom literal infix 및 기존 operator grammar = 현재 동작 유지
~~~

- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: trailing `>` 판정을 type RHS의 generic closer와 comparison operator를 구분하도록 보정한다. 주석도 실제 문법 경계와 일치시킨다. Kotlin 전용으로 유지하고 sibling scan 계약을 보존한다.
  - [ ] `scripts/readability_audit_test.py`: `as List<Int>`, `is List<Int>`, nested generic type, no-space `value>`와 뒤 sibling을 같은 fixture에서 검증한다. generic 함수는 각각 1 LOC, comparison은 2 LOC이며 전체 function name set이 누락 없이 일치해야 한다.
  - [ ] `scripts/readability_baseline.json`: 전체 worktree projection의 기존 identity 값이 바뀔 때만 reason을 보존해 고정점 갱신하고 신규 helper violation은 허용하지 않는다.
- 테스트 작성: 작성한다. 기존 operator grammar test를 확장하거나 focused test를 추가해 generic cast/type-test 뒤 sibling identity/start/end/LOC 누락을 직접 assert한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.TestMultiLanguageFunctionIdentity.test_kotlin_depth_zero_operator_grammar_continuations_span_full_expression
python3 - <<'PY'
from scripts import readability_audit as ra

source = '''class C {
  fun cast(value: Any) = value as List<Int>
  fun typed(value: Any) = value is Map<String, List<Int>>
  fun greater(value: Int) = value>
    0
  fun done() = 3
}
'''
actual = {f['name']: (f['start'], f['end'], f['loc'])
          for f in ra._extract_functions_kotlin(source, 'probe.kt')}
assert set(actual) == {'C.cast', 'C.typed', 'C.greater', 'C.done'}, actual
assert actual['C.cast'] == (2, 2, 1), actual['C.cast']
assert actual['C.typed'] == (3, 3, 1), actual['C.typed']
assert actual['C.greater'] == (4, 5, 2), actual['C.greater']
assert actual['C.done'] == (6, 6, 1), actual['C.done']
print('kotlin-generic-type-closer=exact')
print('kotlin-no-space-comparison-and-siblings=exact')
PY
~~~

기대 결과: focused unit PASS, generic cast/type-test는 각 1 LOC, no-space comparison은 2 LOC, 네 sibling identity가 모두 보존된다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `scripts/readability_audit.py` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1 |
| `scripts/readability_audit_test.py` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1 |
| `scripts/readability_baseline.json` | 실제 worktree projection 고정점 갱신 시 |

## 최종 검증

~~~bash
python3 -m unittest scripts.readability_audit_test
python3 - <<'PY'
from scripts import readability_audit as ra

source = '''class C {
  fun cast(value: Any) = value as List<Int>
  fun typed(value: Any) = value is Map<String, List<Int>>
  fun greater(value: Int) = value>
    0
  fun done() = 3
}
'''
actual = {f['name']: (f['start'], f['end'], f['loc'])
          for f in ra._extract_functions_kotlin(source, 'probe.kt')}
assert set(actual) == {'C.cast', 'C.typed', 'C.greater', 'C.done'}, actual
assert actual['C.cast'] == (2, 2, 1), actual['C.cast']
assert actual['C.typed'] == (3, 3, 1), actual['C.typed']
assert actual['C.greater'] == (4, 5, 2), actual['C.greater']
assert actual['C.done'] == (6, 6, 1), actual['C.done']
print('kotlin-generic-type-closer=exact')
print('kotlin-no-space-comparison-and-siblings=exact')
PY
rm -f /tmp/iop-readability-followup-q.json /tmp/iop-readability-followup-r.json /tmp/iop-readability-followup-tracked-8.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-q.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-r.json
cmp /tmp/iop-readability-followup-q.json /tmp/iop-readability-followup-r.json
python3 scripts/readability_audit.py --check --input-mode tracked --output /tmp/iop-readability-followup-tracked-8.json
python3 - <<'PY'
import collections
import json

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

기대 결과: 전체 unit PASS, generic closer/no-space comparison probe exact, worktree JSON byte-identical, tracked/worktree RATCHET OK, identity 중복 0, report/baseline exact projection, Make target PASS, whitespace 오류 없음. 코드/테스트 LOC로 baseline 값이 증가하면 기존 reason을 identity 기준으로 보존해 고정점으로 수렴시키되 신규 helper violation은 허용하지 않는다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
