WIP: migrate to protocol stage for better protocol handling. - #1501
Draft
FFengIll wants to merge 8 commits into
Draft
WIP: migrate to protocol stage for better protocol handling.#1501FFengIll wants to merge 8 commits into
FFengIll wants to merge 8 commits into
Conversation
FFengIll
marked this pull request as draft
August 5, 2026 05:23
…x design notes Front-load the design and plan documents for the Protocol Stage feature port (from codex/protocol-stage-hardening) as the first commit of the branch, so every subsequent code commit is traceable to a written plan: - .design/protocol-stage-chain.md: composable protocol stage chain rationale - .design/protocol-stage-tool-loop.md: stage tool-loop semantics (max rounds, side-effect commit, failover replay safety) - .design/protocol-recording-redesign.md: recording boundary redesign - .design/harness-matrix.md: cross-stage test matrix layout - docs/guardrails.md, README.md, cli/harness/README.md: user-facing notes No code changes; documentation only.
…g from hardening Port the self-contained stage core from codex/protocol-stage-hardening (off #1491): - internal/protocol/stage: bridge/compose/registry/topology + anthropic/openai/responses bridges, guardrail, toolloop - internal/protocol/{assembler,nonstream,stream,transform,wire,request}: protocol-layer additions and adjustments the stage depends on - internal/record: new request recording lifecycle (recorder, provider_endpoint, boundary matrix) - internal/obs: record/sink/slim additions + request_record test - internal/guardrails/mutate: RewriteAnthropicToolUseEventDecision used by stage guardrail All dependencies (protocol/*, guardrails, record, obs) land in this commit so the unit builds clean: go vet ./internal/protocol/... ./internal/guardrails/... ./internal/record/... ./internal/obs/ Wiring into the server (protocol_stage_*.go glue, forwarding path fix, server skeleton 3-way merge) and cross-stage test matrix follow in subsequent commits. Batch 1+2 of the protocol-stage-hardening port.
…o new package layout Port the server-side stage integration from codex/protocol-stage-hardening, adapting to main's post-#1493..#1495 package extraction (gateway core now lives in protocolserver, MCP engine in mcpserver): - protocol_stage_*.go glue (pipeline/selector/recording/tool_loop/anthropic/openai endpoints): moved into package protocolserver (was package server on hardening), since ProtocolHandler now lives there. Rewired imports: server/{forwarding,recording} -> protocolserver/*; server/transform folded into protocolserver/transform; mcpmodule alias -> internal/mcpserver. - 16 files hardening modified at old internal/server paths (anthropic_message, openai_chat, openai_responses, protocol_{dispatch,handler}, failover_dispatch, guardrails_runtime_ai, mcp_hooks, module/mcp/{continuation_store,format_adapter,generic_loop_processor, generic_stream_interceptor,tool_executor}, servertool/executor): applied via clean 3-way merge (base=#1491) against their new protocolserver/mcpserver homes — 0 conflicts. - anthropic_beta_stage*.go + tool_executor/continuation_store tests: moved to package mcpserver (their referenced symbols Tool/NewAnthropicBetaAdapter live there now); package mcp -> mcpserver. - internal/mcp/runtime: ListServerToolsForAnthropicBetaInjection added by hardening. - Restored the dropped `internal/protocol` import in protocol_handler.go (lost in 3-way merge). Builds clean: go vet ./internal/{server,protocolserver,mcpserver,mcp,protocol,record,obs,guardrails}/... Server-skeleton wiring (server.go/lifecycle/options 3-way merge) follows next. Batch 3 of the protocol-stage-hardening port.
Complete the server-side wiring for the ported Protocol Stage pipeline (hardening's server.go/server_lifecycle.go/server_options.go changes, 3-way merged against main's post-#1493..#1495 skeleton): - server.go: pass ProtocolStageEnabled into protocolserver.ProtocolHandlerDeps; add protocolStageEnabled and servertoolProviders fields to the Server struct. - server_options.go: add WithProtocolStage and WithServertoolProviders options (servertool import repointed to internal/protocolserver/servertool). - server_lifecycle.go: add ForceFlushRecordings (auto-merged clean; used by the protocol harness so a run leaves a complete recording artifact before exit). The two real conflicts (server.go handler construction, server_options.go import) resolved in favor of main's protocolserver layout, retaining hardening's additive ProtocolStageEnabled/servertoolProviders surface. go build ./... is clean end-to-end (server + cli). Batch 4 of the protocol-stage-hardening port.
…t MCP runtime after builtin registration WithServertoolProviders stored providers on the Server but nothing ever read them, so in-process server-owned tools never reached the MCP runtime's virtual registry. Re-register them in registerAdviserFromConfig so they survive pipeline rebuilds and config hot reload. Also restore the integration branch's construction order: RegisterBuiltinTools must run before NewRuntime. The port created the runtime first, so on a first-run config (no persisted MCP runtime yet) NewRuntime saw a nil config and returned nil, disabling the whole stage tool loop and leaving requests to fall back to legacy. Registering builtins first seeds the MCP runtime config and yields a usable runtime on first boot.
…ate stash on session Problem 1: a pure-managed round followed by a pure-external round returned the external round with no continuation, so the server-tool results from the earlier round never reached the model's final answer on the client's next turn. Track internal rounds accumulated during the loop and stash them ahead of the external assistant message; generalize the merge so a trailing assistant segment receives the client's results as a new user turn instead of folding them into a managed-results message. Problem 2: on IP-fallback / no-session requests the continuation store silently no-oped, so a mixed round executed the server-owned tool, filtered its block, and dropped the result with no error. Put now reports failure and the store exposes CanStash; the stage checks it before executing owned tools and returns ErrContinuationUnavailable instead of committing side effects it cannot carry. An external-only round after internal rounds degrades to delivering the round without a stash (guardrail blocking still works).
…fects after dispatch Problem 3: a pure-managed tool call on the last allowed round failed with ErrMaxRounds, discarding the tool calls, while a mixed round on the same round succeeded. maxRounds now bounds server-tool executions: the last budgeted round executes its tools and the model gets one extra provider round to produce the final answer. A further server-tool request fails closed. Applies to the Beta complete/stream stages and the OpenAI Chat stage; unify both stage defaults behind DefaultMaxRounds=3 (matching the legacy MCP loop) and set the production Beta loop explicitly. Problem 4: the Chat tool loop marked side effects committed only on a successful executor return. An executor that dispatches an irreversible action and then loses its response left the attempt uncommitted, so failover could replay the action on another provider. ToolResult gains a Dispatched signal (matching the Beta stage's existing behavior); the Chat stage commits when err == nil || result.Dispatched, keeping pre-dispatch validation and policy failures uncommitted.
Port the integration-test surface and user-facing wiring for the Protocol Stage
feature from codex/protocol-stage-hardening:
- internal/protocoltest: bridge/mcp/guardrail/recording matrix helpers + tests
(bridge_matrix, mcp_matrix, mcp_recording_*, protocol_stage_server_test,
protocol_stage_tool_loop_test, record_artifacts, guardrails); testenv gains
rootServer + ProtocolStage/servertool options + ForceFlushRecordings;
failover/matrix/virtual_client tests updated. Repointed stale imports
(server/servertool -> protocolserver/servertool; server/module/mcp -> mcpserver).
- internal/command: expose EnableProtocolStage + WithProtocolStage plumbing
(options/server_options{,_test}.go, command/server.go).
- cli/harness + cli/tingly-box: matrix and main_test updates.
- gui/wails3/run.go: pass WithProtocolStage(opts.EnableProtocolStage) in all launch modes.
Design notes for this feature live in a dedicated front-loaded commit
(docs(protocol-stage)) at the head of the branch.
Full module builds clean: go build ./... and go vet ./internal/... ./cli/...
produces the tingly-box binary. (experiments/ is untracked working-tree scratch,
not part of this port.)
Batch 5 (final) of the protocol-stage-hardening port.
FFengIll
force-pushed
the
feat/protocol-stage-hardening-port
branch
from
August 5, 2026 05:39
c0075fc to
3527eea
Compare
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.
No description provided.