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

# Kotlin depth-0 operator grammar 연속식 보정

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

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

## 배경

117개 unit과 공식 ratchet은 통과하지만 Kotlin depth-0 continuation이 제한된 기호 regex에 묶여 있다. 공식 Kotlin grammar가 newline을 허용하는 comparison, containment/type/cast, open range, custom infix와 일부 leading operator를 현재 extractor가 선언 줄에서 종료한다. 이 문법 계열과 고정점 baseline만 좁게 보정한다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 선택된 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.
- 현재 아카이브 작업: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_local_G09_5.log`
- 현재 아카이브 리뷰: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_local_G09_5.log`
- 판정: FAIL
- Required 1 / Suggested 0 / Nit 0
  - Kotlin continuation regex가 single `<`/`>`, `in`/`is`/`as`, `..<`, custom infix identifier 등 공식 grammar의 depth-0 연속식을 누락한다.
- 영향 파일: `scripts/readability_audit.py`, `scripts/readability_audit_test.py`, `scripts/readability_baseline.json`.
- 검증 증거: 공식 117 unit, Kotlin/Shell 기존 probe, worktree 2회 byte-identical JSON, tracked/worktree RATCHET, baseline exact projection, Make target, `git diff --check`는 PASS했다. 독립 Kotlin probe의 `less`, `contained`, `typed`, `cast`, `range`, `custom`은 모두 기대 2 LOC 대신 1 LOC였다.
- Roadmap carryover: `readability-baseline` Task는 PASS 전까지 미완료로 유지한다.
- 추가 상세가 필요하면 위 `plan_local_G09_5.log`와 `code_review_local_G09_5.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`

### SDD 기준

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

### 테스트 환경 규칙

`test_env=local`. `agent-test/local/rules.md`와 `agent-test/local/testing-smoke.md`를 읽었다. Python stdlib unittest, 임시 `/tmp` JSON, worktree/tracked audit, 반복 `cmp`, Make target, whitespace 검증을 적용한다. `kotlinc`는 현재 runner에 없음을 `command -v kotlinc`로 확인했으며, 문법 기준은 Kotlin 공식 grammar와 deterministic extractor fixture로 고정한다. 사용자 실행/runtime 동작은 바꾸지 않으므로 edge-node 진단, 보조 E2E, full-cycle, 외부 provider/Docker 검증은 대상이 아니다.

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

- 기존 `test_kotlin_depth_zero_expression_continuations_span_full_expression`은 trailing `+`와 leading `.`만 고정한다.
- 공식 grammar의 trailing comparison `<`/`>`, `in`/`!in`, `is`/`!is`, `as`/`as?`, `..<`, custom infix identifier와 newline-before가 허용되는 logical/cast operator는 검증하지 않는다.
- sibling function을 삼키지 않는 assertion은 기존 예시에만 있고 위 문법 계열을 통과한 뒤의 경계를 검증하지 않는다.

### 심볼 참조

renamed/removed symbol은 없다. 수정 대상은 `KOTLIN_EXPRESSION_SUFFIX_RE`, `KOTLIN_EXPRESSION_PREFIX_RE`, `_kotlin_expression_continues`, `_find_expression_end` 호출 경계다.

### 분할 판단

split 정책을 재평가했으며 같은 `12+03,06,10,11_readability_baseline` subtask의 단일 후속 plan을 유지한다. 한 Kotlin continuation classifier와 한 회귀 fixture, 같은 self-referential baseline projection을 함께 바꾸므로 구현·테스트·고정점을 분리하면 동일 파일 충돌과 재생성만 중복된다. predecessor `03`, `06`, `10`, `11` 충족 근거는 기존 Archive Evidence Snapshot의 archived `complete.log` 경로로 이미 고정되어 있다.

### 범위 결정 근거

`scripts/readability_audit.py`의 Kotlin depth-0 continuation, 대응 unit, 실제 projection 값만 수정한다. 이번 리뷰에서 통과한 Shell lexer와 `scripts/readability_read_sets.json`, Makefile, runtime 소스, roadmap, spec/contract는 수정하지 않는다. 신규 helper violation은 baseline에 넣지 않는다. 더 정확한 Kotlin 측정으로 기존 장기 함수가 새로 드러나면 identity/LOC를 확인하고 기존 부채 사유를 명시한 경우에만 baseline에 추가한다.

### 빌드 등급

`local-G09`. 두 Python 파일의 문법 분류와 in-memory fixture로 결정적으로 검증할 수 있지만 Kotlin operator grammar, sibling boundary, self-referential baseline 고정점을 함께 보존해야 한다.

### Spec 판단

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

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] Kotlin grammar가 허용하는 depth-0 operator/infix continuation을 실제 종료 줄까지 측정하고 sibling function 경계를 보존한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1] Kotlin operator grammar continuation

- 문제: `scripts/readability_audit.py:489-492`의 finite regex는 일부 기호만 포함한다. `scripts/readability_audit.py:684-690`은 이 regex와 세 leading chain token 외의 depth-0 newline을 종료로 판정해 공식 grammar가 허용하는 comparison, word operator, range, infix continuation을 `loc=1`로 축소한다.
- 해결 방법:

~~~text
Before
depth-0 suffix = selected symbolic regex
depth-0 prefix = . / ?. / ?:

After
Kotlin grammar category별 suffix/prefix token 판정
- comparison/equality/logical/additive/multiplicative/elvis/range
- in/!in, is/!is, as/as?
- custom infix identifier 뒤 RHS continuation
- newline-before가 허용되는 logical/elvis/cast/member-access
다음 declaration/annotation/type close는 continuation으로 취급하지 않음
~~~

- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: 단일 부분 regex 대신 Kotlin grammar category를 드러내는 token-aware continuation helper로 보정하고 Dart/다른 brace language 계약을 유지한다.
  - [ ] `scripts/readability_audit_test.py`: comparison, containment/type/cast, open range, custom infix, leading logical/cast와 마지막 sibling function의 start/end/LOC를 한 deterministic fixture로 고정한다.
  - [ ] `scripts/readability_baseline.json`: 전체 worktree projection의 기존 identity 값이 바뀔 때만 reason을 보존해 고정점 갱신하고 신규 helper violation은 허용하지 않는다.
- 테스트 작성: 작성한다. `test_kotlin_depth_zero_operator_grammar_continuations_span_full_expression`이 각 연속 함수를 2 LOC, 마지막 sibling을 1 LOC로 assert한다. 기존 leading chain/`+` 테스트도 유지한다.
- 중간 검증:

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

기대 결과: 3 tests PASS이고 comparison/word/range/custom infix/leading operator의 각 함수는 2 LOC, 뒤의 sibling은 1 LOC다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `scripts/readability_audit.py` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR-1 |
| `scripts/readability_audit_test.py` | 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 {
  infix fun Int.join(other: Int) = this + other
  fun less() = 1 <
    2
  fun contained() = 1 in
    listOf(1)
  fun typed(value: Any) = value is
    String
  fun cast(value: Any) = value as
    String
  fun range() = 1..<
    3
  fun custom() = 1 join
    2
  fun leadingLogical() = true
    && false
  fun leadingCast(value: Any) = value
    as String
  fun done() = 3
}
'''
actual = {f['name']: (f['start'], f['end'], f['loc'])
          for f in ra._extract_functions_kotlin(source, 'probe.kt')}
for name in ('C.less', 'C.contained', 'C.typed', 'C.cast', 'C.range',
             'C.custom', 'C.leadingLogical', 'C.leadingCast'):
    assert actual[name][2] == 2, (name, actual[name])
assert actual['C.done'][2] == 1, actual['C.done']
print('kotlin-operator-grammar-continuations=exact')
print('kotlin-sibling-boundary=exact')
PY
rm -f /tmp/iop-readability-followup-k.json /tmp/iop-readability-followup-l.json /tmp/iop-readability-followup-tracked-6.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-k.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-l.json
cmp /tmp/iop-readability-followup-k.json /tmp/iop-readability-followup-l.json
python3 scripts/readability_audit.py --check --input-mode tracked --output /tmp/iop-readability-followup-tracked-6.json
python3 - <<'PY'
import collections
import json

with open('/tmp/iop-readability-followup-k.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, Kotlin operator grammar 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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
