<!-- task=m-iop-agent-cli-runtime/17+13_project_log_records plan=3 tag=REVIEW_API -->

# Bound Sealed Quota Identities at the Project-Log Boundary

## For the Implementing Agent

Implement every checklist item, run the exact verification commands, and fill every implementation-owned section in `CODE_REVIEW-cloud-G03.md` with actual notes and stdout/stderr. Keep both active files in place and report ready for review; only the code-review agent may append a verdict, archive logs, write `complete.log`, or create a control-plane stop state. 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, classify the next state, or create `USER_REVIEW.md`.

## Background

The record validator now rejects unsealed quota values, but a valid shared projection seal does not enforce this package's stricter identity length and sensitive-content limits. A sealed observation can therefore make oversized or secret-like provider/target text durable inside an otherwise valid project-log record. This follow-up closes only that remaining record boundary.

## Archive Evidence Snapshot

- Task path: `agent-task/m-iop-agent-cli-runtime/17+13_project_log_records`
- Archived plan: `agent-task/m-iop-agent-cli-runtime/17+13_project_log_records/plan_local_G04_2.log`
- Archived review: `agent-task/m-iop-agent-cli-runtime/17+13_project_log_records/code_review_cloud_G04_2.log`
- Verdict: `FAIL`
- Findings: 1 Required, 0 Suggested, 0 Nit.
- Affected files: `apps/agent/internal/projectlog/record.go`, `apps/agent/internal/projectlog/record_test.go`.
- Verification evidence:
  - Fresh focused/package tests, formatting, vet, and `git diff --check` passed.
  - A reviewer-only temporary regression test failed because valid sealed observations with an adapter longer than `MaxIDLength` and a `Bearer provider-secret` target were both accepted. The temporary test was removed after capture.
- Roadmap carryover: this packet remains a strict S12 `project-logs` subset and intentionally has no `Roadmap Targets`; PASS must not check the Milestone Task.

## Analysis

### Files Read

- `apps/agent/internal/projectlog/record.go`
- `apps/agent/internal/projectlog/record_test.go`
- `packages/go/agentpolicy/quota.go`
- `packages/go/agentpolicy/failure_policy_test.go`
- `packages/go/agentprovider/cli/status/quota.go`
- `packages/go/agentprovider/cli/status/quota_test.go`
- `packages/go/agenttask/types.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-task/m-iop-agent-cli-runtime/17+13_project_log_records/plan_local_G04_2.log`
- `agent-task/m-iop-agent-cli-runtime/17+13_project_log_records/code_review_cloud_G04_2.log`

### SDD Criteria

- SDD: `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`
- Status: `[승인됨]`; SDD and Milestone locks are released and no active SDD `USER_REVIEW.md` exists.
- Targeted scenario: S12, Milestone Task `project-logs`.
- Evidence Map driver: the WORK_LOG loop/attempt/locator and archive-reconciliation fixture must preserve bounded, stable evidence before terminal archive closure.
- This packet hardens only the quota identity fields admitted by the strict record input contract. It does not claim the S12 journal, dynamic frontier, exactly-once archive lifecycle, or the `project-logs` Roadmap Task.

### Verification Context

- No external verification handoff was supplied.
- Sources: `agent-test/local/rules.md`, `agent-test/local/platform-common-smoke.md`, the archived review evidence above, related source/tests, and fresh repository-native probes.
- Preflight: Go resolves to `/config/.local/bin/go`; `go version go1.26.2 linux/arm64`; `GOROOT=/config/opt/go`. No service, credential, port, or external runner is required.
- Fresh passing commands: `gofmt -d`, focused/package `go test -count=1`, `go vet`, and `git diff --check`.
- Fresh failing evidence: a temporary production-validator test constructed valid sealed observations through `status.NormalizeQuotaSnapshot` and `agentpolicy.NormalizeQuotaObservation`; both oversized adapter and sensitive target variants returned nil from `ProjectLogRecord.Validate`.
- Constraint: the target files are untracked, so `git diff --check` does not inspect them; `gofmt -d` and fresh compilation/tests remain mandatory.
- Confidence: high. The failing cases call the production normalization and record validation boundaries directly.

### Test Coverage Gaps

- Existing coverage proves valid sealed and canonical corrupt observations pass and an unsealed observation fails.
- No test applies project-log `MaxIDLength` or sensitive-content rules to the exported identities of an otherwise valid sealed observation.

### Symbol References

None. No symbol is renamed or removed, and no production importer currently references `apps/agent/internal/projectlog`.

### Split Judgment

Keep one compact packet because the validator change and its regression matrix are one record-boundary invariant with one focused PASS oracle.

### Scope Rationale

Do not change `packages/go/agentpolicy` or `packages/go/agentprovider/cli/status`; their sealed projection remains the shared integrity boundary, while this package independently enforces its stricter durable record limits. Do not change archive construction, journal persistence, retention, replay, event sinks, WORK_LOG projection, daemon wiring, or the full S12 matrix.

### Final Routing

- evaluation_mode: `isolated-reassessment`
- finalizer: `finalize-task-policy.sh pair`
- build closures: scope/context/verification/evidence/ownership/decision all closed
- build scores: scope `1`, state `0`, blast `0`, evidence `0`, verification `1`; grade `G02`
- build base route: `local-fit`
- build route: `recovery-boundary`, `cloud`, `PLAN-cloud-G02.md`
- review closures: scope/context/verification/evidence/ownership/decision all closed
- review scores: scope `1`, state `0`, blast `0`, evidence `1`, verification `1`; grade `G03`
- review route: `official-review`, `cloud`, `CODE_REVIEW-cloud-G03.md`
- large_indivisible_context: `false`
- matched loop risks: `boundary_contract`, `variant_product` (count `2`)
- recovery signals: `review_rework_count=2`, `evidence_integrity_failure=false`
- capability-gap evidence: none

## Implementation Checklist

- [ ] Enforce project-log identity bounds and sensitive-content rejection for valid/stale sealed quota observations while preserving canonical corrupt evidence, with oversized and sensitive regression coverage.
- [ ] Run fresh focused/package tests, formatting, vet, and diff verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Validate Sealed Quota Identities

#### Problem

`apps/agent/internal/projectlog/record.go:160-164` calls `json.Marshal` to prove the private shared projection seal, but the shared quota boundary intentionally accepts arbitrary trimmed adapter and target identities. The project-log validator consequently accepts sealed identities that exceed `MaxIDLength` or match its sensitive-content patterns.

#### Solution

Keep JSON marshaling as the integrity-seal check. For valid or stale observations, additionally pass `SnapshotID`, `Adapter`, and `Target` through the existing required `validateIdentity` helper. A canonical corrupt observation remains valid because its exact empty shape is already proven by `QuotaObservation.MarshalJSON`; malformed corrupt or unsealed values still fail the seal check.

Before (`record.go:160-164`):

```go
if r.QuotaObservation != nil {
	if _, err := json.Marshal(r.QuotaObservation); err != nil {
		return fmt.Errorf("%w: invalid safe quota observation", ErrInvalidIdentity)
	}
}
```

After:

```go
if err := validateQuotaObservation(r.QuotaObservation); err != nil {
	return err
}
```

The helper must validate the seal first, preserve only the canonical corrupt empty projection, and apply the project-log identity rules to every identity in valid/stale projections.

#### Modified Files and Checklist

- [ ] `apps/agent/internal/projectlog/record.go` — add the project-log validation layer for sealed quota identities.
- [ ] `apps/agent/internal/projectlog/record_test.go` — add sealed oversized adapter and sensitive target cases while retaining valid, corrupt, and unsealed coverage.

#### Test Strategy

Extend `TestRecordValidationMatrix` with table-driven unsafe sealed observations created through the real status and policy normalization functions. Assert `errors.Is(err, ErrUnboundedField)` for an adapter longer than `MaxIDLength` and `errors.Is(err, ErrSensitiveContent)` for a `Bearer provider-secret` target. Keep the canonical corrupt and unsealed assertions unchanged.

#### Verification

```bash
go test -count=1 ./apps/agent/internal/projectlog -run 'TestRecord'
```

Expected: valid/canonical corrupt observations pass, while unsealed, oversized, and sensitive variants fail with the intended error categories.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/agent/internal/projectlog/record.go` | REVIEW_API-1 |
| `apps/agent/internal/projectlog/record_test.go` | REVIEW_API-1 |

## Final Verification

Fresh test execution is required; cached Go test output is not acceptable.

```bash
gofmt -w apps/agent/internal/projectlog/record.go apps/agent/internal/projectlog/record_test.go
test -z "$(gofmt -d apps/agent/internal/projectlog/record.go apps/agent/internal/projectlog/record_test.go)"
go test -count=1 ./apps/agent/internal/projectlog -run 'TestRecord|TestArchive'
go test -count=1 ./apps/agent/internal/projectlog ./packages/go/agentpolicy
go vet ./apps/agent/internal/projectlog
git diff --check
```

Expected: formatting is stable; strict record/archive regressions and the shared quota projection package pass fresh; vet and diff checks are clean.

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