# Stage 2 farming and workshop capability benchmark

**Status:** accepted Protocol 2 contract  
**Date:** 2026-08-20  
**Runtime:** native x86_64 Classic Dwarf Fortress 0.47.05 with DFHack 0.47.05-r8

## Decision

Stage 2 is the first benchmark of composable fortress-management capability. It asks an inference model to discover an observed carpenter workshop, choose a legal directional farm location, build and configure the farm, complete native seed planting, and produce a requested bed or barrel through a newly queued workshop job.

This is a capability benchmark, not a claim that an agent can run a fortress. It adds no task endpoint, arbitrary host or DFHack executor, campaign loop, public game service, or website workflow. Stage 1 remains unchanged.

## Architecture

```text
precommitted scenario
        |
        v
canonical inference request -- fresh external model call --> typed decision
        |                                                   query | action | finish
        v
Python contract validation
        |
        +-- query ----------> typed DFHack observation
        |
        `-- action --> preflight --> single-use token --> apply --> receipt
                           |                         |
                           `---- exact fences -------'
                                      |
                                      v
                          paused native DF process
                                      |
                                      v
                     semantic + 80x25 visual evidence
```

Inference remains outside the runtime safety boundary. The model receives the same scenario, capability manifest, current observation, and bounded transcript through `InferenceAdapter.decide`. It cannot call DFHack, read host files, choose a private tool, or add a task-specific endpoint. Python and Lua revalidate every query and action.

## Stable identities

| Field | Value |
|---|---|
| benchmark | `farming-workshop-v1` |
| contract | `1` |
| source case | `classic-farming-workshop@1` |
| trajectory | `classic-linux-farming-workshop-trajectory-v1` |
| command | `classic-linux-protocol2-command-v1` |
| preflight | `classic-linux-protocol2-preflight-v1` |
| receipt | `classic-linux-protocol2-receipt-v1` |
| query result | `classic-linux-protocol2-query-result-v1` |
| capability manifest | `classic-linux-protocol2-capability-manifest-v1` |

The capability manifest is canonical-JSON hash bound. Scenario IDs and versions are precommitted. Changing semantics requires a prospective contract or scenario version.

## Public observations

`inspect_fortress` returns bounded, typed state:

- citizen IDs, positions, and current job IDs;
- item counts for food, drink, plants, seeds, wood, beds, and barrels;
- seed crop IDs, raw IDs, seasonal eligibility, and available seed units;
- building identity, type, subtype, bounds, completion stage, farm crop settings, active jobs, and workshop recipe catalog;
- whether an active job was created by this run;
- exact completed workshop-job evidence, including worker, recipe, before/after product units, and produced-unit delta;
- exact completed farm-job evidence, including farm, worker, job type, start, completion, and outcome.

`inspect_region` accepts at most 256 tiles inside the current observed map bounds and returns tile type, shape, material, hidden/outside/subterranean state, flow, occupancy, building identity, citizen pathability, and 1x1 farm-placement eligibility. It is read-only discovery.

The synchronized 80x25 character plane and glyph plane remain attached to every mutation fence and trajectory. The semantic view does not replace native visual evidence.

## Public action primitives

| Capability | Effect | Important bound |
|---|---|---|
| `place_building` | Place one farm plot | citizen-reachable rectangle covered by a causally prior eligible `inspect_region` result at the same revision and tick; 1..5 by 1..5; at most 25 tiles |
| `configure_building` | Set one farm season crop | completed observed farm, current seed-bearing crop, available seeds |
| `enqueue_job` | Queue one observed workshop recipe | completed observed workshop/furnace; queue length below 10 |
| `set_job_priority` | Change `do_now` | active job created by this run only |
| `cancel_job` | Cancel a job | active job created by this run only |
| `advance` | Advance native simulation | exact integer 1..1200 ticks |

No primitive chooses a worker, teleports an entity, creates an item, fabricates a completion, or bypasses Dwarf Fortress job assignment, pathing, hauling, construction, material, or production rules.

Stage 1's combined `prioritize_job_advance_120` contract is not changed. Protocol 2 separates priority and time advancement only inside the new Stage 2 boundary.

## Mutation and evidence boundary

Every action carries caller-owned run and UUID command IDs plus exact save, site, bridge epoch, revision, frame digest, capability, arguments, and canonical payload hash.

1. Preflight validates the complete identity and frame fence, action schema, current target, reachability, resources, and allowlist.
2. Preflight stores the canonical arguments and returns a single matching token.
3. Apply requires that token, rechecks save/site/epoch/revision/frame state without causing a second render, and applies only the stored arguments.
4. Repeated command IDs with the same payload return an explicit duplicate receipt; conflicting reuse fails.
5. Instant actions leave Dwarf Fortress paused. `advance` resumes only for the exact interval and returns paused.
6. Each successful action increments the bridge revision exactly once and yields a typed receipt plus synchronized semantic and visual observations.

A transient frame mismatch may trigger at most the shared 10-attempt frame-fence refresh bound before failing closed. Only the exact frame-mismatch error is retryable; unsupported requests and other errors fail immediately. The mismatch is never converted into an un-fenced mutation.

## Precommitted scenarios

| Scenario | Spatial requirement | Workshop product | Minimum farm |
|---|---|---|---:|
| `north-farm-bed` | north of observed carpenter | bed | 4 tiles |
| `south-farm-barrel` | south of observed carpenter | barrel | 4 tiles |
| `east-farm-bed` | east of observed carpenter | bed | 4 tiles |
| `west-farm-barrel` | west of observed carpenter | barrel | 4 tiles |

A pass requires all of the following in native causal evidence:

- no founder loss;
- one newly placed, completed farm of the required size and direction;
- a receipt showing an observed in-season crop configured on that farm;
- at least one completed native `PlantSeeds` job on that farm;
- a completed newly queued workshop job for the requested product;
- a positive before/after product-unit delta;
- all objective evidence remains satisfied for at least `1,200` additional native ticks;
- no model, decision, preflight, apply, observation, stale-evidence, or turn-budget failure;
- exact cleanup, zero owned processes, and source/save integrity hashes.

## Failure taxonomy

Transport and contract failures are explicit: `model-failed`, `invalid-decision`, `unsupported-query`, `unsupported-action`, `preflight-rejected`, `apply-rejected`, `observation-failed`, `stale-evidence`, `corrupt-evidence`, and `turn-budget-exhausted`.

Scoring adds observable frontier failures: `founder-loss`, `farm-construction-incomplete`, `farm-direction-goal-missed`, `farm-crop-not-configured`, `farm-too-small`, `farm-production-missing`, `workshop-production-missing`, and `holdout-incomplete`.

A later recovery does not erase an unsafe or invalid attempt from the episode outcome. This makes near misses evidence, rather than silently converting them into passes.

## Replay and cleanup

The trajectory records:

- exact runtime, source, save, scenario, capability-manifest, provider, and model identities;
- canonical inference-request hashes and typed decisions;
- per-turn decision outcomes or exact typed errors;
- all queries, preflights, requests, receipts, semantic observations, and visual observations;
- scorer outcome and failure reasons;
- disposable install/save/home removal, owned-process count, source-install hash, and golden-save hash.

`dwarf-society replay <trajectory>` autodetects Stage 1 or Stage 2. Stage 2 replay verifies the trajectory digest, stable identities, manifest and scenario, causally prior region proof for placement, command payloads, bounded frame retries, receipt/revision/tick chains, exact inference/program-event chain termination, exact pass or failure reasons, cleanup, and source/save integrity. Truncated programs, invented failures, and uncaused placements fail replay.

`scripts/run-stage2-apple.sh` is the Stage 2 operator smoke. It starts the existing `dkt230-x86` QEMU profile, runs the reviewed reference policy through the public primitives, strictly replays the result, verifies physical farming and workshop production, proves the admitted save hash unchanged, removes the disposable runtime volume, and leaves no owned process.

## Known capability frontier

The accepted proof establishes bounded farm construction/configuration, native seed planting, one newly queued carpenter product, a `1,200`-tick post-objective holdout, exact advancement, typed causal evidence, strict replay, and cleanup. It does not establish crop harvest across seasons, stockpile management, labor configuration, hauling strategy, multi-workshop production planning, recovery from resource exhaustion, a longitudinal campaign, or autonomous fortress control.
