Skip to content

fix(routines): flat schedule schema + coercion so chat proposals survive provider schema conversion - #544

Merged
milind-soni merged 1 commit into
mainfrom
fix/routine-propose-contract
Aug 28, 2026
Merged

fix(routines): flat schedule schema + coercion so chat proposals survive provider schema conversion#544
milind-soni merged 1 commit into
mainfrom
fix/routine-propose-contract

Conversation

@milind-soni

@milind-soni milind-soni commented Aug 28, 2026

Copy link
Copy Markdown
Owner

What changed

propose_routine's schedule schema and input handling in the agents MCP proxy. Harness dialect and server are untouched.

  • Flat schema, no oneOf/const/format. The schedule was a oneOf of two const-discriminated branches — exactly the JSON-Schema composition keywords several agent CLIs flatten or drop when converting MCP tools into their provider's function-call format (codex-rs only began preserving oneOf in June 2026, codex-rs/tools/src/json_schema.rs #24118; its large-schema compaction still prunes compositions; other drivers simplify harder). A model that never saw the branches guesses shapes forever. The schema is now one flat object (type: once|weekly|daily + at/time/weekdays) with the rules in descriptions.
  • Coercion for what models actually send (normalizeScheduleInput): "daily" → weekly-on-all-7-days on the wire; JSON-string schedules parsed; weekday names case-folded, short names (monsun) expanded.
  • Guiding errors instead of walls: sub-day intervals are named as unsupported with the closest alternatives spelled out; weekly without weekdays points at "daily"; unknown types list the three supported shapes with examples. Same for propose_routine_action update changes.

Why

Field report hours after 0.1.38 shipped #540: a bot (gpt-5.6-sol) failed agents_propose_routine on every attempt — tried a 30-min interval, concluded "only weekly is supported, best I can do is daily", sent type:"daily", failed six more times, then told the user "the routine proposal tool is returning errors every time — it's not accepting any input."

I reproduced the full pipeline against a live server (real proxy → real /api/internal/routine-requests): valid payloads work — the field failures are all model-shaped inputs, and the old replies never taught the model the correct shape:

payload before after
valid weekly ✅ card ✅ card (byte-identical dialect)
type:"daily" Invalid discriminator value. Expected 'once' | 'weekly' ✅ card (weekly × 7 days)
schedule as JSON string ❌ needs name/instructions/schedule ✅ card
["Mon","FRI"] ❌ (short names) / ✅ caps only ✅ card
30-min interval Invalid discriminator value… ❌ with instructions naming the limit + closest shapes
weekly w/o weekdays expected array, received undefined ❌ with use {"type":"daily"} guidance

How it was verified

  • npx vitest run server/drivers/agents-proxy.test.ts — 20 pass. New: flat-schema assertion (regexp-guards against any composition keyword reappearing in the tool surface), daily-alias wire shape, case/short weekday folding, string-schedule parsing, guided errors that never reach the harness. Existing weekly/once passthrough tests unchanged.
  • Mutation check: disabling the daily alias fails the new tests; restoring passes.
  • Live end-to-end (dev server on a scratch data dir + real proxy): the table above is from that run.
  • pnpm typecheck clean. Lint: this file has 35 pre-existing anti-slop findings on main; the additions follow the file's existing no-deps typeof-boundary style (6 more of the same rules; lint is not a CI gate — flagging for transparency).

Note for reviewers

The same conversion fragility applies to any future MCP tool schema: avoid oneOf/anyOf/const in tool inputs — flat objects + description-enforced rules + instructive errors survive every driver. Might be worth a line in CONTRIBUTING.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved routine scheduling support for one-time, weekly, and daily routines.
    • Daily schedules are automatically interpreted across all seven days.
    • Weekday names are normalized consistently, including abbreviated and capitalized forms.
    • Schedule details provided as JSON text are now recognized automatically.
  • Bug Fixes

    • Unsupported or incomplete schedules now return clearer guidance before a routine is submitted.

…a, model-shaped coercion, guiding errors

Field report (0.1.38, hours after #540 shipped): a bot's propose_routine
failed on every attempt — the model tried a 30-minute interval, was told no,
fell back to "daily", failed again, then gave up ("the routine proposal tool
is returning errors every time — it's not accepting any input").

Reproduced the whole pipeline against a live server: the endpoint and proxy
are correct for valid payloads. What actually breaks in the field is the
tool's advertised schema: schedule was a oneOf of two const-discriminated
branches — exactly the JSON-Schema keywords several agent CLIs flatten or
drop when converting MCP tools into their provider's function-call format
(codex only began preserving oneOf in June 2026; other drivers still
simplify). A model that never saw the branches guesses shapes forever, and
every guess failed with a message that never taught it the right one.

Three changes, all proxy-side — the harness dialect is untouched:

- The schedule schema is now one flat object (type enum once|weekly|daily +
  at/time/weekdays), free of oneOf/const/format, so it survives any
  conversion. Rules live in descriptions and are enforced with words.
- normalizeScheduleInput coerces what models actually send: "daily" becomes
  weekly-on-all-seven-days on the wire, JSON-string schedules are parsed,
  weekday names are case-folded and short names (mon..sun) expanded.
- Unsupported shapes now answer with instructions instead of a wall: a
  sub-day interval is named as unsupported with the closest alternatives,
  weekly-without-weekdays points at "daily", unknown types list the three
  supported shapes with examples.

Verified end-to-end against a live server: daily / stringified / short-cap
payloads now produce confirmation cards, interval and missing-weekdays get
guiding errors, and the valid-weekly path is byte-identical. Mutation check:
disabling the daily alias fails the new tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openmausbot-docs Ready Ready Preview Aug 28, 2026 10:34am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7430d4a8-500a-4615-a5e7-f2d8ab545bfa

📥 Commits

Reviewing files that changed from the base of the PR and between 7983055 and 9476dfe.

📒 Files selected for processing (2)
  • server/drivers/agents-proxy.test.ts
  • server/drivers/agents-proxy.ts

📝 Walkthrough

Walkthrough

The routine schedule schema now uses a flat type enum. Schedule inputs are parsed, normalized, and validated before harness calls. Unsupported schedules return guidance through both proposal and update paths.

Changes

Routine schedule handling

Layer / File(s) Summary
Schedule contract and normalization
server/drivers/agents-proxy.ts, server/drivers/agents-proxy.test.ts
The schedule schema accepts once, weekly, and daily. The normalizer parses JSON strings, validates schedule fields, normalizes weekdays, and expands daily schedules to all weekdays.
Routine tool integration
server/drivers/agents-proxy.ts, server/drivers/agents-proxy.test.ts
Routine proposal and update paths return schedule errors before harness calls. Tests cover normalized requests and unsupported schedules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPTool
  participant routineFields
  participant normalizeScheduleInput
  participant Harness

  MCPTool->>routineFields: Submit routine schedule
  routineFields->>normalizeScheduleInput: Normalize schedule
  normalizeScheduleInput-->>routineFields: Normalized schedule or error
  alt Valid schedule
    routineFields->>Harness: Send normalized routine
  else Unsupported schedule
    routineFields-->>MCPTool: Return guidance error
  end
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/routine-propose-contract

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@milind-soni
milind-soni merged commit 875fac4 into main Aug 28, 2026
8 of 9 checks passed
milind-soni added a commit that referenced this pull request Aug 28, 2026
…sion eats composition keywords (#547)

Codified from the #544 field failure: schedule was a oneOf of const-branches,
several engines' MCP-to-provider converters flattened it, and models guessed
shapes forever. The rule, the coercion posture, and the errors-must-teach
posture now live next to the driver SPI guidance so the next tool surface
doesn't relearn it in production.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
pull Bot pushed a commit to dubbypanda/OpenMausBot that referenced this pull request Aug 29, 2026
…turns while things boot

Stolen from vercel-labs/fx's terminal monitors. A bot that starts a dev
server or kicks off a long job currently polls with repeated computer_exec
or screenshot calls — one model inference per peek. wait_for runs the whole
wait in ONE round trip: a 2s poll loop on the box (http_ready / tcp_ready /
output_matches / file_exists, bounded 1-240s), then the settled screen rides
back in the same result, matching the file's act-and-observe contract.

The schema is flat (enum + per-condition fields described in words) and bad
input answers with a copyable example instead of a wall — both per the
CONTRIBUTING "MCP tool schemas" rules from milind-soni#544. A timeout returns advice
(inspect with computer_exec) rather than an invitation to wait again, and
the preview poker learns the tool name so the panel refreshes.

Verified: 22/22 proxy contract tests (schema flatness guard, free-of-charge
guidance on bad input, single-round-trip with frame, timeout advice, bash -n
on every generated shell); mutation check (breaking the loop's sentinel
fails 3 tests); tsc clean; lint parity with main (14 = 14 findings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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