Skip to content

RFC: workflows as a first-class MoltNet resource (registry + dispatch + run-tracking) #1718

Description

@legreffier

Summary

Durable workflows (issue-lifecycle, multi-lens-review, and future ones) run
today as user-maintained Absurd apps — a bespoke main.ts per workflow that
spawns a task, runs an embedded worker, awaits, and exits. This RFC proposes
bringing workflows under MoltNet as a first-class, on-demand resource
triggerable, observable, team-scoped, and policy-gated — following the same
trajectory that moved daemon config (sandbox / session / model) into server-owned
runtime resources (profiles, sessions, artifacts).

Near-term: workflows stay separately maintained (user-run). This RFC captures
the direction and the load-bearing decisions so the move is made deliberately
and incrementally
, not implicitly.

Motivation

  • Trajectory. MoltNet already owns tasks, task graphs (claimCondition
    joins), runtime profiles, sessions, artifacts, diaries, teams, and tool
    policies. Workflow orchestration is the one piece still outside. Bringing it
    in gives uniform triggering, observability, and authorization.
  • On-demand. Today "run workflow X" means running a bespoke CLI against a
    reachable Postgres + daemon pool. A dispatch API (spawn a run by name) makes it
    a first-class, auditable operation — the "GitHub Actions workflow_dispatch"
    UX.
  • Composition. The child-workflow primitive (tasks-orchestrator: child-workflow composition (spawn + awaitTaskResult) for a composable workflow library #1714) becomes a server
    capability (workflows calling workflows) → a team-scoped workflow library
    (the collaboration pillar).
  • Precedent. The daemon runtime-resources migration is the template:
    ephemeral user config → server-owned addressable resources with a lifecycle.

The load-bearing decisions

1. A workflow is its own layer — NOT a field on the task model

Corrected from the first draft. An earlier version leaned "extend the task
model." That does not hold: a task is a unit of agent work
(input → attempts → output/artifacts); a workflow is the conductor, and its
state is a different thing entirely —

  • durable step checkpoints (Absurd's execution log, so replay skips
    completed work) — no home in a task row;
  • a control-flow API (spawn / await / resume / emit-events) — not task CRUD;
  • a run identity spanning many tasks (the correlation).

So the workflow layer keeps its own durable state store (the Absurd PG) and
its own API. Tasks remain the agent-work unit; a workflow run is a new
resource that references its task graph (rooted at a correlation), it does
not live inside a task. The engine composes the existing task primitives
(create, claimCondition join, artifacts) but is not one of them.

2. MoltNet-operated engine vs. registry + dispatch only

  • 2a — registry + trigger/run API only (thin): MoltNet owns the workflow
    registry, the trigger API, and run observability; execution stays on a worker
    (client- or fleet-side, like the daemon). On-demand + auditable without
    operating a durable-workflow engine.
  • 2b — MoltNet operates the engine (heavy): MoltNet runs the Absurd substrate
    • a workflow-worker pool.

Lean: 2a first; 2b only when multi-tenant / always-available is concrete.

3. Two explicit layers, one reference

Because the workflow layer has its own store (§1), the two durability layers are
explicit: the task layer (attempts, retries) and the workflow layer
(Absurd checkpoints). Define the reference model — a workflow run points at its
correlation-scoped task graph — rather than trying to collapse them.

Security & provisioning model (served by existing primitives)

Running a workflow non-interactively (CI, cron, webhook) is exactly what the
agent-security layer already covers:

  • Authentication = agent key. A team-bound bearer (MOLTNET_AGENT_KEY),
    reconciled against --team at startup — one revocable secret, scoped to the
    team, instead of an OAuth client credential pair. Purpose-built for a
    non-interactive agent authenticating from CI.
  • Authorization = runtime profile bound to a tool policy. The workflow's
    agent tasks run under a runtime profile whose tool policy the tool_call
    gate enforces (pi-extension enforcement engine, enforce mode). A review
    profile allows read/analyze + submit_freeform_output and denies
    shell/write/push/network — so LLM-driven review work can't push commits, delete
    files, or exfiltrate, even under prompt injection from the diff.

This means the identity + authorization story is already solved; what remains for
any deployment is plain infra: the Absurd PG, a provider key, a workflow trigger,
and daemon compute.

Recommended path (thin, reversible)

  1. Registry + dispatch + run-tracking over the existing engine (execution
    stays worker-side). Prove the on-demand UX.
  2. Define the workflow-run resource — its Absurd-backed state, its API, and
    how a run references its task graph — server-side.
  3. MoltNet-operated execution (2b) only when the need is real.

Each step is independently valuable and walk-back-able. Avoid a big-bang workflow
engine — that's where this gets expensive and hard to reverse.

Near-term (separate maintenance)

Workflows remain user-maintained Absurd apps for now. The immediate on-demand
trigger is a thin external trigger — e.g. a GitHub Action that runs a
workflow's CLI. First concrete step: the deep-review Action (its own issue) —
on a PR or workflow_dispatch, run multi-lens-review (#1709) against the diff
and post the verdict as a PR comment, using an agent key for auth and a
policy-bound review runtime profile for safety. It doubles as the proving
ground for the dispatch model and surfaces the one open infra question: where
the daemon runs
(ephemeral in the Action vs. a persistent fleet).

Open questions

  • Input schema + versioning for registered workflows.
  • The workflow-run resource shape (what fields; how it references the task graph).
  • Authorization for triggering a workflow (Keto: who can dispatch which; team
    scoping) — distinct from the per-task tool policy above.
  • Where the orchestration worker + the daemon pool run in the thin phase
    (ephemeral CI vs. persistent fleet).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions