Coding Agent workflow
Flowman's primary workflow starts a coding session, optionally plans and waits for human approval, then runs implementation and follow-up chat. One workflow implementation (src/workflows/coding-agent.ts) serves every Coding Agent run. Projects create child workflows (derived templates) to preconfigure fields and give each use case its own slug, card, and run history — without duplicating workflow code.
How a run flows
Whether a run starts from the UI, an email trigger, or another connector, the path is the same. The HTTP route is always /api/workflows/coding-agent. The optional workflowSlug in the request body selects which workflow record owns the run.
UI / trigger dispatch
→ POST /api/workflows/coding-agent (body may include workflowSlug)
→ beforeOpenRun hooks (prompt template merge, conflict guard)
→ Convex openRun (records run under child slug, merges promptTemplate)
→ Workflow SDK start(codingAgent)
→ plan session OR direct implementation
→ local runtime polls Convex OR provider cloud node actionWorkflow code path
- Route handler —
src/app/api/workflows/coding-agent/route.tsusescreateWorkflowRouteto open a Convex run, snapshot project env vars, and callstart()from the Workflow SDK. - Orchestrator —
codingAgent()insrc/workflows/coding-agent.tsvalidates input, resolves the repository, then either opens a planning session (default) or jumps straight to implementation whenplanModeis false. - Step actions — shared steps in
src/workflows/shared/actions/coding-agent.tscreate Convex coding sessions, bind approval webhooks, and wait for plan decisions before queuing implementation. - Execution — local runs are polled and executed by a self-hosted coding-agent runtime (
pnpm runtime:coding). Provider cloud runs are driven directly from Convex viaconvex/cursorCloudNode.tsorconvex/openAiCloudNode.tsand never touch a runtime environment.
Plan mode vs direct start
When planMode is true (the default), the workflow opens a planning session, pauses for clarification questions or plan approval on the Approvals page, then queues implementation after approval. When planMode is false, the workflow skips planning and opens a single implementation turn immediately.
Before a local planning session starts, the runtime refreshes the repository's base branch so the plan is written against current code rather than a checkout left behind by an earlier run. Uncommitted changes fail the session before the agent starts, unless the session already carries an approved dirty worktree — those only refresh origin/<base> and leave the checkout untouched. See Branch management for the isolated versus current-branch rules, including the repository-level agent-managed opt-out for local sessions.
After implementation completes, the workflow run closes. Follow-up chat turns reuse the same coding session thread but do not reopen the workflow run.
Child workflows (derived templates)
A child workflow is a project-scoped workflow record derived from the base coding-agent workflow. It shares the same route (/api/workflows/coding-agent) and the same orchestrator code, but has its own slug, name, description, category, visibility, parameter defaults, and field visibility.
Creating a child workflow
- Open Workflows in the project and click New Workflow.
- Choose a name and optional slug. Flowman copies all Coding Agent parameters from the base workflow via
createCodingAgentTemplatein Convex. - For each parameter, choose whether it stays visible in the run modal or is hidden with a saved default. Hidden fields are still submitted when the run starts.
- Required fields cannot be hidden unless they have a valid default. Attachments cannot be saved as defaults because uploads are per-run.
Examples: a Plan New Feature template might expose only repository and prompt while defaulting runtime environment, provider, plan mode, and branch mode. A Cursor Cloud Fix template might hide agentProvider=cursor and location=cloud so users only pick a repository and describe the bug. Flowman also insert-only seeds a Cursor Local child (cursor-local, agentProvider=cursor) and a Claude Local child (claude-local, agentProvider=claude), each with its provider and location=local hidden and the provider's default model preselected; the base coding-agent workflow serves as Codex Local. Existing projects receive Claude Local the next time the Workflows page loads. Existing workflows with either slug are left unchanged.
Slug vs route: how runs are recorded
The route handler's factory slug is always coding-agent, but callers pass the child slug in the request body. Convex openRun looks up the workflow by that slug, verifies it belongs to the project and that its route matches the request path, then stores the run under the child workflow's workflowId. Trigger dispatch preserves the matched workflow slug automatically (triggers docs).
POST /api/workflows/coding-agent
Content-Type: application/json
{
"projectSlug": "my-project",
"workflowSlug": "debug-issue",
"input": {
"repositoryId": "codingRepositories_...",
"runtimeEnvironmentId": "runtimeEnvironments_...",
"agentProvider": "codex",
"location": "local",
"prompt": "Fix the flaky test in auth.test.ts"
}
}Editing child workflows
Use the pencil action on a child workflow card to edit name, description, category, public/private visibility, exposed fields, and saved defaults. The slug stays fixed. Only child workflows can be edited this way — the base coding-agent workflow is seeded and not editable through the template editor. See Organize workflows for categories, visibility, and prompt-template distinctions.
Design mode PRDs
Child workflows can save hidden designMode as true with planMode still true. Flowman then uses the normal clarification loop but asks the agent for a Markdown PRD/design document. The ready state opens Review Design, and Finalize PRD completes the run without queuing implementation.
Prompt template wrapping
Every Coding Agent workflow (base and child) carries a hidden promptTemplate parameter. At run start, Flowman replaces {prompt} with the user's submitted prompt before the orchestrator and stored run input see it. The template default is {prompt} (pass-through). promptTemplate is stripped from persisted input after merging.
Hidden promptTemplate default:
"Debug the following issue: {prompt}"
User submits prompt:
"Fix the flaky test in auth.test.ts"
Stored run input.prompt (what the agent receives):
"Debug the following issue: Fix the flaky test in auth.test.ts"This is separate from project prompt templates under Build → Prompts, which help users compose the prompt field in the run modal. Child workflow prompt templates wrap that text automatically at runtime.
Coding providers
Workflow input agentProvider selects the coding backend. Flowman supports three providers today: Codex, Cursor, and Claude Code.
Each provider and location pair is a Coding Agent Integration. Its capability contract drives model choices, supported tuning, repository and runtime requirements, attachments, project environment delivery, and git behavior. Workflow callers do not forward those capabilities individually.
Codex
- Runtime: local or OpenAI cloud, selected with
location. - Executor: self-hosted coding-agent runtime running the Codex app-server against a local repository checkout (or an ephemeral temp directory when no repository is configured).
- Requirements:
runtimeEnvironmentId, runtime host withCODING_AGENT_RUNTIME_TOKEN,FLOWMAN_ENVIRONMENT, and Convex URL, or an enrolled workstation credential instead. OptionalCODEX_BIN/CODEX_MODELoverrides on the runtime host. - Tuning: supports
model,reasoningEffort, andspeed(Codex service tier). New runs default to GPT-5.6 Sol with High reasoning. Sol, Terra, and Luna support Low through Max locally; GPT-5.5 and GPT-5.3-Codex-Spark keep Low through Extra high. - Env vars: repository commands start from an isolated OS and tooling baseline, then receive the immutable run-start project environment snapshot. Flowman runtime credentials and deployment selectors are not inherited. The same snapshot is reapplied when Flowman resumes the thread, while local skills remain available through the retained home, path, Codex home, and SSH agent settings. Skills that depend on environment-only credentials must receive them through project environment variables.
- Previews: preview commands use the same isolated baseline and run-start project environment snapshot as repository commands.
- Branch / PR: full local git branch management, dirty-worktree protection, Commit to PR via
gh, and preview sessions through zrok — unless the repository uses agent-managed branching, in which case Flowman does not commit, push, or create pull requests itself; its run-detail shortcuts ask the agent to do so through the repository's own skills. - Agent-managed worktrees and PRs: after each turn the runtime reads which checkouts the agent edited (edit tool paths and command directories), maps them to the workspace's registered repositories through their shared git directory, and records each worktree's branch on the run. This is read-only; Flowman never creates, switches, or removes those worktrees. Pull requests are then linked automatically: the GitHub
pull_requestwebhook (opened, reopened, ready for review, closed) matches the PR's head branch to a recorded worktree, and a lookup through the project's GitHub App runs whenever a new branch is recorded, so missed webhooks are recovered. Only separate worktrees are matched, never the registered checkout, so a release PR fromdevelopis not attributed to a run. Worktrees must be observed within the last 30 days to link. - Changes on the run page: project members can expand Changeson a coding run to see each repository's file tree and diffs, loaded from GitHub through the project's GitHub App. A repository with a linked pull request shows that PR's files with review comments; one whose branch is pushed but has no PR yet (Flowman's pushed working branch, or a worktree branch observed for an agent-managed run) is compared with its base branch, up to GitHub's 300-file compare limit.
- Local snapshots: after each local implementation or follow-up turn, and on Refresh worktree, the runtime records the checkouts the turn touched, including the turn's working directory (a Flowman-managed run always includes its own checkout), and snapshots each one; failed turns are captured too once the agent has run. Each snapshot covers everything since the merge base with its base branch, including uncommitted and untracked files. It uses a temporary git index, so the checkout's index, files and branches are not changed; gitignored files and the repository's worktree copy paths (for example
.env.local) are never included. Only the latest snapshot per checkout is kept, with per-file patches up to 512 KiB and 5 MB in total. The run page shows a snapshot for repositories with no PR or pushed branch, offers it beside GitHub otherwise, and flags uncommitted files or unpushed commits that GitHub does not show yet. Snapshots stay readable while the runtime machine is offline. A snapshot of a registered checkout that the run shares with other work is labelled as such.
Codex — OpenAI cloud
- Uses background Responses with resumable event streams, one durable OpenAI conversation, and reusable Hosted Shell containers. Planning receives a separate read-only container; approval starts a fresh implementation container that follow-ups reuse while it remains active.
- New runs default to
gpt-5.6-solwith High reasoning. GPT-6 Astra, Terra, and Luna are also available. GPT-5.6 supports None, Low, Medium, High, Extra high, and Max in OpenAI cloud;gpt-5.5keeps Low through Extra high. - GPT-6 Astra (
gpt-6-astra) supports Low, Medium, High, Extra high, and Max reasoning in both local Codex and OpenAI cloud, with Standard or Fast processing. Astra API projects with EU data residency must use Standard processing. - Existing
gpt-5.6selections remain valid and continue to use OpenAI's Sol alias, but the alias is hidden when choosing a new model. Flowman does not expose GPT-5.6 Pro mode. - Requires a canonical GitHub repository URL, project env var
OPENAI_API_KEY, and a connected Flowman GitHub App with contents and pull-request write access. It never uses ChatGPT or Codex subscription credentials. - GitHub, the npm registry, and the Convex management/version APIs are included in every outbound allowlist. Optional comma-separated domains may be added with
OPENAI_CLOUD_ALLOWED_DOMAINSand must also be approved in the OpenAI organization allowlist. OpenAI Hosted Shell does not expose unrestricted or wildcard outbound access: an organization admin must enable container networking and approve every requested domain. The complete project environment snapshot is uploaded into each Hosted Shell container for agent commands; sensitive values are redacted from persisted output. - Project variables named
CONVEX_DEPLOYMENTor ending in_CONVEX_DEPLOYMENTautomatically add the corresponding<deployment>.convex.clouddomain for values such asdev:chatty-ibex-315. - Images are forwarded by public URL; non-image attachments remain stored in Flowman. Implementations use a deterministic
codex/...branch and must commit and push before Flowman creates or updates a PR.
Cursor
Cursor supports both local and cloud execution via the location field (defaults to local). Codex uses the same location field for OpenAI Hosted Shell.
Cursor — local
- Executor: self-hosted coding-agent runtime using
@cursor/sdkagainst the registered repository path on the runtime host. - Requirements:
runtimeEnvironmentIdandCURSOR_API_KEYon the runtime host. OptionalCURSOR_DEFAULT_MODELwhen workflow input omits a model. - Models: shared Cursor catalog with Composer 2.5 (default), Cursor Grok 4.7, Cursor Grok 4.6, Cursor Grok 4.5, Auto, and the expanded Claude / GPT / Gemini / Kimi / GLM IDs listed in the Cursor model matrix below. Selected IDs are submitted unchanged.
- Behavior: same branch management, conflict guards, Commit to PR, and preview flows as Codex local runs, including the agent-managed opt-out. Reasoning effort is available for supported Cursor models and is sent as Cursor
effortorreasoningparams. Speed / service-tier controls remain unavailable. - Env vars: the run-start project environment snapshot is applied to the coding-agent runtime process while a Cursor Local thread starts or a turn runs, so local tools and skills can read those keys. Keys prefixed with
CURSOR_are excluded. Cursor Local calls that apply process env run one at a time so concurrent turns cannot inherit another project's secrets. Flowman restores the previous process values when the call ends.
Cursor — cloud
- Executor:Cursor's cloud infrastructure. Convex schedules node actions that drive the Cursor SDK directly — no self-hosted runtime polls or executes the session.
- Input: set
agentProvider: "cursor"andlocation: "cloud".runtimeEnvironmentIdandbranchModeare ignored. - Repository: requires a configured GitHub URL on the repository record. Cursor clones that URL into an isolated VM and may auto-create a pull request.
- Env vars: the run-start snapshot of project environment variables (
record.env) is injected into the cloud VM. Keys prefixed withCURSOR_are excluded. - Concurrency: cloud runs skip the local repository conflict guard — multiple cloud sessions on the same repository can run in parallel because each gets its own VM.
- Attachments: images are sent as public Convex file URLs. PDF and text attachments are stored in Flowman but not forwarded to Cursor Cloud.
- Models: same shared Cursor catalog as local Cursor — Composer 2.5 remains the default when no model is specified; Composer 2.5, Cursor Grok 4.7, Cursor Grok 4.6, Cursor Grok 4.5, and Auto stay available; newly added Claude / GPT / Gemini / Kimi / GLM IDs appear with the ticket display names. Supported models expose Reasoning effort (including Max where Cursor provides it); Composer/Auto do not. Service-tier / Speed controls remain unavailable.
- PR completion:cloud runs do not queue the local "complete PR" skill; they finalize from GitHub webhooks like local runs. Cursor Cloud polyrepo runs reconcile structured branch data, final-summary PR URLs, and—when a GitHub App is connected—a bounded workspace branch lookup. While an implementation is active, Check agent status offers a one-shot, idempotent recovery if the provider finished after the stream drain stopped; it does not poll in the background.
Claude Code
- Runtime: local only (
agentProvider: "claude"). A cloud location is rejected when the run starts. - Billing:runs on the runtime host's Claude Code login, so turns use that account's Claude subscription (Pro or Max) rather than per-token API billing. Flowman withholds
ANTHROPIC_API_KEYandANTHROPIC_AUTH_TOKENfrom Claude Code and fails a turn whose login still reports API-key authentication (anapiKeyHelperor Console login). Anthropic does not allow third-party products to offer claude.ai login, so run each host on its owner's own Claude Code login. - Requirements:
runtimeEnvironmentIdand a signed-in Claude Code on the runtime host: runclaudeand use /login with your Claude.ai account, or setCLAUDE_CODE_OAUTH_TOKENfromclaude setup-tokenon headless hosts. Because Claude Code is not sandboxed, its commands can read the host login (the token or the saved~/.claudecredentials); Flowman redactsCLAUDE_CODE_OAUTH_TOKENfrom every saved Claude Code output. The runtime uses the Claude Agent SDK's bundled Claude Code unlessCLAUDE_BINpoints at another executable. Flowman skills (for example the pull-request completion skill used by Commit to PR) must be linked into~/.claude/skillsso Claude Code can load them. - Models:
opus(default),sonnet, andhaikualiases resolve to the newest model the plan allows; pinned Opus 5.5, Fable 5.1, Sonnet 5, and Haiku 4.5 are also offered, and custom model IDs pass through. Reasoning effort (Low, Medium, High, Extra high, Max) maps to Claude Code effort; Speed is unavailable. - Behavior: same branch management, conflict guards, Commit to PR, and preview flows as Codex local runs, including the agent-managed opt-out. Planning turns run in Claude Code plan mode with read-only tools; a plan submitted with ExitPlanMode is captured for review without leaving plan mode. Planning and read-only turns deny MCP tools unless the project env var
FLOWMAN_CLAUDE_READ_ONLY_MCP_TOOLSlists them, as comma-separated exact tool names (mcp__server__tool) or whole-server wildcards (mcp__server__*); Claude Code settings cannot pre-approve others around this list. List only tools that cannot mutate data. Jira refinement and investigation turns run shell commands in Claude Code's sandbox with the repositories write-denied; network traffic goes through the sandbox proxy, local listeners are allowed so clients such as mongosh can tunnel through it, and direct DNS is blocked, so a skill that connects with amongodb+srv://URI must resolve its SRV records another way (for example over DNS-over-HTTPS) or use a seed-listmongodb://URI. Implementation and follow-up turns auto-approve edits and commands and are not sandboxed by Flowman. The user and repositoryCLAUDE.md, skills, and settings load as in an interactive session. Threads resume from Claude Code's transcripts on the runtime host; a missing transcript starts a fresh thread. - Env vars:the run-start project environment snapshot becomes the Claude Code process environment on top of the runtime's safe tooling baseline. Keys prefixed with
ANTHROPIC_orCLAUDE_CODE_are excluded so a project cannot switch Claude Code to API billing or another endpoint.
Provider × location matrix
| Provider | location | Executor | Runtime env required | Repository checkout |
|----------|----------|-----------------------|----------------------|----------------------------|
| codex | local | Self-hosted runtime | Yes | Local path on runtime host |
| codex | cloud | OpenAI Hosted Shell | No | OpenAI container clones GitHub |
| cursor | local | Self-hosted runtime | Yes | Local path on runtime host |
| cursor | cloud | Cursor Cloud (Convex) | No | Cursor VM clones workspace GitHub URLs (standalone or full polyrepo set) |
| claude | local | Self-hosted runtime | Yes | Local path on runtime host |Codex model matrix
| Integration | Models offered for new runs | Reasoning efforts |
|--------------|-----------------------------------------------------|-------------------------------------------|
| Both | gpt-6-astra | low, medium, high, xhigh, max |
| Codex local | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna | low, medium, high, xhigh, max |
| OpenAI cloud | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna | none, low, medium, high, xhigh, max |
| Both | gpt-5.5 (plus gpt-5.3-codex locally) | low, medium, high, xhigh |Cursor model matrix
| Integration | Default | Models offered for new runs |
|---------------|---------------|---------------------------------------------------------------------------------------------|
| Cursor local | composer-2.5 | composer-2.5, grok-4.7, grok-4.6, grok-4.5, auto, plus the expanded Cursor catalog below |
| Cursor cloud | composer-2.5 | Same shared Cursor catalog: existing choices retained, plus Claude/GPT/Gemini/Kimi/GLM IDs |
Expanded Cursor catalog (exact IDs; shared by local and cloud):
composer-2.5, grok-4.7, grok-4.6, grok-4.5, auto,
claude-opus-5, claude-opus-4-8, gpt-5.6-sol, gpt-5.5, claude-fable-5, claude-sonnet-5,
gpt-5.6-terra, claude-sonnet-4-6, composer-2, gpt-5.3-codex, claude-opus-4-7, gpt-5.4,
claude-opus-4-6, claude-opus-4-5, gpt-5.2, gpt-5.6-luna, gemini-3.6-flash, gemini-3.1-pro,
gpt-5.4-mini, gpt-5.4-nano, claude-haiku-4-5, claude-sonnet-4-5, gpt-5.1, gemini-3-flash,
gemini-3.5-flash, claude-sonnet-4, gpt-5-mini, gemini-2.5-flash, kimi-k3, kimi-k2.7-code,
glm-5.2
Cursor reasoning effort is model-specific (for example Opus/Fable/Sonnet Max,
GPT Extra high / Max, Grok 4.7 and Grok 4.6 low/medium/high/xhigh, Grok 4.5
low/medium/high). Grok 4.7 is submitted as Cursor's 500k-context variant.
Composer and Auto have no effort control. Cursor Cloud requires a complete
id+params variant (context/fast/thinking companions included); Flowman fills
those from Cursor's default variant when sending a selected effort.
Cursor still does not expose service-tier / Speed controls in Flowman.
Codex-only catalogs are unchanged.For Codex and Cursor, location: "cloud" removes the local runtime requirement. Cloud runs require a registered repository with a canonical GitHub URL; Codex cloud additionally enforces the OpenAI key, GitHub App, and Hosted Shell model prerequisites above.
Project defaults
Under Project settings → Coding agent, admins can set default provider, model, reasoning effort, and speed. These apply when a run or child workflow does not override them. Legacy codexDefaults project fields still resolve as implicit Codex defaults.
Runtime environments
A runtime environment is a named, project-scoped target for local execution. Each environment maps repository records to filesystem paths on a specific host; paths may be absolute or home-relative (~/git/repo), which the runtime expands to its own home directory. The runtime also registers that home directory (homeDirectory), so Convex hands every runtime build absolute paths for that environment. One coding-agent runtime process should run per environment/VPS pair in production; the runtime heartbeats to Convex and polls for queued turns scoped to its FLOWMAN_ENVIRONMENT value.
Runtime metadata can advertise which providers the host supports (codingAgentProviders: ["codex", "cursor"]). Cloud sessions never select a runtime environment.
Enrolling a workstation
In Settings → Runtime environments, chooseAdd workstation on an environment. Flowman shows a one-time command, valid for 15 minutes:pnpm runtime:coding:enroll --code fre_… --convex-url https://…. Run it from the flowman-web checkout on the new host. It trades the code for a credential that belongs to that machine alone and writes two files under~/.config/flowman(or$XDG_CONFIG_HOME/flowman,$FLOWMAN_CONFIG_DIR):runtime.jsonwith the Convex URL and environment slug, andcredentials.jsonwith the credential, readable by the owner only. The host then needs no.env.localand never holds the web app's secrets.
Values in the process environment take precedence over the enrollment, so a leftoverCODING_AGENT_RUNTIME_TOKENin.env.localstill wins; the enroll command warns when it finds one. Each enrolled machine is listed under its environment with when it last connected. Revoking one stops that machine immediately and leaves other machines and the environment token working. Rotating the environment token does not affect enrolled machines, and deleting the environment removes their credentials. Enrollment fails with a clear error when the code is wrong, expired, or already used, or when the environment is disabled or already has 25 enrolled machines.
Without a.env.local, a host keeps its other settings in an optionalenvobject: non-secret settings such asFLOWMAN_WORKTREE_ROOTinruntime.json, and host secrets such asCURSOR_API_KEYincredentials.json. The runtime and the doctor apply them beneath the process environment, and re-enrolling keeps them.
A new runtime environment serves no repositories. On a repository's edit page, tick each environment whose host has the checkout, optionally with a host-specific path. Unticking an environment removes its location, so the doctor only checks the checkouts a host is meant to have. A repository created through the API without a list of environments is registered only when the project has exactly one environment. Each repository card shows a pill for every enabled environment with a path for it (or No runtime), so repositories a new host still lacks stand out.
Max turns on each runtime environment caps how many coding turns it runs at once (1–20). Edit it on the environment in settings; a running runtime picks up the new limit on its next heartbeat, within 30 seconds.
Diagnosing a runtime host
Run pnpm runtime:coding:doctor before starting the runtime on a new macOS or Linux machine. The command is read-only: it checks Node, pnpm, Git, workspace dependencies, where the runtime connection comes from (.env.local, the process environment, or the enrollment), and the environment slug and token or enrolled credential against Convex, then verifies every repository location registered for the environment: the path must exist, be the root of a Git checkout, and fetch from its remote without prompting for credentials. The runtime never clones, so a missing checkout or a failing fetch is a hard failure. Failures produce a non-zero exit code; GitHub CLI, zrok, mongosh, Playwright Chromium, and SSH-agent gaps remain warnings because they affect optional capabilities.
On macOS the doctor also checks whether the login keychain is readable in the current session. Claude Code and the GitHub CLI keep their logins there, and it is locked over SSH or before anyone logs in, so both look signed out from such a session and a runtime started there cannot use them. Start the runtime from Terminal in a logged-in desktop session, or give Claude Code a CLAUDE_CODE_OAUTH_TOKEN from claude setup-token so it does not depend on the keychain.
Set FLOWMAN_CODING_PROVIDERS (for example cursor) on hosts that should serve only one provider. The runtime advertises that list as its codingAgentProviders and the doctor skips the other provider's checks. With Codex enabled it verifies the CLI, app-server login, and the complete Flowman skills checkout; with Cursor enabled it verifies CURSOR_API_KEY and loads @cursor/sdk in a child process so a missing native addon surfaces before the first session. With Claude Code enabled it runs claude auth status without API credentials: a subscription login passes, a signed-out or missing Claude Code warns, and an API-billed login fails.
The skills checkout defaults to $HOME/git/skills/skills. Set FLOWMAN_SKILLS_REPO to the Git repository root when it lives elsewhere. Every immediate child containing SKILL.md must be enabled in Codex and resolve through its symlink back to that checkout.
Worktree runs
Local builtin runs normally share the repository's registered checkout, so Flowman allows only one active Implementation or Review session per repository. Choose the Worktree branch mode (branchMode: "worktree") to give a run its own git worktree instead: several worktree runs, and one shared-checkout run, can then implement on the same repository at the same time. Worktree mode is available for standalone repositories with Flowman-managed branching on a local runtime; cloud runs, agent-managed repositories, and polyrepo workspaces keep their existing behavior.
When starting a local run from a Refined Jira ticket, the drawer shows a Run in a worktree checkbox under Start asfor eligible repositories. It starts checked when the start template's branch mode defaults to Worktree, and unchecking it runs in the registered checkout instead. For repositories that cannot use a worktree, a template default of Worktree falls back to an isolated branch rather than failing the start.
On the run's first turn (planning included) the runtime fetches origin/<base>, adds a detached worktree at that tip, copies the repository's Files copied into worktrees from the registered checkout, and runs its Worktree setup command (for example pnpm install --frozen-lockfile) with the project environment. Copy and setup rerun on the next turn until they succeed once. Keep copied files and setup output gitignored, otherwise the dirty-worktree gate pauses the run. The implementation turn then cuts the feature/<slug>working branch inside the worktree, exactly like isolated mode, and every later turn, sandbox root, dirty-state check, and preview uses the worktree path shown in the run's context panel.
Worktrees live on the runtime host under FLOWMAN_WORKTREE_ROOT (default /Volumes/ExternalSSD/flowman/worktrees) as <root>/<run-slug>/<repo-folder>. Set the variable in .env.local on hosts without that volume; a ~/ prefix is expanded. If the root sits on an unmounted volume — including a leftover mount-point folder under /Volumes, /mnt, or /media that is on the same device as its parent — the turn fails instead of creating the folder on the system drive, and pnpm runtime:coding:doctorwarns about it, along with the root's free space. FLOWMAN_WORKTREE_SETUP_TIMEOUT_MS (default 15 minutes) bounds the setup command.
When the run is marked done, its PR completes, a Worktree plan is rejected (or a design finalized), or the run is cancelled, the base_syncturn stops the run's previews, waits for them to stop, and removes the worktree instead of syncing the base branch. The working branch stays in the repository. A worktree with uncommitted or untracked changes, or a detached HEAD holding commits no branch reaches, is kept and reported on the run; remove it with git worktree remove when you no longer need it. Cancelling a run while its setup command is still running aborts the setup, and the agent is never started.
Previews of a worktree run serve the worktree and never adopt a server already listening on the target port. The target's port is the preferred one: the runtime starts the command with PORT set to the first free port at or above it that no other Flowman preview holds, and the run shows the port it actually used. Next.js reads PORT natively; other dev servers need it passed through, for example vite --port $PORT. Each preview still gets its own zrok URL. Apps that talk to a shared backend (such as a single Convex dev deployment) still share that backend across worktree previews.
Visual verification
Local implementation and follow-up prompts advertise an ambient visual verification capability — there is no toggle. When the agent's changes affect a web UI and a running instance is reachable (a playground or dev server it or the repository's skills started), the prompt instructs it to capture and inspect the page before finishing, fix layout, clipping, console-error, and responsive problems, and re-check mobile — the same self-verifying behavior Codex desktop exhibits with its in-app browser. Agent judgment decides when a change warrants looking; non-UI changes skip it entirely.
The runtime injects $FLOWMAN_VISUAL_CAPTURE into every session's shell environment. It invokes a fixed helper CLI:
node "$FLOWMAN_VISUAL_CAPTURE" --url <http(s) url> --out <dir> \
[--viewport WxH] [--full-page] [--wait-ms N] [--headed] [--linger-ms N]
# Writes into <dir>:
# screenshot.png rendered page (viewport or full page)
# aria.yaml accessibility snapshot
# console.json console errors/warnings + page errors
# meta.json final URL, title, HTTP status, viewport
# Exit codes: 0 captured · 2 browser not installed · 3 URL unreachable
# 4 browser failed to startSession sandboxes may deny Chromium launches, so the runtime hosts a loopback capture proxy (advertised to sessions as $FLOWMAN_VISUAL_CAPTURE_PROXY): the helper forwards the capture to the unrestricted runtime process, which renders in a private staging directory and returns the artifacts in the response — the sandboxed helper persists them into the out dir itself, so every durable write stays enforced by the session's own sandbox. Direct launch remains the fallback and the manual/standalone mode; in that mode everything the browser writes (profile, temp files) stays under the output directory. Prerequisite on the runtime host: pnpm --filter @flowman/coding-agent-runtime install-browsers (downloads headless Chromium); Linux hosts also need the system libraries from pnpm --filter @flowman/coding-agent-runtime exec playwright install-deps chromium. The doctor reports this as an optional check; when the browser is missing the helper explains the fix and the agent notes it in its summary instead of failing the task.
Captures are headless by default. On a runtime host with a display, set FLOWMAN_VISUAL_CAPTURE_HEADED=1 (and optionally FLOWMAN_VISUAL_CAPTURE_LINGER_MS, up to 30000) on the runtime process to open a visible browser window for every capture and keep it on screen after the artifacts are written — so you can watch the agent's visual reviews live. The policy is applied host-side by the capture proxy; the --headed and --linger-ms flags cover manual and direct runs. Parallel captures each open their own window.
For flows the one-shot capture cannot express — sign-in, menus, multi-step navigation — agents drive a long-lived interactive browser session through the proxy:
node "$FLOWMAN_VISUAL_CAPTURE" session open → {"sessionId":"…"}
node "$FLOWMAN_VISUAL_CAPTURE" session goto --session <id> --url <url>
node "$FLOWMAN_VISUAL_CAPTURE" session fill --session <id> --ref e2 --text <value>
node "$FLOWMAN_VISUAL_CAPTURE" session click --session <id> --ref e5
node "$FLOWMAN_VISUAL_CAPTURE" session screenshot --session <id> --out <file.png>
node "$FLOWMAN_VISUAL_CAPTURE" session close --session <id>Every command returns the page's interactive elements as refs (e1, e2, …) plus an accessibility outline and console errors, so the agent reads the page and reacts step by step — login state persists across commands. When a page needs sign-in and credentials are available, the agent logs in through a session and verifies the real pages. Headed sessions apply per-action slow motion (FLOWMAN_VISUAL_CAPTURE_SLOWMO_MS, default 400ms) so you can follow the automation; sessions cap at 3 concurrent and auto-close after 5 idle / 30 total minutes.
To test the automation end to end, either run the helper directly on the runtime host, or start a direct coding run with no repository and a prompt like "Use the visual capture tool at $FLOWMAN_VISUAL_CAPTURE on https://example.com and tell me the page title, main heading, and any console errors." The run output contains the answer, proving env plumbing, sandbox, Chromium, and agent usage in one shot.
Intake and Refined columns (Jira queue)
Creating Jira requests in Flowman
Members and customers can use New Ticket in the top bar to raise a Jira Service Management request without leaving Flowman. Customer View raises the request as the impersonated customer. Jira projects come from the project-qualified queues in JIRA_QUEUE_ID; customers see only projects represented by their assigned queues. Flowman shows only request types mapped by Jira work profiles and loads their visible fields from Jira when selected. Stable service-desk and request-type IDs win; a unique request-type name is used as a fallback. Missing or ambiguous mappings are reported as configuration errors.
Supported fields are text, multiline text, Markdown description, number, date, boolean, single- and multi-choice, and Jira-backed user pickers. Optional unsupported fields are omitted; an unsupported field required by Jira blocks submission. Description Markdown is converted to Jira ADF. Up to five images, PDFs, or text files may be attached, with a 20 MB limit per file. Files are uploaded to Jira as public request attachments and Flowman deletes the forwarded draft blobs.
Jira automation must apply exactly one recognized repo: label and route the issue into one of the configured queues. After Jira creates the request, Flowman performs a bounded Intake refresh and verifies the queue, repository, and customer visibility. A fully verified request returns to Coding with a success message. If the Jira issue exists but an attachment or visibility check fails, Flowman still returns with the issue key and a partial-success warning. An ambiguous Jira timeout is marked indeterminate and is never retried automatically, preventing duplicate requests.
The board opens with two Jira columns before Planning: Intake (tickets that are not yet refined) and Refined (tickets carrying the Refined label). Both pull from the configured Jira Service Management queues and have not become coding sessions yet. Sync is pull on demand: nothing polls in the background. Press the refresh control in the Intake column header and Flowman resolves each project-qualified entry in JIRA_QUEUE_ID, reads that queue's own JQL filter, and searches Jira with it. The same refresh can be triggered from a Jira org-level Automation rule on issue create via POST /api/webhooks/jira/intake-refresh with Authorization: Bearer <secret>. Set JIRA_INTAKE_WEBHOOK_SECRET on the app (or reuse FLOWMAN_INTERNAL_DISPATCH_TOKEN). The request body must include the Jira issue (at least issue.key or issue.fields.project.key); Flowman refreshes every project whose JIRA_QUEUE_ID watches that Jira project key.
Tickets appear in both views. On the Board they sit in Intake or Refined; the refresh control lives on Intake only. On the List, grouping by Status puts them in leading Intake and Refined groups (each omitted when empty), and grouping by Repositoryfiles each ticket with that repository's sessions — so a repository with nothing but intake tickets still gets its own group. Click a ticket to open a right-hand detail drawer with description, people, labels, and the latest comment (fetched live from Jira). From that drawer, Edit switches the title and description into an editable form. Description editing uses Markdown for headings, emphasis, links, lists, block quotes, inline code, code blocks, and horizontal rules. Save writes both fields to the same Jira issue, keeps the drawer open, and replaces the form with values confirmed by a fresh Jira read. Cancelexits edit mode without writing. If Jira's title or description changed after editing began, Flowman does not overwrite those fields — it refreshes the drawer and asks you to review the newer values. When the existing Jira description contains formatting Markdown cannot represent (for example panels, tables, media, or mentions), Flowman warns that unsupported formatting may be lost and requires explicit confirmation before saving. Empty titles are blocked; empty descriptions are allowed. While a save is pending, duplicate submissions are prevented and failures leave your draft in place with an actionable error. From a Refined ticket you can start a Cloud or Local coding run directly from that drawer. From an Intake ticket, Exclude from intake asks for confirmation, then adds the exact flowman:exclude label in Jira without removing other labels, and removes the ticket from Intake only after that label is verified. The action is not offered for Refined tickets. Restoration is done in Jira by removing the label; the ticket returns on a later successful Intake refresh when it still matches a configured queue and is not refined. From either Intake or Refined, Rejectasks for a required rejection reason, then uses Jira's exact Reject transition to Canceled, posts the reason as a comment on the Jira issue once that status is verified, and removes the ticket from the Flowman board only after the comment is accepted. Rejection stays disabled while refinement or investigation is still active, leaves Jira labels and other fields unchanged, and keeps the ticket visible with a retryable error when the required transition is missing, ambiguous, or fails, or when the comment cannot be posted (a retry posts the reason without repeating the transition, and never posts it twice). From either column, Delete Jira ticketasks you to type the issue key back, then permanently deletes the issue in Jira and removes it from the board only after Jira confirms the issue is gone. Deletion follows the same eligibility rules as rejection, does not delete subtasks (Jira refuses the request and the ticket stays on the board), and cannot be undone. Both actions reserve the ticket first: a second reject or delete of the same ticket, or a refinement, investigation, or run started on it, is refused until the removal finishes. If Jira cannot find the issue before it has confirmed Flowman's own delete request, the ticket stays on the board with an error, since that usually means the issue is hidden from the Jira account rather than deleted; a ticket Jira really did delete leaves the board on the next Intake refresh. Use Open in Jira when you need the full issue page or fields outside title and description.
A refresh both adds new tickets and updates ones already pulled, so labels, status, assignee, and priority stay current. Tickets that have left the queue are hidden from the board but their rows are kept, so a ticket that returns to the queue reappears without losing anything Flowman recorded against it. Tickets rejected from Flowman keep their dismissal and do not reappear merely because Jira still returns them. Each refresh pulls at most JIRA_INTAKE_MAX_ISSUES tickets (200 by default); when that cap is hit, nothing is hidden, because a capped pull has an incomplete view of the queue.
Jira label conventions drive the card, and none are stored — each is re-read from the ticket's labels on every render, so editing a label in Jira takes effect on the next refresh:
Refined— moves the ticket from the Intake column into Refined. Matched case-insensitively.flowman:exclude— hides a non-refined ticket from Intake. If the ticket also carriesRefined, Refined takes precedence and the ticket stays in the Refined column. Matched case-insensitively; hidden from the chip row.repo:<name>— maps the ticket to a registered repository, for examplerepo:qualip-web. The name is compared as a repository slug, so casing and separators do not matter. A ticket with norepo:label still appears on the board with no repository; one pointing at an unregistered name, or carrying more than one, is flagged on the card instead of being dropped. Matched tickets count toward the repository filter chips just like coding sessions.workflow:cloud,workflow:local,workflow:local:codex,workflow:local:cursor, orworkflow:local:claude— seeds the Start as choice when you start a coding run from the Refined drawer.workflow:cloudis Cursor Cloud.workflow:localandworkflow:local:codexare Codex Local.workflow:local:cursoris Cursor Local.workflow:local:claudeis Claude Local. Default is Cloud when absent. LikeRefinedandrepo:, these labels are hidden from the chip row.
Jira work profiles
Project owners configure Jira work profiles under Settings → Jira. Each profile maps one or more JSM request types or Jira issue types to either Refine or Investigate, with profile-specific agent instructions. Flowman resolves a JSM request type first, then an issue type, and finally compares normalized Jira names when stable IDs are unavailable. A Jira identity can belong to only one profile. Unmapped tickets remain visible but cannot start work until an owner adds a mapping.
A ticket explicitly converted from a completed investigation has one higher-precedence exception: Flowman resolves it to the existing Suggest improvementrefinement profile even when its Jira request or issue type still maps to investigation. If that profile is removed or changed away from Refine, the ticket stays blocked until an owner restores it. The override is cleared only after a later refinement is successfully posted and verified.
The built-in profiles classify Report a bug as an investigation, with Bug as its issue-type fallback. Suggest a new feature and Suggest improvement are separate refinement profiles. There is intentionally no New Featureissue-type fallback because it cannot distinguish a feature from an improvement. Profile instructions are always enclosed by Flowman's immutable safety, transport, and output rules.
Refining an Intake ticket
Open an unrefined Intake ticket to link a registered repository and start Refine ticket. Choose Codex Local, Cursor Local, or Claude Local in the drawer (Jira workflow:local:cursor and workflow:local:claude default to Cursor Local and Claude Local). Linking is an explicit Jira write: Flowman removes every existing repo: label, adds exactly one repo:<registered-slug>, and preserves all unrelated labels. Refinement is enabled only when an online runtime for the selected provider has a configured filesystem path for that repository. A single eligible runtime is selected automatically; otherwise you must choose one. Claude Local needs a runtime that advertises Claude Code support, with the Flowman skills linked into ~/.claude/skills. Its ticket turns, including the approved Jira posting turn, run read-only: shell commands run in Claude Code's sandbox with the repository write-protected, and network access stays open so it can reach Jira. The sandbox is built into macOS; Linux hosts need bubblewrap and socat. Without them the ticket turn fails rather than running unconfined.
The hidden refine-ticket workflow resumes one agent thread and requires the installed skill with that exact name. Repository inspection is read-only. If the selected runtime does not expose the skill, the run fails with an installation message instead of substituting a generic refinement. Only one active refinement is allowed for an Intake ticket.
- Questions use the coding-session dialog. Choice questions have bounded options; nuanced questions use free-form answers. Optional questions can be skipped, and Done—draft now sends partial answers while recording the remainder as unresolved.
- The completed Markdown draft opens in the same dialog with View Run, Revise, Post, and Cancel. Closing the dialog only dismisses it; clicking the Intake card reopens the pending session.
- Posting sends the exact
postresponse to the same agent thread. The agent must write the approved Markdown to a local file and pass that path to the skill's bundled posting command; stdin and-are unreliable in coding-agent shells. The command converts the file, posts and verifies the Jira changes as one non-interactive operation, and avoids ad hoc shell pipelines. It posts an internal Jira comment containing@agent-ticket-refinedand adds the exactRefinedlabel while preserving current labels. No Jira write is permitted before this approval. - The skill verifies the exact rendered body, internal visibility, comment identity,
Refined, and all prior labels, then returns structured proof to Flowman. The ticket moves to Refined only after every proof passes. A partial or uncertain result keeps the approved draft and shows Retry missing Jira update; retries reuse a matching comment, repair an earlier raw-Markdown comment in place, and perform only missing mutations.
While the workflow is active, the ticket remains in Intake with Refining, Questions, Draft ready, Posting, or Posting failed state. Cancelling retains the run and session audit history, clears the active link, makes no Jira changes, and permits a fresh refinement.
Local development stores Workflow SDK state in .workflow-data so a Next.js build does not erase active question or posting webhooks. If an older local run loses its webhook, Flowman will not bypass Jira posting verification. Use Cancel to clear the orphaned active link without a Jira write, then start a fresh refinement.
Investigating a test or production ticket
Tickets resolved to an investigation profile show Investigate ticket. The hidden investigate-jira-ticket workflow starts immediately on the selected registered repository and online Codex Local, Cursor Local, or Claude Local runtime. The ticket leaves Intake as soon as the run is linked. The session snapshots the resolved profile and compiled instructions, so later settings edits apply only to future runs.
- The workflow requires the installed
investigate-ticketskill with that exact name and never invokesrefine-ticket. Before approval, investigation is read-only: the agent cannot edit repository files, post to Jira, change labels, or mutate another external system. Diagnostic questions are allowed; feature-design and implementation-planning questions are not. - An explicit
testorproductionstage in the ticket or a later reviewer answer selects that environment. A later answer wins; conflicting or unrecognized environment names require a diagnostic clarification. When no stage is stated, the skill defaults to production and records the selection basis under Target Environment. Stage-specific checks run against only the selected environment. - Owners may add repository-specific investigation instructions in Jira settings, including exact installed skill names. These overrides are used only by investigation sessions. If a required skill is missing, the run reports the blocker instead of imitating the skill.
- The report separates evidence from hypotheses and records impact, checks, findings, root-cause confidence, mitigations, next actions, and open questions. Reviewers can Revise, Post findings & mark done, or Cancel.
- On approval, Flowman sends exact
postto the same skill session without repository write access. The skill deterministically converts the reviewed Markdown to Jira wiki markup so headings, lists, links, and code render correctly; posts it as an internal JSM comment; addsInvestigatedincrementally; and performs the available transition into Jira's Done status category. It re-reads Jira to verify content, rendered structure, privacy, final status, and preservation of every prior label. It does not addRefined. - Flowman validates the skill's structured result and expected Jira key; it does not perform a second Jira mutation or verification call. A partial result preserves the report and returns to review for an explicit retry. The skill finds an existing matching internal comment and retries only missing work so it does not duplicate the report or repeat an already-completed transition.
Cancelling returns the ticket to Intake and keeps the run for audit history. A failed or cancelled run exposes the same restore control on run detail. Successfully posted investigations remain linked to their completed run and outside Intake.
When the findings identify code work, open the completed investigation's run menu and choose Convert to refinement. Flowman resumes the same Codex thread with an exact Jira-only command. The skill removes only Investigated, requires a transition named exactly Reopen issue unless Jira is already Open or Reopened in its open status category, preserves the report and every other label, and verifies the issue is returned by at least one configured Intake queue. Flowman does not clear the active run link until all checks succeed.
A successful conversion keeps the original completed run and report for audit, but returns the ticket to Intake as Suggest improvement. It does not start refinement or add Refined; select the ticket and begin refinement normally. Partial Jira updates remain linked to the investigation and expose an explicit retry. Retries accept an already-Open or already-Reopened issue, repeat only missing mutations, and never edit or duplicate the findings comment.
From a Refinedticket's drawer, choose Cloud (Cursor Cloud), Codex Local, Cursor Local, or Claude Local and press Start. Jira workflow: labels still pick the default (workflow:local and workflow:local:codex select Codex Local; workflow:local:cursor selects Cursor Local; workflow:local:claude selects Claude Local). The latest Jira comment becomes the implementation prompt (planMode: false), and the matched repo: repository is required. Local runs need an online runtime environment. After start, the ticket is linked to the new run and leaves Intake / Refined until you restore it: delete the run, or use Return to Refined on a failed or cancelled run detail page (the run itself is kept for history).
When a linked local implementation is marked done—or all of its pull requests become terminal—Flowman queues the required flowman-pull-request-completed skill. The skill completes branch synchronization and verification, then transitions the linked Jira issue through the unique Done-category transition. It re-reads Jira, verifies the final status and preservation of existing labels, and only then marks the Flowman run done. Retrying is idempotent when Jira succeeded but Flowman completion did not. Cloud implementations retain their provider-native completion path because they do not have the project's locally installed skill.
Credentials come from project environment variables, never from the host process. Set JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN, and JIRA_QUEUE_ID. Use strict project-qualified entries such as FS:53,CA:13; Flowman trims entries, ignores blanks, uppercases project keys, deduplicates exact queue references, and combines tickets by Jira issue identity. Bare values such as 53 are rejected. JIRA_INTAKE_MAX_ISSUES is a shared cap across the combined refresh. If a queue fails or the cap prevents a complete evaluation, successful results are applied, uncertain existing tickets are preserved, and the refresh feedback identifies the project-qualified partial failure or truncation. The account behind the token needs agent access to every configured service desk — a customer or collaborator account is rejected even though it can see the same issues in the portal. Any project member can refresh; only admins can set the variables.
Further reading
- codingAgent action reference — full input schema, branch management, email triggers, failure behavior, and plan revision details.
- Organize workflows — categories, child template field rules, and public template copying.
- Getting started — register repositories, start the runtime, and launch your first run.
- Environment variables — how
record.envreaches workflows and provider cloud execution.