<!-- task=m-openai-compatible-output-validation-filters/02+01_repeat_guard plan=0 tag=OFR-REPEAT -->

# Output Filter Recovery: caller-neutral repeat guard

## 이 파일을 읽는 구현 에이전트에게

구현 전에 선행 task의 `complete.log`를 확인하고, 완료 전 `CODE_REVIEW-cloud-G10.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 반드시 채운다. 검증 명령을 실행하고 active 파일을 유지한 채 review-ready 상태를 보고한다. 종결, archive, `complete.log` 작성은 code-review skill 전용이다. 막히면 정확한 blocker, 시도한 명령/출력, 재개 조건만 구현 evidence에 기록하며 사용자 질문, user-input 도구, control-plane stop 파일 생성, 다음 상태 분류를 하지 않는다.

## 배경

현재 `repeat_guard`는 500-rune rolling hold shape만 등록하고 모든 batch를 pass한다. 이 작업은 raw OpenAI-compatible request history와 current stream만 사용해 caller-neutral 반복을 판정하고, 안전한 content continuation 또는 side-effect-safe terminal로 수렴시키며 S03/S04/S09-S12의 결정론적 evidence를 고정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-output-validation-filters.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-output-validation-filters.md)
- Task ids:
  - `repeat-guard`: rolling content/history/action 반복 감지와 safe continuation
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- 규칙/테스트: `agent-ops/rules/project/rules.md`, `agent-ops/rules/common/rules-roadmap.md`, `agent-ops/skills/common/router.md`, `agent-ops/skills/common/plan/SKILL.md`, `agent-ops/skills/common/finalize-task-routing/SKILL.md`, `agent-ops/rules/project/domains/edge.md`, `agent-ops/rules/project/domains/platform-common.md`, `agent-ops/rules/project/domains/testing.md`, `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/testing-smoke.md`, `agent-test/dev/rules.md`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/platform-common-smoke.md`, `agent-test/dev/testing-smoke.md`
- 로드맵/설계/계약: `agent-roadmap/ROADMAP.md`, `agent-roadmap/current.md`, `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`, `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-output-validation-filters.md`, `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/SDD.md`, `agent-contract/index.md`, `agent-contract/outer/openai-compatible-api.md`, `agent-contract/inner/edge-config-runtime-refresh.md`, `agent-spec/index.md`, `agent-spec/runtime/stream-evidence-gate.md`, `agent-spec/input/openai-compatible-surface.md`, `agent-spec/runtime/provider-pool-config-refresh.md`
- 소스: `apps/edge/internal/openai/stream_gate_filters.go`, `apps/edge/internal/openai/stream_gate_policy.go`, `apps/edge/internal/openai/openai_request_rebuilder.go`, `apps/edge/internal/openai/stream_gate_runtime.go`, `apps/edge/internal/openai/stream_gate_tunnel_codec.go`, `apps/edge/internal/openai/chat_decode.go`, `apps/edge/internal/openai/responses_decode.go`, `apps/edge/internal/openai/chat_types.go`, `apps/edge/internal/openai/responses_types.go`, `packages/go/config/edge_types.go`, `packages/go/streamgate/filter_contract.go`, `packages/go/streamgate/event.go`, `configs/edge.yaml`, `go.mod`
- 테스트: `apps/edge/internal/openai/stream_gate_filters_test.go`, `apps/edge/internal/openai/stream_gate_policy_test.go`, `apps/edge/internal/openai/stream_gate_pipeline_test.go`, `apps/edge/internal/openai/stream_gate_vertical_slice_test.go`, `packages/go/config/stream_evidence_gate_config_test.go`, `packages/go/streamgate/event_test.go`

### SDD 기준

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/openai-compatible-output-validation-filters/SDD.md`, `[승인됨]`, 잠금 해제.
- 대상: S03, S04, S09, S10, S11, S12 → `repeat-guard`.
- Evidence Map은 Korean multi-byte 6문단 repeat, 200/500-rune pending/look-behind, stream-open cursor/single `[DONE]`, tool side-effect 차단, reasoning alias/history 미전송, completed progress/no-progress, 온도 명시/생략 fixture를 요구한다. 각 항목을 구현 체크리스트와 fresh/local+dev 검증에 직접 연결한다.

### 테스트 환경 규칙

- `test_env=local + dev`. local fresh test는 `-count=1`; `git diff --check`, edge/platform-common package test, `make test`를 적용한다.
- dev는 roadmap의 live acceptance 때문에 필수다. runner workdir=`/Users/toki/agent-work/iop-dev`, clean `origin/main` sync, 같은 source/build identity로 참여 환경 전체 rebuild/redeploy/restart가 선행되어야 한다.
- dev preflight: branch/HEAD/dirty, `git rev-parse`, `git status --short`, source sync; `go version`, `go env GOMOD`; `iop-edge --help`, `iop-edge version`; config=`build/dev-runtime/edge.yaml`; Edge identity/port `127.0.0.1:18083`; provider host OS/arch와 `ornith:35b` route/capacity=4 확인. credential은 remote environment에서만 읽고 stdout/stderr에 출력하지 않는다.
- mismatch가 있으면 `git fetch origin && git checkout main && git pull --ff-only origin main` 뒤 전체 build/deploy/restart를 수행하고 smoke를 재시작한다. dirty/divergent checkout에서는 live evidence를 만들지 않는다.

### 테스트 커버리지 공백

- 기존 filter test는 registration/hold/pass foundation만 검증한다: Unicode rolling detector, committed look-behind, typed continuation intent가 없다.
- 기존 policy test는 selector/capability만 검증한다: raw role/channel history, reasoning alias, progress/no-progress preflight가 없다.
- vertical slice는 diagnostic filter recovery만 검증한다: repeated content의 safe prefix/cursor, duplicate opening/prefix, single `[DONE]`, tool side-effect boundary가 없다.
- live `ornith:35b` capacity+1 × 3 evidence harness가 없다. deterministic fixture와 분리된 env-gated dev smoke를 추가해야 한다.

### 심볼 참조

- rename/remove 없음.
- 변경 호출점: `newOpenAIOutputFilter`는 `stream_gate_policy.go:94`에서 생성된다. `openAIOutputFilterContext`는 Chat/Responses registry 구성 호출점 전체에 전달되므로 raw history semantic view 추가 시 모든 struct literal을 compile-time로 갱신한다.

### 분할 판단

- 이 패킷의 안정 계약은 “request 밖 state 없이 history/current-stream 반복을 판정하고, pure filter intent + Core/Rebuilder 경계로 safe prefix continuation 또는 no-repair terminal을 만든다”이다.
- producer `01_resume_notice_builder`가 recovery source/Rebuilder contract를 독립 PASS한 뒤 시작한다. 현재 `agent-task/m-openai-compatible-output-validation-filters/01_resume_notice_builder/complete.log`가 없으므로 predecessor는 `missing`; runtime은 완료 전 이 task를 실행하지 않는다.
- 후속 provider/schema/ops 작업은 동일 filter/runtime 파일을 만지므로 충돌 방지를 위해 직렬화한다.

### 범위 결정 근거

- cross-request TTL/session lineage와 Pi session parsing은 명시적으로 제외한다.
- assistant final content, tool call, signed/encrypted/unknown reasoning field mutation은 제외한다.
- provider error matcher, schema validator, managed length continuation은 후속 task로 제외한다.
- live smoke raw SSE/output은 ignored `agent-test/runs/**`만 사용하고 tracked plan/spec에 복제하지 않는다.
- 외부 dependency를 추가하지 않는다.

### 최종 라우팅

- evaluation_mode=`first-pass`; finalizer=`finalize-task-policy.sh pair`.
- build closure: scope=2, state=2, blast=2, evidence=2, verification=2 → G10; base/final=`grade-boundary`, lane=`cloud`, filename=`PLAN-cloud-G10.md`.
- review closure: scope=2, state=2, blast=2, evidence=2, verification=2 → G10; route=`official-review`, lane=`cloud`, filename=`CODE_REVIEW-cloud-G10.md`.
- `large_indivisible_context=false`.
- matched loop-risk signatures: `temporal_state`, `concurrent_consistency`, `boundary_contract`, `structured_interpretation`, `variant_product` (5).
- recovery signals: `review_rework_count=0`, `evidence_integrity_failure=false`.
- capability gap: 없음.

## 구현 체크리스트

- [ ] [OFR-REPEAT-1] Chat/Responses raw history를 role/channel/action별 bounded semantic view로 preflight하고 no-lineage/no-unsafe-mutation 규칙을 테스트한다.
- [ ] [OFR-REPEAT-2] Unicode rolling/look-behind repeat 및 action guard를 pure filter decision/typed continuation intent로 구현한다.
- [ ] [OFR-REPEAT-3] resume builder와 연결해 safe prefix/cursor, temperature 후보, duplicate opening/prefix 및 single terminal 경계를 검증한다.
- [ ] [OFR-REPEAT-4] 계약/spec/config 예시를 갱신하고 결정론적 generic fixture와 dev `ornith:35b` capacity+1 × 3 smoke evidence를 완료한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [OFR-REPEAT-1] caller-neutral history preflight

#### 문제

- `stream_gate_policy.go:42-50`의 context에는 endpoint/model/scheme/request ref만 있어 incoming role/channel/action history가 없다.
- typed Chat decode만 사용하면 `reasoning`, `reasoning_text` 같은 provider alias와 unknown/signed field의 mutation 경계를 안정적으로 구분할 수 없다.

#### 해결 방법

ingress canonical raw body를 endpoint별 parser로 한 번 읽어 immutable/bounded semantic view를 만든다. Chat은 `role=user|assistant`, `content`, `reasoning_content`, `reasoning`, `reasoning_text`, completed tool call/result/error를 분리한다. Responses는 item/reasoning/function-call/result provenance를 자체 parser로 읽고 Chat field를 재사용하지 않는다. whitespace/control normalization 뒤 hash만 filter input에 보존한다.

Before (`apps/edge/internal/openai/stream_gate_policy.go:42`):

```go
type openAIOutputFilterContext struct {
    environment string
    endpoint    string
    modelGroup  string
    hasScheme   bool
    requestRef  string
}
```

After:

```go
type openAIOutputFilterContext struct {
    environment string
    endpoint    string
    modelGroup  string
    hasScheme   bool
    requestRef  string
    history     openAIRepeatHistorySnapshot
}
```

user occurrence가 하나라도 있으면 assistant anchor 후보에서 제외한다. reasoning history 미전송은 occurrence=0으로 처리하고, stable lineage/TTL을 추정하지 않는다. sanitation은 plain assistant reasoning 반복 fragment에만 허용한다.

#### 수정 파일 및 체크리스트

- [ ] `apps/edge/internal/openai/chat_decode.go`: raw Chat history role/channel alias parser.
- [ ] `apps/edge/internal/openai/responses_decode.go`: Responses item provenance parser.
- [ ] `apps/edge/internal/openai/stream_gate_policy.go`: immutable history snapshot 전달.
- [ ] `apps/edge/internal/openai/stream_gate_policy_test.go`: aliases, user exclusion, history omitted, no identity/TTL, progress/no-progress.

#### 테스트 작성

- 작성: `TestOpenAIRepeatHistoryChatAliases`, `TestOpenAIRepeatHistoryResponsesProvenance`, `TestOpenAIRepeatHistoryDoesNotInferLineage`, `TestOpenAIRepeatHistoryProgressBoundary`.
- signed/encrypted/unknown reasoning, final content, tool args sentinel이 mutation 대상이 아니고 observation raw 값에도 나타나지 않는지 검증한다.

#### 중간 검증

```bash
go test -count=1 ./apps/edge/internal/openai -run 'TestOpenAIRepeatHistory'
```

기대 결과: PASS, caller 제품/session에 따른 분기 0건.

### [OFR-REPEAT-2] Unicode rolling 및 action filter

#### 문제

- `stream_gate_filters.go:144-179`는 repeat batch에 항상 `repeat_rolling_clear` pass를 반환한다.
- 현재 500-rune hold는 evidence 크기만 정할 뿐 repeated fragment, committed look-behind, action side-effect를 판정하지 않는다.

#### 해결 방법

Unicode rune 기준 rolling inspector를 filter request-local state로 구성하고 `EvidenceBatch.ChannelPending()`과 `CommittedLookBehind()`를 함께 읽는다. current response가 아니라 incoming completed tool/result/error만 progress 근거로 사용한다. content 반복은 safe cursor를 가진 continuation intent, unreleased repeated action은 terminal violation, released tool/side-effect 가능 구간은 no-auto-repair fatal decision을 반환한다.

Before (`apps/edge/internal/openai/stream_gate_filters.go:160`):

```go
descriptor := "repeat_rolling_clear"
...
return streamgate.NewFilterDecision(
    streamgate.FilterDecisionKindPass, openAIOutputFilterConsumerID,
    f.ID(), f.ruleID, evidence, nil,
)
```

After:

```go
match := f.repeatInspector.Evaluate(f.history, batch)
if match.ContentLoop {
    directive, _ := streamgate.NewRecoveryDirectiveContinuation(match.Cursor, match.SnapshotRef)
    intent, _ := streamgate.NewRecoveryIntent(
        streamgate.RecoveryStrategyContinuationRepair, directive,
        "repeat_content_detected", f.priority,
    )
    return repeatRecoveryDecision(match, intent)
}
return repeatPassOrSafeStop(match)
```

fingerprint/count/offset만 sanitized evidence에 넣고 raw content/reasoning/tool payload는 넣지 않는다. idle은 시간 기반 release가 아니라 evidence 미충족 terminal error로 끝낸다.

#### 수정 파일 및 체크리스트

- [ ] `apps/edge/internal/openai/stream_gate_filters.go`: rolling/history/action evaluator, typed intent, sanitized evidence.
- [ ] `apps/edge/internal/openai/stream_gate_filters_test.go`: 200/500 rune, Korean UTF-8 split, look-behind, action/progress/side-effect matrix.
- [ ] `packages/go/config/edge_types.go`: repeat threshold/bound defaults가 필요할 경우 기존 filter policy 안에서 bounded validation.
- [ ] `packages/go/config/stream_evidence_gate_config_test.go`: omitted/default/min/max/invalid config.
- [ ] `configs/edge.yaml`: generic repeat policy 예시와 raw-free 설명.

#### 테스트 작성

- 작성: `TestRepeatGuardKoreanMultibyteRolling`, `TestRepeatGuardCommittedLookBehind`, `TestRepeatGuardActionProgressMatrix`, `TestRepeatGuardIdleDoesNotRelease`.
- fixture는 긴 한국어 문단 6개를 UTF-8 multi-byte boundary로 분할하고 다시 반복하며 200/500 rune 두 threshold를 검증한다.

#### 중간 검증

```bash
go test -count=1 ./packages/go/config ./apps/edge/internal/openai -run 'Test(StreamGate|OpenAI|RepeatGuard).*Repeat'
```

기대 결과: PASS, race/order에 무관한 stable fingerprint/cursor.

### [OFR-REPEAT-3] continuation dispatch와 downstream 단일성

#### 문제

- foundation recovery는 generic patch를 적용하지만 stream-open 뒤 committed safe prefix와 새 attempt opening/prefix 중복을 repeat 의미로 검증하지 않는다.
- caller temperature 생략 여부에 따른 `[0.2, 0.4, 0.6]` 순차 후보 계약이 없다.

#### 해결 방법

OFR-RESUME의 recovery source cursor를 사용해 반복 구간을 제외한 content/reasoning만 rebuild한다. caller가 temperature를 명시하지 않은 경우에만 attempt별 0.2→0.4→0.6을 적용하고 명시값은 보존한다. Core commit state가 tool/side-effect 또는 exact-replay 불가 상태면 continuation을 만들지 않는다. ReleaseSink는 첫 response-start/role과 committed prefix를 유지하고 replacement attempt의 opening/prefix를 억제하며 final `[DONE]`/terminal을 한 번만 보낸다.

Before (`apps/edge/internal/openai/openai_request_rebuilder.go:446`):

```go
case streamgate.RecoveryDirectiveKindContinuation:
    patchEntry, err = r.patches.takeContinuation(
        directive.SnapshotRef(), directive.Cursor(),
    )
```

After:

```go
case streamgate.RecoveryDirectiveKindContinuation:
    source, err = r.recoverySource.Take(
        directive.SnapshotRef(), directive.Cursor(),
    )
    temperature = continuationTemperature(plan.AttemptIndex(), ingressTemperature)
```

#### 수정 파일 및 체크리스트

- [ ] `apps/edge/internal/openai/openai_request_rebuilder.go`: cursor source와 temperature candidate/body patch.
- [ ] `apps/edge/internal/openai/stream_gate_runtime.go`: continuation-safe commit/side-effect eligibility와 one-dispatch binding.
- [ ] `apps/edge/internal/openai/stream_gate_tunnel_codec.go`: replacement opening/role/known prefix와 duplicate terminal 억제.
- [ ] `apps/edge/internal/openai/stream_gate_pipeline_test.go`: temperature/abort/rebuild/dispatch ordering.
- [ ] `apps/edge/internal/openai/stream_gate_vertical_slice_test.go`: same downstream SSE safe prefix, single `[DONE]`, cancel/tool boundary.

#### 테스트 작성

- 작성: `TestRepeatGuardContinuationTemperatureCandidates`, `TestRepeatGuardPreservesCallerTemperature`, `TestRepeatGuardStreamOpenNoDuplicatePrefix`, `TestRepeatGuardToolSideEffectNoRepair`.

#### 중간 검증

```bash
go test -count=1 ./apps/edge/internal/openai -run 'TestRepeatGuard(Continuation|Preserves|StreamOpen|ToolSideEffect)'
```

기대 결과: PASS, recovery cycle당 dispatch 1회, `[DONE]` 1회.

### [OFR-REPEAT-4] 계약 동기화와 local/dev evidence

#### 문제

- `agent-contract/outer/openai-compatible-api.md:94`와 `agent-spec/runtime/stream-evidence-gate.md:99`는 repeat guard를 foundation-only로 기록한다.
- roadmap acceptance는 generic fixture 외에 `ornith:35b` capacity+1 동시 요청 5개를 최소 3회 실행한 live evidence를 요구한다.

#### 해결 방법

구현과 같은 변경에서 caller-neutral history/current-stream 범위, 500-rune default, safe mutation/side-effect 금지, temperature 후보, degraded no-lineage behavior를 계약/spec에 기록한다. dev smoke는 env-gated test harness가 ignored prompt/artifact directory를 읽고 5 concurrent × 3 runs를 수행하며 raw SSE/output은 `agent-test/runs/**`에만 저장한다. tracked review에는 model/provider/attempt/fingerprint/offset/decision과 `reproduced|not_reproduced`만 기록한다.

Before (`agent-spec/runtime/stream-evidence-gate.md:99`):

```text
production Core registry는 ... repeat/schema/provider-error foundation ...
```

After:

```text
repeat_guard는 request-local history/current stream만 사용하고 rolling evidence와 committed look-behind에서 continuation 또는 safe stop을 결정한다.
```

#### 수정 파일 및 체크리스트

- [ ] `agent-contract/outer/openai-compatible-api.md`: repeat guard caller-visible/stream contract.
- [ ] `agent-contract/inner/edge-config-runtime-refresh.md`: repeat policy defaults/bounds/restart snapshot.
- [ ] `agent-spec/runtime/stream-evidence-gate.md`: current repeat filter lifecycle.
- [ ] `agent-spec/input/openai-compatible-surface.md`: raw Chat/Responses history boundary.
- [ ] `agent-spec/runtime/provider-pool-config-refresh.md`: provider capability/config snapshot.
- [ ] `apps/edge/internal/openai/stream_gate_vertical_slice_test.go`: env-gated dev harness 또는 동등한 기존 profile-compatible live entry.

#### 테스트 작성

- deterministic tests는 OFR-REPEAT-1~3에서 필수 작성한다.
- dev harness는 `IOP_OPENAI_SMOKE_PROMPT_FILE`을 ignored path에서 읽고 credential을 출력하지 않으며, 미재현을 test PASS로 위장하지 않고 sanitized `not_reproduced` evidence로 남긴다.

#### 중간 검증

```bash
git diff --check
go test -count=1 ./packages/go/streamgate ./packages/go/config ./apps/edge/internal/openai
```

기대 결과: PASS.

## 의존 관계 및 구현 순서

- runtime predecessor: `01_resume_notice_builder`.
- 구현 시작 조건: `agent-task/m-openai-compatible-output-validation-filters/01_resume_notice_builder/complete.log` 존재. 현재 상태는 `missing`.
- directory `02+01_repeat_guard`의 `+01` 외 추가 runtime dependency는 없다.

구현 순서는 OFR-REPEAT-1 → 2 → 3 → 4다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `apps/edge/internal/openai/chat_decode.go` | OFR-REPEAT-1 |
| `apps/edge/internal/openai/responses_decode.go` | OFR-REPEAT-1 |
| `apps/edge/internal/openai/stream_gate_policy.go` | OFR-REPEAT-1 |
| `apps/edge/internal/openai/stream_gate_filters.go` | OFR-REPEAT-2 |
| `packages/go/config/edge_types.go` | OFR-REPEAT-2 |
| `configs/edge.yaml` | OFR-REPEAT-2 |
| `apps/edge/internal/openai/openai_request_rebuilder.go` | OFR-REPEAT-3 |
| `apps/edge/internal/openai/stream_gate_runtime.go` | OFR-REPEAT-3 |
| `apps/edge/internal/openai/stream_gate_tunnel_codec.go` | OFR-REPEAT-3 |
| `apps/edge/internal/openai/stream_gate_policy_test.go` | OFR-REPEAT-1 |
| `apps/edge/internal/openai/stream_gate_filters_test.go` | OFR-REPEAT-2 |
| `packages/go/config/stream_evidence_gate_config_test.go` | OFR-REPEAT-2 |
| `apps/edge/internal/openai/stream_gate_pipeline_test.go` | OFR-REPEAT-3 |
| `apps/edge/internal/openai/stream_gate_vertical_slice_test.go` | OFR-REPEAT-2/3/4 |
| `agent-contract/outer/openai-compatible-api.md` | OFR-REPEAT-4 |
| `agent-contract/inner/edge-config-runtime-refresh.md` | OFR-REPEAT-4 |
| `agent-spec/runtime/stream-evidence-gate.md` | OFR-REPEAT-4 |
| `agent-spec/input/openai-compatible-surface.md` | OFR-REPEAT-4 |
| `agent-spec/runtime/provider-pool-config-refresh.md` | OFR-REPEAT-4 |

## 최종 검증

Local:

```bash
go version && go env GOMOD
git diff --check
go test -count=1 ./packages/go/streamgate ./packages/go/config ./apps/edge/internal/openai
make test
```

Dev preflight와 smoke는 `/Users/toki/agent-work/iop-dev`에서 clean/synced/rebuilt source로 실행한다:

```bash
git status --short
git rev-parse --abbrev-ref HEAD
git rev-parse HEAD
git fetch origin
git rev-parse origin/main
go version && go env GOMOD
iop-edge --help
iop-edge version
go test ./apps/edge/...
iop-edge smoke openai --base-url http://127.0.0.1:18083
IOP_OPENAI_BASE_URL=http://127.0.0.1:18083 IOP_OPENAI_MODEL=ornith:35b IOP_OPENAI_SMOKE_CONCURRENCY=5 IOP_OPENAI_SMOKE_RUNS=3 IOP_OPENAI_SMOKE_PROMPT_FILE=agent-test/runs/output-filter-recovery/repeat-prompt.txt IOP_OPENAI_SMOKE_ARTIFACT_DIR=agent-test/runs/output-filter-recovery/repeat-guard go test -count=1 -v ./apps/edge/internal/openai -run TestDevRepeatGuardOrnithSmoke
```

기대 결과: local 전부 PASS. dev는 main/HEAD 일치와 clean worktree, Edge config `build/dev-runtime/edge.yaml`/port 18083/`ornith:35b` capacity 4를 확인하고 5 concurrent × 3 runs를 완료한다. raw prompt/SSE/output/token은 stdout이나 tracked 파일에 남기지 않는다. 실제 반복이면 abort/continuation/safe stop evidence, 미재현이면 sanitized `not_reproduced`를 남기며 어느 경우에도 deterministic S03 fixture가 PASS해야 한다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다.
