Skip to main content

Task directory contract

One directory per run, the same shape whether Farmslot dispatched the worker or an engineer started the task from a skill. Farmslot defines the files; @farmslot/agent-runtime and any harness built on it consume them.

Two documents, one checklist

The task document says what the work is. The execution checklist says how to do it, step by step. Only the checklist has steps.

FileHoldsSteps counted?
TASK.mdTicket block, description, acceptance criteria, screenshots, comments, PR comment summary (pr-complete), mode preamble, project addendum, mark instructions, pointers to inputs/, appended gateway contractsno
CHECKLIST.mdThe selected execution template, placeholders expanded, nothing injected or appendedyes

Because the checklist carries no run data, a project can select a template from any catalog source and get the same bytes a standalone skill materializes for the same template id. The provenance digest proves it.

This is the only layout the task writer produces. One flow keeps its own pairing: lightweight interactive dev writes the operator-agreed plan into CHECKLIST.md and renders its template into TASK.md, because the plan there is decided live with the operator rather than selected from a catalog.

Layout

<task>/
TASK.md task document (built by @farmslot/agent-runtime)
CHECKLIST.md execution checklist (template, placeholders rendered, otherwise verbatim)
mark shim: exec ${FARMSLOT_MARK_CMD:-<recorded command>} "$DIR" "$@"
SIGNAL.json written only by mark
checklist-target.json role switches write it; absent = CHECKLIST.md + SIGNAL.json
inputs/
handoff.json the task record: identity, flow, task, report paths,
executionTemplate (selected checklist + digests),
templateProvenance (Farmslot only: repo revisions, selection source)
worker-terminal-contract.json artifacts required before a terminal mark
bug-input.json ticket as fetched, comments included (when a ticket exists)
… flow-specific inputs (planning context, PR comments, inherited context)
assets/ ticket attachments
artifacts/ worker output: reports, recipes, evidence; sandbox.json readiness record
acceptance-status.json acceptance-criteria ledger; written only by `farmslot-agent ac`
subtasks/ child checklist units, written only by mark sub
index.json registry of registered units (id, parent step, paths, source digests)
<id>.md child checklist, materialized from a skill, template, or inline text
<id>-SIGNAL.json child signal (WorkerSignal + the parent link)

Producers and consumers

One producer writes the shared layer on every surface: taskInit / farmslot-agent task init in @farmslot/agent-runtime. The gateway composes the same pieces (renderTemplatePlaceholders, buildTaskDocument, writeTaskDir) around its control-plane steps; mm-harness wraps the CLI with MetaMask defaults; the recipe-cook skill calls mm-harness.

FileProducerConsumer
TASK.mdtask init; worker updates STATUS and may append notesworker, family follow-ups, review brief
CHECKLIST.mdtask initworker, mark, progress parser, Command Center progress
marktask init; command is a project value (vars.mark_cmd)worker
checklist-target.jsonrole switch onlymark, progress path resolution (default when absent)
SIGNAL.jsonmark onlyrun monitor, publication gate, closeout
inputs/handoff.jsontask inithandoff closeout, learning packages, replay and eval, farmslot run
inputs/worker-terminal-contract.jsontask init from project.json worker_terminalmark terminal commands, artifact contract check, monitor hold
inputs/bug-input.jsontask init from the fetched ticketfarmslot run, review inputs
subtasks/index.jsonmark sub onlyworker, gateway task watcher, review-workspace progress publisher, progress projection, view mirror
subtasks/<id>.mdmark sub start (materialized from the named source)worker, mark sub, progress projection, view mirror
subtasks/<id>-SIGNAL.jsonmark sub onlygateway task watcher, review-workspace progress publisher, progress projection, terminal contract check, view mirror
artifacts/acceptance-status.jsonfarmslot-agent ac onlyterminal contract check, PR body / gate summary, run detail AC panel
artifacts/sandbox.jsonharness preparation (mm-harness prepare)worker, evidence package, Command Center (later)
artifacts/*workerpublication gate, review, retrospective (see worker artifacts by flow)

Provenance

The selected checklist travels inside inputs/handoff.json as executionTemplate, identical in shape on both surfaces:

{
"executionTemplate": {
"selectionReason": "configured-default",
"id": "fix-bug/autonomous.mobile",
"sourceId": "package:example-checklists",
"flow": "fix-bug",
"platforms": ["mobile", "ios", "android"],
"labels": [],
"relativePath": "fix-bug/autonomous.mobile.md",
"sha256": "<source digest>",
"renderedSha256": "<CHECKLIST.md digest>"
}
}

sha256 is the template source. renderedSha256 is the checklist file as written. For a placeholder-free template they are equal, and equal to a skill-side materialization of the same id. Farmslot adds templateProvenance beside it (project repository revision, selection source, render time) for the run record; the reference is never stored twice. Task dirs written before 0.9 keep inputs/template-provenance.json; readers fall back to it for one release.

Handoff metadata

inputs/handoff.json is the run's identity for closeout and learning packages:

FieldFarmslot valueSkill value
attemptIdrun idrandom UUID
surfacefarmslotskill
projectproject namecheckout basename
repoowner/name from repo_url or ci.repoowner/name from the git remote
domaineffective run domain, when any--domain, when any
flowflow typetask kind
tasktitle, sourceKind (jira, github-issue, github-pr, text), ticket, source URLtitle, text or file, ticket, ref
task.acceptanceCriteriathe ticket's criteria, in order; position N is ledger id AC-N--acceptance values, same ids
taskDocumentTASK.mdTASK.md
reportthe terminal contract's complete.report (artifacts/pr-description.md for dev / fix-bug)artifacts/pr-description.md (dev / fix-bug)
learningsartifacts/learnings.mdartifacts/learnings.md

Outcome file: one name on both surfaces

dev and fix-bug workers finish with artifacts/pr-description.md whether Farmslot dispatched the run or an engineer ran the skill. It is the PR body in the repository's PR-template shape and carries the proof summary (what changed, root cause, validation, evidence paths). Most runs want a PR, and the file is usable before anyone decides to open one.

StageFarmslotSkill
Worker outcome (terminal contract complete.report)artifacts/pr-description.mdartifacts/pr-description.md
Publicationthe gateway fills the evidence section from evidence-manifest.json and publishes after the human gatethe evidence packaging step adds the evidence images and artifact index into pr-package/pr-desc.md, then a PR is opened only on request

review-pr writes artifacts/report.md (with its QA artifacts) on both surfaces; no-change writes artifacts/no-change-report.md. inputs/handoff.json report names the outcome file, so closeout and learning packages read the same file wherever the run happened.

Layers: shared spec, control plane on top

Both surfaces produce the same task directory. Without the control plane (an engineer running the skill) the first two layers exist; with it (Farmslot) the last two are added beside them, and no shared file is renamed.

LayerProducerFilesSurfaces
Worker outputsthe agent following CHECKLIST.mdoutcome file (pr-description.md, report.md, no-change-report.md), learnings.md, recipe.json, recipe-run/, recipe-coverage.md, recipe-quality.json, evidence-manifest.json, validation-summary.{json,md}, before/after media, tool logs (coverage.log, jest.log, …); subtasks/index.json, subtasks/<id>.md, subtasks/<id>-SIGNAL.json through mark subboth, same names
Harness readiness recordsmm-harness during preparationharness-provenance.json, sandbox.md, doctor-fix.json, status.json, launch-verify.txt, fixtures-set.txt, verify.json; task-local .mm-harness/ lockskill today; the farm prepares the same environment in preflight.sh without leaving these records (open)
Gateway-ownedthe gateway, on the orchestrator copydiff.txt, diff.txt.previous.*, diff-stat.json, session-metrics.json, workflow.mmd, pr-package.json, pr-package.md, publication-gate-<slug>.md (slug: letters, digits, dashes), and per review round <n> (digits): self-review-<n>/, self-review-<n>.json, self-review-<n>.md, independent-review-<n>/, independent-review-<n>.json, independent-review-<n>.md, review-loop-<n>/Farmslot only
Review-loop outputsreviewer roles on the slot, change ledgerreview-feedback.<context>.md, review-result.<context>.json, iteration-diff*.{json,txt}Farmslot only

The gateway-owned row is exactly what isGatewayOwnedArtifactMirrorEntry in services/gateway/src/core/artifact-copy-policy.ts recognises: those files are never mirrored back from the slot and never count as worker evidence. Review-loop outputs are mirrored like worker files but belong to the control plane. Under inputs/, runtime-capability-catalog.json and the planning and inherited context files are Farmslot-only as well. A new gateway feature adds a name to one of these rows; it does not reuse a worker name.

Shared inputs/ names: bug-input.json for the ticket as fetched and assets/ for its attachments, on both surfaces. Task directory paths still differ (temp/tasks/<flow>/<slug>-<stamp> on the farm, temp/tasks/recipe-cook/<stamp>-<slug> for the skill); nothing reads the path, so it stays a naming difference.

Simplification ledger

Kept current with the layout. Each row is something the layout still carries that a good default could remove. Rows leave when shipped or rejected. Shipped 2026-09-13: one task-dir producer; provenance folded into handoff.json; ticket-comments.json removed; checklist-target.json optional for readers. Shipped 2026-09-14: the gateway no longer writes the default-valued checklist-target.json (every node runs the 0.9 mark engine).

CandidateTodaySimpler defaultState
two naming schemes for review outputsreview-feedback.<context>.md / review-result.<context>.json and self-review-N.* / independent-review-N.*one scheme, one row in the layers tablecandidate
harness identityPATH resolve through the pack's recipe_runner_resolve_cmd on the farm, task-local lock on the skillone preparation step both surfaces run, which also writes the readiness recordsdecision open
farm defaults pinning farm copies of dev / fix-bug / review-prthree near-copies of the skill templatesdelete them so the catalog default (skills package) winsdeferred until the five-label inventory
task directory path<flow>/<slug>-<stamp> vs recipe-cook/<stamp>-<slug>leave; nothing reads itrejected

What travels to the slot

Dispatch copies TASK.md, then the task-root sidecars (mark, CHECKLIST.md, and checklist-target.json when present), then assets/, inputs/, artifacts/, and subtasks/ as directories. Re-sync and warm-session handoff use the same list. At completion the gateway mirrors artifacts/, TASK.md, and CHECKLIST.md back beside the orchestrator copy as *.worker, and every file under subtasks/ as subtasks/<name>.worker from a directory listing — child ids are chosen at registration, so there is no fixed name list.

A slot-free static review workspace (ADR-058) mirrors the same directory into its operator-visible view/, under the worker's own names rather than *.worker, so subtasks/index.json's own relative paths still resolve there after the workspace is cleaned up.

The mirror travels one way. *.worker files are orchestrator-owned output written from the slot, so the outbound copy skips them: re-dispatching, nudging, or warm-handing off a task directory that already completed once must not put stale copies of the worker's own files back beside the live ones.

subtasks/ is also the one directory the gateway creates on the slot without writing anything into it: both file-watch primitives observe a file through its parent directory, and the directory otherwise appears only with the first mark sub start, so the task watcher would never see a child registered mid-run. The registry, the child checklists, and the child signals stay mark-written.

Project addendum

A project may ship templates/task-document.md. The writer renders it with the same placeholders as worker templates and inserts it into TASK.md before the checklist pointer. Use it for project tooling the worker needs before the first step (runner resolution, environment notes). It is not a checklist: checkboxes there are not counted.

Boundaries

  • The checklist never contains run data. Ticket text, acceptance criteria, and slot facts live in TASK.md and inputs/.
  • TASK.md is never enumerated. A - [ ] inside an acceptance criterion cannot shift a step number.
  • A ticked box is not proof. Proof is the recipe run, its evidence, and the artifacts the terminal contract requires.
  • SIGNAL.json is written by mark only. Hand-written signals are rejected by the monitor.
  • subtasks/ is written by mark sub only, and a step may own one child unit for the life of the task directory. A child unit is observed, never spawned: registering one writes files and starts no process. A parent terminal mark is refused while any registered child is unsettled, by mark and again by the gateway's terminal check. Settled is complete or done: a blocked child is terminal for the run but keeps its step. The verbs and their refusals live in Agent runtime.
  • A child unit is not a role switch. It never writes checklist-target.json and never changes the run's active task file, so a child may hang off CHECKLIST.md or off a role checklist such as SELF-REVIEW.md; the parent link names which. A child progress update is live only while its parent checklist is the active one, and running with no recent mark projects as stale — a projection only, never a status in the file.
  • artifacts/acceptance-status.json is written by farmslot-agent ac only. Enforcement is a per-project opt-in: with worker_terminal.acceptance.require, every criterion in inputs/handoff.json needs a verdict before a terminal success mark, and weak or missing fails unless the contract also sets acceptance.allowWeak. Without it the ledger is informational — watched, shown in run detail and preferred for coverage when present, but never a reason a run cannot close. The gateway creates artifacts/ on the slot so its watch has a parent directory to observe, and writes nothing into it.
  • Lightweight interactive dev keeps its own pairing: CHECKLIST.md is the operator-agreed plan and TASK.md the context.

See also: Agent runtime, Template variables, and Worker artifacts by flow.