Repository navigation
Adapt GLM-5.2 apply_patch function calls to the Codex freeform tool contract #105
Description
Context
Ollama Cloud GLM-5.2 can already run as a CodexHub-routed Codex worker for text generation, MCP tool calls, shell_command, and subagent spawn/wait/close. Parent and child traces both retain model=glm-5.2, upstream=ollama_cloud, and route_mode=codexhub without Luna/Terra fallback.
The remaining worker-qualification blocker is apply_patch. Codex advertises it as a freeform custom tool, but GLM emits a normal function call whose JSON argument contains the raw patch:
{
"type": "function_call",
"name": "apply_patch",
"arguments": { "patch": "*** Begin Patch\n..." }
}Codex expects the equivalent tool item as a custom/freeform call with the patch in the raw input. The current translation seam does not adapt this shape, no tool output is produced, and the model repeats the patch call until the run is stopped.
Outcome
Add a narrow, fail-closed compatibility adapter so third-party models that emit the exact function-call representation of apply_patch can complete the normal Codex file-edit contract. This is a compatibility bridge, not a change to Codex's advertised tool schema or permission model.
Scope
- Translate only
function_call(name="apply_patch", arguments={"patch": <non-empty string>})into the custom/freeformapply_patchcall with raw patch input. - Cover both buffered/body and streaming conversion paths.
- Preserve call/item identifiers and ordering needed for the matching tool result.
- Emit a specific sanitized telemetry marker/counter when this compatibility adaptation is used.
- Leave every unrelated function call unchanged.
- Do not hide
apply_patch, weaken the Worker file-edit contract, or permit shell-based file-writing as a substitute. - Do not change routing, retry, provider selection, model fallback, or planner behavior.
Acceptance criteria
- Exact valid GLM-shaped
apply_patchfunction calls execute once as the normal Codex freeform tool and receive the correctly paired result. - Missing, null, non-string, empty/whitespace-only, nested, conflicting, or extra argument fields fail closed before execution.
- IDs and event ordering are preserved in both body and streaming paths; duplicated or post-terminal semantic events fail closed.
- Non-
apply_patchfunctions and already-correct custom/freeform calls are byte/semantically unchanged by this adapter. - Focused telemetry distinguishes adapted, rejected, and untouched calls without recording patch contents.
- A sanitized reproduction of the current GLM retry loop is red before the change and green after it.
- A real qualification run completes
shell_command -> apply_patch -> shell_command, verifies the file changed exactly once, and confirms the route stayed on GLM-5.2 with no Luna/Terra fallback.
Verification
- Focused protocol-translation and Gateway tests for valid/invalid body and streaming cases.
- Existing protocol/routing/Gateway suites.
- Full Python suite and report-only quality gate.
- Sanitized exact CLI replay plus a real CodexHub-routed GLM-5.2 smoke run.
Dependencies and sequencing
Blocked by #26 because both work items own the protocol-translation/proxy compatibility seam. Implement from the merged #26 result rather than reconciling two concurrent versions of that hotset.
This issue is the shortest-path blocker for qualifying GLM-5.2 as an execution Worker that can replace Luna/Terra. Native/full-protocol work in #63-#67 remains a separate longer track.
Regression addendum — current Codex Desktop history IDs (2026-07-14)
A real #84 GLM-5.2 Worker continuation exposed a regression in the request-history inverse adapter delivered by PR #110. The Gateway rejected the request locally with invalid_apply_patch_function_call before Ollama Cloud execution.
Sanitized structural evidence from the exact failed request:
- The request contains 159 input items and exactly one
apply_patchcustom-tool history pair. - The call and output are adjacent, share one non-empty
call_id, and the call status iscompleted. - Current Codex Desktop includes a native
idfield on both items. - Replaying the exact request through
_adapt_apply_patch_custom_tool_historyis deterministically red. - Removing only the call-side or only the output-side
idremains red; removing both nativeidfields makes the complete request green with exactly one adapted pair. - The existing negative fixture incorrectly classifies the call-side
idas an arbitrary unexpected field even though Adapt GLM-5.2 apply_patch function calls to the Codex freeform tool contract #105 acceptance requires item identifiers to be preserved across the compatibility boundary.
No prompt, patch text, response text, local path, Task ID, call ID, or raw rollout was published.
Regression scope
- Accept the exact current Codex-native completed
custom_tool_call/custom_tool_call_outputpair when its supportedidmetadata is present. - Preserve
call_id, ordering, result pairing, and the existing sanitized telemetry semantics. - Translate or discard only explicitly recognized native metadata that the structured third-party request shape cannot carry.
- Continue to fail closed for arbitrary extra fields, malformed IDs, missing status, empty patches, duplicates, unpaired history, and post-terminal semantics.
- Add a sanitized structural regression fixture matching the real field shape.
- Do not change routing, retry, provider selection, model fallback, advertised tools, permission behavior, or Use semantic Provider dirty detection when edits are reverted #84 Provider dirty-detection code.
Regression acceptance criteria
- The sanitized current-Desktop pair containing supported native
idfields converts once to the correctly paired structured function history. - The complete sanitized reproduction is red before the fix and green after it.
- Existing malformed/unpaired/duplicate cases remain fail-closed, with a different arbitrary-extra-field fixture replacing the obsolete
idrejection case. - The recovered Use semantic Provider dirty detection when edits are reverted #84 Worker can continue past its retained
apply_patchhistory on GLM-5.2 without Luna/Terra or protocol fallback.
Execution contract
Execution-Contract: v2
Verification-Class: strict
Verification-Commands: python -m pytest -q tests/test_routing.py -k apply_patch; python -m pytest -q; python scripts/report_quality_gates.py (report-only)
Manual-Evidence: One bounded continuation of the retained #84 Worker after the fix reaches the next normal model/tool boundary on GLM-5.2 without the history-pair 502 or route fallback.
Architecture-Decision: resolved
Review-Owner: orchestrator
Expected hotset: src-python/codex_proxy.py, tests/test_routing.py, and at most one sanitized structural fixture under tests/fixtures/.
Activity
NOirBRight commentedon Jul 12, 2026
Dispatch
Architecture guardrail: adapt only the exact
function_call(name="apply_patch", arguments={"patch": <non-empty string>})shape. Malformed or extra fields fail closed; unrelated functions and already-correct custom calls remain unchanged. This is the shortest-path gate for GLM-5.2 Worker qualification.NOirBRight commentedon Jul 12, 2026
Plan
apply_patchfunction-call → freeform adaptation, preserving IDs/order and emitting sanitized telemetry.dev.NOirBRight commentedon Jul 12, 2026
Plan: inspect the restored checkpoint and confirm the retry fixture is sanitized; review adapter semantics and close scoped gaps; run focused, full-suite, quality-gate, fixture-replay, and bounded GLM qualification checks; then complete a two-axis review and publish a draft PR to dev.
NOirBRight commentedon Jul 12, 2026
Diagnosis update (2026-07-13): #105's live response adapter is working under a bounded minimal GLM request; the blocking broad CLI run eagerly expanded 205 App/MCP tools and produced no core tool call for the adapter to transform. A controlled minimal-vs-200-namespace-tool A/B reproduced the boundary (minimal green in ~3s; large surface red at the 8s bound).
#108 now owns the generic, capability-driven deferred-core tool-surface fix and blocks #105's final
shell_command -> apply_patch -> shell_commandacceptance. The #105 implementation is safely checkpointed atcb1724deceb031d0a43000660293e3480c6b49f3; development must remain sequential because both Issues touch the Gateway compatibility hotset.NOirBRight commentedon Jul 12, 2026
INTEGRATED DEBUG OWNERSHIP / NEW EVIDENCE
#108 has removed the large namespace surface as intended, but the unchanged #105 acceptance still fails after the first successful patch. In bounded isolated GLM runs, the first exact one-hunk apply_patch call executes successfully; the next request contains the matching custom tool call/output, yet Gateway request preparation converts that pair into developer-text transcripts. GLM then emits another apply_patch; a guarded 60-second run produced 55 adapted repeat calls with no route fallback or proxy request error.
This remains inside #105 acceptance criteria 1, 6, and 7 (paired result, retry-loop reproduction, and shell -> apply_patch -> shell). No weaker qualification will be accepted and no new Issue is needed.
The existing #108 integration Worker is now the sole Debug Owner for the tightly coupled #105/#108 hotset; the earlier #105 Worker remains paused. Next step is a bounded differential diagnostic, not a product change: compare the current text-history path against an exact structured function_call/function_call_output history bridge while holding model, prompt, tools, route, and patch constant. If structured history produces the required second shell, implement the narrow fail-closed inverse history adapter under #105, preserving call_id/order/result and leaving unrelated custom tools, official traffic, routing, and advertised apply_patch unchanged. If it does not, stop and report rather than adding prompt hacks, repeated-call suppression, or forced tool choice.
WIP: checkpoint #105/#108 GLM qualification
NOirBRight commentedon Jul 13, 2026
ROUTE PRIORITY DECISION — Responses primary, Chat conditional fallback
shell_command -> apply_patch -> shell_command, change the file exactly once, and retain GLM/Ollama Cloud/CodexHub identity with no retry or model/provider fallback.NOirBRight commentedon Jul 13, 2026
Completed by PR #110, merged into dev at 2916190. The accepted GLM-5.2 Responses qualification completed shell_command -> apply_patch -> shell_command, changed exactly one file/hunk/replacement, retained glm-5.2 / ollama_cloud / codexhub identity with no Luna/Terra fallback, and is covered by the committed fail-closed evidence replay.
NOirBRight commentedon Jul 14, 2026
Resolved by PR #121, merged into dev at db1ea61. The current-Desktop native id history-pair regression is covered by focused fail-closed tests; the full repository CI matrix passed. The retained real GLM-5.2 / Ollama Cloud / CodexHub #84 Worker then crossed the former history-pair 502 boundary, completed normal shell -> apply_patch -> shell progression, and reached subsequent model/tool boundaries without Luna/Terra fallback or duplicate-patch evidence. The regression addendum and original qualification acceptance are satisfied.