@remits/remits-cli 0.1.113 → 0.1.115

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.
@@ -0,0 +1,174 @@
1
+ # Account Targeting and Resolution
2
+
3
+ > A `remits-cli` skill reference. **Load this when** you need to know which account and which repo a piece of work belongs to, why a request resolved the way it did, or whether a record is test or production data.
4
+ >
5
+ > The table of contents below carries **real line numbers** (`- L84 Some Heading`), resolved when
6
+ > this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
7
+ > only those. The entry text is the heading verbatim, so it also greps.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Account Targeting Model](#account-targeting-model)
12
+ - [Read the shape first](#read-the-shape-first)
13
+ - [Which repo does the work belong in](#which-repo-does-the-work-belong-in)
14
+ - [Repo selection rules](#repo-selection-rules)
15
+ - [Account Resolution: how a request travels the account graph](#account-resolution-how-a-request-travels-the-account-graph)
16
+ - [Seeing an account's edges](#seeing-an-accounts-edges)
17
+ - [When a subscription "doesn't work"](#when-a-subscription-doesnt-work)
18
+ - [The same block answers the non-branch questions](#the-same-block-answers-the-non-branch-questions)
19
+
20
+ ## Account Targeting Model
21
+
22
+ **Establish the account's shape before you touch anything.** Which repo you work in, which account you run
23
+ against, and where a fix belongs are answered by the account's structure — never by its name.
24
+
25
+ The account model itself — types, component inheritance, primary vs membership edges, the three independent
26
+ edge properties, the user model — is in the always-loaded `platform-overview.md` and in depth in
27
+ `features/account-management.md` (`mcp_get_guide`). What follows is only what changes **what you type**.
28
+
29
+ ### Read the shape first
30
+
31
+ `account-info.json` (in a repo) and `mcp_account_view` (remotely) both carry a `resolution` block — the one
32
+ place these facts appear. Field-by-field detail is under **`mcp_account_view`** in `tool-reference.md`. The
33
+ four that decide a CLI action:
34
+
35
+ | Read | To decide |
36
+ |---|---|
37
+ | `role` + `summary` | `OWNER` (the files here **are** its components) vs `SUBSCRIBER` (it resolves another account's components under a variant branch). Read this before you touch anything. |
38
+ | `type` | whether this repo is where the change belongs — see below |
39
+ | `resolvedDatabaseName` | where its data actually lands. **Check this first when documents are "missing".** |
40
+ | `relationships` | every link upward, primary first, each with its own `branchName` / `databaseName` / `domainName`. **More than one entry means the account can legitimately resolve differently depending on the path a request travelled** — establish which one a failing request used before comparing behavior. |
41
+
42
+ Two more, easily confused: top-level **`componentBranches`** lists the variant branches this account
43
+ **owns**, with drift and subscriber counts — check it before editing a shared component. And
44
+ `resolution.branchName` is the **repo's trunk sync branch**, *not* a component-variant branch.
45
+
46
+ ### Which repo does the work belong in
47
+
48
+ | Situation | Target |
49
+ |---|---|
50
+ | Feature, enhancement, shared behavior fix | usually the owning `PLATFORM` / `PRODUCT` account — **not** the `CLIENT` that reported it |
51
+ | Production investigation, client-specific data issue | the affected `CLIENT` account's data and runtime history |
52
+ | Both | confirm the symptom on the `CLIENT`, then move to the owning repo to change code |
53
+
54
+ ### Repo selection rules
55
+
56
+ - **Inside the target implementation repo**: make sure you are on the relevant branch and the checkout is
57
+ current before reading source or editing. Run `git fetch origin`; when the tree is clean,
58
+ `git pull --ff-only origin <branch>`; then confirm `git log origin/<branch>..<branch>` and
59
+ `git log <branch>..origin/<branch>` are both empty. A worktree can have an isolated staging workspace
60
+ and still be based on a stale commit. Then read `account-info.json` and inspect `components/` directly.
61
+ - **Inside one repo but supporting a different account**: switch to that account's indexed repo if it
62
+ exists. For tickets, prefer `implementationAccountId` / `implementationAccountName` over the reporting
63
+ `accountId` when choosing that repo; a subscriber or client often reports the symptom while the
64
+ implementation lives in an upstream `PLATFORM` / `PRODUCT` repo.
65
+ - **Only use `mcp_component_view` / `mcp_component_grep` when the correct repo is not available locally,**
66
+ or when you are deliberately comparing local source to the live DB after reading the files. They are not
67
+ the normal way to learn source on a machine with the account repo.
68
+ - **Outside any repo**: rely on the tools, and `mcp_get_guide` for the front-stage guides.
69
+ - **Never create a new local repo/directory just because a ticket references an account name.** Resolve the
70
+ account type and parent hierarchy first, and work only from an existing indexed repo unless the user
71
+ explicitly asks you to clone one.
72
+
73
+ For access questions — "who can see this client account?", "why does this user see the wrong data?" — use
74
+ `mcp_account_user_admin` first (`action:'users'`, `action:'user'`, or `action:'user_update'`). Use
75
+ `mcp_sql_query` only when you need raw join-table investigation. Remember a user's custom fields are stored
76
+ **per bound account**, so the same person can differ per account.
77
+
78
+ **Test-data flags are first-class safety evidence.** `Account.testAccount` and `User.testUser` are set by
79
+ the `account(...)` / `user(...)` factories when the execution data lane is `test` (which is how
80
+ `mcp_account_user_admin` creates); direct inserts such as `new Account(...).save()` are also flagged by the
81
+ domain `beforeInsert` hook in the test lane. Prod-data creates leave them false. Updating an existing real
82
+ account/user in test mode does not convert it into test data, though its Firestore extension-field writes
83
+ still go to the test lane. `Object.testMode` /
84
+ `Event.testMode` / `Alert.testMode` (and `testMode` inside test-lane Audit documents) identify lifecycle
85
+ rows in the test data lane. Agent-facing surfaces expose these fields:
86
+
87
+ - `account-info.json`, `account-hierarchy.json`, and `mcp_account_view`: `resolution.testAccount` for the
88
+ described account, and `testAccount` on returned hierarchy nodes.
89
+ - `mcp_account_user_admin`: `testAccount` on `hierarchy`, `account`, and `account_create` results;
90
+ `testUser` on `users` / `user` results.
91
+ - `mcp_record_listing`: top-level `dataMode` (the lane it searched — listings are **filtered** by lane,
92
+ so `totalItems:0` in the wrong lane reads exactly like "no such record"), plus `testMode` per record.
93
+ - `mcp_record_view`: `record.dataMode` (the lane read in) next to `record.testMode` (the lane the row
94
+ belongs to). This one loads **by id and does not filter**, so those two can legitimately disagree —
95
+ and when they do, that is the finding.
96
+ - `mcp_object_activity`: top-level `dataMode`, `object.testMode`, and `testMode` on Event/Alert
97
+ timeline entries.
98
+ - `mcp_event_diagnostics`: top-level `dataMode` and `result.event.testMode`.
99
+ - `remits-cli token inspect`: owner `account.testAccount` and `user.testUser`, plus a `safety` block
100
+ carrying `dataMode`, `dataModeDeclared`, and an explicit warning when the token does not declare one.
101
+ - `remits-cli whoami`: the resolved account / user / branch / lane / host tuple for the **next tool
102
+ call**. It does not describe `remits-cli test run`, which ignores the stored session lane — see below.
103
+
104
+ Two surfaces deliberately have **no** lane flags, and both mislead if you forget it:
105
+
106
+ - **`mcp_sql_query` reads MySQL directly and is lane-blind.** `object` / `event` / `alert` come back with
107
+ **both lanes mixed**, and `user` / `account` with test fixtures mixed into real records. Filter
108
+ explicitly — `test_mode = 0`, `test_user = 0`, `test_account = 0` — or use the purpose-built tool.
109
+ - **Persisted AI session rows** store no durable per-grouping `testMode` / `dataMode`. Use
110
+ `mcp_ai_session_search.dataMode` as the current execution lane only, never as proof of the historical
111
+ grouping's lane.
112
+
113
+ If any of those fields contradict the lane you intended, stop and rerun the command with an explicit
114
+ `--data-mode test` or `--data-mode prod`. Never infer prod/test from an account name, URL, branch name, or
115
+ the mere existence of a created account.
116
+
117
+ **A tool's `dataMode` input never widens the lane.** Tools that accept a `dataMode` argument clamp it
118
+ against the lane the *command* was launched with: it may narrow `prod` -> `test`, never escalate
119
+ `test` -> `prod`. So `--data-mode test --input '{"dataMode":"prod"}'` stays in **test**, and the response's
120
+ `dataMode` — not your input — is the truth. To reach prod data, pass `--data-mode prod` on the command
121
+ line. (Under MCP, the launch lane is the caller's own `dataMode` argument, which defaults to `prod`.)
122
+
123
+ ## Account Resolution: how a request travels the account graph
124
+
125
+ Component inheritance, branch variants, and where data physically lives are all decided by **how the
126
+ current request reached the executing account**. Read this before debugging *"my subscriber isn't picking
127
+ up the branch"* or *"why is this account reading the wrong collection"* — those are almost always
128
+ resolution questions, not component bugs.
129
+
130
+ > The model behind it — the linear inheritance walk, the ambiguity rule, the three orthogonal edge
131
+ > properties, and path-scoped storage namespaces — is in **`platform-overview.md` → *Account Structure***,
132
+ > which is already loaded. What follows is how to *observe* it from the CLI.
133
+
134
+ **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id`, stamped onto
135
+ the `Object` / `Event` / `Alert` / `ObjectLog` records a run creates, so async workers re-resolve on the
136
+ same branch. A **null** anchor means "walk the structural chain". Entry points that already know the
137
+ branch supply an anchor; from the CLI you supply it explicitly with `--as-account`, `--variant-branch`, or
138
+ by using the edge's own host.
139
+
140
+ ### Seeing an account's edges
141
+
142
+ ```bash
143
+ remits-cli tool --name mcp_account_view --input '{"accountId": 101}' --data-mode prod
144
+ # then read: resolution.relationships, resolution.resolvedDatabaseName, resolution.componentBranch
145
+ ```
146
+
147
+ `resolution.relationships` returns one entry per structural link, primary first, each carrying its own
148
+ `branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so
149
+ this block is the **only** place that answers "how many parents does this account really have, and which
150
+ link carries what?"
151
+
152
+ ### When a subscription "doesn't work"
153
+
154
+ 1. Does the account have a primary parent, or is it membership-only? Count `resolution.relationships` and
155
+ see which is `primary: true`. (Membership-only **and** several edges ⇒ ambiguous ⇒ trunk, by design.)
156
+ 2. Which **edge** carries the `branchName`? `remits-cli components branch <name> --subscribers` prints
157
+ `via primary|membership edge -> parent N`.
158
+ 3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
159
+ account's own edge selects the branch, exactly as production would.
160
+ 4. Check the resolved layer, not the source text: `testComponentSource`, or the `variant:<id>:<hash>`
161
+ compile signature (`branch-variants.md` → *Diagnosing a variant*).
162
+
163
+ ### The same block answers the non-branch questions
164
+
165
+ - *"Why are this account's documents not where I expect?"* → compare `databaseName` vs
166
+ `resolvedDatabaseName`, and check for a `databaseName` on one of the `relationships` edges — it applies
167
+ to everything below that edge.
168
+ - *"Why does this hostname land on the wrong account?"* → compare `domainName` vs `resolvedDomainName` and
169
+ the edge `domainName`. An **edge** host wins over the account's own, and additionally supplies the path
170
+ travelled — which is what makes that edge's branch variants apply.
171
+
172
+ **Users are not part of this graph** (see `platform-overview.md`). Use `mcp_account_user_admin`
173
+ (`action:'users'`, `action:'user'`, or `action:'user_update'`) for user membership and account-scoped user
174
+ fields. Drop to `mcp_sql_query` against `user` / `user_account` only for raw join-table evidence.
@@ -0,0 +1,222 @@
1
+ # Agent Sessions, Service, and Control Center
2
+
3
+ > A `remits-cli` skill reference. **Load this when** you are registering this session as an agent, diagnosing why a routed ticket never started, or a question involves the background service, the control center, or multiple authenticated sessions.
4
+ >
5
+ > The table of contents below carries **real line numbers** (`- L84 Some Heading`), resolved when
6
+ > this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
7
+ > only those. The entry text is the heading verbatim, so it also greps.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Registering This Session As An Agent](#registering-this-session-as-an-agent)
12
+ - [What registration actually does](#what-registration-actually-does)
13
+ - [What `serve` adds](#what-serve-adds)
14
+ - [Resolving which agent a command means](#resolving-which-agent-a-command-means)
15
+ - [Which agent gets a ticket](#which-agent-gets-a-ticket)
16
+ - [Why a routed ticket might not have started](#why-a-routed-ticket-might-not-have-started)
17
+ - [Background Service and Control Center](#background-service-and-control-center)
18
+ - [Control Center](#control-center)
19
+ - [Configuring the Preferred Agent](#configuring-the-preferred-agent)
20
+ - [Multi-Session Support](#multi-session-support)
21
+
22
+ ## Registering This Session As An Agent
23
+
24
+ `remits-cli agent` is how a terminal session makes itself available to work support tickets. It is
25
+ independent of everything else — no background service is required, and every tab is its own agent.
26
+
27
+ ```bash
28
+ remits-cli agent serve # routable AND autonomously working tickets — the usual choice
29
+ remits-cli agent workers # what this session is running right now
30
+ remits-cli agent list # who else is available across the accounts you cover
31
+ remits-cli agent release # stop serving and stop receiving tickets right now
32
+
33
+ remits-cli agent register # presence ONLY — nothing starts on its own
34
+ remits-cli agent work --wait 300 # ask for tickets routed here (manual loop)
35
+ remits-cli agent status --state working --ticket 22454 --activity "reproducing"
36
+ ```
37
+
38
+ `serve` is `register` plus a supervisor. Everything below about registration applies to both.
39
+
40
+ **Defaults exist so the user does not have to type them.** With no flags, `register` targets the
41
+ production platform (`REMITS_BASE_URL` when set) and the **prod** lane, and claims every account repo
42
+ indexed on this machine. That is a deliberate exception to the CLI's test-first default: registering
43
+ presence mutates no business data, and a test-lane agent is silently useless for support — it appears
44
+ online and can never be routed a production ticket. Every command after `register` reuses the
45
+ platform, lane and identity it registered with.
46
+
47
+ ### What registration actually does
48
+
49
+ - Mints an `agentId` for this session and tells the platform which account repos this machine has
50
+ checked out — read from the machine-wide index (`~/.remits-cli/account-repos.json`), which is why
51
+ the working directory does not matter. The platform **verifies** those claims against what your user
52
+ can access and returns the ones it refused, so "I registered but never get tickets for account 52"
53
+ is answerable from the registration output alone.
54
+ - Starts a small detached heartbeat process **anchored to the agent process that owns this terminal**
55
+ (it walks up the process tree to find `claude`/`codex`/`gemini`, then an interactive shell). When
56
+ that process ends, the heartbeat ends and the session stops being routable within a couple of
57
+ minutes. Closing the tab is a valid way to go offline; `agent release` just makes it immediate.
58
+ - Registers in the session's **data lane**. A `test`-lane run only ever reaches a `test`-lane agent,
59
+ which is what keeps fixture traffic away from a production terminal.
60
+ - Declares **capacity** — how many tickets this session can genuinely run at once (`--max-concurrent`,
61
+ default 1). The router will not exceed it.
62
+
63
+ ### What `serve` adds
64
+
65
+ The same detached process that maintains presence also polls for work. On each poll it publishes the
66
+ tickets it currently has running, renews their claims, and takes at most enough new ones to fill its
67
+ capacity. For each new ticket it launches a headless worker:
68
+
69
+ | | headless invocation | edit mode | investigate mode |
70
+ |---|---|---|---|
71
+ | `codex` | `codex exec --cd <repo>` | `--sandbox workspace-write` | `workspace-write with investigation-only brief and no edit lease` |
72
+ | `claude` | `claude -p --add-dir <repo>` | `--permission-mode acceptEdits` | `--permission-mode plan` |
73
+ | `gemini` | `gemini -p` in `<repo>` | `--approval-mode auto_edit` | `--approval-mode plan` |
74
+
75
+ **Which of the three it launches:** `--worker-agent` if you pass it, otherwise **whatever agent this
76
+ session is** (detected from the process the agent anchored to — which finds nothing in a plain tab),
77
+ otherwise `~/.remits-cli/config.json`'s `agent` (set it with `remits-cli config set --agent NAME`),
78
+ otherwise `claude`. **It prints which it chose and why on startup**, because in the intended
79
+ plain-tab setup the choice comes from a config file you may have set months ago. So running `agent serve` inside a Codex tab gives you Codex workers
80
+ without a flag, and the label the operator sees (`codex@repo`) is resolved the same way — the label
81
+ and the worker are one answer, not two.
82
+
83
+ The brief always arrives on **stdin**, never in argv — it is a page of prose and argv has a hard
84
+ length limit, so an argv brief would fail on exactly the detailed tickets that most need the detail.
85
+ The brief itself is generated by the platform, so all three worker types are told the same thing.
86
+ In `--mode investigate`, the supervisor still claims tickets and may update them with findings, but it
87
+ does **not** acquire or renew the repository edit lease. The brief says the run was launched for
88
+ investigation only and forbids file edits, component staging, commits, and manual lease escalation.
89
+
90
+ **Following a run.** `remits-cli start`'s control center lists every worker; **Follow live** streams
91
+ that run's transcript into the page, rendering commands with their exit codes, file edits, and the
92
+ agent's own messages as distinct things. It keeps working after the worker exits — a finished run is
93
+ usually the one worth reading. For a terminal instead, each worker prints a `tail -f` for its log in
94
+ `~/.remits-cli/workers/`.
95
+
96
+ **There is no terminal to attach to, by design.** A worker is spawned with pipes and runs
97
+ non-interactively (`codex exec`, `claude -p`), so there is no tty and no prompt to type at — the
98
+ whole point is that it needs no supervision. Following the transcript is the way to watch, and it is
99
+ strictly better than a terminal would be: the output is structured, so it can be read as events
100
+ rather than scraped back out of ANSI text.
101
+
102
+ **No worker ever commits.** The brief forbids `git commit`/`git push` and nothing in the supervisor
103
+ runs git — a worker leaves its changes in the working tree and says so on the ticket, for a human to
104
+ review.
105
+
106
+ ### Resolving which agent a command means
107
+
108
+ Order: `--agent-id`, then `REMITS_AGENT_ID`, then the single agent registered for this working
109
+ directory. With several registered and no way to tell them apart, the command **fails and lists the
110
+ candidates** rather than guessing — routing work to the wrong tab is silent, and a message is not.
111
+
112
+ ### Which agent gets a ticket
113
+
114
+ The platform walks a ladder — the ticket's implementation account first (a defect seen on a client is
115
+ usually fixed in the platform repo above it), then the ticket's own account, then its
116
+ `PLATFORM`/`PRODUCT` ancestors — and takes the first account with an available agent. Among those it
117
+ prefers an **idle** agent, then the one that has waited longest, so work spreads across your tabs
118
+ instead of piling onto whichever one most recently ran a command. A `paused` agent stays visible and
119
+ is never routed to, and so is one **at capacity** — those are different facts and stay separate:
120
+ `paused` means a human stopped this session, at-capacity means its workers are all busy.
121
+
122
+ ### Why a routed ticket might not have started
123
+
124
+ In order of likelihood:
125
+
126
+ 1. **The session registered but never served.** `agent register` starts nothing. `agent workers`
127
+ showing none while a ticket is routed here is this.
128
+ 2. **At capacity.** Check `agent workers`; the ticket starts when one finishes.
129
+ 3. **Another session is running it.** A claim held by a different agent blocks it until that claim is
130
+ dropped or expires.
131
+ 4. **No local checkout.** The worker still starts, and its brief tells it to locate the repo rather
132
+ than guess — but if the account genuinely is not on this machine it will say so and stop.
133
+ 5. **It failed twice already** and was released back to the queue. The transcripts in
134
+ `~/.remits-cli/workers/` say why.
135
+
136
+ ## Background Service and Control Center
137
+
138
+ `remits-cli start` runs an optional background service for the **human** watching:
139
+
140
+ - Maintains persistent WebSocket connections to the Remits platform
141
+ - Hosts a localhost browser **control center** (typically `http://127.0.0.1:8787/`)
142
+
143
+ ```bash
144
+ remits-cli start
145
+ remits-cli start --foreground true
146
+ remits-cli status
147
+ remits-cli whoami
148
+ remits-cli stop
149
+ ```
150
+
151
+ Compatibility aliases still exist:
152
+
153
+ ```bash
154
+ remits-cli listen
155
+ remits-cli listen status
156
+ remits-cli listen stop
157
+ ```
158
+
159
+ **The service is not part of ticket delivery.** Agents register and collect work on their own, so the
160
+ dashboard being down never stops a ticket reaching an agent. What the service adds is visibility: it
161
+ scans for `account-info.json` files to rebuild the repo index, keeps one WebSocket per platform URL,
162
+ and holds a PID lock (`~/.remits-cli/listener.pid`) so only one runs per machine.
163
+
164
+ ### Control Center
165
+
166
+ The control center shows, in one browser view:
167
+ - which agent sessions are registered, what each is doing right now, and its recent activity
168
+ - live and recent worker runs, with historical transcript follow/stop controls keyed to the exact run
169
+ - the open support-ticket queue, and which agent each ticket is routed to
170
+ - a control to route a ticket at a specific available agent
171
+ - websocket connection and topic health
172
+ - the discovered account repo index and the important global/per-repo files
173
+
174
+ **What it shows is what the agents see.** The ticket queue comes from the platform's own generic
175
+ endpoint, not from any product's tool, so the control center reads the same on every Remits platform
176
+ and never implies a workflow that a particular product does not have. Product-specific views belong
177
+ in that product's Embeddables.
178
+
179
+ When a user asks a broad or vague question about remits-cli behavior, prefer reasoning from the
180
+ control center state before spelunking individual files.
181
+
182
+ ### Configuring the Preferred Agent
183
+
184
+ ```bash
185
+ remits-cli config # Show current config
186
+ remits-cli config set --agent claude # Use Claude Code (default)
187
+ remits-cli config set --agent codex # Use OpenAI Codex CLI
188
+ remits-cli config set --agent gemini # Use Gemini CLI
189
+ ```
190
+
191
+ The agent preference is stored in `~/.remits-cli/config.json` and applies globally.
192
+
193
+ ## Multi-Session Support
194
+
195
+ The CLI supports multiple authenticated sessions simultaneously. Sessions are stored in `~/.remits-cli/sessions.json` as a map keyed by `accountId + dataMode + baseUrl`, so the same account can stay authenticated against both localhost and production without one session overwriting the other. When you run any command from an account repo, the CLI automatically resolves the best matching session based on the `account-info.json` in that directory and any `--base-url` or `--data-mode` flags provided. If the same account is authenticated against multiple hosts and you omit `--base-url`, the CLI may legitimately choose either localhost or a deployed host depending on the best session match, so agents should treat `--base-url` as mandatory whenever host matters.
196
+
197
+ ```bash
198
+ # Authenticate for an account (run from its repo, or pass --account-id)
199
+ remits-cli auth
200
+ remits-cli auth --account-id 42
201
+ remits-cli auth --account-id 42 --base-url http://localhost:8080
202
+
203
+ # List all active sessions
204
+ remits-cli sessions list
205
+
206
+ # Remove a session (removes all sessions for the account, or narrow with --base-url / --data-mode)
207
+ remits-cli sessions remove --account-id 42
208
+ remits-cli sessions remove --account-id 42 --base-url http://localhost:8080
209
+ ```
210
+
211
+ You can work in multiple account repos simultaneously across different terminal windows — each uses its own session. You can also be authenticated against different base URLs (e.g., localhost for development and production) for the same account at the same time.
212
+
213
+ Use `remits-cli whoami` when you need a compact proof of the active target before a sensitive operation.
214
+ It prints the resolved Account ID, User ID, current git branch, data mode, and base URL. `remits-cli status`
215
+ prints the same session tuple after the service/dashboard status. Pass `--base-url`, `--account-id`, and
216
+ `--data-mode` when host or lane matters; do not infer those values from the repo directory or account name.
217
+
218
+ **The reported data mode describes the next `tool` / `tools` / `token` call, not `test run`.** It falls back
219
+ to the stored session lane, whereas `remits-cli test run` deliberately ignores that and defaults to `test`
220
+ unless you pass `--data-mode prod` explicitly. So `whoami` can read `prod` while a test run goes to the test
221
+ lane — which is the safe direction, but not the one you would predict from the output alone. `whoami` says
222
+ so in its own output; when a test run genuinely needs prod data, pass the flag.