<!-- task=m-iop-owned-single-request-agent-execution/11+10_workspace_command plan=0 tag=API milestone-task=tool-executor -->

# Bounded Workspace Command Executor

## For the Implementing Agent

Do not start until packet 10 has `complete.log`. Use only operator-owned exact command templates, implement within the listed boundary, run every verification command, and fill `CODE_REVIEW-cloud-G10.md`. Do not introduce shell/PTY/general argv execution or own review finalization.

## Background

Packet 10 deliberately returns typed unsupported for command. This packet activates only exact operator-configured command ids in the fixed workspace cwd, with environment allowlisting, shared output bounds, deadline, process-group cancellation, and race-safe result ownership.

## Analysis

### Files Read

- `AGENTS.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-ops/rules/project/domain/node/rules.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/node-smoke.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`
- `apps/node/internal/node/node.go`
- `apps/node/internal/node/cancel_handler.go`
- `apps/node/internal/node/run_handler.go`
- `apps/node/internal/transport/session.go`
- `packages/go/config/edge_types.go`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-contract/inner/execution-runtime.md`
- `agent-spec/runtime/edge-node-execution.md`

### SDD Criteria

- S05 requires command success/failure/timeout/large output plus process cancel and consistent typed results.
- D06 excludes interactive terminal, shell, desktop, scheduler, and long-lived agent processes.
- Fixed cwd, process group, output cap, timeout, and environment allowlist are mandatory and fail closed.

### Verification Context

- Current source has no Node subprocess path; the only `exec.Command` use is host setup, so there is no compatible executor to extend.
- Target is Mac, while CI host is Linux. Unix process-group implementation must be build-tagged for Darwin/Linux and tested on Linux; an unsupported fallback keeps other builds explicit.
- Tests use the Go test binary as an exact configured executable, not `/bin/sh`.

### State and Concurrency Findings

- One tool call owns one process group and one terminal result; timeout, explicit cancel, context cancel, exit, and output overflow race through a single completion path.
- Output writers must share one total cap and continue draining after truncation so child pipes cannot deadlock.
- Cancel addresses only `(execution_id, tool_call_id)` and cannot kill another request's process.

### Test Coverage Gaps

- No exact-template command lookup, minimal environment builder, process group owner, capped writer, or cancel race exists.

### Symbol References

- Packet 07 defines command templates; packet 09 defines command/cancel messages; packet 10 owns the runtime and Node handler.
- Do not reuse provider `runManager`, `OnCancel`, `exec.CommandContext`'s single-process kill, or any caller shell codec.

### Split Judgment

- Command/process correctness is one indivisible slice: start, output drain, timeout/cancel group kill, and wait/result ownership must be reviewed together.
- Cleanup of request artifacts and all processes remains packet 13, which builds on this per-tool primitive.

### Scope Rationale

- Include exact command id execution, cwd/env/bounds, Unix group lifecycle, typed cancel/result, tests, contract/spec.
- Exclude arbitrary argv/shell, PTY, network sandbox claims, cleanup orchestration, provider loop, and public output.

### Final Routing

- `evaluation_mode=first-pass`; build closures true, scores 1/2/2/1/2 = G08.
- Finalizer route `risk-boundary`, lane `cloud`, filename `PLAN-cloud-G08.md`; risks `temporal_state`, `concurrent_consistency`, `boundary_contract`, `variant_product` (4).
- Review scores 2/2/2/2/2 = G10; official filename `CODE_REVIEW-cloud-G10.md`; no recovery/capability gap.

## Dependencies and Execution Order

1. Require packet 10 completion.
2. Implement capped output and process-group lifecycle before runtime dispatch.
3. Wire command and cancel through the existing workspace handler, then update docs.

## Implementation Checklist

- [ ] Resolve only operator-defined command ids to absolute executable/fixed args and build a minimal allowlisted environment in the fixed workspace cwd.
- [ ] Own Unix process groups with one terminal result across exit, timeout, context cancel, explicit cancel, and shared stdout/stderr truncation races.
- [ ] Integrate command/cancel into the workspace runtime and Node handler without touching provider cancellation or permitting shell/PTY/arbitrary argv.
- [ ] Prove success/nonzero/timeout/cancel/group-child/output/env/cross-request behavior and synchronize command contract/spec limits.
- [ ] Run dependency, focused race, package, vet, cross-build, documentation, and whitespace verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

## Implementation Plan

### [API-1] Implement exact-template process execution

**Problem**

- Packet 10's planned `file_executor.go` returns unsupported for command; no process owner or bounded writer exists.
- `apps/node/internal/node/cancel_handler.go:12` cancels provider runs and must not be overloaded with workspace process identity.

**Solution**

No command executor exists. Add:

```go
type commandExecution struct {
	done chan struct{}
	cancelOnce sync.Once
	// process/result state guarded by one owner
}

func (r *Runtime) executeCommand(ctx context.Context, request Request, input CommandInput) Result
func (r *Runtime) Cancel(executionID, toolCallID string) CancelResult
```

Resolve `command_id` to the immutable config template and run its absolute executable plus fixed args only. Reject request argv, unknown ids, disabled capability, unapproved env names/invalid values, and timeout/output bounds. Set `cmd.Dir` to the configured root; set an explicit minimal environment containing only allowed request entries (no implicit inheritance). Route stdout/stderr through one concurrency-safe total byte budget, retain separate bounded streams, mark truncation, and continue discarding overflow.

**Modified Files and Checklist**

- [ ] `apps/node/internal/workspace/command_executor.go` — lookup, minimal env, start/wait/result arbitration, shared capped output.
- [ ] `apps/node/internal/workspace/command_process_unix.go` — Darwin/Linux process group creation and group signal/kill.
- [ ] `apps/node/internal/workspace/command_process_other.go` — explicit unsupported fallback for non-Unix builds.
- [ ] `apps/node/internal/workspace/command_executor_test.go` — helper-process success, exit, cwd/env, timeout, cancel, child group, output, and request isolation.

**Test Strategy**

- Use `os.Executable()` plus `-test.run=TestWorkspaceCommandHelperProcess` as the exact template. The helper emits stdout/stderr, spawns a child, blocks, exits non-zero, and records cwd/env as directed.

**Verification**

- `go test -race ./apps/node/internal/workspace -run 'Test(CommandExecutor|WorkspaceCommandHelperProcess)' -count=1`
- Expected: one typed outcome wins every race, descendants die, and retained output never exceeds the shared cap.

### [API-2] Activate typed command and cancel handling

**Problem**

- Packet 10 leaves `WorkspaceToolOperation_COMMAND` and `WorkspaceCancelRequest` unsupported at the Node handler boundary.

**Solution**

Before (packet 10 contract):

```go
case iop.WORKSPACE_TOOL_OPERATION_COMMAND:
	return unsupportedResult(req)
```

After:

```go
case iop.WORKSPACE_TOOL_OPERATION_COMMAND:
	return n.workspaceRuntime.Execute(ctx, decodeCommand(req))
```

Decode only command id/env/timeout/output cap; validate identity before dispatch. Implement `OnWorkspaceCancel` through the workspace runtime, make duplicate cancel idempotent, and return typed not-found without touching another execution. Keep cleanup unsupported.

**Modified Files and Checklist**

- [ ] `apps/node/internal/workspace/runtime.go` — track active commands by exact request/tool identity and expose race-safe cancel.
- [ ] `apps/node/internal/node/workspace_handler.go` — decode command and map cancel/result status.
- [ ] `apps/node/internal/node/workspace_handler_test.go` — command/cancel mapping, duplicate/not-found, and raw-free errors.
- [ ] `agent-contract/inner/edge-node-runtime-wire.md` — define exact-template trust boundary, cwd/env/output/process semantics, and exclusions.
- [ ] `agent-spec/runtime/edge-node-execution.md` — mark command/cancel implemented with named tests.

**Test Strategy**

- Extend direct Node handler tests and assert raw sentinels never appear in logged/typed error text.

**Verification**

- `go test -race ./apps/node/internal/node -run 'TestNodeWorkspace(Command|Cancel)' -count=1`
- `rg --sort path -n 'command id|fixed args|process group|environment allowlist|stdout|stderr|PTY|shell' agent-contract/inner/edge-node-runtime-wire.md agent-spec/runtime/edge-node-execution.md`
- Expected: typed command/cancel is active and the fixed-template/non-interactive boundary is explicit.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/node/internal/workspace/command_executor.go` | API-1 |
| `apps/node/internal/workspace/command_process_unix.go` | API-1 |
| `apps/node/internal/workspace/command_process_other.go` | API-1 |
| `apps/node/internal/workspace/command_executor_test.go` | API-1 |
| `apps/node/internal/workspace/runtime.go` | API-2 |
| `apps/node/internal/node/workspace_handler.go` | API-2 |
| `apps/node/internal/node/workspace_handler_test.go` | API-2 |
| `agent-contract/inner/edge-node-runtime-wire.md` | API-2 |
| `agent-spec/runtime/edge-node-execution.md` | API-2 |
| `agent-task/m-iop-owned-single-request-agent-execution/11+10_workspace_command/CODE_REVIEW-cloud-G10.md` | API-1, API-2 |

## Final Verification

1. `test -f agent-task/m-iop-owned-single-request-agent-execution/10+09_workspace_files/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/10+09_workspace_files/complete.log' | wc -l)" -eq 1`
2. `go test -race ./apps/node/internal/workspace -run 'Test(CommandExecutor|WorkspaceCommandHelperProcess)' -count=1`
3. `go test -race ./apps/node/internal/node -run 'TestNodeWorkspace(Command|Cancel)' -count=1`
4. `go test ./apps/node/internal/workspace ./apps/node/internal/node ./apps/node/internal/transport -count=1`
5. `go vet ./apps/node/internal/workspace ./apps/node/internal/node`
6. `GOOS=darwin GOARCH=arm64 go test -c -o /tmp/iop-workspace-command-darwin.test ./apps/node/internal/workspace`
7. `rg --sort path -n 'command id|fixed args|process group|environment allowlist|stdout|stderr|PTY|shell' agent-contract/inner/edge-node-runtime-wire.md agent-spec/runtime/edge-node-execution.md`
8. `git diff --check`

Expected: packet 10 is uniquely complete; process and Node mapping tests pass under race; Darwin compilation succeeds; exact-template boundaries are documented. Cached tests are not acceptable.

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