<!-- task=m-agent-readable-repository-refactor/13_inventory_query plan=4 tag=REVIEW_REVIEW_REVIEW_REVIEW_IQRY -->

# Inventory query 미적용 구현 및 검증 evidence 복구 계획

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

구현과 검증을 마친 뒤 `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션에 실제 변경 내용과 명령 출력을 채우고 active 파일을 그대로 둔 채 리뷰 준비 상태를 보고한다. 종결, 로그 아카이브, `complete.log` 작성은 code-review 전용이다. 선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 실제 구현을 막는 경우에만 review stub의 `사용자 리뷰 요청`에 연결 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`/로그/`complete.log`를 만들지 않는다. 환경·secret·서비스 차단, 일반 범위 변경, 후속 에이전트가 채울 수 있는 evidence 공백은 사용자 리뷰 요청이 아니다.

## 배경

네 번째 리뷰 루프에서 이전 계획의 production/test 분리 구현이 전혀 적용되지 않은 상태가 확인됐다. 기존 package test는 통과하지만 scalar regression은 fixture overwrite로 실행되지 않고 readability ratchet의 신규 위반 4건도 그대로다. 구현과 exact 검증을 실제로 수행하고 review stub의 evidence까지 채워 `inventory-query` 완료 판단이 가능하게 한다.

## 사용자 리뷰 요청 흐름

선택된 Milestone의 `구현 잠금 > 결정 필요`만 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 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:
  - `inventory-query`: env/model/node/provider selector로 canonical inventory의 bounded 결과를 얻는 결정적 query 경로
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Prior task: `agent-task/m-agent-readable-repository-refactor/13_inventory_query`
- Archived plan: `agent-task/m-agent-readable-repository-refactor/13_inventory_query/plan_local_G06_3.log`
- Archived review: `agent-task/m-agent-readable-repository-refactor/13_inventory_query/code_review_local_G06_3.log`
- Verdict: FAIL
- Required: 3 — provider collector 분리 미적용, scalar/entity/dedupe regression 분리 미적용, 구현·검증 review evidence 미작성
- Suggested: 0
- Nit: 0
- Affected files: `scripts/inventory-query/main.go`, `scripts/inventory-query/main_test.go`, 누락된 `scripts/inventory-query/provider_query.go`, 누락된 `scripts/inventory-query/provider_query_test.go`, active review stub
- Verification evidence: 기존 `go test -count=1 ./scripts/inventory-query`와 `git diff --check`는 PASS했다. test list에는 `TestQueryProviderMatchesMapEntries`가 없었다. worktree readability audit은 `main.go` 558 LOC, `queryProviders` 141 LOC, `main_test.go` 830 LOC, `TestQueryProviderMatchesMapScalarAndNestedEntity` 94 LOC의 신규 위반으로 exit 4였다.
- Roadmap carryover: `inventory-query`는 PASS 전까지 미완료다.
- Allowed narrow reread: 위 archived plan/review 두 파일만 추가 세부가 필요할 때 읽는다.

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/private/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-ops/rules/project/domain/platform-common/rules.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/testing-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/README.md`
- `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-spec/index.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`
- `agent-contract/index.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `scripts/inventory-query/main.go`
- `scripts/inventory-query/main_test.go`
- `agent-task/m-agent-readable-repository-refactor/13_inventory_query/plan_local_G06_3.log`
- `agent-task/m-agent-readable-repository-refactor/13_inventory_query/code_review_local_G06_3.log`

### SDD 기준

Milestone은 `SDD: 불필요`다. 외부 API/proto/config schema나 runtime 책임 경계를 바꾸지 않고 동일 package 안의 책임 분리와 테스트 재배치를 수행하는 동작 보존형 리팩터링이라는 기록 사유를 유지한다.

### 테스트 환경 규칙

`test_env=local`이다. `agent-test/local/rules.md`, `agent-test/local/testing-smoke.md`, `agent-test/local/platform-common-smoke.md`가 존재해 모두 읽었다. 외부 provider/runtime 없이 현재 checkout에서 fresh targeted/package/full Go test, compiled binary의 canonical dev/dev-corp selector 및 0/1/2/help 계약, worktree readability ratchet, `git diff --check`를 적용한다. Docker, 외부 host, endpoint, secret은 필요하지 않으며 binary/cache/JSON evidence는 `/tmp`에만 둔다.

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

- 기존 `TestQueryProviderMatchesMapScalarAndNestedEntity`는 scalar fixture를 query 전에 덮어써 scalar map value 동작을 검증하지 않는다.
- 계획된 `TestQueryProviderMatchesMapEntries`와 focused test file이 없으므로 scalar, entity `id`, entity `provider`, key+value dedupe case의 독립 실행 증거가 없다.
- 기존 canonical provider shape, 정렬, dedupe, binary exit/help 테스트는 유지되며 분리 뒤 회귀 기준으로 재사용할 수 있다.
- readability audit은 현재 신규 위반 4건을 재현하며 구현 후 새 위반 0건이어야 한다.

### 심볼 참조

외부 rename/remove는 없다. `queryProviders` 참조는 `scripts/inventory-query/main.go`와 `scripts/inventory-query/main_test.go`의 동일 `main` package 안에 한정된다. private helper 이동만 수행하고 CLI/API 소비자는 변경하지 않는다.

### 분할 판단

split decision policy를 재평가했다. production collector 분리와 regression 분리는 같은 provider traversal 계약 및 하나의 readability audit 결과로 함께 닫혀야 하고 동일 package·검증 환경을 공유한다. 별도 subtask로 나누면 한쪽이 완료돼도 ratchet이 계속 실패하며 같은 파일을 반복 수정하므로 기존 `13_inventory_query` 단일 follow-up이 더 안전하다.

### 범위 결정 근거

`agent-test/*/inventory.yaml`, `agent-test/README.md`, deploy skill, agent-spec, agent-contract, roadmap, API/proto/config/runtime 코드는 수정하지 않는다. CLI JSON/exit/help/schema 동작, readability baseline/threshold/allowlist는 바꾸지 않는다. 변경 범위는 `scripts/inventory-query` package와 active review evidence에 한정한다.

### 빌드 등급

`local-G06`. 같은 Go package의 private traversal/test 분리이며 exact unit/binary/audit 명령으로 완전히 판정할 수 있고 외부·interactive 판단이 없다.

## 구현 체크리스트

- [ ] provider traversal을 명명된 collector/helper 책임으로 실제 분리하고 기존 canonical path/value/sort/dedupe 동작을 유지하면서 production LOC/함수 크기 위반을 제거한다.
- [ ] scalar/entity `id`/entity `provider`/key+value dedupe provider map regression을 독립 subtest로 실제 실행하고 focused test file로 분리해 test LOC/함수 크기 위반을 제거한다.
- [ ] fresh targeted/package/full Go test, canonical selector, compiled 0/1/2/help, worktree readability audit와 diff 검증을 exact 명령으로 실행하고 실제 stdout/stderr를 review stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_IQRY-1] Provider collector 구현 적용

#### 문제

`scripts/inventory-query/main.go:317`의 `queryProviders`가 141 LOC로 entry match, singular provider, map/list collection, direct provider, generic recursion을 계속 한 함수에 보유한다. `scripts/inventory-query/provider_query.go`도 존재하지 않아 `main.go` 558 LOC와 함수 크기 신규 위반이 유지된다.

#### 해결 방법

같은 `main` package에 `provider_query.go`를 추가하고 `selector`, `matches`, `seen`을 소유하는 명명된 collector를 둔다. `queryProviders`는 collector 생성, traversal, 정렬만 담당하며 map/list entry, singular/direct provider 처리는 작은 메서드로 분리한다. 기존 exact selector, YAML path, actual value, 정렬, path dedupe 계약은 유지한다.

Before (`scripts/inventory-query/main.go:317`):

```go
func queryProviders(data map[string]interface{}, selector string) []match {
	var matches []match
	seen := make(map[string]bool)
	// 모든 provider traversal branch
}
```

After:

```go
func queryProviders(data map[string]interface{}, selector string) []match {
	collector := newProviderCollector(selector)
	collector.collect(data, "")
	sortMatches(collector.matches)
	return collector.matches
}
```

#### 수정 파일 및 체크리스트

- [ ] `scripts/inventory-query/main.go` — 기존 provider traversal block 제거, 공통 CLI/query 경계 유지.
- [ ] `scripts/inventory-query/provider_query.go` — 명명된 collector와 entry/collection traversal helper 추가.

#### 테스트 작성

이 항목 자체의 새 테스트는 추가하지 않는다. 기존 provider tests와 다음 항목의 독립 regression이 동작 보존을 검증한다.

#### 중간 검증

```bash
gofmt -d scripts/inventory-query/main.go scripts/inventory-query/provider_query.go
GOCACHE=/tmp/iop-inventory-query-followup-gocache go test -count=1 ./scripts/inventory-query -run 'TestQueryProvider'
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-inventory-readability-provider.json
```

gofmt가 무출력이고 provider tests와 readability audit이 exit 0이어야 한다.

### [REVIEW_REVIEW_REVIEW_REVIEW_IQRY-2] 독립 provider map regression 적용

#### 문제

`scripts/inventory-query/main_test.go:596`은 scalar fixture를 `main_test.go:613`에서 덮어쓴 뒤 query하므로 scalar case를 실행하지 않는다. 계획된 `provider_query_test.go`와 `TestQueryProviderMatchesMapEntries`가 없고, 기존 파일 830 LOC/함수 94 LOC 위반도 유지된다.

#### 해결 방법

기존 긴 regression을 `main_test.go`에서 제거하고 `provider_query_test.go`의 table-driven `TestQueryProviderMatchesMapEntries`로 대체한다. 각 subtest는 새 fixture에서 scalar value, entity `id`, entity `provider`, key+value dedupe를 독립 실행하고 exact entry path/value, 중복 없음, 정렬을 assert한다.

Before (`scripts/inventory-query/main_test.go:608`):

```go
activeGroup["providers"] = map[string]interface{}{
	"scalar-provider": "gx10-vllm",
}
activeGroup["providers"] = map[string]interface{}{
	"some-other-key": map[string]interface{}{"id": "gx10-vllm"},
}
matches := queryProviders(data, "gx10-vllm")
```

After:

```go
for _, tc := range cases {
	t.Run(tc.name, func(t *testing.T) {
		data := parseFixture(t)
		setProviderEntries(t, data, tc.entries)
		assertProviderEntryMatches(t, queryProviders(data, selector), tc)
	})
}
```

#### 수정 파일 및 체크리스트

- [ ] `scripts/inventory-query/main_test.go` — 덮어쓰는 긴 regression 제거.
- [ ] `scripts/inventory-query/provider_query_test.go` — 독립 scalar/entity/dedupe cases와 focused helper 추가.

#### 테스트 작성

작성한다. `TestQueryProviderMatchesMapEntries`에서 네 case를 독립 실행하고 exact path/value, path uniqueness, sort order를 검증한다.

#### 중간 검증

```bash
set -o pipefail
gofmt -d scripts/inventory-query/main_test.go scripts/inventory-query/provider_query_test.go
GOCACHE=/tmp/iop-inventory-query-followup-gocache go test ./scripts/inventory-query -list '^TestQueryProviderMatchesMapEntries$' | grep -qx 'TestQueryProviderMatchesMapEntries'
GOCACHE=/tmp/iop-inventory-query-followup-gocache go test -count=1 ./scripts/inventory-query -run '^TestQueryProviderMatchesMapEntries$'
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-inventory-readability-provider-test.json
```

test name grep과 fresh test, readability audit이 모두 exit 0이고 gofmt는 무출력이어야 한다.

### [REVIEW_REVIEW_REVIEW_REVIEW_IQRY-3] Exact 검증과 review evidence 완성

#### 문제

`agent-task/m-agent-readable-repository-refactor/13_inventory_query/code_review_local_G06_3.log:60` 이하의 구현 완료 표·체크리스트는 전부 미체크였고, 설계/변경 기록은 placeholder, 검증 결과는 모두 `(output)`이었다. 실제 구현과 검증을 리뷰가 신뢰할 수 없다.

#### 해결 방법

모든 코드 변경 뒤 아래 최종 검증을 exact하게 실행하고 active `CODE_REVIEW-local-G06.md`의 완료 표, 구현 체크리스트, 계획 대비 변경, 주요 설계 결정, 검증 결과에 실제 내용과 stdout/stderr를 기록한다. 출력이 긴 `go test ./...`는 `/tmp` evidence 파일에 저장하고 명령, exit code, 파일 경로, 마지막 요약을 남긴다.

#### 수정 파일 및 체크리스트

- [ ] `agent-task/m-agent-readable-repository-refactor/13_inventory_query/CODE_REVIEW-local-G06.md` — 모든 구현 소유 필드와 actual output 완성.

#### 테스트 작성

새 테스트를 추가하지 않는다. 이 항목은 앞 항목의 테스트 존재/실행 여부와 전체 검증 evidence를 확정한다.

#### 중간 검증

```bash
rg -n '^\\| \\[REVIEW_REVIEW_REVIEW_REVIEW_IQRY-|^- \\[ \\]|\\(output\\)|_구현 에이전트가' agent-task/m-agent-readable-repository-refactor/13_inventory_query/CODE_REVIEW-local-G06.md
```

구현 소유 영역에서 미완료 체크박스, `(output)`, 구현 placeholder 문구가 없어야 한다. 코드리뷰 전용 체크리스트의 미체크 항목은 이 검색 판정에서 제외해 기록한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `scripts/inventory-query/main.go` | REVIEW_REVIEW_REVIEW_REVIEW_IQRY-1 |
| `scripts/inventory-query/provider_query.go` | REVIEW_REVIEW_REVIEW_REVIEW_IQRY-1 |
| `scripts/inventory-query/main_test.go` | REVIEW_REVIEW_REVIEW_REVIEW_IQRY-2 |
| `scripts/inventory-query/provider_query_test.go` | REVIEW_REVIEW_REVIEW_REVIEW_IQRY-2 |
| `CODE_REVIEW-local-G06.md` | REVIEW_REVIEW_REVIEW_REVIEW_IQRY-3 및 모든 항목 evidence |

## 최종 검증

```bash
export GOCACHE=/tmp/iop-inventory-query-followup-gocache
mkdir -p "$GOCACHE"
set -o pipefail
gofmt -d scripts/inventory-query/main.go scripts/inventory-query/provider_query.go scripts/inventory-query/main_test.go scripts/inventory-query/provider_query_test.go
go test ./scripts/inventory-query -list '^TestQueryProviderMatchesMapEntries$' | grep -qx 'TestQueryProviderMatchesMapEntries'
go test -count=1 ./scripts/inventory-query -run 'Test(QueryProviderMatchesMapEntries|QueryProviderAcrossCanonicalShapes|ValidateInventoryRejectsInvalidProjectionObjectTypes|ValidateInventoryRequiresProviderModelAndNodes|RunClassifiesProviderSchemaError|RunHelpUsesInjectedStreams)$'
go test -count=1 ./scripts/inventory-query
go build -o /tmp/iop-inventory-query-followup ./scripts/inventory-query
/tmp/iop-inventory-query-followup --env dev --provider gx10-vllm > /tmp/iop-inventory-followup-dev-provider.json
/tmp/iop-inventory-query-followup --env dev-corp --provider corp-dgx-spark-01-ornith > /tmp/iop-inventory-followup-corp-provider.json
python3 - <<'PY'
import json

with open("/tmp/iop-inventory-followup-dev-provider.json", encoding="utf-8") as handle:
    dev = json.load(handle)
with open("/tmp/iop-inventory-followup-corp-provider.json", encoding="utf-8") as handle:
    corp = json.load(handle)

dev_paths = {item["path"]: item["value"] for item in dev["matches"]}
corp_paths = {item["path"]: item["value"] for item in corp["matches"]}
assert isinstance(dev_paths["model.active_edge_model_group.providers.gx10-vllm"], dict)
assert isinstance(dev_paths["nodes[1].provider"], dict)
assert corp_paths["model.aliases.ornith:35b.providers[0]"] == "corp-dgx-spark-01-ornith"
assert isinstance(corp_paths["nodes[0].provider"], dict)
PY
set +e
/tmp/iop-inventory-query-followup --help >/tmp/iop-inventory-followup-help.out 2>/tmp/iop-inventory-followup-help.err
help_rc=$?
/tmp/iop-inventory-query-followup --env dev --node definitely-not-a-node >/tmp/iop-inventory-followup-zero.out 2>/tmp/iop-inventory-followup-zero.err
zero_rc=$?
/tmp/iop-inventory-query-followup --env unsupported >/tmp/iop-inventory-followup-error.out 2>/tmp/iop-inventory-followup-error.err
error_rc=$?
set -e
test "$help_rc" -eq 0
test "$zero_rc" -eq 1
test "$error_rc" -eq 2
test -s /tmp/iop-inventory-followup-help.out
test ! -s /tmp/iop-inventory-followup-help.err
test ! -s /tmp/iop-inventory-followup-zero.out
test ! -s /tmp/iop-inventory-followup-error.out
python3 scripts/readability_audit.py --check --input-mode worktree --output /tmp/iop-inventory-readability-followup-final.json
go test -count=1 ./... | tee /tmp/iop-inventory-followup-go-test-all.out
git diff --check
```

모든 명령은 exit 0, gofmt/diff-check는 무출력이어야 한다. Readability audit은 baseline/threshold/allowlist 변경 없이 새 위반 0건이어야 하며 Go test는 `-count=1` fresh 실행을 사용한다.

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