<!-- task=provider_stream_buffer_compat plan=0 tag=STREAM_BUFFER -->

# Provider stream single-event buffer compatibility hardening

## For the Implementing Agent

Filling the implementation-owned sections in `CODE_REVIEW-*-G??.md` is mandatory. Run every verification command, paste actual notes and stdout/stderr into the active review file, keep the active PLAN/CODE_REVIEW files in place, and report ready for review; only the code-review skill may finalize or archive the task. If blocked, record only the exact blocker, attempted commands/output, and resume conditions 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 configured repeat policy currently inherits Core's 4,096-rune default hard buffer for both its rolling text guard and terminal action sibling. A provider tunnel SSE frame can decode into one valid semantic content or reasoning event larger than that default, causing Core to emit `buffer_overflow` before repeat evaluation and Edge to return a 502 `provider_tunnel_error` even though the provider response is valid. This patch aligns only the repeat policy's hold envelope with Core's existing bounded 1 MiB maximum and preserves exact provider wire release.

## Analysis

### Files Read

- Workflow and rules: `AGENTS.md`, `agent-ops/rules/project/rules.md`, `agent-ops/rules/private/rules.md`, `agent-ops/rules/common/rules-roadmap.md`, `agent-ops/rules/common/rules-agent-spec.md`, `agent-ops/rules/project/domain/edge/rules.md`, `agent-ops/rules/project/domain/platform-common/rules.md`, `agent-ops/rules/project/domain/testing/rules.md`, `agent-ops/skills/common/router.md`, `agent-ops/skills/common/plan/SKILL.md`, `agent-ops/skills/common/finalize-task-routing/SKILL.md`, and `agent-ops/skills/common/plan/templates/review-stub-template.md`.
- Verification profiles: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`, and `agent-test/local/testing-smoke.md`.
- Contracts and current specs: `agent-contract/index.md`, `agent-contract/outer/openai-compatible-api.md`, `agent-contract/inner/edge-node-runtime-wire.md`, `agent-spec/index.md`, `agent-spec/runtime/stream-evidence-gate.md`, and `agent-spec/input/openai-compatible-surface.md`.
- Edge source: `apps/edge/internal/openai/stream_gate_filters.go`, `apps/edge/internal/openai/stream_gate_policy.go`, `apps/edge/internal/openai/stream_gate_tunnel_codec.go`, `apps/edge/internal/openai/stream_gate_release_sink.go`, and the relevant runtime assembly/error paths in `apps/edge/internal/openai/stream_gate_runtime.go`.
- Core source: `packages/go/streamgate/evidence_tail.go`, plus the relevant plan installation and overflow mapping paths in `packages/go/streamgate/runtime.go` and filter resolution accessors in `packages/go/streamgate/filter_registry.go`.
- Tests: `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`, and `packages/go/streamgate/stream_release_test.go`.
- Active task state: `agent-task/m-openai-compatible-output-validation-filters/02+01_repeat_guard/PLAN-cloud-G10.md`; it remains a separate Milestone verification task and is not reused or modified by this patch.

### SDD Criteria

Not applicable. This is a first-pass, non-roadmap compatibility patch and must not check or modify any Milestone Task or SDD state.

### Verification Context

- No `verification_context` handoff was supplied. The repository-native fallback is the current OpenAI contract/spec, Core hold implementation, production Edge tunnel codec/runtime/release sink, existing exact-wire tests, local test rules, and Makefile targets.
- Local preflight on 2026-07-30: branch `feature/openai-compatible-output-validation-filters`, HEAD `213eee4e28dfa69ff1e412faf23978ee9a9a3b9f`, clean worktree; Go resolves from `/config/.local/bin/go` to `/config/opt/go/bin/go`, version `go1.26.2 linux/arm64`, `GOROOT=/config/opt/go`, `GOMOD=/config/workspace/iop/go.mod`.
- Existing focused harness proof passed uncached: `go test -count=1 ./apps/edge/internal/openai -run 'Test(RepeatGuardIdleDoesNotRelease|OpenAIOutputFilterRegistrations|OpenAITunnelCodecTerminalWire|StreamGateConfiguredRepeatActionSplitLifecycle)$'`.
- `make -n proto` and `make -n test-openai-lemonade` confirm both repository-native commands resolve in this checkout. `gofmt -d` is clean for the three planned code/test files.
- No external runner, provider, shared runtime, dev deployment, credential, or network endpoint is required. The decisive oracle is an in-process production registry + tunnel event source + Core runtime + tunnel release sink driven by deterministic provider frames.
- Fresh `-count=1` and `-race -count=1` results are required; cached Go test output is not acceptable. Confidence is high because the failure and success paths share the exact production codec/Core/sink boundary.

### Test Coverage Gaps

- Existing Core tests cover explicit `max_buffer_runes`, Unicode rune counting, overflow, discard, and single terminal behavior, but no Edge test asserts the repeat text guard and its automatically registered action sibling compose to a bound above 4,096.
- Existing tunnel tests cover Chat/Responses parsing, content/reasoning event shapes, terminal framing, split tool identity, and byte-identical release only with small semantic deltas.
- No current test drives one content or reasoning semantic event larger than 4,096 runes through the configured repeat policy and full tunnel runtime. `STREAM_BUFFER-1` closes the policy-composition gap; `STREAM_BUFFER-2` closes the transport regression gap.

### Symbol References

None. No symbol is renamed or removed, and no new public API or dependency is introduced.

### Split Judgment

Keep one compact plan. The indivisible invariant is that a repeat-enabled provider tunnel must accept one valid coalesced semantic event within the existing 1 MiB Core bound and release the original provider SSE bytes exactly once; the hold configuration and runtime regression cannot independently prove that invariant.

### Scope Rationale

- Change only the repeat policy's rolling text/reasoning hold and terminal action sibling hold. Both must use the same bound because Core composes blocking requirements on one channel using the minimum positive `max_buffer_runes`.
- Preserve `hold_evidence_runes` as the repeat evaluation window. The new hard bound is storage/transport compatibility, not a larger repeat-detection window.
- Preserve the schema gate's default 4,096-rune terminal bound. Requests with `metadata.scheme` have a distinct full-output validation policy and are not part of this communication patch.
- Do not change `packages/go/streamgate`, codec framing/parsing, release queue semantics, 502 error serialization, config schema, contracts, specs, roadmap, deployment, or provider output filtering/evaluation logic.
- Do not split or reserialize provider frames. One raw frame is attached to the first semantic event; semantic fragmentation would require group-aware release semantics to avoid releasing the full raw frame before every derived event is evaluated.
- Values above 1 MiB remain intentionally bounded by Core and may still fail closed.

### Final Routing

- `status=routed`; `evaluation_mode=first-pass`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`.
- Build closures: scope/context/verification/evidence/ownership/decision closed. Grade scores `1/1/1/1/1`, base basis `local-fit`, final route basis `risk-boundary`, lane `cloud`, grade `G05`, canonical file `PLAN-cloud-G05.md`.
- Review closures: scope/context/verification/evidence/ownership/decision closed. Grade scores `1/1/1/1/1`, route `official-review`, lane `cloud`, grade `G05`, adapter `codex`, model `gpt-5.6-sol`, reasoning effort `xhigh`, canonical file `CODE_REVIEW-cloud-G05.md`.
- `large_indivisible_context=false`.
- Positive loop risks: `temporal_state`, `boundary_contract`, `structured_interpretation`, and `variant_product`; count `4`.
- Recovery signals: `review_rework_count=0`, `evidence_integrity_failure=false`; risk boundary matched, recovery boundary did not.
- Capability gap: none.

## Implementation Checklist

- [ ] [STREAM_BUFFER-1] Align both repeat-policy hold requirements to the existing bounded single-event compatibility limit and lock their composed Core envelope with a unit regression.
- [ ] [STREAM_BUFFER-2] Add Chat/Responses content/reasoning tunnel regressions for one semantic delta above 4,096 runes with byte-identical successful release.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [STREAM_BUFFER-1] Repeat-policy hold envelope

#### Problem

`apps/edge/internal/openai/stream_gate_filters.go:131-173` builds the repeat rolling guard and its terminal action sibling with Core's default constructors. Both therefore carry the 4,096-rune default from `packages/go/streamgate/evidence_tail.go:74-81,122-174`. `packages/go/streamgate/evidence_tail.go:480-483` composes same-channel blocking requirements using the minimum positive bound, so changing only the rolling guard would leave the action sibling's 4,096 limit effective.

Before:

```go
// apps/edge/internal/openai/stream_gate_filters.go:131-173
func (f *openAIOutputFilter) HoldRequirement(streamgate.FilterContext) streamgate.FilterHoldRequirement {
	switch f.kind {
	case openAIOutputFilterRepeatGuard:
		req, err := streamgate.NewFilterHoldRequirementRolling(
			f.channel,
			[]streamgate.EventKind{
				streamgate.EventKindTextDelta,
				streamgate.EventKindReasoningDelta,
			},
			f.holdRunes,
		)
		// ...
	case openAIOutputFilterRepeatActionGuard:
		req, err := streamgate.NewFilterHoldRequirementTerminalGate(
			f.channel,
			[]streamgate.EventKind{
				streamgate.EventKindToolCallFragment,
				streamgate.EventKindTerminal,
			},
			streamgate.EventKindTerminal,
		)
		// ...
	case openAIOutputFilterSchemaGate:
		req, err := streamgate.NewFilterHoldRequirementTerminalGate(
			// ...
		)
	}
}
```

#### Solution

- Add one unexported Edge constant, `openAIRepeatHoldMaxBufferRunes = 1 << 20`, documented as the bounded allowance for a coalesced provider semantic event, not the rolling evidence window.
- Use `NewFilterHoldRequirementRollingWithMaxBuffer` for `openAIOutputFilterRepeatGuard` and `NewFilterHoldRequirementTerminalGateWithMaxBuffer` for `openAIOutputFilterRepeatActionGuard`, including their defensive fallback calls.
- Leave `openAIOutputFilterSchemaGate` on `NewFilterHoldRequirementTerminalGate` so its default bound does not silently change.
- Extend the filter registration test surface with `TestOpenAIRepeatHoldBufferContract`: resolve the production repeat registration without a schema, compile `EvidencePlan`, and assert the composed channel maximum is `openAIRepeatHoldMaxBufferRunes`, rolling evidence remains the configured value, both repeat participants carry the same maximum, and a standalone schema requirement remains 4,096.

After:

```go
// A provider may coalesce one valid content/reasoning delta above Core's
// default hold size. This remains Core-bounded and does not widen evidence.
const openAIRepeatHoldMaxBufferRunes = 1 << 20

case openAIOutputFilterRepeatGuard:
	req, err := streamgate.NewFilterHoldRequirementRollingWithMaxBuffer(
		f.channel,
		[]streamgate.EventKind{
			streamgate.EventKindTextDelta,
			streamgate.EventKindReasoningDelta,
		},
		f.holdRunes,
		openAIRepeatHoldMaxBufferRunes,
	)
	// fallback uses the same explicit bound
	return req
case openAIOutputFilterRepeatActionGuard:
	req, err := streamgate.NewFilterHoldRequirementTerminalGateWithMaxBuffer(
		f.channel,
		[]streamgate.EventKind{
			streamgate.EventKindToolCallFragment,
			streamgate.EventKindTerminal,
		},
		streamgate.EventKindTerminal,
		openAIRepeatHoldMaxBufferRunes,
	)
	// fallback uses the same explicit bound
	return req
case openAIOutputFilterSchemaGate:
	// Keep the existing default constructor and bound.
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/stream_gate_filters.go` — add the private compatibility bound and apply it to both repeat hold requirements only.
- [ ] `apps/edge/internal/openai/stream_gate_filters_test.go` — add `TestOpenAIRepeatHoldBufferContract` covering participant, composed-plan, evidence-window, and schema non-regression assertions.
- [ ] `agent-task/provider_stream_buffer_compat/CODE_REVIEW-cloud-G05.md` — record implementation decisions and actual item verification output.

#### Test Strategy

Write a regression test because this is a bug fix. `TestOpenAIRepeatHoldBufferContract` must use production registration/resolution rather than only direct constructor calls so omission of the automatically registered action sibling fails the test. It must assert exact rune limits and preserve the schema boundary explicitly.

#### Verification

```bash
gofmt -d apps/edge/internal/openai/stream_gate_filters.go apps/edge/internal/openai/stream_gate_filters_test.go
go test -count=1 ./apps/edge/internal/openai -run '^Test(OpenAIRepeatHoldBufferContract|OpenAIOutputFilterRegistrations)$'
```

Expected: no formatting diff; the fresh focused tests pass and prove the repeat pair composes to 1 MiB while its evidence window and schema bound remain unchanged.

### [STREAM_BUFFER-2] Large semantic-delta exact-wire tunnel regression

#### Problem

`apps/edge/internal/openai/stream_gate_tunnel_codec.go:239-286,323-408,444-556` parses a complete SSE frame into one content or reasoning event and retains the original frame for release. `packages/go/streamgate/evidence_tail.go:1569-1582` checks the event's rune count against the composed hold bound before evaluation. Existing exact-wire fixtures at `apps/edge/internal/openai/stream_gate_pipeline_test.go:375-443` use only small deltas, so they do not reproduce the valid greater-than-4,096 event that currently terminates as a 502.

Before:

```go
// apps/edge/internal/openai/stream_gate_pipeline_test.go:375-443
func TestOpenAITunnelCodecTerminalWire(t *testing.T) {
	tests := []struct {
		name     string
		endpoint string
		frames   [][]byte
	}{
		{
			name:     "chat finish then done",
			endpoint: openAIRebuildEndpointChat,
			frames: [][]byte{
				[]byte("data: {\"choices\":[{\"delta\":{\"content\":\"answer\"}}]}\n\n"),
				// ...
			},
		},
		// Responses and transport-end variants also use small payloads.
	}
}
```

#### Solution

- Add `TestStreamGateConfiguredRepeatGuardLargeTunnelDelta` beside the production tunnel lifecycle tests.
- Use a table with four cases: Chat content, Chat reasoning, Responses output text, and Responses reasoning. Each case must encode one SSE JSON frame containing 5,000 distinct Korean runes, followed by the endpoint terminal wire.
- Configure only blocking `repeat_guard` with the normal 500-rune evidence window and no schema metadata. Build the production registry, real tunnel event source/Core runtime, and real tunnel release sink over buffered `ProviderTunnelFrame` fixtures.
- For every case, require runtime success, one HTTP 200 start, one successful terminal, zero recovery dispatches, no `provider_tunnel_error`/`buffer_overflow`, and caller body bytes exactly equal to the concatenated provider frames.
- Keep the provider frame whole. Do not add codec fragmentation or a replacement serializer.

After:

```go
func TestStreamGateConfiguredRepeatGuardLargeTunnelDelta(t *testing.T) {
	payload := uniqueKoreanRunes(5000)
	tests := []struct {
		name     string
		endpoint string
		frame    []byte
	}{
		{name: "chat content", endpoint: openAIRebuildEndpointChat, frame: chatContentFrame(payload)},
		{name: "chat reasoning", endpoint: openAIRebuildEndpointChat, frame: chatReasoningFrame(payload)},
		{name: "responses output text", endpoint: openAIRebuildEndpointResponses, frame: responsesTextFrame(payload)},
		{name: "responses reasoning", endpoint: openAIRebuildEndpointResponses, frame: responsesReasoningFrame(payload)},
	}
	// Each case runs production registry -> endpoint codec -> Core -> tunnel sink
	// and compares the caller body with the original semantic + terminal frames.
}
```

Use local fixture construction with `encoding/json` or existing imports; do not add a package dependency. Helper names in the outline are illustrative and may be replaced by a compact local frame builder within the same test.

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/stream_gate_pipeline_test.go` — add the four-case production runtime regression and exact-wire assertions.
- [ ] `agent-task/provider_stream_buffer_compat/CODE_REVIEW-cloud-G05.md` — record the actual regression output and any justified deviation.

#### Test Strategy

Write the integration regression in the existing pipeline test file. The 5,000-rune distinct payload is above the old default and below the new bound, avoids accidentally triggering repeat detection, exercises UTF-8 rune rather than byte accounting, and covers both endpoint and semantic-kind axes.

#### Verification

```bash
gofmt -d apps/edge/internal/openai/stream_gate_pipeline_test.go
go test -count=1 ./apps/edge/internal/openai -run '^TestStreamGateConfiguredRepeatGuardLargeTunnelDelta$'
```

Expected: no formatting diff; all four fresh subtests return the original wire once with HTTP 200 and a successful terminal.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/openai/stream_gate_filters.go` | STREAM_BUFFER-1 |
| `apps/edge/internal/openai/stream_gate_filters_test.go` | STREAM_BUFFER-1 |
| `apps/edge/internal/openai/stream_gate_pipeline_test.go` | STREAM_BUFFER-2 |
| `agent-task/provider_stream_buffer_compat/CODE_REVIEW-cloud-G05.md` | STREAM_BUFFER-1, STREAM_BUFFER-2 implementation evidence |

## Dependencies and Execution Order

1. Implement `STREAM_BUFFER-1` so the production registry resolves the corrected composed hold bound.
2. Implement `STREAM_BUFFER-2` against that production registry and retain exact provider wire.
3. Run the focused commands, then the complete final verification, and fill the active review evidence.

## Final Verification

```bash
command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT && go env GOMOD
make proto
git diff --exit-code -- proto/gen/iop
gofmt -d apps/edge/internal/openai/stream_gate_filters.go apps/edge/internal/openai/stream_gate_filters_test.go apps/edge/internal/openai/stream_gate_pipeline_test.go
go test -count=1 ./apps/edge/internal/openai -run '^Test(OpenAIRepeatHoldBufferContract|StreamGateConfiguredRepeatGuardLargeTunnelDelta)$'
go test -race -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./packages/go/config
make test-openai-lemonade
IOP_E2E_BIND_TIMEOUT=60 IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh
git diff --check
```

Expected: the host Go identity remains unchanged; proto generation produces no diff; formatting is clean; fresh focused and race suites pass; the repository-native OpenAI provider tunnel and mock smokes pass; and the final diff has no whitespace errors. No live provider, external runner, or cached Go result is accepted as a substitute for the deterministic large-delta regression.

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