Skip to content

Send the provider a combinator-free planner schema - #2052

Merged
ppXD merged 1 commit into
mainfrom
fix/send-planner-a-combinator-free-wire-schema
Sep 30, 2026
Merged

ppXD merged 1 commit into
mainfrom
fix/send-planner-a-combinator-free-wire-schema

Conversation

@ppXD

@ppXD ppXD commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Since Expose complete oracle shapes to structured planner models #1854, every planner call to the hosted vLLM gateway has failed with an empty HTTP 500 (litellm.InternalServerError: InternalServerError: Hosted_vllmException - {"error":{"message":"","type":"InternalServerError","param":null,"code":500}}), followed by 429s. The planner node parks on SupervisorInfraPark, and the live planner gates have been dark on main since. vLLM compiles the forced tool's schema into a decoding grammar, and Expose complete oracle shapes to structured planner models #1854's per-kind acceptance oneOf branches break that compile. Other callers of the same model get 200s.
  • StructuredLLMCompletionRequest.WireJsonSchema is a new optional field. AnthropicClient sends it as tools[0].input_schema, and OpenAiClient (which CustomClient delegates to) sends it as function.parameters. When it is null, both fall back to JsonSchema. The planner sends PlannerSchema.WireSchema, which is ResponseSchema passed through JsonSchemaCombinators.Strip. Strip drops oneOf/anyOf/allOf/not/if/then/else at every schema position and keeps property names and literals. The result is the flat typed acceptance the gateway answered before Express oracle payload requirements in the model schema #1849. It still declares formatVersion, kind, argv, artifactPaths, oraclePaths, rubric and schema.
  • Validation, the bounded re-ask and the schema quoted in the system prompt all still use the full ResponseSchema (StructuredResponseValidation.cs:137 is unchanged), so the per-kind requirements are still enforced. The wire schema is the full schema minus one conjunct, so it accepts everything the full schema accepts.
  • Diagnostics: InfraPark now logs the fault message. When a park outlives the E2E ride, InfraParkRide's skip message now appends the error stored on the park marker.

Test plan

  • Red on main: StructuredResponseContractTests.The_planner_request_sends_the_provider_a_combinator_free_schema_that_still_declares_every_acceptance_field failed for Anthropic and OpenAI with 6 combinator paths (acceptance.oneOf plus 5 not).
  • Portability guard: JsonSchemaCombinatorsTests.Every_schema_the_code_sends_to_a_model_is_combinator_free_on_the_wire covers 14 wire forms. A reflection check fails if Core gains a static *Schema field that is not listed.
  • Superset: TypedModelSchemaBranchTests checks that the wire schema equals the full schema minus the acceptance oneOf, and that every valid oracle reply is also valid on the wire. A_reply_the_wire_schema_admits_but_the_contract_rejects_still_earns_the_reask_and_the_fault checks that validation is unchanged.
  • Mutations:
    • WireSchema = ResponseSchema: the capture test (both providers), the guard and 2 wire tests go red.
    • Validating against the wire schema: the validation-unchanged test goes red (both providers).
    • Planner stops sending WireJsonSchema: the capture test goes red (both providers).
    • A combinator in TapeSummarySchema plus an unlisted *Schema field: the guard and the completeness check go red.
  • Unit suite: 11544 passed, 1 skipped. Related integration classes: 73/73, including PlannerCassetteDriftTests. ExpectedPlannerKey is unchanged because the cassette key hashes JsonSchema, not the wire schema. PR CI is green. The unit lane's first attempt failed once in LocalProcessDurableRunnerTests.A_controllers_deadline_stop_is_timed_out_whatever_the_clock_or_the_oom_counter_reads (IOException: No such process: '/proc/<pid>/stat'), a liveness race in NativeProcess.IsRunning that this PR does not touch; the rerun passed.
  • Live: real-model run 36569420459 on f3d07b1. The aux job "session · planner · delivery arcs" succeeded.
    • Census on this run: ok 2 0 0 RealModelPlanner and ok 1 0 0 Live_planner_keeps_executable_content_checks_separate_from_file_obligations_and_the_real_oracle_rejects_wrong_content. Main run 36565851194 at 2eae0e5 had UNMEASURED 0 0 2 and UNMEASURED 0 0 1.
    • Planner calls (POST /v1/messages): the acceptance arm got 200 (4429 ms) plus a re-ask 200 (3390 ms); the whole-loop planner got 200 (5646 ms) plus a re-ask 200 (4088 ms).
    • Across the whole job: 24 responses, all 200. No 500, no 429, no infra park.
  • What the model authored once it could answer: the whole-loop arm fanned out over its 4 model-authored subtasks, but plan.author dropped all 4 acceptances (TestsPass without argv, LlmJudge without artifactPaths). The report-only acceptance arm logged the same miss as INFORMATIONAL-FAIL: "content-equals-accepted (TestsPass): Planner acceptance requires a non-empty argv array.; report-file-present (ArtifactPresent): Planner acceptance requires a non-empty artifactPaths array." That arm's class doc already records this model-quality gap; it is now measured instead of dark.
  • The same run's "supervisor arcs" job failed in its headline arc. The live model spawned with persona slug claude-code, which is rejected on turns 2–9 as naming no active persona. The run then stopped for no progress, and arbitration turned Success into Failure. This PR does not change that path: supervisor requests set no WireJsonSchema, so their tool schema is byte-identical. The same rejection appears 4–7 times in each of main's last three supervisor runs.

Since #1854 expanded the planner acceptance's per-kind oneOf branches,
every planner call to the hosted vLLM gateway has failed with an empty
HTTP 500 (followed by 429s), the planner node parked on
SupervisorInfraPark, and the live planner gates measured nothing on
every main run. The same model answers every other caller, and heads
without #1854 got 200s. vLLM compiles the forced tool's schema into a
decoding grammar, and its error names no keyword.

The request now carries an optional WireJsonSchema that both provider
clients send as the tool schema, falling back to JsonSchema. The
planner sends PlannerSchema.WireSchema: ResponseSchema with oneOf,
anyOf, allOf, not, if, then and else stripped at every schema
position, which leaves the flat typed acceptance the gateway answered
before #1849 while still declaring every acceptance field. Dropping a
combinator only removes a constraint, so the wire schema accepts every
reply the contract does; validation, the bounded re-ask and the schema
quoted in the prompt all keep the full ResponseSchema.

A guard pins every schema the code sends to a model as combinator-free
on the wire. The park log and the E2E ride's unresolved-park message
now carry the fault text, so the next outage shows the gateway's words.
@ppXD
ppXD merged commit ac484f2 into main Sep 30, 2026
16 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant