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

# Readability audit ratchet 신뢰성 보정

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

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

## 배경

첫 리뷰의 기본 검증은 성공했지만, audit가 index에 아직 없는 선행 split 신규 파일을 누락해 post-commit 상태에서 즉시 깨지는 baseline을 만들었다. 로드맵의 파일 분류별 threshold, task-local read set, 사유 필수 ratchet이 실제 검증 경로에 연결되지 않았고, 다언어 함수 추출은 일반적인 string/generic/method 코드에서 false negative를 낸다. 이 후속은 도구·schema·baseline·테스트를 하나의 결정적 계약으로 보정한다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 선택된 Milestone의 구현 잠금 결정만 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 구현 중 직접 사용자 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

- 이전 작업: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_local_G07_0.log`
- 이전 리뷰: `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_local_G07_0.log`
- 판정: FAIL
- Required 4 / Suggested 0 / Nit 0
  - index-only 233개 baseline은 post-commit projection 303개에서 신규 함수 초과 14건으로 깨진다.
  - 운영/테스트/Skill/함수 threshold 계층이 1,000/120 두 값으로 축소되었다.
  - `readability_read_sets.json`은 로드되지만 사용되지 않고 테스트도 task LOC 합계를 검증하지 않는다.
  - 다언어 brace parser가 string/comment/generic/method에서 잘못된 LOC/identity를 내고 `(path, metric)` ratchet이 새 함수와 빈 사유를 통과시킨다.
- 영향 파일: `Makefile`, `.gitignore`, `scripts/readability_audit.py`, `scripts/readability_audit_test.py`, `scripts/readability_baseline.json`, `scripts/readability_read_sets.json`
- 기존 검증 증거: unit 13개, tracked-only audit 2회 cmp, `make readability-audit`, `git diff --check`는 PASS했다. 리뷰 경계 검증은 post-commit projection 14건 FAIL, empty reason/새 function identity가 `[]`로 통과, Go `"}"` 포함 5줄 함수를 2줄로 계측, Dart `Future<void>` 함수 누락을 재현했다.
- Roadmap carryover: `readability-baseline` Task는 PASS 전까지 미완료로 유지한다.
- 추가 상세가 필요하면 위 `plan_local_G07_0.log`와 `code_review_local_G07_0.log`만 좁게 재확인한다.

## 분석 결과

### 읽은 파일

- `.gitignore`
- `Makefile`
- `scripts/readability_audit.py`
- `scripts/readability_audit_test.py`
- `scripts/readability_baseline.json`
- `scripts/readability_read_sets.json`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-spec/index.md`
- `agent-task/archive/2026/07/m-agent-readable-repository-refactor/03+02_openai_provider_tools/complete.log`
- `agent-task/archive/2026/07/m-agent-readable-repository-refactor/06+04,05_core_edge_tests/complete.log`
- `agent-task/archive/2026/07/m-agent-readable-repository-refactor/10+07,08,09_adapter_control_plane_tests/complete.log`
- `agent-task/archive/2026/07/m-agent-readable-repository-refactor/11_client_tests/complete.log`
- `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/plan_local_G07_0.log`
- `agent-task/m-agent-readable-repository-refactor/12+03,06,10,11_readability_baseline/code_review_local_G07_0.log`

### SDD 기준

선택 Milestone은 `SDD: 불필요`다. 외부 API/proto/config schema와 runtime 책임 경계를 바꾸지 않는 동작 보존형 리팩터링이라는 기록 사유를 유지한다.

### 테스트 환경 규칙

`test_env=local`. `agent-test/local/rules.md`와 `agent-test/local/testing-smoke.md`를 읽었다. Python stdlib unittest, 독립 `/tmp` JSON 반복 cmp, tracked/worktree 입력 mode, Make target, `git diff --check`를 적용한다. production/runtime 동작을 바꾸지 않는 repository audit 도구 범위이므로 repo 내 edge-node 진단, 보조 E2E smoke, full-cycle 실제 구동은 필요하지 않다.

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

- tracked-only 검증은 untracked 신규/삭제를 반영한 post-commit projection을 검증하지 않는다.
- threshold 테스트는 공통 1,000/120 경계만 보고 운영/테스트/Skill 분류별 warning/split/exception을 보지 않는다.
- allowlist 테스트는 empty reason, duplicate/invalid schema, 같은 파일의 새 함수 identity를 보지 않는다.
- read-set 테스트는 task path LOC 합계, ordered output, missing path, budget 초과/사유를 보지 않는다.
- 함수 추출 테스트는 string/comment brace, Go receiver, Dart generic/async, Kotlin/Swift identity, Python class 제외를 보지 않는다.

### 심볼 참조

제거·rename할 외부 심볼은 없다. `readability_audit.py`의 현재 내부 helper와 `Makefile` `readability-audit` 호출만 함께 갱신한다.

### 분할 판단

분할 정책을 우선 검토했다. 모든 Required가 같은 Python CLI의 measurement 모델, baseline schema, 하나의 unit suite를 공유하며 policy/parser/read-set 중 하나만 먼저 완료하면 baseline이 의도적으로 깨진 중간 상태가 된다. 기존 `12+03,06,10,11_readability_baseline` subtask 내 단일 후속 계획으로 유지하는 편이 검증과 리뷰에 안전하다. predecessor `03`, `06`, `10`, `11`은 위 명시 archived `complete.log`로 모두 충족된다.

### 범위 결정 근거

`Makefile`과 가독성 audit 스크립트/JSON/테스트만 갱신한다. production source, CI workflow, API/proto/config schema, roadmap/agent-spec/agent-contract, 선행 split 테스트 파일은 수정하지 않는다. 리뷰에서 `.gitignore`의 Python cache ignore는 이미 비동작 보정했으므로 후속 구현 범위를 늘리지 않는다.

### 빌드 등급

`local-G09`: 다언어 함수 계측·ratchet schema·task read-set·baseline을 함께 바꾸는 고복잡도 후속이지만, 요구 행동은 Milestone과 실패 증거에 명시되어 있고 외부 환경 없이 다섯 파일과 결정적 테스트로 폐합 검증할 수 있다.

## 구현 체크리스트

- [ ] [REVIEW_REFACTOR-1] 운영/테스트/Skill/함수 threshold 계층을 구현하고 분류별 경계 테스트를 통과시킨다.
- [ ] [REVIEW_REFACTOR-2] 다언어 함수 계측과 함수 단위 allowlist identity/reason ratchet을 보정하고 경계 regression test를 통과시킨다.
- [ ] [REVIEW_REFACTOR-3] ordered task-local read set LOC 합계와 budget/reason 검증을 audit JSON과 ratchet에 연결하고 결정성 테스트를 통과시킨다.
- [ ] [REVIEW_REFACTOR-4] tracked/worktree 입력 mode로 pre-commit과 post-commit 동일성을 검증하고 전체 현재 소스를 기준으로 baseline을 재생성한 뒤 unit, 반복 JSON cmp, Make audit를 모두 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REFACTOR-1] 파일 분류별 threshold 정책

- 문제: `scripts/readability_audit.py:31-51`은 Go/Dart/Shell/Python/Kotlin/Swift만 입력으로 두고 파일 종류와 관계없이 1,000줄, 함수 120줄만 사용한다. 운영 500/800/1,000, 테스트 800/1,000, 함수 80/120, Skill 진입 문서 300/500 기준이 출력과 ratchet에 없다.
- 해결 방법:

~~~text
Before
DEFAULT_MAX_FILE_LOC = 1000
DEFAULT_MAX_FUNCTION_LOC = 120

After
production: warning=500, split_review=800, exception=1000
test: warning=800, split_review=1000
function: warning=80, split_review=120
skill_entrypoint: warning=300, split_review=500
~~~

  `agent-ops/skills/**/SKILL.md`만 추가 Markdown 계측 입력으로 포함하고, 파일 분류·level·threshold를 각 finding에 기록한다. 기존 초과는 level별 baseline에 고정하고 신규/증가 finding을 실패시킨다.
- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: 파일 분류와 threshold level 모델을 추가한다.
  - [ ] `scripts/readability_audit_test.py`: `test_policy_thresholds_by_file_kind` 및 Skill Markdown 경계 테스트를 추가한다.
  - [ ] `scripts/readability_baseline.json`: 새 policy key/level 기준으로 재생성한다.
- 테스트 작성: 작성한다. 운영 500/800/1,000, 테스트 800/1,000, 함수 80/120, Skill 300/500의 exact-boundary와 one-over 분류를 fixture로 assert한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.py
~~~

기대 결과: 파일 분류별 경계와 finding level 테스트 PASS.

### [REVIEW_REFACTOR-2] 다언어 함수 identity와 allowlist ratchet

- 문제: `scripts/readability_audit.py:114-220`의 문자 단위 brace counting은 string/comment의 괄호를 코드로 오인하고 Dart generic/async를 누락하며 Go method·Kotlin·Swift를 `unknown`으로 기록한다. `scripts/readability_audit.py:462-503`은 `(path, metric)` 하나로 함수 finding을 덮어쓰고 baseline reason/schema를 검증하지 않는다.
- 해결 방법:

~~~text
Before
brace character count + largest_function only
ratchet key = (path, metric)

After
comment/string-aware lexical scan + stable qualified function identity
one finding per oversized function
ratchet key = (path, metric, level, function identity when applicable)
validated single-source allowlist(value + non-empty reason)
~~~

  Python은 function/async function만 계측하고 class 전체를 function으로 계측하지 않는다. Go/Dart/Kotlin/Swift/Shell은 comment/string/raw literal을 건너뛰는 렉서와 언어별 시그니처 탐지로 name/receiver를 보존한다. baseline의 중복, empty reason, 잘못된 metric/level/value는 `--check`의 구성 오류로 실패시킨다.
- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: 다언어 lexical function extraction과 개별 finding을 구현한다.
  - [ ] `scripts/readability_audit.py`: allowlist schema와 identity/value/reason ratchet을 단일 source of truth로 정리한다.
  - [ ] `scripts/readability_audit_test.py`: string/comment brace, Go receiver, Dart generic/async, Kotlin/Swift name, Python class 제외, 같은 파일의 새 함수, empty/duplicate reason regression을 추가한다.
  - [ ] `scripts/readability_baseline.json`: 안정적 identity와 사유 필수 allowlist로 재생성한다.
- 테스트 작성: 작성한다. 리뷰에서 재현한 Go `"}"` 오계측, Dart `Future<void>` 누락, `unknown` identity, empty reason과 새 함수 숨김을 각각 하나의 regression test로 고정한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.py
~~~

기대 결과: 다언어 계측과 allowlist identity/schema regression PASS.

### [REVIEW_REFACTOR-3] task-local read set 합계와 budget ratchet

- 문제: `scripts/readability_audit.py:462`의 `read_sets` 인수는 미사용이고 `scripts/readability_audit_test.py:305`는 task별 파일 목록과 LOC 합계가 아니라 import 문자열만 비교한다. `scripts/readability_read_sets.json:4-144`의 ordered files와 budget/reason은 audit JSON에 나타나지 않는다.
- 해결 방법:

~~~text
Before
per-file import strings named read_set; task config ignored

After
task_read_sets[] = {task_id, ordered files[{path, loc}], total_loc, budget, reason}
missing/duplicate path and invalid budget = configuration failure
new/increased total or reasonless over-budget entry = ratchet failure
~~~

  task-local read set은 파일 확장자와 관계없이 명시된 경로의 physical LOC를 합산하며 JSON 순서는 config의 ordered path를 그대로 보존한다. 파일별 import 추출을 task-local read set으로 표기하지 않는다.
- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: read-set schema 검증, ordered path LOC, total/budget finding을 구현한다.
  - [ ] `scripts/readability_read_sets.json`: 각 task에 `max_total_loc`와 필요한 non-empty reason을 둔 하나의 schema로 정리한다.
  - [ ] `scripts/readability_audit_test.py`: ordered totals, missing/duplicate path, exact budget/one-over, empty reason, repeated JSON determinism 테스트를 추가한다.
  - [ ] `scripts/readability_baseline.json`: task total 기준선을 포함한다.
- 테스트 작성: 작성한다. 임시 트리의 code/Markdown/JSON 파일을 섞어 ordered path별 LOC와 total을 assert하고 동일 입력 JSON이 byte-identical함을 확인한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts.readability_audit_test.py
~~~

기대 결과: task read-set schema, LOC total, budget/reason, determinism regression PASS.

### [REVIEW_REFACTOR-4] post-commit projection과 baseline 재생성

- 문제: `scripts/readability_audit.py:315-339`의 `git ls-files -z`는 삭제된 index 파일을 나열하고 untracked 신규 파일을 누락한다. 현재 baseline은 233개만 보지만 post-commit projection은 303개이며 신규 함수 초과 14건이 있다.
- 해결 방법:

~~~text
Before
one implicit git ls-files input mode

After
--input-mode tracked: existing tracked files only
--input-mode worktree: tracked + non-ignored untracked - deleted files
Make target: --check --input-mode worktree
baseline: full current worktree projection after policy/parser/read-set fixes
~~~

  두 mode는 NUL-delimited Git 출력과 실제 파일 존재를 기준으로 정렬하고 input mode를 JSON에 기록한다. worktree mode가 현재 변경을 커밋한 뒤 tracked mode와 동일한 결과를 낼 수 있도록 baseline을 재생성한다.
- 수정 파일 및 체크리스트:
  - [ ] `scripts/readability_audit.py`: tracked/worktree input mode와 deleted/excluded 필터를 구현한다.
  - [ ] `scripts/readability_audit_test.py`: untracked 신규·tracked 수정·삭제·제외 파일 fixture의 mode별 경계 테스트를 추가한다.
  - [ ] `scripts/readability_baseline.json`: 수정된 전체 worktree projection을 기준으로 사유 필수 baseline을 재생성한다.
  - [ ] `Makefile`: `readability-audit` target을 worktree input mode로 실행한다.
- 테스트 작성: 작성한다. 임시 Git repo에서 tracked/untracked/deleted/generated path를 각각 만들고 mode별 정확한 sorted input을 assert한다.
- 중간 검증:

~~~bash
python3 -m unittest scripts/readability_audit_test.py
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup.json
~~~

기대 결과: unit PASS, 현재 신규/삭제를 포함한 audit ratchet PASS.

## 의존 관계 및 구현 순서

1. REVIEW_REFACTOR-1의 policy key/level을 고정한다.
2. REVIEW_REFACTOR-2의 function identity와 allowlist schema를 policy key에 연결한다.
3. REVIEW_REFACTOR-3의 task read-set total을 같은 audit/ratchet JSON에 연결한다.
4. REVIEW_REFACTOR-4에서 완성된 모델을 기준으로 baseline을 재생성하고 전체 검증한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `scripts/readability_audit.py` | REVIEW_REFACTOR-1, REVIEW_REFACTOR-2, REVIEW_REFACTOR-3, REVIEW_REFACTOR-4 |
| `scripts/readability_audit_test.py` | REVIEW_REFACTOR-1, REVIEW_REFACTOR-2, REVIEW_REFACTOR-3, REVIEW_REFACTOR-4 |
| `scripts/readability_baseline.json` | REVIEW_REFACTOR-1, REVIEW_REFACTOR-2, REVIEW_REFACTOR-3, REVIEW_REFACTOR-4 |
| `scripts/readability_read_sets.json` | REVIEW_REFACTOR-3 |
| `Makefile` | REVIEW_REFACTOR-4 |

## 최종 검증

~~~bash
python3 -m unittest scripts/readability_audit_test.py
rm -f /tmp/iop-readability-followup-a.json /tmp/iop-readability-followup-b.json /tmp/iop-readability-followup-tracked.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-a.json
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-readability-followup-b.json
cmp /tmp/iop-readability-followup-a.json /tmp/iop-readability-followup-b.json
python3 scripts/readability_audit.py --check --input-mode tracked --output /tmp/iop-readability-followup-tracked.json
make readability-audit
git diff --check
~~~

기대 결과: 전체 unit PASS, worktree JSON byte-identical, tracked/worktree mode의 실패 없음, 파일 분류별 threshold·함수 identity·read-set total·allowlist reason 증거가 JSON에 존재, Make target PASS, diff whitespace 오류 없음. repo 내 edge-node 진단, 보조 E2E smoke, full-cycle 실제 구동은 production/runtime 미변경으로 생략한다.

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