---
name: routine-feasibility-loop
type: routine
license: CC BY-NC-SA 4.0
description: >-
  Driver routine for bot.Trader Mandate A's feasibility pipeline — grinds every
  ready Phase-2b feasibility-study ticket in one operator-triggered batch, composing
  skill-feasibility-study per ticket: fetch real venue data through the platform
  adapter, run the gauntlet, file the verdict to the strategy's roadmap doc, and
  surface the recommendation to the Owner. Propose-only — never flips the Trader
  Registry stage, never promotes or retires, paper and demo only, Mandate A only,
  never touches Path B or Forge build tickets. Sits beside routine-delivery-loop
  and skill-fleet-dispatch as the third operator-surface loop, owned by
  operator-bottrader and invoked on demand or from task-bottrader-operator-loop.
  Read when running the feasibility loop, or when reasoning about how bot.Trader
  screens candidate strategies. Runs scheduled: the desk stage unattended as a
  batch with no credentials, the empirical stage through the repo's
  workflow_dispatch feasibility workflow.
---

# feasibility-loop

The driver routine of bot.Trader's feasibility pipeline. Feasibility screening is a perpetual, low-hit-rate function of Mandate A, not a pre-trading phase — most candidates fail the gate cheaply, the model is continuous test-and-learn, and each new asset class opens a fresh backlog — so the value of a loop is the long-term throughput of a never-ending funnel: funnel breadth, not time-to-first-trade. The run leg is a manual operator step today because it needs live venue credentials and is not a Forge job; this routine automates that leg, propose-only. It sequences the per-study behaviour in `skill-feasibility-study` across every ready run ticket in one session, mirroring how `routine-delivery-loop` sequences exec, qa and merge. Owned by `operator-bottrader` under **Mandate A**; the manual APP-540 run (FX cross-sectional momentum, NO_SIGNAL) is the reference implementation.

## Trigger

Two modes, split by the credential decision below.

- **(a) Operator-triggered batch.** "Run the feasibility loop" grinds the eligible queue in one attended Cowork session and returns a stack of verdicts. Still available on demand.
- **(b) Scheduled / unattended — live.** The Owner stood the schedule up in August 2026; it fired five times between 2026-08-27 and 2026-08-31 and stopped each time at the wording that used to sit here, which said the mode was held (APP-1063, APP-1066). It is not held. The **desk stage** (`skill-feasibility-study` steps 1–6 as a desk study — structural advantage, venue feasibility, cost hurdle, verdict) needs no venue credentials and runs unattended in full. The **empirical stage** (toolkit build, real venue data, the gauntlet) runs only through the repo's `workflow_dispatch` feasibility workflow (APP-908; OANDA secrets are repo secrets), which this routine triggers and reads back; it never needs the token in the session. The device-bridge / session-secret framing is retired.

Owned by `operator-bottrader`, Mandate A only. Invoked on demand, or surfaced from `task-bottrader-operator-loop`. This file is canon; where mode (b) is ever wired to a schedule, its Cloud Routine / Cowork config is a thin loader that reads this file verbatim, and behaviour changes are made here, never in the config.

**Mandate A only — the hard exclusion.** Sweep only bot.Trader **Mandate A**. **Never** run for Path B and never feed any output to B's agent: B is given a goal and guardrails and nothing else, and briefing it with a strategy would destroy the experiment (`operator-bottrader`, the separation rule; `skill-feasibility-study`). **Never** touch Forge build tickets (`agent:F0-RGE`) — those are the delivery loop's lane, not this one.

## Behaviour

A run has two stages. **The desk stage runs as a batch**: every eligible desk-study ticket in the promoted queue is run in one pass, ranked by cost floor against plausible gross edge — the cheap gate is exactly where throughput was the constraint (two of two FX-era candidates killed, CSM on merit and Carry on venue costs; the gauntlet works, one-at-a-time did not — APP-1066). The multiple-comparisons discipline is unchanged: the denominator travels with each candidate, and origin changes the prior, never the threshold. **The empirical stage serialises**: from the first toolkit build onward, one eligible run ticket at a time, highest priority first, until none remains or a genuine budget is exhausted. Finish an in-flight ticket before ending the run. The driver calls `skill-feasibility-study`; it does not duplicate that skill's logic, so updates to the skill are picked up automatically.

**Opening declaration.** After the eligibility scan and before the first study, state in the run's output the eligible count and the strategies it is committing to run this batch. (Same discipline as `routine-delivery-loop` step 0: a count on the record is harder to rationalise a soft stop around than one recalled after the fact.)

**Eligibility scan.** A ticket is eligible if it is in the `bot.Trader` **project** (Path A — Mandate A; Path B lives in the separate `bot.Trader-B` project, so the project field *is* the mandate split and no label is needed for it), carries the `[Phase 2b]` **phase prefix** in its title and names a **feasibility study or desk study** there — the standing title convention for a run ticket, e.g. "[Phase 2b] Run the desk feasibility study on FX Carry (skill-feasibility-study)", "[Phase 2b] Run FX Scheduled Macro-Event empirical feasibility" — is on the **human / research lane** (the `agent`-group value `human`, not `agent:F0-RGE`; the toolkit *build* tickets share the word "feasibility" in their titles and are separated by exactly this clause, since they carry no `human`), in **Todo**, unblocked (its toolkit build has merged — no open blocked-by relation), and carries no unmet dependency named in a PM comment. **Every clause here is a live field or a live convention — there is no** `feasibility-run` **label in the workspace and no label carrying** `feasibility` **at all (verified 2026-09-10, APP-1266), so the scan never names one.** **If** the label route is later taken as an Owner workspace-config action, the same edit that puts `feasibility-run` into this scan must also add it to `convention-linear` *Labels*; adding it here alone re-creates the exact defect this wording fixes — a routine naming a label canon does not define. One strategy per ticket. Exclude Path B tickets and Forge build tickets outright. If the top candidate is ineligible, move to the next by priority. End the run cleanly if no eligible ticket exists, and report the zero-eligible result rather than re-querying to confirm it.

For each eligible ticket, run the full cycle before moving on:

1. **Claim.** Move the run ticket to In Progress.
2. **Run the study (composes `skill-feasibility-study`).** For a desk-stage ticket: run the desk study in-session, no credentials. For an empirical-stage ticket, following that skill for the detail: dispatch the repo's feasibility workflow (`workflow_dispatch`, APP-908) → it fetches real venue data through the platform adapter (prices; financing / rollover rates for a carry-type strategy) → run the gauntlet the strategy doc defines (parameter sweep → walk-forward → diagnostics → verdict) → apply the academic sanity check → mark what is known, assumed, and unknown. Use the strategy doc's **fixed** pass/fail thresholds; real numbers only, confirmed against source.
3. **File the verdict.** Write the verdict and a status-history row to the strategy's roadmap / Strategy doc, and comment the verdict **with its evidence** on the run ticket, in plain language readable without opening the model.
4. **Surface the recommendation to the Owner.** Raise the promote / backtest / retire recommendation as a **separate surfaced Owner-decision item** (assign Aled, state the recommendation and why). This keeps the Owner gate visible and distinct from the run ticket's own closure.
5. **Dispose the run ticket.** Move the run ticket to **Done** at verdict-filed — its job (produce and file the verdict) is complete. The gate decision rides on the separate surfaced item from step 4, not on this ticket, so closing it never implies the gate has been cleared.
6. **Stop on this ticket.** Continue to the next eligible ticket.

**Batch report.** Close every run with: strategies run, each verdict (for example STRONG / WEAK / NO), each recommendation, and the queue of Owner gate decisions now awaiting a ruling. On an early stop, name the stop condition concretely — queue empty, or a budget with a number; the scale or diversity of the remaining queue is not a stop condition. Log what ran, what was skipped, and why (no silent caps).

## Guardrails

The guardrails are the point of the artefact.

- **Propose-only — never clears the gate.** Files the verdict and surfaces the recommendation; that is the boundary. Never flips the Trader Registry stage (promote / retire), never edits the Registry active/retired tables, never opens backtest tickets, until the Owner rules. Promote and retire are the Owner's gate (`operator-bottrader`; `skill-feasibility-study`; APP-541).
- **Paper / demo only, zero real capital.** Inherits the Mandate A envelope; any move toward real capital is the Owner's alone.
- **Mandate A only.** Never runs for Path B, never feeds B's agent its output.
- **No bar bending.** Uses the strategy doc's fixed pass/fail thresholds; never waives or loosens a gate to keep a favoured idea alive. A cheap rejection is the gate working, not a wasted run.
- **Lane integrity.** Only human / research-lane run tickets. The delivery loop never touches these, and this loop never touches Forge builds — directly addressing the APP-658 friction class (the delivery loop closing a human-lane ticket without an artefact). It also never merges, and never touches other verticals or the Pipeline team.
- **No silent caps.** Logs what it ran, what it skipped, and why; declares its queue at the start and names any early stop concretely.
- **Composes, does not duplicate.** The per-study behaviour lives in `skill-feasibility-study`; this routine only sequences it and handles the queue.

## Setup

- **Mode (a):** operator-triggered in an attended Cowork session — "run the feasibility loop". Connectors: Linear; GitHub for the empirical workflow. No merge, no Forge.
- **Mode (b):** a Cowork scheduled task whose config carries the canonical loader line in `core-operating-model` (*The loader line — one form*) and nothing else — the schedule is the Owner's config surface and exists today; this artefact holds the behaviour, not the schedule. Connectors: Linear; GitHub for the empirical workflow. Credentials never enter the session: the empirical stage's OANDA secrets are repo secrets read by the workflow.
- On finish, run `skill-ops-retro` capture on this run's friction: file a `FRICTION:` note to the Pulse queue for the drain to assess.

---

*To change behaviour, edit this file (or the per-study behaviour in `skill-feasibility-study`), not the loader config.*
