<!-- task=m-iop-agent-cli-runtime/10+06_state_recovery plan=2 tag=REVIEW_REVIEW_API -->

# Fence Every Durable Mutation and Preserve Successor Leases

## For the Implementing Agent

Implement only the lease-fencing correction below. Run every verification command, fill the implementation-owned sections of `CODE_REVIEW-cloud-G08.md` with actual notes and stdout/stderr, keep both active files in place, and report ready for review. If blocked, record the exact blocker, attempted commands/output, and resume condition in the 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 renewal supervisor added by the preceding follow-up does not yet make lease ownership an atomic write fence. Provider and reviewer results can return after a successor replaces the durable claims but before the next renewal tick cancels the context, because `mutateDecision` does not validate the active claims in the same state snapshot and compare-and-swap that stores the result. Integration claims are not enrolled in renewal. Reconciliation cleanup also reloads the current project and workspace tokens and deletes them, which removes a successor's claims after ownership loss, while the device claim is never released.

A focused reviewer reproducer blocked `Reviewer.Review`, replaced the project and workspace leases with successor tokens, and released the review before the next renewal tick. The stale review result committed the work through `completed`, and cleanup deleted both successor leases. The reported focused, repeated, race, shared-package, vet, formatting, proto, and diff checks all passed, so the correction must repair both the runtime invariant and the blind test matrix.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-iop-agent-cli-runtime/10+06_state_recovery/plan_local_G06_1.log`
- Prior review: `agent-task/m-iop-agent-cli-runtime/10+06_state_recovery/code_review_cloud_G07_1.log`
- Verdict: `FAIL`
- Findings: Required=1, Suggested=0, Nit=0.
- Required finding: active claims are validated by separate loads instead of inside the guarded durable mutation; integration is not renewed; cleanup releases reloaded successor tokens and omits device release.
- Affected files: `packages/go/agenttask/manager.go`, `packages/go/agenttask/intent.go`, `packages/go/agenttask/reconcile.go`, `packages/go/agenttask/dispatch.go`, `packages/go/agenttask/review.go`, `packages/go/agenttask/integration_queue.go`, and the lease test harness/tests.
- Fresh reviewer evidence: the implementation's named focused tests, twenty-repeat run, race run, shared-package run, vet, formatting, proto generation, and `git diff --check` passed.
- Focused failure evidence: `go test -count=1 -run '^TestReviewerReproducerRejectsReviewResultAndPreservesSuccessorLeases$' -v ./packages/go/agenttask` failed because the late review reached `completed` and both successor leases were absent after reconciliation.
- Roadmap carryover: SDD S09 and Milestone task `state-recovery` remain incomplete until every durable result commit is atomically fenced and cleanup cannot delete successor ownership.

## 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:
  - `state-recovery`: singleton/workspace lease, checkpoint, restart reconciliation
- Completion mode: check-on-pass

## 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/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/finalize-task-routing/SKILL.md`
- `agent-ops/skills/common/create-test/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/testing-smoke.md`
- `agent-spec/index.md`
- `agent-contract/index.md`
- `agent-contract/inner/agent-runtime.md`
- `agent-contract/inner/iop-agent-cli-runtime.md`
- `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`
- `packages/go/agenttask/ports.go`
- `packages/go/agenttask/intent.go`
- `packages/go/agenttask/manager.go`
- `packages/go/agenttask/reconcile.go`
- `packages/go/agenttask/dispatch.go`
- `packages/go/agenttask/review.go`
- `packages/go/agenttask/integration_queue.go`
- `packages/go/agenttask/manager_test.go`
- `packages/go/agenttask/integration_queue_test.go`
- `packages/go/agenttask/test_support_test.go`

No implementation spec matched this standalone state-recovery task in `agent-spec/index.md`, so no `agent-spec/archive/**` content was read.

### SDD Criteria

- SDD: `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`; status approved, implementation lock released.
- Target: S09 / `state-recovery`.
- Acceptance: duplicate daemon and workspace-manager ownership must remain singular, live child work must not be duplicated, and ambiguous recovery must block.
- Evidence Map: device singleton/workspace lease, process identity, checkpoint, restart, and archive-fault tests must prove no duplicate owner and exact retained state.
- This follow-up therefore requires a single atomic invariant: the exact claims that authorize an external operation must still match the state snapshot used for every resulting durable mutation, including CAS retries.

### Test Environment Rules

- `test_env=local`.
- `agent-test/local/rules.md` did not exist at review start, so the mandatory `create-test` fallback created the ignored English local rule set. The applicable `platform-common-smoke.md` and `testing-smoke.md` profiles were read.
- Apply environment identity capture, host Go/GOROOT consistency, focused deterministic tests, repeat tests, race tests, shared-package regression, `make proto`, vet, formatting, contract text checks, and `git diff --check`.
- No verification leaves the current checkout. Edge-Node full-cycle wire validation is not applicable because this correction changes the host-neutral `agenttask` lease lifecycle and no proto, transport, binary entrypoint, or external provider implementation.
- The deterministic test seam may extend only package-internal manager/ticker construction. Do not expose test control through the public runtime contract.

### Test Coverage Gaps

- `TestDeviceLeaseRenewsDuringLongInvocation` manually extends the stored device expiry and never observes a supervisor renewal or a second manager's denied takeover.
- `TestWorkspaceProjectLeaseRenewsDuringLongReview` manually extends project/workspace expiries and does not drive a renewal tick.
- `TestLeaseLossCancelsInvocationBeforeCommit` waits for a real 200 ms renewal interval, so it does not prove that a result returned immediately after token replacement is rejected before the next tick.
- `TestIntegrationLeaseRenewsAndFencesLateResult` checks a separate pre-commit load, but the integration claim is not in `leaseSet` and the validation is subject to a load/CAS time-of-check/time-of-use gap.
- No committed test proves that cleanup releases only captured exact claims, releases the device claim, and preserves successor project/workspace/integration tokens.

### Symbol References

- No exported symbol rename or removal is planned.
- `LeaseRecord` persistence shape and JSON compatibility must remain unchanged.
- Any ticker/test seam and context fence carrier must remain package-internal.
- `mutateDecision`, `Manager.mutate`, and every `changeWork` caller are the central reference chain; the implementation must audit which state mutations use a guarded reconciliation context and which lease-management operations intentionally use an unfenced base context.

### Split Judgment

Keep one plan. Atomic mutation fencing, dynamic integration renewal, exact-token cleanup, and the deterministic regression matrix are inseparable parts of one temporal ownership invariant. Splitting them would leave an intermediate implementation that can still commit stale results or delete successor leases.

### Scope Rationale

- Keep the existing state envelope, locator format, provider invocation identity, review semantics, integration outcomes, lease duration policy, and recovery observation model unchanged.
- Do not add a new daemon, proto field, transport, provider smoke, overlay backend, or change-set backend.
- The existing contracts already state the required renewal, atomic-fence, cancellation/rejection, and exact-token-release behavior. Change contract text only if the corrected internal mechanism changes a documented semantic; otherwise verify conformance without editorial churn.
- Preserve unrelated dirty-worktree changes.

### Final Routing

- `evaluation_mode=isolated-reassessment`
- `finalizer=finalize-task-policy.sh`, `finalizer_mode=pair`
- Build closures: scope/context/verification/evidence/ownership/decision all closed.
- Build scores: scope=1, state=2, blast=1, evidence=2, verification=1; grade `G07`.
- Build base route basis: `local-fit`.
- Build route basis: `recovery-boundary` because `review_rework_count=2`.
- Build lane: `cloud`; filename `PLAN-cloud-G07.md`.
- Review closures: scope/context/verification/evidence/ownership/decision all closed.
- Review scores: scope=2, state=2, blast=1, evidence=2, verification=1; grade `G08`.
- Review route: `official-review`; lane `cloud`; filename `CODE_REVIEW-cloud-G08.md`.
- Review adapter/model/effort: `codex`, `gpt-5.6-sol`, `xhigh`.
- `large_indivisible_context=false`
- Positive loop risks: `temporal_state`, `concurrent_consistency`, `boundary_contract`; count=3.
- Recovery signals: `review_rework_count=2`, `evidence_integrity_failure=false`.
- Capability gap: none.

## Implementation Checklist

- [ ] Atomically validate the exact active lease claims inside every guarded durable mutation and reject late provider, reviewer, and integrator results even when ownership changes before the next renewal tick.
- [ ] Enroll integration claims in the reconciliation renewal lifetime and cancel guarded work when any tracked renewal loses ownership.
- [ ] Stop the renewal supervisor before cleanup, release only originally captured exact device/project/workspace/integration claims, and preserve all successor tokens.
- [ ] Replace the misleading lease tests with deterministic renewal, takeover-denial, immediate-loss, cancellation, no-stale-commit, device-release, and successor-retention coverage.
- [ ] Run every focused, repeated, race, shared-package, vet, formatting, proto, contract, and diff verification command and fill all implementation-owned sections in `CODE_REVIEW-cloud-G08.md`.

### [REVIEW_REVIEW_API-1] Make the lease set an atomic durable-write fence

**Problem:** `packages/go/agenttask/manager.go:70-89` validates each claim with separate store loads, while `packages/go/agenttask/manager.go:358-395` performs durable mutations without consulting the lease set. A successor can replace ownership after `Reviewer.Review`, `ProviderInvocation.Wait`, or `Integrator.Integrate` starts and before the original owner mutates state. A pre-commit `Validate` still leaves a time-of-check/time-of-use gap; only validation against the same loaded state and CAS revision can close it.

**Solution:** Bind the reconciliation's `leaseSet` to the guarded context. Snapshot its exact scope/subject/token claims under the lease-set lock, validate those claims against the `ManagerState` loaded by `mutateDecision`, and run the change plus CAS against that revision. On CAS conflict, reload and revalidate before retrying. Keep lease acquisition, renewal, and exact-token release on explicit internal paths so stale guarded context values cannot block safe cleanup or recursively fence lease maintenance. Every provider/reviewer/integrator result must enter durable state only through the guarded mutation path.

Before:

```go
// packages/go/agenttask/manager.go:358
state, revision, err := m.store.Load(ctx)
next := cloneState(state)
result, err := change(&next)
_, err = m.store.CompareAndSwap(ctx, revision, next)
```

After:

```go
state, revision, err := m.store.Load(ctx)
if err := validateContextClaims(ctx, state); err != nil {
	return zero, err
}
next := cloneState(state)
result, err := change(&next)
_, err = m.store.CompareAndSwap(ctx, revision, next)
// A successor write changes revision, so a retry reloads and fails the fence.
```

**Modified Files and Checklist:**

- [ ] `packages/go/agenttask/manager.go`: carry the active lease set in guarded contexts, snapshot claims safely, and validate them inside `mutateDecision` on every CAS attempt.
- [ ] `packages/go/agenttask/intent.go`: expose state-snapshot claim matching and retain explicit exact-token acquire/renew/release operations without changing persisted `LeaseRecord`.
- [ ] `packages/go/agenttask/dispatch.go`: ensure provider locators, submission acceptance, and following state transitions use the guarded atomic mutation path.
- [ ] `packages/go/agenttask/review.go`: ensure PASS/WARN/FAIL review results and all related failure/blocker mutations are rejected after ownership loss.
- [ ] `packages/go/agenttask/integration_queue.go`: add each acquired integration claim to the active set before the external call, remove it only after the supervisor can no longer renew it, and commit outcomes through the atomic fence.

**Test Strategy:** Block provider, reviewer, and integrator calls. Replace an exact tracked token and release the external result immediately, before any renewal tick. Assert `ErrLeaseLost` or the guarded cancellation, no durable result/terminal transition, and exact successor-token retention. Exercise CAS conflict so a successor write between initial load and CAS is reloaded and rejected.

**Verification:** The focused immediate-loss and successor-retention tests in Final Verification must pass without wall-clock expiry sleeps as the correctness oracle.

### [REVIEW_REVIEW_API-2] Close the renewal and cleanup lifecycle with deterministic evidence

**Problem:** `packages/go/agenttask/integration_queue.go:48-68` acquires an integration claim but never adds it to `leaseSet`. `packages/go/agenttask/reconcile.go:22-23` registers `leases.Close` before later cleanup defers, so cleanup executes first, and `packages/go/agenttask/reconcile.go:76-90` reloads current tokens instead of retaining the originally acquired claims. The device claim is not released. The existing tests at `packages/go/agenttask/manager_test.go:773-956` simulate renewal by editing expiry directly and therefore cannot verify the supervisor.

**Solution:** Retain immutable claim handles for every acquired scope. Use one ordered cleanup that first stops and joins the renewal supervisor, then releases only those captured exact handles through an unfenced best-effort CAS path. Remove/release short-lived integration claims without racing a renewal snapshot. Add a package-internal manual ticker or renewal-trigger seam so tests can explicitly advance the manager clock, trigger renewal, observe expiry extension, and attempt acquisition by a second manager. Keep channel gates for external calls and observable cancellation.

Before:

```go
defer leases.Close()
defer func() {
	state, _ := m.load(context.WithoutCancel(ownedCtx))
	m.releaseProject(context.WithoutCancel(ownedCtx), state.Projects[projectID].Lease.Token, string(projectID))
}()
```

After:

```go
defer func() {
	leases.Close()
	for _, claim := range capturedClaims {
		m.releaseExact(context.WithoutCancel(baseCtx), claim)
	}
}()
```

**Modified Files and Checklist:**

- [ ] `packages/go/agenttask/reconcile.go`: retain acquired handles, order supervisor shutdown before release, release the device claim, and never reload a token for cleanup.
- [ ] `packages/go/agenttask/manager.go`: make dynamic claim add/remove and supervisor shutdown race-safe; add only a package-internal deterministic tick seam if required.
- [ ] `packages/go/agenttask/integration_queue.go`: use the same tracked lifetime and exact cleanup for integration claims.
- [ ] `packages/go/agenttask/test_support_test.go`: provide deterministic renewal triggers, second-manager fixtures, external-stage gates, and cancellation observations.
- [ ] `packages/go/agenttask/manager_test.go`: replace manual-expiry "renewal" with actual device/project/workspace renewal and takeover denial; cover provider/review immediate loss, CAS retry fencing, device release, and successor retention.
- [ ] `packages/go/agenttask/integration_queue_test.go`: prove integration renewal, forced loss before the next tick, no stale integration commit, cancellation, and successor retention.
- [ ] `agent-contract/inner/agent-runtime.md`: change only if needed to keep the shared lease contract aligned; otherwise verify the existing atomic-fence and exact-token language.
- [ ] `agent-contract/inner/iop-agent-cli-runtime.md`: change only if needed to keep S09 standalone behavior aligned; otherwise verify the existing language.

**Test Strategy:** Drive renewal explicitly: capture original expiries, advance the fake clock, trigger one or more renewal passes, assert the exact tokens remain and expiries increase, then prove a second manager is denied. Separately replace tokens while each external stage is blocked, release the stage before the renewal trigger, and assert the result cannot commit. Trigger renewal after loss to prove guarded cancellation. End reconciliation normally and after loss to prove owned claims are removed but successor claims survive.

**Verification:** Run the named focused suite once with `-v`, repeat it twenty times, then run the race and shared-package suites. The tests must not treat `time.Sleep` beyond a ticker interval as the ownership oracle.

## Modified Files Summary

| File | Item |
|---|---|
| `packages/go/agenttask/manager.go` | REVIEW_REVIEW_API-1, REVIEW_REVIEW_API-2 |
| `packages/go/agenttask/intent.go` | REVIEW_REVIEW_API-1 |
| `packages/go/agenttask/reconcile.go` | REVIEW_REVIEW_API-2 |
| `packages/go/agenttask/dispatch.go` | REVIEW_REVIEW_API-1 |
| `packages/go/agenttask/review.go` | REVIEW_REVIEW_API-1 |
| `packages/go/agenttask/integration_queue.go` | REVIEW_REVIEW_API-1, REVIEW_REVIEW_API-2 |
| `packages/go/agenttask/test_support_test.go` | REVIEW_REVIEW_API-2 |
| `packages/go/agenttask/manager_test.go` | REVIEW_REVIEW_API-2 |
| `packages/go/agenttask/integration_queue_test.go` | REVIEW_REVIEW_API-2 |
| `agent-contract/inner/agent-runtime.md` | REVIEW_REVIEW_API-2, if semantic text changes are required |
| `agent-contract/inner/iop-agent-cli-runtime.md` | REVIEW_REVIEW_API-2, if semantic text changes are required |

## Final Verification

```bash
command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT
make proto
go test -count=1 -run '^(TestLeaseSupervisorRenewsAllTrackedScopesAndDeniesTakeover|TestLeaseLossImmediatelyFencesProviderAndReviewResults|TestLeaseLossImmediatelyFencesIntegrationResult|TestLeaseCleanupReleasesOwnedClaimsAndPreservesSuccessors)$' -v ./packages/go/agenttask
go test -count=20 -run '^(TestLeaseSupervisorRenewsAllTrackedScopesAndDeniesTakeover|TestLeaseLossImmediatelyFencesProviderAndReviewResults|TestLeaseLossImmediatelyFencesIntegrationResult|TestLeaseCleanupReleasesOwnedClaimsAndPreservesSuccessors)$' ./packages/go/agenttask
go test -count=1 -race ./packages/go/agentstate ./packages/go/agenttask
go test -count=1 ./packages/go/...
go vet ./packages/go/agentstate ./packages/go/agenttask
gofmt -d packages/go/agenttask/ports.go packages/go/agenttask/intent.go packages/go/agenttask/manager.go packages/go/agenttask/reconcile.go packages/go/agenttask/dispatch.go packages/go/agenttask/review.go packages/go/agenttask/integration_queue.go packages/go/agenttask/test_support_test.go packages/go/agenttask/manager_test.go packages/go/agenttask/integration_queue_test.go
rg --sort path -n 'Every external result|only exact tokens|atomic fence' agent-contract/inner/agent-runtime.md agent-contract/inner/iop-agent-cli-runtime.md
git diff --check
```

Every command must pass with fresh output. After completing all code changes, fill every implementation-owned section in `CODE_REVIEW-cloud-G08.md`.
