Skip to content

Saved workflows: arguments, launcher, Settings page and /workflow (stacked on #719) - #724

Open
katulevskiy wants to merge 50 commits into
zeronsh:mainfrom
katulevskiy:workflows-saved
Open

katulevskiy wants to merge 50 commits into
zeronsh:mainfrom
katulevskiy:workflows-saved

Conversation

@katulevskiy

@katulevskiy katulevskiy commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #719 (workflows card), which is stacked on #712, #708 and #707. Until those merge, this PR's diff also contains their commits; review git log workflows-card..workflows-saved (6 commits). It is independent of #723 (mobile), which is also stacked on #719.

Summary

Saved (reusable) workflows with typed arguments. A saved workflow is a Starlark file with a frontmatter comment block, kept in <project>/.zeron/workflows, ~/.zeron/workflows or built in. Agents list, start and save them through MCP; people use Settings → Workflows, a launcher with a generated args form, /workflow, and "Run again" / "Save as workflow…" on the cards.

Screenshots

Captured from the real desktop app on an isolated fixture against the mock harness (scripted agents, not real models). The Settings paths are the fixture's temp directories.

Settings → Workflows: Global, per-project (with an override and an unusable file) and Built-in

Settings list

Detail view: description, arguments, highlighted script, recent runs

Settings detail

Recent runs of one workflow

Run history

Launcher: generated args form with a validation error

Launcher

/workflow in the composer

Slash popup

Approval for a saved workflow, showing the name and argument values

Approval

"Save as workflow…"

Save dialog

Card header with the saved chip and Run again, and an ad-hoc run with Save as workflow…

Saved card
Ad-hoc card

Design

  • Format: a strict frontmatter grammar (name, description, when_to_use, args with string | int | number | bool | json, required, default, description). Every error carries line:col and all errors are reported at once; hostile input (huge files, duplicate keys, path traversal in names) is covered by tests. The parser lives in zeron-workflow; the shared types and argument rules (validate_args, slug names) live in zeron-proto so engine, desktop and mobile share one contract without linking the interpreter.
  • Scopes: project beats global beats built-in, and every list says who wins. Project folders are treated as untrusted: a symlinked folder or any non-regular file is refused, with size and count limits. The scan is on demand (no watcher: a list reads two small folders, so there is no cache to go stale).
  • Engine and MCP: start_workflow {saved: {name, scope?, args}} validates arguments before approval, and the approval shows the saved name, scope and values. save_workflow always asks (stock labels Save workflow / Deny), writes atomically inside the workflows folder only, and refuses to overwrite unless the question said it would. list_saved_workflows lists them. New RPCs WorkflowSavedList/Get/Save/Delete/Runs are forwardable, so a client addressing a chat's host lists the host's workflows (capability workflows-saved-v1).
  • Runs record savedName / savedScope in the header, WorkflowGet returns the run's args and saved, and resume still replays the stored copy, so editing the file afterwards doesn't matter.
  • UI: a person's launcher click, /workflow or Run again is the approval (there is no live agent turn to carry the question), so the launcher shows exactly what the approval would. The engine honours that byUser shortcut only together with saved, and the MCP tools never send it.
  • Built-ins: pr-review, fix-until-green, repo-audit, each run against the fake host in tests; the guide's frontmatter example is also executed in a test.
  • Details: docs/workflows.md, docs/workflows-ui.md, docs/workflow-guide.md, docs/mcp.md.

Testing

  • zeron-workflow 97, zeron-proto 88, zeron-mcp 40, zeron-rpc 16, zeron-engine lib 450 (2 ignored), zeron-ui lib 1523.
  • Engine integration: workflows 21, workflow_e2e 2, workflows_saved 21 (new), ask_child 16.
  • cargo check --workspace --all-targets clean; the new files are clippy-clean (older warnings elsewhere, e.g. in composer.rs, are untouched).

Not verified

  • A real model running a saved workflow (the screenshots use scripted mock agents).
  • The launcher's Run, the Save dialog's Save and Run again were exercised by gpui tests, capture knobs and RPC, not hand-clicked in the live app.
  • macOS, Windows, iOS and mobile (mobile does not list or launch saved workflows yet).
  • Limitations: delete-only (no in-app editor); the "new chat" launch target covers local projects only; approval shows argument values alphabetically, not in declaration order. Unrelated small fix included: list() prefers the store's status for a settled run when the doc projection lags behind (no dedicated test), and saved-workflow rows in the composer load apart from the provider catalog so a slow catalog can't leave /workflow on a skeleton.

Claude TodoWrite, OpenCode, ACP plans and Codex update_plan expose an
in-progress step that TodoItem flattened into done/not-done. Add a serde-
defaulted status (written only for in-progress, so existing docs are byte-
identical and old builds/edge ignore it; unknown values derive from done),
map it in every normalizer, and wire Codex turn/plan/updated into the live
plan chip.
The agent's checklist is now a tray stacked above the queue/composer: a
collapsed header (Todo 2/5 plus the current item), an expanded list with
check / active / pending glyphs, and a 3-item focus window with N earlier /
N later fold rows for lists over 6. Open state is remembered per chat for the
app run, a finished list tidies itself to a compact done state once the turn
is idle, and every control is keyboard reachable with tooltips. The
transcript chip's detail marks the in-progress item [~].
ZERON_MOCK_TODO walks an 8-item list through every panel state for live
checks; docs/todo-panel.md records the data model, behaviour and limits.
Child ask (engine::ask): hidden child chat under the requester, run-scoped
submit_result validated against a caller-supplied JSON Schema (boon), repair
rounds, nudge, typed failures, cancellation and archive-on-exit, plus a
FakeAsk backend. Goal mode: additive session-doc goal state, goal commands on
the command plane, machine-origin queue/message origin, pure state machine and
prompts, doc-host controller with restart recovery, and tests.
The expanded goal list was cut off by the todo tray tucked over it. Every
expanded list now keeps a bottom clearance equal to the tuck overlap (the todo
tray too, a one-line change in todo_panel.rs), the goal list's height share
shrinks as trays stack below it, and it follows the newest round. Retook the
goal-mode screenshots and added a stacked-trays one.
The in-process verifier entry was dropped when the verifier returned, but the
verdict was applied only after taking the controller lock. A tick (turn end,
queue watcher, status change) in that window saw 'verifying' with no verifier
and judged the same round again. Reserve the entry under the lock together with
the Verifying ledger write, release it under the lock after the verdict is
applied, and make a second start for the same (goal, round) a no-op.
…g; steadier tests; demo host ignores workflow commands
…der, no env reads per sync, no run clone per state change
…ples

The frontmatter parser/writer lives in zeron-workflow (strict, line:col errors, all
collected); types and the argument rules (validate_args, parse_text, slug names)
live in zeron-proto so every client shares them. Three built-in workflows
(pr-review, fix-until-green, repo-audit) run against the fake host in tests.
… with args, run history)

Saved files are scanned on demand from the project, the global folder and the
built-ins, with shadowing and per-file errors. start_workflow accepts saved
{name, scope, args}: arguments are validated before approval, the approval shows
the saved name and values, and runs record saved_name/saved_scope in their
header. Saving asks through the existing input-question mechanism and writes
atomically inside the workflows folder only. WorkflowSaved* RPCs are forwardable;
capability workflows-saved-v1.
…ved} MCP tools, guide section

The tools steer the model (prefer a saved workflow, save only with the user's
agreement); save always asks the user; an agent can never pass byUser. The guide
gains a Saved workflows section whose frontmatter example runs against the fake
host in tests.
…d args form, /workflow, Save as workflow and Run again

Settings → Workflows lists Global, per-project and built-in workflows (rescanned on
demand) with a detail view: arguments table, highlighted script, recent runs, Run /
Open file / Delete. The launcher turns a workflow's declared arguments into typed
fields (defaults prefilled, required markers, inline errors) and starts the run in the
open chat or a new chat in a project. /workflow lists saved workflows in the slash
popup and starts one with key=value arguments, opening the launcher when a required
argument is missing. Run cards show the saved name; ended runs offer Run again (same
arguments) or Save as workflow…. The approval block names the saved workflow and its
argument values.
… apart from the provider catalog

docs/workflows.md (saved workflows), workflows-ui.md (Settings, launcher, /workflow,
card actions), mcp.md; nine screenshots from the live app. The composer fetches the saved
workflows independently of the provider's command catalog, run lists trust a settled
run's store status over a lagging doc projection, and run history says how long ago.
@katulevskiy

Copy link
Copy Markdown
Contributor Author

Force-pushed: rebased the stack onto current main (80b946b, v0.2.101). CI was failing on the stacked PRs because the merged Cargo.lock had zeron-workflow at 0.2.100 while the workspace is now 0.2.101, so every --locked job stopped at once. The lockfile now differs from main only by our own packages. Also bumped SettingsSection::ALL for upstream's new Voice entry alongside ours. Verified locally with --locked: workflows_saved 21, workflows 21, zeron-workflow 97, zeron-mcp 40, zeron-ui 1611.

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