<!-- task=m-iop-agent-cli-runtime/09+05_workflow_evidence plan=2 tag=REVIEW_REFACTOR -->

# Gate Recovered Submissions Before Official Review

## For the Implementing Agent

Implementation is complete only after the implementation-owned sections in `CODE_REVIEW-cloud-G06.md` contain actual notes and command output. Run the listed verification, fill the review artifact, keep both active files in place, and report ready for review. If blocked, record the exact blocker, attempted command/output, and resume condition only 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 belongs to the code-review skill.

## Background

The normal dispatch path checks both `Submission.Ready` and provider-neutral workflow evidence before official review. The recovered-submission path skips both checks and moves directly to `reviewing`, so a restart can bypass SDD S08 for incomplete, placeholder, inactive, or mismatched artifacts. This follow-up closes that recovery-only gap without changing the artifact matcher contract.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-iop-agent-cli-runtime/09+05_workflow_evidence/plan_local_G02_1.log`
- Prior review: `agent-task/m-iop-agent-cli-runtime/09+05_workflow_evidence/code_review_cloud_G05_1.log`
- Verdict: FAIL
- Findings: Required=1, Suggested=0, Nit=0. `RecoveryExecutionSubmitted` bypasses `Submission.Ready` and `gateSubmissionEvidence` before official review.
- Affected files: `packages/go/agenttask/reconcile.go`, `packages/go/agenttask/manager_test.go`
- Reviewer verification: focused matcher/manager tests, package tests, race, vet, all `packages/go/...` tests, formatting, boundary search, and `git diff --check` passed; the passing suite does not exercise recovered placeholder/mismatched evidence.
- Roadmap carryover: `workflow-evidence` remains pending until recovery uses the same matcher gate and records review-invocation/locator evidence.

## 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:
  - `workflow-evidence`: common artifact matcher, review gate, Pi evidence repair
- Completion mode: check-on-pass

## Analysis

### Files Read

- `agent-task/m-iop-agent-cli-runtime/09+05_workflow_evidence/plan_local_G02_1.log`
- `agent-task/m-iop-agent-cli-runtime/09+05_workflow_evidence/code_review_cloud_G05_1.log`
- `agent-task/m-iop-agent-cli-runtime/09+05_workflow_evidence/plan_cloud_G09_0.log`
- `agent-task/m-iop-agent-cli-runtime/09+05_workflow_evidence/code_review_cloud_G09_0.log`
- `agent-task/archive/2026/07/m-iop-agent-cli-runtime/05_contract_boundary/complete.log`
- `packages/go/agenttask/reconcile.go`
- `packages/go/agenttask/dispatch.go`
- `packages/go/agenttask/workflow_evidence.go`
- `packages/go/agenttask/workflow_evidence_test.go`
- `packages/go/agenttask/manager_test.go`
- `packages/go/agenttask/manager_integration_test.go`
- `packages/go/agenttask/integration_queue_test.go`
- `packages/go/agenttask/test_support_test.go`
- `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-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `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: `[승인됨]`; SDD lock: `해제`
- Acceptance Scenario: S08 / Milestone Task `workflow-evidence`
- S08 requires every provider to pass the same matcher and forbids official review before a match; the Evidence Map requires a provider-neutral matcher/Pi same-context repair matrix with review-invocation and locator evidence.
- The checklist therefore gates recovered submissions before the `reviewing` transition and adds recovery-specific complete, incomplete, placeholder, mismatch, and Pi rematch assertions.

### Verification Context

- Handoff: the official review supplied one Required finding, fresh command output, `review_rework_count=2`, and `evidence_integrity_failure=false`.
- Repository-native sources: local/platform-common test rules, the S08 SDD row, `agent-runtime` review contract, the normal dispatch gate, recovery state transition, and current harness fakes.
- Preflight: `/config/.local/bin/go` resolves to `/config/opt/go/bin/go`; Go is `go1.26.2 linux/arm64`; `GOROOT=/config/opt/go`; branch `feature/iop-agent-cli-runtime`; HEAD `18c9f08c078202dc4a7d4998fb17d9718d1576a7`; the intended sync basis is the current dirty milestone worktree.
- Required verification: focused recovery/matcher tests, affected package tests, race, `go vet ./packages/go/...`, fresh all-package tests, formatting, forbidden-boundary search, and `git diff --check`.
- External verification: not applicable. This internal recovery-gate fix does not change a user entrypoint, provider process, external service, credential, proto, or field runtime.
- Gap: the existing recovered-submission test accepts only default complete evidence and never asserts zero reviewer calls for recovered incomplete/placeholder/mismatch cases.
- Confidence: high; the bypass is a direct transition from `RecoveryExecutionSubmitted` to `reviewing`.

### Test Coverage Gaps

- Normal dispatch complete/placeholder/mismatch and Pi repair/rematch behavior is covered by `TestManagerWorkflowEvidenceGateAndPiRepair`.
- Recovery with a complete submission is covered by `TestRestartRetainsLiveChildWithoutDuplicateInvocation`, but it relies on the fake evidence default and does not prove the matcher is invoked.
- Recovered `Ready=false`, placeholder, inactive/identity-mismatched evidence, and Pi repair/rematch are not covered. Add one table-driven recovery regression that asserts blocker codes, observe/repair counts, and reviewer-call counts.

### Symbol References

- None. No symbol is renamed or removed.

### Split Judgment

- Keep one plan: the recovered transition and its regression matrix are one invariant and cannot independently PASS.
- Split predecessor `05_contract_boundary` is satisfied by `agent-task/archive/2026/07/m-iop-agent-cli-runtime/05_contract_boundary/complete.log`.

### Scope Rationale

- Modify only `packages/go/agenttask/reconcile.go` and `packages/go/agenttask/manager_test.go`.
- Do not change public ports, artifact identity/completeness types, the normal dispatch gate, contracts, roadmap documents, archive logs, or common Agent-Ops files.
- Preserve current Pi-only same-context repair, durable locator/ordinal validation, mandatory rematch, and typed blocker codes.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`; status=`routed`
- Build closures: scope/context/verification/evidence/ownership/decision all `true`; capability gap: none.
- Build scores: scope=1, state=2, blast=1, evidence=1, verification=1; grade=`G06`; base basis=`local-fit`; route basis=`recovery-boundary`; route=`cloud G06`; filename=`PLAN-cloud-G06.md`.
- Review closures: scope/context/verification/evidence/ownership/decision all `true`; capability gap: none.
- Review scores: scope=1, state=2, blast=1, evidence=1, verification=1; route basis=`official-review`; route=`cloud G06`; filename=`CODE_REVIEW-cloud-G06.md`.
- `large_indivisible_context=false`; matched loop risks: `temporal_state`, `boundary_contract`; count=2.
- Recovery signals: `review_rework_count=2`, `evidence_integrity_failure=false`; recovery boundary matched.

## Implementation Checklist

- [ ] Gate `RecoveryExecutionSubmitted` with `Submission.Ready` and `gateSubmissionEvidence` before persisting `submitted`/`reviewing`; retain typed blockers and Pi repair/rematch behavior.
- [ ] Add a recovery regression matrix proving complete evidence can review while recovered incomplete, placeholder, inactive/identity-mismatched evidence cannot, and Pi reviews only after same-context repair plus rematch.
- [ ] Run every Final Verification command exactly and paste actual stdout/stderr into `CODE_REVIEW-cloud-G06.md`.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_REFACTOR-1] Enforce the workflow evidence gate during recovery

**Problem:** `packages/go/agenttask/reconcile.go:257` validates recovered submission identity and then transitions directly to `submitted`/`reviewing`. Unlike `dispatch.go:271-280`, it does not reject `Ready=false` or invoke `gateSubmissionEvidence`, so `runWork` can call the reviewer after restart without a workflow artifact match.

**Solution:** clone the recovered submission, apply the same readiness and evidence checks used by normal dispatch, and only then commit the recovered submission and `reviewing` transition.

Before (`reconcile.go:257`):

```go
if err := validateSubmission(project, work, *observation.Submission); err != nil {
    // block stale checkpoint
}
return m.changeWork(ctx, projectID, workID, func(current *WorkRecord) error {
    // transition directly to submitted/reviewing
})
```

After:

```go
submission := cloneRecoveredSubmission(observation.Submission)
if err := validateSubmission(project, work, submission); err != nil {
    // block stale checkpoint
}
if !submission.Ready {
    // block as submission_incomplete
}
if blocker := m.gateSubmissionEvidence(ctx, project, work, submission); blocker != nil {
    // persist the typed evidence blocker
}
return m.changeWork(ctx, projectID, workID, func(current *WorkRecord) error {
    // generation check, then persist submitted/reviewing
})
```

Use an inline defensive clone or an existing local helper; do not add a public API solely for cloning.

**Modified Files and Checklist:**

- [ ] `packages/go/agenttask/reconcile.go` — gate recovered submissions before any reviewable state is persisted.
- [ ] `packages/go/agenttask/manager_test.go` — add the recovery evidence regression matrix and exact call-count assertions.
- [ ] Confirm `packages/go/agenttask/dispatch.go`, workflow evidence ports/types, contracts, roadmap documents, and archive evidence remain unchanged.

**Test Strategy:** Add `TestRestartRecoveredSubmissionRequiresWorkflowEvidence` in `manager_test.go`. Cover complete evidence, `Ready=false`, non-Pi placeholder, inactive or wrong identity, successful Pi repair/rematch, and Pi repair without a fresh match. Assert final/blocker state, `Observe`/`Repair` counts, and `Reviewer` calls; every unmatched case must keep reviewer calls at zero.

**Verification:** The focused recovery/matcher test command passes with fresh execution, followed by package, race, vet, and all-package regression commands.

## Modified Files Summary

| File | Item |
|---|---|
| `packages/go/agenttask/reconcile.go` | REVIEW_REFACTOR-1 |
| `packages/go/agenttask/manager_test.go` | REVIEW_REFACTOR-1 |

## Dependencies and Execution Order

`05_contract_boundary` is complete at `agent-task/archive/2026/07/m-iop-agent-cli-runtime/05_contract_boundary/complete.log`. Implement the recovery gate, add the matrix, run verification, then stop for official review.

## Final Verification

```bash
command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT
git status --short
go test -count=1 -run 'TestRestartRecoveredSubmissionRequiresWorkflowEvidence|TestManagerWorkflowEvidenceGateAndPiRepair|TestMatchWorkflowEvidence' -v ./packages/go/agenttask
go test -count=1 ./packages/go/agenttask
go test -race -count=1 ./packages/go/agenttask
go vet ./packages/go/...
go test -count=1 ./packages/go/...
test -z "$(gofmt -d packages/go/agenttask/reconcile.go packages/go/agenttask/manager_test.go)"
test -z "$(rg --sort path -n 'USER_REVIEW|complete\.log' packages/go/agenttask || true)"
git diff --check
```

Every command must pass. Go test cache output is not accepted because every test command uses `-count=1`.

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