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

# Repair the CLI Service Boundary and Verification

## 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 stop files, classify the next state, archive logs, or write `complete.log`.

## Background

The first review found that the command tree compiles only because its default snapshot loader manufactures a private dummy handle that a concrete service cannot consume. The fake-based matrix also omits `serve`, overstates the unselected-start assertion, does not validate JSON, and records output that cannot come from the stated commands. This follow-up makes the port consumable without taking ownership of the binary or runtime adapter and restores trustworthy command-level evidence.

## 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_local_G06_0.log`
- Predicted review archive: `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/code_review_cloud_G06_0.log`
- Verdict: FAIL
- Findings: 3 Required, 0 Suggested, 0 Nit.
- Affected files: `apps/agent/internal/command/root.go`, `apps/agent/internal/command/service.go`, `apps/agent/internal/command/root_test.go`, and the active review evidence.
- Reviewer verification: fresh focused, race, vet, formatting, and diff checks passed, but the fake suite did not exercise a usable production request boundary and the submitted stdout/stderr was reconstructed.
- 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 headless transcript evidence.

## 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/milestones/iop-agent-cli-runtime.md`
- `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`
- `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/PLAN-local-G06.md`
- `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/CODE_REVIEW-cloud-G06.md`

### 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 command integration and a headless operation transcript.
- This follow-up repairs only the command-level prerequisite: real config-path delegation, manual-selection error propagation, side-effect-free preview delegation, complete command parsing, and stable output. It does not claim the S10 Roadmap Task; the downstream binary/contract packet still owns binary and transcript evidence.

### Verification Context

- No separate verification-context handoff was supplied.
- Repository-native sources: agent domain rules, local test rules, the command tests, the S10 SDD row, and the two inner contracts listed above.
- Local preflight: Go resolves to `/config/opt/go/bin/go`; reviewer host reports `go1.26.2 linux/arm64`.
- Commands already confirmed in this checkout: focused package test, race test, vet, `gofmt -d`, and `git diff --check`.
- Constraint: the worktree contains intentional sibling milestone work, so this packet must modify only its exact claimed files and must confirm their hashes remain stable during verification.
- Gap: no binary or external runner is required here; binary/headless transcript verification remains downstream.
- Confidence: high for the command boundary and deterministic package tests.

### Test Coverage Gaps

- `serve` exists but is absent from `TestCommandMatrix`.
- Request DTO contents are not asserted, so dummy snapshot construction passes unnoticed.
- `TestStartRequiresSelectedMilestone` injects a prebuilt error and does not assert zero mutation.
- Text checks accept any non-empty output, JSON is not parsed, and unsupported output formats silently fall back to text.
- The review evidence does not contain literal output from the stated commands.

### Symbol References

- Remove `SnapshotHandle` and `loadSnapshot`; all references are confined to `apps/agent/internal/command/root.go`, `service.go`, and `root_test.go`.
- `NewRoot` is currently referenced only by `apps/agent/internal/command/root_test.go`; the downstream binary task is the future consumer.

### Split Judgment

Keep one atomic follow-up. The service request shape, command construction, and recording-fake assertions form one compatibility invariant and cannot independently PASS if separated.

### Scope Rationale

Do not add the `apps/agent/cmd/agent` entry point, a concrete runtime service, bootstrap wiring, local control, subprocesses, project logs, Makefile changes, contract edits, or Roadmap completion evidence. Those remain owned by their existing downstream packets.

### 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 `1`, evidence `2`, verification `1`
- build base route: `local-fit`; final route: `recovery-boundary`, `cloud-G05`, filename `PLAN-cloud-G05.md`
- review closures: scope/context/verification/evidence/ownership/decision all `true`
- review grade scores: scope `1`, state `0`, blast `1`, evidence `2`, verification `1`
- review route: `official-review`, `cloud-G05`, filename `CODE_REVIEW-cloud-G05.md`
- large_indivisible_context: `false`
- positive loop risks: `boundary_contract`, `variant_product` (count `2`)
- recovery signals: `review_rework_count=1`, `evidence_integrity_failure=true`
- capability-gap evidence: none

## Implementation Checklist

- [ ] Replace the dummy snapshot boundary with exact config-path requests owned by `Service`, and reject unsupported output formats.
- [ ] Strengthen the complete command, request, mutation, and stable text/JSON regression matrix.
- [ ] Run fresh focused, race, vet, formatting, stale-symbol, and diff verification and record literal stdout/stderr.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Make the Service Port Consumable

#### Problem

`apps/agent/internal/command/root.go:329` returns `SnapshotHandle{inner: struct{}{}}` without loading either configuration, while `apps/agent/internal/command/service.go:110` hides the payload from every concrete service outside the package. Commands also treat every unknown `--output` value as text.

#### Solution

Remove the command-owned snapshot loader and handle. Carry the exact repo-global and user-local paths through a small request value so the concrete `Service` owns loading and runtime adaptation:

```go
// Before: apps/agent/internal/command/service.go:103
type ProjectListRequest struct {
    Snapshot SnapshotHandle
}
```

```go
// After
type RuntimeConfigPaths struct {
    RepoGlobalPath string
    UserLocalPath  string
}

type ProjectListRequest struct {
    Config RuntimeConfigPaths
}
```

Apply the same request boundary to project, milestone, preview, start, stop, resume, and status commands. Make `printOutput` accept exactly `text` or `json` and return a typed CLI error for any other value.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/command/service.go` — replace the private snapshot handle with reusable exact config paths.
- [ ] `apps/agent/internal/command/root.go` — construct path-based requests, remove the dummy loader and unused imports, and validate output format.

#### Test Strategy

Regression tests are required in `root_test.go`: record exact request DTOs for every command and assert invalid output fails without a second service call.

#### Verification

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

Expected: the full matrix and invalid-output boundary pass with fresh execution.

### [REVIEW_API-2] Make the Command Evidence Meaningful

#### Problem

`apps/agent/internal/command/root_test.go:124` omits `serve`, checks only non-empty output, and does not assert request contents. `apps/agent/internal/command/root_test.go:232` supplies an arbitrary error rather than tying empty selection to zero mutations. Hand-built JSON is not validated and the prior review output was not literal output from its commands.

#### Solution

Extend the recording fake with captured requests, a mutation counter, and explicit selected-Milestone state. Cover every command including `serve`, assert exact flags and positional arguments, ensure preview and rejected start leave the mutation counter at zero, and compare exact stable text. Parse every advertised JSON response with `encoding/json`; use `encoding/json` in production formatting so arbitrary safe strings cannot produce Go-only escapes.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/command/service.go` — render response JSON through `encoding/json` while preserving deterministic field order.
- [ ] `apps/agent/internal/command/root_test.go` — add the complete matrix, request assertions, mutation assertions, exact text, valid JSON, and unsupported-format regression.
- [ ] `agent-task/m-iop-agent-cli-runtime/18+14,15_cli_command_tree/CODE_REVIEW-cloud-G05.md` — paste literal stdout/stderr from the exact commands.

#### Test Strategy

Add or strengthen `TestCommandMatrix`, `TestPreviewIsSideEffectFree`, `TestStartRequiresSelectedMilestone`, `TestStableTextAndJSONOutput`, and `TestUnsupportedOutputFormat`. Use only deterministic in-memory fakes and parse JSON with `encoding/json.Valid` or `json.Unmarshal`.

#### Verification

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

Expected: all command/config tests pass with fresh execution.

## Modified Files Summary

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

## Final Verification

```bash
gofmt -d apps/agent/internal/command/root.go apps/agent/internal/command/service.go apps/agent/internal/command/root_test.go
go test -count=1 ./apps/agent/internal/command
go test -count=1 -race ./apps/agent/internal/command
go vet ./apps/agent/internal/command
! rg --sort path -n 'loadSnapshot|SnapshotHandle' apps/agent/internal/command
git diff --check
```

Expected: formatting has no diff; fresh, race, and vet checks pass; removed placeholder symbols have no matches; and the worktree diff has no whitespace errors. Cached test output is not acceptable.

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