Skip to content

Adapt GLM-5.2 apply_patch function calls to the Codex freeform tool contract #105

Closed

Description

@NOirBRight

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/freeform apply_patch call 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

  1. Exact valid GLM-shaped apply_patch function calls execute once as the normal Codex freeform tool and receive the correctly paired result.
  2. Missing, null, non-string, empty/whitespace-only, nested, conflicting, or extra argument fields fail closed before execution.
  3. IDs and event ordering are preserved in both body and streaming paths; duplicated or post-terminal semantic events fail closed.
  4. Non-apply_patch functions and already-correct custom/freeform calls are byte/semantically unchanged by this adapter.
  5. Focused telemetry distinguishes adapted, rejected, and untouched calls without recording patch contents.
  6. A sanitized reproduction of the current GLM retry loop is red before the change and green after it.
  7. 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_patch custom-tool history pair.
  • The call and output are adjacent, share one non-empty call_id, and the call status is completed.
  • Current Codex Desktop includes a native id field on both items.
  • Replaying the exact request through _adapt_apply_patch_custom_tool_history is deterministically red.
  • Removing only the call-side or only the output-side id remains red; removing both native id fields makes the complete request green with exactly one adapted pair.
  • The existing negative fixture incorrectly classifies the call-side id as 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_output pair when its supported id metadata 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 id fields 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 id rejection case.
  • The recovered Use semantic Provider dirty detection when edits are reverted #84 Worker can continue past its retained apply_patch history 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

  1. added
    bugSomething isn't working
    ready-for-agentFully specified, ready for an AFK agent
    on Jul 12, 2026
  2. self-assigned this
    on Jul 12, 2026
  3. NOirBRight commented on Jul 12, 2026

    @NOirBRight
    OwnerAuthor

    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.

  4. NOirBRight commented on Jul 12, 2026

    @NOirBRight
    OwnerAuthor

    Plan

    1. Reproduce the sanitized GLM retry loop at the existing buffered and streamed protocol-translation seams.
    2. Add only the exact-shape, fail-closed apply_patch function-call → freeform adaptation, preserving IDs/order and emitting sanitized telemetry.
    3. Run focused and full verification, the report-only quality gate, and the GLM shell → apply_patch → shell qualification before opening a draft PR to dev.
  5. NOirBRight commented on Jul 12, 2026

    @NOirBRight
    OwnerAuthor

    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.

  6. NOirBRight commented on Jul 12, 2026

    @NOirBRight
    OwnerAuthor

    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_command acceptance. The #105 implementation is safely checkpointed at cb1724deceb031d0a43000660293e3480c6b49f3; development must remain sequential because both Issues touch the Gateway compatibility hotset.

  7. NOirBRight commented on Jul 12, 2026

    @NOirBRight
    OwnerAuthor

    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.

  8. added a commit that references this issue on Jul 13, 2026
  9. NOirBRight commented on Jul 13, 2026

    @NOirBRight
    OwnerAuthor

    ROUTE PRIORITY DECISION — Responses primary, Chat conditional fallback

  10. NOirBRight commented on Jul 13, 2026

    @NOirBRight
    OwnerAuthor

    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.

  11. added
    ready-for-agentFully specified, ready for an AFK agent
    and removed
    ready-for-agentFully specified, ready for an AFK agent
    on Jul 13, 2026
  12. NOirBRight commented on Jul 14, 2026

    @NOirBRight
    OwnerAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions