iop/agent-task/m-iop-agent-cli-runtime/plan_local_G07_0.log
toki 45d4bd98fd fix(agent): task-loop 검증 경계와 상태 검사를 보강한다
리뷰에서 확인된 Milestone 식별자와 컴파일 바이너리 상태 검증의 빈틈을 보완하고, 활성 agent-task에서는 dispatcher만 실행 경로로 사용하도록 고정한다.
2026-07-31 18:40:40 +09:00

383 lines
24 KiB
Text

<!-- task=m-iop-agent-cli-runtime plan=0 tag=API -->
# Plan - CLI surface completion
## For the Implementing Agent
`CODE_REVIEW-*-G??.md`의 구현 소유 섹션 작성은 필수다. 아래 구현과 검증 명령을 실행하고 실제 notes/output을 채운 뒤 활성 Plan/Review 파일을 그대로 둔 채 review 준비 완료를 보고한다. 최종 verdict, log rename, `complete.log`, archive 이동은 code-review skill만 수행한다. 막히면 정확한 blocker, 시도한 명령과 출력, 재개 조건만 구현 소유 evidence 필드에 기록하며 사용자 질문, user-input 도구, control-plane stop 파일, 다음 상태 분류, archive/log/`complete.log` 생성은 하지 않는다.
## Background
`cli-surface`는 현재 Milestone의 유일한 미완료 Task지만, `milestone list`가 Milestone이 아니라 선택된 Plan의 work unit을 출력하고 `status`가 여러 work의 overlay/integration을 마지막 값 하나로 축약한다. 또한 모든 같은-Milestone task가 archive된 현재 checkout에서 config-free dry-run이 active task directory 부재를 오류로 취급한다. S10을 닫으려면 조회·선택 가능한 Milestone 집합, work별 관측 투영, archive-only 완료 상태, 실제 binary transcript를 하나의 일관된 CLI 계약으로 고정해야 한다.
## Archive Evidence Snapshot
- `agent-task/archive/2026/07/m-iop-agent-cli-runtime/19+14,16,18_cli_binary_contract/complete.log`: PASS 시점에도 `cli-surface`는 partial이었고, complete S10 command transcript와 authoritative daemon/runtime wiring이 후속 조건으로 남았다.
- `agent-task/archive/2026/07/m-iop-agent-cli-runtime/24+19,21,22,23_daemon_wiring/complete.log`: daemon/runtime wiring과 lifecycle 검증은 PASS했으므로 이 Plan은 그 ownership을 재구성하지 않는다.
- `agent-task/archive/2026/07/m-iop-agent-cli-runtime/25+19,21,22,23,24_logged_smoke_closure/complete.log`: 로그인 macOS logged smoke와 field evidence는 PASS했다. 이번 변경은 provider 실행 경로가 아니라 S10 CLI 의미와 archive-only local regression을 닫는다.
- Roadmap carryover: `cli-surface`만 unchecked이며 exact `Roadmap Completion`과 fresh `go test -count=1 ./apps/agent/...` PASS가 필요하다.
## Roadmap Targets
- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md)
- Task ids:
- `cli-surface`: headless binary, split config, Milestone 조회·선택·preview, lifecycle, work별 overlay/integration/blocker 관측
- Completion mode: check-on-pass
## Analysis
### Files Read
- Runtime/CLI source: `apps/agent/internal/command/root.go`, `apps/agent/internal/command/service.go`, `apps/agent/cmd/agent/main.go`, `apps/agent/internal/taskloop/workflow.go`, `apps/agent/internal/taskloop/module.go`.
- Changed-behavior tests: `apps/agent/internal/command/root_test.go`, `apps/agent/cmd/agent/main_test.go`, `apps/agent/internal/taskloop/workflow_test.go`.
- Related call sites and state shape: `apps/agent/internal/bootstrap/module.go`, `apps/agent/internal/bootstrap/module_test.go`, `packages/go/agenttask/types.go`.
- Configuration/build inputs: `configs/iop-agent.runtime.yaml`, `configs/iop-agent.local.example.yaml`, `configs/iop-agent.providers.yaml`, `Makefile`, `go.mod`, `.gitignore`.
- Domain/verification sources: `agent-ops/rules/project/domain/agent/rules.md`, `agent-ops/rules/project/domain/testing/rules.md`, `agent-test/local/rules.md`, `agent-test/local/testing-smoke.md`.
- Roadmap/contract sources: `agent-roadmap/current.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-contract/index.md`, `agent-contract/inner/iop-agent-cli-runtime.md`.
- Prior evidence: the three exact archive paths in `Archive Evidence Snapshot`.
### SDD Criteria
- SDD: `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`; 상태는 승인·잠금 해제다.
- Target: Acceptance Scenario `S10` / Milestone Task `cli-surface`.
- Evidence Map: binary entry point, split configuration, validation, discovery, selection, lifecycle, status commands와 authoritative `taskloop.Runtime` composition, persisted lifecycle/restart, no-real-provider deterministic evidence를 요구한다.
- 이 기준은 checklist를 Milestone catalog/selection, work별 status, archive-only projection, built-binary transcript로 구성하게 했고, final verification에 focused/race/full Go suites, binary build/run, Darwin cross-build, logged-smoke preflight를 포함하게 했다.
### Verification Context
- Handoff: 없음. repository-native fallback으로 현재 source/tests, contract S10 change checklist, local test rules를 사용했다.
- Environment: local checkout `/config/workspace/iop-s0`, branch `feature/iop-agent-cli-runtime`, HEAD `fc361f363ccdcc4b25c37c9d4760f4b7864c3581`, upstream `origin/feature/iop-agent-cli-runtime`, clean/synced.
- Toolchain: `/config/.local/bin/go`, `go1.26.2 linux/arm64`; `make`와 `jq` 사용 가능. Module은 `go 1.24`이며 새 dependency는 필요 없다.
- Applied criteria: fresh `-count=1` tests, deterministic fakes only, actual changed CLI entrypoint full-cycle, race/vet/build/cross-build, logged-smoke host preflight, `git diff --check`.
- Baseline evidence: focused command/bootstrap/lifecycle tests와 `make build-agent`, tracked-config validate는 PASS했다. `go test -count=1 ./apps/agent/...`는 `TestRunTaskLoopConfigFreeDryRun` 하나만 active task group 부재로 FAIL했다.
- Constraints: unit/integration tests는 real provider CLI를 실행하지 않는다. Linux에서 logged-in macOS provider smoke를 재실행하지 않으며, 이미 PASS한 S14 evidence를 유지하고 이번 checkout에서는 non-Darwin preflight gate만 검증한다.
- Gaps/confidence: agent 전용 local profile은 없고 `testing-smoke`를 적용한다. repository-native Go tests와 Make targets가 명시적이므로 confidence는 medium-high다.
- External Verification Preflight: required external run 없음. Darwin artifact는 local cross-build만 수행하며 `/tmp/iop-agent-cli-surface-darwin-arm64`에 생성한다. live macOS/provider 검증은 이 변경의 필수 gate가 아니고 기존 S14 evidence 범위다.
### Test Coverage Gaps
- Milestone discovery: 기존 `TestRunFullHeadlessS10Transcript`는 `milestone list`가 work id `"1"`을 포함하는지만 확인해 잘못된 의미를 고정한다. 선택 전 조회, 둘 이상의 Milestone, list/select 동일 catalog 검증이 없다.
- Archive-only workflow: `scanWorkflow`는 archive를 읽기 전에 active directory 부재로 실패한다. `TestRunTaskLoopConfigFreeDryRun`은 repository 현재 상태에 의존하며 hermetic archive-only fixture가 없다.
- Status projection: command tests는 singular overlay/integration 하나만 검증한다. 둘 이상의 work id/state/dispatch ordinal/overlay/integration/blocker가 손실 없이 유지되는 회귀 테스트가 없다.
- Binary transcript: in-process `run` transcript와 serve signal binary test는 있으나 built binary로 list/select/preview/status/lifecycle 전체를 잇는 S10 transcript가 없다.
- Contract: S10 evidence row는 lifecycle wiring을 설명하지만 Milestone catalog, per-work status, archive-only completion semantics를 명시하지 않는다.
### Symbol References
- `MilestoneEntry`의 `Aliases`, `WriteSet`, `Isolation`은 실제 Milestone summary 필드로 교체한다. 참조는 `apps/agent/internal/command/service.go`, `apps/agent/cmd/agent/main.go`, `apps/agent/internal/command/root_test.go`에만 있다.
- `StatusResponse.Overlay`, `StatusResponse.Integration`, `StatusResponse.IntegrationPos`는 `Works []WorkStatusEntry`로 교체한다. 참조는 `apps/agent/internal/command/service.go`, `apps/agent/cmd/agent/main.go`, `apps/agent/internal/command/root_test.go`에만 있다.
- `WorkView`에는 durable `WorkRecord.DispatchOrdinal` 투영을 추가한다. 현재 생성/소비 call site는 `apps/agent/internal/taskloop/module.go`, `apps/agent/internal/bootstrap/module.go`이며 additive change다.
- `WorkflowSnapshot`은 제거하지 않는다. `apps/agent/cmd/agent/main.go`의 Milestone list 사용만 새 catalog API로 전환하고 bootstrap call sites는 유지한다.
### Split Judgment
단일 Plan으로 유지한다. 조회 결과에 나온 Milestone이 실제 selection parser와 동일해야 하고, 선택된 workflow의 각 work 상태와 archive-only 완료 상태가 같은 binary transcript에서 함께 증명되어야 한다. 이를 분리하면 Milestone list가 선택 불가능하거나 status가 새 runtime projection을 소비하지 않는 invalid intermediate state가 생기므로 독립 PASS 경계가 없다.
### Scope Rationale
- provider discovery/invocation, selection policy, quota/failure, daemon/local-control/client-process ownership은 변경하지 않는다.
- repo-global/local config schema와 durable state schema는 변경하지 않는다.
- `agenttask` integration queue 알고리즘은 변경하지 않고 이미 저장된 `DispatchOrdinal`만 읽기 projection에 노출한다.
- active Roadmap 문서는 구현 중 수정하지 않는다. PASS review의 completion event가 `cli-surface`만 check하도록 `Roadmap Targets`를 고정한다.
- logged-in macOS field smoke는 이미 S14에서 완료됐고 provider execution path가 변하지 않으므로 재실행하지 않는다.
### Final Routing
- evaluation_mode: `first-pass`
- finalizer: `finalize-task-policy.sh`, mode `pair`
- Build closures: scope/context/verification/evidence/ownership/decision 모두 `true`; capability gap 없음.
- Build grade scores: scope `2`, state `1`, blast `1`, evidence `1`, verification `2` = `G07`; base/final route `local-fit`, lane `local`.
- Review closures: 모두 `true`; grade `G07`; route `official-review`, lane `cloud`, adapter `codex`, model `gpt-5.6-sol`, reasoning `xhigh`.
- `large_indivisible_context=false`; positive loop risks: `boundary_contract`, `structured_interpretation` (`loop_risk_count=2`).
- Recovery signals: `review_rework_count=0`, `evidence_integrity_failure=false`; risk/recovery boundary 미충족.
- Canonical files: `PLAN-local-G07.md`, `CODE_REVIEW-cloud-G07.md`.
## Implementation Checklist
- [x] [API-1] Implement one deterministic selectable-Milestone catalog over active and archive-only task groups, make missing active directories archive-aware, and add normal/boundary regression tests.
- [x] [API-2] Replace work-unit-shaped Milestone output and lossy singular status fields with exact Milestone summaries and ordered per-work status DTOs, then update exact text/JSON and adapter tests.
- [x] [API-3] Make the S10 transcript and config-free dry-run hermetic, add a built-binary headless transcript, update the S10 contract evidence, and run the complete local verification matrix without real provider execution.
- [x] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.
### [API-1] Selectable Milestone catalog and archive-only workflow
**Problem**
`apps/agent/internal/taskloop/workflow.go:126-131` aborts before `readArchivedUnits` when `agent-task/m-<milestone>` is absent, even when the same Milestone has valid archived completions. `apps/agent/internal/taskloop/module.go:429-444` validates selection with that parser, and there is no API that lists the exact task-group identities it accepts.
Before (`apps/agent/internal/taskloop/workflow.go:126-131`):
```go
func scanWorkflow(root, milestone string) ([]agenttask.WorkUnit, string, error) {
activeRoot := filepath.Join(root, "agent-task", taskGroupPrefix+milestone)
entries, err := os.ReadDir(activeRoot)
if err != nil {
return nil, "", fmt.Errorf("taskloop: read active task group: %w", err)
}
```
After:
```go
func scanWorkflow(root, milestone string) ([]agenttask.WorkUnit, string, error) {
activeRoot := filepath.Join(root, "agent-task", taskGroupPrefix+milestone)
entries, err := readOptionalTaskGroup(activeRoot)
if err != nil {
return nil, "", fmt.Errorf("taskloop: read active task group: %w", err)
}
// Merge active units with same-Milestone complete.log evidence.
}
func scanMilestones(root string) ([]MilestoneView, error) {
// Discover exact m-* identities from active and archived task-group roots,
// deduplicate/sort them, and summarize each through scanWorkflow.
}
```
**Solution**
- Treat only `fs.ErrNotExist` for the exact active group as an empty active set; preserve fail-closed behavior for permission, malformed pair, ambiguous work id, and invalid path errors.
- Discover candidate Milestone ids from direct `agent-task/m-*` directories and `agent-task/archive/YYYY/MM/m-*` directories, validate exact slugs, deduplicate, sort, and summarize through the same `scanWorkflow` used by selection.
- Add `Runtime.Milestones(ctx, projectID)` returning id, selected flag, total/completed work counts. Keep `SelectMilestone` bound to `scanWorkflow`, so every listed entry is selectable and archive-only completed entries remain valid.
- Return an explicit error when no workflow-backed Milestone exists; do not infer Roadmap order or numeric dependencies.
**Modified Files and Checklist**
- [x] `apps/agent/internal/taskloop/workflow.go`: optional active group handling, catalog discovery, deterministic summary.
- [x] `apps/agent/internal/taskloop/module.go`: public read projection and selected-state decoration.
- [x] `apps/agent/internal/taskloop/workflow_test.go`: active+archive catalog, archive-only selection/inspection, malformed and empty boundaries.
**Test Strategy**
- Write `TestWorkflowMilestonesListsSelectableTaskGroups`: active and archived task groups are deduplicated/sorted and all entries pass selection parsing.
- Write `TestWorkflowArchiveOnlyMilestoneRemainsSelectable`: remove active group, create exact archive completion, assert snapshot/selection and completed counts.
- Write `TestInspectTaskGroupArchiveOnly`: configuration-free projection returns completed units without provider/runtime construction.
- Preserve existing malformed/missing/ambiguous artifact matrix.
**Verification**
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-cache go test -count=1 ./apps/agent/internal/taskloop -run '^(TestWorkflowMilestonesListsSelectableTaskGroups|TestWorkflowArchiveOnlyMilestoneRemainsSelectable|TestInspectTaskGroupArchiveOnly|TestWorkflowRejectsMalformedMissingAndAmbiguousArtifacts)$'
```
Expected: PASS; no provider process or durable mutation.
### [API-2] Lossless CLI Milestone and work status contracts
**Problem**
`apps/agent/cmd/agent/main.go:203-225` labels selected workflow work units as Milestones. `apps/agent/cmd/agent/main.go:415-430` overwrites singular overlay/integration fields while iterating ordered work, and fabricates `IntegrationPos` from slice index instead of durable dispatch ordinal.
Before (`apps/agent/cmd/agent/main.go:211-224`, `apps/agent/cmd/agent/main.go:415-430`):
```go
snapshot, err := reader.WorkflowSnapshot(ctx, req.Project)
entries := make([]command.MilestoneEntry, 0, len(snapshot.Units))
for _, unit := range snapshot.Units {
entries = append(entries, command.MilestoneEntry{
ID: string(unit.ID),
})
}
for index, work := range view.Works {
if work.Overlay != "" {
response.Overlay = work.Overlay
}
if work.Integration != "" {
response.Integration = work.Integration
response.IntegrationPos = index + 1
}
}
```
After:
```go
milestones, err := reader.Milestones(ctx, req.Project)
for _, milestone := range milestones {
entries = append(entries, command.MilestoneEntry{
ID: milestone.ID, Selected: milestone.Selected,
WorkUnits: milestone.WorkUnits,
CompletedWorkUnits: milestone.CompletedWorkUnits,
})
}
for _, work := range view.Works {
response.Works = append(response.Works, command.WorkStatusEntry{
ID: work.WorkUnitID, State: string(work.State),
Overlay: work.Overlay, Integration: work.Integration,
DispatchOrdinal: uint64(work.DispatchOrdinal),
})
}
```
**Solution**
- Redefine `MilestoneEntry` as an actual Milestone summary: id, selected, completed, work-unit count, completed-work count.
- Add `WorkStatusEntry` and replace `StatusResponse` singular overlay/integration/position fields with stable ordered `Works`; include work id, work state, actual `DispatchOrdinal`, overlay mode, integration outcome, and scoped blocker list.
- Project `WorkRecord.DispatchOrdinal` through `taskloop.WorkView`; never derive queue semantics from sorted index.
- Update text and JSON output exactly, always emitting empty arrays as `[]`, and update command help from “active PLAN work units” to workflow-backed Milestones.
- Keep top-level project blocker aggregation for compatibility with project status while preserving each work blocker in its work entry.
**Modified Files and Checklist**
- [x] `apps/agent/internal/taskloop/module.go`: add durable dispatch ordinal to `WorkView`.
- [x] `apps/agent/internal/command/service.go`: DTOs and stable text/JSON serializers.
- [x] `apps/agent/internal/command/root.go`: accurate Milestone list help text.
- [x] `apps/agent/cmd/agent/main.go`: catalog and per-work projection mapping.
- [x] `apps/agent/internal/command/root_test.go`: exact multi-entry text/JSON matrices and nil-to-empty arrays.
- [x] `apps/agent/cmd/agent/main_test.go`: list/select parity and multi-work adapter status regression.
**Test Strategy**
- Update `TestCommandMatrix`, `TestCommandJSONOutputMatrix`, `TestStatusIncludesOverlayIntegrationAndBlockers`, and `TestStableTextAndJSONOutput` with two work entries and exact ordinal/scoped blocker assertions.
- Add `TestRunMilestoneListAndSelectionShareCatalog`: unselected project lists both Milestones, selects one listed id, and persists it.
- Add `TestAdapterStatusPreservesAllWork`: two durable work records retain both ids/states/overlay/integration/ordinals without last-value collapse.
**Verification**
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-cache go test -count=1 ./apps/agent/internal/command ./apps/agent/cmd/agent -run '^(TestCommandMatrix|TestCommandJSONOutputMatrix|TestStatusIncludesOverlayIntegrationAndBlockers|TestStableTextAndJSONOutput|TestRunMilestoneListAndSelectionShareCatalog|TestAdapterStatusPreservesAllWork)$'
```
Expected: PASS with exact deterministic text/JSON.
### [API-3] Hermetic S10 binary evidence and contract closure
**Problem**
`apps/agent/cmd/agent/main_test.go:211-220` reads the current repository instead of a fixture, so archiving the active task group breaks the full suite. `apps/agent/cmd/agent/main_test.go:428-505` calls in-process `run` and expects work id `"1"` from `milestone list`, so it neither proves correct Milestone discovery nor the built binary surface.
Before (`apps/agent/cmd/agent/main_test.go:211-220`, `apps/agent/cmd/agent/main_test.go:440-444`):
```go
exit := run([]string{"task-loop", "--dry-run", "--task-group", "m-iop-agent-cli-runtime"}, stdout, stderr)
{
name: "milestone list",
args: append([]string{"milestone", "list", "iop-s0"}, readBase...),
want: "1",
},
```
After:
```go
root := newArchiveOnlyRepositoryFixture(t)
t.Chdir(root)
exit := run([]string{"task-loop", "--dry-run", "--task-group", "m-completed"}, stdout, stderr)
{
name: "milestone list",
args: append([]string{"milestone", "list", "iop-s0"}, readBase...),
want: "milestone-1",
},
```
**Solution**
- Make config-free dry-run create a temp module root and exact archive-only task-group completion fixture; assert `status=complete`, zero stderr, and no repo/global state dependency.
- Update the in-process S10 transcript so Milestone list shows actual ids, selection is limited to listed entries, status includes all work entries, and new invocations see persisted selection/lifecycle.
- Add `TestBuiltBinaryHeadlessS10Transcript` that builds `./apps/agent/cmd/agent` once into `t.TempDir()`, runs validate/list/select/preview/status/start/stop/resume as subprocesses with temp configs/workspace, and asserts no real provider invocation by using read-only commands plus the existing proof-owned fake/no-op lifecycle fixture boundary.
- Update `agent-contract/inner/iop-agent-cli-runtime.md` S10 evidence and change checklist with catalog/list-select parity, per-work status/ordinal, archive-only complete projection, and exact test names.
**Modified Files and Checklist**
- [x] `apps/agent/cmd/agent/main_test.go`: hermetic fixture, corrected in-process transcript, built-binary transcript.
- [x] `agent-contract/inner/iop-agent-cli-runtime.md`: S10 evidence/verification contract.
- [x] `agent-task/m-iop-agent-cli-runtime/CODE_REVIEW-cloud-G07.md`: record actual implementation decisions and raw verification output.
**Test Strategy**
- Update `TestRunTaskLoopConfigFreeDryRun` to be hermetic and require `status=complete`.
- Update `TestRunFullHeadlessS10Transcript` to assert real Milestone ids and per-work status.
- Write `TestBuiltBinaryHeadlessS10Transcript` for the compiled entrypoint and exact exit/stdout/stderr behavior.
- Keep fake-provider lifecycle/rollback/restart and daemon signal tests as non-real-provider regression evidence.
**Verification**
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-cache go test -count=1 ./apps/agent/cmd/agent -run '^(TestRunTaskLoopConfigFreeDryRun|TestRunFullHeadlessS10Transcript|TestBuiltBinaryHeadlessS10Transcript|TestCommandAdapterFakeProviderPersistedLifecycleRollbackAndRestart|TestRunPersistedSelectionEnablesPreviouslyUnselectedStart|TestRunServeCancellationAndCleanup|TestBuiltBinarySignalShutdownCleansDaemon)$'
```
Expected: PASS; test logs show no real provider CLI launch.
## Modified Files Summary
| File | Item |
|------|------|
| `apps/agent/internal/taskloop/workflow.go` | API-1 |
| `apps/agent/internal/taskloop/module.go` | API-1, API-2 |
| `apps/agent/internal/taskloop/workflow_test.go` | API-1 |
| `apps/agent/internal/command/service.go` | API-2 |
| `apps/agent/internal/command/root.go` | API-2 |
| `apps/agent/internal/command/root_test.go` | API-2 |
| `apps/agent/cmd/agent/main.go` | API-2 |
| `apps/agent/cmd/agent/main_test.go` | API-2, API-3 |
| `agent-contract/inner/iop-agent-cli-runtime.md` | API-3 |
| `agent-task/m-iop-agent-cli-runtime/CODE_REVIEW-cloud-G07.md` | API-3 |
## Final Verification
Fresh execution is required; cached Go test output is not acceptable.
```bash
gofmt -w apps/agent/internal/taskloop/*.go apps/agent/cmd/agent/*.go apps/agent/internal/command/*.go
```
Expected: formatting completes without error.
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-cache go test -count=1 ./apps/agent/internal/taskloop ./apps/agent/internal/command ./apps/agent/cmd/agent
```
Expected: focused packages PASS.
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-race-cache go test -count=1 -race ./apps/agent/internal/taskloop ./apps/agent/internal/command ./apps/agent/cmd/agent ./apps/agent/internal/bootstrap ./packages/go/agenttask ./packages/go/agentstate
```
Expected: race suites PASS with deterministic fakes and no real provider process.
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-cache go test -count=1 ./apps/agent/...
```
Expected: all agent packages PASS, including archive-only config-free dry-run.
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-cache go vet ./apps/agent/internal/taskloop ./apps/agent/internal/command ./apps/agent/cmd/agent ./apps/agent/internal/bootstrap ./packages/go/...
```
Expected: no vet findings.
```bash
make build-agent
build/bin/iop-agent --help
build/bin/iop-agent validate --repo-config configs/iop-agent.runtime.yaml --local-config configs/iop-agent.local.example.yaml --provider-catalog configs/iop-agent.providers.yaml
```
Expected: binary builds, full command surface is shown, tracked split configs validate.
```bash
TMPDIR=/tmp GOTMPDIR=/tmp GOCACHE=/tmp/iop-cli-surface-darwin-cache GOOS=darwin GOARCH=arm64 go build -trimpath -o /tmp/iop-agent-cli-surface-darwin-arm64 ./apps/agent/cmd/agent
```
Expected: Darwin arm64 binary is produced outside the repository.
```bash
make test-iop-agent-logged-smoke-preflight
```
Expected: syntax/schema/self-test PASS and Linux host gate exits through the expected non-Darwin preflight path before provider login or process launch.
```bash
rg --sort path -n '\b(MilestoneEntry|MilestoneListResponse|StatusResponse|WorkStatusEntry|IntegrationPos|WorkView)\b' apps/agent agent-contract/inner/iop-agent-cli-runtime.md
git diff --check
git status --short
```
Expected: all changed symbol call sites are intentional, `IntegrationPos` has no production/test references, no whitespace errors, and only declared task/source files are modified.
After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.