<!-- task=m-iop-agent-cli-runtime/11+06_workspace_overlay plan=1 tag=REVIEW_API -->

# Enforce Overlay Write Confinement and Immutable Base Identity

## For the Implementing Agent

Implement the two review fixes, run every verification command, and fill the implementation-owned sections of `CODE_REVIEW-cloud-G10.md` with actual notes and stdout/stderr. Keep both active files in place and report ready for review. If blocked, record only the exact blocker, attempted commands/output, and resume condition in implementation-owned evidence fields. Do not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, or write `complete.log`; finalization is owned by the code-review skill.

## Background

The first overlay implementation materializes isolated task views, but it marks writable-root enforcement as active without an executable confinement boundary. It also permits an idempotent retained overlay to be rebound to a different canonical root because the root and configuration revision are absent from the immutable snapshot identity. Both defects violate S18 and make the prior absolute-path denial evidence untrustworthy.

## Archive Evidence Snapshot

- Prior task: `agent-task/m-iop-agent-cli-runtime/11+06_workspace_overlay`
- Prior plan: `agent-task/m-iop-agent-cli-runtime/11+06_workspace_overlay/plan_cloud_G09_0.log`
- Prior review: `agent-task/m-iop-agent-cli-runtime/11+06_workspace_overlay/code_review_cloud_G09_0.log`
- Verdict: `FAIL`
- Required findings:
  - `packages/go/agentworkspace/overlay.go:241` self-attests `EnforcesWritableRoots`; an admitted callback can write the canonical absolute path.
  - `packages/go/agentworkspace/snapshot.go:57` omits canonical/config/grant identity; replay can return root B with root A's retained view.
- Suggested findings: None.
- Nit findings: None.
- Affected files: `packages/go/agentworkspace/overlay.go`, `packages/go/agentworkspace/snapshot.go`, `packages/go/agentworkspace/overlay_test.go`, and the strict admission/dispatch types required to carry verified confinement identity.
- Fresh verification: target suites, 20-count overlay repetition, overlay race test, all `packages/go/...`, vet, gofmt, and `git diff --check` passed; focused reviewer probes reproduced both Required findings.
- Roadmap carryover: S18 / `overlay-workspace` remains incomplete until actual absolute writes are denied and retained overlays cannot be rebound.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md`
- Milestone link: [Milestone document](agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md)
- Task ids:
  - `overlay-workspace`: pinned base and task writable layer
- Completion mode: check-on-pass

## Analysis

### Files Read

- `go.mod`
- `packages/go/agentworkspace/overlay.go`
- `packages/go/agentworkspace/snapshot.go`
- `packages/go/agentworkspace/overlay_test.go`
- `packages/go/agentguard/types.go`
- `packages/go/agentguard/canonical.go`
- `packages/go/agentguard/gitmeta.go`
- `packages/go/agentguard/permit.go`
- `packages/go/agentguard/admission_integration_test.go`
- `packages/go/agenttask/ports.go`
- `packages/go/agenttask/dispatch.go`
- `packages/go/agenttask/scheduler.go`
- `packages/go/agenttask/scheduler_test.go`
- `packages/go/agenttask/test_support_test.go`
- `packages/go/agentprovider/catalog/factory.go`
- `configs/iop-agent.providers.yaml`
- `agent-contract/index.md`
- `agent-contract/inner/agent-runtime.md`
- `agent-contract/inner/iop-agent-cli-runtime.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-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/user_review_1.log`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`

### SDD Criteria

- SDD: `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`
- Status: approved; SDD lock: released.
- Target: S18 / `overlay-workspace`.
- Acceptance: concurrent unattended tasks must share one pinned dirty base, keep independent task/temp/cache views, and be unable to modify the canonical root, sibling layers, or shared Git metadata.
- Evidence Map: dirty/untracked/mode/symlink fingerprint, overlapping/disjoint task views, real canonical absolute-path denial, Git/temp/cache isolation, and identical-base/no-cross-write/canonical-unchanged traces.
- Plan consequence: descriptor admission alone is insufficient. Final verification must launch an offline helper through the production confinement path and observe denied writes, then prove exact root/config/grant replay identity.

### Test Environment Rules

- `test_env=local`.
- `agent-test/local/rules.md` was present and read.
- Matched profile: `agent-test/local/platform-common-smoke.md`.
- Applied commands: host Go identity, fresh target package tests, package-wide regression, race/repetition, vet, gofmt, and deterministic diff checks.
- `make proto` is not required because this follow-up changes no protobuf source.
- No verification leaves the current checkout; remote, field, provider-login, and full-cycle Edge-Node preflight are not applicable.

### Test Coverage Gaps

- Existing coverage proves that a tampered descriptor containing the canonical/sibling path is rejected, but it does not execute a child through the production confinement path or attempt an actual absolute write.
- Existing idempotency coverage checks canonical content drift at one root, but it does not change the canonical root while preserving caller-supplied identity strings.
- Existing temp/cache and task-view separation coverage remains useful and must stay.

### Symbol References

- No symbol is intentionally renamed or removed.
- If a confinement proof/revision is added to `IsolationDescriptor`, update construction and validation references in `agentworkspace`, `agentguard`, and `agenttask` fixtures found by `rg --sort path 'IsolationDescriptor|EnforcesWritableRoots|PreparedIsolation|DispatchRequest' packages/go`.

### Split Judgment

Keep one plan. Executable write confinement and immutable base identity jointly determine whether a prepared descriptor is truthful and replay-safe; either fix alone leaves S18 unsafe and cannot produce an independently valid `complete.log`. Predecessor `06+05_config_registry` is satisfied by `agent-task/archive/2026/07/m-iop-agent-cli-runtime/06+05_config_registry/complete.log`.

### Scope Rationale

- Include the minimum strict admission/dispatch fields needed to bind a concrete confinement revision to the prepared overlay and provider invocation.
- Exclude change-set construction, three-way integration, rollback, and cleanup state; those remain S19 / subtask `12`.
- Exclude provider selection, quota/failover, local-control, client processes, and CLI command surface.
- Do not treat a capability boolean, an allow-list comparison, or a fake callback that merely promises confinement as executable denial evidence.

### Final Routing

- `evaluation_mode=isolated-reassessment`
- `finalizer=finalize-task-policy.sh`, `finalizer_mode=pair`
- Closures: build/review `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`.
- Build grade scores: scope=2, state=2, blast=2, evidence=2, verification=2; grade `G10`.
- Review grade scores: scope=2, state=2, blast=2, evidence=2, verification=2; grade `G10`.
- Build base/route basis: `grade-boundary`; lane `cloud`; filename `PLAN-cloud-G10.md`.
- Review route basis: `official-review`; lane `cloud`; filename `CODE_REVIEW-cloud-G10.md`; adapter `codex`, model `gpt-5.6-sol`, reasoning `xhigh`.
- `large_indivisible_context=false`.
- Positive loop risks: `concurrent_consistency`, `boundary_contract`, `variant_product`; count=3.
- Recovery signals: `review_rework_count=1`, `evidence_integrity_failure=true`; recovery boundary matched but does not replace the grade-boundary basis.
- Capability gap: none.

## Implementation Checklist

- [ ] Replace self-attested writable-root enforcement with a concrete invocation confinement proof bound to the exact overlay/profile revision, and add offline child-process tests that allow task view/temp/cache writes while denying canonical, sibling-layer, snapshot, and shared-Git writes.
- [ ] Bind canonical root plus configuration/grant revisions into workspace snapshot and overlay identities, reject retained-record root/revision rebinding, and add an exact root-A/root-B replay regression.
- [ ] Run fresh target, repetition, race, package-wide, vet, formatting, and diff verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Make writable-root enforcement executable and identity-bound

**Problem:** `packages/go/agentworkspace/overlay.go:227-247` unconditionally returns:

```go
descriptor := &agentguard.IsolationDescriptor{
    // ...
    WritableRoots:         []string{record.Locator.ViewRoot, record.Locator.TempRoot, record.Locator.CacheRoot},
    EnforcesWritableRoots: true,
}
```

The materialized copy changes the working directory but does not prevent a child from opening the canonical root or a sibling layer by absolute path. `agentguard.Admit` validates declarations; it does not install filesystem confinement.

**Solution:** replace the boolean self-attestation with a mandatory concrete confinement owner that is resolved before admission and is tied to the exact task root, view, temp/cache roots, canonical root, sibling/snapshot root, provider profile revision, and confinement revision. Unsupported or unavailable confinement must fail `Prepare` with no admissible descriptor. The production invocation path must consume that exact proof; a caller cannot fabricate or omit it. The offline conformance fixture must launch a child through the same boundary and assert:

```text
view/temp/cache write -> success
canonical/sibling/snapshot/shared Git write -> EACCES or EPERM
```

Do not satisfy the test with descriptor mutation or a fake callback that returns a denial without an OS/provider sandbox attempt.

**Modified Files and Checklist:**

- [ ] `packages/go/agentworkspace/overlay.go`: require and persist exact confinement evidence; remove unconditional enforcement.
- [ ] `packages/go/agentworkspace/confinement.go`: add the host-neutral confinement request/proof and strict unavailable error.
- [ ] `packages/go/agentworkspace/confinement_linux.go`: implement Linux child filesystem confinement with existing `golang.org/x/sys` primitives; fail closed when the kernel feature is unavailable.
- [ ] `packages/go/agentworkspace/confinement_darwin.go`: implement the matching macOS child policy; fail closed when the platform sandbox cannot be installed.
- [ ] `packages/go/agentworkspace/confinement_unsupported.go`: reject unsupported platforms explicitly.
- [ ] `packages/go/agentguard/types.go`: carry the non-empty confinement revision in the descriptor/canonical workspace.
- [ ] `packages/go/agentguard/canonical.go`: validate and seal the confinement revision with the permit inputs.
- [ ] `packages/go/agenttask/ports.go`: carry the verified confinement proof through `PreparedIsolation`/`DispatchRequest` without exposing a forgeable boolean.
- [ ] `packages/go/agenttask/dispatch.go`: reject missing/mismatched confinement proof before `ProviderInvoker.Start`.
- [ ] `packages/go/agenttask/test_support_test.go`: update strict fakes to provide a bound proof and assert it reaches invocation.
- [ ] `packages/go/agentworkspace/overlay_test.go`: replace descriptor-only denial with actual child write attempts.
- [ ] `packages/go/agentworkspace/confinement_test.go`: cover unavailable, tampered, and allowed/denied path cases.
- [ ] `go.mod` / `go.sum`: promote only the already-present `golang.org/x/sys` dependency if the platform implementation imports it directly.

**Test Strategy:** add `TestOverlayBackendConfinementDeniesActualAbsoluteWrites` and platform-specific helper-process cases. Use no network or provider login. Assert the canonical tree and Git status remain byte-for-byte unchanged after the denied attempts, sibling output is absent, and temp/cache outputs are isolated.

**Verification:**

```bash
go test -count=1 -run '^(TestOverlayBackendConfinement|TestOverlayBackendConcurrent)' -v ./packages/go/agentworkspace
go test -count=1 ./packages/go/agentguard ./packages/go/agenttask
```

Expected: real child writes outside the task roots fail; all strict-port tests pass.

### [REVIEW_API-2] Bind retained overlays to the exact canonical/config/grant base

**Problem:** `packages/go/agentworkspace/snapshot.go:57-63` records only Git/tree evidence, while `packages/go/agentworkspace/overlay.go:553-561` validates caller-supplied IDs/revision strings without comparing the canonical root or config revision. A replay can therefore combine a new `BaseRoot` with an old retained view.

**Solution:** add normalized canonical root, configuration revision, and grant revision to `WorkspaceSnapshot`; include them in `snapshotRevision`. Persist the same values in `OverlayRecord` and include them in `overlayRevision`. Pass immutable request inputs into snapshot capture, and reject any retained record whose root/config/grant identity differs before constructing a descriptor. Keep content-addressed snapshot reuse only when the complete base identity matches.

Before:

```go
type WorkspaceSnapshot struct {
    SchemaVersion    uint32
    Revision         string
    GitRevision      string
    GitIndexRevision string
    Entries          []SnapshotEntry
}
```

After:

```go
type WorkspaceSnapshot struct {
    SchemaVersion    uint32
    Revision         string
    CanonicalRoot    string
    ConfigRevision   string
    GrantRevision    string
    GitRevision      string
    GitIndexRevision string
    Entries          []SnapshotEntry
}
```

**Modified Files and Checklist:**

- [ ] `packages/go/agentworkspace/snapshot.go`: record and hash canonical/config/grant identity.
- [ ] `packages/go/agentworkspace/overlay.go`: persist, validate, and replay-check the same identity.
- [ ] `packages/go/agentworkspace/overlay_test.go`: add root-rebinding, config-rebinding, and grant-rebinding regressions.
- [ ] `agent-contract/inner/iop-agent-cli-runtime.md`: keep S18 source/evidence claims aligned with the implemented identity and confinement behavior.
- [ ] `agent-contract/inner/agent-runtime.md`: clarify that admission consumes executable confinement evidence rather than a self-attested flag.

**Test Strategy:** add `TestOverlayBackendRejectsRetainedIdentityRebinding`. Prepare against root A, then vary root, config revision, and grant revision one at a time using the same idempotency key. Every replay must fail without changing the retained record; an exact replay must still return the original overlay.

**Verification:**

```bash
go test -count=1 -run '^TestOverlayBackend(RejectsRetainedIdentityRebinding|IdempotencyAndFailureRetention)$' -v ./packages/go/agentworkspace
```

Expected: exact replay succeeds; every identity rebind fails.

## Modified Files Summary

| File | Item |
|---|---|
| `packages/go/agentworkspace/overlay.go` | REVIEW_API-1, REVIEW_API-2 |
| `packages/go/agentworkspace/snapshot.go` | REVIEW_API-2 |
| `packages/go/agentworkspace/overlay_test.go` | REVIEW_API-1, REVIEW_API-2 |
| `packages/go/agentworkspace/confinement.go` | REVIEW_API-1 |
| `packages/go/agentworkspace/confinement_linux.go` | REVIEW_API-1 |
| `packages/go/agentworkspace/confinement_darwin.go` | REVIEW_API-1 |
| `packages/go/agentworkspace/confinement_unsupported.go` | REVIEW_API-1 |
| `packages/go/agentworkspace/confinement_test.go` | REVIEW_API-1 |
| `packages/go/agentguard/types.go` | REVIEW_API-1 |
| `packages/go/agentguard/canonical.go` | REVIEW_API-1 |
| `packages/go/agenttask/ports.go` | REVIEW_API-1 |
| `packages/go/agenttask/dispatch.go` | REVIEW_API-1 |
| `packages/go/agenttask/test_support_test.go` | REVIEW_API-1 |
| `agent-contract/inner/iop-agent-cli-runtime.md` | REVIEW_API-2 |
| `agent-contract/inner/agent-runtime.md` | REVIEW_API-2 |
| `go.mod` / `go.sum` | REVIEW_API-1 |

## Dependencies and Execution Order

`06+05_config_registry` is complete. Implement REVIEW_API-1 and REVIEW_API-2 together before running final verification because the descriptor revision must cover both confinement and base identity.

## Final Verification

```bash
command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT
go test -count=1 ./packages/go/agentworkspace ./packages/go/agentguard ./packages/go/agenttask
go test -count=20 ./packages/go/agentworkspace
go test -count=1 -race ./packages/go/agentworkspace ./packages/go/agenttask
go test -count=1 ./packages/go/...
go vet ./packages/go/agentworkspace ./packages/go/agentguard ./packages/go/agenttask
gofmt -d packages/go/agentworkspace packages/go/agentguard packages/go/agenttask
rg --sort path 'EnforcesWritableRoots: true|WritableRootConfinement: true' packages/go
git diff --check
```

Expected: all commands pass; the deterministic search returns only evidence-backed production construction or explicit test fixtures, with no unconditional overlay self-attestation.

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