<!-- task=m-change-aware-test-routing/05_user_entrypoint_runner_contract plan=1 tag=REVIEW_USER_ROUTE_RUN -->

# Plan - REVIEW_USER_ROUTE_RUN

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 마지막 단계다. 구현 후 아래 검증 명령을 실행하고 실제 stdout/stderr를 남긴 뒤 active 파일을 유지한 채 리뷰 준비 상태를 보고한다. 사용자 결정, 사용자 소유 환경, scope 충돌이 구현을 막으면 사용자에게 직접 묻지 말고 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 기록한다. finalization, `complete.log`, archive 이동은 code-review 전용이다.

## 배경

첫 리뷰에서 non-dry-run wrapper가 Bash `printf` option parsing 오류로 실행 결과와 결과 record 라인을 출력하지 못하는 것이 확인됐다. 또한 `run_test_by_change.sh --classify-only`가 분류만 출력하고 종료한다는 기존 CLI 계약과 다르게 `### 실행 계획`까지 출력한다. 이번 follow-up은 두 Required 이슈만 고치고, 실제 runner 장시간 실행 대신 temp repo stub smoke로 non-dry-run 출력 계약을 검증한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 chat에서 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. code-review가 사용자 리뷰 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/change-aware-test-routing.md`
- Task ids:
  - `routing-skill`: 프로젝트 스킬 또는 기존 `run-proto-socket-test-matrix` 스킬에 "작업된 내용을 바탕으로 테스트" 요청 라우팅 절차를 추가한다.
  - `auto-test-shell`: `run_test_by_change.sh` 자동 라우팅 shell을 추가해 `테스트 해봐` 요청의 기본 실행 단위로 쓴다.
  - `speed-test-shell`: `run_speed_test.sh` 속도 전용 shell을 추가해 quick/daytime과 full/nightly 후보를 관리한다.
  - `stability-test-shell`: `run_stability_test.sh` 안정성 전용 shell을 추가해 quick stress와 nightly long-run 후보를 관리한다.
  - `test-profile-docs`: `agent-test/local/rules.md`와 관련 profile 문서에 4개 shell entrypoint와 주야간 분리 기준을 반영한다.
  - `result-report-contract`: 결과 보고에 선택 이유, 실행 명령, 결과 record, skipped/nightly-pending 항목을 남긴다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-roadmap/current.md`
- `agent-roadmap/milestones/change-aware-test-routing.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
- `agent-test/local/rules.md`
- `agent-test/local/proto-socket-full-matrix.md`
- `agent-test/local/proto-socket-performance-baseline.md`
- `agent-task/m-change-aware-test-routing/05_user_entrypoint_runner_contract/plan_cloud_G07_0.log`
- `agent-task/m-change-aware-test-routing/05_user_entrypoint_runner_contract/code_review_cloud_G07_0.log`
- `agent-ops/skills/project/run-proto-socket-test-matrix/SKILL.md`
- `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh`
- `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh`
- `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh`
- `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh`
- `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh`

### 테스트 환경 규칙

- test_env: `local`.
- `agent-test/local/rules.md` 존재 및 읽음.
- 적용 profile: `agent-test/local/proto-socket-full-matrix.md`, `agent-test/local/proto-socket-performance-baseline.md`.
- 적용 명령: shell syntax는 `bash -n`; user-entrypoint route는 `run_test_by_change.sh --route <mode> --dry-run`; non-dry-run wrapper 계약은 temp repo stub runner smoke로 검증한다.
- 실제 `run_performance.sh --quick`, `run_stress.sh --quick`, `run_matrix.sh --all`은 장시간/환경 의존 실행이므로 이번 follow-up에서는 stub으로 대체한다. 목적은 wrapper의 stdout/stderr, exit-status, result-record reporting 계약 회복이다.

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

- 첫 구현 검증은 dry-run 중심이라 non-dry-run 출력 실패를 잡지 못했다. 이번 follow-up은 auto/speed/stability non-dry-run smoke에서 `invalid option` 부재와 `실행 명령`, `선택 이유`, `exit code`, `결과 기록`, `nightly-pending` 라인을 직접 검증한다.
- `--classify-only` 기존 옵션은 별도 smoke가 없었다. 이번 follow-up은 `### 실행 계획`이 출력되지 않는지 검증한다.

### 심볼 참조

- renamed/removed symbol: none.

### 분할 판단

- split decision policy를 평가했다.
- single plan으로 둔다. 두 Required 이슈는 같은 shell wrapper 출력 계약 안에 있고, 수정 파일이 같은 runner boundary에 모여 있다.
- API-vs-call-site split은 적용하지 않는다. public API 변경이 아니라 CLI option/reporting contract 복구다.
- test strategy는 동일한 shell syntax + temp repo smoke로 묶어 검증 가능하다.

### 범위 결정 근거

- 실제 performance/stress/full matrix runner 동작 변경은 범위 밖이다.
- anchor runner executor 구현은 범위 밖이다.
- roadmap 문서 상태 갱신은 범위 밖이다. PASS 시 code-review completion event를 통해 runtime이 처리한다.
- 새 test file은 만들지 않는다. shell wrapper 계약은 temp repo smoke와 deterministic `rg`로 검증한다.

### 빌드 등급

- build: `cloud-G07`; review: `cloud-G07`.
- shell/CLI workflow, stdout/stderr parsing, exit-status/result-record 계약 회복이 중심이므로 기존 cloud-G07 라우팅을 유지한다.

## 구현 체크리스트

- [ ] [REVIEW_USER_ROUTE_RUN-1] auto/speed/stability non-dry-run 출력에서 dash-leading `printf` 오류를 제거하고 결과 보고 라인이 실제로 출력되게 한다.
- [ ] [REVIEW_USER_ROUTE_RUN-2] `run_test_by_change.sh --classify-only`가 분류 결과만 출력하고 종료하도록 기존 CLI 계약을 복구한다.
- [ ] [REVIEW_USER_ROUTE_RUN-3] shell syntax, classify-only smoke, dry-run smoke, stubbed non-dry-run smoke, deterministic `rg` 검증을 실행하고 실제 stdout/stderr를 review stub에 남긴다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_USER_ROUTE_RUN-1] Non-dry-run printf/report contract

`문제`: `run_test_by_change.sh:828`, `run_speed_test.sh:85`, `run_stability_test.sh:88` 등에서 `printf '- ...'` 형식 문자열을 사용한다. Bash `printf`가 선행 `-`를 option으로 해석해 `printf: - : invalid option`을 내고, `실행 명령`, `선택 이유`, `exit code`, `결과 기록`, nightly-pending 후보 라인이 누락된다.

`해결 방법`: 새 non-dry-run 출력 중 format string이 `-`로 시작하는 모든 `printf`를 `printf -- '- ...\n'` 또는 `printf '%s\n' '- ...'`로 바꾼다. 같은 패턴이 남지 않았는지 deterministic search를 추가한다.

Before:

```bash
printf '- 실행 명령: `%s`\n' "$cmd"
```

After:

```bash
printf -- '- 실행 명령: `%s`\n' "$cmd"
```

`수정 파일 및 체크리스트`:

- [ ] `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh`
- [ ] `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh`
- [ ] `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh`

`테스트 작성`: 별도 test file은 만들지 않는다. temp repo stub runner smoke로 non-dry-run stdout/stderr 계약을 검증한다.

`중간 검증`:

```bash
rg --sort path -n "printf ['\"]-" agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh
```

기대: 결과 없음. 필요한 경우 `printf -- '- ...'` 패턴은 아래 명령으로 확인한다.

### [REVIEW_USER_ROUTE_RUN-2] Classify-only contract

`문제`: `run_test_by_change.sh:22`는 `--classify-only`를 "분류 결과만 출력하고 종료"로 문서화하지만, `run_test_by_change.sh:725`는 `dry_run`만 조기 반환한다. 실제 `bash .../run_test_by_change.sh --classify-only`가 `### 실행 계획`까지 출력했다.

`해결 방법`: 분류/파일 목록 출력 뒤 `classify_only=1`이면 추천 결과와 실행 계획을 출력하지 않고 반환한다. 상단 주석/usage의 "실제 테스트를 실행하지 않는다" 문구도 현재 non-dry-run 실행 계약과 충돌하지 않게 정리한다.

Before:

```bash
[ "$dry_run" -eq 1 ] && return
```

After:

```bash
[ "$classify_only" -eq 1 ] && return
[ "$dry_run" -eq 1 ] && return
```

`수정 파일 및 체크리스트`:

- [ ] `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh`

`테스트 작성`: 별도 test file은 만들지 않는다. 옵션 smoke로 검증한다.

`중간 검증`:

```bash
out="$(bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh --classify-only)" && ! printf '%s\n' "$out" | rg -q "### 라우팅 추천 결과|### 실행 계획|실행 예정|nightly-pending 후보"
```

### [REVIEW_USER_ROUTE_RUN-3] Verification evidence recovery

`문제`: 첫 구현의 review stub 검증은 dry-run smoke만 남겼고, non-dry-run wrapper 계약을 직접 실행하지 않아 Required 이슈를 놓쳤다.

`해결 방법`: syntax, classify-only, dry-run, non-dry-run stub smoke를 모두 실행하고 stdout/stderr를 review stub에 남긴다. temp repo smoke는 실제 장시간 runner 대신 stub `run_performance.sh`, `run_stress.sh`, `run_matrix.sh`가 record를 생성하게 만든다.

`수정 파일 및 체크리스트`:

- [ ] `agent-task/m-change-aware-test-routing/05_user_entrypoint_runner_contract/CODE_REVIEW-cloud-G07.md`

`테스트 작성`: 별도 test file은 만들지 않는다.

`중간 검증`: 아래 최종 검증 명령을 모두 실행한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh` | REVIEW_USER_ROUTE_RUN-1, REVIEW_USER_ROUTE_RUN-2, REVIEW_USER_ROUTE_RUN-3 |
| `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh` | REVIEW_USER_ROUTE_RUN-1, REVIEW_USER_ROUTE_RUN-3 |
| `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh` | REVIEW_USER_ROUTE_RUN-1, REVIEW_USER_ROUTE_RUN-3 |
| `agent-task/m-change-aware-test-routing/05_user_entrypoint_runner_contract/CODE_REVIEW-cloud-G07.md` | REVIEW_USER_ROUTE_RUN-3 |

## 최종 검증

```bash
bash -n agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh
bash -n agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh
bash -n agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh
out="$(bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh --classify-only)" && ! printf '%s\n' "$out" | rg -q "### 라우팅 추천 결과|### 실행 계획|실행 예정|nightly-pending 후보"
rg --sort path -n "printf ['\"]-" agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh
tmp="$(mktemp -d /tmp/proto-user-route-run-nondry.XXXXXX)" && repo="$tmp/repo" && scripts="$repo/agent-ops/skills/project/run-proto-socket-test-matrix/scripts" && mkdir -p "$scripts" "$repo/agent-test/runs" "$repo/go/transport" && cp agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh "$scripts/" && printf '%s\n' '#!/usr/bin/env bash' 'set -u' 'script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"' 'repo_root="$(cd "$script_dir/../../../../.." && pwd)"' 'mkdir -p "$repo_root/agent-test/runs"' 'record="$repo_root/agent-test/runs/20990101-000000-proto-socket-performance-quick.md"' 'ref="$(git -C "$repo_root" rev-parse --short HEAD 2>/dev/null || echo unknown)"' 'printf "%s\n" "---" "overall_result: PASS" "---" "" "- git ref: $ref" > "$record"' 'printf "%s\n" "결과 기록 파일: ${record#$repo_root/}"' > "$scripts/run_performance.sh" && printf '%s\n' '#!/usr/bin/env bash' 'set -u' 'script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"' 'repo_root="$(cd "$script_dir/../../../../.." && pwd)"' 'mkdir -p "$repo_root/agent-test/runs"' 'record="$repo_root/agent-test/runs/20990101-000001-proto-socket-stress-quick.md"' 'ref="$(git -C "$repo_root" rev-parse --short HEAD 2>/dev/null || echo unknown)"' 'printf "%s\n" "---" "overall_result: PASS" "---" "" "- git ref: $ref" > "$record"' 'printf "%s\n" "결과 기록 파일: ${record#$repo_root/}"' > "$scripts/run_stress.sh" && printf '%s\n' '#!/usr/bin/env bash' 'set -u' 'script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"' 'repo_root="$(cd "$script_dir/../../../../.." && pwd)"' 'mkdir -p "$repo_root/agent-test/runs"' 'record="$repo_root/agent-test/runs/20990101-000002-proto-socket-full-matrix.md"' 'ref="$(git -C "$repo_root" rev-parse --short HEAD 2>/dev/null || echo unknown)"' 'printf "%s\n" "---" "overall_result: PASS" "---" "" "- git ref: $ref" > "$record"' 'printf "%s\n" "결과 기록 파일: ${record#$repo_root/}"' > "$scripts/run_matrix.sh" && chmod +x "$scripts"/*.sh && cd "$repo" && git init -q && git config user.email smoke@example.com && git config user.name Smoke && printf 'package transport\n' > go/transport/server.go && git add . && git commit -q -m baseline && base_ref="$(git rev-parse --short HEAD)" && for profile in proto-socket-full-matrix proto-socket-performance-quick proto-socket-stress-quick; do record="agent-test/runs/20260610-000000-${profile}.md"; printf '%s\n' '---' 'overall_result: PASS' '---' '' "- git ref: $base_ref" > "$record"; done && git add agent-test/runs && git commit -q -m records && printf 'package transport\n' > go/transport/stress_payload_1mb.go && bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_test_by_change.sh --route auto > "$tmp/auto.out" 2>&1 && bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_speed_test.sh > "$tmp/speed.out" 2>&1 && bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stability_test.sh > "$tmp/stability.out" 2>&1 && ! rg -n "invalid option" "$tmp" && rg -n "실행 명령:|선택 이유:|exit code:|결과 기록:|nightly-pending 후보" "$tmp/auto.out" "$tmp/speed.out" "$tmp/stability.out"
git diff --check
```

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