리뷰에서 확인된 Milestone 식별자와 컴파일 바이너리 상태 검증의 빈틈을 보완하고, 활성 agent-task에서는 dispatcher만 실행 경로로 사용하도록 고정한다.
383 lines
24 KiB
Text
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`.
|