Skip to content

feat: local-only tutorial executor for chant run (no workflow runtime required) #73

@lex00

Description

@lex00

chant's Op composite currently requires Temporal for execution — chant run <op> spawns a Temporal worker and submits a workflow. That's the right shape for production deploys, but it's a real barrier in the Quick Start: a new user has to install Temporal (or sign up for Temporal Cloud) before they can run chant run hello.

A small local-only executor closes that gap without competing with Temporal. Single Node process, no durability across restarts, no gates, no schedules — just enough to run *.op.ts files end-to-end so the tutorial works in 30 seconds.

The graduation story: tutorial → local runner; production → Temporal lexicon (or, eventually, Inngest / Restate / etc.). Two clearly-scoped offerings, neither watered down.

Approach

A new mode for chant run that parses the Op directly from *.op.ts (chant already builds an Op IR during chant build) and executes phases sequentially in the current process. No codegen output, no SQLite, no worker, no daemon. Activated by chant run --local <op> and auto-detected when no Temporal lexicon is configured in chant.config.ts.

Lives in packages/core/src/runtime/local/. Reuses pre-built activities from the Temporal lexicon (shell, build, kubectlApply, helmInstall, stateSnapshot, stateDiff, etc. — they're plain Node functions; only the workflow side was Temporal-coupled).

Tasks

  • packages/core/src/runtime/local/executor.ts — walks the Op IR, runs phases sequentially (or in parallel when phase.parallel === true)
  • Activity invocation — call the activity function directly with declared args; honour outcomeAttribute by storing the result in an in-memory map keyed by __r0/__r1 and surfacing it to stdout (no search-attribute persistence)
  • Retry policy — honour the activity profile's maximumAttempts / startToCloseTimeout in-process (setTimeout for backoff, Promise.race for timeout)
  • onFailure handling — try/catch around main phases, run onFailure phases on rejection, re-throw at the end
  • Progress output — phase-name banners, per-step status, final summary. No UI; structured stdout is the surface.
  • chant run --local <name> flag wiring in packages/core/src/cli/handlers/run.ts
  • Auto-detect: if chant.config.ts has no Temporal lexicon configured, default to local mode and print a one-liner explaining the choice
  • Tests — unit tests for the executor against fixture Op IRs; smoke test for an existing shell-only Op running end-to-end without Temporal
  • Docs — new section in guide/ops.mdx ("Try it locally without Temporal") and a callout in Quick Start; explicitly document what's NOT supported (gates, schedules, durable cross-process state)

Done when

  • A user with chant installed but no Temporal can chant init → write hello.op.tschant buildchant run hello and see it work in under a minute
  • Existing Temporal-backed Ops continue to work unchanged (no regression)
  • Docs make the local-vs-Temporal trade-off explicit
  • Test suite covers happy path + onFailure + retry

Out of scope (intentionally)

  • Gates (gate() step) — require persistence across processes. Local mode errors with "use the Temporal lexicon for gate-based workflows."
  • Schedules (TemporalSchedule) — require an external scheduler. Same error.
  • Durable state across process restart — explicitly not supported; if the process dies mid-run, you start over.
  • Search attributes / chant run history — local mode has no persistence layer to query.
  • Worker pools / cross-process parallelism.
  • Web UI — stdout is the UI.

Why this and not a full internal runtime

A full internal runtime (SQLite-backed, durable, with gates and schedules) would be technically interesting but breaks chant's stated boundary (philosophy.mdx: "chant doesn't deploy"). It also imports permanent maintenance of state-machine edge cases that Temporal has spent years solving. The local executor is deliberately scoped to be a teaching tool, not a production runtime — anyone going to prod uses the Temporal lexicon (or eventually Inngest / Restate / etc. as additional lexicons).

Why now

The audit thread (#21#67) settled chant's observational-state story. The remaining friction in the Quick Start is the Temporal install. Closing that gap unlocks the tutorial path without committing chant to maintaining a real durable runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions