<!-- task=m-iop-agent-cli-runtime/23+14,16,22_client_process_manager plan=0 tag=API -->

# Daemon-Owned Flutter and Unity Process Manager

## For the Implementing Agent

Complete all implementation-owned fields in `CODE_REVIEW-cloud-G10.md` with actual verification output, leave active artifacts in place, and report ready for review. The official reviewer alone owns next-state classification, logs, archives, and `complete.log`. Record blockers and resume evidence without asking the user or creating control-plane files.

## Background

The single device-local daemon must remain the only process owner for future Flutter and Unity clients. S15 requires duplicate-safe launch, crash/reconnect handling, and Unity detail routing through the daemon to Flutter without allowing client exit to stop runtime ownership.

## 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:
  - `client-process-manager`: daemon-owned Flutter/Unity start, stop, reconnect, crash recovery, and detail routing
- Completion mode: check-on-pass

## Analysis

### Files Read

- `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/inner/agent-runtime.md`
- `agent-contract/inner/iop-agent-cli-runtime.md`
- `packages/go/agentconfig/runtime_config.go`
- `packages/go/agentconfig/runtime_config_test.go`
- `packages/go/agentconfig/watcher.go`
- `packages/go/agentstate/store.go`
- `packages/go/agenttask/ports.go`
- `packages/go/agenttask/types.go`
- `go.mod`

### SDD Criteria

Approved SDD scenario S15 maps to `client-process-manager`. The Evidence Map requires fixture Flutter/Unity process ownership, duplicate launch convergence, disconnect/crash/reconnect, and Unity detail command evidence with PID/start/focus traces. The checklist also enforces the contract states `stopped|starting|connected|crashed`, user-local policy ownership, and daemon survival when clients exit.

### Verification Context

No handoff was supplied. Repository-native sources are local rules plus platform-common/testing smoke profiles. Current runner is Linux arm64 with Go 1.26.2; fixture clients must be the test binary itself (`-test.run=TestHelperProcess`) so no external Flutter/Unity SDK is required. Use fresh and race tests, process cleanup assertions, Darwin cross-build, `go vet`, and `git diff --check`. Cached output is not accepted.

### Test Coverage Gaps

- User-local config has no `ClientProcessSpec`.
- No daemon-owned subprocess manager or stable process identity record exists.
- No local-control handler binds client operations to a process owner.
- No test proves client exit leaves the host alive or Unity detail cannot bypass Flutter routing.

### Symbol References

`agentconfig.UserLocalRuntimeConfig`, `RuntimeConfig`, and their deep-copy/validation paths gain client specifications; all constructors/tests that compare these structs must be updated. Search call sites with the exact final verification command before editing.

### Split Judgment

Process state, durable identity, restart policy, and local-control commands are one invariant and stay together. Predecessors 14, 16, and 22 are encoded in the directory name; none currently has an active or archived `complete.log`. The manager consumes the completed S11 protocol and extends the user-local example created by the configuration-fixture packet rather than changing either boundary.

### Scope Rationale

Do not implement Flutter or Unity application code, direct client-to-client IPC, launch arbitrary shell strings, inherit credential environment fields from config, or let a client stop/restart the daemon. Do not add login-service installation; only honor a validated launch policy when the daemon starts.

### Final Routing

- evaluation_mode: `first-pass`
- finalizer: `finalize-task-policy.sh pair`
- build: `cloud-G10`, basis `grade-boundary`, filename `PLAN-cloud-G10.md`
- review: `cloud-G10`, basis `official-review`, filename `CODE_REVIEW-cloud-G10.md`
- large_indivisible_context: `true`
- positive loop risks: `process identity/reaping`, `crash restart concurrency`, `control idempotency integration` (count `3`)
- recovery signals: rework `0`, evidence-integrity failure `false`
- capability-gap evidence: none

## Implementation Checklist

- [ ] Extend only user-local runtime configuration with strict Flutter/Unity process specs, launch/restart policy, and no environment/credential fields.
- [ ] Implement one daemon-owned process slot per client kind with PID/start identity, cancellation, reaping, duplicate convergence, and bounded crash restart.
- [ ] Persist client lifecycle records and reconcile live, exited, stale, and ambiguous identities without duplicate launch.
- [ ] Implement authenticated local-control client operations and Unity-detail-to-Flutter start/focus routing with command-id idempotency.
- [ ] Prove fixture process ownership, disconnect/crash/reconnect, duplicate launch, focus routing, and daemon survival under race.
- [ ] Update the standalone contract with actual S15 source/test paths and run Darwin cross-build verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [API-1] Add User-Local Client Process Specs

#### Problem

`packages/go/agentconfig/runtime_config.go:29-37` contains device and project data but no validated client executable or restart policy, although the contract assigns it to user-local config.

#### Solution

Add a `clients` map keyed only by `flutter` or `unity`. Define absolute executable, argument arrays, launch-on-daemon-start, restart-on-crash, bounded restart policy, and Flutter focus arguments. Reject repo-global client fields, arbitrary environment maps, relative executables, duplicate/unknown kinds, negative limits, and Unity detail configuration that bypasses Flutter.

```go
type ClientProcessSpec struct {
    Executable     string
    Args           []string
    LaunchOnStart  bool
    RestartOnCrash bool
    FocusArgs      []string
}
```

#### Modified Files and Checklist

- [ ] `packages/go/agentconfig/runtime_config.go` — add strict user-local client specs, validation, merge/copy behavior.
- [ ] `packages/go/agentconfig/runtime_config_test.go` — cover valid/boundary/unknown/immutable cases.
- [ ] `configs/iop-agent.local.example.yaml` — add disabled fixture-shaped Flutter/Unity examples without device secrets.

#### Test Strategy

Add `TestClientProcessSpecsAreUserLocalAndImmutable` and `TestClientProcessSpecValidationMatrix`.

#### Verification

```bash
go test -count=1 ./packages/go/agentconfig -run 'TestClientProcess'
```

Expected: valid local specs load and all unsafe fields fail strict decode/validation.

### [API-2] Implement Singleton Process Ownership

#### Problem

`agent-contract/inner/iop-agent-cli-runtime.md:122-128` assigns client start, stop, reaping, focus, and duplicate suppression to the daemon, but no package implements that owner.

#### Solution

Add a manager with one locked slot per kind. Start only an argv vector with explicit working directory, record PID plus an OS-verified start identity, begin one waiter, and converge concurrent duplicate starts to the live record. Stop by exact identity, signal then bounded wait/kill, always reap, and never cancel the daemon context on client exit.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/clientprocess/manager.go` — implement lifecycle and per-kind concurrency.
- [ ] `apps/agent/internal/clientprocess/process.go` — wrap start identity, signaling, wait, and focus.
- [ ] `apps/agent/internal/clientprocess/types.go` — define states/records/results.
- [ ] `apps/agent/internal/clientprocess/manager_test.go` — test singleton, stop, reaping, focus, and daemon survival.

#### Test Strategy

Use the Go test helper process. Assert exactly one distinct PID, one waiter, no zombie, idempotent stop, and a still-live parent context after every child outcome.

#### Verification

```bash
go test -count=1 -race ./apps/agent/internal/clientprocess -run 'TestManager|TestDuplicate|TestDaemon'
```

Expected: all lifecycle and concurrency tests pass.

### [API-3] Persist and Reconcile Client State

#### Problem

`packages/go/agentstate/store.go:50-136` can persist opaque host records, but process ownership would be lost or duplicated after daemon restart without an identity-bound journal.

#### Solution

Store versioned client slots under an integration-record key. On restart inspect the exact PID/start token: adopt proven live processes, retain connected/disconnected projection, mark proven exits, and block ambiguous identity instead of launching a replacement. Apply restart policy only to a conclusively reaped daemon-owned crash, with a bounded backoff/attempt budget.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/clientprocess/store.go` — persist/reconcile CAS records.
- [ ] `apps/agent/internal/clientprocess/store_test.go` — cover live/exited/stale/ambiguous/restart cases.
- [ ] `apps/agent/internal/clientprocess/manager.go` — connect durable transitions.

#### Test Strategy

Inject inspector outcomes and CAS conflicts; restart a second manager over the same real state file and assert no second child starts for live/ambiguous records.

#### Verification

```bash
go test -count=1 ./apps/agent/internal/clientprocess -run 'TestStore|TestReconcile|TestCrashRestart'
```

Expected: no duplicate launch and exact retained blocker/state.

### [API-4] Add Local-Control Client Operations

#### Problem

`agent-contract/inner/iop-agent-cli-runtime.md:103-112` establishes protocol operations/idempotency, but S15 still lacks implementations for `client.start`, `client.stop`, `client.focus`, and `client.detail`.

#### Solution

Add a client-operation adapter in the existing `localcontrol` package. Dispatch only after predecessor authorization and command acceptance. Route Unity `client.detail` to Flutter: start Flutter if absent, otherwise focus it, then return the Flutter process identity. Never call Unity-to-Flutter directly.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/localcontrol/client_operations.go` — implement client commands over a `clientprocess` port.
- [ ] `apps/agent/internal/localcontrol/client_operations_test.go` — verify authorization/idempotency inheritance and detail routing.

#### Test Strategy

Write `TestClientOperationMatrix`, `TestUnityDetailStartsOrFocusesFlutter`, and `TestRejectedClientCommandHasZeroProcessCalls`.

#### Verification

```bash
go test -count=1 ./apps/agent/internal/localcontrol -run 'TestClient|TestUnity'
```

Expected: all paths route through the daemon manager exactly once.

### [API-5] Prove S15 and Record Sources

#### Problem

`agent-contract/inner/iop-agent-cli-runtime.md:41-43,122-128` has requirements but no concrete S15 source or lifecycle trace.

#### Solution

Add actual config, manager, store, control adapter, and test paths to the contract. Add one S15 fixture test that runs Flutter and Unity helpers, crashes/reconnects, performs duplicate launches and detail routing, and records a deterministic PID/start/focus trace without leaking process environment.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/clientprocess/manager_test.go` — add `TestS15ClientLifecycleTrace`.
- [ ] `agent-contract/inner/iop-agent-cli-runtime.md` — record actual S15 sources and evidence.

#### Test Strategy

Run the named test fresh and under race. Validate cleanup using exact child identities, not broad process searches.

#### Verification

```bash
go test -count=1 -race ./apps/agent/internal/clientprocess ./apps/agent/internal/localcontrol -run 'TestS15|TestUnityDetail'
GOOS=darwin GOARCH=arm64 go test -c -o /tmp/clientprocess-darwin.test ./apps/agent/internal/clientprocess
```

Expected: deterministic lifecycle trace and Darwin cross-build pass.

## Dependencies and Execution Order

Required predecessor directories are `14+13_cli_config_fixtures`, `16+13,15_bootstrap_composition`, and `22+16_local_control`. Each must produce one same-group active or archived `complete.log`; all are currently missing. After they complete, implement API-1, then API-2/API-3, then API-4/API-5.

## Modified Files Summary

| File | Item |
|------|------|
| `packages/go/agentconfig/runtime_config.go` | API-1 |
| `packages/go/agentconfig/runtime_config_test.go` | API-1 |
| `configs/iop-agent.local.example.yaml` | API-1 |
| `apps/agent/internal/clientprocess/manager.go` | API-2, API-3 |
| `apps/agent/internal/clientprocess/process.go` | API-2 |
| `apps/agent/internal/clientprocess/types.go` | API-2 |
| `apps/agent/internal/clientprocess/manager_test.go` | API-2, API-5 |
| `apps/agent/internal/clientprocess/store.go` | API-3 |
| `apps/agent/internal/clientprocess/store_test.go` | API-3 |
| `apps/agent/internal/localcontrol/client_operations.go` | API-4 |
| `apps/agent/internal/localcontrol/client_operations_test.go` | API-4 |
| `agent-contract/inner/iop-agent-cli-runtime.md` | API-5 |

## Final Verification

```bash
gofmt -w packages/go/agentconfig/runtime_config.go packages/go/agentconfig/runtime_config_test.go apps/agent/internal/clientprocess/*.go apps/agent/internal/localcontrol/client_operations.go apps/agent/internal/localcontrol/client_operations_test.go
go test -count=1 ./packages/go/agentconfig ./apps/agent/internal/clientprocess ./apps/agent/internal/localcontrol
go test -count=1 -race ./apps/agent/internal/clientprocess ./apps/agent/internal/localcontrol ./packages/go/agentstate
go vet ./packages/go/agentconfig ./apps/agent/internal/clientprocess ./apps/agent/internal/localcontrol
GOOS=darwin GOARCH=arm64 go test -c -o /tmp/clientprocess-darwin.test ./apps/agent/internal/clientprocess
git diff --check
```

Expected: config, lifecycle, persistence, control routing, race, vet, Darwin build, and diff checks pass with no orphaned fixture process. After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
