Agent Guide
Fairway is built for coding agents working in parallel. This guide is the operator-facing contract for an agent that is already inside a repo with Fairway configured.
Canonical definitions live in Concepts. This guide uses those terms procedurally and does not create alternate meanings for task, session, decision, evidence, review, role, lane, or promotion.
If Fairway is not configured yet, stop here and use the quickstart. It proves one bounded task, decision, evidence row, closeout, and readback before introducing the advanced coordination model on this page. Do not make a new adopter learn sessions, lanes, worktrees, watchers, or shared-team operation before that first result.
Cold Start In Consumer Repos
When fairway init is run in a consumer repository, it writes
.fairway/AGENTS.md as the local agent breadcrumb. That file is intentionally
short: it states the Fairway execution source of truth, the start-of-session
ritual, role resolution order, session registration expectation, and where to
find this full guide for the installed Fairway version.
Agents landing cold in a repo should first look for .fairway/AGENTS.md. Repo
maintainers should paste the bootstrap block printed by fairway init into the
root AGENTS.md, CLAUDE.md, or equivalent provider entrypoint so external
agents are directed to the Fairway contract before editing.
Re-running fairway init preserves an edited .fairway/AGENTS.md. Use
fairway init --refresh-agent-contract only when intentionally replacing the
local breadcrumb with the current generated contract. New projects receive the
minimal working-memory routine directly in that generated contract. Existing
projects inherit it only after an intentional refresh so local agent
instructions are never silently overwritten.
Current generated contracts are versioned managed files. Inspect or update them with:
fairway agent-contract status
fairway agent-contract plan
fairway agent-contract apply
Keep repository-specific additions in .fairway/AGENTS.local.md. For an older
unversioned contract, fairway agent-contract apply --adopt-legacy preserves
the prior file there before writing the managed contract. Binary upgrades do
not change project process unless the embedded agent-contract revision changes.
For agents without a Fairway source checkout or network access, the installed binary carries this guide:
fairway agent-guide
fairway agent-guide --path
fairway agent-guide --output .fairway/agent-guide.md
First Rule
The Fairway DB is the execution source of truth. Do not edit queue state files,
SQLite rows, or generated dashboard artifacts directly. Use fairway commands
so claims, evidence, handoffs, reviews, sessions, checkpoints, and audit history
stay consistent.
Coordination Budget
Fairway coordinates engineering work; it is not a reason to multiply work units, reviews, validation runs, or waiting states.
- Keep related findings in the current task when they share an owner, implementation boundary, and acceptance proof. Create another task only for a distinct owner, an independently deliverable slice, or an explicit deferral.
- Use focused tests while iterating. Run broad CI, UAT, exhaustive security scans, and release gates once at the coherent commit or release boundary.
- For security assessment, prefer one full baseline scan followed by one committed-diff closeout scan. Retry an identical externally failed scan no more than once. Preserve partial artifacts and switch to a bounded differential check instead of repeatedly restarting the same expensive scan.
- Record a checkpoint when the objective, owner, blocker, decision, evidence, or next action materially changes. Do not checkpoint routine command progress.
- A review request should cover the coherent boundary. Do not stop for a new review packet after each same-owner correction; incorporate requested changes and return one refreshed boundary.
- If coordination consumes more time than implementation, or 30 minutes pass without new code, evidence, or a decision, perform a causal reset and remove unnecessary tasks, reviews, waits, or repeated gates.
Execution Surface Limits
Provider surfaces are replaceable execution attachments, and some surfaces have local sandbox limits. A Desktop-hosted provider may read the repo and edit workspace files but still fail at git or cache boundaries. Treat these as execution-surface findings, not as task logic failures.
Known symptoms:
fatal: Unable to create '.git/index.lock': Operation not permitted
open ~/Library/Caches/go-build/...: operation not permitted
browser launch or local capability probe fails only on one provider surface
Use the right surface instead of burning provider turns on a known boundary:
- for Go commands from sandboxed Desktop surfaces, set
GOCACHE=/tmp/fairway-go-cache; - for reviewed git stage/commit/push boundaries, use a tmux or SSH lane started outside the Desktop sandbox and capture the output back into Fairway evidence;
- for browser, SSH, Kubernetes, or filesystem-sensitive work, run a non-live capability probe on the exact execution surface and retire failed surfaces for that task/scope until replacement proof exists.
The intended split is:
Fairway decides and records.
Desktop threads coordinate and review.
tmux/SSH/provider lanes execute only the approved command boundary.
Do not repeatedly debug .git/index.lock in a Desktop provider after verifying
there is no stale lock and a normal terminal can write the index. Route the
approved command boundary to the configured git lane, record the commit SHA,
rerun merge-ready, and continue.
Start Of Work
fairway task-detail <task-id>
fairway claim <task-id>
fairway checkpoint record <task-id> --state active --owner <role> --summary "Started bounded work"
Run config validation, role preflight, and ready-queue discovery when the configuration, tool version, repository, role, or selected task changed. Do not repeat them mechanically before every edit.
Short direct work expected to complete in one burst does not require a provider session if the task is closed, reset, blocked, or handed off before the burst ends. Register a provider session for delegated, parallel, long-running, approval-gated, deploy/UAT, or high-risk work.
For tmux-backed lanes, especially provider sessions that cannot be inspected by the host application directly, register enough metadata for coordination:
fairway session upsert \
--id <session-id> \
--role <role> \
--provider <codex|claude|gemini|shell> \
--backend tmux \
--name <tmux-session-name> \
--tmux-pane <session:window.pane> \
--transcript <path-to-transcript> \
--task-id <task-id> \
--pid <pid> \
--worktree <path> \
--branch <branch>
fairway checkpoint record <task-id> \
--state active \
--owner <role> \
--summary "Started tmux-backed provider lane; transcript: <path-to-transcript>"
The example tmux adapter performs the same registration, transcript capture, and initial checkpoint in one provider-neutral command:
FAIRWAY_PROVIDER=claude \
FAIRWAY_PROVIDER_COMMAND="claude" \
FAIRWAY_TRANSCRIPT=".fairway/transcripts/claude-<role>-<task-id>.log" \
examples/session-adapters/tmux.sh <role> <task-id>
For prompt-file based lanes that do not need tmux, use session launch as the
repeatable provider attachment. Dry-run first so the coordinator can see the
provider command, prompt file, transcript path, worktree, branch, and session
metadata before anything starts:
fairway session launch \
--role <role> \
--provider <codex|claude|gemini|shell> \
--task-id <task-id> \
--prompt-file prompts/<track>/<task-id>.md \
--transcript .fairway/transcripts/<provider>-<role>-<task-id>.log \
--command "<provider-command>" \
--dry-run
Run the same command without --dry-run after review. It records the session
and an initial checkpoint, but it does not claim the task or mark it done.
Fairway coordination should work through task state, evidence, handoffs, checkpoints, and session records. Provider-specific chat history is useful, but it is not the coordination source of truth.
Thread Steering Vs Fairway Notification
Fairway handoffs, Fairway notifications, and Codex Desktop thread steering are different operations.
Definitions:
| Term | Meaning |
|---|---|
| Fairway handoff recorded | A durable task handoff/checkpoint exists in Fairway. This does not prove a provider thread received a prompt. |
| Fairway notification recorded | Fairway has a notification row or adapter event. This is proof of routing state, not proof of review or completion. |
| Thread steered | A prompt was actually sent into an existing provider thread using a host tool such as send_message_to_thread. |
| Thread checked | The target thread was read after steering, and its response/status was reconciled back into Fairway. |
Do not claim "sent to the thread" unless the host tool accepted the message for that thread. If only Fairway was updated, say "Fairway handoff recorded; thread/manual steering still required."
When the host environment exposes desktop thread tools, use this sequence:
- Discover tool availability before claiming capability. For Codex Desktop,
look for
send_message_to_thread,read_thread, andlist_threads. - Confirm the target thread id when needed with
list_threads. - Send the exact prompt with the thread messaging tool.
- Record a Fairway handoff, notification, or checkpoint that includes the
target thread id, role/domain, task id, and prompt summary. Use
--state thread_steeredonly after direct thread tooling accepts the message. - Later read the same thread to determine whether it is working, waiting, blocked, complete, or asking for input.
- Record the result back into Fairway as evidence, review, checkpoint, notification acknowledgement, or handoff. Provider chat is not durable authority.
When thread tools are not exposed:
-
Record the Fairway handoff/checkpoint/notification normally. Use
--state handoff_recordedwhen Fairway state was updated but no provider/thread delivery proof exists. -
Produce a clearly labeled manual relay block:
Manual thread relay requiredtarget_thread: <thread-id>role_or_domain: <review|ops|security|backend|frontend|architecture|orchestrator>task: <fairway-task-id>prompt:<exact text to paste> -
Do not claim the target thread was steered.
-
Continue other non-conflicting ready work if available; otherwise leave an
awaiting_inputcheckpoint naming the missing manual/thread relay.
Review steering prompts should include the repo path, task id, commit or worktree path, changed files, validation already run, requested review domains, and the expected verdict format: approve or changes requested with concrete blockers.
Durable lane, replaceable provider attachment: a Fairway lane or track is the durable coordination identity. Provider sessions are replaceable execution attachments. A long-lived provider session may carry useful working memory, but the lane can move between Codex, Claude, Gemini, tmux, or shell without changing task identity, ownership, checkpoints, evidence, reviews, or merge gates.
For long-running tracks, use Fairway track memory as the durable resume source:
fairway memory show --track <track-id>
fairway memory update --track <track-id> ...
fairway memory packet --track <track-id> --for <provider>
Legacy local tmp-ux/*memory*.md files are migration inputs only. Do not create
new project-local files as a parallel memory authority. Temporary drafts and
experiments may still use tmp-ux, but a provider must be able to resume from
the repository and Fairway without those files.
Project-owned engineering knowledge is separate from execution memory:
fairway knowledge init
fairway knowledge status
fairway knowledge lint
fairway knowledge lint --fail-on-warning
fairway knowledge ingest --source docs/design/example.md --page architecture/example.md --owner architecture --review-by 2026-10-22
fairway knowledge query --task FW-375 --topic "knowledge lifecycle" --format packet
fairway knowledge promote architecture/example.md --target docs/design/example.md --reviewed-commit <sha>
Knowledge pages are derived context, not task state or canonical authority.
Treat lint warnings as owner work and lint errors as unsafe knowledge state;
use --fail-on-warning only when the project intentionally promotes warnings
into a CI gate. A canonical source class conflicts with source frontmatter that
declares source_of_truth: false; canonical sources that declare
implementation_state: not-assessed remain queryable but produce a warning.
Neither command approves architecture, risk, merge, release, or deployment.
Ingest and promotion are preview-first and write only with explicit --apply.
Ingest creates a source-linked draft and index entry without copying source
content. Query selects a bounded set of indexed pages with status and
deduplicated provenance labels. Its packet reports the current repository
revision and whether each page is current at that revision, current at its
recorded source revision, stale, or unverifiable. Promotion fails closed unless
the page and citation chain are verified and current, the target is under a
configured canonical root, and its bytes match the explicitly supplied
reviewed commit.
Apply records only a normal Git diff; it never creates review approval or
writes the canonical target.
Use memory cold-start --knowledge-topic <text> when domain context is needed
for a resume. Execution memory renders first. Optional knowledge has a separate
--knowledge-budget-bytes limit and cannot displace the current objective,
blocker, stop condition, or next action. Shared Fairway evidence references are
rendered once: when knowledge cites evidence already named by track memory, the
knowledge provenance entry owns the rendered identity, carries
memory_referenced: true, and preserves its highest authority label. The
embedded memory packet omits only that duplicate evidence ID.
Use the active backlog selected by .fairway/config.toml as the implementation
queue. In this repository that is
docs/roadmap/fairway-product-backlog.yaml. Treat examples/*.yaml and
docs/archive/*.yaml as source material or provenance only. If a candidate task
from those files becomes active work, promote it into the active backlog and
import/reconcile the DB before claiming it.
When reviewing a task, check Fairway DB visibility before treating a YAML text search miss as a blocker. The runtime DB is authoritative for task status, evidence, handoffs, notifications, reviews, sessions, checkpoints, batches, and usage, and it can contain valid tasks that have not yet been exported back to a configured YAML queue file. Use:
fairway task-detail <task-id>
fairway list --status todo --status in_progress --status blocked --status done
fairway reconcile active --dry-run
fairway db export .fairway/fairway-state-snapshot.json
Escalate a YAML miss only when it indicates real config/import/export drift or
when the reviewer needs a portable queue artifact that has not been provided.
Do not block an implementation solely because rg <task-id> <queue-source.yaml>
does not find a DB-visible task.
Product boundaries are explicit: Fairway coordinates work; it does not auto-claim, auto-approve, auto-merge, auto-push, deploy, perform destructive cleanup, store provider credentials/transcripts/prompts by default, or gate completion on provider usage. See design/product-boundaries.md before adding a new adapter, controller, tracker, usage, or release automation path.
For approval-gated consumer critical flows, use the Fairway template in design/consumer-critical-flow-governance.md. The template keeps the durable rule explicit: flow map before implementation, non-live preflight before live window, bounded retry before causal reset, and Fairway evidence before handoff. Consumer repos own product scripts, fixtures, runbooks, and evidence contracts; Fairway owns the reusable coordination state, packets, waits, notifications, and review/handback evidence.
Active Work Visibility
Dashboard wall visibility is driven by both task state and session state. A task
marked in_progress tells Fairway the task is claimed. A running
agent_sessions row tells Fairway which provider attachment is actively working
that task. Agents must keep both current.
Use this order when starting or switching to a task:
# 1. Register or refresh the provider attachment.
fairway session upsert \
--id <stable-session-id> \
--role <provider-role> \
--provider <codex|claude|gemini|shell> \
--backend <codex-thread|tmux|zellij|shell> \
--task-id <task-id> \
--status running \
--worktree <path> \
--branch <branch>
# 2. Claim the task, or keep the existing claim if the coordinator already did.
fairway --as <task-owner-role> claim <task-id>
# 3. Record the active checkpoint, or emit a provider-event "started" event.
fairway checkpoint record <task-id> \
--state active \
--owner <provider-role> \
--summary "<provider> session <stable-session-id> active on <task-id>"
# 4. Confirm the session/task link is visible.
fairway session status --all
If step 2 returns "already claimed" and the claim belongs to the expected lane, continue with steps 3 and 4. If it belongs to another lane, stop and hand off or ask the coordinator to reassign it.
When a provider switches tasks, upsert the same session ID with the new
--task-id, record a completion, blocked, or handoff checkpoint for the old
task, then record an active checkpoint for the new task. Do not leave a
running session pointed at stale work.
The provider role and task owner role can differ. For example, an orchestrator
Codex thread may temporarily execute a backend task. In that case, keep the task
owned by backend for routing/review, but register the session with
--role orchestrator --provider codex --task-id <backend-task-id>. The session
row explains who is attached; the task definition explains who owns the work.
If an active provider is not registered, the wall can show an in-progress task without a live session. That is a coordination gap. Fix it by upserting the session and recording an active checkpoint; do not assume the dashboard can infer provider state from task status alone.
Short direct coordinator/orchestrator work is the exception. A coordinator may
briefly work a task without registering a provider session when the work is
expected to finish in one short burst, the task has a fresh checkpoint naming
the active owner, and the task will be closed, reset, blocked, or handed off
before the burst ends. In that case the wall may show in_progress without session; read it as intentionally un-attached only while the checkpoint is
fresh. High-risk stabilization, UAT, production-readiness, delegated provider
work, tmux/Claude/Codex external work, or anything expected to span multiple
checkpoints must register a provider session and emit a started provider
event.
Delegated Provider Sessions
When one agent delegates work to another provider session, the delegating agent must keep the coordination loop explicit. Starting or steering a child session is not enough; the parent needs a watcher or heartbeat that notices when the child needs attention.
Minimum delegation checklist:
- register the provider session with
fairway session upsert, - associate it with the current Fairway task,
- immediately feed a
startedevent throughprovider-event.shso the delegated session creates anactivecheckpoint, - feed provider runtime state through
provider-event.shor equivalent Fairway commands, - record
awaiting_inputcheckpoints for approvals, questions, failures, or stale/no-progress states, - record a completion checkpoint plus evidence or handoff when the delegated session completes,
- leave task status, review approval, and merge readiness to normal Fairway gates.
Approval-sensitive steps are explicit coordination events. If a delegated session reaches a step that may require human approval or coordinator-side execution, such as staging, committing, pushing, dependency installation, remote changes, privileged commands, or destructive cleanup, it should not wait silently in provider chat. It must:
- record an
awaiting_inputcheckpoint with the exact blocked operation, - notify the coordinator session with the command it wants to run, the verification already completed, and the current git/Fairway state,
- wait for the coordinator to either perform the operation, grant permission, or redirect the task,
- reconcile after notification so it does not create duplicate commits, pushes, reviews, or status changes.
For Codex-backed sessions, coordinator notification is usually a follow-up message to the owning Codex thread. For tmux/Claude/Gemini/shell lanes, use the provider watcher, transcript bridge, or manual checkpoint plus the team’s chosen coordinator channel. In all cases, the Fairway checkpoint is the durable signal; provider chat is only the transport.
When a task stalls on current vendor, platform, or provider behavior, do not burn hours inside one provider session. After one serious local evidence pass, consult a second current-info source such as Gemini, web search, vendor docs, or another agent with relevant context. This is especially useful for Apple signing/notarization, Cloudflare, Pomerium, GitHub/GitLab runners, Homebrew policy changes, Kubernetes/kind, container registries, MAAS/LXD, OpenClaw, Keycloak, and provider-specific network or deployment behavior.
The second source is advisory, not task authority. Validate the finding locally or against the target environment, then record a Fairway checkpoint or evidence row with the original symptom, the source consulted, the confirmed interpretation or rejected hypothesis, and the next action. If the finding is a real platform prerequisite, block the exact task only when no safe progress remains; otherwise create a scoped follow-up and continue the next ready task.
Provider-specific watchers, such as a Codex thread monitor, should live outside Fairway core. Their job is to read provider runtime state and translate it into provider-neutral Fairway facts:
waiting_on_approvalorwaiting_on_inputbecomes a checkpoint withstate=awaiting_inputand a summary of the requested action.completedbecomes adonecheckpoint plus evidence or a handoff, then the owning task can move through normal Fairway gates.failedbecomes a blocked checkpoint or task status with the failure reason.- stale/no-progress sessions become stale session records or checkpoints, not silent background work.
For Codex-style delegated threads, the operating pattern is:
# 1. Register the delegated session.
fairway session upsert \
--id <codex-thread-id> \
--role <role> \
--provider codex \
--backend codex-thread \
--task-id <task-id> \
--worktree <path> \
--branch <branch>
# 2. Run a provider adapter or heartbeat outside Fairway core. A provider
# monitor can call the generic event adapter whenever runtime state changes.
examples/session-adapters/provider-event.sh \
--provider codex \
--backend codex-thread \
--external-session-id <codex-thread-id> \
--role <role> \
--task-id <task-id> \
--state waiting_on_approval \
--summary "<short reason>" \
--transcript <path-to-transcript>
# 3. If no adapter is available, record the equivalent Fairway fact manually.
fairway checkpoint record <task-id> \
--state awaiting_input \
--owner <role> \
--summary "Delegated Codex thread is waiting on approval: <short reason>"
Provider and utility adapters must read existing session state from
fairway --json session status --all, not from human table output. If the JSON
cannot be parsed, a matching session is missing required fields, the session is
attached to another task, or the session is already terminal, the adapter must
refuse the event and leave task state unchanged.
When the coordinator handles the blocked operation, it should notify the delegated session with the resulting commit, push, or command outcome and tell the delegated session whether to stop, continue, or only report final summary. That avoids duplicate git operations while still keeping the implementation session’s working context intact.
The generic event adapter supports started, running, waiting_on_approval,
waiting_on_input, completed, failed, stale, and no_progress. It
refreshes the session record first, then records
the mapped checkpoint, evidence, handoff, or stale-session event. Use started,
waiting/stale/failure states, and completed as mandatory lifecycle events for
active delegated sessions; use running only as a metadata refresh.
fairway reconcile active checks that provider sessions have the matching
lifecycle checkpoint for their attached task. Running or started sessions need
an active checkpoint that names the Fairway session id, external session id,
or transcript artifact. Waiting, failed, stale, and no-progress sessions need
an awaiting_input checkpoint. Completed sessions need a done checkpoint.
The finding is advisory and provider-neutral; it tells the coordinator which
Fairway checkpoint is missing without polling provider APIs.
Do not make Fairway depend on Codex, Claude, Gemini, or any provider API. Fairway should expose the session/checkpoint/evidence surfaces; provider watchers should feed those surfaces.
CI Monitor Utility Rule
CI, deploy, smoke, and UAT polling should be delegated to watcher utilities instead of long-running agent conversations:
Agents do not poll CI. Watchers poll CI and emit Fairway handbacks.
Agents act on handbacks.
Agents should start or link the monitor, record the expected wait window, then switch to safe non-conflicting work or pause. The monitor utility should poll, record heartbeat/checkpoint state, attach evidence, close the monitor session, and emit a handback or resume-needed finding. Agents act on those handbacks; they should not spend provider tokens repeatedly checking the same pipeline status.
Use the provider-neutral CI monitor adapter when a project wrapper can supply a poll command:
examples/session-adapters/ci-monitor.sh \
--task-id <task-id> \
--batch-id <batch-id> \
--monitor-kind ci \
--external-run-id <pipeline-or-run-id> \
--poll-command "<command that prints success/failure status>" \
--source-sha "$(git rev-parse HEAD)" \
--manual-until <date-or-rfc3339> \
--artifact <pipeline-url-or-log> \
--dry-run
Remove --dry-run after checking the generated Fairway commands. The adapter
records monitor session proof, watcher lifecycle, heartbeats, evidence, and the
final reconcile active --dry-run handback. On failed, timeout, or stale runs
it recommends a follow-up prefix such as CI-FIX, CD-FIX, UAT-BUG,
OPS-FIX, HARNESS-FIX, or DOC-FIX; it does not create tasks unless a
project wrapper chooses to do that explicitly.
Before launching separate CI runs for multiple small tasks, check whether they can be grouped into a work batch with one branch, one validation command set, one review path, and one CI/deploy-run. Use separate runs only when ownership, rollback, risk, sequencing, or failure diagnosis requires it.
Do not treat every provider thread branch as a remote CI branch. Local
worktrees and scratch branches are isolation tools. Remote pushes are promotion
events and need an explicit push intent such as main-validation,
integration, review, release, backup, or exception. In the normal
flow, worker/provider threads validate locally and hand off to the coordinator
or reviewer/merge lane; that lane merges to the configured main branch and
pushes one integrated batch for CI.
Tool-First Operating Rule
Do not route deterministic, repetitive, or pollable work through an agent just because an agent can do it. Prefer this order:
- utility or script for deterministic/pollable work,
- report or classifier for repetitive summarization,
- lightweight provider session for bounded implementation or docs,
- high-context agent/reviewer for judgment, architecture, release, or risk.
Fairway should make tool output easy to consume. A utility should emit enough structured state for Fairway to record:
- task id, batch id, role, and provider/tool name;
- command or external run id;
- started, heartbeat, completed, failed, timeout, or stale state;
- evidence artifact paths or URLs;
- recommended next action;
- whether a human/agent decision is required.
Agents should act on those recorded facts instead of re-running status checks or re-summarizing raw logs in conversation. If a repeated workflow cannot be captured by a utility yet, create a Fairway backlog task for the missing tool surface rather than adding more provider-routing rules.
Use the generic utility event adapter for deterministic checks that are not CI/deploy polling loops:
examples/session-adapters/utility-event.sh \
--task-id <task-id> \
--batch-id <batch-id> \
--utility-name codegen-drift \
--utility-kind codegen \
--command "make codegen-check" \
--external-run-id <run-or-scan-id> \
--source-sha "$(git rev-parse HEAD)" \
--artifact dist/codegen-drift.log \
--state completed \
--recommended-next-action "continue review; generated artifacts are clean"
Supported utility states are started, heartbeat, completed, failed,
timeout, and stale. Terminal utility events record checkpoint state,
evidence, watcher/session closure, and a reconcile active --dry-run handback.
Use --decision-required when a human or agent must choose the next action.
Provider Usage Accounting
Provider usage is attribution and planning telemetry, not a completion gate.
Record counts and metadata only. Do not store prompts, transcripts, secrets,
model inputs, generated content, messages, cookies, or provider API tokens as
usage metadata. Unknown values stay unknown; do not report unavailable token
counts as 0.
Adapters should emit usage through the generic Fairway command path:
fairway record usage <task-id> \
--provider codex \
--session-id <fairway-session-id> \
--external-session-id <codex-thread-id> \
--role <role> \
--phase implementation \
--source provider_reported \
--confidence exact \
--input-tokens <n> \
--cached-input-tokens <n> \
--output-tokens <n> \
--total-tokens <n>
If the provider only exposes running totals, record start/end snapshots:
fairway record usage <task-id> \
--provider codex \
--source derived_snapshot \
--confidence estimated \
--started-token-snapshot <n> \
--completed-token-snapshot <n>
If usage is unavailable but the session should still be attributed, record the
provider and session identity with --source unknown --confidence unknown and
omit numeric fields.
examples/session-adapters/provider-event.sh accepts the same usage fields,
for example:
examples/session-adapters/provider-event.sh \
--provider codex \
--backend codex-thread \
--external-session-id <codex-thread-id> \
--role <role> \
--task-id <task-id> \
--state completed \
--summary "implemented requested slice" \
--usage-source provider_reported \
--usage-confidence exact \
--usage-phase implementation \
--input-tokens <n> \
--cached-input-tokens <n> \
--output-tokens <n> \
--total-tokens <n>
For provider-supported OpenTelemetry, use the generic OTel bridge. It accepts
OTLP JSON logs, metrics, or traces from stdin or --input, maps only structural
usage metadata, and emits fairway record usage.
examples/session-adapters/provider-otel-ingest.sh \
--input dist/provider-otel.json \
--task-id <task-id> \
--role <role> \
--provider <codex|claude|gemini|shell> \
--dry-run
Remove --dry-run after checking the generated command. If the Fairway binary
is not on PATH, set FAIRWAY_BIN=/path/to/fairway. Task context should come
from OTel resource attributes when possible:
fairway.task_idfairway.session_idfairway.rolefairway.trackfairway.phasefairway.usage.sourcefairway.usage.confidence
Provider-specific OTel mappings, such as Codex response.completed and Claude
Code token metrics, should plug into this bridge or call fairway record usage
with already-normalized fields. Do not enable prompt, tool-body, raw API body,
auth-token, transcript, or generated-content telemetry for usage accounting.
For Codex specifically, use the Codex adapter rather than reading private Codex SQLite/auth/log/transcript files:
examples/session-adapters/codex-usage-adapter.sh \
--mode auto \
--input dist/codex-usage.jsonl \
--task-id <task-id> \
--session-id <fairway-session-id> \
--role <role> \
--dry-run
The adapter supports Codex-shaped OTel JSON, codex exec --json /
newline-delimited JSON with turn.completed.usage, including
reasoning_output_tokens, and explicit snapshot mode:
examples/session-adapters/codex-usage-adapter.sh \
--mode snapshot \
--task-id <task-id> \
--session-id <fairway-session-id> \
--started-token-snapshot <n> \
--completed-token-snapshot <n>
Remove --dry-run only after confirming the generated command contains counts
and metadata, not prompt text or generated content.
For Claude Code, enable usage-only OTel and keep content logging disabled:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
export OTEL_RESOURCE_ATTRIBUTES="fairway.task_id=<task-id>,fairway.session_id=<session-id>,fairway.role=<role>,fairway.phase=implementation"
unset OTEL_LOG_USER_PROMPTS
unset OTEL_LOG_RAW_API_BODIES
Then feed exported OTLP JSON through:
examples/session-adapters/provider-otel-ingest.sh \
--input dist/claude-code-otel.json \
--provider claude \
--task-id <task-id> \
--role <role> \
--dry-run
Claude Code token and cost metrics map into Fairway usage records; cost is preserved only as safe metadata for later planning, not pricing or gating.
Use fairway task-detail <task-id> or fairway usage report --by provider to
inspect recorded usage. Rollups are available by provider, task, epic,
role, day, kind, and phase.
If you need machine-readable output, put global flags before the subcommand:
fairway --json ready
fairway --json task-detail T-001
fairway --json workflow check --mode deploy
Use FAIRWAY_ROLE=<role> or --as <role> when the current worktree cannot be
resolved to one configured role.
If your repo uses workstream profiles, read the configured profile before claiming profile-shaped work:
fairway config validate
fairway adoption artifact --limit 5 --gap-limit 5
The adoption artifact shows configured gate modes, named profile gates, route
samples, and evidence-backed gate evaluation. fairway merge-ready also checks
the profile gates for the target task: missing blocking gates fail readiness,
while missing advisory and report_only gates appear as warnings. Treat
advisory gates as evidence expectations, not as optional background noise.
If your repo uses rule packs, treat them as reusable operating knowledge for the task. Rule packs do not approve work by themselves. They identify applicable rules, expected evidence, review domains, recommended commands, and stop conditions. Record selected rules and non-applicable rationale as evidence when the project requires it.
When adopting a new project rule source, follow the checklist in
docs/design/rule-packs.md#project-adoption-checklist: verify the local path,
choose advisory/blocking/disabled mode deliberately, check review-domain
vocabulary, run fairway config validate and fairway rules validate, add CI
validation, and start advisory before promoting a source to blocking.
Profile-shaped work should carry task metadata when the coordinator or dashboard needs architecture context:
fairway add T-010 \
--title "Map platform evidence ownership" \
--role arch \
--kind architecture-map \
--profile platform-foundation \
--owning-domain platform \
--owning-layer service \
--source-paths cmd/api,packages/services \
--source-paths docs/api \
--review-domains architecture,backend \
--review-domains governance \
--acceptance "current owners are mapped" \
--acceptance "target owners and review routes are mapped" \
--risk-level medium \
--migration-type ownership-map
--acceptance, --source-paths, --target-paths, and --review-domains are
repeatable. The path/domain flags also accept comma-separated values, so use
repeated flags when it keeps long task definitions readable. task-detail
renders acceptance checks as separate bullets and path/domain metadata as
flattened lists in the order supplied.
When you spawn follow-up work, Fairway inherits the parent task metadata unless you override a metadata flag explicitly. Keep those fields accurate; they drive review routing, readiness reports, and dashboard workstream grouping.
Work Batches
Use work batches when several granular tasks share the same implementation and validation surface. The task remains the accountability unit; the batch is the branch/worktree/CI/deploy-run/evidence unit.
Batch by shared validation surface when tasks have the same domain, touched contracts, review domains, rollback behavior, and proof commands. Do not batch when ownership, review routing, rollback, live-environment risk, or dependency order need independent proof.
Typical coordinator flow:
fairway batch create BATCH-001 \
--title "Platform facade validation slice" \
--branch feature/platform-facade \
--worktree ../worktrees/platform-facade \
--task PF-101 \
--task PF-102 \
--validation-command "go test ./..." \
--validation-command "npm test" \
--review-domain arch \
--review-domain backend \
--rollback-criteria "revert the shared branch if API contract changes fail" \
--split-criteria "split if frontend and backend failures need different owners" \
--expected-ci "GitHub Actions platform validation"
After shared validation, record the batch evidence once and map it to member tasks:
fairway batch evidence BATCH-001 \
--command-text "go test ./... && npm test" \
--result pass \
--artifact https://ci.example/runs/123 \
--artifact-type ci
fairway batch link BATCH-001 --pipeline-id gha-123 --deploy-run-id deploy-456
fairway batch show BATCH-001
batch evidence maps proof to every member task by default. Only use
--map-to-tasks=false when the evidence is a batch note and does not satisfy
any individual task acceptance check. Individual tasks still move through
normal set-status, review, profile gates, and merge-ready; a passing batch
does not close tasks automatically.
Before review or deploy boundaries, run:
fairway audit work-coverage --dry-run --since-duration 24h
The audit reports work_batch_candidate when several related same-domain tasks
have separate CI/deploy evidence and are not already in a batch. Treat that as
a signal to batch future work when it shares validation, not as a retroactive
requirement to rewrite history.
Workflow Guard
Use workflow check at task, review, and deploy boundaries. It keeps the
operating model short by turning the repeated manual checks into one command.
Minimal Working-Memory Routine
Use working memory only for tasks that cross provider replacement, context loss, long waits, or multiple execution bursts:
start: memory cold-start --track <task-id>
gear shift: record one checkpoint and update memory only when objective,
blocker, decision, or next action materially changes
closeout: workflow closeout <task-id> --dry-run
When closeout reports terminal_task_active_memory, choose one disposition:
promote stable cross-task value into a reviewed canonical target, or archive the
completed execution memory. Do not keep completed guidance active and do not
create a review task solely to make this decision. Short single-burst tasks do
not require a memory record.
# Normal task boundary: warns on dirty files and unpushed commits.
fairway workflow check
# Close/review boundary: fail if the task slice is not committed and the lane
# has unresolved branch/worktree/session closeout debt.
fairway workflow check --mode close --task-id <task-id> --require-clean
fairway workflow closeout <task-id> --dry-run
# Deploy/UAT boundary: require clean pushed source and active-work
# reconciliation before creating deploy evidence.
fairway workflow check --mode deploy --require-clean --require-pushed
# Coverage boundary: check whether commits, files, evidence, and reviews map
# back to Fairway task metadata.
fairway audit work-coverage --since-ref main --dry-run
# Outcome boundary: attach explicit operational or corrective outcomes. Do not
# infer incidents, rollbacks, or corrective links from prose.
fairway record outcome T-001 --kind incident --source-ref INC-1042
fairway record outcome T-001 --kind corrective --related-task T-017
fairway record outcome T-001 --kind reopen --transition-id 418
# Learning boundary: classify failed CI/deploy/smoke/UAT evidence and confirm
# actionable failures have follow-up tasks.
fairway audit ci-learning --template
# Coordination-design boundary: check whether docs, command examples, and
# incident lessons map to Fairway backlog tasks.
fairway audit docs-backlog
# Process-intelligence boundary: measure whether review/gate overhead is
# improving speed, quality, or safety.
fairway delivery report --since 168h
# Automation boundary: find repeated deterministic work before it becomes
# recurring LLM scheduling or bookkeeping.
fairway automation candidates --since 168h
The command reports:
- dirty docs/code and the right next action;
- commits that have not been pushed, so CI has not run;
- missing upstream tracking;
- active reconciliation findings such as
in_progresswork without a session or evidence without a status decision; - deploy-run guidance for CI/CD/UAT attempts;
- lane closeout findings such as missing reviews, active sessions/watchers, dirty worktrees, unmerged branches, remote branch leftovers, and explicit branch preservation reasons.
audit work-coverage is advisory. It exposes observed, eligible, covered, and
excluded denominators. Commit coverage uses only task IDs in commit metadata
and canonical task commit_sha links; path ownership is reported separately
and cannot inflate that numerator. The report also lists the resolved analyzed
tip and configured generated or high-churn path exclusions. For completed tasks
with a canonical commit it additionally
reports mature 7/14/30-day same-file touch facts and separately lists explicit
structured outcomes. A later touch is not silently called a defect. The audit
also catches changed files outside task source_paths / target_paths,
evidence that still needs a status decision, done tasks without required
evidence, and missing review-domain approvals. Run it before review handoff,
deploy/UAT attempts, release readiness checks, and control-effectiveness
analysis.
control report is the canonical read-only control-effectiveness surface:
fairway control report --since 720h \
[--profile fairway-adoption] [--control review:security] \
[--format text|json]
The report first shows commit/task and changed-file coverage, then compares
mature tasks with an observed control only against tasks with an explicit
policy-backed waiver or deferral. Missing control records and skipped evidence
without a durable actor and stable row identity remain unknown; immature
tasks remain right_censored; unavailable Git outcome facts remain outside
the outcome denominator. Results are stratified by profile, risk,
eligible-file size band, and 7/14/30-day horizon.
Classifications are discriminating, insufficient_sample,
insufficient_coverage, high_friction, mandatory_invariant, or
redesign_candidate. They are observational recommendations. The command
cannot approve or waive a review, change a rule, mutate task state, merge,
deploy, or release. In particular, redesign_candidate means only: no
measurable incremental signal under the current sample, coverage, risk
controls, and outcome definition.
audit docs-backlog is advisory. It scans coordination design docs for task
ids, task path coverage, documented Fairway command examples, and known
coordination topics. Run it after incident retrospectives, design reviews, and
large coordination-model updates so doc-only capabilities, stale completed
tasks, and consumer-specific lessons that should become Fairway product tasks
do not remain hidden in chat or local notes. The audit does not change task,
review, merge-ready, or release state.
delivery report is advisory. It measures delivery velocity and process
overhead from existing Fairway task transitions, evidence, reviews, handoffs,
notifications, and wait projections. Use it when tuning review profiles or
process pilots to compare overhead with outcomes such as defects caught,
rework, blocked time, cycle time, and avoided unsafe actions. It does not make
metrics into review, merge, deploy, or release gates.
automation candidates is advisory. It groups repeated command, evidence, and
notification patterns and recommends a likely implementation surface such as a
script, Fairway CLI command, dashboard panel, watcher, or packet template. Use
it to apply the manual-once, checklist-second, automate-third rule. It does not
auto-create tasks or mutate workflow.
audit ci-learning is advisory. audit failure-routing is the same read model
with known-failure routing help text and a failure_routing_ok human status
label. It turns failed CI, deploy, smoke, UAT, and
coordination evidence into a learning record: failure class, root cause, missed
gate, expected local reproduction command, owner, owning domain/layer, evidence
artifact path, suggested follow-up prefix/kind, and forbidden actions until
review. It recognizes artifact contracts, provider API/4xx behavior, browser
surface failures, setup gates, callback gaps, redaction findings, uncommitted
reviewed files, and undelivered review handoffs. It also checks that actionable
failures have a CI-FIX-*, CD-FIX-*, OPS-FIX-*, HARNESS-FIX-*,
UAT-BUG-*, or DOC-FIX-* task. The report recommends follow-ups only; task
creation still requires an explicit operator command, dry-run/apply workflow,
or configured policy.
advisory validate checks optional advisory-provider output before anyone
treats it as useful coordination input:
fairway advisory validate T-001 \
--action render_packet \
--target-role backend \
--confidence 0.74 \
--rationale "retry packet should be refreshed from recorded task facts" \
--cited-fact "task:T-001 status=blocked" \
--record-evidence
Accepted actions are advisory only: inspect_task, route_review,
record_evidence, refresh_memory, render_packet, create_follow_up,
wake_provider, run_preflight, and record_checkpoint. Risk flags require
--requires-human; cited facts must point at Fairway task/evidence/review/
checkpoint/session/handoff/notification facts. --record-evidence writes
advisory-recommendation evidence only. It does not approve reviews, accept
risk, claim work, merge, push, deploy, run live actions, or mutate an
environment.
For Fairway release attempts, create one release-run task/checkpoint and render
a release-run packet before tagging. Run the manual Release Rehearsal
workflow against the exact pushed main SHA, then bind its successful numeric
run id into the final annotated tag as
fairway-rehearsal-run: <run-id>. The tag workflow promotes that exact packet
without rebuilding:
fairway packet release-run <release-task-id> \
--version vX.Y.Z \
--tag vX.Y.Z \
--source-sha "$(git rev-parse HEAD)" \
--release-notes docs/release-notes.md \
--changelog-state "CHANGELOG.md updated" \
--ci-status pass \
--docs-status pass \
--signing-status pass \
--notary-status pass \
--release-url "https://github.com/fairway-run/fairway/releases/tag/vX.Y.Z" \
--homebrew-tap-commit <tap-commit-sha> \
--verification-command "brew fetch --cask --force fairway-run/tap/fairway"
After the GitHub release and Homebrew tap update are observable, run
fairway release verify. Do not mark a release as Homebrew-usable while the
GitHub release is still a draft; asset URLs can return 404 until the draft is
published.
For deploy work, create one deploy-run task for the attempt and create
CI-FIX-*, CD-FIX-*, UAT-BUG-*, OPS-FIX-*, HARNESS-FIX-*, or
DOC-FIX-* follow-ups only for actionable findings.
Shared Dashboard Access
If the dashboard is exposed to teammates through a tunnel or identity-aware proxy, run it in shared read-only mode. Read-only dashboard access is for observation, review context, and coordination visibility; it is not permission to mutate tasks. Record task changes, evidence, reviews, handoffs, and release actions through the Fairway CLI from a trusted local worktree.
Do not trust proxy identity headers unless the origin is reachable only through that trusted proxy/tunnel and JWT/header verification is in place. See dashboard-sharing.md.
Lane Closeout Rule
Fairway task completion is not the same as lane completion. The older lane worktree model had an important invariant:
finish task -> review/merge -> clean lane -> next task
Fairway must preserve that invariant. A lane should not claim the next
implementation task just because the current task is marked done. A lane is
ready for the next task only when its current task or batch has a recorded
closeout decision:
- task status is decided: done, blocked, todo/reset, or explicit follow-up;
- evidence is attached and acceptance checks are accounted for;
- required reviews are approved, waived with reason, or the task remains review-gated;
- the task or batch commit exists and is associated with the work;
- CI/deploy/UAT result is recorded when that work boundary applies;
- branch is merged and deleted from configured remotes, or intentionally preserved with a reason;
- role worktree is clean and on the expected branch;
- provider sessions, watchers, and monitor utilities are ended or intentionally still running with fresh checkpoints;
fairway reconcile active --dry-runand the relevant workflow check are clean or have explicit findings.
Treat this as the lane boundary:
task done != lane done
If a branch or worktree must remain after the task is done, record why. Common valid reasons are review pending, CI pending, blocked dependency, preserved release branch, follow-up batch, or operator-approved investigation. Branches left behind without one of those reasons are cleanup debt.
Use the closeout guard before moving a lane to the next implementation task:
fairway task-detail <task-id>
fairway merge-ready <task-id>
fairway workflow closeout <task-id> --dry-run
fairway workflow check --mode close --task-id <task-id> --require-clean
fairway reconcile active --dry-run
Then merge and delete the task branch, or record a preserve reason as evidence or checkpoint:
fairway workflow closeout <task-id> --dry-run \
--preserve-branch-reason "release branch retained until tag cut"
workflow closeout --apply deletes only a verified merged origin/<branch>
remote branch when the closeout report has no blockers. It does not delete
local branches or worktrees; operators still perform or approve those cleanup
commands explicitly.
Remote branch cleanup is downstream of push intent. If a provider thread created a scratch branch and it was never meant to be reviewed or validated remotely, do not push it. If it was pushed by exception, closeout must record why it was pushed and whether it was merged, deleted, or intentionally preserved.
Use the explicit intent command before pushing a worker branch remotely:
fairway record push-intent <task-id> \
--intent main-validation \
--branch main \
--remote origin
fairway record push-intent <task-id> \
--intent exception \
--branch scratch/<task-id> \
--remote origin \
--reason "operator requested remote backup before risky rebase"
workflow closeout and workflow check --mode close report a remote branch
without matching push-intent evidence as closeout debt.
Claim Work
fairway claim T-001
fairway task-detail T-001
Claiming moves a ready task to in_progress and records the owner/branch.
If another agent wins the claim first, Fairway returns an already-claimed error;
do not keep working that task unless the coordinator reassigns it.
For epic-sized work, claim the next ready descendant:
fairway claim --in E-001
During Work
Keep local notes however your agent runtime prefers, but record durable facts in Fairway:
fairway record evidence T-001 \
--command-text "go test ./..." \
--result pass \
--artifact dist/test.log \
--artifact-type test
Use pass, fail, partial, skipped, or blocked honestly. A skipped or
blocked check is better than undocumented silence.
If work becomes blocked:
fairway set-status T-001 blocked --reason "waiting for API fixture"
Blocked transitions require a reason in the default config.
Reconciliation Checkpoint
After every significant work burst, reconcile Fairway state before leaving the track:
fairway session status
fairway status-report
fairway ready
fairway list --status todo,in_progress,blocked
fairway watcher status
fairway reconcile active --dry-run
Do not guess CLI subcommands from natural language. Check fairway --help or
the group help first (fairway session --help, fairway watcher --help,
fairway workflow --help). Use fairway watcher status [--include-done] for
watcher rows; there is no fairway watcher list. Use fairway task-detail,
fairway tree, fairway ready, fairway list --status <state>, and
fairway update --dependencies for task and dependency inspection/update;
there is no grouped task command or depends shortcut in the current CLI.
Use fairway list --status todo --ready when an empty or surprising ready queue
needs dependency context.
Use fairway session reconcile --dry-run when you specifically want to inspect
session-local cleanup such as dead PIDs, missing tmux panes, or stale sessions.
Use fairway reconcile active --dry-run for the broader end-of-burst check.
The expected end-of-burst state is:
- active sessions are zero, or each running session is intentionally attached to a non-terminal task,
in_progresscontains only work that is actively owned or deliberately left open with a fresh checkpoint,- tasks with pass evidence are moved to
doneor have a documented reason they remain open, - tasks with fail, partial, skipped, or blocked evidence are moved to
blocked, reset totodo, or split into explicit follow-up work, - parent/backlog tasks are not left
in_progressunless the parent itself has a current rollup artifact or checkpoint.
Do not use todo to hide meaningful partial progress without recording a
handoff, checkpoint, or follow-up task. If the project config supports extended
states such as needs_followup, partial, waiting_for_prereq, or stale,
use those states consistently; otherwise use blocked with a clear reason or
create a follow-up task and close/reset the parent.
Approved live operations may record gate or runtime evidence while the task is
still in_progress, but only with a bounded closeout marker. Keep the provider
session running and record a fresh active checkpoint with --target-close-by
covering the operation window:
fairway checkpoint record <task-id> \
--state active \
--owner ops \
--target-close-by 2026-06-13T03:15:00Z \
--summary "Provider session <session-id> active; approved live operation window with expected closeout"
While that window is open, fairway reconcile active --dry-run treats evidence
recorded after activation as active evidence capture, not as final closeout
debt. This is temporary. If the session is missing, the checkpoint is stale, the
checkpoint has no open target-close-by, or the window expires, reconciliation
again reports status_decision_required until the task is explicitly moved to
done, blocked, todo, or a configured closeout state. Do not use this
pattern to park unbounded live work.
For repeated exact-window live operations, also record the current handshake
phase so the coordinator/control thread can see the loop state without polling
chat. The full architecture is in
docs/design/live-operation-control-room.md; the examples below are the CLI
surface for that model:
fairway live-window record <task-id> \
--phase packet-prepared \
--next-owner governance \
--next-action route exact-window reviews \
--artifact .fairway/artifacts/<packet>.md
fairway live-window record <task-id> \
--phase approvals_ready \
--next-owner architecture-control \
--next-action authorize operator handoff \
--authorization-state "approvals recorded; execution not authorized" \
--command "fairway live-window record <task-id> --phase execution_authorized" \
--prompt "Authorize the drill operator for the approved window" \
--target-close-by 2026-06-13T18:20:00Z \
--missed-deadline-action "escalate to Architecture Control and reschedule window"
fairway live-window record <task-id> \
--phase operator_running \
--next-owner ops \
--next-action run browser smoke \
--authorization-state "execution authorized" \
--target-close-by 2026-06-13T19:20:00Z
fairway live-window status --task <task-id>
fairway live-window control-room --stale
fairway coordinator plan
The supported phases are packet-prepared, reviews-routed,
approvals-readback, gate-authorized, gate-running, closeout, and
next-decision, plus live-operation control-room phases packet_ready,
approvals_ready, execution_authorized, operator_running,
closeout_required, done, and blocked. These records are normal
checkpoints with typed summaries, not a second phase store. Use them to name the
next owner, deadline, authorization state, exact prompt or command, and
missed-deadline behavior after every approval/execution/blocked/done/retry
handoff.
This is also a token-budget boundary. LLM/provider turns should not be used as the scheduler for routine live-operation waits. Fairway should hold the durable phase, next actor, deadline, exact action/prompt/command, authorization status, and missed-deadline behavior so provider turns can focus on judgment, implementation, review, and exception handling. Optional tmux or zellij control-room panes should make this state visible without every agent rereading chat or asking Architecture Control what happened.
Side Work
Do not split your assigned task into Fairway subtasks for ordinary execution steps. Use local scratch notes for that.
Use Fairway only when the orchestrator needs to see the work:
fairway spawn --id T-099 --title "Fix discovered billing route regression" --sibling
For long-running side tracks, create a packet and checkpoint:
fairway packet context T-001 \
--goal "finish API contract" \
--owner backend \
--acceptance "contract tests pass"
fairway checkpoint record T-001 \
--state active \
--owner backend \
--summary "waiting on API fixture"
Watcher work should use watcher packets and lifecycle records:
fairway packet watcher W-001 --owner C-ops/watch --process ci \
--command "gh run watch" --success "green" --failure "red"
fairway watcher start W-001 --task T-001 --owner C-ops/watch --process ci
fairway watcher finish W-001 --result pass --artifact dist/ci.log
fairway watcher status --include-done
Before leaving a CI/deploy/UAT/provider monitor active, prove that a real watcher exists. Record the automation id, PID, tmux pane, external run id plus polling command, or a manual checkpoint window with an explicit expiry. If a monitor task only creates Fairway session/task rows and no backing heartbeat or bounded checkpoint exists, reset or close it before ending the work burst. That state is stale bookkeeping, not live monitoring.
When the final monitored item completes, hand control back to the work loop. Close the deploy-run/watch tasks and monitor sessions, then record or send the next action: push held branches, start the next ready task, request review, or state that no ready work remains. A monitor heartbeat finishing successfully is not the same thing as the overall track being complete.
For Codex/Claude/tmux/provider-backed coordinators, this handback should be a real continuation prompt to the owning coordinator session whenever ready work remains. The monitor should not only delete its heartbeat and exit. Include the working-memory path, Fairway config path, completed monitor summary, and this instruction:
The monitored CI/deploy/UAT window is complete. Read the selected Fairway track
memory packet, check Fairway status and ready tasks, and continue with the next
non-conflicting task unless a documented stop condition applies. Record a
checkpoint explaining the selected next action.
If the monitor cannot send that provider prompt, record a resume_needed
checkpoint or finding with the coordinator session id and the next ready task
summary. Clean Fairway state plus ready work is not enough; the execution lane
also needs a continuation signal. fairway reconcile active reports the
fallback condition as monitor_completion_resume_needed when all monitors are
closed, no active sessions/watchers remain, and ready work is still queued. The
dashboard diagnostics tab shows the same finding.
Use provider-neutral session fields for monitor proof:
fairway session upsert \
--id ci-monitor-T-001 \
--role ops/watch \
--backend ci-monitor \
--task-id T-001 \
--status running \
--monitor-kind ci \
--automation-id gha-heartbeat-T-001
fairway session upsert \
--id deploy-run-T-002 \
--role ops/watch \
--backend deploy-monitor \
--task-id T-002 \
--status running \
--monitor-kind deploy \
--external-run-id deploy-123 \
--poll-command "gh run view deploy-123"
For a short manual monitor, record an active checkpoint with
--target-close-by <date>. After that date, fairway reconcile active reports
the monitor as monitor_session_without_backing_proof unless another backing
proof is attached. The dashboard diagnostics tab shows the same active
reconciliation finding.
Platform-foundation work should use the narrower packet type that matches the
task. If a repo defines [[packet_templates]], use those fields as the packet
contract. Generic templates render with fairway packet template <name>:
fairway packet template architecture-map T-010 \
--field scope="route ownership" \
--field current_owner=mixed \
--field target_owner=D-arch \
--field migration_risk="route moves can hide auth regressions" \
--field acceptance="owners and review routes are explicit"
Built-in packet commands remain available for common profiles:
fairway packet architecture-map T-010 \
--scope "route ownership" \
--current-owner mixed \
--target-owner D-arch \
--migration-risk "route moves can hide auth regressions" \
--source-doc doc/architecture/platform-foundation/ownership.md \
--acceptance "owners and review routes are explicit"
fairway packet boundary-guard T-011 \
--guard-intent "report imports across package boundaries" \
--finding "cmd/api imports billing internals" \
--false-positive "generated client code" \
--graduation-criteria "zero critical findings for two releases" \
--proof-command "go test ./..."
fairway packet retry T-011 \
--kind preflight \
--source-sha abc1234 \
--operator-surface local-shell \
--artifact-dir .fairway/artifacts/T-011/retry-001 \
--evidence-contract "preflight output and rollback proof recorded" \
--allowed-action "run non-live smoke" \
--forbidden-action "live execution" \
--expires-at 2026-06-14T21:00:00-05:00 \
--prior-failure-closure "prior setup failure fixed and reviewed" \
--next-action "record packet as evidence before retry handoff"
fairway packet vertical-slice T-012 \
--target-seam "platform evidence facade" \
--old-path cmd/api/evidence.go \
--new-path packages/services/platform/evidence.go \
--adapter "thin route adapter" \
--proof-command "go test ./cmd/api ./packages/services/platform" \
--rollback-plan "revert adapter wiring"
packet retry renders a bounded retry packet only. It does not authorize live
execution, approve risk, or replace Fairway status, review, and gate checks.
Handoffs
When work crosses a role boundary, hand it off instead of reaching across:
fairway record handoff T-001 \
--to ui \
--payload "Backend contract is ready; see dist/openapi-check.txt"
Use --payload @path/to/file for longer handoffs.
When a delegated provider is closing a task and the next required action belongs to another actor, record a completion handback instead of relying on chat state:
fairway record completion-handback T-001 \
--to ops \
--next-action "schedule the next exact-window drill packet" \
--completion-state blocked-with-follow-up \
--evidence .fairway/artifacts/T-001/closeout.md \
--approval-boundary "review-only handback; no deploy authority" \
--provider codex \
--target <thread-or-adapter-target> \
--state thread_steered
The command writes a normal handoff and a linked notification row. Use
--state notification_failed --reason "<why>" when delivery cannot happen; that
records the failure explicitly so the coordinator can decide the next relay
path. A pending cross-role completion handback (handoff_recorded) blocks
terminal closeout until delivery or failure proof is recorded. The handback does
not approve, merge, push, deploy, wake providers from the dashboard, or replace
task status closeout.
Use --completion-state for the closeout outcome, not for delivery proof.
Supported outcomes are done, reviewed, merge-ready,
blocked-with-follow-up, monitor-completed, live-window-closeout, and
live-window-next-decision. For repeated live-operation loops, a
live-window closeout or next-decision checkpoint without a completion
handback is surfaced by fairway coordinator plan as a closeout-to-next-owner
wait. Pending completion handbacks age by [coordinator].notification_ack_timeout
and become stale coordinator actions rather than silent idle work.
To render or record a bounded wake for stale completion handbacks, use the coordinator tick surface:
fairway coordinator tick --completion-handback-wake --task <task-id>
fairway coordinator tick --completion-handback-wake --task <task-id> --send --state thread_steered
The command uses fixed prompts, stable duplicate-suppression signatures, and
provider targets for the next owner. Missing targets are recorded as
notification_failed. Fresh waits and terminal tasks are not woken, and the
dashboard remains read-only.
A handoff is not the same thing as a delivered provider/thread message. When the coordinator actually sends, attempts, or receives acknowledgement for a provider notification, record that state separately:
fairway record notification T-001 \
--domain ui \
--provider codex \
--target <thread-or-adapter-target> \
--state notification_delivered
Use --state notification_failed --reason "<why>" when the provider target
could not be contacted. Use handoff_recorded when Fairway recorded the
routing state but no provider delivery proof exists, notification_delivered
when an adapter or provider confirms delivery, thread_steered only when
direct thread tooling accepted the message, review_acknowledged when the
target reviewer/control lane confirmed receipt, and review_recorded only when
the review was recorded in Fairway.
Notification state never substitutes for fairway record review, status
changes, merge, push, deploy, or release gates.
A review-gated task with missing required reviews and only a handoff, a failed
notification, or no delivered reviewer notification is notification-blocked,
not normal review wait. fairway coordinator plan, task detail, dashboard task
detail, and workflow closeout expose that state so the coordinator retries or
manually relays the reviewer notification before waiting for review.
For parked review waits, use the bounded wake surface rather than writing custom provider prompts into Fairway state:
fairway review-waits wake --task <task-id>
fairway review-waits wake --task <task-id> --send --state thread_steered
The first command renders the fixed wake prompt without writing notification
state. The second records a provider delivery fact on the coordinator domain
after a coordinator/provider adapter has sent or accepted the prompt. Fairway
suppresses duplicate wake signatures and records notification_failed if no
wake target is configured. Wake prompts are status-aware: resolved review waits
on blocked, in-progress, todo, or otherwise non-review tasks are review-wait
only and do not authorize merge-ready or reviewed-lane closeout. The
dashboard remains read-only and does not send wake prompts.
Review
Route review based on changed paths:
fairway route review T-001 --path cmd/api/routes.go --path doc/api/openapi.draft.yaml
Reviewers record a verdict:
fairway record review T-001 \
--reviewer governance \
--verdict approve \
--reason "route and evidence look good"
When the reviewer identity must differ from the required review domain, use
--domain. For example, an independently assigned reviewer named
ops-reviewer can satisfy an ops review domain without weakening
no-self-review:
fairway record review T-001 \
--reviewer ops-reviewer \
--domain ops \
--verdict approve \
--reason "independent ops review"
When every required review domain is approved, fairway coordinator plan
surfaces a review-complete handback for the coordinator or reviewer/merge
lane. The handback prevents review completion from being trapped in provider
chat, but it does not merge, push, deploy, or release. The coordinator still
runs fairway merge-ready <task-id> and performs the configured promotion step
explicitly. If you record delivery of that handback, include the current
review_signature from coordinator plan or task detail in the notification
reason; commit-only acknowledgement is not enough when the required review set
changes on the same commit.
Use changes rather than approve when more work is required. No agent should
approve its own work.
Finish Work
Before marking done, record the evidence that proves the acceptance checks:
fairway record evidence T-001 --command-text "go test ./..." --result pass
fairway set-status T-001 done
fairway merge-ready T-001
If gates fail, fix the missing evidence/review/handoff or record why the task is not ready. Do not force a green story into the DB.
External Tracker Mirrors
Plane, Jira, Linear, and similar tools are planning mirrors, not execution stores. Use tracker commands to render or link planning context, but do not let external issue state drive Fairway task status, sessions, evidence, reviews, or merge gates.
For the Plane spike, set local environment variables and run dry-run commands:
export PLANE_BASE_URL=http://localhost:8088
export PLANE_WORKSPACE=fairway-eval
export PLANE_PROJECT=FWPLANE
fairway tracker plane export --task-id FW-122
fairway tracker plane import --fixture examples/tracker-adapters/plane/evaluation-workspace.yaml
fairway tracker plane comment --task-id FW-122 --external-id FWPLANE-122
fairway tracker plane --apply paths are intentionally unsupported in the
spike. Plane tokens must come from environment or OS credential storage and must
not be committed.
End your session when the runtime exits:
fairway session end <session-id> --reason normal --exit-code 0
What Not To Do
- Do not edit Fairway DB rows by hand.
- Do not keep working after losing a claim.
- Do not switch roles by changing branches inside a role worktree.
- Do not create Fairway subtasks for private implementation steps.
- Do not self-review.
- Do not mark
donewithout evidence, even when the config allows it. - Do not rely on Jira, Linear, GitHub Issues, or a chat thread as the execution source of truth. Link them if useful; keep execution state in Fairway.
Useful Commands
fairway ready
fairway task-detail <task-id>
fairway status-report
fairway health-report
fairway dispatch-plan --role <role>
fairway coordinator plan
fairway checkpoint status
fairway session status
fairway session reconcile --dry-run
fairway dashboard start
fairway dashboard status
See design/cli.md for the complete command surface.
Fairway sessions are records created by fairway session upsert or
fairway session launch. Host applications may show their own subagent history
or worker list; those entries are not Fairway sessions unless they were
registered with Fairway. Use fairway session status for live Fairway-tracked
lanes, and fairway session reconcile --dry-run before assuming a host-app
sidebar count represents active Fairway work.