<!-- task=m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter plan=4 tag=REVIEW_REVIEW_REFACTOR milestone-task=transfer -->

# Validate owner integration records before resumable export

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G10.md` is mandatory. Implement only this follow-up scope, run every verification command, paste actual notes and stdout/stderr, keep the active pair in place, and report ready for review. 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 control-plane stop files, classify the next state, archive logs, or write `complete.log`; finalization belongs to the code-review agent.

## Background

The exporter now emits exact disjoint owner payload members and rejects output anywhere below the canonical workspace boundary. The follow-up review found that integration records still become resumable after only a key-prefix and version-field check, allowing structurally corrupt version-1 records to bypass non-resumable quarantine.

## Archive Evidence Snapshot

- `agent-task/m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter/plan_cloud_G10_3.log` and `code_review_cloud_G10_3.log` are the predicted archives for the failed plan-3 loop. The review has one Required finding: structurally invalid owner integration records with an accepted version are classified as resumable instead of quarantined.
- Fresh reviewer verification passed the predecessor PASS check, manifest SHA check, pinned commit check, focused owner/output tests, full package test, race test, vet, `git diff --check`, and `gofmt -d`. A focused reviewer reproducer failed because `client-process/flutter = {"schema_version":1}` was accepted as resumable. `evidence_integrity_failure=false`; the recorded implementation commands were accurate, but the behavioral oracle was incomplete.
- Exact/disjoint owner payload binding and arbitrary-name workspace confinement remain accepted. The `transfer` Milestone contribution remains S02 exporter evidence only; Task 06 still owns actual current-input reconciliation, private owner export, isolated staging build, and final bundle closure.

## Analysis

### Files Read

- `agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-chronos-extraction-decoupling.md`
- `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-chronos-extraction-decoupling/SDD.md`
- `agent-spec/runtime/iop-agent-cli-runtime.md`
- `agent-contract/inner/iop-agent-cli-runtime.md`
- `agent-task/m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter/PLAN-cloud-G10.md`
- `agent-task/m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter/CODE_REVIEW-cloud-G10.md`
- `agent-task/m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter/plan_cloud_G10_2.log`
- `agent-task/m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter/code_review_cloud_G10_2.log`
- `cmd/iop-chronos-transfer/main.go`
- `cmd/iop-chronos-transfer/main_test.go`
- `packages/go/agentstate/store.go`
- `packages/go/agentconfig/runtime_config.go`
- `packages/go/agenttask/types.go`
- `packages/go/agentpolicy/decision.go`
- `packages/go/agentworkspace/overlay.go`
- `packages/go/agentworkspace/integrator.go`
- `apps/agent/internal/clientprocess/store.go`
- `apps/agent/internal/clientprocess/types.go`
- `apps/agent/internal/localcontrol/ledger.go`
- `apps/agent/internal/projectlog/store.go`
- `apps/agent/internal/taskloop/module.go`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`
- `agent-ops/rules/project/domain/testing/rules.md`

### SDD Criteria

- `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-chronos-extraction-decoupling/SDD.md` is `[승인됨]` with its lock released; the exact first-line scope remains `milestone-task=transfer`.
- This follow-up targets S02. D01 and the S02 Evidence Map prohibit presenting unauthenticated, invalid, or unowned residual state as resumable and require versioned full-state provenance for the later isolated staging bundle.
- The checklist therefore requires validation against each owning durable record identity and payload contract before resumable routing, plus byte-preserving quarantine for every failed validation. Final verification remains local exporter evidence and does not claim the downstream real export or staging closure.

### Verification Context

- No external handoff was supplied. Repository-native evidence is the active pair, approved SDD, exact prior review logs, current exporter/source schemas, and local tests.
- Local preflight: `go version` reported `go1.26.2 linux/arm64`; `go env GOMOD` reported `/config/workspace/iop-s1/go.mod`. No external provider, credential, daemon, device state, or `iop-agent` execution is required or permitted.
- Fresh reviewer commands all passed except the intentional focused reproducer: exact predecessor PASS, manifest SHA `7026fedc8b0eb24eab5c4124d3818400f66db5f1d116a71e7781f346fbd256a7`, pinned commit availability with a different HEAD, focused tests, full package test, race test, vet, formatting, and whitespace checks.
- The failing reproducer used the production classifier with exact key `client-process/flutter` and payload `{"schema_version":1}`; it returned resumable `state:client-process-records`. Confidence is high because the defect is a direct classifier branch and deterministic unit oracle.

### Test Coverage Gaps

- Existing quarantine coverage rejects only `schema_version=99`; it does not reject an accepted version with missing, unknown, inconsistent, or corrupt family-specific fields.
- Existing tests do not verify exact durable key shapes for client, project-log, workspace-integration, selection, and route records, nor integrity verification for formats that carry integrity metadata.
- Exact payload member binding, lease/recovery partitioning, residual-file quarantine, output confinement, pinned-tree determinism, receipt shape, live-writer/socket rejection, and self-contained behavior are already covered and must remain passing.

### Symbol References

- No public or cross-package symbol is renamed. The affected internal classifier is `ownerIntegrationRecordLogicalID`; owning record encoders and validators are reference inputs only.

### Split Judgment

- Keep one compact follow-up. Exact key validation, strict payload validation, and quarantine routing form one atomic admission invariant: a record cannot be labeled resumable until all three agree, and each family needs the same table-driven acceptance matrix.
- Encoded predecessor 03 remains satisfied by the unique archived predecessor `complete.log` verified by the final command.

### Scope Rationale

- Modify only the exporter classifier/validation helpers and its tests. Do not modify owning runtime packages, the manifest, state fixture/schema, SDD/spec/contract, state store, receipt keys, output confinement, Chronos repository, or Task 06 artifacts.
- Do not normalize, rewrite, or drop invalid bytes. Preserve the original opaque record under the non-resumable quarantine payload so later audit evidence remains exact.

### Final Routing

- `evaluation_mode=isolated-reassessment`; finalizer=`finalize-task-policy.sh`; mode=`pair`.
- Build closures are all true. Scores=`2,2,2,2,2`, grade=`G10`, base/final route=`grade-boundary`, lane=`cloud`.
- Review closures are all true. Scores=`2,2,2,2,2`, route=`official-review`, lane=`cloud`, grade=`G10`.
- `large_indivisible_context=false`; matched loop risks=`boundary_contract, structured_interpretation, variant_product` (3); `review_rework_count=2`; `evidence_integrity_failure=false`; recovery boundary is present but does not replace the grade-boundary basis; no capability gap.
- Canonical files: `PLAN-cloud-G10.md`, `CODE_REVIEW-cloud-G10.md`.

## Implementation Checklist

- [ ] Validate every supported owner integration-record family against its exact durable key identity and complete versioned payload structure/integrity before resumable routing; preserve every unknown or invalid record only in non-resumable quarantine.
- [ ] Add table-driven valid/invalid family coverage, including malformed version-1 payloads and invalid durable identities, while preserving exact disjoint payload binding and the accepted workspace/output behavior.
- [ ] Run the focused validation/quarantine tests and the fresh full package, race, vet, baseline, formatting, and whitespace verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_REVIEW_REFACTOR-1] Enforce strict owner integration-record admission

**Problem**

`cmd/iop-chronos-transfer/main.go:1048-1067` maps opaque integration records by prefix and a single version field. Consequently a payload such as `{"schema_version":1}` under `client-process/flutter` is marked resumable although the owning durable decoder rejects it, violating D01 and the inherited invalid-residual quarantine requirement.

```go
// before: cmd/iop-chronos-transfer/main.go:1055
case strings.HasPrefix(key, "client-process/"):
    return "state:client-process-records", schemaVersion == 1
```

**Solution**

Keep the record bytes opaque in the emitted payload, but gate routing through family-specific validation. Require the exact key form owned by each family, strict one-document decoding with no unknown or trailing fields, the supported version, required identity fields and enum/state invariants, and checksum/integrity verification where the durable format defines it. Only a fully valid record returns its resumable logical ID; every other syntactically preserved record returns invalid and is copied byte-for-byte into `state:broken-residual-records`.

```go
// after
id, err := validateOwnerIntegrationRecord(key, record)
if err != nil {
    quarantine[key] = append(json.RawMessage(nil), record...)
    continue
}
documents[id].IntegrationRecords[key] = append(json.RawMessage(nil), record...)
```

**Modified Files and Checklist**

- [ ] `cmd/iop-chronos-transfer/main.go` — validate exact keys and strict payload contracts for client-process, local-control ledger, project-log journal/replay, workspace integration, task-loop selection, and route-decision records before resumable classification.
- [ ] `cmd/iop-chronos-transfer/main_test.go` — add valid controls and malformed version-1/key/payload/integrity cases and prove invalid bytes occur only in non-resumable quarantine.

**Test Strategy**

- Add `TestOwnerIntegrationRecordValidationMatrix` with one valid and multiple invalid cases per supported record family. Assert exact logical-ID routing for valid controls and rejection for wrong suffixes, missing/unknown fields, identity mismatch, invalid lifecycle values, unsupported versions, trailing values, and integrity mismatch where applicable.
- Strengthen `TestInvalidOwnerResidualIsOnlyNonResumableQuarantine` so it requires every supplied invalid record marker to be present in quarantine and absent from every resumable member.
- Preserve `TestOwnerStatePayloadsAreExactAndDisjoint`, focused owner snapshot tests, output confinement tests, and the full suite.

**Verification**

- `go test -count=1 ./cmd/iop-chronos-transfer -run 'TestOwnerIntegrationRecordValidationMatrix|TestInvalidOwnerResidualIsOnlyNonResumableQuarantine|TestOwnerStatePayloadsAreExactAndDisjoint'` — strict family admission and byte-preserving quarantine PASS.

## Modified Files Summary

| File | Item |
|------|------|
| `cmd/iop-chronos-transfer/main.go` | REVIEW_REVIEW_REFACTOR-1 |
| `cmd/iop-chronos-transfer/main_test.go` | REVIEW_REVIEW_REFACTOR-1 |
| `agent-task/m-iop-agent-chronos-extraction-decoupling/04+03_bundle_exporter/CODE_REVIEW-cloud-G10.md` | REVIEW_REVIEW_REFACTOR-1 |

## Final Verification

1. `bash -lc 'set -euo pipefail; mapfile -t predecessor_logs < <({ test ! -f agent-task/m-iop-agent-chronos-extraction-decoupling/03+02_inventory_closure/complete.log || printf "%s\n" agent-task/m-iop-agent-chronos-extraction-decoupling/03+02_inventory_closure/complete.log; find agent-task/archive -path "*/m-iop-agent-chronos-extraction-decoupling/03+02_inventory_closure/complete.log" -type f -print 2>/dev/null; } | sort -u); test "${#predecessor_logs[@]}" -eq 1; awk '\''BEGIN {in_summary=0; count=0} /^## Summary$/ {in_summary=1; next} in_summary && /^## / {in_summary=0} in_summary {line=$0; while (match(line, /final verdict: PASS/)) {count++; line=substr(line, RSTART+RLENGTH)}} END {exit count==1 ? 0 : 1}'\'' "${predecessor_logs[0]}"'` — exactly one predecessor completion has exactly one final PASS in Summary.
2. `test "$(sha256sum scripts/fixtures/iop-agent-chronos-ownership-manifest.tsv | awk '{print $1}')" = 7026fedc8b0eb24eab5c4124d3818400f66db5f1d116a71e7781f346fbd256a7` — the current-byte manifest baseline remains unchanged.
3. `git cat-file -e 3155be0e275437a8eedc1aa93497955a7d30465b^{commit} && test "$(git rev-parse HEAD)" != 3155be0e275437a8eedc1aa93497955a7d30465b` — the pinned commit remains available and distinct from HEAD.
4. `go test -count=1 ./cmd/iop-chronos-transfer` — the complete fresh exporter suite PASSes.
5. `go test -count=1 -race ./cmd/iop-chronos-transfer` — the fresh exporter race suite PASSes.
6. `go vet ./cmd/iop-chronos-transfer` — no findings.
7. `gofmt -d cmd/iop-chronos-transfer/main.go cmd/iop-chronos-transfer/main_test.go` — no output.
8. `git diff --check` — no whitespace errors.

All Go tests require fresh output. Run commands from the repository root under Bash; no external provider, credential, device state, daemon, or `iop-agent` execution is used.

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