iop/agent-task/m-iop-agent-cli-runtime/17+13,16_client_process_manager/PLAN-cloud-G10.md

14 KiB

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 문서
  • 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 13 and 16 are encoded in the directory name; neither currently has an active or archived complete.log. The manager consumes the completed S11 protocol rather than changing its security 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.

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

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

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

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

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

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 13_standalone_host_foundation and 16+13_local_control. Each must produce one same-group active or archived complete.log; both 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

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.