feat(recording): multi-select capture-point model, recording becomes a rule flag - #1644
Merged
0x0079 merged 4 commits intoSep 10, 2026
Merged
Conversation
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
August 26, 2026 16:49
046e756 to
d02d71b
Compare
0x0079
changed the base branch from
main
to
claude/recording-ctx-unification-v8wl8w
August 26, 2026 16:56
Base automatically changed from
claude/recording-ctx-unification-v8wl8w
to
main
August 27, 2026 10:32
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
2 times, most recently
from
August 27, 2026 11:32
748fae1 to
7652379
Compare
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
August 27, 2026 14:46
7652379 to
dd7402a
Compare
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
August 27, 2026 17:48
dd7402a to
bf01437
Compare
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
August 29, 2026 08:56
bf01437 to
e040f83
Compare
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
August 31, 2026 12:52
e040f83 to
fa32641
Compare
FFengIll
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
September 9, 2026 03:42
fa32641 to
c4f87c4
Compare
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
September 10, 2026 03:17
c4f87c4 to
641544d
Compare
录制配置从三档模式枚举改为沿链路采集点的多选集合,并将启用权交给 flag 体系(scenario 默认 + rule 覆盖),详见 .design/recording.md §3.5。 构建在两块已就位的基座上:multi_enum flag 类型(上一 commit 的 flag 基建 PR)与 recorder 的 ctx 传递形态(#1649)。 点位模型(typ.RecordingPoint): - client_request(入站请求)/ upstream_request(出站请求)/ upstream_response(服务返回,值域内、UI 暂不放开——尚无采集实现)/ client_response(最终返回);逗号分隔存储 - 旧三档枚举值(request/request_response/staged_request_response)由 ParseRecordingMode 兼容展开,存量配置零迁移;写入口严格校验并存 归一化形态 flag 融入: - RuleFlags.Recording 进 registry(multi_enum,observability 类别, Shared/override 继承,scenario 侧沿用 recording_v2 key);registry 测试补点位值域护栏 - resolveRuleFlagsWithScenario 解析继承并归一化;handler prologue 用 typ.EffectiveRecording 决定 recorder 创建(存 gin ctx,下游经 recording.FromGin 自取,不新增任何签名透传) - GetOrCreateScenarioSink 改为接收请求的 effective mode:rule 开、 scenario 关也能建 sink;GetScenarioRecordMode dep 移除 recorder/chain 按点位过滤: - recorder 构造/绑定时归一化 mode,emit 按 Has(point) 挑字段; chain 的 StagePre/StagePost 分别按 client_request/upstream_request 挂载(recorder.Wants,nil-safe);obs.NewSink 放开三档校验 覆盖补齐(修 P5): - OpenAI Chat/Responses handler 接上 recorder(此前传 nil,OpenAI 入 站从不录制);OpenAI→OpenAI 纯透传路径补上 emit(此前 recorder 有 值也永不落盘) 前端: - recording 走 registry 驱动的 multi_enum 复选控件,零 per-flag UI; RecordingV2Control 改为点位多选菜单(旧值归一化展示);codegen 已跑 测试: - typ 点位模型单测;protocoltest flag 套件补 recording 行为用例 (rule 级单独启用 → 落盘 → 断言恰好所选点位)。测试冲刷走既有 导出面(GetOrCreateScenarioSink + obs.Sink.ForceFlush),不为测试 新增生产 API Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7tYK69i7kpVE3d5pa4Dn2
响应侧采集质量不达标(流式靠组装/合成兜底),先保障接入链路与 client_request / upstream_request 两个 request 点位扎实有效。全部以 注释方式暂停,恢复时取消注释即可(.design/recording.md §3.5): - recorder emit:FinalResponse 分支注释(内部采集 SetAssembledResponse 与 AttachRecorderHooks 组装机制原样保留) - flag registry:client_response 选项注释(与 upstream_response 并列, 各附恢复条件);description 同步 - RecordingV2Control:client_response 勾选项注释;归一化容忍全值域, 存量含响应点位的配置在切换时不丢值 - 既有响应录制断言(anthropic_recording_e2e / hook_external 三例) 注释保留,改为钉住"暂停期不落 final_response";protocoltest 的 recording 用例增补:存量选中 client_response 也只落 request 点位 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7tYK69i7kpVE3d5pa4Dn2
…sponse client_request's "client" names the source (the client sent it). Copying that prefix onto the response point made "client" name the destination instead (the response sent to the client) — the same prefix meaning opposite things depending on which side of the pair you're reading, which reads as "a response the client produced". upstream_response is unaffected: "upstream's response" is a source-based name on both sides. Renamed RecordClientResponse/client_response -> RecordFinalResponse/ final_response everywhere (typ.RecordingPoint, the legacy-mode expansion table, flag registry, RecordingV2Control, config validation error strings, .design docs). This also aligns the point name with the layer that already used "final" — obs.Record.FinalResponse / json key final_response — so the naming is now consistent top to bottom. No migration needed: the point is not yet reachable through the UI (its option is commented out, response-side capture is paused), so no stored rule/scenario config can contain the literal string "client_response". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7tYK69i7kpVE3d5pa4Dn2
internal/protocol/request/{openai_responses_to_anthropic,openai_responses_to_chat}_test.go
had a struct field's newline silently dropped (CallID/Output collapsed
onto one line with stray tabs) as this branch was kept up to date with
main. gofmt -l didn't flag it — the joined form is syntactically valid
Go, just visually inconsistent with the surrounding style. Unrelated to
recording; picked up while auditing this branch's formatting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7tYK69i7kpVE3d5pa4Dn2
0x0079
force-pushed
the
claude/recording-point-flags-v8wl8w
branch
from
September 10, 2026 05:39
d51ebdd to
876cd9d
Compare
0x0079
added a commit
that referenced
this pull request
Sep 10, 2026
…1658) > Stacked on #1656 — merge that first; this PR's diff then shows only the work below. ## Summary The flag registry had no multi-select value type, so any flag needing "pick several of these options" had to invent per-flag UI. ## Key Changes - **`FlagTypeMultiEnum`**: multi-select over the spec's Options, stored comma-separated; the empty set is the inactive state (no first-option-default rule, unlike enum). - **Registry constraints**: test-enforced — ≥2 options, non-empty unique values/labels. - **Frontend**: `FlagCatalogDialog` renders a registry-driven checkbox group; `multiEnumValues` / `toggleMultiEnumValue` helpers plus `flagDefault`/`isFlagActive` cases — no per-flag UI code. ## Notes - No flag uses the type yet; the first consumer (recording capture points) is #1644. Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Recording enablement was a scenario-only 3-mode enum outside the rule-flag system, and OpenAI inbound traffic was never recorded at all. This PR remodels recording as a multi-select set of capture points and integrates it into the flag registry with rule-level override, narrowing the supported surface to the two request-side points.
Key Changes
typ.RecordingPoint(client_request/upstream_request); legacy values (request,request_response,staged_request_response) parse compatibly with zero config migration.recordingrule flag:multi_enum,Shared/override— a rule's point set overrides the scenario-levelrecording_v2default, resolved bytyp.EffectiveRecordingat the handler prologue.recording.FromGin), including success/failure emits on the pure passthrough paths.upstream_response/client_responsestay in the value domain but their UI options and emits are commented out — capture quality is not acceptable yet; configs selecting them fall back to request points only (pinned by test).GetOrCreateScenarioSinktakes the request's effective mode, so rule-enabled recording works even when the scenario default is off; depth filtering belongs to the recorder.recordingrenders through the registry-drivenmulti_enumcontrol;RecordingV2Controlbecomes multi-select on the same options.Testing
recordingcase in the protocoltest flag suite: rule-level enablement → gzip JSONL on disk → slim record carries exactly the selected points. Tests flush via the existing exported surface (GetOrCreateScenarioSink+obs.Sink.ForceFlush) — no production API added for testing.typunit tests for parsing/normalization/legacy-value compatibility, plus a registry guard pinning the flag's options to theRecordingPointvalue domain.