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.
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.
Start Of Session
fairway config validate
fairway preflight --role <role>
fairway session upsert --role <role> --provider <codex|claude|gemini|shell>
fairway ready
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>
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.
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.
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.
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>"
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.
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.
If you need machine-readable output, put global flags before the subcommand:
fairway --json ready
fairway --json task-detail T-001
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.
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 \
--review-domains architecture,backend \
--risk-level medium \
--migration-type ownership-map
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.
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 reconcile active --dry-run
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.
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
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 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"
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.
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"
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.
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 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.