> ## Documentation Index
> Fetch the complete documentation index at: https://runelite.zip/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflow builders and plans

> Typed, pre-packaged state machines for banking, combat, production, pouches, spellbooks, and enchanting.

The `com.n3plugins.sdk.workflow` package ships builders for common automation domains. Each builder accepts an immutable plan and returns a configured `TypesafeCarouselStateMachine`, so you get the domain's full state machine (opening, reconciliation, verification, teardown) without writing it. See the [automation loop guide](/guides/automation-loop) for how to pulse and observe the machines these builders return.

## Bank restocking — `BankWorkflowBuilder`

`BankRestockPlan` describes the expected inventory and equipment states for a restock run:

```java theme={null}
BankRestockPlan plan = BankRestockPlan.builder()
    .inventoryLoadout(myInventoryLoadout)
    .equipmentLoadout(myEquipmentLoadout)
    .build();

TypesafeCarouselStateMachine<BankRestockState> bankMachine = BankWorkflowBuilder.create(plan);
```

The machine walks `OPEN_BANK` (nearest accessible registered bank), `DEPOSIT` (unneeded inventory and equipment), `WITHDRAW` (required loadout items), `EQUIP` (gear from inventory), and `CLOSE_BANK`.

### Reusing the bank cycle

Caller-owned workflows that need selective deposits or withdrawals can reuse the shared interface cycle without handing item policy to a restock plan:

```java theme={null}
BankWorkflowBuilder.BankCycle<MyState> bank = BankWorkflowBuilder.cycle(
    runtime::bankOpen,
    runtime::openBank,
    runtime::closeBank,
    runtime::tick);

bank.open(MyState.DEPOSIT, MyState.VERIFY_BANK_OPEN);
bank.verifyOpen(MyState.DEPOSIT, 80);
bank.close(MyState.RETURN, MyState.VERIFY_BANK_CLOSE, "Returning to work");
bank.verifyClose(MyState.RETURN, 8, "Returning to work");
```

`BankCycle` records the dispatch tick and requires a later bank-interface observation. Your code keeps deposit, withdrawal, loadout, and return-state policy.

### Range-aware fulfillment

Use `LoadoutFulfillmentBuilder` when requirements have minimum or maximum ranges, item variants, noted withdrawal mode, strict foreign-item handling, or an acquisition policy. Its equipment-first sequence reopens the bank before inventory reconciliation after equipping. Non-standard `Withdraw-X` and `Deposit-X` actions enter `SUBMIT_BANK_QUANTITY`, submit the visible prompt on a later pulse, and return to the requesting phase.

States: `OPEN_BANK`, `EQUIPMENT`, `INVENTORY`, `SUBMIT_BANK_QUANTITY`, `VERIFY`. Read the terminal `FulfillmentResponse` with `LoadoutFulfillmentBuilder.responseOf(machine)`.

## Combat — `CombatWorkflowBuilder`

`CombatPlan` configures targeting, thresholds, and looting:

```java theme={null}
CombatPlan plan = CombatPlan.builder()
    .targetPredicate(npc -> npc.getName().equals("Gargoyle"))
    .eatFoodBelow(50, ItemID.SHARK)
    .toggleSpecAt(50)
    .enablePrayersDuring(Prayer.PROTECT_FROM_MELEE)
    .lootItems(ItemID.GRANITE_MAUL, ItemID.COINS_995)
    .build();

TypesafeCarouselStateMachine<CombatState> combatMachine = CombatWorkflowBuilder.create(plan);
```

The machine walks `FIND_TARGET`, `ATTACK_TARGET`, `FIGHTING` (eating, potions, special attacks, prayers), `WAIT_FOR_DEATH` (disables combat prayers), and `LOOT_ITEMS`.

## Production — `ProductionWorkflowBuilder`

`ProductionPlan` defines product selection, quantity, and restock parameters:

```java theme={null}
ProductionPlan plan = ProductionPlan.builder()
    .optionName("Willow longbow")
    .quantity(ProductionQuantity.ALL)
    .restockLoadout(fletchingLoadout)
    .build();

TypesafeCarouselStateMachine<ProductionState> productionMachine =
    ProductionWorkflowBuilder.create(plan);
```

The machine walks `WAIT_FOR_PRODUCTION_OPEN`, `SELECT_OPTION` (by name or index), `SELECT_QUANTITY`, `WAIT_FOR_PRODUCTION_COMPLETE` (watches animations and inventory), and `BANK_RESTOCK`.

## Rune pouch — `PouchWorkflowBuilder`

`PouchPlan` declares the rune loadout the pouch must hold:

```java theme={null}
PouchPlan plan = PouchPlan.builder()
    .requiredRunes(runePouchLoadout)
    .build();

TypesafeCarouselStateMachine<PouchState> pouchMachine = PouchWorkflowBuilder.create(plan);
```

The machine walks `EVALUATE` (compare pouch contents), `EMPTY` (insufficient or incorrect contents), and `FILL` (fill from inventory).

## Spellbook swap — `SpellbookWorkflowBuilder`

`SpellbookPlan` declares the target spellbook:

```java theme={null}
SpellbookPlan plan = SpellbookPlan.builder()
    .targetSpellbook(Spellbook.ANCIENT)
    .build();
```

The machine walks `CHECK_CAPE` (Magic cape equipped or in inventory), `INTERACT` (Spellbook swap on cape or altar), and `SELECT` (finish the dialogue).

## Jewellery enchanting — `EnchantingWorkflowBuilder`

`EnchantingPlan` models a typed spell, the unenchanted input, the exact enchanted output, per-cast rune quantities, and ordered owned-staff candidates. `EnchantingWorkflowBuilder` validates the Standard spellbook and boosted Magic level, prepares bank supplies, equips the chosen staff, and closes the bank before casting. It counts inventory and rune-pouch quantities and treats an equipped elemental staff as infinite coverage only for the runes its `StaffOption` declares.

The builder snapshots input and output quantities before each cast and reports progress only after the input decreases and the configured output increases. Preserve that confirmation boundary: an accepted interaction does not confirm an enchantment.

## Essence pouch planning — `EssencePouchWorkflowBuilder`

A pure planner (no dispatch) shared with the runecrafting workflow. `EssencePouchPlan` holds the pouches you carry plus the runecrafting level that gates usable capacities:

```java theme={null}
EssencePouchPlan plan = EssencePouchPlan.builder()
    .runecraftLevel(74)
    .pouchItemIds(Arrays.asList(ItemID.SMALL_POUCH, ItemID.MEDIUM_POUCH, ItemID.LARGE_POUCH))
    .build();

int space = plan.availableSpace();
```

Helpers on the builder:

* `availableSpace(itemIds, runecraftLevel)` totals free essence space across usable pouches. A non-degraded colossal pouch supersedes smaller degraded pouches.
* `colossalCapacity(level)` returns colossal capacity at the level (25/50/75/85 map to 8/16/27/40).
* `isDegradedPouch(itemId)` identifies degraded variants.
* `hasDegradationPrevention(equipmentIds)` checks for a runecrafting, trimmed, or max cape plus a Redwood lantern.
* `fillOperations(guardianEssence, pouches)` and `emptyOperations(inventoryFreeSlots, pouches)` return the ordered `EnumSet<EssencePouchOperation>`. The builder consumes the pouch list in caller order, so pass pouches in the order you want them filled or emptied.

`build()` throws `IllegalStateException` when `pouchItemIds` was never supplied. Repair routing is deliberately excluded because repair NPCs are minigame-specific.
