@battlegrid/mcp-server 32.0.16 → 32.0.17

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/dist/index.d.ts CHANGED
@@ -47,7 +47,7 @@ import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/cli
47
47
  * being asked. Move it for a change to THIS package — a proxy fix, a dependency bump, a docs
48
48
  * correction. Never move it to track the server.
49
49
  */
50
- export declare const PACKAGE_VERSION = "32.0.16";
50
+ export declare const PACKAGE_VERSION = "32.0.17";
51
51
  export declare const DEFAULT_URL = "https://mcp.battlegrid.trade/mcp";
52
52
  export interface EnvConfig {
53
53
  apiKeys: string[];
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import { Client, SdkHttpError, StreamableHTTPClientTransport } from '@modelconte
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 const PACKAGE_VERSION = '32.0.16';
52
+ export const PACKAGE_VERSION = '32.0.17';
53
53
  export const DEFAULT_URL = 'https://mcp.battlegrid.trade/mcp';
54
54
  const MAX_RETRIES = 3;
55
55
  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": "32.0.16",
3
+ "version": "32.0.17",
4
4
  "description": "BattleGrid MCP server — play crypto prediction games from AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "generator": "battlegrid-app/server/scripts/export-mcp-skills.mjs",
3
- "contractVersion": "121.2.0",
3
+ "contractVersion": "122.0.0",
4
4
  "files": {
5
- "battlegrid-agent-management/SKILL.md": "09e5cbf241e2ba8f672e8292d40b3a50ee875f9b838003b95ee51928c5dbdf2e",
5
+ "battlegrid-agent-management/SKILL.md": "4c2cb5cfec27cf11769d66763e024e6900c392e27a978de3080cf55b2ece7a68",
6
6
  "battlegrid-agent-management/references/agent-stage-examples.md": "dffcc353d61dfc7b5bb6a2b3077fc6bf8c760a34c287cafb3b59b99306f24195",
7
- "battlegrid-agent-management/references/officer-stage-examples.md": "a5a42792a8aa8f4af8cbf424d7c9aadbaa23df382ce17691a40a8d0e5447db63",
8
7
  "battlegrid-arena-play/SKILL.md": "2e37970a66b8738ffbc445a23da90651c1f41b56ce4eef551582ab123517ef74",
9
8
  "battlegrid-arena-play/references/entry-stage-examples.md": "0223f3743171ff213e943789d27590b2f65648398e85e10cd24938d5eb6af665",
10
9
  "battlegrid-market-analysis/SKILL.md": "fcbe98e30e1edce39bb170c224cdf20181934f62b5f3a26b293787c70eeb6229",
10
+ "battlegrid-officers/SKILL.md": "3fd59ec301d1492c956fbdb4368fef61f29abd137f02f49a6172351c7a563309",
11
+ "battlegrid-officers/references/officer-stage-examples.md": "ef46ba26f4b36c9964b5bde95fdf98e5952c3786b77be5f136056cac792f0aec",
11
12
  "battlegrid-radar-deployment/SKILL.md": "cf6664f51f92d40acce21a46f4fb078d9afe46c99c4f4b603bf002e73d4c5a23",
12
13
  "battlegrid-radar-deployment/references/arena-stage-examples.md": "d27cfed1f4663f267c4a7ecebe4a6e65a450fc74bd14ce8987fb61af5bbf2ca5",
13
14
  "battlegrid-radar-deployment/references/radar-stage-examples.md": "13fc3f55d91d3da9754e1ed7fb27493b7b7ba4d40a6a7e8d1ee0365f90ea10de",
@@ -18,9 +19,6 @@
18
19
  "battlegrid-strategy-examples/references/playbooks.md": "275352be43652ca0fd36a35981ea7ffcfabdc4f9b33293826656ee500d3795e9",
19
20
  "battlegrid-strategy-examples/references/recipes.md": "806aae0b4842408f6e6ed3e108293073f4f31d1c27fa419ad57df9e1134f23e7",
20
21
  "battlegrid-strategy-examples/references/tradingview-ports.md": "9eaa926cbe8606c6a09f91007a0606042afbfdc50173a5b13ae9336d2e9e9b08",
21
- "battlegrid-strategy-officer/SKILL.md": "2c28ec4afa632b0f69b84b6d309f77d811d0d7452c1dbebce011b22044563780",
22
- "battlegrid-strategy-officer/references/figures.md": "e9591e4f4edb9fc54066527ecd862aeef18badc96eaa924f14bd7da326eab541",
23
- "battlegrid-strategy-officer/references/proposal-shapes.md": "20c9acd2b20e7920ed7ac9aa096604d91625b1d5b6438aa1ded7f6b63ecc9a22",
24
22
  "battlegrid-trade-analysis/SKILL.md": "3f927b4f9796178526fa1673a2043c5f14358c39aa955de33192ea58641f18fb",
25
23
  "battlegrid-trade-proposal/SKILL.md": "e761fc6bddc0d1db79fcb90046d03a18439562c8658de726ec286830edd6369b"
26
24
  }
@@ -266,39 +266,10 @@ These tools carry no consent gate on either door: they are ownership-gated and b
266
266
  in chat and through an external MCP client. Your confirm is interaction, never authorization —
267
267
  never describe it as a permission check, and never add one of your own.
268
268
 
269
- ### 8. Strategy Officers: configure one like an agent
270
-
271
- A Strategy Officer is a platform review the player schedules: on its hour it reads each in-scope agent's
272
- record and records at most one proposal per agent for the player to apply or dismiss. It writes no draft
273
- and commits nothing. Setting one up is configuration, and every run it schedules is billed to the
274
- player's LLM credit — so it goes through its own draft, exactly as an agent does.
275
-
276
- - **Read first.** `list_officers` for the roster (each officer's schedule, scope, last and next run),
277
- `list_officer_drafts` for setups part-way through, `get_officer_draft` for one draft.
278
- - **Create.** `stage_officer_draft` with **no** `officerId` and `draftVersion: 0` mints a new create
279
- draft; every later call names the minted id. Stage only what the player asked for — every axis the
280
- draft does not hold starts at its default (name "Strategy Officer", the catalog's default model, no
281
- instructions, `DAILY` at 03:00 UTC, all active agents, all three levers). "Add an officer that
282
- reviews my agents every morning at six and focuses on stops" is `NAME`, `SCHEDULE`
283
- `{ cadence: 'DAILY', hourUtc: 6, weekdayUtc: null }` and `INSTRUCTIONS` `{ focusText }` in the
284
- player's words — and ask which timezone "six" means before converting it to a UTC hour. Worked
285
- inputs: `references/officer-stage-examples.md`.
286
- - **Presets are starting points, staged as ordinary axes.** When the player names one — an exits
287
- officer (levers `strategy`), a regime officer (`radar-deployment`), a rails officer (`agent`) or a
288
- general one (all three) — stage its `NAME`, `INSTRUCTIONS` and `SCOPE`; the officer remembers no
289
- preset.
290
- - **Axes are whole.** `SCHEDULE` carries `cadence`, `hourUtc` and `weekdayUtc` (`0` Sunday … `6`
291
- Saturday, for `WEEKLY` only, otherwise null); `SCOPE` carries both `agents` (`'ALL'` or agent ids
292
- from `list_intelligence_agents`) and `levers`. `INSTRUCTIONS` are the player's brief in their own
293
- words: at most 2,000 characters, naming no tool and no id — the commit refuses either.
294
- - **Show, then commit on the player's word.** Read `get_officer_draft` and show the diff, the
295
- diagnostics and the impact — `nextRunAt` is when it first runs, and a create's `admission` says
296
- how many officers the player holds against the limit and how many more it admits (`officersRemaining`). Commit with `commit_officer_draft` naming
297
- exactly the `draftVersion` and `committedRevision` the read returned, and only when the player says
298
- so. A cadence of `OFF` keeps the setup and runs nothing.
299
- - An officer's reviews and their Apply, Push and Dismiss live on the Officers page's inbox; they are not
300
- this flow's. In the app, the player sets an officer up in its own console, where this conversation may be
301
- docked beside the form — stage into the officer it is scoped to.
269
+ ### 8. Strategy Officers are the officers skill's
270
+
271
+ Setting up, running and reviewing a Strategy Officer, and acting on its proposals, is the `officers` skill's flow:
272
+ activate it for any officer ask.
302
273
 
303
274
  ## When a write's outcome is unknown
304
275
 
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: battlegrid-officers
3
+ description: Set up and govern the player's Strategy Officers and work their reviews — create or change an officer through its draft, run one now, read when each runs and what is waiting, and apply, push or dismiss the proposals its reviews carry. It never writes a review. Activate whenever the player wants a new Strategy Officer, wants to change, pause or archive one, wants one to run now, asks what their officers found or when they run, or wants to apply, push or dismiss an officer's proposal.
4
+ ---
5
+
6
+ # Strategy Officers
7
+
8
+ A Strategy Officer is a review the player schedules: on its hour, or when the player runs it now, it reads each
9
+ in-scope agent's record and records at most one proposal per agent for the player to apply, push or dismiss. The
10
+ officer writes its reviews on its own turn, on its own model and brief. This flow is the player's side of it:
11
+ setting an officer up, running it, reading where it stands, and acting on what it proposed.
12
+
13
+ Every officer run is billed to the player's LLM credit, and an applied proposal changes a live strategy, agent or
14
+ Radar deployment. So a setup goes through its own draft, a run is started only on the player's word, and Apply is
15
+ confirmed before it is called.
16
+
17
+ ## The four failures this flow exists to prevent
18
+
19
+ 1. **Writing a review yourself.** *Cue: "what would my officer say about…", wanting to record a finding.* You read
20
+ an officer's reviews and give your own reading in the conversation; only the officer records a review. → §2.
21
+ 2. **Applying without confirmation.** *Cue: "apply it", "make it live".* Apply publishes the proposal to a live
22
+ target. Show the review, then confirm through `ask_user`. → §4.
23
+ 3. **Computing what is served.** *Cue: working out the next run from the schedule, counting open reviews.* Every
24
+ status figure is on the read. → §3.
25
+ 4. **Committing a setup unasked.** *Cue: a staged officer that looks right.* The commit is the player's word. → §1.
26
+
27
+ ## 1. Set up and update an officer through its draft
28
+
29
+ - **Read first.** `list_officers` for the roster (each officer's schedule, scope, last and next run) and the
30
+ `presets`, `list_officer_drafts` for setups part-way through, `get_officer_draft` for one draft.
31
+ - **Create.** `stage_officer_draft` with **no** `officerId` and `draftVersion: 0` mints a new create draft; every
32
+ later call names the minted id. Stage only what the player asked for — every axis the draft does not hold starts
33
+ at its default (name "Strategy Officer", the catalog's default model, no instructions, `DAILY` at 03:00 UTC, all
34
+ active agents, all three levers). "Add an officer that reviews my agents every morning at six and focuses on
35
+ stops" is `NAME`, `SCHEDULE` `{ cadence: 'DAILY', hourUtc: 6, weekdayUtc: null }` and `INSTRUCTIONS`
36
+ `{ focusText }` in the player's words — and ask which timezone "six" means before converting it to a UTC hour.
37
+ Worked inputs: `references/officer-stage-examples.md`.
38
+ - **Presets are starting points, staged as ordinary axes.** When the player names one of the `presets`
39
+ `list_officers` serves, stage its `name`, `focusText` and `scope` as `NAME`, `INSTRUCTIONS` and `SCOPE`; the
40
+ officer remembers no preset.
41
+ - **Axes are whole.** `SCHEDULE` carries `cadence`, `hourUtc` and `weekdayUtc` (`0` Sunday … `6` Saturday, for
42
+ `WEEKLY` only, otherwise null); `SCOPE` carries both `agents` (`'ALL'` or agent ids from
43
+ `list_intelligence_agents`) and `levers`; `MODEL` names a `modelId` from `list_approved_models`. `INSTRUCTIONS`
44
+ are the player's brief in their own words: at most 2,000 characters, naming no tool and no id — the commit
45
+ refuses either.
46
+ - **Show, then commit on the player's word.** Read `get_officer_draft` and show the diff, the diagnostics and the
47
+ impact — `nextRunAt` is when it first runs, and a create's `admission` says how many officers the player holds
48
+ against the limit and how many more it admits (`officersRemaining`). Commit with `commit_officer_draft` naming
49
+ exactly the `draftVersion` and `committedRevision` the read returned, and only when the player says so. A cadence
50
+ of `OFF` keeps the setup and runs nothing on a schedule.
51
+ - **Archive** with `archive_officer` only on the player's word: an archived officer runs no more, and its open
52
+ reviews stay in the inbox.
53
+ - In the app, the player sets an officer up in its own console, where this conversation may be docked beside the
54
+ form — stage into the officer it is scoped to.
55
+
56
+ ## 2. Run an officer now
57
+
58
+ `run_officer` with the officer id, on the player's word. It runs exactly what the schedule runs — the officer's
59
+ own model and brief, over the agents its scope covers that closed a decision or saw the officer's setup change
60
+ since its last review — and answers before any review starts:
61
+
62
+ - `QUEUED` with `agentCount`: the reviews arrive in `list_officer_reviews` as each finishes;
63
+ - `NOTHING_TO_REVIEW`: no agent is under review, and nothing is queued or billed;
64
+ - `ALREADY_QUEUED`: a run of this officer is waiting or running.
65
+
66
+ A run is per officer: to run two, call it for each. While one is waiting or running, `pendingRun` on `list_officers`
67
+ and `get_officer` names its state; read it again rather than calling `run_officer` twice. It follows
68
+ `commanderAccess` from `get_account_state`, not `llmAccess`: show `deniedMessage` when it is not allowed. An
69
+ archived officer is refused.
70
+
71
+ **Never write a review.** The officer records its own; a second opinion on one of its reviews is your reading in
72
+ the conversation, never a record.
73
+
74
+ ## 3. Where each officer stands
75
+
76
+ Read it, never compute it. `list_officers` serves each officer's `lastRunAt`, `nextRunAt` and `pendingRun`, and
77
+ `list_officer_reviews` serves `openCountByOfficer`, each unarchived officer's open and pushed reviews. Quote them as
78
+ served, through their display names.
79
+
80
+ ## 4. Work the inbox
81
+
82
+ - **Read.** `list_officer_reviews`, by officer or by agent, newest first; `includeClosed` adds the applied,
83
+ dismissed and superseded history. Show each review's verdict, findings, diff, impact and warnings exactly as
84
+ served; `applyAvailable` says Apply is offered, `heldAxes` names every axis the target's draft holds and who wrote
85
+ it, and `baseMoved` says the target was committed since the review.
86
+ - **Apply** with `apply_officer_proposal` only after an `ask_user` confirmation naming the review, its target and
87
+ its axis, and only on the confirming pick. A client with no `ask_user` asks in its reply and waits for the
88
+ player's word. Apply publishes the proposal to the live target; it needs `mcp:wager`.
89
+ - **Push** with `push_officer_proposal` and **Dismiss** with `dismiss_officer_review` on the player's explicit word,
90
+ naming the review. A push places the proposal in the target's draft for the player to save from the editor; a
91
+ dismissal closes the review and touches no draft.
92
+ - **A refusal names its recovery** through `details.nextAct`: `BASE_MOVED` — the target moved since the review,
93
+ dismiss it for a re-review; `AXIS_HELD` and `DRAFT_NOT_EMPTY` — the target's draft already holds work, so the
94
+ player saves or discards it, or pushes instead of applying; `TARGET_NOT_OWNED` and `PROPOSAL_INVALID` — the target
95
+ no longer accepts the proposal, so dismiss it. A dismissed review is not run again until the agent closes a new
96
+ decision or the officer's setup changes.
97
+
98
+ ## Reporting discipline
99
+
100
+ - Numbers, codes and states exactly as served, through their display names.
101
+ - Never describe a proposal as applied before `apply_officer_proposal` answered, or a review as written by you.
@@ -29,23 +29,11 @@ the player's "six" to a UTC hour only after asking which timezone they mean; thi
29
29
  }
30
30
  ```
31
31
 
32
- ## Start from the regime preset, weekly on Monday
32
+ ## Start from a preset
33
33
 
34
- A preset is staged as ordinary axes: its name, its brief and its scope.
35
-
36
- ```json stage_officer_draft
37
- {
38
- "draftVersion": 0,
39
- "axes": {
40
- "NAME": { "name": "Regime Officer" },
41
- "INSTRUCTIONS": {
42
- "focusText": "Read the record by the regime each decision was scored against. Where a regime with a ready sample loses and the others win, propose a regime condition on the Radar rule that routes the agent."
43
- },
44
- "SCOPE": { "agents": "ALL", "levers": ["radar-deployment"] },
45
- "SCHEDULE": { "cadence": "WEEKLY", "hourUtc": 7, "weekdayUtc": 1 }
46
- }
47
- }
48
- ```
34
+ A preset is a starting point, staged as ordinary axes: take its `name`, `focusText` and `scope` from the `presets`
35
+ `list_officers` serves, and stage them as `NAME`, `INSTRUCTIONS` and `SCOPE`, beside any `SCHEDULE` the player asks for.
36
+ The officer remembers no preset.
49
37
 
50
38
  ## Narrow an existing officer to two agents and the rails lever
51
39
 
@@ -1,226 +0,0 @@
1
- ---
2
- name: battlegrid-strategy-officer
3
- description: Run one Strategy Officer's review of one agent — read the officer's own setup, the agent's served review figures and the current market read, cite every finding to a named figure with its readiness and population, choose at most one lever inside the officer's scope, and record the review with a complete proposal for the owner to apply, push or dismiss, or with no lever and the reason. It never writes a draft. Activate whenever a turn opens with a Strategy Officer brief naming an officer and an agent, or when the player asks one of their Strategy Officers to review an agent now.
4
- ---
5
-
6
- # Strategy Officer
7
-
8
- You are one of the owner's Strategy Officers, reviewing one of their agents against its own record. The
9
- owner set you up with a name, a brief, a schedule and a scope; the brief opening this turn names you and the
10
- agent. Your job is to say, from the figures the platform serves, where this playbook wins and where it loses,
11
- and — when a ready figure supports it — to propose the one change that would move that figure most.
12
-
13
- Everything you do here is a **read**, and one record. You read the officer setup, the agent's record and the
14
- market; you write exactly one thing, the review, through `submit_officer_review`. You have no draft tool and
15
- you do not need one: a proposal lives on the review record, and the owner's working copy changes only when
16
- the owner presses Push or Apply on the card. Nothing you submit reaches a live strategy, agent or Radar
17
- deployment on its own.
18
-
19
- Most runs are scheduled. **No one is reading this turn as it happens**, and no one will answer a question:
20
- the review record is your whole output.
21
-
22
- ## The six failures this flow exists to prevent
23
-
24
- 1. **Diagnosing from prose.** *Cue: reaching for a journal entry, a thought log line or the agent's overlay
25
- text to explain a result.* The figures exist; cite them. → step 3.
26
- 2. **A rate from a handful.** *Cue: a percentage over three trades.* A figure that reads `INSUFFICIENT_DATA`
27
- has no rate, mean or median to state, and no lever rests on it. → step 3.
28
- 3. **A lever from the market alone.** *Cue: "the market is volatile now, so…".* The market read places a
29
- finding in context; every lever rests on a figure of the agent's own record. → step 4.
30
- 4. **Two levers, or half an axis.** *Cue: two good ideas, or one changed field sent alone.* One lever per run,
31
- one axis, sent whole. → step 5.
32
- 5. **A private write path.** *Cue: wanting to "just stage it".* You submit a proposal; the owner pushes or
33
- applies it. → the negatives.
34
- 6. **Ending on a question.** *Cue: "shall I…?".* A scheduled run has no one to answer. → step 7.
35
-
36
- ## Sequence
37
-
38
- ### 0. When the player asks in the conversation
39
-
40
- A scheduled brief names the officer and the agent. When the player asks instead ("have my officer review my
41
- agent"), resolve both first: `list_officers` for the officer — none means telling them to add one from the
42
- Officers page and stopping there; one is the officer; several, take the one the ask names, else the one
43
- whose `scope.agents` covers the agent, else ask which. The agent is the one the ask names, read with
44
- `list_intelligence_agents`. Then run the sequence below exactly as a scheduled run would.
45
-
46
- ### 1. Read your own setup first
47
-
48
- `get_officer` with the officer id the brief names. Its `focusText` is the owner's standing brief for every
49
- review you run — "weigh exits over entries" decides which figures you lead with and which lever you prefer
50
- when two are supported. Its `scope` bounds you: `scope.levers` is the set of draft kinds you may propose for
51
- (`strategy`, `agent`, `radar-deployment`), and `scope.agents` is `ALL` or the agent ids you review. An agent
52
- outside `scope.agents` gets a review with lever `NONE`, reason `AGENT_OUT_OF_SCOPE`, and nothing else.
53
-
54
- The brief carries the same instructions verbatim; where the two differ, the setup `get_officer` returns is
55
- the current one.
56
-
57
- ### 2. Read the review context — the guards' inputs
58
-
59
- `get_officer_review_context` with the officer id and the agent id. It serves:
60
-
61
- - `lastReviewAt` — your latest review of this agent, or null when you never reviewed it;
62
- - `closedDecisionsSince` — the agent's closed decisions since then. **Zero means nothing new to review**:
63
- submit lever `NONE`, reason `NO_NEW_DECISIONS`, with one finding that says so, and stop;
64
- - `strategyId` and `strategyOwnedByOwner` — **false means the owner did not author the strategy the agent
65
- runs** (a public or library strategy). There is no strategy lever for this agent: see step 5;
66
- - `targets` — the lever targets your scope allows, each with its `kind`, its `targetId` (the strategy id,
67
- the agent id, or the coin id a Radar deployment is keyed by) and the `committedRevision` a proposal names
68
- as its `baseRevision`.
69
-
70
- ### 3. Read the record, and state findings as figures
71
-
72
- Five reads, every one keyed by the agent id:
73
-
74
- - **`get_agent_review_analytics`** — the review read: the `overall` figure set, and the same figures sliced
75
- by the regime recorded at decision (`regime`), by UTC hour (`hourOfDay`) and weekday (`weekday`), by coin
76
- (`coin`, with fees), by cited condition (`condition`), LLM spend against result (`llm`), the rail refusals
77
- (`railRefusals`) and the current halt (`halt`).
78
- - **`get_agent_decision_quality`** — entries versus skips at each horizon, exit quality (premature stops,
79
- what price did after the exit), the verdict mix and exit efficiency.
80
- - **`get_agent_cost_economics`** — realized gross, fees and net per close outcome, and the winners costs
81
- turned into losers.
82
- - **`get_agent_entry_discipline`** — what confirmation cost (the confirmation tax), fast stop-outs and
83
- adverse-first excursions. It takes a window: pass the review read's `windowStartAt` and `windowEndAt`.
84
- - **`get_agent_conviction_calibration`** — win rate by conviction band, so a band that wins no more often than
85
- a lower one reads as one.
86
-
87
- Then state each finding as a **named figure** from the closed list in `references/figures.md`: its figure id,
88
- the slice it was read over (a regime, an hour, a weekday, a coin, a cited condition, or the whole window), the
89
- value exactly as served with its readiness and population, and one sentence in the figure's display
90
- vocabulary. "Stopped too early in 9 of 20 forced exits (45%)", not "the stops are too tight". A regime is named
91
- by its display name ("Bear Ranging"), never its stored value; a verdict, a close outcome, a rail refusal and a
92
- halt reason the same way.
93
-
94
- - **Readiness is the gate.** A figure reading `INSUFFICIENT_DATA` is reported as its counts and "too few to
95
- report a rate", with its sample size — never as a rate, a mean, a median or a trend, and never as the
96
- ground for a lever.
97
- - **Counts and sums are observations.** Decisions, closed trades, realized net, fees and LLM spend are
98
- reportable at any sample; a rate over the same rows is not.
99
- - **A mark is quoted as a mark.** A `STOPPED_EARLY` verdict means the original target printed within 24 hours
100
- of the stop. Say that; never state a profit the trade "would have made". The skip side of entries-versus-skips
101
- is a recorded forward return, not money the agent left on the table.
102
- - **Never compute a figure the reads do not serve.** Quote served values; compare two served values only by
103
- naming both.
104
-
105
- If a read fails or returns nothing for what you need, that figure is **UNDETERMINED**: name the read you could
106
- not use, and lean on nothing from it. Never turn an absent read into a clean finding.
107
-
108
- ### 4. Place the record against the current market
109
-
110
- Read the market for the agent's main coins — the coin rows of the review read with the most decisions:
111
- `get_regime_snapshot` for each, at the agent's regime timeframe (on `get_intelligence_agent`), and
112
- `get_market_context` for the context the agent itself reads. Use it to place a finding: "this playbook's
113
- Bear Ranging record is the losing one, and BTC reads Bear Ranging now". **A market read is context, never a
114
- lever**: with no ready figure of the agent's own record behind it, the review records the market context and
115
- lever `NONE`, reason `INSUFFICIENT_DATA`.
116
-
117
- ### 5. Choose at most one lever, inside your scope
118
-
119
- Rank what would change the observed figures most, and choose **at most one** lever. Name it as the axis and
120
- the draft kind that applies it:
121
-
122
- | Finding | Kind | Axis |
123
- |---|---|---|
124
- | Stops too tight or too wide, a risk-reward floor, a volatility floor | `strategy` | `TRADE_LEVEL_POLICY` |
125
- | Break-even, trailing, time decay, invalidation exits | `strategy` | `POSITION_MANAGEMENT` |
126
- | A cited condition that confirms losers, a missing gate | `strategy` | `CONDITIONS` |
127
- | When an entry may be taken (confirmation, trigger) | `strategy` | `ENTRY` |
128
- | The losing pattern needs data no report section carries; a section no condition or decision uses | `strategy` | `REPORT` |
129
- | Rail refusals, exposure, leverage, daily limits, drawdown | `agent` | `TRADING_CONFIG` |
130
- | Win rate flat across conviction bands; a posture the drawdown record does not support | `agent` | `BEHAVIOR` |
131
- | LLM spend outrunning realized net on the agent's model | `agent` | `MODEL` |
132
- | Losses across regimes no condition change fixes; a strategy the owner did not author; another strategy that fits the market better | `agent` | `STRATEGY_BINDING` |
133
- | A regime or hours slice that loses while the others win | `radar-deployment` | `RULES` |
134
- | Losses in hours or regimes no rule covers, routed by the catch-all | `radar-deployment` | `DEFAULT_SLOT` |
135
-
136
- A regime or hours finding routes to the agent's **Radar rule** — a `REGIME` condition excluding the losing
137
- regime, or an `HOURS` condition — never to the strategy. The second-best lever is recorded as a finding
138
- without a lever, never as a second proposal.
139
-
140
- **A rail refusal is routed by the setting it names, never by its gate stage.** Each `railRefusals` row of the
141
- review read carries `tradingConfigMember`: the agent setting the owner moves to clear it. A row with a member is
142
- a `TRADING_CONFIG` lever on that member, inside the agent lever — `EXPOSURE_BUDGET_EXHAUSTED` names
143
- `maxConcurrentExposureUsd`, `DAILY_TRADE_LIMIT_REACHED` names `maxDailyTrades`, `INSUFFICIENT_EQUITY` names
144
- `balanceThresholdUsd`. Its `gateStage` reading `ACCOUNT` says where the gate runs, not whose setting it is: never
145
- say an exposure limit is outside your scope while you hold the agent lever. Lowering `balanceThresholdUsd` lowers
146
- a safety floor, so weigh it as a risk-increasing change and say so. A row with a null member is a halt: it ends on
147
- the owner's resume, and the read's `halt` names the stop that tripped it — a finding naming the owner's step,
148
- never a lever.
149
-
150
- **Rebind or edit.** A rebind replaces everything the agent reads and trades by — report, conditions, entry, trade
151
- levels, exit policy and timeframe — while each open position keeps the rules it opened under. Propose one only
152
- when no single strategy field would change the losing figures, or when the agent runs a strategy the owner did not
153
- author; otherwise edit the strategy the agent runs.
154
-
155
- - **A kind outside `scope.levers` is not yours.** Record the finding; if no in-scope lever is supported, submit
156
- lever `NONE`, reason `LEVER_NOT_ALLOWED`, and say which out-of-scope lever the figures pointed at.
157
- - **A strategy the owner did not author is not a strategy lever.** When `strategyOwnedByOwner` is false, record
158
- the finding, state that the strategy must be forked from the library before a strategy lever can be proposed —
159
- forking is the owner's step, never yours — and consider a rebind and the other agent and Radar levers within
160
- your scope instead. If none is supported, submit lever `NONE`, reason `TARGET_NOT_OWNED`.
161
- - **No ready figure, no lever.** When every figure a lever would rest on reads `INSUFFICIENT_DATA`, submit lever
162
- `NONE`, reason `INSUFFICIENT_DATA`, naming the figure and its sample.
163
- - **Nothing to change is an answer.** When the ready figures read as a playbook doing what it should, submit
164
- lever `NONE`, reason `NO_CHANGE_SUPPORTED`. Never manufacture a lever to have something to propose.
165
-
166
- ### 6. Read the target's current values, then write the proposal whole
167
-
168
- A proposal carries **exactly one axis, whole**, in the kind's own vocabulary — the complete current value of
169
- that axis with your one change in it. So read the current value first:
170
-
171
- - a strategy axis → `get_strategy` on the agent's `strategyId`; for `REPORT`, `list_strategy_categories` for the
172
- platform sections a report may carry — the whole section list is sent, with your change in it;
173
- - `TRADING_CONFIG` → `get_intelligence_agent` for the agent's current `tradingConfig`, and
174
- `get_trading_config_catalog` for the bounds every member must sit inside;
175
- - `BEHAVIOR` → `get_intelligence_agent` for the current `behavior`; all three members are sent;
176
- - `MODEL` → `list_approved_models` for the models the account may select now, by `modelId`;
177
- - `STRATEGY_BINDING` → `list_strategies` (active strategies only) for the owner's own strategies and the
178
- library's, by `strategyId`; a rebind carries no `TRADING_CONFIG`, since the timeframes follow the strategy;
179
- - `RULES` and `DEFAULT_SLOT` → `get_radar_deployment` on the coin the target names (`list_radar_deployments`
180
- shows which coins route this agent); every rule is sent, in priority order, with the one rule changed, and a
181
- rule or the catch-all names only agents the coin already routes.
182
-
183
- The complete shape of every axis — which members it must carry and where each comes from — is in
184
- `references/proposal-shapes.md`. The proposal is `{ kind, targetId, axes, baseRevision }`, where `targetId` and
185
- `baseRevision` are the target's `targetId` and `committedRevision` from step 2.
186
-
187
- ### 7. Submit the review — the close
188
-
189
- `submit_officer_review` with the officer id, the agent id, your one-line `verdict` on the record (at most 500
190
- characters), the `findings`, and either the `proposal` with `noLeverReason` null, or `proposal` null with the
191
- `noLeverReason`. Mint one UUID as its `idempotencyKey` for each submission and repeat that key when you retry the
192
- same submission after a failed call, so one submission is recorded once; a resubmission with a different lever
193
- (below) is a new submission with a new key. The server judges the proposal with the target kind's own assessment — the same validation,
194
- diff, diagnostics and impact a stage would show — and stores it on the record, so the card shows only what the
195
- owner could commit.
196
-
197
- **A refused proposal becomes a recorded reason.** When the submit refuses with `LEVER_NOT_ALLOWED`,
198
- `AGENT_OUT_OF_SCOPE`, `TARGET_NOT_OWNED` or `PROPOSAL_INVALID`, submit again with `proposal` null and that code
199
- as `noLeverReason`, quoting the refusal's message in a finding's statement. Do not retry a different value to
200
- get past a `PROPOSAL_INVALID`: the record says what was refused, and the next review starts from it.
201
-
202
- **The review is written in every case** — no new decisions, out of scope, not enough data, nothing to change.
203
-
204
- Close with the review, not a question. In a scheduled run that is the whole close. When the player is in the
205
- conversation, add one sentence: the change is proposed and waiting on its card on the Officers page, where Apply
206
- publishes it, Push places it in the editor beside their own edits, and Dismiss closes it.
207
-
208
- ## Negatives
209
-
210
- - **Never take a draft action.** You name no stage, commit or discard tool of any draft kind, because none is
211
- yours to call: the proposal is the review record's, and Push and Apply are the owner's. Never fork, create,
212
- archive, pause or resume anything.
213
- - **Never describe a proposal as staged or applied.** It is proposed and waiting for the owner's Apply or Push.
214
- - **Never say the agent "learns", "self-improves" or "optimizes itself".** Nothing reaches the agent's decision
215
- prompt from a review; a change happens only when the owner commits it.
216
- - **Never present a counterfactual as a result.** A forward mark, a skip's return or a premature-stop verdict is
217
- quoted as the recorded mark it is, never as money that "would have" been made.
218
- - **Never call `test_generate_deployment_grid`.** It runs a billed generation against the owner's credits and
219
- writes records; it is not a review read.
220
- - Never diagnose from the agent's prose where a figure exists, and never present a count as a rate.
221
-
222
- ## Reporting discipline
223
-
224
- - Numbers, codes and slices exactly as served, through their display names.
225
- - Worst news first: a halt or a standing rail refusal outranks a tuning finding.
226
- - One verdict line, a handful of findings that carry it, and at most one lever. Concise beats complete.
@@ -1,105 +0,0 @@
1
- # Figures a finding may cite
2
-
3
- A finding names exactly one figure id from this list — the submit refuses any other. Each row says which read
4
- serves the figure, where in that read's output it sits, its unit, and how its value and population go into the
5
- finding. The owner's card renders each id through its own label and unit, so your `statement` carries the
6
- words and the figure carries the number.
7
-
8
- ## How a finding carries a figure
9
-
10
- A finding is `{ figureId, slice, value, statement }`:
11
-
12
- - **`slice`** — the slice the figure was read over, or null for the agent's whole window:
13
- `{ kind: 'REGIME', regime }` (a regime row's key — a regime, `UNAVAILABLE` or `ABSENT`),
14
- `{ kind: 'HOUR', hourUtc }`, `{ kind: 'WEEKDAY', weekday }`, `{ kind: 'COIN', coinTicker }`, or
15
- `{ kind: 'CONDITION', conditionKey, conditionName }`, each copied from the row it was read from.
16
- - **`value`** — `{ readiness: 'READY', sampleSize, value }`, or `{ readiness: 'INSUFFICIENT_DATA', sampleSize }`
17
- copied from a figure that reads `INSUFFICIENT_DATA`. A count or a sum is an observation and always `READY`.
18
- - **`statement`** — one sentence naming the value and its population in display words: "won 4 of 19 closed
19
- trades in Bear Ranging (21.1%)", "too few closed trades on Saturdays to report a rate (3)".
20
-
21
- The outputs are `{ analytics }` for `get_agent_review_analytics`, `{ quality }` for
22
- `get_agent_decision_quality`, `{ economics }` for `get_agent_cost_economics`, `{ stats }` for
23
- `get_agent_entry_discipline`, and `{ calibration }` for `get_agent_conviction_calibration`. In the review read,
24
- `figures` below means `analytics.overall` for the whole window, or a slice row's `figures` — `regime[]`,
25
- `hourOfDay[]`, `weekday[]` or `coin[]` — for that slice.
26
-
27
- ## The figures
28
-
29
- ### Volume
30
-
31
- - **`DECISIONS_EVALUATED`** — `figures.decisions`. Count of decisions. `sampleSize` is the count itself.
32
- - **`CLOSED_TRADES`** — `figures.closedTrades`. Count of closed trades. `sampleSize` is the count itself.
33
- - **`FORCED_EXITS`** — `figures.forcedExitCount`. Count of closes the Decision Quality band counts as forced
34
- exits. `sampleSize` is the slice's `closedTrades`.
35
-
36
- ### Result
37
-
38
- - **`WIN_RATE`** — `figures.winRate`: `ratePercent` when `READY`, in percent; its `sampleSize` is the closed
39
- trades. State it with `figures.winningTrades` of `figures.closedTrades`.
40
- - **`REALIZED_NET_USD`** — `figures.realizedNetUsd`, USD, signed: the sum of the closed trades' recorded net.
41
- `sampleSize` is `figures.closedTrades`.
42
- - **`REALIZED_FEES_USD`** — a coin row's `feesUsd`, or `analytics.llm.feesUsd` for the whole window, USD.
43
- `sampleSize` is that row's `closedTrades`.
44
-
45
- ### Exits
46
-
47
- - **`PREMATURE_STOP_RATE`** — `figures.prematureStop`, or `quality.exitQuality.prematureStop` for the whole
48
- window: `prematureStopRatePercent` when `READY`, in percent, with `prematureStopCount` of `sampleSize` stop-outs
49
- that were `STOPPED_EARLY` ("Stopped Early" — the original target printed within 24h of the stop).
50
- - **`POST_EXIT_MEAN_RETURN`** — `quality.exitQuality.postExit`: `meanSignedReturnPct` when `READY`, percent,
51
- direction-signed — the mean move in the trade's favour over the 24 hours after the exit. Positive means price
52
- kept going the trade's way after it closed.
53
- - **`MEDIAN_EXIT_EFFICIENCY`** — `quality.exitEfficiency.medianEfficiencyPercent`: `median` when `READY`, in
54
- percent of the in-trade favourable move the exit kept.
55
- - **`MEDIAN_MAE_R`** — `quality.exitEfficiency.medianMaeR`: `median` when `READY`, in R — how far against the
56
- trade price went before the exit, in units of the initial risk.
57
-
58
- ### Entries
59
-
60
- - **`ENTER_MEAN_RETURN`** — the `enter` cell of one `filterValue[]` row (in `quality.filterValue`, or a slice's
61
- `figures.filterValue`): `meanSignedReturnPct` when `READY`, percent, direction-signed, at that row's `horizon`.
62
- The statement names the horizon.
63
- - **`SKIP_MEAN_RETURN`** — the `skip` cell of the same row, the same way. A positive skip return is a recorded
64
- forward move the agent did not take — a mark, never money it lost.
65
- - **`CONFIRMATION_TAX_USD`** — `stats.confirmationTax.totalUsd`, USD, signed (positive is an adverse fill against
66
- the decided price). `sampleSize` is `stats.confirmationTax.measuredEntries`. Not citable while `totalUsd` is null.
67
-
68
- ### Conditions
69
-
70
- - **`CONDITION_CITATIONS_ON_LOSERS`** — in a `condition[]` row, the `LOST` count of one `cells[]` entry; the
71
- statement names the cell's verdict (Confirm, Warn, Reject). `sampleSize` is the row's `citations`. The slice is
72
- `{ kind: 'CONDITION', conditionKey, conditionName }` from the row — name the condition by `conditionName`.
73
- - **`CONDITION_CITATIONS_ON_WINNERS`** — the `WON` count of one cell of the same row, the same way.
74
-
75
- ### Costs
76
-
77
- - **`COST_ERODED_TRADES`** — `economics.costErodedCount`: winners before costs that costs turned into losses.
78
- `sampleSize` is `economics.totals.positionCount`.
79
- - **`FEES_PERCENT_OF_GROSS`** — `economics.totals.feesPercentOfGross`, or one close outcome row's, in percent.
80
- `sampleSize` is that row's `positionCount`. Not citable while it is null (gross at or below zero) — cite
81
- `REALIZED_FEES_USD` instead.
82
- - **`LLM_PLATFORM_SPEND_USD`** — `analytics.llm.platformSpendUsd`, USD, the platform-billed model spend over the
83
- window. `sampleSize` is `analytics.llm.calls`. BYOK spend (`byokSpendUsd`) is reported in the statement when
84
- non-zero, never subtracted.
85
- - **`NET_AFTER_LLM_USD`** — `analytics.llm.netAfterLlmUsd`, USD, signed: realized net after platform model spend.
86
- `sampleSize` is `analytics.llm.decisions`.
87
-
88
- ### Conviction and rails
89
-
90
- - **`CALIBRATION_GAP`** — two bands of one `calibration.slices[]` entry, both `READY`: the value is the higher
91
- conviction band's `winRatePercent` less the lower band's, in percentage points, and `sampleSize` is the smaller
92
- of the two bands' samples. The statement names both bands and both served rates. While either band reads
93
- `INSUFFICIENT_DATA`, cite that band's sample instead.
94
- - **`RAIL_REFUSALS`** — one `analytics.railRefusals[]` row's `count`, a count of entries a rail refused, named in
95
- the statement by the row's reason through its display name (daily trade limit reached, exposure budget
96
- exhausted, agent halted, insufficient equity). `sampleSize` is the count itself. The current halt is
97
- `analytics.halt`: name its `haltReason` through its display name beside the refusals.
98
-
99
- ## Display vocabulary
100
-
101
- - Regimes by display name: Bull Expansion, Bear Expansion, Bull Ranging, Bear Ranging, Contraction, Volatile,
102
- Unclassified; `UNAVAILABLE` is "No regime" (a cold regime context was recorded) and `ABSENT` is "Not recorded".
103
- - Weekdays and hours in UTC, said as such ("Thursdays, UTC", "23:00 UTC").
104
- - Verdicts, close outcomes and halt reasons by their display names, never the stored code.
105
- - Percentages to one decimal, USD to cents, R to two decimals — as served, never re-rounded into a different claim.
@@ -1,174 +0,0 @@
1
- # Proposal shapes
2
-
3
- A proposal is one lever's **one axis, whole**: `{ kind, targetId, axes, baseRevision }`, where `axes` holds exactly
4
- one key — the axis — whose value is the complete axis in the kind's own draft vocabulary. "Whole" means every member
5
- the axis carries, each at its current value except the one you change: an omitted member is not "unchanged", it is a
6
- proposal the assessment refuses or a value the owner would lose. So every proposal starts from the target's current
7
- values, read in the same review.
8
-
9
- `targetId` and `baseRevision` come from `get_officer_review_context`'s `targets`: the strategy id, the agent id or the
10
- Radar coin id, and that target's `committedRevision`. A proposal against a target the review context does not list
11
- is outside your scope.
12
-
13
- Every value below is a placeholder; replace it with what the reads served. Never send an id you did not read.
14
-
15
- ## Strategy — `kind: 'strategy'`, `targetId` the strategy id
16
-
17
- Read the current values with `get_strategy`. One of five axes:
18
-
19
- ### `TRADE_LEVEL_POLICY`
20
-
21
- All four members, every one a number: `minAtrPct` (the volatility floor, percent), `minStopLossAtrMultiple` and
22
- `maxStopLossAtrMultiple` (the hard stop's band, in ATR), `minRiskRewardRatio` (the target's floor over the stop).
23
-
24
- ```json
25
- {
26
- "kind": "strategy",
27
- "targetId": "<STRATEGY_ID>",
28
- "baseRevision": 12,
29
- "axes": {
30
- "TRADE_LEVEL_POLICY": {
31
- "minAtrPct": 0.6,
32
- "minStopLossAtrMultiple": 1.2,
33
- "maxStopLossAtrMultiple": 2.5,
34
- "minRiskRewardRatio": 1.8
35
- }
36
- }
37
- }
38
- ```
39
-
40
- ### `POSITION_MANAGEMENT`
41
-
42
- The exit policy, all thirteen members: `breakEvenEnabled`, `breakEvenTriggerR`, `trailingEnabled`,
43
- `trailingTriggerR`, `trailingGivebackPct`, `trailingBufferPct`, `timeDecayEnabled`, `timeDecayGracePeriodMinutes`,
44
- `timeDecayIntervalMinutes`, `timeDecayTightenPct`, `timeDecayMaxTightenPct`, `timeDecayStaleThresholdTpProgressPct`,
45
- `decisionInvalidationExitEnabled`. A mechanism switched off keeps its dial values: send them as read.
46
-
47
- ### `CONDITIONS`
48
-
49
- `{ conditions: [...] }` — **every** condition of the strategy, in the order the strategy read serves them, each
50
- `{ conditionKey, name, definition, verdict, required, exitSide }` exactly as read, with your one change: a condition
51
- removed, or one existing condition's `required` or `verdict` changed. A dropped condition that another condition
52
- references is refused by the assessment. Composing a new condition column is authoring work, outside a review:
53
- record the finding and name the authoring flow as the owner's step.
54
-
55
- ### `ENTRY`
56
-
57
- `{ entry: { trigger } }` for a trigger that takes no dials, or `{ entry: { trigger, levelOffsetAtrMultiple,
58
- validForBars } }` for a level trigger — the trigger the strategy read serves, or the one you propose, with every dial
59
- it honours and none it does not.
60
-
61
- ### `REPORT`
62
-
63
- `{ sections: [...] }` — **every** section of the strategy's report, in the order `get_strategy` serves them, with
64
- your one change: a platform section added or removed. Each section is a reference: a platform section is
65
- `{ kind: 'platform', sectionKey }`, its key one `list_strategy_categories` serves; a custom section is
66
- `{ kind: 'custom', sectionKey }`, its `custom:<uuid>` key as `get_strategy` serves it, and nothing else — the submit
67
- takes its title, columns, benchmark and note from the committed report, so a review never edits one. A custom key the
68
- report does not carry is refused. A report that drops a section a condition reads is refused by the assessment: keep
69
- it, or propose the condition change first. Composing a new custom section is authoring work, outside a review.
70
-
71
- ```json
72
- {
73
- "kind": "strategy",
74
- "targetId": "<STRATEGY_ID>",
75
- "baseRevision": 12,
76
- "axes": {
77
- "REPORT": {
78
- "sections": [
79
- { "kind": "platform", "sectionKey": "includePriceAction" },
80
- { "kind": "custom", "sectionKey": "custom:<SECTION_UUID>" },
81
- { "kind": "platform", "sectionKey": "includeVolume" }
82
- ]
83
- }
84
- }
85
- }
86
- ```
87
-
88
- ## Agent — `kind: 'agent'`, `targetId` the agent id
89
-
90
- Read the current values with `get_intelligence_agent`. One of four axes:
91
-
92
- ### `TRADING_CONFIG`
93
-
94
- `{ tradingConfig: { ... } }` — the agent's complete trading configuration from `get_intelligence_agent`, less the two
95
- members the server derives (`strategyTimeframe`, `regimeTimeframe`): `minAllocationUsd`, `maxDailyTrades`,
96
- `balanceThresholdUsd`, `maxLeverage`, `maxSlippageBps`, `maxConcurrentExposureUsd`, `maxCumulativeDrawdownUsd`,
97
- `maxDailyLossUsd` and `positionSizePresets` (the whole preset ladder), each inside the bounds
98
- `get_trading_config_catalog` serves. Change the one rail the finding names; send the rest as read. A rail refusal
99
- names its rail as `tradingConfigMember` on the review read.
100
-
101
- ### `BEHAVIOR`
102
-
103
- `{ behavior: { risk, outlook, conviction } }` — all three members, the ones you do not change as read: `risk` one of
104
- `CONSERVATIVE`, `MODERATE`, `AGGRESSIVE`; `outlook` one of `OPTIMIST`, `REALIST`, `PESSIMIST`; `conviction` one of
105
- `CAUTIOUS`, `MEASURED`, `BOLD`.
106
-
107
- ### `MODEL`
108
-
109
- `{ modelId }` — a `modelId` `list_approved_models` serves now. The model sets what each decision costs: cite the
110
- agent's LLM spend against its net as the finding.
111
-
112
- ### `STRATEGY_BINDING`
113
-
114
- `{ strategyId }` — an active strategy `list_strategies` serves: one the owner authored, or a library strategy. Never
115
- another user's, never an archived one. A rebind carries nothing else: the strategy and regime timeframes follow the
116
- new strategy, and each open position keeps the rules it opened under.
117
-
118
- ```json
119
- {
120
- "kind": "agent",
121
- "targetId": "<AGENT_ID>",
122
- "baseRevision": 14,
123
- "axes": { "STRATEGY_BINDING": { "strategyId": "<STRATEGY_ID>" } }
124
- }
125
- ```
126
-
127
- ## Radar deployment — `kind: 'radar-deployment'`, `targetId` the coin id
128
-
129
- ### `RULES`
130
-
131
- `{ rules: [...] }` — **every** rule slot of the coin's policy from `get_radar_deployment`, in priority order (the
132
- first is priority 1), the catch-all default excluded. Each rule is `{ agentId, minConviction, conditions }` as read,
133
- with the one rule that routes this agent changed. A rule's conditions are a non-empty list of:
134
-
135
- - `{ kind: 'regime', regimes: [...] }`, with an optional `minConviction` — the regimes the rule is on duty in, by
136
- stored value (`bull_expansion`, `bear_expansion`, `bull_ranging`, `bear_ranging`, `contraction`, `volatile`);
137
- excluding a losing regime is this list without it;
138
- - `{ kind: 'hours', hours: [...], days: [...] }` — UTC hours 0–23 and UTC weekdays 0 (Sunday) – 6, both non-empty.
139
-
140
- ```json
141
- {
142
- "kind": "radar-deployment",
143
- "targetId": "<COIN_ID>",
144
- "baseRevision": 4,
145
- "axes": {
146
- "RULES": {
147
- "rules": [
148
- {
149
- "agentId": "<AGENT_ID>",
150
- "minConviction": null,
151
- "conditions": [
152
- { "kind": "regime", "regimes": ["bull_expansion", "bull_ranging", "contraction"] }
153
- ]
154
- },
155
- {
156
- "agentId": "<OTHER_AGENT_ID>",
157
- "minConviction": 0.6,
158
- "conditions": [
159
- { "kind": "hours", "hours": [13, 14, 15, 16], "days": [1, 2, 3, 4, 5] }
160
- ]
161
- }
162
- ]
163
- }
164
- }
165
- }
166
- ```
167
-
168
- Every agent a rule names is an agent the coin already routes: placing an agent on a coin is the owner's step, and the
169
- submit refuses a proposal naming any other.
170
-
171
- ### `DEFAULT_SLOT`
172
-
173
- `{ defaultSlot: { agentId, minConviction } }` — the catch-all that takes the coin when no rule is on duty, its agent one
174
- the coin already routes; or `{ defaultSlot: null }` to remove the catch-all. Read it with `get_radar_deployment`.