<!-- task=m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence plan=3 tag=REVIEW_REVIEW_REVIEW_API milestone-task=cleanup-observation -->

# Close the Exact Observation Log Schema Oracle

## For the Implementing Agent

Resolve Required finding R1 exactly as mapped below. Strengthen only the test-local Zap field-schema oracle and its regression cases, run every verification command with fresh test execution, fill `CODE_REVIEW-cloud-G03.md` with literal notes and stdout/stderr, then leave both active files in place and report ready for review. Do not change production code, metric/log schemas, API or wire contracts, living specs, Node behavior, or external Claude/Mac qualification scope. 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-state files, classify the next state, archive logs, or write `complete.log`.

## Background

The third implementation rejects unknown metric labels and compares the production log tuple multiset, but its log helper receives `observer.LoggedEntry.ContextMap()`, which overwrites duplicate keys before validation. It also accepts three numeric Go types for fields produced as Zap `Int64Type` and accepts any bounded non-empty correlation, so the test can pass schema drift that the plan claims to reject.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/plan_cloud_G03_2.log`
- Prior review: `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/code_review_cloud_G03_2.log`
- Verdict: `FAIL` with Required finding R1.
- R1: the real-POST log oracle validates a lossy `ContextMap`, accepts `int`, `int64`, or `float64` for both numeric fields, and does not require the production-generated correlation shape.
- Fresh verification evidence: both predecessor checks, the focused real-POST test, the full OpenAI package, focused service observation tests, vet, deterministic spec checks, and `git diff --check` pass. The remaining defect is a test-oracle gap proven by the helper and Zap encoder implementations, not a production failure.
- Roadmap carryover: preserve `milestone-task=cleanup-observation`; deterministic S07 allowlist evidence remains in scope and actual Claude/Mac timing qualification remains deferred to S12 `claude-smoke`.

## Finding Resolution Map

| Finding | Disposition | Direct-fix Targets | Verified Current Evidence | Changed Precondition |
|---------|-------------|--------------------|---------------------------|----------------------|
| R1 | direct-fix | `apps/edge/internal/openai/single_request_handler_test.go` | `singleRequestLogKey` consumes `ContextMap`, whose Zap encoder overwrites duplicate keys, and lines 572-577 accept `int`, `int64`, or `float64`; lines 789-790 accept any bounded non-empty correlation. | The helper validates the raw Zap field slice before map conversion, requires exactly one of each production key with its exact Zap type, requires the generated correlation format, and has direct mutation regressions plus the same real-POST tuple/privacy assertions. |

## Analysis

### Files Read

- `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/plan_cloud_G03_2.log`
- `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/code_review_cloud_G03_2.log`
- `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/plan_cloud_G05_1.log`
- `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/code_review_cloud_G05_1.log`
- `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/complete.log`
- `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/15+14_observation_adapters/complete.log`
- `apps/edge/internal/openai/single_request_handler_test.go`
- `apps/edge/internal/service/single_request_metrics.go`
- `apps/edge/internal/service/single_request_metrics_test.go`
- `apps/edge/internal/service/single_request_observation.go`
- `apps/edge/internal/service/single_request_observation_test.go`
- `apps/edge/internal/bootstrap/single_request_observation_test.go`
- `agent-spec/index.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-contract/index.md`
- `agent-contract/outer/anthropic-compatible-api.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`
- `/config/go/pkg/mod/go.uber.org/zap@v1.27.0/zaptest/observer/logged_entry.go`
- `/config/go/pkg/mod/go.uber.org/zap@v1.27.0/zapcore/memory_encoder.go`
- `/config/go/pkg/mod/go.uber.org/zap@v1.27.0/field.go`
- `/config/go/pkg/mod/go.uber.org/zap@v1.27.0/zapcore/field.go`

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`; status `[승인됨]`, lock released, no `USER_REVIEW.md`.
- First-line scope: `milestone-task=cleanup-observation`.
- Targeted scenario: S07 `cleanup-observation`.
- Evidence Map driver: S07 requires cleanup-race, user-result-preservation, and raw-free timing/log/metric allowlist coverage. This packet closes only the linked production-adapter log-schema oracle; predecessor packets retain cleanup and timing ownership.
- The checklist therefore requires exact raw Zap field cardinality, uniqueness, types, closed tuple values, generated raw-free correlation, and the same real POST. S12 actual Claude/Mac qualification remains excluded.

### Verification Context

- No separate neutral verification handoff was supplied. Repository-native sources were the active/archived loop artifacts, local Edge test rules, the production observer, Zap observer encoding, related tests, the living specs, the Anthropic contract, and SDD S07.
- Predecessors are uniquely complete at `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/complete.log` and `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/15+14_observation_adapters/complete.log`.
- Fresh commands pass for the focused OpenAI test, full OpenAI package, focused service observation tests, vet, spec status/deferral checks, and whitespace. The current host reports Go 1.26.2 while the module baseline is Go 1.24; this test-only change uses existing Zap/Go APIs and adds no language-version dependency.
- Constraints: preserve unrelated dirty-worktree changes; do not change production projections or specs; use `-count=1` for fresh Go evidence; do not use dispatcher or external provider execution.
- External verification preflight: not applicable. Actual Claude/Mac timing is an explicit S12 exclusion.
- Gap: the current log oracle cannot detect duplicate raw fields, numeric Zap type drift, or a safe-looking constant correlation. Confidence: high, based on direct helper and Zap encoder inspection plus fresh passing commands.

### Test Coverage Gaps

- Covered: exact metric label names, expected metric/log lifecycle tuple counts, shared bounded correlation, public/log sentinel exclusion, unlabeled ingress, affected package/service regressions, and living-spec consistency.
- Missing: a direct regression proving duplicate raw Zap fields fail before `ContextMap` can collapse them.
- Missing: direct regressions proving `duration_ms` and `tool_count` reject non-`Int64Type` fields and correlation rejects a bounded non-generated constant.

### Symbol References

- `singleRequestLogKey` is test-local and is referenced only by `TestAnthropicSingleRequestObservation`; the follow-up may change its parameter from `map[string]interface{}` to the raw Zap field slice.
- No production symbol is renamed or removed.

### Split Judgment

- Keep one compact packet. The raw field validator, adversarial mutation cases, and real-POST assertion form one test oracle and live in one file.
- Runtime predecessors decoded from `16+14,15_observation_evidence` are satisfied by the exact packet 14 and packet 15 archive `complete.log` paths above.

### Scope Rationale

- Include only `apps/edge/internal/openai/single_request_handler_test.go` plus the active review evidence file.
- Exclude production service/metric/log code because current runtime output is correct and the defect is the integration-test oracle.
- Exclude living specs and contracts because their current scoped S07 statements remain correct once the oracle is strict.
- Exclude Node behavior, dashboards, external smoke, and roadmap mutation.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalize-task-policy.sh` ran once in `pair` mode and returned `status=routed`.
- Build closures: scope/context/verification/evidence/ownership/decision are all true. Scores `scope=1`, `state=0`, `blast=0`, `evidence=1`, `verification=1` produce G03 with base `local-fit`.
- Build signals: `large_indivisible_context=false`; positive loop-risk signatures are `boundary_contract` and `structured_interpretation`; `loop_risk_count=2`; `review_rework_count=3`; `evidence_integrity_failure=true`; `risk_boundary_matched=false`; `recovery_boundary_matched=true`.
- Build route: `recovery-boundary`, lane `cloud`, canonical filename `PLAN-cloud-G03.md`, catalog route `worker/cloud/G03`.
- Review closures are all true with scores `1/0/0/1/1`; route `official-review`, lane `cloud`, G03, canonical filename `CODE_REVIEW-cloud-G03.md`, catalog route `review/cloud/G03`.

## Dependencies and Execution Order

1. Packet 14 is satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/complete.log`.
2. Packet 15 is satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/15+14_observation_adapters/complete.log`.
3. Make the raw-field validator fail closed before relying on `ContextMap` or tuple projection.
4. Add adversarial validator regressions, then rerun the real-POST and package checks.

## Implementation Checklist

- [ ] Resolve R1 by validating exactly nine unique raw Zap fields with the production key-to-type mapping before deriving the closed log tuple.
- [ ] Require the production-generated correlation format and add table-driven regressions for duplicate, missing, unexpected, wrong-type, and bounded constant-correlation drift while retaining the real-POST tuple/privacy assertions.
- [ ] Keep production lifecycle behavior, metric/log schemas, API/wire contracts, Node behavior, and living specs unchanged.
- [ ] Run every dependency, focused, package, service-observation, vet, formatting, deterministic spec-consistency, and whitespace command with cache disabled where specified.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

## Implementation Plan

### [REVIEW_REVIEW_REVIEW_API-1] Enforce the raw Zap log schema

**Problem**

- `apps/edge/internal/openai/single_request_handler_test.go:543` accepts a `map[string]interface{}` created by `entry.ContextMap()`. Zap builds that map by assigning fields by key, so duplicate keys are overwritten before the helper can reject them.
- `apps/edge/internal/openai/single_request_handler_test.go:572-577` accepts `int`, `int64`, and `float64` for fields that production emits with `zap.Int64`/`zap.Int`, both represented as `zapcore.Int64Type`.
- `apps/edge/internal/openai/single_request_handler_test.go:789-790` accepts any bounded non-empty correlation, including a fixed constant that is not produced by `newSingleRequestCorrelationID`.

**Solution**

Validate `entry.Context` directly. Require exactly one each of `correlation`, `event_class`, `stage`, `operation`, `outcome`, `error_class`, `duration_ms`, `tool_count`, and `has_result`; reject duplicate, missing, and unknown keys; require the six string fields to be `zapcore.StringType`, both numeric fields to be `zapcore.Int64Type`, and `has_result` to be `zapcore.BoolType`. Derive the tuple only after that validation and accept only the production correlation shapes (`sr-` plus 32 lowercase hexadecimal characters or the bounded `sr-fallback-` base-36 form).

Before (`apps/edge/internal/openai/single_request_handler_test.go:784`):

```go
ctxMap := entry.ContextMap()
key, corrVal, err := singleRequestLogKey(ctxMap)
```

After:

```go
key, corrVal, err := singleRequestLogKey(entry.Context)
```

Add `TestSingleRequestLogSchemaRejectsDrift` in the same file. Construct one valid raw field slice with Zap constructors, then table-test at least duplicate key, missing key, unknown key, wrong numeric type, wrong boolean/string type, and bounded non-generated correlation mutations. Each mutation must fail, while the valid schema succeeds with the expected tuple.

**Modified Files and Checklist**

- [ ] `apps/edge/internal/openai/single_request_handler_test.go` — validate the raw field schema, generated correlation shape, direct mutation regressions, and the existing real-POST projection.

**Test Strategy**

- Add `TestSingleRequestLogSchemaRejectsDrift` as a table-driven helper regression in `apps/edge/internal/openai/single_request_handler_test.go`.
- Retain `TestAnthropicSingleRequestObservation` as the production-adapter integration path and pass its raw captured fields to the strict helper.
- Do not add a fake observer or modify production output. The unit mutations prove the oracle fails on drift; the real POST proves the oracle is connected to production.

**Verification**

- `go test ./apps/edge/internal/openai -run 'TestSingleRequestLogSchemaRejectsDrift|TestAnthropicSingleRequestObservation' -count=1`
- `go test ./apps/edge/internal/service -run 'TestSingleRequestMetrics|TestSingleRequestObservationLifecycleIntegration' -count=1`
- Expected: all valid production entries pass, and duplicate/missing/unknown/wrong-type/constant-correlation mutations fail deterministically.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/edge/internal/openai/single_request_handler_test.go` | REVIEW_REVIEW_REVIEW_API-1 / R1 |
| `agent-task/m-iop-owned-single-request-agent-execution/16+14,15_observation_evidence/CODE_REVIEW-cloud-G03.md` | REVIEW_REVIEW_REVIEW_API-1 evidence |

## Final Verification

1. `test -f agent-task/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/complete.log' | wc -l)" -eq 1`
2. `test -f agent-task/m-iop-owned-single-request-agent-execution/15+14_observation_adapters/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/15+14_observation_adapters/complete.log' | wc -l)" -eq 1`
3. `go test ./apps/edge/internal/openai -run 'TestSingleRequestLogSchemaRejectsDrift|TestAnthropicSingleRequestObservation' -count=1`
4. `go test ./apps/edge/internal/openai -count=1`
5. `go test ./apps/edge/internal/service -run 'TestSingleRequestMetrics|TestSingleRequestObservationLifecycleIntegration' -count=1`
6. `go vet ./apps/edge/internal/openai`
7. `gofmt -d apps/edge/internal/openai/single_request_handler_test.go`
8. `test "$(sed -n 's/^status: //p' agent-spec/input/openai-compatible-surface.md | head -n 1)" = "부분"`
9. `! rg --fixed-strings 'Those drivers, raw-free cleanup observation, and actual Claude qualification remain separate work' agent-spec/runtime/edge-node-execution.md`
10. `git diff --check`

Expected: both predecessors remain uniquely complete; the strict validator rejects every known raw-field/type/correlation mutation; the production-adapter real POST still proves the exact lifecycle metric/log tuple and privacy boundary; affected package, service observation tests, vet, formatting, spec consistency, and whitespace pass. Cached Go test output is not acceptable.

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