<!-- task=m-iop-agent-cli-runtime/18+14,15_cli_command_tree plan=3 tag=REVIEW_TEST -->

# Make CLI Output Regression Evidence Exact

## For the Implementing Agent

Filling implementation-owned sections in `CODE_REVIEW-*-G??.md` is mandatory. Run the verification commands exactly, paste literal stdout/stderr, keep active files in place, and report ready for review. Finalization is code-review-only. If blocked, record the exact blocker, attempted commands/output, and resume condition in implementation-owned evidence. Do not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, or write `complete.log`.

## Background

Pre-dispatch validation and response projection now behave correctly, but the submitted regressions do not enforce the exact contracts claimed by the review evidence. Several JSON variants are checked only for syntax, decoded checks cover selected fields, the command matrix accepts any non-empty output, and invalid-output errors are matched by substring. This follow-up is test-only and closes those evidence gaps without changing production DTOs or command behavior.

## Archive Evidence Snapshot

- Prior task: `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree`
- Predicted plan archive: `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/plan_cloud_G05_2.log`
- Predicted review archive: `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/code_review_cloud_G05_2.log`
- Verdict: FAIL
- Findings: 2 Required, 0 Suggested, 0 Nit.
- Affected files: `apps/agent/internal/command/root_test.go` and the active review evidence.
- Reviewer verification: both focused suites, the full package, race, vet, formatting, stale-symbol, and diff checks passed; source hashes were stable. Static review proved that the claimed exact/full JSON and exact-error assertions are absent.
- Roadmap carryover: this remains an S10 command-tree prerequisite and must not add `Roadmap Targets`; the downstream binary/contract closure owns `cli-surface` completion and the headless transcript.

## Analysis

### Files Read

- `apps/agent/internal/command/root.go`
- `apps/agent/internal/command/service.go`
- `apps/agent/internal/command/root_test.go`
- `apps/agent/internal/command/config_test.go`
- `agent-contract/inner/iop-agent-cli-runtime.md`
- `agent-contract/inner/agent-runtime.md`
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md`
- `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`
- `agent-task/archive/2026/07/m-iop-agent-cli-runtime/14+13_cli_config_fixtures/complete.log`
- `agent-task/archive/2026/07/m-iop-agent-cli-runtime/15+13_host_lifecycle/complete.log`
- `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/PLAN-cloud-G05.md`
- `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/CODE_REVIEW-cloud-G05.md`
- `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/plan_cloud_G05_1.log`
- `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/code_review_cloud_G05_1.log`

### SDD Criteria

- SDD: `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`, status `[승인됨]`, lock released.
- Targeted scenario: S10 / `cli-surface`.
- Evidence Map row: binary plus split-config CLI command integration and a headless operation transcript.
- This follow-up supplies trustworthy command-level output regression evidence only. It does not claim S10 completion, so `Roadmap Targets` remains omitted and the downstream binary task retains binary/transcript ownership.

### Verification Context

- No separate verification-context handoff was supplied.
- Repository-native sources: the agent and testing domain rules, local test rules, command source/tests, S10, and the two inner contracts listed above.
- Local preflight: Go resolves to `/config/.local/bin/go`; `go version go1.26.2 linux/arm64`; `GOROOT=/config/opt/go`.
- Fresh reviewer commands passed: both focused suites, full package, race, vet, `gofmt -d`, stale-symbol search, and `git diff --check`.
- Constraint: the worktree contains intentional sibling milestone work and `apps/agent` is untracked, so `git diff` alone is not an isolation oracle. The reviewed source hashes remained unchanged across verification.
- Split predecessors are satisfied by `agent-task/archive/2026/07/m-iop-agent-cli-runtime/14+13_cli_config_fixtures/complete.log` and `agent-task/archive/2026/07/m-iop-agent-cli-runtime/15+13_host_lifecycle/complete.log`.
- No external runner, binary, credential, or service is required. Cached test output is not acceptable; use `-count=1`.
- Confidence: high. The missing assertions are directly visible in the test source and the production output paths passed fresh verification.

### Test Coverage Gaps

- `TestCommandMatrix` records only whether stdout is non-empty instead of the exact text emitted by each command.
- No JSON command result is compared to an exact serialized value.
- Provider list, Milestone list/select, start, stop, and resume JSON are checked only with `json.Valid`.
- Validate, project list, preview, and status decode only selected fields instead of comparing the complete payload.
- `TestUnsupportedOutputFormat` matches the error by substring instead of enforcing the exact error.

### Symbol References

- No production symbols are renamed or removed.
- New test helpers remain local to `apps/agent/internal/command/root_test.go`.

### Split Judgment

Keep one compact follow-up. Exact text, exact JSON, complete decoded payloads, and the exact invalid-output error are one command-output evidence invariant across the same response matrix. Subtasks `14` and `15` are already satisfied by the exact predecessor completion logs listed above.

### Scope Rationale

Do not modify `root.go` or `service.go`; fresh review found their pre-dispatch validation, newline ownership, and response field projections correct. Do not add the binary, concrete `Service`, bootstrap wiring, contract changes, full-cycle transcript, or Roadmap completion evidence because existing downstream packets own them.

### Final Routing

- evaluation_mode: `isolated-reassessment`
- finalizer: `finalize-task-policy.sh pair`
- build closures: scope/context/verification/evidence/ownership/decision all `true`
- build grade scores: scope `1`, state `0`, blast `0`, evidence `2`, verification `1`
- build base route: `local-fit`; final route: `recovery-boundary`, `cloud-G04`, filename `PLAN-cloud-G04.md`
- review closures: scope/context/verification/evidence/ownership/decision all `true`
- review grade scores: scope `1`, state `0`, blast `0`, evidence `2`, verification `1`
- review route: `official-review`, `cloud-G04`, filename `CODE_REVIEW-cloud-G04.md`
- large_indivisible_context: `false`
- positive loop risks: `variant_product` (count `1`)
- recovery signals: `review_rework_count=3`, `evidence_integrity_failure=true`
- capability-gap evidence: none

## Implementation Checklist

- [ ] Replace weak output assertions with exact command-boundary text/JSON cases and complete decoded payload equality for every advertised response type.
- [ ] Require the exact unsupported-output error while preserving zero service calls, zero mutations, and empty stdout.
- [ ] Run fresh focused, package, race, vet, formatting, diff, and hash-stability verification and record literal stdout/stderr.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_TEST-1] Enforce Exact Text and JSON Output

#### Problem

`apps/agent/internal/command/root_test.go:336` accepts any non-empty command output. Lines 463-568 never compare JSON to exact serialized output; several response types use only `json.Valid`, and the decoded checks cover selected fields rather than the full payload.

#### Solution

Replace `wantOutput bool` with exact expected stdout in the text command matrix. Add one table-driven JSON command matrix covering validate, provider list, project list, Milestone list/select, preview, start, stop, resume, and status. Hard-code every expected field and compare both the serialized command output and the complete decoded JSON value. Use a local helper so every variant applies the same checks:

```go
import "reflect"

func assertExactJSON(t *testing.T, got, want string) {
	t.Helper()
	if got != want {
		t.Fatalf("unexpected JSON output:\ngot:  %q\nwant: %q", got, want)
	}
	var gotValue, wantValue any
	if err := json.Unmarshal([]byte(got), &gotValue); err != nil {
		t.Fatalf("decode actual JSON: %v", err)
	}
	if err := json.Unmarshal([]byte(want), &wantValue); err != nil {
		t.Fatalf("decode expected JSON: %v", err)
	}
	if !reflect.DeepEqual(gotValue, wantValue) {
		t.Fatalf("decoded JSON mismatch:\ngot:  %#v\nwant: %#v", gotValue, wantValue)
	}
}
```

Keep direct formatter assertions only where they add a distinct normal/boundary case; do not derive expected output by calling the production formatter under test.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/command/root_test.go` — assert exact text at the command boundary and exact/full decoded JSON for all response variants.

#### Test Strategy

Strengthen `TestCommandMatrix` and add `TestCommandJSONOutputMatrix`. Consolidate `TestStableTextAndJSONOutput` and `TestStatusIncludesOverlayIntegrationAndBlockers` only when the resulting cases still cover normal, empty-list, boolean, retryability, and integration-position values with hard-coded expected output.

#### Verification

```bash
go test -count=1 ./apps/agent/internal/command -run 'TestCommandMatrix|TestCommandJSONOutputMatrix|TestStableTextAndJSONOutput|TestStatusIncludesOverlayIntegrationAndBlockers'
```

Expected: every text/JSON command projection is exact, parses, and matches the complete expected value.

### [REVIEW_TEST-2] Enforce the Exact Invalid-Output Error

#### Problem

`apps/agent/internal/command/root_test.go:605` uses `strings.Contains`, so a wrapped, prefixed, or otherwise changed error still passes despite the plan's exact error requirement.

#### Solution

Compare the complete error string in every invalid-output table case:

```go
if got, want := err.Error(), "unsupported output format: xml"; got != want {
	t.Errorf("error = %q, want %q", got, want)
}
```

Retain the current zero service call, zero mutation, and empty stdout assertions for all read and mutation commands.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/command/root_test.go` — replace substring error matching with exact equality.
- [ ] `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/CODE_REVIEW-cloud-G04.md` — record literal output from every required command.

#### Test Strategy

Strengthen `TestUnsupportedOutputFormat` without changing its command table. Each case must compare the exact error and retain the zero-dispatch/zero-mutation/output assertions.

#### Verification

```bash
go test -count=1 ./apps/agent/internal/command -run 'TestUnsupportedOutputFormat'
```

Expected: all read and mutation commands reject `xml` with the exact error before dispatch.

## Dependencies and Execution Order

- Predecessor `14`: satisfied by `agent-task/archive/2026/07/m-iop-agent-cli-runtime/14+13_cli_config_fixtures/complete.log`.
- Predecessor `15`: satisfied by `agent-task/archive/2026/07/m-iop-agent-cli-runtime/15+13_host_lifecycle/complete.log`.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/agent/internal/command/root_test.go` | REVIEW_TEST-1, REVIEW_TEST-2 |
| `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/CODE_REVIEW-cloud-G04.md` | REVIEW_TEST-2 |

## Final Verification

```bash
sha256sum apps/agent/internal/command/root.go apps/agent/internal/command/service.go apps/agent/internal/command/root_test.go
gofmt -d apps/agent/internal/command/root_test.go
go test -count=1 ./apps/agent/internal/command -run 'TestCommandMatrix|TestCommandJSONOutputMatrix|TestStableTextAndJSONOutput|TestUnsupportedOutputFormat|TestStatusIncludesOverlayIntegrationAndBlockers'
go test -count=1 ./apps/agent/internal/command
go test -count=1 -race ./apps/agent/internal/command
go vet ./apps/agent/internal/command
git diff --check
sha256sum apps/agent/internal/command/root.go apps/agent/internal/command/service.go apps/agent/internal/command/root_test.go
```

Expected: before/after hashes match within the verification run; formatting has no diff; fresh focused, package, race, and vet checks pass; and the worktree has no whitespace errors.

After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
