<!-- task=m-iop-agent-cli-runtime/12+10,11_change_set_integration plan=2 tag=REVIEW_API -->

# Restore Hierarchy-safe Type Replacement

## For the Implementing Agent

Implement only this follow-up scope. Run every verification command, fill the implementation-owned sections in `CODE_REVIEW-*-G??.md` with actual notes and output, keep both active files in place, and report ready for review. Finalization belongs to the code-review skill. If blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields; do not ask the user, call user-input tools, create stop files, classify the next state, archive logs, or write `complete.log`.

## Background

The second review confirmed that candidate validation isolation and terminal-only ordinal admission now pass, but found a remaining S19 hierarchy defect. `applyPreparedChanges` writes a parent type replacement before deleting the change-set-owned descendants beneath it, so a clean directory-to-file or directory-to-symlink change set is rejected as a non-empty-directory error. The fix must preserve the managed-predecessor protections from the prior follow-up while making valid type replacements atomic.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-iop-agent-cli-runtime/12+10,11_change_set_integration/plan_cloud_G08_1.log`
- Prior review: `agent-task/m-iop-agent-cli-runtime/12+10,11_change_set_integration/code_review_cloud_G10_1.log`
- Verdict: FAIL; Required=1, Suggested=0, Nit=0.
- Affected files: `packages/go/agentworkspace/integrator.go`, `packages/go/agentworkspace/integrator_test.go`, and temporary reviewer evidence in `packages/go/agentworkspace/review_reproducer_test.go`.
- Evidence: fresh `TestSerialIntegrator` and focused AgentTask ordinal suites passed, while `TestReviewReproducerDirectoryToRegularReplacement` failed because candidate apply attempted to replace `tree/` before deleting `tree/child.txt`.
- Roadmap carryover: `change-set-integration` remains incomplete until S19 clean type replacement, managed-descendant safety, exact rollback, deterministic ordinal, revised retry, restart, and retention evidence all pass.

## 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:
  - `change-set-integration`: immutable change set and serial integration
- Completion mode: check-on-pass

## Analysis

### Files Read

- `packages/go/agentworkspace/change_set.go`
- `packages/go/agentworkspace/integrator.go`
- `packages/go/agentworkspace/integrator_test.go`
- `packages/go/agentworkspace/review_reproducer_test.go`
- `packages/go/agenttask/integration_queue.go`
- `packages/go/agenttask/integration_queue_test.go`
- `agent-contract/inner/agent-runtime.md`
- `agent-contract/inner/iop-agent-cli-runtime.md`
- `agent-spec/runtime/edge-node-execution.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-task/m-iop-agent-cli-runtime/12+10,11_change_set_integration/plan_cloud_G10_0.log`
- `agent-task/m-iop-agent-cli-runtime/12+10,11_change_set_integration/code_review_cloud_G10_0.log`
- `agent-task/m-iop-agent-cli-runtime/12+10,11_change_set_integration/plan_cloud_G08_1.log`
- `agent-task/m-iop-agent-cli-runtime/12+10,11_change_set_integration/code_review_cloud_G10_1.log`

### SDD Criteria

- SDD: `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`; status `[승인됨]`, lock released.
- Target: S19 / `change-set-integration`.
- Acceptance requires every clean three-way result to integrate automatically, while conflict, unmanaged drift, and validation failure retain the overlay without partial canonical mutation.
- Evidence Map S19 requires clean/conflict, managed/unmanaged drift, validation rollback, terminal-deferred queue advance, retry revision, restart, retention, and atomic/no-duplicate `IntegrationRecord` evidence. This follow-up adds the missing clean hierarchy type-replacement evidence and reruns the existing matrix.

### Verification Context

- No separate `verification_context` handoff was supplied.
- Repository-native sources: `agent-test/local/rules.md`, `agent-test/local/platform-common-smoke.md`, the active and archived review evidence, S19, both runtime contracts, and the package tests listed above.
- Preconditions: local checkout at `/config/workspace/iop-s0`; Go 1.26.2 resolves to `/config/opt/go/bin/go`; Git resolves to `/bin/git`; no external service or credential is required.
- Fresh review evidence: `go test -count=1 -run '^TestSerialIntegrator' ./packages/go/agentworkspace` passed; the focused AgentTask ordinal/retry/restart suite passed; `go test -count=1 -run '^TestReviewReproducer' -v ./packages/go/agentworkspace` failed `TestReviewReproducerDirectoryToRegularReplacement` with `refusing to replace non-empty directory`.
- Constraints: use the existing temporary Git fixture; do not add dependencies or leave reviewer-only verification files in the repository; fresh `-count=1` output is required.
- External verification: not applicable. The change remains inside the host-neutral workspace integration backend and its deterministic package tests.
- Confidence: high; the failing operation order is visible at `integrator.go:1058-1069` and has a deterministic reproducer.

### Test Coverage Gaps

- Directory-to-regular replacement with owned descendants is currently covered only by the temporary failing reviewer reproducer.
- Directory-to-symlink replacement follows the same `removeEmptyDirectory` path and has no regression.
- Directory deletion with an independent managed-predecessor descendant is already covered and must remain passing.

### Symbol References

None. No public or internal symbol rename is required.

### Split Judgment

Keep one plan. Delete ordering, parent type installation, candidate validation, canonical apply, and rollback all use the same `applyPreparedChanges` transaction and cannot independently PASS.

### Scope Rationale

Limit production changes to `packages/go/agentworkspace/integrator.go`. Add permanent regressions to `integrator_test.go` and remove `review_reproducer_test.go` after migrating its useful case. Do not change change-set identity, candidate isolation, merge policy, rollback schema, AgentTask ordinal admission, contracts, roadmap state, configuration, protobuf, or binary entrypoints.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`.
- Build closures: scope/context/verification/evidence/ownership/decision all true. Scores=`1/2/2/1/1`, grade=G07, base=`local-fit`, route=`recovery-boundary`, lane=`cloud`, filename=`PLAN-cloud-G07.md`.
- Review closures: scope/context/verification/evidence/ownership/decision all true. Scores=`1/2/2/2/2`, grade=G09, route=`official-review`, lane=`cloud`, filename=`CODE_REVIEW-cloud-G09.md`.
- `large_indivisible_context=false`.
- Positive loop risks: `temporal_state`, `concurrent_consistency`, `boundary_contract`; count=3.
- Recovery signals: `review_rework_count=2`, `evidence_integrity_failure=true`; recovery boundary=true.
- Capability gap: none.

## Implementation Checklist

- [ ] Apply change-set-owned descendant deletions before directory-to-regular or directory-to-symlink parent replacement, with permanent regressions for both variants and no loss of managed-predecessor content.
- [ ] Run fresh focused, race, package-wide, vet, formatting, reviewer-artifact cleanup, and diff verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Order hierarchy type replacements atomically

**Problem:** `packages/go/agentworkspace/integrator.go:1058` sorts all non-delete operations ahead of deletes. A directory-to-regular or directory-to-symlink change set therefore calls `installRegular` or `installSymlink` while the directory still contains owned descendants; `removeEmptyDirectory` rejects the otherwise clean change set.

**Solution:** Make hierarchy ordering operation-aware. Apply deletions deepest-first before writes, then apply non-deletions shallowest-first. Keep `removeEmptyDirectory` and `removePreparedTarget` non-recursive so any unexpected independent descendant remains a hard error or preserved container rather than being destroyed.

Before (`packages/go/agentworkspace/integrator.go:1058`):

```go
sort.SliceStable(ordered, func(left, right int) bool {
	leftDelete := ordered[left].Result == nil
	rightDelete := ordered[right].Result == nil
	if leftDelete != rightDelete {
		return !leftDelete
	}
```

After:

```go
sort.SliceStable(ordered, func(left, right int) bool {
	leftDelete := ordered[left].Result == nil
	rightDelete := ordered[right].Result == nil
	if leftDelete != rightDelete {
		return leftDelete
	}
```

**Modified Files and Checklist:**

- [ ] `packages/go/agentworkspace/integrator.go` — delete owned descendants before parent type installation while retaining deepest-delete/shallowest-write ordering.
- [ ] `packages/go/agentworkspace/integrator_test.go` — add a table-driven permanent regression for non-empty directory replacement by regular file and symlink; assert integrated outcome, exact final type/content/target, removed owned descendants, and stable managed-descendant behavior.
- [ ] `packages/go/agentworkspace/review_reproducer_test.go` — remove the temporary reviewer-only file after its useful regression is represented in the canonical suite.

**Test Strategy:** Add `TestSerialIntegratorReplacesNonEmptyDirectoryByType` in `integrator_test.go` with `regular` and `symlink` cases using the existing temporary Git fixture. Each case freezes a non-empty directory replacement, requires an integrated result, verifies exact canonical type and payload/target, and proves the original child is absent. Keep `TestSerialIntegratorPreservesManagedDescendantAcrossDirectoryDeletion` as the independent-predecessor guard.

**Verification:**

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

Expected: both type replacements integrate and the managed-predecessor directory deletion regression remains passing.

## Modified Files Summary

| File | Item |
|---|---|
| `packages/go/agentworkspace/integrator.go` | REVIEW_API-1 |
| `packages/go/agentworkspace/integrator_test.go` | REVIEW_API-1 |
| `packages/go/agentworkspace/review_reproducer_test.go` | REVIEW_API-1 |

## Final Verification

```bash
command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT
command -v git
go test -count=1 -run '^(TestSerialIntegratorReplacesNonEmptyDirectoryByType|TestSerialIntegratorPreservesManagedDescendantAcrossDirectoryDeletion)$' -v ./packages/go/agentworkspace
go test -count=1 -run '^TestSerialIntegrator' -v ./packages/go/agentworkspace
go test -count=1 -run '^(TestIntegrationCandidateRequiresTerminalLowerOrdinal|TestIntegrationStopResumePreservesOrdinal|TestIntegrationOutOfOrderWorkerCompletionUsesDispatchOrdinal|TestIntegrationTerminalDeferredAdvancesIndependentQueue|TestRevisedChangeSetUsesNextIntegrationAttempt|TestRestartReplayUsesStableIdempotencyKeysWithoutDuplicateCalls)$' -v ./packages/go/agenttask
go test -count=1 -race ./packages/go/agentworkspace ./packages/go/agentstate ./packages/go/agenttask
go test -count=1 ./packages/go/...
go vet ./packages/go/...
gofmt -d packages/go/agentworkspace/integrator.go packages/go/agentworkspace/integrator_test.go
test ! -e packages/go/agentworkspace/review_reproducer_test.go
git diff --check
```

Expected: every command exits zero; fresh tests prove clean regular/symlink type replacement, hierarchy-safe managed predecessor handling, exact rollback/restart behavior, terminal ordinal admission, and package-wide compatibility. Cached test output is not acceptable. Repository E2E smoke and full-cycle execution remain not applicable because no user execution pipeline changes.

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