<!-- task=m-principal-provider-credential-slot-routing/07+01,02,05_secret_material plan=3 tag=REVIEW_REVIEW_API milestone-task=secret-at-rest -->

# Provider credential boundary-evidence repair plan

## For the Implementing Agent

Filling every implementation-owned section in `CODE_REVIEW-*-G??.md` is mandatory. Run the verification commands, paste actual notes and stdout/stderr, keep the active files in place, and report ready for review; finalization belongs only to the code-review skill. If blocked, record only the exact blocker, attempted commands/output, and resume condition 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`.

## Background

The production fixes now canonicalize durable credential-kind AAD and reject configured encryption without a database. Review found that two required regressions do not assert the invariants they claim: the unknown-kind test discards the observed store state, and the run test matches Client/Edge log messages that production never emits. This follow-up changes only test evidence and preserves the accepted production behavior.

## Archive Evidence Snapshot

- `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/plan_local_G06_2.log` and `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/code_review_cloud_G06_2.log`: current pair closed with `FAIL`; Required findings are exact unknown-kind no-mutation assertions and real Client/Edge listener-start assertions.
- Fresh reviewer commands passed the focused and full Control Plane suites, `go vet`, `git diff --check`, reviewer-probe guard, and credential leak guard. Production correctness is accepted; test evidence is incomplete. `review_rework_count=2`; `evidence_integrity_failure=true`.
- `plan_cloud_G08_1.log` and `code_review_cloud_G09_1.log` remain the earlier production-boundary failure evidence. `plan_cloud_G08_0.log` and `code_review_cloud_G09_0.log` remain the unimplemented scaffold.
- The exact predecessor completions for indices 01, 02, and 05 remain satisfied.

## Analysis

### Files Read

- `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`
- `agent-roadmap/phase/operational-observability-provider-management/milestones/principal-provider-credential-slot-routing.md`
- `agent-roadmap/sdd/operational-observability-provider-management/principal-provider-credential-slot-routing/SDD.md`
- `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/PLAN-local-G06.md`
- `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/CODE_REVIEW-cloud-G06.md`
- `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/plan_cloud_G08_1.log`
- `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/code_review_cloud_G09_1.log`
- `agent-task/archive/2026/08/m-principal-provider-credential-slot-routing/01_principal_store/complete.log`
- `agent-task/archive/2026/08/m-principal-provider-credential-slot-routing/02+01_credential_catalog/complete.log`
- `agent-task/archive/2026/08/m-principal-provider-credential-slot-routing/05+01,02_management_core/complete.log`
- `apps/control-plane/internal/credentialops/service.go`
- `apps/control-plane/internal/credentialops/service_test.go`
- `apps/control-plane/cmd/control-plane/server.go`
- `apps/control-plane/cmd/control-plane/credential_store_test.go`
- `apps/control-plane/internal/wire/client.go`
- `apps/control-plane/internal/wire/edge_server.go`
- `agent-test/local/rules.md`
- `agent-test/local/control-plane-smoke.md`

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/principal-provider-credential-slot-routing/SDD.md`; status `[승인됨]`, lock released, no user review.
- First-line contribution scope remains `milestone-task=secret-at-rest`.
- Targeted acceptance remains S09: ciphertext must open with the correct durable key revision and exact principal/slot/kind context across rotation and restart.
- Evidence Map row S09-S13 requires encrypted-store inspection and a secret scan. It makes rejected-input store immutability and fail-closed process composition part of trustworthy `secret-at-rest` evidence, which drives the two exact assertion repairs and the fresh package/full Control Plane verification below.

### Verification Context

- No separate verification handoff was supplied. Repository-native sources were `agent-test/local/rules.md`, `agent-test/local/control-plane-smoke.md`, the source/tests above, and the current review evidence.
- Local preflight resolved `/config/.local/bin/go`, Go `1.26.2 linux/arm64`, and `GOROOT=/config/opt/go`. Fresh commands use `GOCACHE=/tmp/gocache-secret-material-evidence`, `GOTMPDIR=/config/tmp`, and `-count=1`.
- Fresh reviewer execution passed both focused suites, the three credential packages, `./apps/control-plane/...`, `go vet`, `git diff --check`, reviewer-probe absence, and the guarded credential leak scan.
- No external runner, credential, network service, or user-controlled authorization is required. This packet changes test assertions only, so a new compiled/full-cycle process run is not required; the focused `run` regression invokes the actual composition and listener-start path with temporary addresses.
- Remaining gaps are deterministic assertion gaps in two existing tests. Confidence: high.

### Test Coverage Gaps

- `TestServiceCreateCanonicalizesCredentialKindForAAD/unknown-kind-rejected-before-sealer` calls `ListSlots` and `ProjectionGeneration` only after rejection, then discards both values. It does not compare exact before/after slot state, revision-row count, or projection generation.
- `TestRunWithEncryptionAndNoDatabaseDoesNotStartListener` checks `client wire endpoint listening` and `edge wire endpoint listening`; production emits `starting client wire WS server` and `starting edge wire TCP server`, so the current test cannot detect those listener-start regressions.
- Canonical accepted-kind variants, real-keyring initial/rotated/restart opening, database requirement, omitted/database-only compatibility, HTTP listener boundary, package regressions, vet, and leak guards are otherwise covered and should not be rewritten.

### Symbol References

- No symbol is renamed or removed.
- The exact Client and Edge startup messages are emitted by `ClientServer.Start` and `EdgeServer.Start`; the follow-up test must match those production strings.

### Split Judgment

- Keep one compact test-only follow-up in the existing subtask. Both assertions close the same reviewed `secret-at-rest` evidence packet, and each change is too small to justify another split state.
- Directory dependencies remain satisfied by `01_principal_store`, `02+01_credential_catalog`, and `05+01,02_management_core` at the exact completion paths listed above.

### Scope Rationale

- Do not change `service.go`, `server.go`, crypto/store behavior, config, schema, listeners, wire contracts, roadmap, or living spec; review accepted the production fixes.
- Do not add a new reviewer probe or timing-based external smoke. Use exact state comparisons and actual observer log messages in the existing deterministic tests.
- Preserve unrelated working-tree changes and later sibling task artifacts.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalize-task-policy.sh pair` executed once after the packet was complete.
- Build closures are all true. Scores `1,1,0,1,1` produce `G04`; base basis `local-fit`. `large_indivisible_context=false`; positive risk `boundary_contract` gives `loop_risk_count=1` and no risk boundary.
- `review_rework_count=2` and `evidence_integrity_failure=true` trigger `recovery-boundary`, producing `PLAN-cloud-G04.md`.
- Review closures are all true. Scores `1,1,0,1,1` produce `G04`; route `official-review`, `CODE_REVIEW-cloud-G04.md` using Codex `gpt-5.6-sol` with `xhigh` reasoning.
- No capability gap exists.

## Implementation Checklist

- [ ] Add exact pre/post slot, revision-row, and projection-generation assertions to the unknown credential-kind regression while preserving the zero-sealer-call assertion.
- [ ] Match the actual Client/Edge startup log messages in the encryption-without-database run regression alongside the existing HTTP listener assertion.
- [ ] Run fresh focused and full Control Plane verification, vet, structural/diff/leak guards, and confirm no reviewer probe remains.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_REVIEW_API-1] Prove rejected credential kinds leave durable state unchanged

**Problem:** `apps/control-plane/internal/credentialops/service_test.go:2006-2016` reads post-call slots and projection generation, then assigns both to `_`; it has no baseline, revision-row assertion, or equality check, so the required no-mutation claim is not tested.

**Solution:** Record slots, `credential_slot_revisions` count, and projection generation before the rejected call. After asserting `ErrInvalidCredentialKind` and zero sealer calls, read the same state and compare exact slot snapshots, exact revision counts, and unchanged generation.

Before (`apps/control-plane/internal/credentialops/service_test.go:2006`):

```go
slots, err := storeFake.ListSlots(ctx, issuedFake.Principal.ID)
// ...
gen, err := storeFake.ProjectionGeneration(ctx)
// ...
_ = slots
_ = gen
```

After:

```go
slotsBefore, revisionsBefore, genBefore := credentialState(t, storeFake, issuedFake.Principal.ID)
// rejected CreateSlot and zero sealer calls
slotsAfter, revisionsAfter, genAfter := credentialState(t, storeFake, issuedFake.Principal.ID)
if !reflect.DeepEqual(slotsAfter, slotsBefore) || revisionsAfter != revisionsBefore || genAfter != genBefore {
	t.Fatal("unknown credential kind mutated durable state")
}
```

**Modified Files and Checklist:**

- [ ] Update `apps/control-plane/internal/credentialops/service_test.go` inside the named subtest; reuse existing imports/helpers where practical.
- [ ] Require an empty exact slot snapshot, zero revision rows for this fixture, and identical projection generation before/after rejection.
- [ ] Keep `ErrInvalidCredentialKind` and `fake.callCount() == 0` assertions.

**Test Strategy:** Strengthen the existing named regression using its in-memory SQLite store. No production changes or new test file are needed.

**Verification:** `GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go test -count=1 ./apps/control-plane/internal/credentialops -run 'TestServiceCreateCanonicalizesCredentialKindForAAD|TestServiceSecretHandlingFailureIsBlindAndDoesNotMutate'` exits 0.

### [REVIEW_REVIEW_API-2] Observe the real Client and Edge listener-start boundaries

**Problem:** `apps/control-plane/cmd/control-plane/credential_store_test.go:544-554` looks for Client/Edge `... endpoint listening` messages, while the actual `Start` methods log `starting client wire WS server` and `starting edge wire TCP server`. Those assertions stay green if either network listener starts.

**Solution:** Replace the nonexistent Client/Edge message fragments with the exact production startup messages. Keep the HTTP listener assertion and immediate expected composition error so the test covers all three network listener paths reached by `run`.

Before (`apps/control-plane/cmd/control-plane/credential_store_test.go:544`):

```go
if strings.Contains(entry.Message, "client wire endpoint listening") { /* fail */ }
if strings.Contains(entry.Message, "edge wire endpoint listening") { /* fail */ }
```

After:

```go
if entry.Message == "starting client wire WS server" { /* fail */ }
if entry.Message == "starting edge wire TCP server" { /* fail */ }
```

**Modified Files and Checklist:**

- [ ] Update `apps/control-plane/cmd/control-plane/credential_store_test.go` to match the exact Client/Edge `Start` log messages.
- [ ] Preserve the exact database-required error and HTTP listener absence assertions.
- [ ] Remove the stale nonexistent listener-message strings from the scoped test files.

**Test Strategy:** Strengthen the existing observer-based `run` regression. It uses a valid temporary 0600 key manifest, empty database URL, and temporary listener addresses; no external service is needed.

**Verification:** `GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go test -count=1 ./apps/control-plane/cmd/control-plane -run 'TestComposeCredentialRuntimeEncryptionRequiresDatabase|TestRunWithEncryptionAndNoDatabaseDoesNotStartListener|TestRunAllowsUnconfiguredDatabase|TestComposeCredentialRuntimeSecretDisabledMode'` exits 0.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/control-plane/internal/credentialops/service_test.go` | REVIEW_REVIEW_API-1 |
| `apps/control-plane/cmd/control-plane/credential_store_test.go` | REVIEW_REVIEW_API-2 |
| `agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material/CODE_REVIEW-cloud-G04.md` | all |

## Final Verification

Run from `/config/workspace/iop-s0`; cached test output is not accepted.

```bash
mkdir -p /config/tmp /tmp/gocache-secret-material-evidence
command -v go && go version && go env GOROOT
git status --short --branch
GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go test -count=1 ./apps/control-plane/internal/credentialops -run 'TestServiceCreateCanonicalizesCredentialKindForAAD|TestServiceSecretHandlingFailureIsBlindAndDoesNotMutate'
GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go test -count=1 ./apps/control-plane/cmd/control-plane -run 'TestComposeCredentialRuntimeEncryptionRequiresDatabase|TestRunWithEncryptionAndNoDatabaseDoesNotStartListener|TestRunAllowsUnconfiguredDatabase|TestComposeCredentialRuntimeSecretDisabledMode'
GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go test -count=1 ./apps/control-plane/internal/credentialseal ./apps/control-plane/internal/credentialstore ./apps/control-plane/internal/credentialops
GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go test -count=1 ./apps/control-plane/...
GOCACHE=/tmp/gocache-secret-material-evidence GOTMPDIR=/config/tmp go vet ./apps/control-plane/...
if rg --sort path -n '_ = (slots|gen)|client wire endpoint listening|edge wire endpoint listening' apps/control-plane/internal/credentialops/service_test.go apps/control-plane/cmd/control-plane/credential_store_test.go; then exit 1; fi
git diff --check
test ! -e apps/control-plane/internal/credentialops/reviewer_probe_test.go
credential_leak_pattern='BEGIN'' AGE PRIVATE KEY|active_key_value[[:space:]]*:[[:space:]]*[^<$]'
if rg --sort path -n --hidden --glob '!agent-task/archive/**' --glob '!*.sops.yaml' --glob '!PLAN-*.md' --glob '!*.log' "$credential_leak_pattern" apps/control-plane configs agent-task/m-principal-provider-credential-slot-routing/07+01,02,05_secret_material; then exit 1; fi
```

Expected: every command exits 0; rejected unknown kinds leave exact durable state unchanged; no HTTP, Client wire, or Edge wire startup message appears before the database error; guarded scans emit no match; no secret or key material appears in output.

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