Concept map — start here
A harness is everything around the model that shapes what an agent sees and what it may do. The model is the same in every lab of this workshop. The harness is what changes, and it is what Ruth, Larkspur's head of Model Risk, will sign or refuse to sign.
This page is the map. Read it once before Lab 2 and come back when a lab asks "which layer does this belong in?"
The harness as layers
Seven layers, each owned by one lab. Each card says what question the layer answers and how strong its answer is.
AGENTS.mdWhat does Droid know before it starts?Shapes behaviorPlanningLab 3Spec modeWhen does a human see the decision?Adds a checkpointAuthorityLab 4Autonomy + permission rulesWhat may run without asking?Enforced by configEnforcementLab 5.factory/hooks.jsonWhat can never happen, whatever the model decides?Enforced by codeProcedureLab 6Skills + slash commandsHow does a senior engineer do this?Shapes behaviorIndependent reviewLab 7.factory/droids/Who checks the work with fresh eyes?Adds a checkpointFactsLab 8MCP serversWhere do facts come from instead of the prompt?Shapes behaviorHere are the same layers around one tool call. Look at the colors: the orange layers are enforced by config or code and apply whatever Droid decided, the dark one is a person deciding, and the unstyled ones inform the decision without guaranteeing anything. The docs don't say whether PreToolUse hooks or permission rules are checked first, so the diagram doesn't order them.
Labs 0, 1, 9 and 10 don't add a layer. Lab 0 measures, Lab 1 teaches you to read a session, Lab 9 runs the whole harness headless in CI, and Lab 10 runs it at scale as a Mission. Each depends on the layers being there.
The distinction that matters
The label at the bottom of each card is the most important idea in the workshop. There are four kinds of layer, and they are not equally strong.
| Kind | What it means | Layers | What it cannot do |
|---|---|---|---|
| Shapes behavior | Changes what Droid knows or how it approaches a task. Droid weighs it against everything else it knows. | AGENTS.md, skills and slash commands | Guarantee anything. It is an input to a decision. |
| Adds a checkpoint | Puts a decision in front of someone before it takes effect: a person approving a plan, or a fresh reviewer reading a diff. | Spec mode, custom droids | Hold when nobody is there to look. Headless, a checkpoint with no reviewer is just a delay. |
| Enforced by config | Droid's own configuration decides, before the model gets a say. | Autonomy levels, permission rules | See beyond what it matches. Permission rules match command prefixes. They don't see file edits made with the Edit tool. |
| Enforced by code | Your code runs at a fixed point in the session, every time, and its exit code decides. | Hooks, and facts from MCP checked by a hook | Enforce anything you didn't write a check for. A hook guards what you name. |
The workshop's argument in one line: instructions are requests, controls are code. Lab 2's AGENTS.md is clear, and on one run in three Droid still weighs it against "make the test pass" and edits the rounding. That isn't a defect in Droid. It is what an instruction is. Lab 5's hook makes the same edit impossible, whatever Droid decides.
The weaker layers still matter. They move most of the scoreboard, because a Droid that knows the invariants rarely hits a control. The strong layers matter for the run where it does. You need both: context so the controls rarely fire, and controls for when they must.
I will read every file you show me. Tell me which of them are asking and which of them are enforcing, and do not let me confuse the two.
A note on MCP's label. An MCP server gives Droid facts from your own systems, such as the status of a change ticket. On its own, a fact is still context. Lab 8 makes it a control by having a hook check the ticket's status field in code before a migration can be written. The fact comes over MCP. The enforcement is the hook.
Every harness file
Every file the workshop creates, where it lives, and what it controls.
| File | Location | Scope | What it controls | Lab |
|---|---|---|---|---|
AGENTS.md | Repo root; nested directories such as src/money/AGENTS.md | Project, or one directory tree | What every session knows first: commands, layout, invariants, the never-list, when to stop, how to verify | 2 |
sessionDefaultSettings.interactionMode, specModeModel | .factory/settings.json | Project, or user in ~/.factory/settings.json | Whether sessions start in Spec mode, and which model plans | 3 |
| Saved specs | .factory/docs/ by default when Save spec as Markdown is on (the settings page lists specSaveDir as ~/.factory/specs; check your version) | Project or user | A reviewable record of each approved plan | 3 |
sessionDefaultSettings.autonomyLevel | .factory/settings.json | Project, or user | What runs without asking. Larkspur sets "low" | 4 |
permissionRules | .factory/settings.json | Org, folder, project, user, plus settings.local.json overrides | allow, ask or block for shell commands | 4 |
.factory/hooks.json | Project; also ~/.factory/hooks.json for a user | Project or user | Which of your scripts run on which events and tools | 5 |
.factory/hooks/*.mjs | Project | Project | The checks themselves: protected paths, shell writes, float money, the outcome at Stop | 5 |
.factory/skills/<name>/SKILL.md | Project; also folder-specific and ~/.factory/skills/ | Project, folder or personal | A procedure Droid loads when the task matches its description | 6 |
.factory/commands/*.md and executable scripts | Project; also ~/.factory/commands/ | Project or personal | Slash commands you invoke by name | 6 |
.factory/droids/<name>.md | Project; also ~/.factory/droids/ | Project or personal | A subagent with its own system prompt, model and enforced toolset | 7 |
.factory/mcp.json | Project root; also ~/.factory/mcp.json for a user | Project or user | Which MCP servers Droid can call, and how to start them | 8 |
.github/workflows/droid-scoreboard.yml | Repo | CI | The scoreboard and Droid review on every pull request | 9 |
.factory/skills/review-guidelines/SKILL.md | Project | Project | Custom guidelines for Droid's automated code review | 9 |
.factory/protected-changes.log | Project (written by the Stop hook) | Project | A local record of protected-path changes for the reviewer. It is git-ignored, so CI checks the diff against the base instead | 5, 9 |
mission.md | Repo | One Mission | The goal, milestones and validation for a multi-feature run | 10 |
Two settings the workshop mentions but doesn't depend on: sandbox (operating-system isolation for commands, an extension in Lab 5 and part of the regulated guardrails guide) and enableDroidShield (secret scanning on commits and pushes Droid makes, on by default, covered in Lab 4).
How the layers interact
Each layer has its own rules about what wins when two sources disagree. Here is what Factory's docs actually say. Where they say nothing, this page says nothing.
Permission rules
- Which definition of a rule ID wins: Org, then Runtime, then Folder, then Project, then User. Within one folder,
settings.local.jsoncomes beforesettings.json. A disabled definition still claims its ID. - Which decision wins when several rules match:
blockbeatsask, andaskbeatsallow. - An empty rules array doesn't erase inherited rules. A rule that fails its own tests is skipped. It does not turn into block-all.
- Project and folder rules require workspace trust.
Autonomy and rules together
- Autonomy controls automatic approval, not which tools exist.
- An
allowrule skips the approval prompt. Hooks and the sandbox still apply. - An
askrule needs approval even at High autonomy. Indroid execnobody can approve it, so the run fails.--auto highdoesn't clear it. - A
blockrule has no approval path, even when permission prompts are skipped with--skip-permissions-unsafe.
Hooks
- An
allowrule does not skip a hook. The permission rules page says other controls, including hooks and the sandbox, still apply. - A
PreToolUsehook that exits2blocks the tool call. Any other non-zero exit is a non-blocking error, and the tool runs. - Droid snapshots hooks at session start and warns when the file changes underneath it. Change one, start a new session (or review the change in
/hooks). - Hooks from user, project, plugin and org-managed sources add up rather than override each other. Org-managed hooks always load unless hooks are disabled globally, and
allowManagedHooksOnlymakes Droid ignore user and project hooks. - The docs don't state directly that project hooks run in
droid exec. Lab 5 has you check it before you trust a headless number.
AGENTS.md
The docs describe a mental model, not a strict merge order: the current request takes priority over standing instructions, nested files refine the root file for their directory tree, and project files override personal defaults. Droid searches from the working directory up to the git root, and also checks .factory/, .agents/ and .agent/ at each level.
Skills, commands and droids
- Skills: folder-specific and project skills, then project plugin skills, then personal, then user plugin skills, then built-in.
- Slash commands: the workspace
.factory/commandswins over~/.factory/commands. If a custom command and a skill share a name, the command keeps the slash command. - Custom droids: a project droid wins over a personal droid with the same name.
Settings and the sandbox
- Local overrides (
settings.local.json) merge on top ofsettings.jsonat the same level. - Sandbox settings resolve org, then project, then user.
- Missions inherit MCP, skills, hooks, custom droids and AGENTS.md: the docs say hooks fire during mission execution and project droids are available to workers. So the AGENTS.md, hooks, skills, custom droids and MCP server you build in Labs 2 to 8 carry into Lab 10. Permission rules (Lab 4) aren't on that list. They live in the project
settings.json, which should load for any session in the repository, but the docs don't say Missions inherit them, so check withdroid rules checkbefore you rely on one.
Where each layer lives in .factory/
This is the finished harness. Everything marked new is in harness-reference/ in the companion repo, including review-guidelines/SKILL.md from Lab 9. protected-changes.log is generated, not committed.
AGENTS.mdnew# Lab 2 · shapes behaviormission.mdnew# Lab 10.factory/settings.jsonnew# Lab 4 autonomy + permissionRules (Lab 3's spec settings are optional)hooks.jsonnew# Lab 5 · enforced by codehooks/lib/protected.mjsnew# shared by every hookprotect-paths.mjsnew# PreToolUse · file toolsprotect-shell.mjsnew# PreToolUse · Executeprotected-diff.mjsnew# Stop · checks the outcomemoney-lint.mjsnew# PostToolUse · feedbackprotect-paths.lab8.mjsnew# Lab 8 · allows migrations with an approved CHGskills/money-change/SKILL.mdnew# Lab 6flaky-test-triage/SKILL.mdnew# Lab 6review-guidelines/SKILL.md# Lab 9 · you write itcommands/fee-change.mdnew# Lab 6 · Markdown commandship-check.shnew# Lab 6 · executable commanddroids/money-reviewer.mdnew# Lab 7 · tools: read-onlymcp.jsonnew# Lab 8 · change-control serverprotected-changes.log# written by the Stop hook, git-ignored.github/workflows/droid-scoreboard.ymlnew# Lab 9src/money/protectedmigrations/protectedlegacy/frozen
Where to go next
- New to Droid? Do Setup, then Lab 0 and Lab 1.
- Want the reasoning behind each choice? Read the harness decisions.
- Want a word defined? The glossary has every term in plain English.