Skip to content

v0.11.0

Choose a tag to compare

@trevhud trevhud released this 30 Jul 04:17
08858a5

Added

  • Park-on-auth for Cloudflare Workflows — every MCP-capable runtime
    now parks.
    MCP-backed step.do calls wrap auth failures in
    NonRetryableError (Workflows has no should-retry predicate; a dead
    credential must not burn retries.limit worth of delay) and park
    the instance on a rote_auth_<server> waitForEvent with an
    explicit 30-day timeout — the default is 24 hours and expiry
    throws, failing the instance, which after 30 unreleased days is
    the intended outcome. Release has no broadcast on Workflows, but
    events sent before an instance reaches its wait are buffered
    per-instance
    , so rote mcp release <server> simply sends the
    event to every non-terminal instance via the Cloudflare REST API
    (CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID; in wrangler dev, use npx wrangler workflows instances send-event … --local).
    The credential fix on Workers is re-provisioning (rote mcp export

    • wrangler secret bulk), not a local login — the ROTE_MCP_TOKENS
      KV cache supersedes secrets, so rotated tokens keep working. Proven
      live on workerd (tests/test_mcp_park_cf_e2e.py): instance parks
      with nothing provisioned, ignores a wrong-server event, wakes on the
      real one, and completes — with the retried step driving the real
      @modelcontextprotocol/sdk streamable-HTTP client inside workerd
      against a live MCP server, closing the long-standing "typecheck
      only" gap for the Workers MCP output.
  • Park-on-auth for Inngest, released by broadcast. The Inngest
    adapter emits a runParkable loop around MCP-backed steps: auth
    failures are wrapped in NonRetriableError inside the step (so
    Inngest's per-step retry budget and managed backoff never burn on a
    missing credential), each attempt gets a fresh memoization-safe step
    id, and the run parks on step.waitForEvent for
    <pipeline>/rote.auth.<server>. Release needs no discovery: Inngest
    events fan out to every matching waiter, so rote mcp login /
    rote mcp release send one broadcast per registered pipeline
    (dev server by default, Inngest Cloud with INNGEST_EVENT_KEY,
    or ROTE_INNGEST_EVENT_URL). Because Inngest does not buffer events
    for waits that haven't started, the park loop retries once before
    waiting — a release landing in that gap fixes the credential store
    the retry then reads. Proven live against inngest-cli dev
    (tests/test_mcp_park_inngest_e2e.py): run parks, a wrong-server
    broadcast leaves it parked, the real broadcast wakes it, and it
    completes with live MCP data.

  • Park-on-auth for DBOS TypeScript, released from Python. The
    dbos-ts adapter now emits the same park the Python adapter got:
    MCP-backed steps throw RoteMcpAuthNeeded (typed error in the
    emitted _roteMcp.ts — thrown on dead tokens, failed refresh grants,
    and 401s the refresh can't fix), a shouldRetry predicate keeps auth
    failures out of the retry budget, and the workflow parks on
    DBOS.recv("rote:auth:<server>") with the wait advertised via a
    portably-serialized rote_auth_status event — the one format both
    DBOS SDKs read, which is what lets the existing Python release path
    serve TS apps unchanged (it now scans dbos-ts registry entries,
    derives the TS Postgres system-DB URL, and sends the release message
    in WorkflowSerializationFormat.PORTABLE). New rote mcp release <server> releases parked workflows without a login, for credentials
    fixed out-of-band. The full cross-language loop — TS parks, Python
    reads the event, Python releases, TS resumes against a live MCP
    server — is proven on a real Docker Postgres in
    tests/test_mcp_park_ts_e2e.py. Inngest and Cloudflare parks are
    next.

  • Park-on-auth: workflows suspend on missing MCP credentials instead
    of failing
    (DBOS Python). OAuth is interactive; durable workflows
    run unattended — so when an MCP-backed step finds its credential
    missing or dead (expired with no refresh token, a 401 from the
    server, or an OAuth flow demanding authorization — emitted code never
    opens a browser), it raises RoteMcpAuthNeeded and the workflow
    parks durably on a rote:auth:<server> topic, exempt from the retry
    budget and advertised via the rote_auth_status workflow event.
    rote mcp login <server> now finishes the loop: after a successful
    dance it scans the new app registry (~/.local/share/rote/apps.json,
    recorded by rote emit/rote compile; ROTE_APPS_PATH overrides),
    discovers workflows parked on that server, and releases them — the
    run picks up exactly where it stopped, with the fresh credential.
    Parallel-wave siblings retry once before parking so a stale auth
    failure can't strand a workflow after its release signal was already
    consumed. A workflow parked longer than 30 days times out loudly.
    Proven cross-process on the real DBOS runtime against a live MCP
    server (tests/test_mcp_park_e2e.py). The TS runtimes still fail
    loud on dead credentials — extending the park is a known follow-up.

Changed

  • rote graduate is now rote compile. The operation always was
    compilation: read a source artifact, classify its parts, lower the
    deterministic ones into typed code, keep a runtime for what genuinely
    needs judgment. graduate described what happened to the skill
    socially rather than what the tool does mechanically, and nobody
    reaching for this searches for it. Asked cold to name the operation,
    both Claude and Codex produce "compile" unprompted; the experiment is
    recorded in the rote-cloud repo under seo/reports/verb-experiment.json.

    The rename runs through the whole surface: the command, the
    rote.compiler package (was rote.graduator), Compiler /
    CompilerError / CompilationEvent, the skills/rote-compile agent
    bundle, the /rote:compile plugin skill, the <out>/compiled/
    artifact directory (was <out>/graduated/), the emitted
    compile-report.md (was graduation-report.md), and all docs.

    rote graduate still works. It dispatches to rote compile and
    prints a one-line notice on stderr. It is deliberately absent from
    --help: the alias exists so scripts written against 0.10.x keep
    running, not to offer a second spelling.

    Breaking, without an alias: the --json payload and progress
    sidecar now carry cloud.compilation_id instead of
    cloud.graduation_id, and artifacts land under <out>/compiled/.
    Anything parsing that output needs a one-word edit.

    Not renamed: the rote cloud HTTP routes (/v1/graduations) and
    the server response keys (graduation_id, active_graduation). Those
    name endpoints on a separately deployed platform; renaming them
    client-side would 404 against every released server. See the module
    docstring in src/rote/cloud_compile.py.