@battlegrid/mcp-server 31.2.72 → 31.2.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,98 @@ Seeing package `31.x` alongside handshake `battlegrid@33.x` — the package **be
24
24
 
25
25
  **What this changes for you:** nothing about how you call anything. Upgrading the package no longer waits on a server deploy, and a server deploy no longer strands you on a package that names the wrong contract — reconnect and the announcement follows. **Contract breaking-change notes are no longer keyed to package versions**, since a contract move is no longer a release here; the v11-and-earlier notes below are kept as history, and the live vocabulary is always discovery.
26
26
 
27
+ ## Contract history — v92 (every section you read is the JSON the agent reads)
28
+
29
+ **Breaking at v92: the section fields are reshaped, the report preview's default narrows, and its size is measured
30
+ in characters.** v92 is the contract on top of v91. Every tool that published a section as markdown text publishes
31
+ it as the JSON object the agent reads, and `preview_strategy_report` takes a detail level.
32
+
33
+ ### Reshaped output — a section is its JSON
34
+
35
+ A **section read** is `{ shape: "table", section }` — `{ id, title, notes, provenance?, anchorTf, groups, columns,
36
+ rows, addendum? }`, rows positional and every cell its raw value, each column stating its `unit`, `precision`, bar
37
+ set (`bars`) and, on a rank column, the ranked set's `universe`, with a key absent where the column has nothing to
38
+ say — or `{ shape: "facts", section }`, `{ id, title, facts, glossary }`. It replaces:
39
+
40
+ - **`preview_strategy_report.renderedSections[]`**: `{ sectionKey, section: { title, text }, structure,
41
+ authoredNote }` → `{ sectionKey }` and a section read, or `{ sectionKey, shape: "skipped" }` for a row that
42
+ rendered nothing. A custom section's authored note is its section's `notes`.
43
+ - **`get_market_context.sections[]`**: `{ kind, title, content, structure }` → `{ kind }` and a section read.
44
+ - **`get_context_source_full_preview.content`** → **`read`**, a section read or `null`.
45
+ - **`get_context_sources_preview`**: each source's `value` → **`section`**, a section read or `null`, a coin-row
46
+ table sampled to its first five coins.
47
+ - **`get_signal_log.reportSections[]`**: a section read, `skipped`, or `{ sectionKey, shape: "text", title, text }`
48
+ — a row stored before custom sections carried a structure, kept as its agent read it.
49
+ - **`preview_strategy_report.conditionsTableText`** and **`.tradeConditionsBlockText`** → **`gridConditions`** and
50
+ **`tradeConditions`**, each `{ glossary, register }`: the glossary prose and the register object the text carried
51
+ as one JSON line. Each is `null` where its text was.
52
+
53
+ ### Widened output — the register names what decided the verdict
54
+
55
+ - **`decidedBy`** joins the conditions register: on each `GRID` coin and on the `TRADE` register, the key of the
56
+ condition the `verdict` came from, `null` when no single condition made the call. It reaches you in
57
+ `gridConditions.register`, `tradeConditions.register`, and the register `get_agent_prompt_context_preview` shows,
58
+ and the conditions glossary gains the sentence defining it.
59
+
60
+ ### Narrowed default, new input — `preview_strategy_report` `detail`
61
+
62
+ - **`detail`** is `"concise"` (the default) or `"detailed"`. A concise preview is exactly what the agent reads.
63
+ - **`conditionColumns`** and **`conditionOutcomes`** leave the top level. With each row's `headerBindings`
64
+ (`{ sectionKey, columnIndexByHeader }`) they make up **`authoring`**, served only at `"detailed"` and `null`
65
+ otherwise. If you read either field, ask for `"detailed"` and read it under `authoring`.
66
+ - **`marketReadMarkers[]`** lose `resolvedName` and `resolvedValue`.
67
+
68
+ ### Result size in characters
69
+
70
+ - **`tokenCountModel`** and **`budgetUsage.estimatedTokens`** leave the preview. **`budgetUsage.resultChars`** is
71
+ the exact `JSON.stringify` length of the response that carries it, its own digits included, against the cap.
72
+ - **`list_strategy_vocabulary`** drops `budgets.estimatedTokens`, and `previewExecutionLimits.maxResultBytes` is
73
+ renamed **`maxResultChars`**.
74
+ - A preview over the cap is refused with **`PREVIEW_LIMIT_EXCEEDED`** and `limit: "result_chars"`;
75
+ `estimated_tokens` and `mcp_result_bytes` are retired. Every preview refusal now carries its `previewErrorCode`
76
+ and its bound (`deadlineMs`, or `limit`, `actual` and `maximum`) in the error's details.
77
+ - **`preview_strategy_report`**, **`get_market_context`** and **`get_context_sources_preview`** declare the cap as
78
+ `_meta["anthropic/maxResultSizeChars"]`, so Claude Code keeps a large result inline.
79
+
80
+ Something read at v91 is reshaped or gone at v92, and a default narrows — hence the MAJOR.
81
+
82
+ ## Contract history — v91 (every data block an agent reads is one JSON object)
83
+
84
+ **Breaking at v91: one output field is removed, and the published text that is the agent's prompt carries JSON.**
85
+ v91 is the contract on top of v90. Every data block BattleGrid's trading agents read — the strategy's resolved
86
+ conditions, fact sections, and the trade-decision blocks — is one JSON object naming itself with `id` and `title`.
87
+ Instructions stay prose under their `## Title`. The tools that show you the prompt therefore show that JSON.
88
+
89
+ ### Removed output — something you read is gone
90
+
91
+ - **`promptDataSerialization`** leaves `get_signal_log`'s owner detail. There is one serialization, so a trade
92
+ record stores none. `reportSections` and `missingData` are unchanged, and a trade recorded before capture still
93
+ serves both as `null`.
94
+
95
+ ### Reshaped output — the same fields, carrying JSON
96
+
97
+ - **`preview_strategy_report.conditionsTableText`** is the conditions glossary as a `## Strategy Conditions
98
+ Glossary` instruction, then the conditions register as one JSON line: `surface: "GRID"`, and `coins[]` each with
99
+ `outcomes` keyed by condition and `verdict` by display name (`null` when the strategy declares no verdict). It was a
100
+ markdown table followed by the glossary.
101
+ - **`preview_strategy_report.tradeConditionsBlockText`** is the trade glossary, a blank line, then the target coin's
102
+ register as one JSON line: `surface: "TRADE"`, `verdict` as `{ name, description }`, and `conditions[]` each with
103
+ `outcome`, `required`, `provisional`, `counts`, `exitRule` and the evaluator's `evidence`. It was a
104
+ `## STRATEGY CONDITIONS` heading, a table and evidence bullets.
105
+ - **A fact section's text is its JSON**, `{ id, title, facts, glossary }` with `facts` keyed by label, in
106
+ `get_market_context.sections[].content`, `get_context_source_full_preview.content`,
107
+ `preview_strategy_report.renderedSections[].section.text`, and the `get_signal_log` report of trades recorded
108
+ from v91 on. The session field is a fact section. A table section's text is still its markdown.
109
+ - **`get_agent_prompt_context_preview.promptSections[].content`** is each section as the agent reads it: a data
110
+ section's JSON, a table's included, and an instruction's prose.
111
+
112
+ ### Widened enum
113
+
114
+ - **`get_agent_prompt_context_preview.promptSections[].kind`** gains `strategy-conditions-glossary`, the instruction
115
+ read before the conditions register. The market-context reads exclude it, as they exclude the register.
116
+
117
+ Something read at v90 is gone or reshaped at v91 — hence the MAJOR.
118
+
27
119
  ## Contract history — v90 (Arena and radar deployments join the draft lifecycle)
28
120
 
29
121
  **Breaking at v90: input is refused and reshaped, output is reshaped, five error codes are removed.** v90 is the
@@ -2820,6 +2912,11 @@ you reach over MCP:
2820
2912
  > request; `skills/EXPORT.json` records a hash per file and `src/__tests__/skill-provenance.test.ts`
2821
2913
  > fails CI on a hand edit. Change the skill upstream and let the export lane bring it here.
2822
2914
 
2915
+ A client connected to BattleGrid's remote server directly receives its `initialize` instructions:
2916
+ the rules that span tools and one routing card per workflow, each naming the `battlegrid-*` skill
2917
+ above that holds the full procedure. This proxy does not relay those instructions; the skills
2918
+ installed beside it carry the same workflows.
2919
+
2823
2920
  ## License
2824
2921
 
2825
2922
  [MIT](LICENSE)
package/dist/index.d.ts CHANGED
@@ -49,7 +49,7 @@ import { type Implementation, type Prompt, type Resource } from '@modelcontextpr
49
49
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
50
50
  * correction. Never move it to track the server.
51
51
  */
52
- export declare const PACKAGE_VERSION = "31.2.72";
52
+ export declare const PACKAGE_VERSION = "31.2.75";
53
53
  export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
54
54
  export interface EnvConfig {
55
55
  apiKeys: string[];
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema
52
52
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
53
53
  * correction. Never move it to track the server.
54
54
  */
55
- export const PACKAGE_VERSION = '31.2.72';
55
+ export const PACKAGE_VERSION = '31.2.75';
56
56
  export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
57
57
  const MAX_RETRIES = 3;
58
58
  const RETRY_DELAYS_MS = [2000, 4000, 8000];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@battlegrid/mcp-server",
3
- "version": "31.2.72",
3
+ "version": "31.2.75",
4
4
  "description": "BattleGrid MCP server — play crypto prediction games from AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
3
- "contractVersion": "88.1.0",
3
+ "contractVersion": "92.0.0",
4
4
  "files": {
5
- "battlegrid-agent-management/SKILL.md": "b2f04d76480de2f9fdbd00f8cb25610d2b72fcaa4f87ca57a09bd9ffc78fdbba",
6
- "battlegrid-arena-play/SKILL.md": "4cfaa92a8d0dcaf477b227abd774e7aa8642c185d72108dfd401be0cf8aecd7f",
7
- "battlegrid-market-analysis/SKILL.md": "3a9943de9f482a8230fb2983efd2f2d8b8c17d49c242e49b68e7243c20c3aa7b",
8
- "battlegrid-radar-deployment/SKILL.md": "a624807c9d1f0e1fa650592bfbbdab228d75098d66a9c2d4d3cd3f85e5d3796c",
9
- "battlegrid-strategy-authoring/SKILL.md": "a5066740d7d57f095545fd812b0228a8eef6e3b08c87047596a0e1639ae4971d",
5
+ "battlegrid-agent-management/SKILL.md": "e5ffab64e6a369d5c783aed2b1c7c513b01b870063fd859c9cc4d9fc45d30aac",
6
+ "battlegrid-arena-play/SKILL.md": "01b2192c19b93b2315ff1a2b512ee0bcb7fd809c7d77fbbd93307fe2f45924a3",
7
+ "battlegrid-market-analysis/SKILL.md": "875bda71dd766ad06ac8e508679bcd3400a600cf74d2eac6c922dbe23175f325",
8
+ "battlegrid-radar-deployment/SKILL.md": "aaa4d13d32d470582d4bab0dc71594d4adf6b665bf45ee3384ee794dd8f82ba4",
9
+ "battlegrid-strategy-authoring/SKILL.md": "d4c5c87d9306193be8be77b1ade4938bbbc8805d66bc5267c00c2a8db156a92e",
10
10
  "battlegrid-strategy-doctor/SKILL.md": "14f20bff9e2b5a59d11634fe3615b1298d672d5e2eba06d3dc8698b571b3bee3",
11
- "battlegrid-strategy-examples/SKILL.md": "7ec80099efe3a923e1a701703eba5edbbe3ce044628273c806dfd688f2fd715c",
12
- "battlegrid-strategy-examples/references/playbooks.md": "63aa07f3d55f97e53b2bf6401528807ab23bd093a4bbc37e598158249db24a59",
11
+ "battlegrid-strategy-examples/SKILL.md": "d49ed36a864d1ead53c942979639d331b2bf375b50fffd745548a0acde39d641",
12
+ "battlegrid-strategy-examples/references/playbooks.md": "9e89b5cc8d80fc27bc331862fdc2403c26b36131b132614782c8425df74bcd28",
13
13
  "battlegrid-strategy-examples/references/recipes.md": "61382acdd295fa159f055b787ba501f1c015f383f59a6714a973057f10de611f",
14
14
  "battlegrid-strategy-examples/references/tradingview-ports.md": "2b13adbc137178955edcca31b6ab40c01c745a932a0750b91db8820c736700bb",
15
- "battlegrid-trade-analysis/SKILL.md": "f5d817022ab912f00e9c17ffa720a7599f6e6fa4b806d873fe5e7520c51a8dcd",
16
- "battlegrid-trade-proposal/SKILL.md": "9bf7b7648fb009db118a3a428b15abf38df5ba56a2ef4c8a4eae3d979bdc8bcb"
15
+ "battlegrid-trade-analysis/SKILL.md": "a1e666ca03bd641413096ed14806f101354f4e913e005faad6026a486143af0c",
16
+ "battlegrid-trade-proposal/SKILL.md": "d207a30065c17e13efaf5391805e5603e6956633cba5d0a1ff60e276518d9696"
17
17
  }
18
18
  }
@@ -84,7 +84,8 @@ omit `agentId` and send `draftVersion: 0` on the first call, then reuse the id i
84
84
  `{ strategyId }`, all four required to create, plus TRADING_CONFIG with every agent-owned field —
85
85
  or no TRADING_CONFIG at all, to take the platform seed. The build then survives a refresh, lists in
86
86
  the Agents Hub as *not yet created*, and opens on the create screen, where **the player's own
87
- Create creates it**. Staging creates nothing and spends no slot.
87
+ Create creates it**. Staging creates nothing and spends no slot. The avatar is minted by the server
88
+ at creation — there is no avatar to stage.
88
89
 
89
90
  **Then read it, confirm it, and commit it.** `get_agent_draft` serves the draft, its
90
91
  `draftVersion`, a `committedRevision` of `null` and the diagnostics: a missing or invalid axis is
@@ -110,6 +111,11 @@ Profile → Wallet tab path instead, because no control renders there. **Create
110
111
  way**: the consent gates entering a game, never commissioning one. A `true` flag is consent alone
111
112
  and not readiness — the pipeline's own refusal at the fee is the authority.
112
113
 
114
+ **When the mandate is trading, the agent needs a trading wallet.** `get_account_state` reports
115
+ `tradingWalletProvisioned`; the wallet is provisioned in the web app, where its key is signed
116
+ locally in the browser, and no tool provisions it. Without one the agent can be created but cannot
117
+ place an order — say so with the create, never after the first refused trade.
118
+
113
119
  ### 3. Stage, read, confirm, commit — every change goes through the draft
114
120
 
115
121
  Before proposing any change to an existing agent, `get_agent_draft`. **A draft means the player is
@@ -242,7 +248,9 @@ keys). The commit holds the configuration to the same risk rules the agent form'
242
248
  the P&L. It carries a schema-level `confirm: true`, so present the position, its unrealized
243
249
  P&L and its protections, and confirm before calling. An exchange rejection comes back as a typed
244
250
  trading error — report it; a success means the close order was *accepted*.
245
- - **`override_agent_protection`** moves the effective stop. Its confirm must say, in words, that
251
+ - **`override_agent_protection`** moves the effective stop — the stop-loss only; take-profit is not
252
+ overridable here. It names the stop it replaces by `effectiveStopLossOrderId`, which
253
+ `get_position_audit_history` serves. Its confirm must say, in words, that
246
254
  the change **moves the stop outside the platform's protective ratchet** — that is exactly what
247
255
  the tool is for — and name the direction: whether the new level sits further from or closer to
248
256
  price than the one the platform is holding. Present the current protection first, then the
@@ -59,7 +59,11 @@ the enablement path there.
59
59
  A direct submission carries `grid`, `reasoning`, `confidenceScore`, `modelName` and
60
60
  `pickReasoning`, and all of them are required.
61
61
 
62
- - **Exactly one cell is captain** (2× multiplier).
62
+ - **Exactly one cell is captain.** The session's preset sets what that is worth: a correct captain's
63
+ points are multiplied by its `captainMultiplier`, and a wrong captain's penalty by its
64
+ `captainWrongPenaltyMultiplier` — both published by `list_game_presets`, and different per preset.
65
+ Read them rather than assuming a number, and captain the pick you are most confident in among
66
+ those with a move worth amplifying.
63
67
  - **`pickReasoning` carries one entry per grid cell — every cell, no coin twice.** Compose it
64
68
  correctly because it is yours to compose, not because something downstream will catch it: the
65
69
  boundary refuses an incomplete payload before the fee moves, and leaning on that check means
@@ -98,12 +102,15 @@ reasoning as the player's, and the attribution is part of what was scored.
98
102
 
99
103
  ### 5. After settlement
100
104
 
105
+ - A session moves PENDING → LIVE → RESOLVING → SETTLED. Two states sit off that path: CANCELLED
106
+ (entry fees refunded) and SETTLEMENT_QUARANTINED (settlement exhausted its retries and awaits an
107
+ operator). Either one is the answer — report it as the status.
101
108
  - `get_market_grid_results` — available only once the session is SETTLED. Before that it returns a
102
109
  typed CONFLICT naming the current status: **report it and stop**. Do not poll it in a loop; the
103
110
  op budget is finite and a settling session is not a failure.
104
- - `get_mcp_reasoning_journal` on request — the reasoning, confidence and picks as recorded at
105
- submit. Compare the outcome against what was actually reasoned, and say where the reasoning was
106
- wrong.
111
+ - `get_mcp_reasoning_journal` on request — the player's own submission: the reasoning, confidence
112
+ and picks as recorded at submit. Compare the outcome against what was actually reasoned, and say
113
+ where the reasoning was wrong. An agent's record is a different journal, `get_agent_journal`.
107
114
 
108
115
  ## Refusals, and what each one means
109
116
 
@@ -20,7 +20,10 @@ Run these in order. Skip a step only when the player's question makes it irrelev
20
20
 
21
21
  1. **Regime.** `get_regime_snapshot` for the current read. `get_regime_history` when the player
22
22
  asks how we got here, or when the snapshot alone would hide a fresh flip — a regime that
23
- turned in the last few periods is a different fact from one that has held for weeks.
23
+ turned in the last few periods is a different fact from one that has held for weeks. Read the
24
+ snapshot's evidence before its label: a label the hysteresis buffer is **holding** is the
25
+ previous bar's, not this bar's, and conviction names which rule matched, not how comfortably —
26
+ the margins to each gate say how close the read is to turning.
24
27
  2. **Market context.** `get_market_context` — this carries the breadth, funding and open-interest
25
28
  picture. Funding and OI are the two figures most often skipped and most often decisive: crowded
26
29
  positioning is the difference between "trend" and "trend about to be squeezed". Report both.
@@ -28,9 +31,11 @@ Run these in order. Skip a step only when the player's question makes it irrelev
28
31
  "strength in majors". A laggard list is as informative as a leader list and is usually omitted.
29
32
  3b. **Many coins on the same indicators — one call, not one per coin.** When the answer needs the
30
33
  indicator modules ACROSS a set of coins (comparing RSI, funding or volatility over the leaders,
31
- over a category, over the session pool), use `preview_strategy_report`: one table per module with
32
- coins as rows, the schema preamble emitted once instead of per coin, and a server-reported token
33
- budget. Ask `list_strategy_categories` for the section catalogue and pass the sections you want,
34
+ over a category, over the session pool), use `preview_strategy_report`: one section JSON per
35
+ module with coins as rows, each column described once instead of per coin, and the exact size of
36
+ the response it served (`budgetUsage.resultChars`). The default `detail: "concise"` is the read;
37
+ `"detailed"` adds only the authoring contract a strategy builder needs. Ask
38
+ `list_strategy_categories` for the section catalogue and pass the sections you want,
34
39
  each as `{ "kind": "platform", "sectionKey": "includeRsi" }`, inside
35
40
  `source: { "kind": "FIELDS", "timeframe": …, "sections": [ … ] }` — no authoring vocabulary is
36
41
  needed. Use `{ "mode": "ranked", "limit": N }` as the coin selection when you have no explicit
@@ -16,6 +16,11 @@ Radar (per coin) and Arena deployment (per preset) are **separate bounded contex
16
16
  no slot shape, no condition union and no resolution type. Never carry a fact from one to the other,
17
17
  and never describe them as one thing with two modes.
18
18
 
19
+ **Deployments are the autonomous trading switches.** An agent has no trading mode of its own: it
20
+ trades a coin on its own only where a Radar policy puts it on duty, and a proposal the player
21
+ approves needs no deployment. An Arena deployment enters an agent into a preset's sessions and grants
22
+ **no** trade permission — to have an agent trade a coin on its own, deploy it on Radar.
23
+
19
24
  ## The five failures this flow exists to prevent
20
25
 
21
26
  1. **A write with no fresh preview.** *Cue: any `commit_radar_deployment_draft`,
@@ -69,6 +74,19 @@ Then read what exists:
69
74
  If the question is *why did my radar agent not fire*, do not start from the journal — **go to step
70
75
  5**, which reads state first and pattern second.
71
76
 
77
+ **In a conversation the radar builder hosts** — your context names the radar builder and a coin id —
78
+ the player's builder is open on that coin whether or not a draft exists yet. Stage every change into
79
+ that coin's draft, naming that `coinId`; it lands in their builder as a proposal. Their own Save
80
+ commits it; commit it yourself only when they explicitly confirm through `ask_user`, after the
81
+ preview this flow requires. The builder's state — its rules, the default agent, who is on duty now —
82
+ arrives as the turn's attached scope. An axis their form rewrote after your proposal is their answer:
83
+ propose it again only on their word.
84
+
85
+ **In a conversation the Arena deploy editor hosts** — your context names the Arena deploy editor and a
86
+ preset id — the same, for that arena's deployment draft, naming that `presetId`. Its scope carries the
87
+ rules, the default agent and what the next session resolves to, marked `simulated` while the player
88
+ is test-routing a regime. Never carry a fact from a radar conversation into it, or the reverse.
89
+
72
90
  ### 2. Stage the change, then preview exactly what would be written
73
91
 
74
92
  **Radar** — the change goes into the player's draft of the coin, never straight to the policy:
@@ -82,11 +100,18 @@ If the question is *why did my radar agent not fire*, do not start from the jour
82
100
  `agentRegimeTimeframe`. Staging writes nothing live and shows the change in the player's builder as
83
101
  a proposal. A refusal naming axes means the player changed them after your read: read again and
84
102
  propose against what they now have.
85
- 2. `preview_radar_resolution` with `request: { kind: "DRAFT", draftVersion }` at the version staging
86
- returned. It composes the draft over the deployed policy **exactly as the commit will write it**.
87
-
88
- To try a slot set without touching the draft, preview `{ kind: "SLOTS", slots }` — it certifies
89
- nothing and cannot be committed.
103
+ 2. `get_radar_deployment_draft` — the draft as it now stands: its `draftVersion`, the
104
+ `committedRevision` it would be published over (null for a coin with no policy yet), the diff
105
+ against the deployed policy, the `diagnostics` a commit would refuse, and the `impact` — who goes
106
+ on duty, whether it trades once committed, and for a first deployment the coin cap
107
+ (`admission.coinCapReached` true means the commit will be refused until a coin is un-deployed).
108
+ Do not confirm a draft whose diagnostics carry an error: stage a fix.
109
+ 3. `preview_radar_resolution` with `request: { kind: "DRAFT", draftVersion }` at the version that read
110
+ returned. It composes the draft over the deployed policy **exactly as the commit will write it**,
111
+ and refuses a draft no longer at that version with `DRAFT_VERSION_MOVED`.
112
+
113
+ To try a slot set without touching the draft, preview `{ kind: "SLOTS", slots }` — it resolves
114
+ explicit slots and nothing can commit it.
90
115
 
91
116
  **Arena** — the same shape, keyed on `presetId`:
92
117
 
@@ -94,8 +119,10 @@ nothing and cannot be committed.
94
119
  draft) and only the axes you are changing, each WHOLE: `RULES` (the complete ordered rule list —
95
120
  first is highest priority; a rule's regime condition names a set), `DEFAULT_SLOT` (the catch-all, or null for none)
96
121
  and `REGIME_ANCHOR` (the anchor override, or null to inherit the arena's). Pausing is never staged.
97
- 2. `preview_deployment_resolution` with `request: { kind: "DRAFT", draftVersion }` at the version
98
- staging returned.
122
+ 2. `get_deployment_policy_draft` — its `draftVersion`, `committedRevision`, diff, `diagnostics` and
123
+ `impact` (the next session's resolution and whether it will play), exactly as for Radar.
124
+ 3. `preview_deployment_resolution` with `request: { kind: "DRAFT", draftVersion }` at the version
125
+ that read returned.
99
126
 
100
127
  A draft that would hold no rule and no catch-all is a withdrawal, which a commit never does — that is
101
128
  `delete_deployment_policy` (step 4).
@@ -110,7 +137,8 @@ and priority that put it on duty, its regime reading at its own `regimeTimeframe
110
137
  qualification verdict and its close clock — and `readings`, one entry per on-duty agent in the same
111
138
  order: what that agent reads on the coin right now (below). For **Arena**: which agent goes on duty,
112
139
  which slot matched and at what priority, the regime and conviction used, and the qualification
113
- verdict. **Render `section` as the server sends it — never re-derive it, and never pick, rank or drop
140
+ verdict, under the resolution's `status` as served — RESOLVED (an agent plays the next session),
141
+ IDLE (the rules ran and none matched), WARMING, NO_SESSION or PAUSED. **Render `section` as the server sends it — never re-derive it, and never pick, rank or drop
114
142
  rows yourself.** A non-null reason does not mean idle (`ON_DUTY_BUT_POSITION_BLOCKED` carries a reason
115
143
  and is not idle), so deciding the headline yourself gets it wrong. `enabledAfterCommit` says whether
116
144
  the policy will play once committed — a paused policy stays paused, and a first Arena deployment
@@ -123,8 +151,8 @@ resolve.
123
151
  **A Radar preview refuses an agent the scan cannot read on the coin.** Every slot agent, on duty now
124
152
  or not, must have every condition the radar acts on — its required conditions, the ones carrying a
125
153
  verdict, its exit rules, and every condition they reference — readable by the radar scan on this
126
- coin. When one is not, the preview itself is refused, issues no certificate, and nothing can be
127
- committed: `CONDITION_UNREADABLE_BY_RADAR_SCAN`, whose `details.context.reachReason` says why
154
+ coin. When one is not, the preview itself is refused, and the draft read reports the same refusal in
155
+ its diagnostics: `CONDITION_UNREADABLE_BY_RADAR_SCAN`, whose `details.context.reachReason` says why
128
156
  (`INSTRUMENT` — the market-data profile of the coin, or of a benchmark section's own instrument, carries
129
157
  no such data, or the market-data service publishes no profile for it at all: the refusal names the kind
130
158
  of data the profile lacks, or says there is none; `AGENT_TIMEFRAME` — the agent has no such rung;
@@ -175,28 +203,32 @@ the reason — and **names what the change does to the stored policy**, read fro
175
203
 
176
204
  Then:
177
205
 
178
- - **Radar** → `commit_radar_deployment_draft({ coinId, previewToken, confirm: true })`, with the
179
- `previewToken` the **live** DRAFT preview returned — no `simulatedRegime`, within five minutes. The
180
- draft ends when it commits.
181
- - **Arena** → `commit_deployment_policy_draft({ presetId, previewToken, confirm: true })`, the same
182
- way: the live DRAFT preview's token, within five minutes. The draft ends when it commits.
206
+ - **Radar** → `commit_radar_deployment_draft({ request: { coinId, draftVersion, expectedRevision } })`,
207
+ naming exactly the `draftVersion` and `committedRevision` your draft read returned — the version
208
+ your preview resolved. Ask with `ask_user` first and commit only after an explicit confirming pick.
209
+ The axes it published leave the draft.
210
+ - **Arena** → `commit_deployment_policy_draft({ request: { presetId, draftVersion, expectedRevision } })`,
211
+ the same way.
183
212
 
184
- **On a typed CONFLICT: re-read, re-preview, re-confirm.** In that order, and all three. The draft or
185
- the policy changed under you, so the state your preview resolved and the radius you stated are both
186
- stale. A certificate is bound to the draft version, the deployment and its content, so a retry
187
- with the old one is refused again — only a fresh preview earns a new one.
213
+ **On `DRAFT_VERSION_MOVED` or a CONFLICT: re-read, re-preview, re-confirm.** In that order, and all
214
+ three. The draft or the deployment changed under you, so the state your preview resolved and the
215
+ radius you stated are both stale. A retry with a bumped number commits something the player was not
216
+ shown — the server refuses a version or revision you did not read, and the confirmation is yours to
217
+ ask for again.
188
218
 
189
219
  ### 4. Pause, resume, discard, delete
190
220
 
191
221
  **Pause a coin's Radar** → `pause_radar_deployment({ coinId })`. No preview and no revision: stopping
192
- never waits on a read. The player's draft is kept. **Resume** → preview the deployed policy with
193
- `request: { kind: "COMMITTED" }`, tell the player which agents will go on duty again, and on their word
194
- `resume_radar_deployment({ coinId, previewToken, confirm: true })` with the token that preview
195
- returned.
222
+ never waits on a read. The player's draft keeps its content and is re-based onto the paused revision.
223
+ **Resume** → preview the deployed policy with `request: { kind: "COMMITTED" }`, tell the player which
224
+ agents will go on duty again, and on their word
225
+ `resume_radar_deployment({ coinId, expectedPolicyId, expectedRevision })` naming the `policyId` and
226
+ `revision` that preview returned — so it re-arms the policy the player was shown, or nothing.
196
227
 
197
228
  **Pause an arena** → `pause_deployment_policy({ presetId })`, and **resume** it the Radar way: preview
198
- `{ kind: "COMMITTED" }`, then `resume_deployment_policy({ presetId, previewToken, confirm: true })` on
199
- the player's word. Both touch only the pause, never the rules or the player's draft.
229
+ `{ kind: "COMMITTED" }`, then `resume_deployment_policy({ presetId, expectedPolicyId, expectedRevision })`
230
+ with the pair it returned, on the player's word. Both touch only the pause, never the rules; the
231
+ player's draft keeps its content.
200
232
 
201
233
  **Entries already made stand.** A pause or a delete of an arena returns `openEntries` — the sessions
202
234
  the player's agent already entered that have not locked, each with its lock time and entry fee. They
@@ -204,21 +236,23 @@ play out unless the player cancels them. Name each one, ask whether to cancel it
204
236
  `cancel_market_grid_submission({ sessionId, confirm: true })` only for the entries they pick — the
205
237
  same cancellation and refund as their own Cancel button. Say nothing is cancelled when they decline.
206
238
 
207
- **Discard a draft** only on the player's word. Call `discard_radar_deployment_draft` or
208
- `discard_deployment_policy_draft` with
209
- `confirm: false` first: the answer names what the draft holds, its version, and when and from where
210
- it was last written. Tell the player, ask, and call with `confirm: true` and `expectedVersion` set to
211
- **the version you were shown**. If the draft moved since, nothing is removed and the answer names what
212
- it now holds — show them again.
239
+ **Discard a draft** only on the player's word. Read it first and tell the player what it holds and
240
+ when and from where it was last written; then
241
+ `discard_radar_deployment_draft({ request: { coinId, draftVersion } })` or
242
+ `discard_deployment_policy_draft({ request: { presetId, draftVersion } })` at **the version you read**.
243
+ If the draft moved since, it is refused with `DRAFT_VERSION_MOVED` and nothing is removed — read it
244
+ again and show them.
213
245
 
214
246
  **Deletes** — `delete_radar_deployment` / `delete_deployment_policy` remove the **entire** policy —
215
247
  every slot and condition — and revoke the standing authority. Un-deploying **also ends the player's
216
248
  draft** of the coin or the arena. There is no preview here, and correctly so: there is no resolution
217
249
  to preview once the policy is gone. The evidence is **the deployment's own read**, which supplies the
218
- `expectedRevision` the delete carries — and for Arena the `expectedPolicyId` beside it.
250
+ `expectedPolicyId` and `expectedRevision` both deletes carry — a policy removed and redeployed since
251
+ your read is refused rather than removed.
219
252
 
220
253
  State what stops, from that read: which agents were on duty (every row of `resolvesNow.onDuty`) or
221
- eligible, what the policy was firing on, and that the slots are not recoverable. Both tools carry a schema-level `confirm: true`.
254
+ eligible, what the policy was firing on, and that the slots are not recoverable — and delete only after
255
+ an explicit confirming pick.
222
256
 
223
257
  **If the player wants to stop trading without losing the slots, that is not a delete** — it is
224
258
  `pause_radar_deployment` for Radar and `pause_deployment_policy` for Arena. Offer that
@@ -306,15 +340,14 @@ Three negatives, all checkable:
306
340
  last decision a row names, `get_radar_close_decisions` for any other — and quote it as stored;
307
341
  never recompute it.
308
342
 
309
- ## Preview-before-commit: enforced for both
343
+ ## Preview-before-commit: your discipline, fenced by the server
310
344
 
311
- A deployment arms only with a certificate. `commit_radar_deployment_draft` and
312
- `commit_deployment_policy_draft` take the certificate a live preview of that draft returned, and
313
- `resume_radar_deployment` and `resume_deployment_policy` one from a live preview of the deployed
314
- policy. Each is bound to your credential, the player, the coin or arena, the subject previewed, the
315
- deployment and its revision, the draft version and the content the preview resolved, and expires after
316
- five minutes. A simulated preview returns none. So a commit of anything the player was not shown is
317
- refused by the server — but the player's confirmation is still yours to ask for, against the preview.
345
+ The preview card and the confirming pick are yours to show and ask for, every time. What the server
346
+ enforces is the fence under them: a commit names the draft version and committed revision its read
347
+ returned, and a resume or a delete names the deployment and revision it read. A draft that moved after
348
+ your preview is refused with `DRAFT_VERSION_MOVED`; a deployment whose revision moved, or that was
349
+ withdrawn and redeployed, is refused as a CONFLICT. So a commit never publishes a draft version your
350
+ preview did not resolve — provided you previewed the version you commit.
318
351
 
319
352
  ## When a write's outcome is unknown
320
353
 
@@ -104,7 +104,9 @@ anywhere; if the two ever disagree, you have created the exact ambiguity this st
104
104
  `get_strategy_signal_definition` for each signal you intend to use.
105
105
 
106
106
  Compose sections, columns, conditions and rules **only** from vocabulary returned in this
107
- conversation. A field you remember from another strategy is not discovery.
107
+ conversation. A field you remember from another strategy is not discovery. A signal's availability
108
+ at a timeframe is structural: it says the signal can be computed there, not that live data is
109
+ flowing or that it will trigger.
108
110
 
109
111
  **Read the answer, not just the call.** Each of these returns one field that decides a composition
110
112
  question you would otherwise guess — and a refusal you would otherwise earn:
@@ -144,7 +146,8 @@ teaches what to compose; this skill stays the authority on the flow.
144
146
 
145
147
  `derive_strategy_rule_view` belongs here, at composition time, and only here: it reports report
146
148
  membership and registry-default allocations for sections and rows you are about to stage. It reads
147
- no draft and no strategy, so it can never stand in for the review in step 5.
149
+ no draft and no strategy, so it can never stand in for the review in step 5. Its suggestions and
150
+ reset-to-default choices change only what you stage next — no tool applies one on its own.
148
151
 
149
152
  `simulate_aggregate_score` does **not** belong here. It is a review tool (step 5), and running it
150
153
  now answers a question about rows you have not staged, not about the draft the player will be
@@ -224,7 +227,13 @@ Once the stage's diagnostics carry nothing you cannot explain, read the draft wi
224
227
  the commit's own: the player is approving all of it, not only what you proposed.
225
228
  - **Whether it can fire.** `preview_strategy_report` with
226
229
  `source: { "kind": "DRAFT", strategyId, draftVersion }`, at the version the read returned,
227
- renders the draft against live market composed exactly as its commit would compose it. Support it
230
+ renders the draft against live market composed exactly as its commit would compose it. Its default
231
+ `detail: "concise"` is exactly what the agent will read — the sections, each surface's conditions
232
+ register with every coin's verdict, and the verdict tally; ask for `"detailed"` only when you need
233
+ each coin's full evaluation or the addressable headers (`authoring`). A preview is bounded by a
234
+ deadline and a result-character cap, both served by discovery as `previewExecutionLimits`
235
+ (`deadlineMs`, `maxResultChars`); one over either is refused whole, never truncated — narrow the
236
+ cohort and preview again. Support it
228
237
  with `get_coin_signal_preview` on the locked universe's main coin(s). The preview's
229
238
  `coinSelection` is its cohort, never strategy state: no strategy has one and `get_strategy` will
230
239
  not return one, so choose it — a short explicit list of the tickers the change is about for a
@@ -246,8 +255,9 @@ revise rather than presenting it as healthy.
246
255
  - `operation` — CREATE, UPDATE or RESTORE. A RESTORE is a draft over an archived strategy: say
247
256
  that committing it also restores the strategy and moves its bound agents, by count, from
248
257
  ORPHANED to BOUND.
249
- - `boundAgentCount`, `openPositionCount` and `timeframeChanged`. A change to signal rules reaches
250
- every bound agent the moment it commits — say so.
258
+ - `boundAgentCount`, `openPositionCount` and `timeframeChanged`. A committed change reaches every
259
+ bound agent the moment it commits, while a position already open keeps the exit rules it opened
260
+ under — say so.
251
261
  - `admission` — for a create draft or an archived strategy, the quota and whether the name is
252
262
  free. It is advisory: the commit decides.
253
263
  - `capital` — each bound agent the drafted stop band would leave unable to place an order, with its
@@ -319,9 +329,13 @@ verbs writes content.
319
329
  commit does, and a full quota or a name already in use is refused there.
320
330
  - **Archiving** — `archive_strategy` with the `strategyId` and the `expectedRevision` you read.
321
331
  State how many agents are bound, that their configuration stays byte-identical, that open
322
- positions are unaffected and that the player's draft is kept.
323
- - **Restoring** — `restore_strategy` with the `strategyId` and `expectedRevision` brings back
324
- unchanged, already-viable content and keeps the player's draft. Content that is not viable is
332
+ positions are unaffected and that the player's draft is kept, re-based onto the revision the
333
+ archive produces.
334
+ - **Restoring** — find the archived strategy with `list_strategies` and `get_strategy` with
335
+ `includeInactive: true`, which add the player's own strategies in any state; another player's
336
+ PRIVATE strategy stays hidden either way. `restore_strategy` with the `strategyId` and
337
+ `expectedRevision` brings back unchanged, already-viable content and keeps the player's draft,
338
+ re-based onto the restored revision. Content that is not viable is
325
339
  refused with `REPAIR_REQUIRED` and stays archived: stage the repair into the strategy's draft and
326
340
  commit it — that commit restores it, and its read says `operation: RESTORE`.
327
341
 
@@ -12,8 +12,8 @@ shapes are binding in the sense that matters — they are checked, not merely as
12
12
  elsewhere in this file are not covered by that gate, and vocabulary moves with deploys, so discovery
13
13
  in the conversation stays the authority — prefer what `list_strategy_vocabulary`,
14
14
  `get_strategy_column_contract`, and `get_strategy_signal_definition` return over anything
15
- printed here, and read exact headers from a preview's `conditionColumns` before conditioning on
16
- them.
15
+ printed here, and read exact headers from a `detail: "detailed"` preview's
16
+ `authoring.conditionColumns` before conditioning on them.
17
17
 
18
18
  ## The full-power checklist
19
19
 
@@ -69,8 +69,8 @@ things you do depends on the operation: on a **CREATE, omit `sectionKey` entirel
69
69
  is refused, since a new strategy owns no custom sections yet; on an **UPDATE or RESTORE, send back
70
70
  the keys `get_strategy` returned** for the sections you are keeping, and omit it on a section you
71
71
  add. Either way you never invent one. To section-qualify a duplicated header, read the key from a
72
- preview's `conditionColumns` or from the qualified candidates a `CONDITION_COLUMN_AMBIGUOUS`
73
- refusal offers.
72
+ `detail: "detailed"` preview's `authoring.conditionColumns` or from the qualified candidates a
73
+ `CONDITION_COLUMN_AMBIGUOUS` refusal offers.
74
74
 
75
75
  **Event columns print only on their event.** `MACD_cross` and `EMA5_13` (Bullish/Bearish) carry a
76
76
  value on the crossing bar and are null on every other one, which reads as UNRESOLVED. Use an event
@@ -145,7 +145,7 @@ decide it (TRUE once `atLeast` bars read TRUE, FALSE once the rest can no longer
145
145
  UNRESOLVED.
146
146
 
147
147
  What a header admits is SERVED, never guessed: every column in the strategy's report catalog
148
- (`preview_strategy_report` → `conditionColumns[].outputs[].conditionHold`, and each scalar metric in
148
+ (`preview_strategy_report` at `detail: "detailed"` → `authoring.conditionColumns[].outputs[].conditionHold`, and each scalar metric in
149
149
  the vocabulary) carries `{ timeframe, maxWindow, maxAtLeast, refusal }` — the timeframe it counts,
150
150
  the largest `of`, the largest `atLeast`, and, when it admits only Once, why. `maxWindow` is the
151
151
  history the store keeps for that column, less the column's own lookback and warm-up. A hold past
@@ -101,10 +101,10 @@ giveback 30 buffer 0.15; timeDecay ON aggressive (45/15, 15→60, stale 30).
101
101
 
102
102
  ## Using a playbook
103
103
 
104
- Discover → confirm headers (`get_strategy_column_contract`, or one `preview_strategy_report`
105
- whose `conditionColumns` lists every addressable header with operators and vocabulary) →
106
- calibrate literals against the previewed live values → stage the draft and fix what its
107
- diagnostics name → read it back and review the drafted rule rows, then the draft's own report
108
- preview — condition outcomes, verdict tally, and `marketReadMarkers` (fix `unknown` / `ambiguous`
109
- markers) → commit per the strategy-authoring flow. Coin selection is call context, not strategy
104
+ Discover → confirm headers (`get_strategy_column_contract`, or one `preview_strategy_report` at
105
+ `detail: "detailed"`, whose `authoring.conditionColumns` lists every addressable header with
106
+ operators and vocabulary) → calibrate literals against the previewed live values → stage the draft
107
+ and fix what its diagnostics name → read it back and review the drafted rule rows, then the draft's
108
+ own report preview — the conditions register (`gridConditions`), verdict tally, and
109
+ `marketReadMarkers` (fix `unknown` / `ambiguous` markers) → commit per the strategy-authoring flow. Coin selection is call context, not strategy
110
110
  state: explicit tickers for focused work, `ranked` (with an optional category) for scanning books.
@@ -33,7 +33,15 @@ Not "what were its returns" — whether it did *the thing it was told to do*.
33
33
  at its own `strategyRevision`, and judging it against the strategy's current revision measures it
34
34
  by rules it is not running.
35
35
  - `get_agent_performance` and `list_trade_outcomes` — judge against that mandate.
36
- - `get_trade_outcome_by_decision` / `get_trade_chart` when a specific trade needs explaining.
36
+ - `get_trade_outcome_by_decision` / `get_trade_chart` when a specific trade needs explaining. A
37
+ measurable trade's `tradeExcursion` reads in R: `mfeR` and `maeR` (the best and worst it reached),
38
+ `exitR`, and `giveBackR` (what it surrendered between its peak and the close). `exitEfficiency` is
39
+ a union — branch on its `state` before reading `efficiencyPercent`. All of them are gross of
40
+ costs; `netPnl` is the after-fee figure.
41
+ - The record around the trades: `get_agent_game_history` and `get_user_agent_game_history` (Market
42
+ Grid results, one agent or all), `get_agent_thought_log` and `get_user_thought_log` (the
43
+ reasoning), `get_agent_activity_feed` and `get_user_activity_feed` (what happened), and
44
+ `get_agent_explorer` for where the agent stands among public agents.
37
45
  - `get_signal_performance` when the question is whether the agent's signals are working, as
38
46
  distinct from whether its trades made money.
39
47
 
@@ -43,9 +51,16 @@ reading a clean report about a messy account learns nothing.
43
51
 
44
52
  ## 3. Open positions, with protections and distance to trigger
45
53
 
46
- - `get_agent_open_positions` per agent, or `list_user_active_positions` for everything at once.
47
- - For each open position, report its protections **and how far price sits from each trigger** —
48
- a stop is a number the player cannot act on; "3.1% from the stop" is one they can.
54
+ - `list_user_active_positions` for everything at once: it carries the live mark and P&L
55
+ (`markPrice`, `unrealizedPnlUsd`, `roePct`) — check `pricingStatus` and `generatedAtMs` before
56
+ calling a mark current. `list_session_agent_positions` is the same view for one Market Grid
57
+ session. `get_agent_open_positions` is an entry-only view with no live mark: never measure a
58
+ distance to a trigger from it.
59
+ - For each open position, report its protections **and how far the live mark sits from each
60
+ trigger** — a stop is a number the player cannot act on; "3.1% from the stop" is one they can.
61
+ - `get_decision_order_attribution` maps an executed order back to the decision that placed it.
62
+ `get_open_orders` and `get_order_status` read the exchange directly — slower, and they fail when
63
+ the exchange is unreachable; report that as UNDETERMINED, never as no orders.
49
64
  - `get_deployment_policy` / `get_radar_deployment` when the protection state comes from standing
50
65
  policy rather than the position itself. `list_pending_approvals` and `list_gate_blocks` when
51
66
  something looks like it should have fired and did not.
@@ -36,6 +36,7 @@ re-sorts a list, re-derives a verdict, or approves on the player's behalf.
36
36
 
37
37
  - `scan_agent_coins` for the agent. The rows arrive server-ranked: qualifying coins first by score,
38
38
  then the rest with their first failing gate, then coins that could not be scored with the reason.
39
+ The scan reads the agent's gates and spends no LLM call.
39
40
  - Read the rows **as written**: the rank, the verdict, the first failing gate, the unscorable
40
41
  label. Never re-sort them and never re-derive a verdict from its numbers — the server's ranking
41
42
  is the answer.
@@ -118,3 +119,6 @@ trade.
118
119
  - Never accept because the conviction reads high, because the scan ranked the coin first, or
119
120
  because the player asked you to "find a trade" — finding is not approving. `get_entry_decision`
120
121
  re-reads the row if the conversation moved on before they answered.
122
+ - Only a conversational decision awaits the player: radar fires and arena challenge reactions
123
+ execute on their own. `list_entry_decisions` with `status: PENDING` lists the ones still waiting
124
+ for an agent.