<!-- task=m-principal-provider-credential-slot-routing/10+08,09_verification_contracts plan=1 tag=TEST milestone-task=slot-smoke,contract-ops -->

# Credential slot qualification과 계약·운영 동기화 계획

## For the Implementing Agent

`CODE_REVIEW-*-G??.md` 구현 담당 섹션을 반드시 채운다. deterministic verification과, 승인된 경우 단 한 번의 live command의 actual sanitized stdout/stderr/exit status를 기록한다. 외부 권한/credential/model이 없으면 대체·재시도하지 말고 exact blocker와 resume command를 남긴다. 구현자는 사용자 질문, next-state 분류, archive, `complete.log`를 수행하지 않는다.

## Background

08/09 구현 뒤에도 credential-free full-cycle fixture, 대표 slot one-shot evidence, canonical wire/public contract와 redaction/revocation runbook이 없으면 milestone을 닫을 수 없다. 이 작업은 deterministic CP-Edge-Node smoke를 상시화하고, one-shot provider qualification을 명시적 opt-in·무재시도로 분리한 뒤 contract/spec/index를 실제 구현과 동기화한다.

## Replan Evidence

- `plan_local_G07_0.log`와 `code_review_cloud_G07_0.log`는 최초 계획/미구현 stub이다.
- 최초 계획은 repository test rule의 OpenAI milestone qualification (`gpt-5.6-luna`, `reasoning_effort=high`) 대신 connectivity 후보 `gpt-5-mini`를 사용했고, secret-safe stderr를 기록하지 말라고 해 actual stdout/stderr evidence 규칙과 충돌했다.
- index 갱신을 optional로 남긴 점과 expected-no-match `rg` exit 처리도 수정했다.

## Archive Evidence Snapshot

- `agent-task/m-principal-provider-credential-slot-routing/08+03,04,07_secure_delivery/complete.log`: 현재 missing.
- `agent-task/m-principal-provider-credential-slot-routing/09+08_migration_attribution/complete.log`: 현재 missing.
- 두 predecessor PASS 뒤에만 S17의 구현 완료 전제가 충족된다.
- prior unimplemented pair: `plan_local_G07_0.log`, `code_review_cloud_G07_0.log`.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/principal-provider-credential-slot-routing.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/operational-observability-provider-management/milestones/principal-provider-credential-slot-routing.md)
- Task ids:
  - `slot-smoke`: Seulgi/vendor fixture와 대표 slot one-shot qualification
  - `contract-ops`: wire/public contract, spec, redaction/revocation 운영 절차 동기화
- Completion mode: check-on-pass

## Analysis

### Files Read

- `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-contract/index.md`
- `agent-contract/inner/client-control-plane-wire.md`
- `agent-contract/inner/control-plane-edge-wire.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-contract/outer/anthropic-compatible-api.md`
- `agent-spec/index.md`
- `agent-spec/control/control-plane-operations.md`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`
- `agent-spec/input/openai-compatible-surface.md`
- `scripts/e2e-provider-capacity-smoke.sh`
- `scripts/e2e-control-plane-edge-wire.sh`
- `scripts/e2e-openai-ollama.sh`
- `packages/go/config/protocol_profile.go`
- `packages/go/config/protocol_profile_test.go`
- `configs/edge.yaml`
- `docs/edge-local-dev-guide.md`
- `docs/openai-usage-grafana.md`
- `Makefile`
- `agent-test/local/rules.md`
- `agent-test/local/control-plane-smoke.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/node-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/testing-smoke.md`

### SDD Criteria

- SDD path: `agent-roadmap/sdd/operational-observability-provider-management/principal-provider-credential-slot-routing/SDD.md`; status: 승인.
- S16: deterministic Seulgi/vendor/model/slot/profile fixture passes without subscription; one provided representative credential slot runs Chat or Messages once and records only provider/model/safe slot ref/date/revision/result.
- S17: implementation-complete contracts/spec/index/runbook match secure wire, auth, lease, migration, attribution and redaction behavior; docs/test evidence contain no raw secret.
- Evidence Map S16-S17 rows가 deterministic/live evidence schema, pointer/drift check와 credential leak scan을 checklist/final verification에 고정한다.

### Verification Context

- 별도 handoff는 없었다. repository-native fallback evidence는 위 scripts/contracts/specs와 local domain/testing profiles다.
- local tools currently include Go, bash, jq, openssl, sops, curl, protoc, protoc-gen-dart, flutter, netstat/nc. `shellcheck` is absent, so `bash -n` is mandatory and absence is recorded, not silently substituted.
- deterministic mode uses random loopback ports and `mktemp -d`, builds fresh binaries, generates CA/issuer/recipient keys and removes them with trap. It never opens external network.
- host SOPS paths are probed only for readability; values are not decrypted during planning. Live execution requires explicit authorization and `IOP_ALLOW_LIVE_PROVIDER=1`.
- repository rule’s current milestone OpenAI qualification is `gpt-5.6-luna` with `reasoning_effort=high`; `gpt-5-mini` is only connectivity preflight and cannot satisfy S16.
- no automatic retry, fallback model/provider or larger output cap. confidence: deterministic high, external availability medium.
- External Verification Preflight: runner=current host, workdir `/config/workspace/iop-s0`, branch `agent/dispatcher-work-log-artifact-loop`, HEAD `7da9d3884dbe2f32a9dc9aacbc50e236c15562e5`, worktree dirty and preserved; fresh binaries/certs/config/DB are built under a task-owned `mktemp -d`; random loopback ports are selected after listener checks; the only external host is the selected provider endpoint after explicit authorization. Source mismatch, missing tool/config/key, occupied runtime identity or unavailable model is a recorded blocker, never an implicit sync/download/fallback.

### Test Coverage Gaps

- existing smokes do not run create→projection→signed/sealed lease→Node injection→attribution→revoke.
- Seulgi profiles have unit coverage but no two-slot full-cycle fixture.
- live guidance calls provider directly rather than through the Control Plane slot path.
- canonical docs currently describe dormant/plain projection and caller provider-auth forwarding.
- no deterministic index pointer/drift check or task-scoped credential leak guard exists.

### Symbol References

- add Make target `test-credential-slot-smoke` and one script/fixture; no production symbol rename.
- contract/spec ids and canonical paths remain stable; index rows are updated in place, never duplicated.

### Split Judgment

- this packet is qualification/evidence, not production workaround. Deterministic/live evidence schema and contract redaction rules are coupled by S16/S17.
- 08 and 09 exact `complete.log` paths are missing and block implementation.

### Scope Rationale

- production Go defects found by the smoke are not patched around in shell/docs; review must request implementation rework.
- live attempt is exactly one short non-stream request, output cap 128, no tool call/body retention/retry. Actual response body and prompt are discarded.
- tracked docs contain placeholders only; private endpoints/key paths contents/alias/lease id/ciphertext/raw response are excluded from evidence.

### Final Routing

- evaluation_mode `isolated-reassessment`; finalizer `finalize-task-policy.sh pair` exactly once.
- build closures 모두 true; scores `2,0,1,2,2` = G07; route `local-fit`; `large_indivisible_context=false`; positive risks `boundary_contract`, `variant_product` (2); `PLAN-local-G07.md`.
- review scores `2,0,1,2,2` = G07; `official-review`, `CODE_REVIEW-cloud-G07.md`, Codex `gpt-5.6-sol` xhigh.
- `review_rework_count=0`, `evidence_integrity_failure=false`; recovery boundary는 없다. live availability는 external evidence gate이며 planning capability gap은 아니다.

## Implementation Checklist

- [ ] Add a credential-free deterministic Seulgi/vendor/two-slot fixture and full TLS/projection/lease/injection/attribution/revoke smoke.
- [ ] Add a separately gated, stdin-only, no-retry representative live one-shot using the repository milestone qualification profile.
- [ ] Synchronize canonical contracts, specs, indexes and public redaction/revocation operations to actual 08/09 behavior.
- [ ] Run fresh deterministic smoke, all affected packages, pointer/drift/leak checks, and capture either authorized live evidence or exact external blocker.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [TEST-1] Deterministic full-cycle credential slot smoke

**Problem:** current smokes exercise isolated wire/capacity paths but not the complete credential slot lifecycle.

**Solution:** Add a script that generates test CA/peer identities, CP lease issuer and Node recipient keys, at-rest manifest, SQLite DB, random ports and a fake upstream. Bootstrap one principal locally, create two same-model slots/routes through the authenticated HTTPS management operation, start credential-plane CP/Edge/Node, call Seulgi Chat and Messages fixtures, and assert exact upstream auth plus safe slot/revision metrics. Revoke the selected slot and prove the next request reaches neither lease decrypt nor upstream. Cleanup must run on signal/failure.

Before (`Makefile:91-92`):

```make
test:
	go test ./...
```

After:

```make
test-credential-slot-smoke:
	./scripts/e2e-credential-slot-smoke.sh
```

**Modified Files and Checklist:**

- [ ] Add `scripts/fixtures/credential-slot-vendors.json` with secret-free OpenAI, Anthropic, Seulgi Chat/Messages profiles and two-slot routes.
- [ ] Add `scripts/e2e-credential-slot-smoke.sh` with strict mode, random ports, fresh builds, deterministic fake provider, process readiness/death checks and cleanup.
- [ ] Add `Makefile` target `test-credential-slot-smoke` without placing it in unrelated default unit targets.

**Test Strategy:** prove profile auth variants, exact route→slot, safe attribution, ciphertext-only DB, plaintext/no-cert/wrong-peer failure, revoke fence, no fallback and sentinel absence from all captured files/output.

**Verification:** syntax/fixture/self-test and `make test-credential-slot-smoke`.

### [TEST-2] Explicit representative live one-shot

**Problem:** S16 requires an actual representative slot path, while direct-provider connectivity does not qualify the implemented IOP route.

**Solution:** Add `--live --secret-stdin` behind `IOP_ALLOW_LIVE_PROVIDER=1`. Consume one secret from stdin into process-local memory, create one temporary CP slot, send one short non-stream request through the full TLS stack with retries disabled, validate structural success, discard provider body and print a single JSON object: `provider`, `model`, `credential_slot_ref`, `credential_revision`, UTC `date`, `result`. The script sanitizes both stdout and stderr and self-tests them with a sentinel.

Before (`scripts/e2e-provider-capacity-smoke.sh:1-7`):

```bash
#!/usr/bin/env bash
set -euo pipefail

# Deterministic local provider-pool capacity smoke.
```

After (`scripts/e2e-credential-slot-smoke.sh`):

```bash
#!/usr/bin/env bash
set -euo pipefail
# --live requires IOP_ALLOW_LIVE_PROVIDER=1 and --secret-stdin; retries remain zero.
```

Repository-default qualification command:

```bash
SOPS_AGE_KEY_FILE=/config/.config/sops/age/keys.txt \
  sops --decrypt --input-type yaml --output-type binary /config/.config/sops/iop/openai.sops.yaml \
  | IOP_ALLOW_LIVE_PROVIDER=1 ./scripts/e2e-credential-slot-smoke.sh \
      --live --secret-stdin --profile openai --model gpt-5.6-luna \
      --reasoning-effort high --max-completion-tokens 128
```

**Modified Files and Checklist:**

- [ ] Implement live gate, stdin-only handling, one-attempt timeout/cost cap, sanitized stdout/stderr and cleanup in the new script.
- [ ] Implement `--self-test-live-gate` with loopback fake provider and sentinel, asserting zero external call and no retained/plain output.
- [ ] Record the exact command, sanitized actual stdout and stderr, and exit status in review. If authorization/source/model is unavailable, record the blocker and exact resume command; do not claim `slot-smoke` PASS without the one-shot evidence.

**Test Strategy:** gate self-test must prove missing opt-in fails before reading stdin/network; authorized real command runs once only. `finish_reason=stop` and nonempty structural content are required; `length`, empty body or unavailable model is FAIL evidence, not a retry trigger.

**Verification:** `./scripts/e2e-credential-slot-smoke.sh --self-test-live-gate`, then the exact live pipeline once only when authorized.

### [TEST-3] Canonical contract/spec/runbook synchronization

**Problem:** canonical docs describe pre-08/09 behavior and indexes may point to stale summaries.

**Solution:** Update existing documents in place with Client WSS/principal-authenticated slot writes and bootstrap exclusion, CP-Edge/Edge-Node mTLS identities, signed Node-sealed lease scope/expiry/revision, managed/legacy source selection, public schema compatibility, safe attribution, and create/rotate/revoke/redaction procedures. Derive every claim from source/tests after 08/09; do not document planned-but-absent behavior.

Before (`agent-contract/inner/client-control-plane-wire.md:23-30`):

```text
현재 MVP는 hello baseline이며, Client는 Edge나 Node TCP/protobuf transport에 직접 연결하지 않는다.
Client는 /client WebSocket으로 Control Plane에 연결한다.
```

After:

```text
Client protobuf wire는 hello-only를 유지하고, post-bootstrap slot write는 server-authenticated HTTPS와 principal auth로 수행한다.
최초 principal/bootstrap은 계속 host-local CLI 전용이며 remote wire에 노출하지 않는다.
```

**Modified Files and Checklist:**

- [ ] Update `agent-contract/inner/client-control-plane-wire.md`, `agent-contract/inner/control-plane-edge-wire.md`, `agent-contract/inner/edge-node-runtime-wire.md`, and `agent-contract/inner/edge-config-runtime-refresh.md`.
- [ ] Update `agent-contract/outer/openai-compatible-api.md` and `agent-contract/outer/anthropic-compatible-api.md`.
- [ ] Update `agent-contract/index.md` summaries/status/read conditions in place.
- [ ] Update `agent-spec/control/control-plane-operations.md`, `agent-spec/runtime/edge-node-execution.md`, `agent-spec/runtime/provider-pool-config-refresh.md`, `agent-spec/input/openai-compatible-surface.md`, and `agent-spec/index.md` in place.
- [ ] Update `docs/edge-local-dev-guide.md` and `docs/openai-usage-grafana.md` with external mounts, TLS startup, safe slot management, revoke/rotate/lease-expiry confirmation and incident redaction checklist.

**Test Strategy:** parse both indexes for every `.md` canonical pointer, search docs against actual message/config symbols, and scan scoped tracked source/docs/task artifacts for private keys/API-token shapes.

**Verification:** pointer/drift/leak commands in Final Verification.

## Dependencies and Execution Order

1. Do not start until both 08 and 09 exact `complete.log` paths exist.
2. Implement TEST-1, TEST-2 self-test, then TEST-3 from verified behavior.
3. Run all deterministic verification before the single authorized live attempt. Never auto-retry.

## Modified Files Summary

| File | Items |
|------|-------|
| `scripts/fixtures/credential-slot-vendors.json` | TEST-1 |
| `scripts/e2e-credential-slot-smoke.sh` | TEST-1, TEST-2 |
| `Makefile` | TEST-1 |
| `agent-contract/inner/client-control-plane-wire.md` | TEST-3 |
| `agent-contract/inner/control-plane-edge-wire.md` | TEST-3 |
| `agent-contract/inner/edge-node-runtime-wire.md` | TEST-3 |
| `agent-contract/inner/edge-config-runtime-refresh.md` | TEST-3 |
| `agent-contract/outer/openai-compatible-api.md` | TEST-3 |
| `agent-contract/outer/anthropic-compatible-api.md` | TEST-3 |
| `agent-contract/index.md` | TEST-3 |
| `agent-spec/control/control-plane-operations.md` | TEST-3 |
| `agent-spec/runtime/edge-node-execution.md` | TEST-3 |
| `agent-spec/runtime/provider-pool-config-refresh.md` | TEST-3 |
| `agent-spec/input/openai-compatible-surface.md` | TEST-3 |
| `agent-spec/index.md` | TEST-3 |
| `docs/edge-local-dev-guide.md` | TEST-3 |
| `docs/openai-usage-grafana.md` | TEST-3 |
| `agent-task/m-principal-provider-credential-slot-routing/10+08,09_verification_contracts/CODE_REVIEW-cloud-G07.md` | all |

## Final Verification

Run deterministic commands from `/config/workspace/iop-s0`; live pipeline only once after explicit authorization.

```bash
command -v go && go version && go env GOROOT
command -v bash && command -v jq && command -v openssl && openssl version
command -v sops && command -v curl && command -v netstat
git status --short --branch
bash -n scripts/e2e-credential-slot-smoke.sh
jq -e '.profiles | length >= 4 and any(.[]; .id == "seulgi_chat") and any(.[]; .id == "seulgi_messages")' scripts/fixtures/credential-slot-vendors.json >/dev/null
./scripts/e2e-credential-slot-smoke.sh --self-test-live-gate
make test-credential-slot-smoke
go test -count=1 ./apps/control-plane/... ./apps/edge/... ./apps/node/... ./packages/go/...
go vet ./apps/control-plane/... ./apps/edge/... ./apps/node/... ./packages/go/...
git diff --check
python3 - <<'PY'
import re
from pathlib import Path
for index in (Path('agent-contract/index.md'), Path('agent-spec/index.md')):
    data = index.read_text()
    targets = set(re.findall(r'`((?:agent-contract|agent-spec)/[^`\s]+\.md)`', data))
    targets.update(re.findall(r'\(((?:agent-contract|agent-spec)/[^)\s]+\.md)\)', data))
    for target in sorted(targets):
        if not Path(target.split('#', 1)[0]).is_file():
            raise SystemExit(f'broken pointer: {index}: {target}')
PY
if rg --sort path -n --hidden --glob '!agent-task/archive/**' --glob '!*.sops.yaml' --glob '!go.sum' --glob '!PLAN-*.md' --glob '!*.log' 'BEGIN (AGE|OPENSSH|RSA|EC) PRIVATE KEY|sk-[A-Za-z0-9_-]{20,}' agent-contract agent-spec docs scripts agent-task/m-principal-provider-credential-slot-routing/10+08,09_verification_contracts; then exit 1; fi
```

Expected: all deterministic commands pass and guarded scan has no match. A `slot-smoke` PASS additionally requires one sanitized live JSON record from the exact one-shot command; absence is an external-execution blocker, not a substituted pass.

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