@battlegrid/mcp-server 31.2.50 → 31.2.59

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,522 @@ 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 — v77 (agent risk rules: each loss stop on its own rail)
28
+
29
+ **Breaking at v77: an agent's `tradingConfig` accepts less.** `create_intelligence_agent` and
30
+ `update_intelligence_agent` refuse values v76 stored, and one loss stop moves to a different bound.
31
+
32
+ ### Rejected input — `tradingConfig`
33
+
34
+ - **`maxLeverage` must be a whole number.** `1.5` was stored and executed at `1`; it is now refused.
35
+ - **`maxSlippageBps` is at most `300`**, the entry slippage ceiling no fill could exceed anyway.
36
+ - **USD fields and the three size presets take at most 2 decimals.**
37
+ - **`maxDailyLossUsd` must be at or below `maxCumulativeDrawdownUsd`.**
38
+ - **A body accepted at v76 can be refused at v77 without one byte of it changing.** Each refusal
39
+ names its field in a plain sentence, such as `Whole numbers only.` or
40
+ `Can’t exceed the drawdown stop ($20.00).`
41
+
42
+ ### Wider input — the drawdown stop
43
+
44
+ - **`maxCumulativeDrawdownUsd` is bounded by the platform's drawdown maximum, not by
45
+ `maxConcurrentExposureUsd`.** Max exposure caps margin open at once; the drawdown stop caps loss
46
+ over time. A `$150` drawdown stop on a `$100` max exposure is accepted.
47
+
48
+ ### Wider output
49
+
50
+ - **`get_trading_config_catalog` gains `maximumMaxCumulativeDrawdownUsd`**, the drawdown stop's
51
+ upper bound.
52
+ - **The execution-failure reason gains `BELOW_AGENT_MIN_ORDER`** (owner-private) on `get_signal_log`
53
+ and `get_public_agent_signal_log_detail`: an order sized under the agent's own `minAllocationUsd`,
54
+ which was reported as `BELOW_EXCHANGE_MINIMUM`.
55
+
56
+ ### Served values
57
+
58
+ - **`get_agent_budget`'s `tradesToday` counts entries filled since 00:00 UTC plus entries still
59
+ waiting to fill.** Refused and expired entries no longer count, and the cap is enforced where an
60
+ entry is created. `DAILY_LIMIT_RESERVED` is no longer emitted.
61
+ - **A daily-loss halt whose drawdown stop is also breached becomes a drawdown halt** when the day
62
+ rolls over, rather than lifting.
63
+ - **A capital-feasibility refusal names what clears it**: the `maxConcurrentExposureUsd` that would,
64
+ and, when one at or above the platform minimum would, a lower `minAllocationUsd`.
65
+
66
+ ## Contract history — v76 (the extreme funding signals in percent a year)
67
+
68
+ **Breaking at v76: `funding_extreme_positive` and `funding_extreme_negative` take their threshold in
69
+ percent a year.** Their parameter is `thresholdAnnualizedPct`, the unit of the strategy grammar's
70
+ `ann` column, where it was `thresholdPct`, a fraction of one hourly funding settlement.
71
+
72
+ ### Rejected input — the retired `thresholdPct`
73
+
74
+ - **Both signals take `thresholdAnnualizedPct`**, bounded 1–43,800 and defaulting to 100 — the
75
+ funding label's crypto `extreme` band, so `100` means 100% a year, and the negative signal fires
76
+ below -100%.
77
+ - **`thresholdPct` on either signal is refused as `VALIDATION_ERROR`** wherever a rule's parameters
78
+ are authored: `compile_strategy_plan`, `update_strategy_signal_rule`, `derive_strategy_rule_view`
79
+ and a strategy draft's `SIGNAL_RULES` axis. `oi_surge` keeps its own `thresholdPct`.
80
+ - **A body accepted at v75 can be refused at v76 without one byte of it changing.** A rate per hourly
81
+ settlement converts to percent a year by × 8,760 × 100: `0.0002` is `175.2`.
82
+
83
+ ### Stale plans
84
+
85
+ - **A plan token compiled before the deploy fails as a stale plan** at `stage_strategy_plan` and
86
+ `apply_strategy_plan`, because the authoring catalog digest covers each signal's parameters,
87
+ defaults and explanation: compile again. The strategy preview's `vocabularyDigest` moves too.
88
+
89
+ ### Served values
90
+
91
+ - **`get_strategy_signal_definition` and `list_strategy_signals` serve the new parameter**, its
92
+ default and its explanation.
93
+ - **Each signal's indicator values gain `funding_annualized_pct`**, the annualized rate the signal
94
+ compared, beside `funding_rate`.
95
+ - **Stored rules move to the new key after the deploy**: a rule at the retired default takes `100`,
96
+ and any other converts exactly. `get_coin_signal_preview` and every stored rule then score at the
97
+ new threshold.
98
+ - **No schema hash moves.** Signal parameters are published as a JSON object on every input and
99
+ output, so the change is in the values accepted and served, and `toolCount` is unchanged.
100
+
101
+ ## Contract history — v75 (readability from each coin's data profile)
102
+
103
+ **Breaking at v75: the seven tools v73 narrowed refuse by the coin's market-data profile, and one
104
+ output vocabulary gains a member.** Which data a coin carries is now the profile the market-data
105
+ service publishes for that coin, no longer the coin's asset class.
106
+
107
+ ### Rejected input — the seven tools v73 narrowed
108
+
109
+ - **A condition reading a kind of data the coin's profile does not serve is refused as
110
+ `INSTRUMENT`**, on the coin or on a benchmark section's own instrument: a spot read on a crypto
111
+ coin no spot venue lists is refused as one on a TradFi coin always was.
112
+ - **A coin the market-data service publishes no profile for refuses every condition that reads
113
+ market data**: a coin enabled since the service's last boot, a roster coin whose candle stream it
114
+ did not subscribe, a ticker with no catalog row.
115
+ - **The tools, codes and `details.context` are v73's**: `preview_radar_resolution` (every kind,
116
+ issuing no `previewToken`); `commit_radar_deployment_draft`, `rebind_intelligence_agent`,
117
+ `compile_strategy_plan` and `restore_strategy` (`VALIDATION_ERROR` with
118
+ `CONDITION_UNREADABLE_BY_RADAR_SCAN`); `apply_strategy_plan`, which applies only what its compile
119
+ checked; and `propose_entry_decision` (`CONDITION_UNREADABLE_ON_COIN`).
120
+ - **A body accepted at v74.1.1 can be refused at v75 without one byte of it changing.** A write whose
121
+ read of the published profiles fails is refused, never admitted unchecked.
122
+
123
+ ### Reclassified reason — `FEED` becomes `INSTRUMENT`
124
+
125
+ - **A spot read for a ticker with no catalog row is `INSTRUMENT`, where it was `FEED`**, and an
126
+ uncatalogued benchmark refuses every market-data read, where only its spot reads refused before.
127
+ - **The `INSTRUMENT` message names what the profile lacks**: "`<instrument>`'s market-data profile
128
+ carries no `<kind>` data", or "the market-data service publishes no data profile for
129
+ `<instrument>`, so it carries no market data", where it read "`<instrument>` has no spot tape".
130
+ - **The readings v74 added report it as the sweep does**: `preview_radar_resolution`'s `readings`
131
+ and `get_agent_coin_qualification`'s `reading` mark a condition on a kind the coin's profile lacks
132
+ `INSTRUMENT`.
133
+
134
+ ### Wider output — `BarFamily` gains `MARKET_STATS`
135
+
136
+ - **The market-stats snapshot is a per-bar value**: a decision reads the snapshot fetched inside the
137
+ `1m` bar ending at its close. A snapshot missing there is a per-bar shortfall, a `families` entry or
138
+ a MISSED close's `INPUTS_OFF_BAR` `lacking` entry naming `MARKET_STATS` at `1m` with its
139
+ `dueBarStart`, `ABSENT` or `GONE`, and the decision waits or misses as for any per-bar family.
140
+ - **A decided close records no `MARKET_STATS` stale sample.** A reading at the latest values still
141
+ does, and its `boundMs` is the coin's market-stats freshness target, never below 300,000 ms.
142
+ - **Five outputs publish the member**: `get_agent_coin_qualification`, `get_radar_close_decisions`,
143
+ `get_signal_log`, `list_gate_blocks` and `preview_radar_resolution`. No input takes the vocabulary.
144
+ A client that validates results against a cached output schema should list the tools again after
145
+ the server deploys.
146
+
147
+ ## Contract history — v74.1.1 (a same-agent request answered from what stopped the fire)
148
+
149
+ **Not breaking: one answer corrected behind unchanged schemas.**
150
+
151
+ - **A request on a pair your own radar policy decides with the same agent** is answered from that
152
+ policy's decision. When the policy's fire enqueued no trade decision and no admission gate refused
153
+ it — a coin cooldown, the hourly fire cap, post-close suppression, a decision already running on the
154
+ coin, a deduplicated fire, or a fire aborted at its re-read — the conversation gains the card the
155
+ request's own unfired fire gets: `close_claimed_by_radar` when another agent's fire holds the coin,
156
+ otherwise `close_refused` with the reading's score against its minimum. It gained
157
+ `close_fired_by_radar` before, though no trade was taken.
158
+ - **Both cards are already in `get_trade_conversation`'s vocabulary**, so no served shape moves.
159
+
160
+ ## Contract history — v74.1 (missed-bar alerts and the entry pause)
161
+
162
+ **Not breaking: four output vocabularies gain a member, and `propose_entry_decision` refuses while an
163
+ entry pause is open.** While bars missed for lack of market data span many coins, the platform pauses
164
+ every new entry the radar decides, a radar fire and a manual request alike, and the pause lifts by
165
+ itself after a quiet period.
166
+
167
+ ### Refused while a pause is open — `propose_entry_decision`
168
+
169
+ - **A request is refused after its own checks and before a watch is registered**, whatever the agent
170
+ or coin: `type: "error"` with `{ origin: "ENGINE", reasonCode: "ENTRIES_PAUSED" }`. The same
171
+ `idempotencyKey` replays the refusal for five minutes; ask again under a fresh key once the pause
172
+ lifts.
173
+ - **A request queued before the pause** whose close qualifies while it is open gets the same ENGINE
174
+ error card in its conversation, and no decision is proposed. `accept_entry_decision` on a decision
175
+ already proposed, and every exit, run as without a pause.
176
+
177
+ ### Wider output — four vocabularies
178
+
179
+ - **`TradeEvaluationAttemptReasonCode` gains `ENTRIES_PAUSED`**: the refusal's `reasonCode`,
180
+ `list_gate_blocks`' `reasonCode`, and the `scanBlockReason` of a refused fire's close record and
181
+ journal row.
182
+ - **`TradingPipelineGateStage` gains `PLATFORM`**: a `list_gate_blocks` entry for each fire the pause
183
+ refused, with no coin, no detail and no decision record.
184
+ - **`RadarFireDisposition` gains `EDGE_PRESERVED_ENTRIES_PAUSED`**: the refused fire keeps its edge,
185
+ so the pair fires at a later close only if that close qualifies on its own data. It is served on
186
+ `get_radar_activity` and `get_radar_activity_summary`, and as a close record's
187
+ `outcome.fireDisposition` beside `outcome.scanBlockReason: "ENTRIES_PAUSED"`, with
188
+ `outcome.outcome` staying `FIRED`.
189
+ - **`RadarIdleReason` gains `ENTRIES_PAUSED`**: the `resolvesNow.reason` of every `SCANNING` card on
190
+ `get_radar_deployment` and `list_radar_deployments` while a pause is open. The section stays
191
+ `SCANNING`, because closes are still decided and recorded.
192
+
193
+ ### Answer corrected — a same-agent request
194
+
195
+ - **When an admission gate refused your own policy's fire** — an entry pause, an account block, a
196
+ position already held on the coin — the request's conversation gains that block's ENGINE error card
197
+ instead of `close_fired_by_radar`.
198
+
199
+ ## Contract history — v74 (preview `conditionReach` replaced by `readings`)
200
+
201
+ **Breaking at v74: one required output field replaced on one tool, and an opt-in reading added to
202
+ another.** Both now serve what an agent reads on a coin, in the decision-record format v73.3
203
+ introduced.
204
+
205
+ ### Reshaped output — `preview_radar_resolution`
206
+
207
+ - **`conditionReach` is gone, replaced by `readings`**: one entry per on-duty agent, in on-duty order,
208
+ each naming `agentId` and `agentDisplayName`. A `kind: "READ"` entry carries the agent's `reading`,
209
+ the one `get_agent_coin_qualification` serves (below). A `kind: "UNSCORABLE"` entry, an agent whose
210
+ evaluation was rejected, carries `coinDataStopped` instead: the coin's stopped-data reading, or
211
+ null. One agent's rejection is its own entry, never a failed preview. The tool always asks for
212
+ readings, so `readings` is null only under a `simulatedRegime`.
213
+ - **`blocksScanGate` has no successor.** Since v73 the preview refuses a slot agent with a condition
214
+ the radar scan cannot read on the coin, so no previewed pair holds a permanently blocking
215
+ condition. Whether the conditions hold the coin is the reading's own gate,
216
+ `reading.live.reading.qualification.gates.requiredConditions`.
217
+ - **Read `reachReason` from the reading.** Each `READ` entry's
218
+ `reading.live.reading.conditions.entries[]` lists the conditions: a `kind: "UNEVALUATED"` entry
219
+ carries the condition's `conditionKey`, `name` and `reachReason`, and a `kind: "EVALUATED"` entry,
220
+ the counterpart of a null `reachReason`, carries its outcome with its clause values.
221
+ `declaration.required` marks the required ones, the set `conditionReach` covered.
222
+ - **A client that validates results against a cached output schema** fails the call until it lists
223
+ the tools again, because the old schema required `conditionReach`. Reconnect, or re-list, after the
224
+ server deploys.
225
+
226
+ ### Wider input and output — `get_agent_coin_qualification`
227
+
228
+ - **`reading` (boolean, default false) asks for the pair's reading**, and each verdict gains the
229
+ required-nullable `reading`, null unless asked. A call without the flag is answered as before, with
230
+ `reading: null`.
231
+ - **`reading.live`** is the reading the verdict was built from, with `anchorBarStatus` (`live` while
232
+ the anchor bar still forms), `scoredBarStart` (the bar the signals and gates were scored on) and
233
+ `observedAt`. It is what the agent reads now, not a decision: the radar decides at the close.
234
+ - **`reading.lastClose`** is the deciding reading of the bar the agent's on-duty row names as its
235
+ last close decision: `state: "RECORDED"` with that bar's close-decision `record`,
236
+ `state: "PENDING"` with its `barStart` while that record is being written, or `state: "NONE"`.
237
+ `get_radar_close_decisions` holds the decisions before it.
238
+
239
+ ## Contract history — v73.3 (decision records)
240
+
241
+ Purely additive: **one read tool, one evaluation-attempt reason and four output fields added.** What
242
+ a decision read is now served: the radar's close decisions on a coin, the report behind a trade, and
243
+ the compose decision behind a gate block.
244
+
245
+ ### Wider surface — one tool added
246
+
247
+ - **`get_radar_close_decisions`** (`coinId`; optional `agentId`, `before`, `limit`; `mcp:read`) — one
248
+ coin's radar close-decision records, newest decided bar first: every bar an agent decided —
249
+ `FIRED`, `CLAIMED`, `NOT_QUALIFIED` or `MISSED` — with the reading it was decided on (a miss's
250
+ answer instead), its fire disposition and its versions. Your own records only: a SYSTEM agent's
251
+ records on a coin belong to every user who deployed it. A page never splits a bar, so it can run
252
+ past `limit`; to page back, pass the oldest record's `bar.barStart` as `before`. `toolCount` goes
253
+ 141 → 142.
254
+
255
+ ### Wider output — the risk budget, Radar reads, the trade conversation, gate blocks and signal logs
256
+
257
+ - **`TradeEvaluationAttemptReasonCode` may read `SCREENED_OUT`** on `get_agent_budget`,
258
+ `reset_agent_drawdown_baseline`, `get_radar_activity`, `get_radar_deployment`,
259
+ `list_radar_deployments`, `preview_radar_resolution`, `get_trade_conversation`,
260
+ `propose_entry_decision`, `list_gate_blocks`, `get_signal_log` and
261
+ `get_public_agent_signal_log_detail`: compose's deterministic pre-model screen found no routable
262
+ direction. That refusal now writes a gate block. It is public: the public profile carries it as is.
263
+ A client holding its own closed copy of the enum rejects the new member; one that renders unknown
264
+ reasons generically is unaffected.
265
+ - **`get_signal_log`'s `log` gains `reportSections`** (the report the model read, section by section,
266
+ in the shape of `preview_strategy_report`'s `renderedSections`), **`promptDataSerialization`** (the
267
+ serialization it was rendered under) and **`missingData`** (the data compose lacked), each null for
268
+ a trade recorded before capture. `get_public_agent_signal_log_detail` carries none of them.
269
+ - **Each `list_gate_blocks` entry gains `decisionRecord`**: the compose decision record written with
270
+ the block — its outcome, its evidence (the reading compose computed, the per-bar values a refused
271
+ decided bar lacked, or none), its missing data and its versions. It is null for a block written
272
+ before capture and for a block raised before compose.
273
+
274
+ ## Contract history — v73.2 (decided-bar refusal)
275
+
276
+ **One member added to the evaluation-attempt reason**, published as output only, and two detail
277
+ fields added to gate blocks. A confirming-close fire now names the bar it decided, and compose reads
278
+ that bar or refuses with the new reason, instead of reading the newest completed bar at its own
279
+ instant.
280
+
281
+ ### Wider output — the risk budget, Radar reads, the trade conversation, gate blocks and signal logs
282
+
283
+ - **`TradeEvaluationAttemptReasonCode` may read `DECIDED_BAR_UNAVAILABLE`** on `get_agent_budget`,
284
+ `reset_agent_drawdown_baseline`, `get_radar_activity`, `get_radar_deployment`,
285
+ `list_radar_deployments`, `preview_radar_resolution`, `get_trade_conversation`,
286
+ `propose_entry_decision`, `list_gate_blocks`, `get_signal_log` and
287
+ `get_public_agent_signal_log_detail`: compose refused a radar fire at the conditions stage, before
288
+ any LLM call, because a per-bar value its conditions gate reads was not on its due bar for the bar
289
+ the radar decided — its entry had not landed, or had gone. It is public: the public profile carries
290
+ it as is. A client holding its own closed copy of the enum rejects the new member; one that renders
291
+ unknown reasons generically is unaffected.
292
+ - **`list_gate_blocks`' `reasonDetail` gains `decidedBarStart`** (ISO 8601, the decided bar's open)
293
+ **and `decidedBarTimeframe`** (the strategy timeframe), present on that reason alone.
294
+
295
+ ## Contract history — v73.1 (strategy card fields)
296
+
297
+ Purely additive: **thirteen read-only fields added to every `list_strategies` row.** The input is
298
+ unchanged, and a client that ignores the fields is unaffected.
299
+
300
+ ### Wider output — `list_strategies`
301
+
302
+ - **The strategy card:** `regimeTimeframe` and `lowerTimeframe` (the derived ladder rungs
303
+ `get_strategy` already serves); `conditionTally`, `signalRuleTally` and `exitTally` (`armed` out of
304
+ `total`, the four exit mechanisms); and `minRequiredCount`, `entryTrigger`, `minRiskRewardRatio` and
305
+ `maxStopLossAtrMultiple`.
306
+ - **`forkedFromStrategyName`**, null when the strategy is not a fork or its source is neither yours
307
+ nor a SYSTEM strategy.
308
+ - **`viewerBoundAgentCount` and `viewerOpenPositionCount`**, your own agents on the strategy and their
309
+ open positions. `boundAgentCount` still counts every user's agents; read the viewer fields for
310
+ yours.
311
+ - **`viewerPerformance`**, your own all-time results on the strategy, null when you never traded it.
312
+ A trade counts toward the strategy recorded on the signal log that opened it, so rebinding an agent
313
+ never moves it.
314
+
315
+ ## Contract history — v73 (unreadable radar conditions refused)
316
+
317
+ **Breaking at v73: seven tools refuse bodies they used to accept, behind unchanged input schemas.**
318
+ A condition the radar acts on — a required one, a direction-setting one (a verdict carrier), an exit
319
+ one, and every condition they reference — must be readable by the radar scan on every coin the radar
320
+ acts on for its agent: each coin a Radar policy slots it on, enabled or paused; each coin it holds an
321
+ open position on, for its exit conditions; and each coin it has a pending manual request on.
322
+
323
+ ### Rejected input — refused behind unchanged schemas
324
+
325
+ - **`VALIDATION_ERROR` with the authoring code `CONDITION_UNREADABLE_BY_RADAR_SCAN`** from
326
+ `preview_radar_resolution` (every kind, for every slot agent whether on duty now or not, issuing no
327
+ `previewToken`), `commit_radar_deployment_draft`, `rebind_intelligence_agent`, and
328
+ `compile_strategy_plan` and `restore_strategy` on behalf of the bound agents the radar acts on (a
329
+ strategy none of whose agents holds a slot, an open position or a pending request is never
330
+ checked). `details.context.reachReason` says why: `INSTRUMENT` (the coin, or a benchmark section's
331
+ own instrument, has no such data), `AGENT_TIMEFRAME` (the agent has no such rung) or `FEED` (the
332
+ radar scan never reads that data: crowd reads, a spot read for an uncatalogued ticker, a timeframe
333
+ the agent's plan does not load).
334
+ - **The message names the first conflicting agent, the condition, the column, the coin and the
335
+ fixes**: stop the radar acting on the condition, make it read a column the scan has on that coin,
336
+ or free the coin — take the agent off that coin's policy, close the position, or cancel the pending
337
+ request. It is a fact about the pair, which no later sweep changes.
338
+ - **A refused compile issues no plan token**, so `stage_strategy_plan` cannot stage that content, and
339
+ **`apply_strategy_plan` applies only a plan whose bound agents still hold the radar slot coins its
340
+ compile checked**: a plan compiled before a deploy moved them fails verification. Compile again.
341
+ - **A condition reading a Session Field scalar** keeps `CONDITION_OPERAND_UNSERVED_IN_LANE`, now
342
+ refused at the rebind and every strategy revision too, not only at the deploy.
343
+ - `stage_radar_deployment_draft` is unchanged: a draft holds no slot, and the preview its commit
344
+ needs refuses. A body accepted under v72 is refused under v73 without one byte of it changing.
345
+
346
+ ### Wider output — `propose_entry_decision`, `get_trade_conversation`
347
+
348
+ - **`propose_entry_decision` refuses a request whose agent the radar scan cannot read on the
349
+ requested coin**, before its watch is registered, and releases its idempotency claim. It returns
350
+ `type: "error"` with `error: { reasonCode: "CONDITION_UNREADABLE_ON_COIN", unreadable: {
351
+ conditionKey, conditionName, column, coinTicker, reachReason } }`: facts only, no prose. Choose
352
+ another coin, another agent, or a condition reading a column the radar reads on that coin, and
353
+ send it again. `get_trade_conversation`'s error card carries the same `reasonCode` and
354
+ `unreadable`.
355
+ - **A client that validates results against a cached output schema** fails a call that returns the
356
+ new error until it lists the tools again, because the old schema has no such member. Reconnect, or
357
+ re-list, after the server deploys.
358
+
359
+ ### Refusals worth knowing before you apply, restore or rebind
360
+
361
+ - **`CONFLICT` with `details.reason: "RADAR_DEPLOYMENT_MOVED"`** from `apply_strategy_plan`,
362
+ `restore_strategy` and `rebind_intelligence_agent`: the agents' radar slot coins moved after the
363
+ write's check. Re-read and retry, so the retry's check covers the moved deployment; for a plan,
364
+ compile again.
365
+ - **`commit_radar_deployment_draft` is a `CONFLICT`** when a slot agent's strategy or timeframes
366
+ changed after its check. Preview again.
367
+
368
+ ## Contract history — v72.0.1 (account read faults withheld on the public profile)
369
+
370
+ **No schema change: one value reclassified on one public read.**
371
+
372
+ ### Reclassified output — `get_public_agent_signal_log_detail`
373
+
374
+ - **`pipeline.attempt.reasonCodes` carries `OWNER_PRIVATE`, with its detail omitted, where it would
375
+ have carried `EQUITY_CHECK_UNAVAILABLE`, `ALLOCATION_CHECK_UNAVAILABLE`,
376
+ `DAILY_COUNT_CHECK_UNAVAILABLE` or `APPROVAL_CHECK_UNAVAILABLE`.** Each says the account has an open
377
+ block, which the public profile withholds. This supersedes the v71.1 note below that the public
378
+ detail carries the two faults v71.1 added. `OWNER_PRIVATE` has been in that field's schema since
379
+ v71, so a client validating against a cached output schema accepts it. `get_signal_log` still
380
+ carries every precise code on your own agents.
381
+
382
+ ## Contract history — v72 (public decision `userId` removed)
383
+
384
+ **Breaking at v72: one output field removed from one public read.**
385
+
386
+ ### Removed output — `get_public_agent_signal_log_detail`
387
+
388
+ - **`log.linkedEntryDecision.userId` is gone.** It was the user the agent acted for. On a SYSTEM
389
+ agent, which has no author and which any user may deploy, that named whoever deployed it, and the
390
+ public profile deliberately shows no owner for a SYSTEM agent. There is **no replacement path** on
391
+ the public read. A PRIVATE agent's author is `ownerUserId` on the agent's public profile, and the
392
+ owner tool `get_signal_log` still carries `linkedEntryDecision.userId` on your own agents.
393
+ - **A client that validates results against a cached output schema** fails the call until it lists
394
+ the tools again, because the old schema required the field. Reconnect, or re-list, after the server
395
+ deploys.
396
+
397
+ ## Contract history — v71.1 (two read-fault refusals)
398
+
399
+ **Two members added to the evaluation-attempt reason**, published as output only. When the server
400
+ cannot read an agent's daily trade count or its trading account's approval, the admission is blocked
401
+ with its own reason instead of borrowing the verdict it could not reach.
402
+
403
+ ### Wider output — the risk budget, Radar reads, the trade conversation, gate blocks and signal logs
404
+
405
+ - **`TradeEvaluationAttemptReasonCode` may read `DAILY_COUNT_CHECK_UNAVAILABLE` or
406
+ `APPROVAL_CHECK_UNAVAILABLE`** on `get_agent_budget`, `reset_agent_drawdown_baseline`,
407
+ `get_radar_activity`, `get_radar_deployment`, `list_radar_deployments`, `preview_radar_resolution`,
408
+ `get_trade_conversation`, `propose_entry_decision`, `list_gate_blocks`, `get_signal_log` and
409
+ `get_public_agent_signal_log_detail`. `DAILY_TRADE_LIMIT_REACHED` and `AGENT_APPROVAL_EXPIRED` still
410
+ mean what they say. A client holding its own closed copy of the enum rejects the new members; one
411
+ that renders unknown reasons generically is unaffected.
412
+
413
+ ## Contract history — v71 (owner-private reasons on the public profile)
414
+
415
+ **Breaking at v71: two public inputs narrow, and one public output widens.**
416
+
417
+ ### Rejected input — `get_public_agent_signal_logs`, `get_public_agent_realized_trades`
418
+
419
+ - **`filter.rejectionReason` refuses the six owner-private failure reasons** —
420
+ `BELOW_EXCHANGE_MINIMUM`, `INSUFFICIENT_BALANCE`, `INSUFFICIENT_MARGIN`,
421
+ `AGENT_APPROVAL_EXPIRED`, `SIGNING_KEY_UNAVAILABLE`, `AGENT_HALTED` — with a validation error,
422
+ where they used to filter. These tools read another user's agent, and a private reason answered a
423
+ question about that user's budget, margin, approval, key or halt. No alias exists; filter by a
424
+ public reason. `filter.expiryReason` still accepts every member.
425
+
426
+ ### Wider output — `get_public_agent_signal_log_detail`
427
+
428
+ - **`pipeline.attempt.reasonCodes`, `pipeline.execution.failureReason` and `.expiryReason` may read
429
+ `OWNER_PRIVATE`** in place of an owner-private reason, whose detail is then omitted. A client
430
+ holding its own closed copy of these enums rejects the new member; one that renders unknown
431
+ reasons generically is unaffected. A failed execution's `executionMessage` is now always null.
432
+ - **`get_signal_log` is unchanged**: on your own agents it still carries every precise code, its
433
+ detail and the narrative.
434
+
435
+ ## Contract history — v70.2 (allocation-check refusal)
436
+
437
+ **One member added to the evaluation-attempt reason**, published as output only. An admission the
438
+ server blocked because it could not read the account's allocation now carries
439
+ `ALLOCATION_CHECK_UNAVAILABLE`; `NO_AGENT_ALLOCATION` survives only on historical rows.
440
+
441
+ ### Wider output — the risk budget, Radar reads, the trade conversation and gate blocks
442
+
443
+ - **`TradeEvaluationAttemptReasonCode` may read `ALLOCATION_CHECK_UNAVAILABLE`** on
444
+ `get_agent_budget`, `reset_agent_drawdown_baseline`, `get_radar_activity`, `get_radar_deployment`,
445
+ `list_radar_deployments`, `preview_radar_resolution`, `get_trade_conversation`,
446
+ `propose_entry_decision` and `list_gate_blocks`. A client holding its own closed copy of the enum
447
+ rejects the new member; one that renders unknown reasons generically is unaffected.
448
+ - **The reason detail no longer declares `availableUsd` or `requiredUsd`** (`get_signal_log`,
449
+ `get_public_agent_signal_log_detail`). No response ever carried them.
450
+
451
+ ## Contract history — v70 (risk-gauge `configured` removed)
452
+
453
+ **Breaking at v70: one output field removed from every risk-budget gauge.**
454
+
455
+ ### Reshaped output — `get_agent_budget`, `reset_agent_drawdown_baseline`
456
+
457
+ - **`budget.gauges.<gauge>.configured` is gone** from all four gauges (`dailyTrades`, `exposure`,
458
+ `drawdown`, `dailyLoss`). It could only read `true`: the daily-trades cap is at least 1, and an
459
+ agent's exposure ceiling, drawdown stop and daily-loss stop are all required and strictly positive.
460
+ Every gauge has a limit, so read `fill`, `remaining` and `breached` and render every meter enabled.
461
+ - **A client that validates results against a cached output schema** fails the call until it lists
462
+ the tools again, because the old schema required the field. Reconnect, or re-list, after the server
463
+ deploys.
464
+
465
+ ## Contract history — v69.7 (insufficient-margin refusal)
466
+
467
+ Purely additive: **one member added to the trade-failure reason**, in both directions. An agent entry
468
+ the shared trading account cannot post the isolated margin for is now refused before any order is
469
+ sent, with its own reason. The same reason replaces `EXCHANGE_ERROR` when the exchange refuses an
470
+ order for margin first.
471
+
472
+ ### Wider input — `get_public_agent_signal_logs`, `get_public_agent_realized_trades`
473
+
474
+ - **`filter.rejectionReason` accepts `INSUFFICIENT_MARGIN`**, where it used to refuse it.
475
+
476
+ ### Wider output — `get_signal_log`, `get_public_agent_signal_log_detail`
477
+
478
+ - **`failureReason` may read `INSUFFICIENT_MARGIN`.** A client that switches exhaustively on the
479
+ reason needs one more arm; one that renders unknown reasons generically is unaffected.
480
+
481
+ ## Contract history — v69.5 (liquidation-buffer refusal)
482
+
483
+ Purely additive: **one member added to the trade-failure reason**, in both directions. An agent entry
484
+ whose hard stop no whole leverage can keep clear of liquidation is now refused before any order is
485
+ sent, with its own reason. Below that, an agent order's reported effective leverage may sit under the
486
+ agent's bound: it is lowered until the isolated liquidation is at least twice as far from the fill as
487
+ the hard stop.
488
+
489
+ ### Wider input — `get_public_agent_signal_logs`, `get_public_agent_realized_trades`
490
+
491
+ - **`filter.rejectionReason` accepts `LIQUIDATION_BUFFER_UNREACHABLE`**, where it used to refuse it.
492
+
493
+ ### Wider output — `get_signal_log`, `get_public_agent_signal_log_detail`
494
+
495
+ - **`failureReason` may read `LIQUIDATION_BUFFER_UNREACHABLE`.** A client that switches exhaustively on
496
+ the reason needs one more arm; one that renders unknown reasons generically is unaffected.
497
+
498
+ ## Contract history — v69 (Radar multi-agent duty)
499
+
500
+ **Breaking: one input removed from two tools, three outputs reshaped.** A Radar policy carries no
501
+ timeframe. Every rule whose conditions match puts its agent on duty at once, and each rule's regime
502
+ condition is read at that agent's own regime timeframe. A coin still holds one position at a time: at
503
+ most one fire per coin per pass, offered in rule priority.
504
+
505
+ ### Rejected input — something you send is no longer accepted
506
+
507
+ - **`preview_radar_resolution`'s `SLOTS` request refuses `deploymentTimeframe`** as an unrecognized key.
508
+ Send `{ kind: "SLOTS", slots }`.
509
+ - **`stage_radar_deployment_draft` refuses the `DEPLOYMENT_TIMEFRAME` axis** the same way. Stage only
510
+ `RULES` and `DEFAULT_SLOT`.
511
+
512
+ ### Reshaped output — something you read has a new shape
513
+
514
+ - **`resolvesNow`** on `get_radar_deployment`, `list_radar_deployments` and `preview_radar_resolution` is
515
+ the coin's state (`section`, `isIdle`, `rotating`, one `reason`, the open-position owner) plus
516
+ **`onDuty`**: one row per agent on duty, in priority order. Each row carries its slot, its regime reading
517
+ at its own regime timeframe, its qualification verdict and gate, cooldown, block, `edgeSpent`, last flip
518
+ and fire, and its own `closeDecision`. The single-winner fields (`onDutyAgentId` and its display pair,
519
+ `matchedSlot*`, `regimeUsed`, and the per-agent fields the rows now carry) are gone. Render every row in
520
+ the order served, and never pick one yourself.
521
+ - **`preview_radar_resolution`'s `conditionReach`** is one flat list, each entry naming its `agentId` and
522
+ `agentDisplayName`.
523
+ - **Each deployment slot gains `agentRegimeTimeframe`**, the timeframe its rule's regime is read at.
524
+
525
+ ### Removed output — a field you read is gone
526
+
527
+ - **`deploymentTimeframe`** on a Radar policy, and the **`DEPLOYMENT_TIMEFRAME`** axis in the radar
528
+ draft's `content` and `axesMeta` on `get_radar_deployment_draft`, `list_radar_deployment_drafts` and the
529
+ stage result.
530
+
531
+ ### Wider enums — a value you may now receive
532
+
533
+ - **`closeDecision.outcome` gains `CLAIMED`**: the agent's close qualified, but a higher-priority agent
534
+ took the coin's one fire that pass. Its edge is preserved, and it fires at a later close only if it
535
+ still qualifies there.
536
+ - **`get_radar_activity` and `get_radar_activity_summary`** gain the events `ON_DUTY_JOINED` and
537
+ `ON_DUTY_LEFT` (duty is journaled per agent) and the fire disposition `EDGE_PRESERVED_COIN_CLAIMED`.
538
+ `curveDigest` describes the first agent on duty, named in `curveAgentName`.
539
+ - **`get_trade_conversation`** may carry the `close_claimed_by_radar` card.
540
+
541
+ The exported `battlegrid-radar-deployment` skill describes the v69 flow.
542
+
27
543
  ## Contract history — v68.1 (Arena regime sets)
28
544
 
29
545
  Purely additive in schema, with **one refusal behind unchanged ones.** An Arena rule's regime condition
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.50";
52
+ export declare const PACKAGE_VERSION = "31.2.59";
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.50';
55
+ export const PACKAGE_VERSION = '31.2.59';
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.50",
3
+ "version": "31.2.59",
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": "68.1.0",
3
+ "contractVersion": "76.0.0",
4
4
  "files": {
5
- "battlegrid-agent-management/SKILL.md": "fbc602e9445d0f72353bfba23c30a364b478bd04a9cc6248418cfeec66717eb1",
5
+ "battlegrid-agent-management/SKILL.md": "5ea8384ff76ccb9fdf00e4ec8b584f88fdba6d1c9d5679ce0b7380c23bfbaab1",
6
6
  "battlegrid-arena-play/SKILL.md": "4cfaa92a8d0dcaf477b227abd774e7aa8642c185d72108dfd401be0cf8aecd7f",
7
7
  "battlegrid-market-analysis/SKILL.md": "22fde9c4eac0261c89a8056ab8b20b66fcbe1fd58447fa0ac84fd410b4d134d7",
8
- "battlegrid-radar-deployment/SKILL.md": "87601043eb3920faa153061e4ea93f4d8618dbdb8fe8c3be52b4e47ddba18d83",
8
+ "battlegrid-radar-deployment/SKILL.md": "17301fa66f4b128154435462ffa967efcb3d016df671b03c485d0f8b840fc891",
9
9
  "battlegrid-strategy-authoring/SKILL.md": "95e91dd1ff36922b7ed0f5eb92c98def98fa39cce37c3d7447269aea1b5a017f",
10
- "battlegrid-strategy-doctor/SKILL.md": "d0b9e9e07648246d5a23b0c17183278b70b5bcbb5f472ac7da13d03e42a6c6c6",
11
- "battlegrid-strategy-examples/SKILL.md": "17e8cf8040813622bd5b82165c7af82e03287fee1f6aac974e0b6c584580dd27",
12
- "battlegrid-strategy-examples/references/playbooks.md": "35c543c090d00ba19e4bcf0a80502afac7a4954643fc535de0b24381c91ecbf4",
10
+ "battlegrid-strategy-doctor/SKILL.md": "a3446f24059be72ae797ba4e9669848d851ac3dc38675923fe3d307f557eb82f",
11
+ "battlegrid-strategy-examples/SKILL.md": "a60b766de81f859f2fa9b352d65937b6a6f3eb24c4e88a064123a4985906bce5",
12
+ "battlegrid-strategy-examples/references/playbooks.md": "02168e3af35268a30fd156f3337d25fe15e6df67da1592f4cb4d31090be3f94f",
13
13
  "battlegrid-strategy-examples/references/recipes.md": "53887daece17995543e7d3fe1bf2577fc41a6254c291acacde48d7de63853641",
14
14
  "battlegrid-strategy-examples/references/tradingview-ports.md": "2b13adbc137178955edcca31b6ab40c01c745a932a0750b91db8820c736700bb",
15
15
  "battlegrid-trade-analysis/SKILL.md": "f49482702f17d7fc44db00c31a19fc6e9350668fe76e8948d22faaa9c7e9d6bf",
16
- "battlegrid-trade-proposal/SKILL.md": "4cdb46a543cc126092874a4b385670ea2160de0384b7b5087c93b0aa72830b79"
16
+ "battlegrid-trade-proposal/SKILL.md": "9bf7b7648fb009db118a3a428b15abf38df5ba56a2ef4c8a4eae3d979bdc8bcb"
17
17
  }
18
18
  }
@@ -25,8 +25,8 @@ step named beside it before doing anything else.
25
25
  configuration change to an agent the player is mid-edit on is staged, not committed. → steps 3
26
26
  and 4.
27
27
  3. **Mis-lever'd halt recovery.** *Cue: "why is it stopped", "start it again", any halted agent.*
28
- There are three halt reasons and they do not share a lever. Offering a drawdown baseline reset
29
- to a daily-loss halt is offering something that cannot work. → step 5.
28
+ The halt reason says why the agent stopped, not what clears it. Offering an exit the server's
29
+ resume verdict does not offer is offering something that cannot work. → step 5.
30
30
  4. **Driving strategy writes from this arc.** *Cue: the answer to an agent problem turns out to be
31
31
  "change the strategy".* You can SEE strategies here because you must name what you bind. That is
32
32
  not permission to author them. → the cross-skill rule below.
@@ -118,7 +118,16 @@ mid-edit in their form.** Then:
118
118
  - `discard_agent_draft` only on the player's word: call it with `confirm:false` first, tell them
119
119
  when the draft was last written and from which surface, ask, then confirm.
120
120
 
121
- With **no draft open**, the update verb below stands, confirm and all.
121
+ **In a conversation the agent console hosts** — your context names the agent console and an agent
122
+ id — the player's form is open on that agent whether or not a draft exists yet. Stage every change
123
+ into that agent's draft, naming that id as `agentId`, create or edit alike: the create recipe's
124
+ omitted id does not apply there, because the screen already holds the id the agent will be created
125
+ at. Create, update, rebind and discard are refused there — the player's own Create, Save or Discard
126
+ is the act. On an existing agent a strategy change is a recommendation the player picks in the
127
+ Strategy step. An axis their form rewrote after your proposal — typed over or undone — is their
128
+ answer: propose it again only on their word.
129
+
130
+ With **no draft open**, the update verb below stands, confirm and all — outside the agent console.
122
131
 
123
132
  ### 4. Lifecycle verbs: read first, state the radius, then confirm
124
133
 
@@ -152,23 +161,33 @@ the reason, and name what would clear it (un-deploy via the radar/deployment too
152
161
  the positions resolve, wait for the session to settle). Never paraphrase the refusal into "it
153
162
  didn't work", and never retry it unchanged.
154
163
 
155
- ### 5. Halt recovery: branch on the served halt reason
156
-
157
- `get_agent_budget` serves `haltReason`. There are exactly three, and the lever differs:
158
-
159
- - **MANUAL** — the player halted it. `resume_intelligence_agent` lifts it unconditionally.
160
- - **DRAWDOWN_BREACH** — cumulative realized loss reached the drawdown stop. Two levers: raise
161
- `maxCumulativeDrawdownUsd` through the update verb, **or** `reset_agent_drawdown_baseline`,
162
- which acknowledges the loss and re-arms the stop from today (it erases no history and journals
163
- the acknowledgement). Then resume.
164
- - **DAILY_LOSS** — realized loss for the day reached the daily limit. Two levers: raise
165
- `maxDailyLossUsd` through the update verb, **or** wait for the UTC-day rollover, which clears it
166
- automatically. **The baseline reset cannot clear a daily-loss halt — never offer it here.**
167
-
168
- **A resume attempted while the breach still holds is refused by the server**, with the current
169
- figure against the limit. Surface those served figures and the applicable lever from the list
170
- above. Do not retry the resume, and do not reach for the reset to "get past" a refusal that names
171
- a different stop.
164
+ ### 5. Halt recovery: offer exactly the served verdict's exits
165
+
166
+ `get_agent_budget` serves `haltReason` — why the agent stopped — and `haltResumeEligibility`, the
167
+ server's resume verdict. **The verdict decides the recovery**; the reason only explains the stop.
168
+
169
+ - **`haltResumeEligibility` is null** — the agent is not halted. Every halt carries a verdict, one
170
+ the player set by hand included, and is recovered through it.
171
+ - **`eligible: true`** — nothing is still breached, and a resume will succeed. Offer
172
+ `resume_intelligence_agent`.
173
+ - **`eligible: false`** — `breachedStop` names the stop still breached, with `breachingFigureUsd`
174
+ against `limitUsd`. Present that stop and exactly the exits the verdict marks open:
175
+ - `canRaiseTriggeringStop` — raise that stop above the figure through the update verb
176
+ (`maxCumulativeDrawdownUsd` for the drawdown stop, `maxDailyLossUsd` for the daily loss limit);
177
+ - `canResetBaseline` — `reset_agent_drawdown_baseline`, which acknowledges the loss and re-arms
178
+ the drawdown stop from today (it erases no history and journals the acknowledgement);
179
+ - a `breachedStop` of `DAILY_LOSS` also clears at the UTC-day rollover.
180
+
181
+ Then resume. An exit the verdict does not mark open is never offered.
182
+
183
+ After any resume, read the response's `haltedAt`. The server re-checks every stop once the halt is
184
+ cleared, so a set `haltedAt` means the agent was halted again at once: a loss settled, or a stop was
185
+ lowered, while the resume ran. Read `get_agent_budget` again and present its `haltReason` and
186
+ verdict; never report that agent as trading.
187
+
188
+ **A resume attempted while a stop is still breached is refused by the server**, naming the same
189
+ stop, figure and exits. Surface them as served. Do not retry the resume; after a raise or a reset,
190
+ read the verdict again rather than assuming it cleared.
172
191
 
173
192
  ### 6. Risk limits are a whole object
174
193
 
@@ -1,12 +1,14 @@
1
1
  ---
2
2
  name: battlegrid-radar-deployment
3
- description: Deploy the player's agents to standing duty — per-coin Radar policies that fire real trades on confirmed regime flips, and per-preset Arena deployment policies that enter sessions automatically. Reads what is deployed now, stages a change into the player's draft, previews what it would actually resolve to, commits it only after the player has seen that preview, and un-deploys with the blast radius stated. Activate whenever the player wants an agent put on duty, wants to change or pause a deployment, asks what would fire right now or why nothing is firing, or wants to stop a coin or a preset being traded automatically.
3
+ description: Deploy the player's agents to standing duty — per-coin Radar policies that put every matching rule's agent on duty and fire real trades at confirmed closes, and per-preset Arena deployment policies that enter sessions automatically. Reads what is deployed now, stages a change into the player's draft, previews what it would actually resolve to, commits it only after the player has seen that preview, and un-deploys with the blast radius stated. Activate whenever the player wants an agent put on duty, wants to change or pause a deployment, asks what would fire right now or why nothing is firing, or wants to stop a coin or a preset being traded automatically.
4
4
  ---
5
5
 
6
6
  # Radar & Deployment
7
7
 
8
- You are handing an agent **standing authority**. A Radar policy fires real trades on confirmed
9
- regime flips with no further human action; a deployment policy enters real sessions the same way.
8
+ You are handing agents **standing authority**. A Radar policy puts EVERY rule whose conditions match
9
+ on duty at once — each rule's regime read at its own agent's regime timeframe — and fires real trades
10
+ at their confirmed closes with no further human action, at most one per coin per pass, offered in rule
11
+ priority; a deployment policy enters real sessions the same way.
10
12
  Nobody is watching when it happens — which is why the player has to see what a policy resolves to
11
13
  *before* it exists, not after.
12
14
 
@@ -18,7 +20,7 @@ and never describe them as one thing with two modes.
18
20
 
19
21
  1. **A write with no fresh preview.** *Cue: any `commit_radar_deployment_draft`,
20
22
  `resume_radar_deployment`, `commit_deployment_policy_draft` or `resume_deployment_policy`.* The preview is the only place the
21
- player sees which agent actually goes on duty and why. → steps 2–3.
23
+ player sees which agents actually go on duty and why. → steps 2–3.
22
24
  2. **A blind retry.** *Cue: a CONFLICT on a write.* Something moved under you — the stored policy,
23
25
  or the player's draft. Retrying overwrites an edit you never read. → step 3.
24
26
  3. **Previewing one thing and writing another.** *Cue: the player adjusts a slot, a bar, a window
@@ -49,7 +51,10 @@ Then read what exists:
49
51
 
50
52
  - `get_radar_deployment` (one coin) or `list_radar_deployments` (the fleet, plus the platform
51
53
  `radarPaused` kill-switch — if that is set, say so up front: nothing will fire whatever you
52
- write).
54
+ write). A `SCANNING` card whose reason is `ENTRIES_PAUSED` is the platform's **entry pause**: new
55
+ entries are paused platform-wide while market data is missing across many coins. Say that up front
56
+ too — the radar takes no new entry on any coin until it lifts, which it does by itself once the
57
+ data is back, and nothing the player changes on a deployment clears it.
53
58
  - **For Radar, also read the player's draft of the coin** with `get_radar_deployment_draft` — or
54
59
  `list_radar_deployment_drafts` when no coin is named yet. The player may be part-way through a
55
60
  change in their radar builder. **If a draft exists, say so** — what it holds, and when and from
@@ -69,16 +74,19 @@ If the question is *why did my radar agent not fire*, do not start from the jour
69
74
  **Radar** — the change goes into the player's draft of the coin, never straight to the policy:
70
75
 
71
76
  1. `stage_radar_deployment_draft` with `draftVersion` = the `version` you read (0 when there was
72
- no draft) and only the axes you are changing, each WHOLE: `DEPLOYMENT_TIMEFRAME`, `RULES` (the
73
- complete ordered rule list — first is highest priority) and `DEFAULT_SLOT` (the catch-all, or
74
- null for none). Staging writes nothing live and shows the change in the player's builder as a
75
- proposal. A refusal naming axes means the player changed them after your read: read again and
77
+ no draft) and only the axes you are changing, each WHOLE: `RULES` (the complete ordered rule list
78
+ — first is highest priority; every rule that matches is on duty, so priority decides only which
79
+ agent is offered the coin's one fire on a pass) and `DEFAULT_SLOT` (the catch-all, on duty only
80
+ when no rule is, or null for none). A Radar policy has **no timeframe**: each rule's regime
81
+ condition is read at its agent's own regime timeframe, which every slot reports as
82
+ `agentRegimeTimeframe`. Staging writes nothing live and shows the change in the player's builder as
83
+ a proposal. A refusal naming axes means the player changed them after your read: read again and
76
84
  propose against what they now have.
77
85
  2. `preview_radar_resolution` with `request: { kind: "DRAFT", draftVersion }` at the version staging
78
86
  returned. It composes the draft over the deployed policy **exactly as the commit will write it**.
79
87
 
80
- To try a slot set without touching the draft, preview `{ kind: "SLOTS", deploymentTimeframe, slots }` —
81
- it certifies nothing and cannot be committed.
88
+ To try a slot set without touching the draft, preview `{ kind: "SLOTS", slots }` — it certifies
89
+ nothing and cannot be committed.
82
90
 
83
91
  **Arena** — the same shape, keyed on `presetId`:
84
92
 
@@ -96,19 +104,68 @@ Both previews run the **same resolver the live sweep runs**, so the preview is t
96
104
  an estimate. Neither writes anything and neither costs the player an LLM call — so previewing
97
105
  repeatedly while iterating is free and correct.
98
106
 
99
- Render the preview as a card and read it out: which agent goes on duty, which slot matched and at
100
- what priority, the regime and conviction used, the qualification verdict, and any typed idle or
101
- blocked reason. **Render `section` as the server sends it — never re-derive it.** A non-null reason
102
- does not mean idle (`ON_DUTY_BUT_POSITION_BLOCKED` carries a reason and is not idle), so deciding
103
- the headline yourself gets it wrong. `enabledAfterCommit` says whether the policy will play once
104
- committed — a paused policy stays paused, and a first Arena deployment always plays.
107
+ Render the preview as a card and read it out. For **Radar**: the coin's `section` and any typed idle
108
+ or blocked reason, then every row of `resolvesNow.onDuty` in the order served — each agent, the slot
109
+ and priority that put it on duty, its regime reading at its own `regimeTimeframe` with conviction, its
110
+ qualification verdict and its close clock — and `readings`, one entry per on-duty agent in the same
111
+ order: what that agent reads on the coin right now (below). For **Arena**: which agent goes on duty,
112
+ 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
114
+ rows yourself.** A non-null reason does not mean idle (`ON_DUTY_BUT_POSITION_BLOCKED` carries a reason
115
+ and is not idle), so deciding the headline yourself gets it wrong. `enabledAfterCommit` says whether
116
+ the policy will play once committed — a paused policy stays paused, and a first Arena deployment
117
+ always plays. A Radar preview simulates a running platform, so it never reports the kill switch or an
118
+ entry pause: those come from step 1's read.
105
119
 
106
120
  **If anything changes after a preview, re-preview.** A preview never vouches for what it did not
107
121
  resolve.
108
122
 
123
+ **A Radar preview refuses an agent the scan cannot read on the coin.** Every slot agent, on duty now
124
+ or not, must have every condition the radar acts on — its required conditions, the ones carrying a
125
+ verdict, its exit conditions, 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
128
+ (`INSTRUMENT` — the market-data profile of the coin, or of a benchmark section's own instrument, carries
129
+ no such data, or the market-data service publishes no profile for it at all: the refusal names the kind
130
+ of data the profile lacks, or says there is none; `AGENT_TIMEFRAME` — the agent has no such rung;
131
+ `FEED` — the radar scan never reads that data for this agent), or
132
+ `CONDITION_OPERAND_UNSERVED_IN_LANE` for a session-field scalar. Read the player the refusal as served:
133
+ it names the agent, the condition, the column and the fixes — stop the radar acting on the condition,
134
+ make it read a column the radar scan has on that coin, or take the agent off the coin. A commit
135
+ re-checks the same rule, so a strategy edit landing after the preview can still refuse it.
136
+
137
+ **`readings` is each on-duty agent's requirements, read now.** A `READ` entry carries the agent's
138
+ `reading` — the same one `get_agent_coin_qualification` returns with `reading: true`, through the same
139
+ resolver:
140
+
141
+ - `reading.live` is the reading its qualification verdict was built from: the gates with their margins
142
+ (`qualification`); every entry-lane condition — required, direction-setting, and the ones they
143
+ reference — with its outcome and its clause values against their thresholds
144
+ (`conditions.entries[]`); the allocated signals with their attribution; and the data it lacked
145
+ (`missingData`). An `UNEVALUATED` entry is a condition this tick could not read, with its
146
+ `reachReason`. A pair the scan can never read was refused above, so every such cause is one the next
147
+ sweep can change — report it as the current reading, never as a reason the deployment will not
148
+ work. `anchorBarStatus` says which bar the conditions read (`live` while it is still forming), and
149
+ `scoredBarStart` names the bar the signals and gates were scored on. **A live reading decides
150
+ nothing**: the radar decides at the close, on the closed bar, so say what it reads right now, never
151
+ that it will fire.
152
+ - `reading.lastClose` is the one decision the agent's on-duty row names as its last close: `RECORDED`
153
+ with that bar's close-decision record — the reading it was decided on, or why it was missed —
154
+ `PENDING` while that record is still being written, or `NONE` when the pair remembers no decision.
155
+ For the decisions before it, call `get_radar_close_decisions` (step 5).
156
+
157
+ An `UNSCORABLE` entry is an on-duty agent the preview could not read on the coin, with
158
+ `coinDataStopped` when the coin's data explains why; it never fails the preview. `readings` is null
159
+ under a simulated regime: duty is simulated there, and every reading would judge the real one.
160
+
161
+ There is no `conditionReach` list and no `blocksScanGate` flag (contract 74.0.0). A condition's reach
162
+ reason is its `UNEVALUATED` entry's `reachReason` in
163
+ `readings[].reading.live.reading.conditions.entries[]`, and whether the conditions hold the coin is the
164
+ reading's own conditions gate, `qualification.gates.requiredConditions` — never a verdict of your own.
165
+
109
166
  ### 3. Confirm against the preview, then commit
110
167
 
111
- One confirmation whose question **references what the preview showed** — the agent it put on duty and
168
+ One confirmation whose question **references what the preview showed** — the agents it put on duty and
112
169
  the reason — and **names what the change does to the stored policy**, read from step 1:
113
170
 
114
171
  - every rule or slot the change **removes** (by agent and priority), because `RULES` and `slots` are
@@ -133,7 +190,7 @@ with the old one is refused again — only a fresh preview earns a new one.
133
190
 
134
191
  **Pause a coin's Radar** → `pause_radar_deployment({ coinId })`. No preview and no revision: stopping
135
192
  never waits on a read. The player's draft is kept. **Resume** → preview the deployed policy with
136
- `request: { kind: "COMMITTED" }`, tell the player what will go on duty again, and on their word
193
+ `request: { kind: "COMMITTED" }`, tell the player which agents will go on duty again, and on their word
137
194
  `resume_radar_deployment({ coinId, previewToken, confirm: true })` with the token that preview
138
195
  returned.
139
196
 
@@ -160,25 +217,41 @@ draft** of the coin or the arena. There is no preview here, and correctly so: th
160
217
  to preview once the policy is gone. The evidence is **the deployment's own read**, which supplies the
161
218
  `expectedRevision` the delete carries — and for Arena the `expectedPolicyId` beside it.
162
219
 
163
- State what stops, from that read: which agents were on duty or eligible, what the policy was
164
- firing on, and that the slots are not recoverable. Both tools carry a schema-level `confirm: true`.
220
+ 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`.
165
222
 
166
223
  **If the player wants to stop trading without losing the slots, that is not a delete** — it is
167
224
  `pause_radar_deployment` for Radar and `pause_deployment_policy` for Arena. Offer that
168
225
  whenever the ask sounds like "pause", "stop for now", or "take it off duty for a while".
169
226
 
170
- ### 5. "Why isn't it firing?" — state, then pattern, then rows
171
-
172
- Three reads, cheapest first. Stop as soon as the player's question is answered.
173
-
174
- 1. **`get_radar_deployment` → `resolvesNow`** for what is true **right now**: the section, the
175
- blocked reason and since when, the qualification block, the cooldown, the last fire, and which
176
- agent is on duty. Most "is it working?" questions end here.
227
+ ### 5. "Why isn't it firing?" — state, then pattern, then the bar, then rows
228
+
229
+ Four reads, cheapest first. Stop as soon as the player's question is answered.
230
+
231
+ 1. **`get_radar_deployment` → `resolvesNow`** for what is true **right now**: the coin's section and
232
+ reason, and one `onDuty` row per agent — its blocked reason and since when, its qualification
233
+ block, its cooldown, whether its edge is spent, its last fire and its close clock. A row whose last
234
+ close reads `CLAIMED` qualified but lost the coin's one fire that pass to a higher-priority agent;
235
+ its edge is preserved and it fires at a later close only if it still qualifies there. A `SCANNING`
236
+ coin whose reason is `ENTRIES_PAUSED` is under the entry pause (step 1): its agents still decide and
237
+ record every close, and a close that qualifies is held — its fire refused, its edge preserved,
238
+ journaled and recorded as `EDGE_PRESERVED_ENTRIES_PAUSED` with `scanBlockReason` `ENTRIES_PAUSED` —
239
+ so it fires at a later close only if that close qualifies on its own data after the pause lifts. A
240
+ lower-priority agent behind that fire reads `CLAIMED` on the same bar: the coin's one fire went to
241
+ the agent the pause refused, not to a trade. That is the whole answer while the pause lasts; never
242
+ send the player to change an agent or a policy for it. Most "is it working?" questions end here.
243
+ When the question is what ONE on-duty agent of a scanning coin needs to fire — which condition
244
+ reads false, how far a gate sits from its threshold — drill into its row
245
+ with `get_agent_coin_qualification({ agentId, coinTickers: [<the coin's ticker>], reading: true })`
246
+ and read the verdict's `reading` as step 2 of the deploy flow describes it: `live` is what the
247
+ agent reads now, and `lastClose` is the decision its row names as its last close, with the reading
248
+ that bar was decided on.
177
249
  2. **`get_radar_activity_summary`** for everything else about "why is it quiet", in ONE call. It
178
250
  carries three parts and they answer three different questions, each on its own scope:
179
251
  - `groups` — which cause recurs and how often, over the window the response names. Quote the
180
252
  counts with that window; never sum them across calls.
181
- - `curveDigest` — how FAR the score sits from firing, over the on-duty agent's ring. This is what
253
+ - `curveDigest` — how FAR the score sits from firing, over the FIRST on-duty agent's ring (the
254
+ lowest-numbered slot on duty now, named in `curveAgentName`). This is what
182
255
  separates "lower the minimum two points and it fires" from "this strategy does not fit this
183
256
  coin": `bestUnqualifiedScorePercent` against `latestThresholdPercent`. Its `ringStartAt` /
184
257
  `ringEndAt` describe the ring, NOT the cause window above.
@@ -186,13 +259,33 @@ Three reads, cheapest first. Stop as soon as the player's question is answered.
186
259
  a pair quiet for longer returns no groups beside populated older rows. That is correct, not a
187
260
  contradiction; each row carries its own `occurredAt`.
188
261
 
189
- For about one sweep after an agent rotation the digest can name the incoming agent while the rows
190
- still show the outgoing one — the two halves are read from different stores. Say "just rotated"
191
- rather than reporting a contradiction.
192
- 3. **`get_radar_activity`** only for what step 2 cannot do: more rows than its ten, a FIRES-only
193
- view, paging back through history, or ONE occurrence's full margins. Rows are lean by default —
194
- pass `detail: 'FULL'` for the margin surface, and `includeCurve: true` only if something will
195
- actually plot the points.
262
+ The groups and rows span every on-duty agent — each row names its own — while the digest describes
263
+ the first one. For about one sweep after the on-duty set changes the digest can name the new first
264
+ agent while the rows still show the old one — the two halves are read from different stores. Say
265
+ "duty just changed" rather than reporting a contradiction. Joins and departures are journaled as
266
+ `ON_DUTY_JOINED` / `ON_DUTY_LEFT`, one row per agent, and a lost coin as
267
+ `EDGE_PRESERVED_COIN_CLAIMED`.
268
+ 3. **`get_radar_close_decisions`** when the question is about ONE close — "why didn't the 14:00 bar
269
+ fire?", "why did it fire there?", "what did it see at that close?". Every bar the close step
270
+ decided has one record, whether a policy's agent or a manual request's watch decided it: its
271
+ `outcome` (`FIRED`, `NOT_QUALIFIED`, `CLAIMED` or `MISSED`), the fire's `fireDisposition` as
272
+ arbitration settled it, and the `evidence` the bar was decided on — the closed-bar reading's gates
273
+ with their margins, every condition's outcome with its clause evidence, each condition the scan
274
+ could not evaluate with its reach reason, the allocated signals and `missingData` — or, for a
275
+ `MISSED` bar, the close step's own answer: the bar never settled, an input was not on its due bar,
276
+ or the window passed. An `outcome` of `FIRED` says the bar qualified and its agent was offered the
277
+ coin's fire; `fireDisposition` says what the fire came to — `FIRED` when a decision was enqueued,
278
+ `EDGE_PRESERVED_ENTRIES_PAUSED` beside `scanBlockReason` `ENTRIES_PAUSED` when the entry pause
279
+ refused it and nothing was traded. It is the only history of not-qualifying closes: the journal
280
+ writes no row for one, and `resolvesNow` keeps only each agent's last decision — the one
281
+ `reading.lastClose` (step 1) carries; this read holds every decision before it. Narrow with
282
+ `agentId`; page back by passing the oldest returned `bar.barStart` as `before`. A bar still
283
+ waiting inside its window has no record yet (`resolvesNow` shows its `closeDecision.state` as
284
+ `DEFERRED`), and records are kept 90 days.
285
+ 4. **`get_radar_activity`** only for what steps 2 and 3 cannot do: more rows than its ten, a
286
+ FIRES-only view, paging back through history, or ONE occurrence's full margins. Rows are lean by
287
+ default — pass `detail: 'FULL'` for the margin surface, and `includeCurve: true` only if something
288
+ will actually plot the points.
196
289
 
197
290
  **Neither of the first two substitutes for the other, and the reason is structural.** The journal is
198
291
  a TRANSITION log, not a state log: a gate that has blocked continuously without crossing again
@@ -200,13 +293,18 @@ inside the window produces no group in the rollup at all. It shows up in `resolv
200
293
  else. So an empty or quiet rollup NEVER means "nothing is blocking it" — check step 1 before saying
201
294
  anything of the sort.
202
295
 
203
- Two negatives, both checkable:
296
+ Three negatives, all checkable:
204
297
 
205
298
  - **Never page journal rows in order to count causes yourself.** The counts are served. Re-deriving
206
299
  them spends the player's op budget on work the server already did and floods the transcript.
207
300
  - **Never sum counts across pages, and never report a windowed count as a lifetime one.** The
208
301
  rollup's counts span every matching row inside `windowStartAt`–`windowEndAt` — quote them with
209
302
  that window ("41 times in the last 7 days"), never as a total.
303
+ - **Never explain one close from the live verdict or the journal.** A not-qualifying close writes no
304
+ journal row, and the live reading is not the reading the close was decided on — at the same moment
305
+ the two can sit on opposite sides of a gate. Read the bar's record — `reading.lastClose` for the
306
+ last decision a row names, `get_radar_close_decisions` for any other — and quote it as stored;
307
+ never recompute it.
210
308
 
211
309
  ## Preview-before-commit: enforced for both
212
310
 
@@ -31,7 +31,10 @@ problem: the fix runs through the flow that owns it, with that flow's own confir
31
31
 
32
32
  Three reads, and they are not interchangeable:
33
33
 
34
- - **`get_agent_budget`** — the health read. `haltReason` (why it stopped, if it did),
34
+ - **`get_agent_budget`** — the health read. `runStatus` (`blocked` / `paused` / `active` — the run-state
35
+ already ranked, so read it rather than re-deriving it from the fields below; a halt reads `paused` even
36
+ while its own `AGENT_HALTED` block stands), `haltReason` (why it stopped, if it did),
37
+ `haltResumeEligibility` (whether a resume would now succeed, and what still blocks it),
35
38
  `blockedReason` + `blockedSince` (the typed pipeline block currently standing, and since when),
36
39
  and `gauges` — four guardrail meters (dailyTrades, exposure, drawdown, dailyLoss), each with a
37
40
  server-computed `breached` flag. **Read `breached`; never compare fill against limit yourself.**
@@ -85,16 +88,22 @@ a diagnosis; "OPEN_POSITION_CONFLICT, 14 times, most recently 2h ago" is.
85
88
 
86
89
  ### 3. Halted agents, and healthy ones
87
90
 
88
- **If `haltReason` is set, name the branch that actually clears it:**
89
-
90
- - **MANUAL** — resume lifts it directly.
91
- - **DRAWDOWN_BREACH** — clears by raising `maxCumulativeDrawdownUsd` **or** by the drawdown
92
- baseline reset, then resuming.
93
- - **DAILY_LOSS** — clears by raising `maxDailyLossUsd` **or** by the UTC-day rollover. **The
94
- baseline reset cannot clear it. Never offer it here.**
95
-
96
- In every case, say that a resume attempted while the stop is still breached is **refused by the
97
- server**, with the current figure against the limit.
91
+ **If the agent is halted, `haltReason` says why and `haltResumeEligibility` says what clears it.**
92
+ Name the recovery from the verdict alone:
93
+
94
+ - **null** — the agent is not halted. A halt the player set by hand carries a verdict like any
95
+ other, and is recovered through it.
96
+ - **`eligible: true`** — nothing is still breached; resuming will succeed.
97
+ - **`eligible: false`** — name `breachedStop`, the stop still breached, with `breachingFigureUsd`
98
+ against `limitUsd`, and exactly the exits the verdict marks open: raising that stop above the
99
+ figure (`canRaiseTriggeringStop`), the drawdown baseline reset (`canResetBaseline`), and, when
100
+ `breachedStop` is `DAILY_LOSS`, the UTC-day rollover. Then resuming. An exit it does not mark
101
+ open is never offered.
102
+
103
+ Say that a resume attempted while a stop is still breached is **refused by the server**, naming the
104
+ same stop, figure and exits. A resume the server accepts is followed by a re-check of every stop, so
105
+ read the resume response's `haltedAt`: set means a loss settled, or a stop was lowered, while the
106
+ resume ran and the agent was halted again at once; `get_agent_budget`'s `haltReason` names the stop.
98
107
 
99
108
  **If the reads are clean, say so.** An agent whose gauges are unbreached, whose blocks are ordinary
100
109
  no-trade verdicts, and whose deployment covers what the player expected, is working — report the
@@ -143,7 +143,10 @@ strategy timeframe, and at or below it the series as the frame carries it. At or
143
143
  timeframe the choice therefore sets only what the live lane shows — no bar is in progress at the
144
144
  anchor's own close — so Developing there is legal and changes no decision. A condition reading a
145
145
  developing bar is single-frame: it holds one close, carries no verdict and cannot take the `exit`
146
- role. The resolved answer for a column is served on `effectiveParameters.bars`.
146
+ role. One exception overrides both the stored value and the default: a column reading a metric with
147
+ no live value (itself, or a `spread` operand) reads completed bars at every rung, because the forming
148
+ bar publishes nothing for it. The resolved answer for a column is served on
149
+ `effectiveParameters.bars`, and it states that exception.
147
150
 
148
151
  **A higher-timeframe level is measured from the current strategy-bar price.** The distance to a `4h`
149
152
  Donchian band, and every candle label classifier (`MA_ALIGN`, `PRICE_ZONE`, `BB_TOUCH`), compares
@@ -158,8 +161,31 @@ market-wide reads with no session dimension, so they resolve everywhere. The fiv
158
161
  scalars (`fieldPlayers`, `fieldUpBias`, `fieldBiasDir`, `captConc`, `picksSpread`) describe a game
159
162
  session the agent is playing in, and **radar runs outside one** — so a radar deployment whose
160
163
  strategy reads one is refused outright (`CONDITION_OPERAND_UNSERVED_IN_LANE`), naming the scalars it
161
- can read instead. The refusal lands at DEPLOY rather than at save, because a strategy carries no lane
162
- of its own: the same strategy is legal, and reads those scalars correctly, on an arena agent.
164
+ can read instead.
165
+
166
+ **What the radar scan can read on a coin.** Beyond the lane, a condition the radar acts on — a
167
+ required one, one carrying a verdict, an exit one, and every condition they reference — must be
168
+ readable by the radar scan on each coin the radar acts on for the agent, or it would be decided unread.
169
+ What a coin can be read for is its market-data profile: the kinds of data the market-data service
170
+ publishes that it serves for that coin, never the coin's asset class. A refusal carries
171
+ `CONDITION_UNREADABLE_BY_RADAR_SCAN` with its `reachReason`: `INSTRUMENT` when the profile of the coin —
172
+ or of a benchmark section's own instrument — carries no such data, or the service publishes no profile
173
+ for it at all (spot-tape columns on a coin no spot venue lists, TradFi or crypto alike; any market-data
174
+ column on a ticker with no catalog row), `AGENT_TIMEFRAME` when the agent has no such rung on the
175
+ enabled ladder, and `FEED` when the radar scan never reads that data for the agent (crowd reads:
176
+ compose reads them, but only for a coin the scan has already admitted). An `INSTRUMENT` refusal names
177
+ the kind of data the profile lacks, or says the coin has no profile. The fixes it names: take the
178
+ condition's radar role away (neither required nor a verdict, or no exit role), read a column the radar
179
+ scan has on that coin, or free the coin.
180
+
181
+ **The refusal lands where an agent meets a coin**: at the radar deploy (its preview, the builder's
182
+ Save and the draft commit); at a manual entry request, as `CONDITION_UNREADABLE_ON_COIN`;
183
+ and, for an agent already deployed or holding an open position or a pending manual request, at a
184
+ strategy save (`compile_strategy_plan`, `apply_strategy_plan`, `restore_strategy`) or a rebind. A save
185
+ is checked only on behalf of those agents: a strategy carries no lane of its own, so one with no agent
186
+ the radar acts on is never checked, and the same strategy is legal, and reads session-field scalars
187
+ correctly, on an arena agent. A refused compile issues no plan token, so nothing can stage that
188
+ content.
163
189
 
164
190
  **The exit role.** `exit: true` makes a settled TRUE reading close open positions its verdict
165
191
  opposes — UP exits SHORTs, DOWN exits LONGs, a NEITHER or `null` verdict exits both. Legal only over
@@ -277,11 +303,14 @@ smaller position, never more risk.
277
303
  trailingBufferPct, timeDecayEnabled, timeDecayGracePeriodMinutes, timeDecayIntervalMinutes,
278
304
  timeDecayTightenPct, timeDecayMaxTightenPct, timeDecayStaleThresholdTpProgressPct,
279
305
  decisionInvalidationExitEnabled }` — how the stop moves after entry, and when a position is
280
- closed for reasons other than its stop.
281
-
282
- Validated bounds: break-even trigger 0.5–2R; trailing trigger 0–2R step 0.01 (0 = trail from
283
- entry), giveback 25–55%, buffer 0.01–1%; timeDecay grace ≥ interval, tighten 0.1–50% to max
284
- 1–100%, stale threshold 0–100% of TP progress. Per-mechanism flags; no umbrella switch.
306
+ closed for reasons other than its stop. The policy is stamped on each position when it opens: an
307
+ edit applies to positions opened after it, and an open position keeps the policy it opened with.
308
+
309
+ Validated bounds: break-even trigger 0.5–2R step 0.01; trailing trigger 0–2R step 0.01 (0 = trail
310
+ from entry), giveback 25–55% step 1, buffer 0.01–1% step 0.01; timeDecay grace 5–1440 min step 5,
311
+ interval 5–480 min step 5, grace ≥ interval while time decay is armed, tighten 0.5–50% step 0.5,
312
+ max tighten 1–100% step 1, stale threshold 0–100% of TP progress step 1. Per-mechanism flags; no
313
+ umbrella switch.
285
314
 
286
315
  `decisionInvalidationExitEnabled` is the post-fill invalidation exit: a **closed**
287
316
  strategy-timeframe candle beyond the decision's invalidation level closes the position
@@ -44,7 +44,7 @@ new longs / new shorts / short covering / long liquidation), `MARK spread ORACLE
44
44
  `CROWDED_LONGS` building block (`ann gte 25` + `oiChg gte 3` + `oiRegime is "new longs"`);
45
45
  `FADE_SHORT` verdict DOWN (ref + `RSI14 gte 65` + `chg24h gte 5`); mirrored `SQUEEZED_SHORTS`
46
46
  / `SQUEEZE_LONG` verdict UP. Rules: `funding_extreme_positive`/`_negative` 3 required
47
- `{"thresholdPct":0.001}` · `oi_surge` 2 `{"thresholdPct":0.03}` · `rsi_overbought` 2
47
+ `{"thresholdAnnualizedPct":100}` · `oi_surge` 2 `{"thresholdPct":0.03}` · `rsi_overbought` 2
48
48
  `{"threshold":65}` · `rsi_oversold` 2 `{"threshold":35}` · `cvd_bear_divergence` 2 ·
49
49
  `cvd_bull_divergence` 2 · `mfi_overbought` 1 · `mfi_oversold` 1. Gates 0.6 / 1 / 0.5. Levels
50
50
  1.0–2.5 ATR, RR 1.5. PM: BE 0.8R, no trail, timeDecay ON (120/60, 10→40, stale 25).
@@ -70,12 +70,18 @@ re-sorts a list, re-derives a verdict, or approves on the player's behalf.
70
70
  `get_trade_conversation({ conversationId })`, using the id the `queued` result carried. It serves
71
71
  the whole transcript in stored order — the queued card, the agent's reasoning, its thesis and
72
72
  setups, the recommendation, a no-trade with its reason and next coins, or a close answer.
73
- - `get_entry_request` re-reads a request still pending; `cancel_entry_request` withdraws it. Both
74
- are `NOT_FOUND` once it has been answered, cancelled or expired, and that is the honest record:
73
+ - `get_entry_request` re-reads a request until it is answered — including while its close has fired
74
+ and the decision is being composed. `cancel_entry_request` withdraws it only while it still awaits
75
+ its close; once the close has fired it is `NOT_FOUND`, and the answer arrives in the conversation.
76
+ Both are `NOT_FOUND` once it has been answered, cancelled or expired, and that is the honest record:
75
77
  the answer is in the conversation.
76
78
  - `type: "error"` — the block, with its remedy: `OPEN_POSITION_CONFLICT` means a position already
77
79
  holds the slot (see step 0) and nothing was queued; `LLM_CREDITS_EXHAUSTED` means top up when
78
- `topupAvailable` is true. A refused request (`RATE_LIMITED`) is a refusal with its retry-after.
80
+ `topupAvailable` is true; `ENTRIES_PAUSED` means the platform has paused new entries while market
81
+ data is missing across many coins, and nothing was queued. The pause is platform-wide, not the
82
+ agent's or the coin's, so never retry it through another agent or on another coin. It lifts by
83
+ itself once the data is back: ask again then, under a fresh `idempotencyKey` — the same key only
84
+ replays this refusal. A refused request (`RATE_LIMITED`) is a refusal with its retry-after.
79
85
  Never "nothing to do".
80
86
  - `type: "recommendation"` and `type: "no_trade"` arrive only as the **replay** of a request made
81
87
  before the queued contract shipped. Report them as step 5 describes and do not expect them from a
@@ -90,8 +96,20 @@ the player's explicit typed word — never inferred from interest, agreement, or
90
96
  trade.
91
97
 
92
98
  - A decided close produces one of: a PROPOSED decision awaiting approval; a no-trade with the
93
- reason and the next coins worth asking about; a close that did not qualify; a window that passed
94
- with no sweep; or a bar the agent's own radar deployment decided first and traded in full.
99
+ reason and the next coins worth asking about; a refusal, `close_refused`, which names the gate that
100
+ failed when the close did not qualify and none when it qualified and the radar's own gates stopped
101
+ its fire; a window that passed with no sweep; a bar the agent's own radar deployment decided first;
102
+ or an error carrying the engine's block, with nothing proposed, when admission refused the
103
+ qualifying close's fire — `ENTRIES_PAUSED` while new entries are paused, an account block, a
104
+ position already held.
105
+ - A bar the agent's own radar deployment decided first is reported from that deployment's decision,
106
+ never decided again. It arrives as `close_fired_by_radar` — a trade taken in full — only when the
107
+ deployment's fire enqueued a trade decision. Otherwise it arrives as what stopped that fire, and
108
+ none of these is a trade: a fire admission refused arrives as the engine's block, exactly as when
109
+ the refused fire was the request's own; a fire the radar's own gates stopped — its coin cooldown,
110
+ its hourly cap, post-close suppression, a decision already running on the coin — arrives as
111
+ `close_refused` with no gate, because the close qualified and nothing was entered; and a close
112
+ whose coin another agent's fire took arrives as `close_claimed_by_radar`.
95
113
  - On a proposal, state the direction, the entry, stop and take-profit levels, the position size,
96
114
  the **`convictionPercent`**, and the expiry (`expiresAt`). No conviction floor is applied on this
97
115
  surface: the conviction is the agent's own reading, and the player judges it.