Skip to content

Configuration

All project configuration lives in one file: .flow/config.json. Read and write it through flowctl so writes stay validated and atomic:

Terminal window
flowctl config get <key> --json # one key (dotted path)
flowctl config get --json # the whole tree
flowctl config set <key> <value> # validated write

A missing file means defaults. An unreadable, malformed, or non-object file is reported and never reset (6.1.0+): readers warn once per process naming the file, with line and column for a JSON syntax error, config set refuses to overwrite it, and flowctl validate reports a root error. Fix the file by hand, then rerun.

Every key below comes from the published JSON Schema at plugins/flow-next/schema/flow-config.schema.json - the same schema /flow-next:setup stamps into the file’s $schema field, and the one the repo’s drift test holds flowctl to. A key flowctl accepts that the schema does not document fails the suite. This page is the human rendering of that contract.

Always-relevant knobs for the default spec-to-PR flow. Every optional layer here is off or conservative by default; this page states what each key does.

KeyTypeWhat it does
review.backendenumDefault review backend (rp, codex, copilot, cursor, claude, host, none) or spec form backend[:model[:effort]], e.g. codex:<model>:high. cursor folds effort into the model name (no :effort rung); claude takes the full grammar with the CLI’s efforts (low, medium, high, xhigh, max); rp, host, and none are bare-only. copilot accepts no none/minimal effort. If unset, review commands require —review or FLOW_REVIEW_BACKEND. Default: null.
review.maxIterationsintegerCumulative review-round cap per scope (default 8, minimum 1 - the cap can never be disabled). The env var MAX_REVIEW_ITERATIONS takes precedence over this key. In an autonomous run (flow —auto) this key may only lower the cap, so an autonomous agent cannot extend its own review gate. Default: 8.
planSync.enabledbooleanEnable plan-sync after task completion. Off by default; run /flow-next:sync manually when a task invalidates a downstream assumption. Default: false.
planSync.crossSpecbooleanCross-spec plan-sync: scan other open specs for stale references after each task (opt-in; increases sync time). Default: false.
memory.enabledbooleanEnable the memory system: skills capture and search categorized learnings under .flow/memory/. Default: true.
judge.enabledbooleanEnable the optional judge when TYPESAFE_API_KEY is present in the environment. Default true; false disables requests. Invalid non-boolean values warn and behave as true. See Optional Jev judgments. Default: true.
scouts.githubbooleanEnable github-scout during planning (requires the gh CLI). Default: false.
makePr.derivedPathsobjectOptional derived-file classification rules for the make-pr export: bucket names (dualCopy, mirror, state) mapped to arrays of rules ({path|prefix, source}). A configured value fully replaces flow-next’s built-in default shapes; never required and not part of the seeded defaults.
pipeline.qaenumOptional live QA stage. String-enum off | on | auto, NOT a bool; any other value, including bool true, is OFF. off: QA runs only when you invoke /flow-next:qa. on: one live /flow-next:qa pass at the all-tasks-done juncture before make-pr on every spec. auto: attended /flow-next:flow and /flow-next:flow —auto both read the key through the flow skill’s gate-selection reference and run the pass only when the spec’s acceptance describes UI behaviour on a drivable surface and a target can be started; otherwise the stage records skipped(config: pipeline.qa=auto: <reason>) and the route advances to make-pr. QA never hard-blocks a run: NEEDS_WORK and BLOCKED advance to the PR, and their findings become open items on a draft PR. flowctl stores the value and never interprets it; drivability is the skill’s judgment. Optional: flow-next runs fully without it. It costs a live-app drive pass per spec plus a running deploy and a configured driver. Values: off, on, auto. Default: "off".

The build loop (/flow-next:flow --auto) and the ship loop (/flow-next:land). All off/conservative by default. The pilot.* keys keep their spelling; they configure flow --auto, which replaced the /flow-next:pilot command (removed in 6.1.0).

KeyTypeWhat it does
pilot.autonomyenumBacklog mode for /flow-next:flow --auto (the key keeps its pilot. spelling). Scalar string-enum (ready | backlog), NOT a bool. ready = flow --auto selects only already-ready specs. Only the literal backlog widens selection to the whole open backlog; the per-run --backlog flag forces it for one run; any other value stays ready. In long-horizon mode a backlog run drives its one selected item to a terminal and stops, and the next invocation selects the next item. Backlog mode never authors a spec or sets ready. It grants no merge authority by itself; --until=merge separately authorizes land for the selected item. Values: ready, backlog. Default: "ready".
pilot.gateClassesenumBacklog-mode force-gate for flow --auto --backlog (the key keeps its pilot. spelling): class names (e.g. risky, prod-config) that force surfacing before action - a matching item is parked with a question (ASKED) instead of advanced full-auto. Empty = full-auto for every workable item. Default: [].

land.* - settings for /flow-next:land, which handles one named pull request. Two keys remain.

KeyTypeWhat it does
land.patienceMinutesintegerMinutes since the last push that land waits before merging, so review bots can post, when the calling flow, not a human in the session, authorized the merge. The window is for bots, not people; raise it if your bots are slower. A human’s current merge authorization waives the wait. When GitHub reports no push time, land uses the head commit’s earliest check-suite creation time, then its committer date. Default: 10.
land.mergeVerdictCommandstring | nullOpt-in repo merge gate: a shell command land runs once per invocation, only after every other merge gate passes, with a 600-second bound. Exit 0 allows the merge; any non-zero exit, a missing or unexecutable command, or a timeout blocks it with NEEDS_HUMAN. It runs from the repository where land was invoked without changing the checkout, so it must judge the remote FLOW_HEAD_SHA, not local HEAD. The environment also supplies FLOW_BASE_REF, FLOW_PR_NUMBER, FLOW_SPEC_ID (empty when several specs match) and space-separated FLOW_SPEC_IDS. Never executed under --dry-run. Unset, null, and an empty string all mean off. Default: "".

Retired land.* keys. A config file that still carries them loads normally. Land prints one notice naming the ignored keys and leaves the file unchanged; delete them when convenient.

Retired keyUse instead
land.releaseRun releases separately from your project’s release instructions.
land.reviewSignal, land.automatedReviewers, land.cleanReviewCommentPatternThe default gate is green checks, a nonblocking GitHub review decision, and zero unresolved threads. Tighten it in your instruction file, with branch protection, or with land.mergeVerdictCommand. See the merge gate.
land.reviewTrigger, land.requestReviewersRequest reviewers outside land.
land.ciFixBudgetLand makes one focused fix or one flake rerun per failure and reports BLOCKED naming the check. It keeps no ledger and sets no label.
land.patienceMinutesAfterReviewland.patienceMinutes, anchored to the last push.

review.backend leaves in 8.0.0. From 8.0.0 the reviewer is chosen in the model-routing block of your CLAUDE.md or AGENTS.md, the one /flow-next:setup proposes, the same way implementers and scouts already are. Until then the key works exactly as the table above describes, and a reviewer named in the prompt still wins. Its rp value (RepoPrompt) is deprecated on the same schedule; set another backend to move off it now. See Review backends.

pipeline.chainStages is gone from the schema. A config that still sets it keeps working: flowctl ignores the key and prints a one-line note. Under flow --auto --tick, make-pr now runs on the next tick; a long-horizon flow --auto run already runs QA and then make-pr as consecutive hops. Delete the key from .flow/config.json when convenient.

artifacts.html.enabled is gone from the schema. A config that still sets it keeps working: flowctl ignores the key and prints a one-line note. For a visual view of a spec, a plan or a diff, run /flow-next:visual, or ask the agent for an HTML page.

Removed in 4.0.0: routing moved into prose

Section titled “Removed in 4.0.0: routing moved into prose”

The work.delegate* keys (packaged codex delegation) and the models.* block (the role map and its verifiedAt / verifiedWith staleness stamps) are gone from the schema and from flowctl. Routing lives in the routing block in your CLAUDE.md / AGENTS.md instead, and implementation offload is the implementer tier plus a bridge recipe.

Leftover keys are inert, not dangerous. A .flow/config.json still carrying them keeps working: flowctl ignores them entirely and prints one non-blocking advisory naming what it found, on the config surfaces and the work entry points, to stderr so a --json read stays parseable. Delete them when convenient.

Bounds for the optional decision-map stage.

KeyTypeWhat it does
chart.maxDecisionsintegerCharting-time decision ceiling (default 12). chart create with an initial-map refuses past this count without —force-size —reason. Later sharpening may grow past it. Default: 12.
chart.claimStaleAfternumberStale-claim age threshold in hours (default 24). release-claim —break-stale —reason is allowed only after a claim is at least this old; always audited (actor, prior owner, age, reason). Default: 24.

The due threshold for the optional feature map.

KeyTypeWhat it does
features.staleAfterCommitsintegerDue threshold (default 50). flowctl features status reports the feature map due a maintain pass when a feature file’s **Last proven:** commit is at least this many default-branch commits old, counting only commits that change a file outside .flow/ and documentation. Values below 1 or non-integers read as the default. See Keep the feature map current. Default: 50.

Projection to Linear / GitHub / GitLab / Jira. Inactive unless enabled.

tracker.* - Tracker-sync bridge settings (Linear / GitHub / GitLab / Jira). See docs/tracker-sync.md.

KeyTypeWhat it does
tracker.versionintegerTracker config schema version. Default: 1. (machine-written)
tracker.enabledbooleanEnable the tracker-sync bridge. The bridge is active iff raw tracker.enabled == true OR raw tracker.type is one of linear/github/gitlab/jira. Optional: flow-next runs fully without it. It costs a bidirectional round-trip per enabled lifecycle event plus a conflict policy and a second place state can be wrong; enable it when other people need to read or edit status where they already work, or run /flow-next:tracker-sync manually and leave the bridge off in between. Spec-only is a first-class mode. See docs/running-lean.md. Default: false.
tracker.typeenumTracker backend: linear, github, gitlab, or jira. Values: linear, github, gitlab, jira (or null). Default: null.
tracker.provenancestring | nullFree-form provenance written by the discovery ceremony on confirmation (who/when/signals). Default: null. (machine-written)
tracker.perEvent.captureenumSync op fired when a spec is captured: off | pull | push | reconcile | comment. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.interviewenumSync op fired after a refine pass updates a spec (the key keeps its historical name): off | pull | push | reconcile | comment. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.planenumSync op fired after planning: off | pull | push | reconcile | comment. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.work.firstClaimenumSync op fired on a task’s first claim: off | pull | push | reconcile | comment. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.work.doneenumSync op fired when a task completes: off | pull | push | reconcile | comment. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.makePrenumSync op fired when make-pr opens a PR: off | pull | push | reconcile | comment. The PR link + In Review push is unconditional whenever the bridge is active. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.resolvePrenumSync op fired after resolve-pr: off | pull | push | reconcile | comment. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.completionReviewenumSync op fired after the spec completion review: off | pull | push | reconcile | comment. The ceremony seeds comment (verdict + R-ID coverage; never terminal Done). Values: off, pull, push, reconcile, comment. Default: "off".
tracker.perEvent.qaenumPost the /flow-next:qa ship verdict as a tracker comment: off | comment only. comment is the only sensible verb for a verdict; the QA skill treats any non-off value as comment. Not switched on by the ceremony’s default-on set - QA-specific opt-in. Values: off, comment. Default: "off".
tracker.perEvent.land.mergedenumPost-merge touchpoint for /flow-next:land. After a confirmed merge, land moves each matching spec’s issue to its terminal status through the tracker API whenever the bridge is active; this leaf does not gate that status write. Land writes no local sync receipt, timestamp, or verdict comment. A failed touchpoint keeps MERGED and names the merge commit; running land again on the merged pull request retries only the touchpoint. A closed spec on an open pull request stays In Review. Values: off, pull, push, reconcile, comment. Default: "off".
tracker.chartsenumOptional chart lifecycle projection. String-enum off|on, NOT a bool: only the literal on projects charts as parent issues with decision children through the tracker facade. Local chart operations always succeed when off or when the bridge is inactive. Values: off, on. Default: "off".
tracker.perTracker.teamIdstring | nullLinear team id. Default: null.
tracker.perTracker.projectIdstring | nullLinear project id. Default: null.
tracker.perTracker.labelMapobjectLabel linkage map (tracker-specific shape). Default: {}.
tracker.perTracker.priorityMapobjectPriority linkage map (tracker-specific shape). Default: {}.
tracker.perTracker.repostring | nullGitHub repo as owner/name, written by the discovery ceremony (machine-written; not part of the seeded defaults). (machine-written)
tracker.perTracker.projectstring | nullGitLab group/subgroup/project path (URL-encoded once for the API, never double-encoded). Default: null.
tracker.perTracker.hoststring | nullSelf-managed GitLab base URL. null resolves from glab config / CI_SERVER_URL; gitlab.com is never assumed. Default: null.
tracker.perTracker.baseUrlstring | nullJira site base URL (Cloud or DC/Server). The JIRA_BASE_URL env var overrides it at runtime. Default: null.
tracker.perTracker.projectKeystring | nullJira project key (the JQL / listOpenIssues scope). Default: null.
tracker.perTracker.authSchemeenumJira auth shape decided once at the discovery ceremony: cloud-basic (Cloud HTTP-basic email:API_TOKEN) or bearer-pat (DC/Server bearer PAT). Credentials still read from env each run, never stored here. Values: cloud-basic, bearer-pat (or null). Default: null.
tracker.perTracker.apiVersioninteger | nullJira REST API version. null until the resolver pins 2; migration converges a legacy 3 to 2. Default: null.
tracker.perTracker.ownerstring | nullGitHub repository owner (discovery-fingerprint input; dynamic per.get read via _FINGERPRINT_KEYS).
tracker.perTracker.issueTypestring | integer | nullJira issue type (name or id) for created issues; a configured value that does not resolve against the live project is an error.
tracker.perTracker.blocksLinkTypestring | nullGitLab link type used for blocks relations (e.g. blocks); probe and mutation use the same resolved name.
tracker.perTracker.preferredTransportstring | nullLinear transport preference (mcp routes through the MCP continuation; anything else uses HTTP).
tracker.perTracker.transportstring | nullLegacy alias for preferredTransport (read second).
tracker.perTracker.sslVerifybooleanVerify TLS certificates against Jira. false is an explicit opt-out for a self-hosted internal-CA / self-signed cert (JIRA_SSL_VERIFY env overrides). Default: true.
tracker.perTracker.statusMapobjectLegacy normalized-status to Jira status map ({name}/{id}; id preferred - names are project-renamable). Live entries migrate into tracker.resolved.destination.statusIds; dead entries are dropped with a warning. Default: {}.
tracker.staleAfterHoursintegerStaleness threshold (hours) consumed by sync list-stale. Default: 24.
tracker.conflictTiebreakenumStatus who-wins tiebreak: flow-wins | tracker-wins | always-ask. Strict enum: invalid CLI writes are rejected; malformed persisted values fail before status work. In autonomous mode always-ask resolves to queue, not prompt. Values: always-ask, flow-wins, tracker-wins. Default: "always-ask".
tracker.readyStatestring | nullReadiness projection: the tracker workflow state meaning ready for work (a Linear state name, Jira status name, or a GitHub/GitLab label). When set, pull-side sync projects it onto the local spec ready flag - one-way, tracker is authoritative. null = projection off. Default: null.
tracker.specIdsenumId scheme for new specs when a tracker bridge is active: flow (native fn-N) or tracker (tracker-keyed KEY-N-slug / synthetic gh-N / gl-N). Strict enum on write; malformed on-disk values fail closed to flow. Not materialized at init so setup can detect never-asked via a raw null read. Values: flow, tracker. Default: "flow".
tracker.transport.timeoutSnumber | nullPer-request timeout in seconds (0-600).
tracker.transport.maxRetriesinteger | nullRetry attempts per call.
tracker.transport.backoffCapSnumber | nullBackoff cap in seconds.
tracker.transport.concurrencyinteger | nullMax concurrent tracker calls.
tracker.resolved.destination.statusIdsobjectNormalized status slots (todo, in_progress, done; optional provider slots) mapped to provider status ids. (machine-written)
tracker.resolved.destination.stateIdsobjectNormalized state slots mapped to provider state ids (Linear). (machine-written)
tracker.resolved.capabilities._sourceobjectMachine-written capability provenance (which probe/endpoint established each flag); GitLab’s resolver persists it alongside the boolean capability keys. (machine-written)
tracker.resolved.scopeResolvedAt.destinationstringISO timestamp of the last successful destination resolution. (machine-written)
tracker.resolved.scopeResolvedAt.destination.statusIdsstringISO timestamp of the last successful destination.statusIds resolution. (machine-written)
tracker.resolved.scopeResolvedAt.destination.stateIdsstringISO timestamp of the last successful destination.stateIds resolution. (machine-written)
tracker.resolved.scopeResolvedAt.capabilitiesstringISO timestamp of the last successful capabilities resolution. (machine-written)
tracker.resolved.resolvedAtstring | nullNon-null only when all required destination fields, required normalized slots, and capability booleans are present. (machine-written)

A few knobs are runtime environment variables rather than config keys:

VariableEffect
FLOW_ACTORActor identity for claims/receipts. Wins over git email, git name, $USER.
FLOW_REVIEW_BACKENDPer-invocation review backend override (bare or backend:model:effort spec form).
MAX_REVIEW_ITERATIONSSession override for review.maxIterations (the config key is the durable form).
FLOW_PR_CREATE_CMDmake-pr’s PR-create seam for App/bot-authored PRs.
FLOW_AUTONOMOUS=1Question-suppression for autonomous drivers.
TYPESAFE_API_KEYTurns on the optional Jev judgments; read at call time only, never written to config or receipts. judge.enabled=false forces them off.
JIRA_BASE_URL / tracker tokensTracker transport credentials and endpoint overrides.
FLOW_NO_DEPRECATION=1 / FLOW_NO_AUTO_MIGRATE=1Silence deprecation notices / disable auto-migration of legacy layouts.

Precedence where both exist: explicit CLI flag → environment → .flow/config.json → built-in default.

Set policy where the team can inspect it - the config file is committed, so flowctl config set IS the policy record. The decisions worth making explicitly as a team: the review backend (cross-family beats same-family), review.maxIterations, whether planSync.crossSpec is worth the reconciliation time at your spec volume, the tracker perEvent map (which lifecycle moments project), and the autonomy posture (pilot.autonomy, and land.mergeVerdictCommand on repos with no branch protection). Everything else has a sane default.