ADR-0012: Strategy authoring — thesis mode, leash dial, and AI co-author
- Status: accepted
- Date: 2026-06-02
- Refines (does not replace): ADR-0009
Context
ADR-0009 replaced the free-text systemPrompt textarea with three structured
fields — entry, exit, riskManagement — so non-developer traders could
author skills without thinking like prompt engineers. That decision held: the
three fields are unambiguously easier than a blank canvas.
After exercising the editor on real strategy ideas, two limitations surfaced:
- The 3-field shape encodes a rules-execution mental model. A trader
who wants the AI to "trade like X" — capturing an edge by judgment rather
than enumerating every rule — has no good place to put a thesis. They end
up shoehorning ambitions into
entry("Open a long when momentum is strong, news is positive, vol is reasonable, …") and the AI faithfully acts on a brittle disjunction of half-defined predicates. The structural bias is toward over-specification, which kills the AI's ability to find better expressions of the same edge. - No flexibility dial. Some skills are mechanical (a moving-average crossover); others are discretionary (a portfolio manager's heuristics). Both share one composer that treats the strategy text as ground truth. There is no way to say "these rules describe my thesis; use judgment on edge cases" without rewriting the rules to enumerate every edge case.
The user-facing summary is sharper: make the editor friendly to traders who don't know what a system prompt is, give them maximum flexibility, and let the AI do as much of the lift as possible.
Decision
Three additions to StrategyConfig, all behind the same payload shape (no
new top-level keys outside strategy):
1. Authoring mode
mode: 'thesis' | 'rules' | 'hybrid' // default: 'thesis'thesis— the trader supplies an edge and constraints; the agent decides each tick using the full context. Fields:thesis,horizon,style,lookFor,avoid,sizing.rules— legacy from ADR-0009; the three fieldsentry,exit,riskManagementare required. The composer instructs the agent to follow them literally.hybrid— both. The composer emits the thesis body first, then the rules as "pin specific behaviors inside the thesis above," with conflict resolution noted.
2. Leash — flexibility dial
leash: 'strict' | 'balanced' | 'adaptive' // default: 'balanced'The leash translates to a paragraph at the top of the body:
| leash | framing |
|---|---|
strict | Follow the strategy literally. Do not improvise. |
balanced | Follow faithfully; use judgment on edge cases the rules don't cover. |
adaptive | Use as guidance; find the best expression of the edge each tick. |
Hard kill conditions in avoid are never optional, regardless of leash.
3. AI co-author — "Draft from a pitch"
A server action (apps/web/app/(app)/skills/draft-strategy-action.ts)
converts a free-text trader pitch into a structured StrategyDraft
(thesis-mode fields + style) via generateObject against a cheap, fast
model routed through OpenRouter. The editor's UI opens this in a modal,
shows the draft, and merges into the form on accept without clobbering
fields the trader already filled.
This removes the cold-start friction of an empty form — the most common moment a non-engineer trader gives up.
Other UX changes that follow from the schema
- Style chip picker —
momentum,mean_reversion,breakout,news_driven,carry_funding,discretionary,custom. Picking a style fills empty thesis fields with a starting template (inapps/web/components/skill-editor/strategy-templates.ts). The style tag is also passed to the agent as a soft signal ("weight technical/news/ funding consistent with this style"). Templates are non-enforced — trader edits freely. - "What the agent sees" preview — a collapsible panel inside the Strategy tab that renders the composed system prompt with platform-owned segments visually distinct from the trader-authored body, plus a sample of the per-tick user message based on the Context tab settings. Builds trust without revealing prompt-engineering details.
Alternatives considered
Alt A — Add only the leash; keep the 3-field shape
- Simpler delta to ADR-0009.
- Doesn't solve the over-specification bias. The trader still has nowhere to put a thesis.
- Not picked.
Alt B — Replace entry/exit/riskManagement entirely with thesis fields
- Cleaner schema, no
modediscriminator. - Breaks mechanical strategies, which are real and useful (e.g., the sim fixture is rules-mode by design). Forces them to express precise rules as squishy prose. Not picked.
Alt C — Free-text systemPrompt override toggle (the deferred Alt B from ADR-0009)
- Maximum flexibility for power users.
- Re-introduces the cognitive load ADR-0009 explicitly removed. Couples user-authored prose to platform behavior again.
- Still deferred. The thesis + adaptive-leash combo covers most "I need more freedom" cases without exposing the prompt boundary.
Alt D — A graph / DSL builder for conditions
- Considered for "what to look for" — a structured list of typed predicates (signal, threshold, time-of-day, …).
- Pushes the editor back toward feeling like an IDE. Conflicts with the trader-friendly goal.
- Not picked. The model interprets natural-language signal lists well enough; gain isn't worth the UI complexity.
Consequences
Positive
- Flexibility without a system-prompt textarea. Traders get genuine agency over how literally the AI reads their strategy via the leash, without ever seeing a prompt.
- Inverts the over-specification bias. Thesis mode pushes the trader toward what edge + what to avoid, which is what they actually know. The model handles the per-tick synthesis it's already good at.
- Mechanical strategies still work. Rules mode is a clean migration
path; the fixture skill and any legacy
entry/exit/riskManagementpayloads parse without change (az.preprocessstep promotes them tomode: 'rules'). - AI co-author closes the cold-start gap. The empty form is the worst UX moment; pitch-to-draft turns it into the best one.
- Composable system prompt. Header / leash / body / footer are independent. Cheaper to evolve, easier to A/B per leash level via sims.
Negative / trade-offs
- Bigger schema surface to validate. Per-mode required fields are
enforced with
superRefine. Errors are routed to the strategy tab via the existing prefix-routing inskill-editor.tsx. Drift risk between the runtime composer and the preview mirror is the highest-attention hazard; both files name the sameHEADER/FOOTERconstants for grep-ability. - Cost of the AI co-author. Each draft is a
generateTextcall to a cheap model (~haiku); critique uses Sonnet. Bounded by a 4k-char pitch cap and a signed-in-user guard. Acceptable for now; revisit if traders abuse it. - No AI SDK
generateObjectfor co-author actions. OpenRouter's translation of structured-output negotiation (tool-calls /response_format) for Anthropic-backed models is unreliable in practice —NoObjectGeneratedError: could not parse the responseshows up often. We usegenerateText+ thegenerateJsonhelper inapps/web/lib/ai-json.ts: it instructs the model to emit JSON in the prompt, strips markdown fences, and validates with the caller's zod schema. The trade is more code (≈140 LoC for the helper) for full control over the parse pipeline and clearer error messages when the model misbehaves. - Adaptive leash widens the agent's freedom. Risk-tab caps remain authoritative (engine-enforced), but a strategy text that's vague about size or symbol scope gives the agent more latitude. Documented in the leash UI; the safety net is unchanged.
Follow-ups completed
- Drift risk eliminated. The composer was extracted into a new
zero-server-dep package
@repo/prompt-compose, consumed by bothagent-runtime(runtime) andapps/web(editor preview). The duplicatedpreview-compose.tsin the web app was deleted. Both contexts now render from the same source. - AI critique shipped.
apps/web/app/(app)/skills/critique-strategy-action.tsis a server action that hands the composed system prompt to a Claude Sonnet–class model with a senior-PM review brief and returns structured findings (severity, title, detail, suggestion). Surfaced via theCritiqueModalnext to "Draft from a pitch" in TabStrategy. - Cross-tab coherence linting shipped. Deterministic heuristics in
apps/web/components/skill-editor/strategy-linter.tsflag contradictions between strategy text and the Context / Risk / Tools tabs (news referenced without news lookback, style requires news but news disabled, interval mismatch, symbol out of scope, missingpropose_order, etc.). Findings render inline at the top of TabStrategy with a one-click "Go to" link to the tab that owns the conflict.
Things we'll need to revisit
- If traders consistently use Adaptive leash to compensate for a thin thesis, the editor should nudge harder during authoring (e.g., a "thesis tightness" gauge).
- The critique uses Sonnet on every click; if usage scales, gate it with a per-user rate limit or move to Haiku with a "Pro" tier on Sonnet.
- Coherence linter is keyword-based and English-only. If multilingual strategy authoring becomes a thing, swap the regex scan for the AI critique (which already handles language-agnostically).
References
packages/skill-schema/src/strategy.ts— schema with mode, leash, style, thesis-mode fieldspackages/prompt-compose/src/compose.ts— single-source composerpackages/agent-runtime/src/system-prompt.ts— re-export shim for runtime consumersapps/web/components/skill-editor/tab-strategy.tsx— the editor UIapps/web/components/skill-editor/strategy-templates.ts— style chip templatesapps/web/components/skill-editor/strategy-linter.ts— deterministic cross-tab coherence checksapps/web/components/skill-editor/strategy-findings.tsx— inline findings UIapps/web/components/skill-editor/critique-modal.tsx— AI critique UIapps/web/app/(app)/skills/draft-strategy-action.ts— Draft-from-a-pitch server actionapps/web/app/(app)/skills/critique-strategy-action.ts— Critique server actionapps/web/lib/ai-json.ts—generateJsonhelper (generateText + extract + zod validate)- ADR-0009 — the original three-field decision this refines
- ADR-0005 — the broader "skill as form" decision