@remits/remits-cli 0.1.113 → 0.1.114

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.
@@ -5,3566 +5,157 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
5
5
 
6
6
  # remits-cli
7
7
 
8
- ## Table of Contents
8
+ `remits-cli` is how a local agent talks to the Remits platform: it stages component source for
9
+ server-side execution, runs Test components, mints embeddable browser tokens, reconciles a repo into
10
+ the live component database, calls the server-side investigation tools, and carries the support-ticket
11
+ lifecycle.
9
12
 
10
- - [Account Targeting Model](#account-targeting-model)
11
- - [Read the shape first](#read-the-shape-first)
12
- - [Which repo does the work belong in](#which-repo-does-the-work-belong-in)
13
- - [Repo selection rules](#repo-selection-rules)
14
- - [Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)](#component-integrity-rules-repo--database-reconciliation-read-before-any-synccommit)
15
- - [How the platform reconciles the repo into the database (the mechanism you must understand)](#how-the-platform-reconciles-the-repo-into-the-database-the-mechanism-you-must-understand)
16
- - [The surfaces and their intended behavior](#the-surfaces-and-their-intended-behavior)
17
- - [Intended workflows](#intended-workflows)
18
- - [Pre-sync safety check (confirm ALL before `components sync` or `components commit`)](#pre-sync-safety-check-confirm-all-before-components-sync-or-components-commit)
19
- - [If something looks wrong — stop, don't paper over](#if-something-looks-wrong--stop-dont-paper-over)
20
- - [Required Local Index Reads](#required-local-index-reads)
21
- - [Big Picture: How remits-cli State Is Organized](#big-picture-how-remits-cli-state-is-organized)
22
- - [Support Ticket Mental Model](#support-ticket-mental-model)
23
- - [You are an agent, and you register yourself](#you-are-an-agent-and-you-register-yourself)
24
- - [Autonomous: one ticket, one process](#autonomous-one-ticket-one-process)
25
- - [If you are the worker](#if-you-are-the-worker)
26
- - [Before you edit anything: where you are, and whether you may](#before-you-edit-anything-where-you-are-and-whether-you-may)
27
- - [Your account's process is binding, and it is already in your brief](#your-accounts-process-is-binding-and-it-is-already-in-your-brief)
28
- - [Seeing the queue as a human does](#seeing-the-queue-as-a-human-does)
29
- - [Agent components are workers too](#agent-components-are-workers-too)
30
- - [Moving a ticket through its lifecycle](#moving-a-ticket-through-its-lifecycle)
31
- - [When a worker needs a decision from a human](#when-a-worker-needs-a-decision-from-a-human)
32
- - [The manual loop](#the-manual-loop)
33
- - [Efficiency Rules](#efficiency-rules)
34
- - [Account Repository Index](#account-repository-index)
35
- - [Two Workflows](#two-workflows)
36
- - [Test Mode vs Prod Mode](#test-mode-vs-prod-mode)
37
- - [The Investigation Model](#the-investigation-model)
38
- - [Which tool reads which record](#which-tool-reads-which-record)
39
- - [Correlation keys](#correlation-keys)
40
- - [Reading a record's `content` — persisted context, not a memory dump](#reading-a-records-content--persisted-context-not-a-memory-dump)
41
- - [HTTP audits](#http-audits)
42
- - [AI activity](#ai-activity)
43
- - [Node Reference Table](#node-reference-table)
44
- - [Runtime node and `localMode`](#runtime-node-and-localmode)
45
- - [Getting Started](#getting-started)
46
- - [Authentication](#authentication)
47
- - [Host vs Data Mode](#host-vs-data-mode)
48
- - [Tool Execution Lifecycle](#tool-execution-lifecycle)
49
- - [Data Mode](#data-mode)
50
- - [Development Workflow](#development-workflow)
51
- - [The Golden Rule: Writing Code Is Not Finishing the Job](#the-golden-rule-writing-code-is-not-finishing-the-job)
52
- - [The Development Fast Loop](#the-development-fast-loop)
53
- - [Step 1: Understand the Request](#step-1-understand-the-request)
54
- - [Step 2: Make the Change](#step-2-make-the-change)
55
- - [Step 3: Stage to Platform](#step-3-stage-to-platform)
56
- - [Step 4: Verify the Change](#step-4-verify-the-change)
57
- - [Step 5: Iterate If Needed](#step-5-iterate-if-needed)
58
- - [Step 6: Update Documentation](#step-6-update-documentation)
59
- - [Temporary Experiment Workflow](#temporary-experiment-workflow)
60
- - [Step 7: Commit and Durable Sync](#step-7-commit-and-durable-sync)
61
- - [Step 8: Close the Ticket](#step-8-close-the-ticket)
62
- - [User Confirmation Preferences](#user-confirmation-preferences)
63
- - [Component Resolution: Staging Cache vs DB (which "version" actually runs)](#component-resolution-staging-cache-vs-db-which-version-actually-runs)
64
- - [The three source layers + the compile cache](#the-three-source-layers--the-compile-cache)
65
- - [Staging cache key format](#staging-cache-key-format)
66
- - [How the platform picks staged vs DB (the compile signature)](#how-the-platform-picks-staged-vs-db-the-compile-signature)
67
- - [When staged overrides apply](#when-staged-overrides-apply)
68
- - [Diagnosing which version is in play](#diagnosing-which-version-is-in-play)
69
- - [Stage / sync / clear with remits-cli](#stage--sync--clear-with-remits-cli)
70
- - [Stale after sync / commit (the in-memory compile cache)](#stale-after-sync--commit-the-in-memory-compile-cache)
71
- - [Account Resolution: how a request travels the account graph](#account-resolution-how-a-request-travels-the-account-graph)
72
- - [Seeing an account's edges](#seeing-an-accounts-edges)
73
- - [When a subscription "doesn't work"](#when-a-subscription-doesnt-work)
74
- - [The same block answers the non-branch questions](#the-same-block-answers-the-non-branch-questions)
75
- - [Branched Component Variants (per-account component overrides)](#branched-component-variants-per-account-component-overrides)
76
- - [Which world does your working tree resolve? (read this before you run anything)](#which-world-does-your-working-tree-resolve-read-this-before-you-run-anything)
77
- - [Two levers, two different questions](#two-levers-two-different-questions)
78
- - [The SDLC is identical on a variant branch](#the-sdlc-is-identical-on-a-variant-branch)
79
- - [Subscribing, unsubscribing, retiring](#subscribing-unsubscribing-retiring)
80
- - [Danger profile on a variant branch (different, not absent)](#danger-profile-on-a-variant-branch-different-not-absent)
81
- - [Inspecting branches and drift](#inspecting-branches-and-drift)
82
- - [Diagnosing a variant](#diagnosing-a-variant)
83
- - [Production Support Workflow](#production-support-workflow)
84
- - [Investigation Strategy](#investigation-strategy)
85
- - [Presenting Findings](#presenting-findings)
86
- - [Verifying a Production Issue Fix](#verifying-a-production-issue-fix)
87
- - [Tool Reference](#tool-reference)
88
- - [Execute a Tool](#execute-a-tool)
89
- - [`mcp_account_view`](#mcp_account_view)
90
- - [`mcp_account_user_admin`](#mcp_account_user_admin)
91
- - [`mcp_firestore_search`](#mcp_firestore_search)
92
- - [`mcp_firestore_patch`](#mcp_firestore_patch)
93
- - [`mcp_object_activity`](#mcp_object_activity)
94
- - [`mcp_record_listing`](#mcp_record_listing)
95
- - [`mcp_record_view`](#mcp_record_view)
96
- - [`mcp_ai_session_search`](#mcp_ai_session_search)
97
- - [`mcp_run_action`](#mcp_run_action)
98
- - [Stopping a run — `controlAction:'interrupt'`](#stopping-a-run--controlactioninterrupt)
99
- - [`mcp_run_agent`](#mcp_run_agent)
100
- - [Controlling a live agent — `pause` / `unpause` / `interrupt`](#controlling-a-live-agent--pause--unpause--interrupt)
101
- - [`mcp_system_logs`](#mcp_system_logs)
102
- - [`mcp_user_activity`](#mcp_user_activity)
103
- - [`mcp_performance_trace`](#mcp_performance_trace)
104
- - [`mcp_event_diagnostics`](#mcp_event_diagnostics)
105
- - [`mcp_component_view`](#mcp_component_view)
106
- - [`mcp_component_grep`](#mcp_component_grep)
107
- - [`mcp_support_ticket`](#mcp_support_ticket)
108
- - [Component branches](#component-branches)
109
- - [`mcp_cache`](#mcp_cache)
110
- - [`mcp_sql_query`](#mcp_sql_query)
111
- - [`mcp_index_search`](#mcp_index_search)
112
- - [`mcp_get_guide`](#mcp_get_guide)
113
- - [`mcp_test_fixture`](#mcp_test_fixture)
114
- - [`mcp_playwright_replay`](#mcp_playwright_replay)
115
- - [`mcp_jvm_spike_triage`](#mcp_jvm_spike_triage)
116
- - [`mcp_support_ticket_queue`](#mcp_support_ticket_queue)
117
- - [Multi-Session Support](#multi-session-support)
118
- - [Registering This Session As An Agent](#registering-this-session-as-an-agent)
119
- - [What registration actually does](#what-registration-actually-does)
120
- - [What `serve` adds](#what-serve-adds)
121
- - [Resolving which agent a command means](#resolving-which-agent-a-command-means)
122
- - [Which agent gets a ticket](#which-agent-gets-a-ticket)
123
- - [Why a routed ticket might not have started](#why-a-routed-ticket-might-not-have-started)
124
- - [Background Service and Control Center](#background-service-and-control-center)
125
- - [Control Center](#control-center)
126
- - [Configuring the Preferred Agent](#configuring-the-preferred-agent)
127
- - [Local State Files](#local-state-files)
128
- - [Command Reference](#command-reference)
129
- - [Prod banners and retryable failures](#prod-banners-and-retryable-failures)
130
- - [Troubleshooting](#troubleshooting)
131
- - [When Something Doesn't Work as Expected](#when-something-doesnt-work-as-expected)
132
- - [Escalation Bundle (tooling/operational issue)](#escalation-bundle-toolingoperational-issue)
13
+ This file is the **index and the rules**. It is deliberately short so it can be loaded in full every
14
+ session. Everything else — the mechanics, the failure modes, and the exact command and tool surface —
15
+ lives in the reference files listed below, which ship with the CLI and are read on demand.
133
16
 
134
- ## Account Targeting Model
17
+ ## How to read this skill
135
18
 
136
- **Establish the account's shape before you touch anything.** Which repo you work in, which account you run
137
- against, and where a fix belongs are answered by the account's structure — never by its name.
19
+ **The reference files are here:**
138
20
 
139
- The account model itself — types, component inheritance, primary vs membership edges, the three independent
140
- edge properties, the user model — is in the always-loaded `platform-overview.md` and in depth in
141
- `features/account-management.md` (`mcp_get_guide`). What follows is only what changes **what you type**.
21
+ `{{SKILL_REFERENCES_DIR}}`
142
22
 
143
- ### Read the shape first
23
+ Every reference named below is a file in that directory. Two mechanics make loading one cheap, and you
24
+ are expected to use both:
144
25
 
145
- `account-info.json` (in a repo) and `mcp_account_view` (remotely) both carry a `resolution` block — the one
146
- place these facts appear. Field-by-field detail is under **`mcp_account_view`** in the Tool Reference. The
147
- four that decide a CLI action:
26
+ 1. **Each reference opens with a table of contents carrying real line numbers** — `- L412 Diagnosing a
27
+ variant`. The numbers are resolved when the CLI installs the file, so they are never stale, and each
28
+ entry is the heading verbatim, so it also greps.
29
+ 2. **Therefore: read the head of the reference, choose your sections, then offset-read only those.**
30
+ `tool-reference.md` alone is around a thousand lines. Loading a whole file because you needed forty
31
+ lines of it is the most common way an agent runs out of room to do the actual work.
148
32
 
149
- | Read | To decide |
150
- |---|---|
151
- | `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. |
152
- | `type` | whether this repo is where the change belongs — see below |
153
- | `resolvedDatabaseName` | where its data actually lands. **Check this first when documents are "missing".** |
154
- | `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. |
33
+ **Naming a reference is not reading it.** These files exist because the behavior they describe is not
34
+ guessable from the command names. An agent that skips one does not fail loudly — it proceeds on a
35
+ plausible assumption and produces work that looks finished and is not. When a task is governed by a
36
+ reference below, load it before you act, not after something surprises you.
155
37
 
156
- Two more, easily confused: top-level **`componentBranches`** lists the variant branches this account
157
- **owns**, with drift and subscriber counts — check it before editing a shared component. And
158
- `resolution.branchName` is the **repo's trunk sync branch**, *not* a component-variant branch.
38
+ ## Which reference, and when
159
39
 
160
- ### Which repo does the work belong in
161
-
162
- | Situation | Target |
163
- |---|---|
164
- | Feature, enhancement, shared behavior fix | usually the owning `PLATFORM` / `PRODUCT` account — **not** the `CLIENT` that reported it |
165
- | Production investigation, client-specific data issue | the affected `CLIENT` account's data and runtime history |
166
- | Both | confirm the symptom on the `CLIENT`, then move to the owning repo to change code |
167
-
168
- ### Repo selection rules
169
-
170
- - **Inside the target implementation repo**: read `account-info.json` and inspect `components/` directly.
171
- - **Inside one repo but supporting a different account**: switch to that account's repo if it exists;
172
- otherwise use `mcp_account_view` / `mcp_component_view` / `mcp_component_grep`.
173
- - **Outside any repo**: rely on the tools, and `mcp_get_guide` for the front-stage guides.
174
- - **Never create a new local repo/directory just because a ticket references an account name.** Resolve the
175
- account type and parent hierarchy first, and work only from an existing indexed repo unless the user
176
- explicitly asks you to clone one.
177
-
178
- For access questions — "who can see this client account?", "why does this user see the wrong data?" — use
179
- `mcp_account_user_admin` first (`action:'users'`, `action:'user'`, or `action:'user_update'`). Use
180
- `mcp_sql_query` only when you need raw join-table investigation. Remember a user's custom fields are stored
181
- **per bound account**, so the same person can differ per account.
182
-
183
- **Test-data flags are first-class safety evidence.** `Account.testAccount` and `User.testUser` are set by
184
- the `account(...)` / `user(...)` factories when the execution data lane is `test` (which is how
185
- `mcp_account_user_admin` creates); direct inserts such as `new Account(...).save()` are also flagged by the
186
- domain `beforeInsert` hook in the test lane. Prod-data creates leave them false. Updating an existing real
187
- account/user in test mode does not convert it into test data, though its Firestore extension-field writes
188
- still go to the test lane. `Object.testMode` /
189
- `Event.testMode` / `Alert.testMode` (and `testMode` inside test-lane Audit documents) identify lifecycle
190
- rows in the test data lane. Agent-facing surfaces expose these fields:
191
-
192
- - `account-info.json`, `account-hierarchy.json`, and `mcp_account_view`: `resolution.testAccount` for the
193
- described account, and `testAccount` on returned hierarchy nodes.
194
- - `mcp_account_user_admin`: `testAccount` on `hierarchy`, `account`, and `account_create` results;
195
- `testUser` on `users` / `user` results.
196
- - `mcp_record_listing`: top-level `dataMode` (the lane it searched — listings are **filtered** by lane,
197
- so `totalItems:0` in the wrong lane reads exactly like "no such record"), plus `testMode` per record.
198
- - `mcp_record_view`: `record.dataMode` (the lane read in) next to `record.testMode` (the lane the row
199
- belongs to). This one loads **by id and does not filter**, so those two can legitimately disagree —
200
- and when they do, that is the finding.
201
- - `mcp_object_activity`: top-level `dataMode`, `object.testMode`, and `testMode` on Event/Alert
202
- timeline entries.
203
- - `mcp_event_diagnostics`: top-level `dataMode` and `result.event.testMode`.
204
- - `remits-cli token inspect`: owner `account.testAccount` and `user.testUser`, plus a `safety` block
205
- carrying `dataMode`, `dataModeDeclared`, and an explicit warning when the token does not declare one.
206
- - `remits-cli whoami`: the resolved account / user / branch / lane / host tuple for the **next tool
207
- call**. It does not describe `remits-cli test run`, which ignores the stored session lane — see below.
208
-
209
- Two surfaces deliberately have **no** lane flags, and both mislead if you forget it:
210
-
211
- - **`mcp_sql_query` reads MySQL directly and is lane-blind.** `object` / `event` / `alert` come back with
212
- **both lanes mixed**, and `user` / `account` with test fixtures mixed into real records. Filter
213
- explicitly — `test_mode = 0`, `test_user = 0`, `test_account = 0` — or use the purpose-built tool.
214
- - **Persisted AI session rows** store no durable per-grouping `testMode` / `dataMode`. Use
215
- `mcp_ai_session_search.dataMode` as the current execution lane only, never as proof of the historical
216
- grouping's lane.
217
-
218
- If any of those fields contradict the lane you intended, stop and rerun the command with an explicit
219
- `--data-mode test` or `--data-mode prod`. Never infer prod/test from an account name, URL, branch name, or
220
- the mere existence of a created account.
221
-
222
- **A tool's `dataMode` input never widens the lane.** Tools that accept a `dataMode` argument clamp it
223
- against the lane the *command* was launched with: it may narrow `prod` -> `test`, never escalate
224
- `test` -> `prod`. So `--data-mode test --input '{"dataMode":"prod"}'` stays in **test**, and the response's
225
- `dataMode` — not your input — is the truth. To reach prod data, pass `--data-mode prod` on the command
226
- line. (Under MCP, the launch lane is the caller's own `dataMode` argument, which defaults to `prod`.)
227
-
228
- Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens,
229
- committing work, and diagnosing production issues.
230
-
231
- ## Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
232
-
233
- Component source-of-truth mistakes are the highest-impact failure in remits-cli. A repo/DB mismatch can
234
- hard-delete live components, spawn duplicates, renumber files, or leave the database and repo describing
235
- different implementations. These rules override the normal fast loop whenever they conflict.
236
-
237
- ### How the platform reconciles the repo into the database (the mechanism you must understand)
238
-
239
- > **First, check which branch you are on.** Everything in this section describes a **TRUNK** sync. Syncing
240
- > from a **non-trunk branch** is a different, much safer operation — it writes `ComponentVariant` overlays
241
- > only and can never create, delete, rename, or overwrite a live component row. Run
242
- > `remits-cli components status` to see which mode your working tree is in, and read
243
- > "Branched Component Variants" for the variant-branch rules (which have their own hazard: tombstones).
244
-
245
- `remits-cli components sync` (and the sync phase of `components commit`) calls
246
- `GitHubClient.syncFromRepository`. On the account's **trunk branch** it is a **full two-way reconcile in
247
- which the GitHub remote is authoritative over the database.** It reads the **remote repo ZIP — not your
248
- local working tree** — and:
249
-
250
- - **Match / update:** each component is matched to a DB row by the **numeric id prefix of its files**
251
- (`58_x.groovy` → component 58), not by name. Matching files overwrite that component's DB fields
252
- (source/schema/html/etc.).
253
- - **Rename:** changing the component's `name:` in `.meta.yml` is supported as a normal update **as long as
254
- the numeric id prefix stays the same**. On staging, the CLI updates the id/name cache aliases and prunes
255
- stale old-name aliases for that id. On trunk sync, the platform canonicalizes the repo filenames to the
256
- current component name (`58_OldName.groovy` with `name: New Name` becomes `58_NewName.groovy`, plus
257
- sidecars) and reports those moves in `syncResults.renamed`.
258
- - **Create:** a file whose id prefix is **not** a live component on the account — including any `new_*` file —
259
- is created as a **brand-new DB row with a fresh server-assigned id**, and the platform renames the repo files
260
- to that id (the `Rename X→Y after component creation` / `Delete old file` commits).
261
- - **Delete:** after the create/update pass, **any live DB component whose id has no matching repo file is
262
- hard-deleted** (`deleteMissingComponentsFor`), in reverse-dependency order. This runs only when the ZIP
263
- "looks healthy" (contains `account-info.json` or `README.md`). Exempt from deletion: `auxiliary` components,
264
- README-purpose prompts, and AGENT-purpose prompts.
265
-
266
- > ### `auxiliary: true` opts a component OUT of the repo entirely — in BOTH directions
267
- >
268
- > This is a silent trap, so know it before you author a sidecar. `auxiliary: true` does not merely
269
- > "de-emphasize" a component:
270
- >
271
- > - **Repo → platform:** the sync **skips the file outright**. A `new_*` component whose `.meta.yml` says
272
- > `auxiliary: true` is never created, so it **never gets a real id** and the file is never renamed. It
273
- > looks like the sync silently ignored your work — because it did.
274
- > - **Platform → repo:** the component's save hooks skip pushing source to GitHub, and repo initialization
275
- > omits it.
276
- > - It is also excluded from `getInformation()` (so AI agents do not discover it) and exempt from the
277
- > deletion pass above.
278
- >
279
- > **Use `auxiliary: true` only for genuinely throwaway components** — ad-hoc reports, experiments, and the
280
- > ephemeral fixtures a Test creates and deletes at runtime (those are created in Groovy with
281
- > `auxiliary: true` and must never touch the repo).
282
- >
283
- > **Use `auxiliary: false` for anything durable** — above all a Test suite that is a regression guard. If you
284
- > want it versioned in git, addressable by a stable id, or discoverable by another agent, it is not
285
- > auxiliary. Symptom to recognize: *"I added `new_Foo.groovy`, synced, and it neither appeared on the account
286
- > nor got renamed."* Check the sidecar's `auxiliary` flag first.
287
-
288
- **The single most important consequence:** the id in a component's **filename is load-bearing**. If a file's id
289
- no longer matches its DB row (a renumber or move across ids), the next sync will **create a duplicate at the
290
- new id and hard-delete the original at the old id**. If a component's files are missing from the repo at sync
291
- time, that component is **hard-deleted from the DB**. This is exactly how a prior session deleted live schemas
292
- and an embeddable.
293
-
294
- **Therefore: never renumber, rename-across-ids, or remove component files as a side effect.** Name-only
295
- renames are fine when every file keeps the same numeric id; either rename the local filename stem yourself or
296
- let trunk sync canonicalize it from `.meta.yml`. Before any sync, the repo must already mirror the live DB:
297
- every live component present at its real id, and nothing extra.
298
-
299
- ### The surfaces and their intended behavior
300
-
301
- | Command | What it touches | Danger |
302
- |---|---|---|
303
- | `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
304
- | `remits-cli components status` | Reads this lane's staging scope (account/user/branch/workspace) and branch resolution, and lists every other lane on the branch. | none |
305
- | `remits-cli components clear` | Clears THIS lane's staging cache without changing DB or git. Never touches another workspace lane. | none |
306
- | `remits-cli components sync` **on trunk** | **Server-side git→DB reconcile of the whole account** (create/update/**delete**/rename). Reads the pushed remote; ignores local files. | **high** |
307
- | `remits-cli components sync` **on a variant branch** | Writes `ComponentVariant` overlays for that branch only. Never touches trunk rows or the account's trunk branch. When the checkout identifies a subscribing account, the branch-local `account-info.json` is refreshed for that subscriber; `--dry-run` reports the plan without writes. | medium (a missing file becomes a **tombstone** that hides the component from subscribers) |
308
- | `remits-cli components commit` | **One shot:** `git add -A` + commit + `git push` + **`components sync`** + `git pull --ff-only`. Blindly stages the *entire* working tree (including any drift) and reconciles it into prod. Inherits the danger of whichever sync mode the branch selects. | **highest on trunk** |
309
-
310
- Key implications:
311
- - **`stage` is always safe** — stage and test as much as you want; it never reconciles or deletes.
312
- - **`components commit` is the most dangerous command**, not a mere convenience wrapper: it `git add -A`
313
- commits and pushes whatever is in the working tree, then immediately syncs. Never run it while the tree
314
- contains drift or unexplained changes. Prefer the explicit, observable
315
- `git commit → git push → components sync → git pull` sequence so each phase can be inspected.
316
- - **`components sync` acts on the pushed remote**, so local edits are invisible to it until committed **and
317
- pushed**, and a drifted **remote** is dangerous even when your local tree looks fine.
318
- - After a successful non-dry-run `components sync` / `components commit`, the server clears the full
319
- staging scope for that lane (account/user/branch/workspace). This is the expected clean state: old Redis aliases should not keep shadowing
320
- the newly synced DB rows. `components sync --dry-run` intentionally leaves staging untouched.
321
-
322
- ### Intended workflows
323
-
324
- **Change existing components (normal path):**
325
- 1. Edit files under `components/` **keeping each component's existing numeric id** (use `new_*` only for
326
- genuinely new components). To rename a component, update `name:` in its `.meta.yml`; keep the id prefix
327
- fixed. Renaming the file stem is optional before trunk sync because the platform will canonicalize it, but
328
- doing it locally keeps the working tree easier to read.
329
- 2. `remits-cli components stage` → verify in test mode. Iterate (edit → stage → run).
330
- 3. When ready to promote: pass the pre-sync safety check below, then
331
- `git add -A && git commit && git push`, `remits-cli components sync`, `git pull --ff-only`.
332
-
333
- **Create a new component:** add `new_Name.groovy` (+ `.json` / `.meta.yml` as applicable). Sync assigns the
334
- durable id and renames the files. Standalone Prompts live in `components/prompts/new_Name.md`, and their
335
- sidecar must include `name`, `summary`, `description`, and `purpose` (usually `CUSTOM`). AGENT prompts do
336
- not live there; they are the `.md` sidecar beside the Utility in `components/agents/`. Do not create a
337
- direct database row to work around an id/name mismatch, and never create a replacement for a component
338
- that was unexpectedly deleted or renumbered.
339
-
340
- **Delete a component (deliberate only):** remove **all** of that component's files from the repo, confirm via
341
- `git status` that only those files are gone, then sync — the delete phase removes exactly that DB row. Deletion
342
- is a real, supported outcome of a missing file, which is precisely why an *accidentally* missing or renamed
343
- file is catastrophic.
344
-
345
- ### Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
346
-
347
- - **You know which sync mode this branch selects.** `remits-cli components status` states it outright. On a
348
- variant branch the id/delete/renumber checks below apply to the **overlay set** instead: confirm every
349
- component absent from the branch is *meant* to be tombstoned for subscribers.
350
- - The user intends durable platform promotion now — not just local edits, staging, or verification.
351
- - `git status --short` shows only intended changes; every rename/delete is explained. **No component file has
352
- been renumbered to a different id.**
353
- - Local branch is committed and pushed; sync will read the intended remote commit.
354
- - Local filenames and live inventory (`mcp_account_view`) **agree on id and name for every component**: no live
355
- component appears locally under a different id, and no expected component is missing a repo file.
356
- > **Do not run this comparison against `account-info.json` alone — it will report false orphans.** That
357
- > file omits `auxiliary: true` components by design, so every auxiliary component looks like a repo file
358
- > with no DB row, i.e. exactly the "a trunk sync will CREATE a duplicate" signal this check exists to
359
- > catch. It also omits README- and AGENT-purpose Prompts, which live at the repo root and as `.md`
360
- > sidecars in `components/agents/` rather than in `components/prompts/`, so they look like DB rows with
361
- > no repo file — the "will be DELETED" signal. Both are benign. Before treating a flagged component as
362
- > drift, confirm against the live row: `mcp_component_view`, or
363
- > `mcp_run_action controlAction:"describe"` for an Action. A component that answers is not an orphan.
364
- - You can state the expected create/update/delete set. **If any delete or renumber is unexpected, stop.**
365
-
366
- ### If something looks wrong — stop, don't paper over
367
-
368
- If sync reports unexpected `deleted` / `created` / `renamed`, uniqueness errors, or missing components — or you
369
- discover id drift — **stop. Do not re-run sync, do not `components commit`, and do not create replacement
370
- components to "make ids line up" or replace a deleted component.** Those actions compound the corruption.
371
- Preserve the repo-local session log and tool responses, and reconcile source-of-truth first.
372
-
373
- **Safe recovery pattern (repo ↔ DB drift):**
374
- 1. Establish DB truth: `mcp_account_view` for the full live inventory; `mcp_component_view` to confirm and read
375
- exact sources.
376
- 2. Fix the **local tree to mirror the live DB** — rename component files back to their real DB ids, reassemble
377
- any split components, remove orphan/duplicate files, and **refresh any local source that differs from the
378
- live DB** (the DB is the running truth; a stale local file would overwrite good DB source on sync).
379
- 3. Keep files for any components that were wrongly deleted so sync **re-creates** them (new ids are fine —
380
- schemas are keyed by title, agents link tools by name).
381
- 4. Make the remote authoritative **non-destructively**: commit the corrected tree, then
382
- `git merge -s ours origin/<branch>` (keeps your tree, supersedes drifted remote history) and a fast-forward
383
- `git push` — no force-push.
384
- 5. Run **one** `components sync`: it updates everything to identical, creates the missing components, and
385
- deletes nothing. Verify with `mcp_account_view`.
386
-
387
- If the mismatch is a genuine platform/tooling defect (not agent drift), follow the Back-Stage Escalation
388
- Workflow instead of improvising.
389
-
390
- ## Required Local Index Reads
391
-
392
- These files are decision inputs. Read them when the related decision depends on them.
393
-
394
- - `~/.remits-cli/account-repos.json`
395
- - The inventory of every local Remits repo. Account repos are keyed by numeric account id.
396
- - It also contains the reserved **`platform`** entry: the local clone of the core Remits platform repo (`type:'PLATFORM_REPO'`, with its `directory` path). `remits-cli` clones it on first authenticated run if it is missing (default `~/remits`, override with `REMITS_PLATFORM_DIR`). Read this entry when you need to analyze a back-stage seam or open a platform-fix PR.
397
- - Read before choosing a repo outside the current working directory.
398
- - Read when a support ticket references an account and you need to locate the correct local repo.
399
- - Read before concluding that a repo does not exist locally.
400
- - `~/.remits-cli/config.json`
401
- - Read when service lifecycle, dashboard, or preferred-agent behavior matters.
402
- - `~/.remits-cli/service-state.json`
403
- - Read when the local control center URL, current dashboard port, repo-scan summary, or websocket status matters.
404
- - Read when the user asks whether the remits-cli service is running or where to open the browser view.
405
- - `~/.remits-cli/agents.json`
406
- - The agent sessions registered from THIS machine, each with the process it is anchored to.
407
- - Read when `remits-cli agent work` says no agent is registered, or when several agents are
408
- registered and a command needs `--agent-id`.
409
- - `~/.remits-cli/activity.log`
410
- - Read when diagnosing service lifecycle, websocket, agent registration, or ticket-routing failures.
411
- - `~/.remits-cli/sessions.json`
412
- - Read when authentication state, active accounts, base URLs, websocket topics, or per-account data mode matters.
413
- - `./.remits-cli/current-session.txt`
414
- - Read before opening repo-local session logs so you know which session file is current.
415
- - `./.remits-cli/sessions/<current-session>.jsonl`
416
- - Read when the question is about what HTTP calls the repo recently made through remits-cli, which payload was sent, or what response/error came back.
417
- - `./.remits-cli/tool-responses/<callId>.json`
418
- - Read when `remits-cli tool` says the full payload was stored externally.
419
- - `./.remits-cli/tools/tools.json`
420
- - Read when the question is about available tool names, cached schemas, or why a tool invocation shape may be invalid.
421
- - `account-info.json`
422
- - Read in the target repo before making component changes or assuming account ownership.
423
- - `account-configurations.json`
424
- - Read only when account configuration values matter. It is generated separately because configuration maps can be large and `account-info.json` deliberately omits them.
425
-
426
- Do not rely on memory for these indexes. Read the file that governs the decision you are making.
427
-
428
- ## Big Picture: How remits-cli State Is Organized
429
-
430
- Think about remits-cli as two cooperating layers:
431
-
432
- 1. **Global machine state** in `~/.remits-cli/`
433
- - This is the cross-repo control plane.
434
- - It answers questions like:
435
- - which accounts are authenticated
436
- - which repos exist locally
437
- - whether the background service is running
438
- - where the control center lives
439
- - which agent sessions are registered from this machine
440
-
441
- 2. **Per-repo state** in `./.remits-cli/`
442
- - This is the request/response and cache layer for one specific working tree.
443
- - It answers questions like:
444
- - which repo-local session log is current
445
- - which `/cli/*` calls were made from this repo
446
- - where a large tool response was written
447
- - which tool schemas were most recently cached here
448
-
449
- When a user asks an indirect question, map it to the right layer first:
450
-
451
- - "Why did this ticket open in the wrong repo?" → start in global state.
452
- - "What exact payload did this tool call send?" → start in per-repo state.
453
- - "Why is the dashboard showing stale repos?" → start in `service-state.json` and `account-repos.json`.
454
- - "Why is the browser page not showing websocket activity?" → start in `service-state.json` and `activity.log`.
455
- - "Why is my agent not getting tickets?" → start in `agents.json`, then `remits-cli agent list`.
456
-
457
- Agents should use this mental model before guessing.
458
-
459
- ## Support Ticket Mental Model
460
-
461
- Support tickets are a **first-class platform capability**, not an account convention. Every Remits
462
- account reads and writes the `support_tickets` collection without owning a Schema, tickets are anchor
463
- `Object`s on the account the work belongs to, and the lifecycle vocabulary is defined once in the
464
- platform.
465
-
466
- **The platform is deliberately not the standard for ticket workflow.** Different Remits
467
- platforms and products integrate with different systems — Zendesk, Jira, a customer's own portal —
468
- and each end client has its own rules for how tickets move. Remits owns the *record* and the *verbs*;
469
- each front-stage platform builds its own workflow on top through its own Embeddables, Rules, and
470
- Actions. So what you see through `remits-cli` is the shared record underneath every one of those
471
- workflows, and never one product's view of it.
472
-
473
- ### You are an agent, and you register yourself
474
-
475
- **If the user says anything like "register to become a support agent", or "work support tickets",
476
- run exactly this and nothing else first:**
477
-
478
- ```bash
479
- remits-cli agent serve
480
- ```
481
-
482
- No flags, no setup, **no need to be in an account repo** — run it from wherever the session started.
483
- It registers this terminal for every account repo indexed on this machine that your user can reach,
484
- against the production platform and the production lane, and then **starts working tickets on its
485
- own**. It returns immediately; a background supervisor does the rest.
486
-
487
- Use `remits-cli agent register` instead **only** when the user wants presence without autonomy — a
488
- human, or you in this very tab, will work the tickets by hand. Registering alone starts nothing: it
489
- makes the session routable and then waits to be asked.
490
-
491
- Three tabs running an agent are three agents. Each is independently routable, each reports its own
492
- activity, and each stops receiving work when its tab closes — a heartbeat anchored to the session dies
493
- with it, so a crashed agent and a quit agent look identical to the platform.
494
-
495
- **Nothing is pushed at you.** A terminal mid-task cannot receive a push, so delivery is you asking.
496
- That is why the routing decision is a durable field on the ticket rather than a message: you can
497
- restart this terminal, register again, and the work is still there.
498
-
499
- Two facts about a ticket are separate and must stay separate:
500
-
501
- | Fact | Field | Means |
502
- |---|---|---|
503
- | **Routed** | `routedAgentId` | which agent session should pick this up — delivery |
504
- | **Claimed** | `claimedAgentId` | a worker process is running on it *right now* |
505
- | **Owned** | `assignedTo` | who has claimed it — accountability |
506
- | **Status** | `status` | where it is in the workflow |
507
-
508
- A ticket routed to you is not yet yours. Claim it with `accept`, and the queue then shows it owned.
509
-
510
- The **claim** is the supervisor's, not yours — it exists so two workers never start on one ticket,
511
- and it expires on its own so a killed worker cannot park a ticket forever. You do not manage it.
512
-
513
- ### Autonomous: one ticket, one process
514
-
515
- ```bash
516
- remits-cli agent serve # workers are whichever agent THIS session is
517
- remits-cli agent serve --worker-agent codex --max-concurrent 2
518
- remits-cli agent serve --mode investigate # read-only workers: no file edits
519
- remits-cli agent workers # what is running right now
520
- remits-cli agent release # stop serving, go offline
521
- ```
522
-
523
- **Run it in a plain terminal tab.** That tab becomes the agent host: `serve` returns immediately, a
524
- detached supervisor is anchored to the tab's shell, and closing the tab stops the agent. Nothing in
525
- that tab is an AI session — the AI only ever appears as the worker processes the supervisor spawns.
526
-
527
- Starting it from inside an AI session works too and self-detects the worker kind, but it is the
528
- lesser setup: it parks an interactive session as a heartbeat holder while its workers do the work.
529
-
530
- When a ticket is routed to this session, the supervisor launches **a fresh headless agent process
531
- for that ticket**, in that account's repo, with a brief the platform generates. That process exits
532
- when the ticket is done.
533
-
534
- **One ticket = one process is the point, and it is why nothing here ever needs `/clear`.** Context
535
- grooming cannot be a discipline: `/clear` and `/new` are commands a *human types into a TUI*, and no
536
- model can invoke them — so a long-lived session working ticket after ticket has no way to reset
537
- itself. A process that exits has nothing to reset. Do not try to solve context growth by being tidy
538
- inside one session; let the session end.
539
-
540
- **`serve` returns immediately, and that is correct.** Do not follow it with a wait, a poll, or a
541
- loop. The supervisor is detached precisely so this tab is free. Report that you are serving and
542
- stop; you have not left the job half done.
543
-
544
- What the supervisor handles for you, so you do not have to think about any of it: collecting routed
545
- work, claiming each ticket so no second worker starts on it, renewing that claim while the worker
546
- runs, reporting the worker's activity to the dashboard, dropping the claim when it exits, retrying a
547
- failed ticket once, and releasing a ticket back to the queue when it has failed too often. Closing
548
- the terminal stops everything and hands any in-flight work back.
549
-
550
- ### The manual loop
551
-
552
- Only when the session registered with `agent register` rather than `agent serve`.
553
-
554
- ```bash
555
- remits-cli agent work --wait 600 # returns as soon as work arrives
556
- remits-cli agent status --state working --ticket 22454 --activity "reproducing the upload failure"
557
- # ... investigate, fix, verify — keep `status` current as what you are doing changes ...
558
- # ... close the ticket lifecycle through remits-cli ticket (accept -> status -> complete, ask, or release) ...
559
- remits-cli agent status --state idle # then ask for work again
560
- ```
561
-
562
- **Nothing wakes this tab.** A routed ticket sits there until someone in this session asks for it, so
563
- if you are working the manual loop you have to keep asking. If you find yourself wishing you could
564
- be woken up, that is what `agent serve` is.
565
-
566
- **Report what you are doing.** `agent status` is how an operator watching the dashboard, or another
567
- agent, knows this session is alive and what it is on. It costs one command and it is the difference
568
- between a visible queue and a silent one. Update it when you change what you are doing, not on a
569
- timer.
570
-
571
- **Work the ticket in the right repo.** A ticket names its `accountId`, and often an
572
- `implementationAccountId` — the platform/product account whose repo holds the code. Resolve that to a
573
- local directory through `~/.remits-cli/account-repos.json` and `cd` there before making changes. You
574
- registered from anywhere; you do not fix anything from anywhere.
575
-
576
- **Capacity is real, not advisory.** A serving session tells the platform how many workers it can run
577
- (`--max-concurrent`, default 1), and the router will not send it more than that. So a session at
578
- capacity is skipped in favour of one that is free, rather than accumulating tickets it will never
579
- start.
580
-
581
- **Lane note:** `register` and `serve` default to the production lane because a support agent works real tickets.
582
- `--data-mode test` registers a fixture agent instead, which will never be routed a production ticket —
583
- use it only when you are deliberately testing the routing itself.
584
-
585
- ### If you are the worker
586
-
587
- You know you are one when `REMITS_SUPPORT_TICKET_ID` is set in your environment. Your whole job is
588
- that one ticket, and your brief is your prompt — follow it. Two things it says that are worth
589
- repeating: **report progress** with `remits-cli agent status --ticket <id> --activity "..."` (a
590
- headless run is invisible otherwise), and **end in a terminal state** — `complete` with a real
591
- resolution, or `update_status` with what you established and what the next agent should try. Exiting
592
- quietly leaves a ticket that looks in-flight forever.
593
-
594
- ### Before you edit anything: where you are, and whether you may
595
-
596
- Presence answers *who*. Two more facts answer *whether you may edit*, and with git worktrees and
597
- workspace lanes an account id no longer identifies a working tree — so an agent that does not ask these
598
- is assuming, and the assumption it makes when it guesses wrong is "this is my repository to edit".
599
-
600
- ```bash
601
- remits-cli ticket where --ticket 22454 # repo account, checkout, branch, staging lane, lease, claim, YOUR phase
602
- remits-cli agent map # every repository, who holds each lease, and where each agent is working
603
- ```
604
-
605
- **Reading, reproducing and investigating are parallel-safe and unrestricted. Editing one account's
606
- repository is exclusive**, enforced by a lease held per repository account per data lane:
607
-
608
- ```bash
609
- remits-cli ticket lease --ticket 22454 # take it when you started read-only and reached an actual edit
610
- remits-cli ticket unlease --ticket 22454 # complete / ask / release already do this for you
611
- ```
612
-
613
- Four things are worth knowing and are not obvious:
614
-
615
- - **A refusal is not an error.** The run continues read-only, and the message names who holds the lease
616
- **and where they are working**, so you can tell a real conflict from a holder in a different worktree.
617
- **Do not wait for a lease and do not poll for one** — investigation is most of the work on most
618
- tickets, and a ticket that turns out to need an edit hands off with `ticket progress --next-step`
619
- saying exactly what change is needed. A lock people queue on turns one stuck worker into a stalled
620
- fleet.
621
- - **A staging workspace does not replace the lease.** Separate lanes stop two runs *resolving* each
622
- other's staged code; they do nothing about two processes writing the same files or pushing the same
623
- branch, which is what actually destroys work.
624
- - **One editing worker per repository account per lane — worktrees do not change this.** Two worktrees
625
- of one repo push to the same branch on the same remote, so per-directory leases would trade file
626
- conflicts for non-fast-forward push conflicts, which surface later and are worse.
627
- - **A spawned ticket worker already has its own staging lane** (`REMITS_WORKSPACE=ticket-<id>`). You do
628
- not set it, and your brief states it. Say which lane you staged into when you report what you verified
629
- — somebody looking at the shared lane will not see your changes.
630
-
631
- `unlease` returns **your own** lease and deliberately cannot touch anybody else's. Breaking a stale one
632
- is a separate, human verb: `remits-cli ticket force-unlease --ticket ID --reason "..."`.
633
-
634
- ### Your account's process is binding, and it is already in your brief
635
-
636
- An account declares how its work is done as Prompts with purpose `OPERATIONS`, selected per the ticket's
637
- `workstream` (`support`, `sdlc`, `incident`, `release`, or whatever that organization calls its
638
- processes) via `Prompt.category`, with an uncategorised one as the catch-all. The matching text is
639
- **inlined verbatim into the brief** of every agent that works one of that account's tickets and is
640
- binding on it — follow it even where it differs from how you would normally proceed, and if it conflicts
641
- with the brief, follow the process and say so on the ticket.
642
-
643
- You do not fetch it: if the brief has a "The process that governs this ticket" section, that is it. If
644
- it says the account documents no process, the likely cause is that the prompt is **staged but not
645
- committed** — an autonomous worker resolves the committed one, so it is never bound by a procedure
646
- nobody has reviewed. The repo root also carries a generated `OPERATIONS.md`; that file is generated
647
- from the prompt, so **edit the prompt, not the file**.
648
-
649
- `workstream` is not `type`: a type classifies the request, a workstream names the procedure, and they
650
- cross — a `defect` handled by incident response out of hours goes through the SDLC in the morning.
651
-
652
- ### Seeing the queue as a human does
653
-
654
- `remits-cli start` opens a browser control center showing the same facts you are acting on: which
655
- agents are registered and what each is doing, which tickets are open, and which agent each is routed
656
- to. An operator can route a ticket to a specific agent from there. It is a view, not a second system —
657
- what it shows and what `remits-cli agent work` returns come from the same records.
658
-
659
- ### Agent components are workers too
660
-
661
- An Agent component that works support tickets must register itself in the same presence registry as
662
- CLI sessions. That makes it visible in `cliAgents()` and `remits-cli agent map`, and lets
663
- `dispatch(...)` / `ticket route` target it by the same `agentId` field:
664
-
665
- ```groovy
666
- def me = workerId() // component:Agent:34
667
- registerWorker([agentId: me, label: 'Support Triage Agent'])
668
- def work = workerWork([agentId: me, runId: threadGroupingId()])
669
- workerStatus([agentId: me, ticketId: work.tickets[0]?.id, activity: 'investigating'])
670
- ```
671
-
672
- `workerWork(...)` is the component-side analogue of `remits-cli agent work` — the same code runs behind
673
- both, so a component and a terminal session get the same answer to the same question. It reads tickets
674
- routed to that worker, claims what it may start, sweeps eligible unrouted work when it is idle (reported
675
- separately as `sweptTicketIds`), takes the repository edit lease when available, and returns `phase`,
676
- `brief`, `briefFacts`, and `editLease`. `phase:'editing'` means the component may edit and stage;
677
- `phase:'investigating'` means another worker holds the repo lease and the component must stay read-only.
678
- When a component reaches a resting point, pass `agentId` to `complete(...)`, `ask(...)`, or
679
- `release(...)` so the ticket verb frees the edit lease and drops its claim.
680
-
681
- Route to a component by its **id** (`component:Agent:34`) — that is what `workerId()` returns and what
682
- the component polls on. `ticket route --agent component:Agent:MyAgent` also works: the name is resolved
683
- and the id form is what gets stored.
684
-
685
- ### Moving a ticket through its lifecycle
686
-
687
- **`remits-cli ticket` is the platform surface, and it is what you should reach for.** It reads and
688
- writes the platform's own ticket record, so the same commands work on every Remits platform.
689
-
690
- ```bash
691
- remits-cli ticket read --ticket 22454 # the full record
692
- remits-cli ticket accept --ticket 22454 # claim it before changing anything
693
- remits-cli ticket progress --ticket 22454 --summary "Traced it to the posting Action" --category investigation
694
- remits-cli ticket status --ticket 22454 --status in_progress
695
- remits-cli ticket complete --ticket 22454 --resolution "What you found and did"
696
- remits-cli ticket ask --ticket 22454 --question "Re-issue or skip?" --context "412 affected"
697
- remits-cli ticket answer --ticket 22454 --message "Skip them." # alias: reply
698
- remits-cli ticket message --ticket 22454 --message "We reproduced it; a fix is staged."
699
- remits-cli ticket planning --ticket 22454 --board-stage ready_for_qa --blocked-by CAB-112
700
- remits-cli ticket release --ticket 22454 # hand it back to the queue
701
- ```
702
-
703
- **See the whole queue before deciding your ticket is unique.** Alert-raised tickets arrive in
704
- clusters, and the same root cause routinely appears under several different account names — so the
705
- first useful question is usually "how many of these are one fix?", and you cannot ask it from a single
706
- ticket.
707
-
708
- ```bash
709
- remits-cli ticket queue --account-id 49 # triage order, most urgent first
710
- remits-cli ticket queue --account-id 49 --unrouted --status open
711
- remits-cli ticket queue --account-id 49 --search "firestore index"
712
- ```
713
-
714
- The `repo` column is the account the **edit lease** excludes on, so it also tells you which of these
715
- could be worked at the same time and which will serialize behind one another.
716
-
717
- Found a second, unrelated problem while working yours? **File it rather than widening the ticket you
718
- were given** — a ticket that describes two things cannot be closed by either fix.
719
-
720
- ```bash
721
- remits-cli ticket create --account-id 49 --subject "Vendor name lost on re-normalization" \
722
- --type defect --priority medium --reference-id vendor-name-lost
723
- ```
724
-
725
- `--reference-id` makes it get-or-create, so a re-run — or another worker reaching the same conclusion
726
- — reconciles onto the same ticket instead of filing a duplicate.
727
-
728
- Marking and evidence, so the next reader does not repeat your work:
729
-
730
- ```bash
731
- remits-cli ticket tag --ticket 22454 --tags firestore-index,cluster-aug
732
- remits-cli ticket artifact --ticket 22454 --type log --label "Failing query" --content "..."
733
- remits-cli ticket note --ticket 22454 --message "Internal: same cause as 23465"
734
- remits-cli ticket field --ticket 22454 --key customerReference --value CR-9182
735
- ```
736
-
737
- `note` is internal — the next worker and a reviewer see it, the requester does not. Use `message` for
738
- anything the requester should read. `field` writes your **organization's own** field, outside the
739
- platform's bounded planning slots.
740
-
741
- **`message`, `answer` and `note` are separated by who reads the result, not by tone.** They are easy to
742
- confuse and they do different things to the ticket:
743
-
744
- | Command | Who reads it | What it does to the ticket |
745
- |---|---|---|
746
- | `ticket message --message "..."` | the requester | appends a public entry. **Does not send anything** |
747
- | `ticket note --message "..."` | the next worker, a reviewer | appends an internal entry |
748
- | `ticket answer --message "..."` | whoever asked | answers the open question and **re-routes**, so a fresh worker resumes |
749
-
750
- `ticket reply` is an **alias of `answer`** — kept because every existing brief says it. Do not read it as
751
- "reply to the customer"; that is `message`. (Some account tools spell the conversational verb `reply`,
752
- which is exactly why this surface teaches `answer`.)
753
-
754
- **Appending a message is not emailing anyone.** The record is deliberately vendor-agnostic; the account
755
- owns the transport. To ask for something to actually go out:
756
-
757
- ```bash
758
- remits-cli ticket deliver --ticket 22454 --channel email --to ops@acme.test \
759
- --subject "Waiting on you" --idempotency-key waiting-22454
760
- remits-cli ticket deliveries --ticket 22454 # what is queued, and what already went
761
- ```
762
-
763
- That records an outbox request. An account-owned Rule or scheduled Action sends it and marks the result
764
- (`ticket delivered --delivery-id ...` / `ticket delivery-failed --delivery-id ... --reason "..."`). Use
765
- this when a ticket is waiting on somebody who is not watching the queue.
766
-
767
- ### When a worker needs a decision from a human
768
-
769
- There are **three** ways a run can end, not two, and the third is what stops an agent guessing:
770
-
771
- | Outcome | Verb | Means |
40
+ | Load it before you | Reference | It owns |
772
41
  |---|---|---|
773
- | Done | `ticket complete --resolution` | finished, here is what I did |
774
- | **Blocked on a choice** | `ticket ask --question` | I understand the work; the DECISION is not mine |
775
-
776
- | Could not finish | `ticket status --status pending_review` + `ticket progress` | stuck, here is the hand-off |
777
-
778
- `ask` puts the question on the ticket as an outbound message and parks it. The queue then shows it as
779
- **awaiting a response** — distinct from "done, review me", which `pending_review` alone cannot express.
780
-
781
- Someone answers with `remits-cli ticket answer --message "..."` (`reply` is an alias), or through an
782
- operator Embeddable.
783
- That **re-routes the ticket by default**, so the supervisor's next poll starts a **fresh worker whose
784
- brief already contains the exchange**. Resumption costs nothing because a worker is one process per
785
- ticket — there is no session to restore.
786
-
787
- Ask when the choice is genuinely not yours: which of two behaviours is wanted, whether to touch live
788
- data, an ambiguity in the request. Do not ask for reassurance about something you can determine
789
- yourself, and ask **once**, with the options laid out.
790
-
791
- Inside an autonomous worker `--ticket` defaults to the ticket that worker was launched for.
792
-
793
- A refusal is an **answer**: "accept it first", "already assigned to someone else", "reopen it
794
- first". Act on the sentence — do not go looking for another tool that lets you skip the step.
795
-
796
- **`mcp_support_ticket` is an account convention, not a platform guarantee.** It belongs to one
797
- particular System Account: it may not exist on the platform you are on, it scopes to its own
798
- account, and it will refuse a ticket owned by a different one. Use it only when you are working in
799
- that account and want its product-specific workflow on top of the record. It offers:
800
- - `read` — always start here to load current state
801
- - `accept` — claim the ticket so other agents do not work it concurrently
802
- - `update_status` — move to `in_progress` or `pending_review`
803
- - `complete` — resolve the ticket with a summary of what was done
804
- - `release` — unassign if you cannot continue
805
- - **Pulling a ticket by number is enough — you do NOT need to know its account first.** The `ticketId` IS the ticket's globally-unique anchor id, and `mcp_support_ticket` resolves the owning account from it for every action that operates on an existing ticket (`read`, `accept`, `update_status`, `complete`, `release`, `record_progress`, `add_artifact`, `get_attachment`). So when a user says "pull ticket 19463", just call `read` with `{ "action": "read", "ticketId": "19463" }` from any authenticated **prod** session (the ticket lives in prod data) — omit `accountId` entirely. The response returns the resolved `accountId`/`accountName` (and `implementationAccountId` when set); use those for any follow-up work. **Only `create` requires an explicit `accountId`** (a brand-new ticket has no anchor to resolve from). If a bare `ticketId` returns "Support ticket not found", double-check you are in `--data-mode prod`, then fall back to passing an explicit `accountId`.
806
- - `read` can return `documentState:'missing_or_empty'` with `ticket.mirrorOnly:true` and `ticket.canMutate:false`. That means the support-ticket anchor exists and the queue row is real, but the backing Firestore `support_tickets/{ticketId}` document is missing or metadata-only. Treat this as a degraded ticket, not as "ticket not found"; run or request the System Account action `Restore Support Ticket Documents From Anchor Mirrors` for the owning account before lifecycle mutations. The restore recovers mirrored scalar fields, but document-only arrays such as alert snapshots, email threads, attachments, worklog entries, and artifacts may be lost.
807
- - If a ticket is part of the request, manage the lifecycle proactively. Do not wait for the human user to remind you to read, accept, update, complete, or release it.
808
-
809
- Ticket-routing context:
810
- - `accountId` / `accountName` identify the account that owns the ticket.
811
- - If present, `implementationAccountId` / `implementationAccountName` identify the owning `PLATFORM` or `PRODUCT` implementation context.
812
- - Prefer `implementationAccountId` when choosing the working directory for a ticket, falling back to `accountId` when the platform/product repo is not available locally. Routing already uses that same ladder, so the repo you were routed through is usually the right one.
813
- - For enhancement work, prefer the platform/product implementation context when deciding where code changes belong.
814
- - For defect investigations, start from the owning ticket account, then move to the platform/product context if the root cause is in shared components.
815
-
816
- Sandbox note:
817
- - Every `remits-cli` command that reaches the Remits service (`auth`, `tools`, `tool`, `components`, `test`, `token`, and all of `ticket` / `agent`) needs outbound network access.
818
- - **Inside an autonomous ticket worker this is already arranged** — the supervisor launches the worker with network enabled, in both the editing and the investigating phase. An investigating worker is restricted from *writing the repository*, never from *talking to the platform*: reading its ticket, running a diagnostic and recording a finding are the whole of what investigating means.
819
- - Elsewhere, `ENOTFOUND`, `EAI_AGAIN`, `ECONNREFUSED`, `EPERM` or a bare exit 6 / `HTTP:000` from a sandboxed session means the command needs escalated permissions or must run outside the sandbox. Do not read it as "the platform is down" — check a second command before concluding anything about the service.
820
-
821
- Use the same repo/context rules as other tools:
822
- - If the ticket targets a `CLIENT` account, do not assume that client's repo is the implementation repo. Confirm the parent `PLATFORM` / `PRODUCT` relationship first.
823
- - Read `~/.remits-cli/account-repos.json` before choosing which local repo to open.
824
- - If the correct repo for the relevant account type exists locally, switch there and inspect `account-info.json` and `/components`.
825
- - If the repo is not available locally, use `mcp_account_view`, `mcp_component_view`, and `mcp_component_grep`.
826
-
827
- ## Efficiency Rules
42
+ | decide which account or repo work belongs in, or explain why a request resolved the way it did | `account-targeting.md` | the `resolution` block, repo selection, multi-parent edges, storage namespaces, test-data flags, the data-lane clamp |
43
+ | run `components sync` or `components commit` — **mandatory** | `component-integrity.md` | how a trunk sync reconciles repo→DB, why a filename's id prefix is load-bearing, the `auxiliary` trap, the pre-sync safety check, the repair procedure |
44
+ | build or change any component | `development-loop.md` | test vs prod mode, the golden rule of verification, the eight-step fast loop, `new_` components and sidecars, the temporary-experiment pattern |
45
+ | conclude "my change isn't working" | `component-resolution.md` | the staged → variant → trunk order, the staging key format, the compile signature that proves what ran, staging workspaces for parallel agents, the stale-compile-cache caveat |
46
+ | work from a non-trunk checkout, or promote a branch | `branch-variants.md` | what a variant is, which world your checkout resolves, `--as-account` vs `--variant-branch`, tombstones, the promotion loop and its phases, subscribe/retire |
47
+ | touch a support ticket | `support-tickets.md` | the ticket record and its verbs, routed vs claimed vs owned, autonomous workers, the repository edit lease, ask/answer/resume, delivery |
48
+ | register this session as an agent, or ask why a routed ticket never started | `agent-sessions.md` | `serve` vs `register`, what registration does, worker spawning per agent kind, routing order, the control center, multi-session auth |
49
+ | investigate live behavior | `investigation.md` | which tool reads which record, correlation keys, reading a record's `content`, HTTP audits, AI activity, node/`localMode`, the production support flows |
50
+ | call any `mcp_*` tool | `tool-reference.md` | every tool's parameters, semantics, and traps — read the tool's entry before building its input |
51
+ | reason about repo discovery, auth state, or what a command actually sent | `cli-state.md` | the global `~/.remits-cli/` control plane vs per-repo `./.remits-cli/`, and which file answers which question |
52
+ | need an exact flag, or the auth / host / async surface | `command-reference.md` | authentication, host vs data mode, the two async mechanisms, hierarchy-scoped reads, the full command list, prod banners |
53
+ | give up on something that misbehaved | `troubleshooting.md` | the symptom→fix table, the two kinds of escalation, and the escalation bundle |
54
+
55
+ ## The rules that are cheaper to state than to look up
56
+
57
+ These are the ones that cost a session more to rediscover than to carry. Each is expanded in the
58
+ reference named after it.
59
+
60
+ - **edit → stage → run, every time.** The platform executes whatever is in the staging cache at the
61
+ moment a run starts. Edit a file, run a test without `remits-cli components stage`, and the test runs
62
+ the OLD code. This is the single most common mistake. (`development-loop.md`)
63
+ - **`stage` is always safe. `components commit` on trunk is the most dangerous command in the CLI.**
64
+ It is not a convenience wrapper: it `git add -A`, commits, pushes, and then reconciles the whole
65
+ pushed repo into the live component database — creating, updating, renaming, and **hard-deleting**
66
+ rows. Prefer the observable `git commit → git push → components sync → git pull`.
67
+ (`component-integrity.md`)
68
+ - **A component filename's numeric id prefix is load-bearing.** Renumber it and the next trunk sync
69
+ creates a duplicate at the new id and hard-deletes the original. A live component whose files are
70
+ missing from the repo at trunk-sync time is hard-deleted. Never renumber, rename across ids, or
71
+ remove component files as a side effect. (`component-integrity.md`)
72
+ - **`auxiliary: true` opts a component out of the repo in BOTH directions.** A `new_*` file marked
73
+ auxiliary is never created, never gets an id, and never gets renamed — it looks exactly like the sync
74
+ silently ignored your work. Use it only for genuinely throwaway components.
75
+ (`component-integrity.md`)
76
+ - **Ask which world your checkout resolves before you run anything:** `remits-cli components status`.
77
+ A trunk checkout and a variant-branch checkout differ on both ends of the loop — what a run resolves
78
+ and what a sync writes. Do not infer it from the branch name. (`branch-variants.md`)
79
+ - **Resolution order is staged → variant → trunk, and each layers over the one beneath.** A populated
80
+ staging cache makes a committed variant look broken through any tokenized entry point; clear it before
81
+ verifying variant resolution. (`component-resolution.md`)
82
+ - **Host and data mode are two independent decisions.** `--base-url` picks the Remits host,
83
+ `--data-mode` picks the data segment on it. Neither implies the other. (`command-reference.md`)
84
+ - **A tool's `dataMode` input never widens the lane.** It may narrow `prod` → `test`, never escalate
85
+ `test` → `prod`. The response's `dataMode` is the truth, not your input. To reach prod data, pass
86
+ `--data-mode prod` on the command line. (`account-targeting.md`)
87
+ - **Never infer prod vs test from an account name, URL, or branch.** Read the explicit flags —
88
+ `testAccount`, `testUser`, `testMode`, and the echoed `dataMode`. `mcp_sql_query` is lane-blind and
89
+ returns both lanes mixed; filter it yourself. (`account-targeting.md`)
90
+ - **"Tool call succeeded" means DISPATCHED, not that the tool did what you asked.** A tool that runs and
91
+ refuses returns HTTP 200 with its own `success: false`. For any mutating call, read `result.success`
92
+ from `./.remits-cli/tool-responses/<callId>.json` before reporting the work as done.
93
+ (`tool-reference.md`)
94
+ - **Writing the code is not finishing the job.** A change is complete when it is verified — a Test
95
+ component (preferred, because it becomes regression protection) or a browser flow through
96
+ `remits-cli token`. "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
97
+ cannot verify, say what you would need and ask. (`development-loop.md`)
98
+ - **Read the account's `resolution` block before acting on it** — `role`, `type`,
99
+ `resolvedDatabaseName`, `relationships`. More than one relationship means the account can legitimately
100
+ resolve differently depending on the path a request travelled. Never infer an account's shape from its
101
+ name. (`account-targeting.md`)
102
+ - **Investigate in prod mode; verify in test mode.** "It looks right in prod data inspection" is not
103
+ proof that a code change works. And never copy live customer data into another account's test
104
+ collection to get realistic verification. (`development-loop.md`)
105
+ - **`remits-cli agent serve` is the whole answer to "work support tickets".** No flags, no setup, no need
106
+ to be in an account repo. `agent register` makes the session routable and starts nothing.
107
+ (`support-tickets.md`)
108
+ - **`remits-cli ticket` is the platform's own ticket surface** and works on every Remits platform.
109
+ `mcp_support_ticket` is one System Account's convention and refuses tickets it does not own.
110
+ (`support-tickets.md`)
111
+ - **Reading and investigating are parallel-safe; editing one account's repository is exclusive**, via a
112
+ lease. A refusal is not an error — carry on read-only and hand off with `ticket progress --next-step`.
113
+ Never wait or poll for a lease. (`support-tickets.md`)
114
+ - **If something looks wrong, stop — do not paper over it.** Unexpected deletes, creates, renames,
115
+ uniqueness errors, id drift, or a 500 from the platform: stop, preserve the session log and tool
116
+ responses, and escalate. Never create replacement components to make ids line up, and never discover
117
+ behavior by running unsupported mutating command variants. (`troubleshooting.md`)
118
+
119
+ ## Orientation: the first four things to establish
120
+
121
+ ```bash
122
+ remits-cli whoami # account, user, branch, data mode, host for the NEXT tool call
123
+ remits-cli components status # trunk or variant checkout, staging lane, what is staged
124
+ remits-cli tools # which tools this account actually has (tools are per-account)
125
+ ```
126
+
127
+ plus the repo's `account-info.json` → `resolution` block for the account's shape.
128
+
129
+ If any command returns 401, run `remits-cli auth` (with the same `--base-url` if you were targeting a
130
+ non-default host).
131
+
132
+ **Files that are decision inputs — read the one that governs the decision, do not rely on memory:**
133
+ `~/.remits-cli/account-repos.json` (every local repo, plus the reserved `platform` entry for the core
134
+ platform clone), `~/.remits-cli/sessions.json` (auth state and lanes), `~/.remits-cli/agents.json`
135
+ (agents registered here), `./.remits-cli/tool-responses/<callId>.json` (the full tool payload), and the
136
+ repo's `account-info.json`. `cli-state.md` maps every remaining question to its file.
137
+
138
+ ## Efficiency rules
828
139
 
829
140
  Keep support and development sessions lean:
830
141
 
831
142
  - Prefer local repo files over remote tools whenever the target account repo exists locally.
832
- - Do not read entire `.remits-cli/sessions/*.jsonl` or large tool response files unless you first narrow to the relevant request, endpoint, tool, or ticket.
833
- - Prefer targeted Firestore queries: use `documentId`, tight `filters`, narrow `fields`, and low `limit` values instead of broad collection scans.
143
+ - Do not read entire `.remits-cli/sessions/*.jsonl` or large tool response files unless you first narrow
144
+ to the relevant request, endpoint, tool, or ticket.
145
+ - Prefer targeted Firestore queries: use `documentId`, tight `filters`, narrow `fields`, and low `limit`
146
+ values instead of broad collection scans.
834
147
  - Do not call `mcp_account_view` repeatedly once you already have the needed account/component context.
835
- - For investigations, follow the shortest path: identify the exact document/ticket/object first, then drill in. Avoid exploratory “maybe this” queries across large collections.
836
- - For ticket replies, read the current ticket state and the new reply, then continue from existing context instead of re-loading broad account state from scratch.
837
- - Verification should be decisive. Avoid repeated identical test/status/tool calls when no new information is likely.
838
-
839
- ## Account Repository Index
840
-
841
- The account repository index file is located at: `{{ACCOUNT_REPO_INDEX_PATH}}`
842
-
843
- This JSON file is automatically maintained by `remits-cli` and tracks all known Remits account repositories on this machine. It is updated whenever any `remits-cli` command runs from an account repo directory. The file maps account IDs to their metadata:
844
-
845
- ```json
846
- {
847
- "37": {
848
- "accountId": 37,
849
- "name": "Acme Corp",
850
- "directory": "/Users/you/Projects/remits-acme-corp",
851
- "updatedAt": "2026-03-23T12:00:00.000Z"
852
- }
853
- }
854
- ```
855
-
856
- **Use this index to:**
857
- - Discover which account repos exist on this machine when working from a different directory
858
- - Navigate to another account's repo to read its `account-info.json` and component source
859
- - Resolve account names and IDs without making remote API calls
860
- - Support cross-account workflows where an agent in one repo needs context from another
861
-
862
- If the index file doesn't exist yet, run any `remits-cli` command from an account repo to bootstrap it.
863
-
864
- ## Two Workflows
865
-
866
- 1. **Development** (test mode) — Build, modify, and verify components using isolated test data.
867
- 2. **Production Support** (prod mode) — Investigate live data, debug issues, trace execution.
868
-
869
- Every CLI response includes `dataMode` so you always know which context you're in.
870
-
871
- ### Test Mode vs Prod Mode
872
-
873
- Treat these as two different jobs:
874
-
875
- - **Prod mode** is for investigation.
876
- - Read live Firestore documents.
877
- - Inspect live object activity, events, alerts, and logs.
878
- - Confirm what actually happened to a customer.
879
- - Do not use prod mode as your final verification environment for a code fix.
880
-
881
- - **Test mode** is for verification.
882
- - Stage local component changes.
883
- - Run Test components.
884
- - Generate token URLs and verify behavior in isolated browser flows.
885
- - Confirm the fix without mutating or depending on live customer processing.
886
-
887
- The correct support loop is usually:
888
- 1. Investigate in **prod mode**
889
- 2. Identify the responsible implementation repo and make the code change locally
890
- 3. Move back to **test mode** for verification
891
- 4. Verify with a Test component, Playwright/browser confirmation, or both
892
-
893
- If a production issue needs realistic verification, do **not** copy live customer data from a production account into another account's test collection.
894
-
895
- The right model is:
896
- - investigate the source document in **prod mode**
897
- - model the relevant conditions in a **Test** component
898
- - or reproduce the scenario through a controlled **test-mode** embeddable/browser flow
899
- - verify the fix there
900
-
901
- Never treat "it looks right in prod data inspection" as sufficient proof that a code change is verified.
902
-
903
- ## The Investigation Model
904
-
905
- Front stage — Schemas, Readers, Actions, Embeddables, Rules, HtmlTemplates, Agents, Tests, and the Firestore
906
- **documents** that hold business data — is described in `platform-overview.md`. This section is only about
907
- the **back-stage lifecycle records** an investigation actually reads, and how to read them.
908
-
909
- ### Which tool reads which record
910
-
911
- The model — Documents (Firestore business data) vs Objects and their record trail (MySQL), linked by
912
- `object_id` — is in `platform-overview.md` → *Objects, Documents, and the Record Trail*. What matters
913
- here is the tool and the filterable fields:
914
-
915
- | Record | Query it with | Fields worth filtering on |
916
- |---|---|---|
917
- | document | `mcp_firestore_search` | any schema field, plus `account_id`, `object_id`, `_lastModifiedAt` |
918
- | `object` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `name`, `referenceId` |
919
- | `object_log` | `mcp_record_listing` / `mcp_record_view` | `type`, `description`, `content`, `threadGroupingId` |
920
- | `event` | `mcp_record_listing` / `mcp_record_view` | `action`, `status`, `eventDate`, `threadGroupingId` |
921
- | `alert` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `active`, `threadGroupingId` |
922
- | user activity session | `mcp_user_activity` | `userId`, `accountId`, `sessionKey`, `focusedOnly`, `traceId` pivots |
923
-
924
- `mcp_record_listing` finds candidates when you do not know the id; `mcp_record_view` opens an exact one;
925
- `mcp_object_activity` returns one Object's whole timeline in order.
926
-
927
- **Check the data lane before concluding a record does not exist** — `--data-mode test` and `prod` read
928
- different lanes (see `platform-overview.md` → *Test and Production Data Lanes*).
929
-
930
- ### Correlation keys
931
-
932
- - **`object_id`** — links documents to records. Pivot `mcp_firestore_search` → `mcp_object_activity`.
933
- - **`threadGroupingId`** — groups every record and log line from one processing chain, and is also the
934
- request's trace id. Pivot into `mcp_system_logs` and `mcp_performance_trace`.
935
- - **`node`** — resolves to `serviceName` + `region` for log queries (table below).
936
- - **`sessionKey`** — salted user-activity session key. Pivot `mcp_user_activity` `sessions` → `story`;
937
- each beat then carries a `traceId` / `threadGroupingId` for trace and log investigation.
938
- - **`sessionId`** — pivots into persisted AI activity via `mcp_ai_session_search`.
939
-
940
- ### Reading a record's `content` — persisted context, not a memory dump
941
-
942
- `content` on an `object`, `object_log`, `event`, or `alert` is the **sanitized snapshot** the platform
943
- deliberately kept of what the producing component knew at that workflow step. It is not everything that was
944
- in memory.
945
-
946
- - `Object.content` — ingestion/request/file context when the Object was created or updated.
947
- - `ObjectLog.content` — the logging component's point-in-time view.
948
- - `Event.content` — the scheduling component's view, plus the explicit event options.
949
- - `Alert.content` — the raising component's view, plus the explicit alert body.
950
-
951
- **Missing or `[REDACTED]` does not mean the component never had it.** Before persisting, the platform drops
952
- non-serializable objects, strips `requestBody` / `params` / `token` recursively, summarizes very large
953
- strings, prunes oversized maps and collections, and redacts secret-looking keys (`password`, `secret`,
954
- `authorization`, `cookie`, `clientSecret`, …) plus any Account/User schema field marked `sensitive: true`.
955
- When explaining a record, distinguish what the workflow *had* at runtime from what the platform *kept*.
956
-
957
- **Provenance travels in the context itself:**
958
-
959
- - **`source_bcd`** — the immediate component that produced this record, e.g. `[Action:18] DataPlus Invoice Posting`
960
- - **`upstream_source_bcd`** — the prior workflow hop, when the context was inherited from one
961
-
962
- **Never interpret `content` in isolation.** Pair it with the record type, `source_bcd`, any
963
- `upstream_source_bcd`, the `object_id` timeline, the `threadGroupingId` chain, and the producing
964
- component's source. The goal is not to find a suspicious record — it is to explain **why the context looks
965
- exactly the way it does relative to the workflow step that produced it**.
966
-
967
- ### HTTP audits
968
-
969
- Raw inbound (Reader) and outbound (`rest(...)`) HTTP is persisted to Firestore — but **only for accounts
970
- with `Account.enableHttpAudits`**. When it is off, the absence of audit documents proves nothing.
971
-
972
- Audits live in **monthly** collections, not one global collection:
973
-
974
- ```
975
- http-audits/http-audits-YYYY-MM/entries
976
- ```
977
-
978
- Start with the month the request ran in, and check the adjacent month if the run may have crossed a
979
- boundary. Query them with `mcp_firestore_search` like any other collection; the filters worth reaching for
980
- are `direction` (`INBOUND` / `OUTBOUND`), `success`, `request.method`, `request.path`, `component.name`, and
981
- `response.statusCode`. Some paths are deliberately excluded from persistence, so a missing audit is not
982
- proof a call was never made.
983
-
984
- Reach for audits when the question is *what exact request went out, what came back, and did this component
985
- actually make the call* — then use the component source plus the payload to place the fault in request
986
- formation, the partner's response, or downstream processing. Full captured shape, websocket behavior, and
987
- embeddable patterns: `features/http-audits.md` (`mcp_get_guide`).
988
-
989
- ### AI activity
990
-
991
- Every `ai()` call and Agent turn is persisted. Two ways in:
992
-
993
- - **`mcp_ai_session_search`** — search groupings and open their detail. A grouping spans every session
994
- sharing one grouping id: the agent turns plus its guardrail and internal `ai()` calls. Use the
995
- **map → open** flow described under its Tool Reference entry, never a whole-detail dump.
996
- - **`ai_request_response([sessionId: id])`** — from inside component code, loads the stored
997
- request/response history for a session (`first: true` / `last: true` for one record). This is also how
998
- you replay a stored provider response in a test.
999
-
1000
- If a document or agent state already carries a session id, that is a direct pivot into persisted AI
1001
- activity.
1002
-
1003
- **Before tuning any prompt, read the session.** `features/ai-session-investigation.md` owns the method —
1004
- per-turn forensics, what each layer proves, and the rule that what a tool **PRODUCED** is not necessarily
1005
- what the model **CONSUMED**. `features/ai-strategy.md` owns what to change once you know. Changing a prompt
1006
- before reading the persisted request/response is guessing.
1007
-
1008
- ### Node Reference Table
1009
-
1010
- | Node Name | Service Name | Region |
1011
- |---|---|---|
1012
- | remitsAdmin-east5 | remits | us-east5 |
1013
- | remitsActions | remits-actions | us-east1 |
1014
- | remitsAdmin | remits | us-east1 |
1015
-
1016
- `mcp_system_logs` accepts `node` directly and resolves it automatically.
1017
-
1018
- ### Runtime node and `localMode`
1019
-
1020
- The deployed `remits` service in `us-east5` (`remitsAdmin-east5`) runs with the platform setting
1021
- `localMode=true`. If someone says "localModel" in this context, confirm they mean this `localMode`
1022
- setting. Operationally, immediate async follow-on work stays on the same Cloud Run service/node instead of
1023
- being sharded to `remits-actions`:
1024
-
1025
- - Pub/Sub-style follow-on messages are handled locally after commit.
1026
- - Near-immediate tasks are handled locally when `localMode` is enabled. Future scheduled tasks still use
1027
- Cloud Tasks.
1028
- - Local worker hops preserve the run context, including staged-source resolution, data mode,
1029
- `threadGroupingId`, and trace correlation.
1030
- - Durable boundaries such as async HTTP ingress and Events carry that same run context across the queue.
1031
-
1032
- For investigations on the default deployed host (`https://remits-529558023549.us-east5.run.app`), do not
1033
- assume "async" means `remitsActions` / `us-east1`. Start with `node:"remitsAdmin-east5"` and the
1034
- `threadGroupingId`; pivot to `remitsActions` only when the Event delivery envelope, log line, or returned
1035
- node says the work actually ran there.
1036
-
1037
- This does not change the data-lane rule: a non-null `TestMode` can exist only to carry branch/staged-source
1038
- resolution. Data isolation is decided by CLI `--data-mode`: a branch-scoped `--data-mode prod` run is still
1039
- prod data, while `--data-mode test` remains isolated test data.
1040
-
1041
- ## Getting Started
1042
-
1043
- ### Authentication
1044
-
1045
- Authenticate once per session. Opens the user's browser for OAuth login:
1046
-
1047
- ```bash
1048
- remits-cli auth --account-id <ACCOUNT_ID>
1049
- remits-cli auth --account-id <ACCOUNT_ID> --base-url http://localhost:8080
1050
- ```
1051
-
1052
- - `--account-id` is auto-resolved from `account-info.json` when you're inside an account repo, so you can usually just run `remits-cli auth`. Pass `--account-id <ID>` explicitly to target a different account (e.g., authenticating into a child account from a parent-account repo) — the explicit flag always wins over the repo's `account-info.json` and any prior session.
1053
- - `--base-url` targets a specific platform instance (e.g., localhost for development). Sessions are stored per account + base URL + data mode, so authenticating against localhost does not overwrite a production session for the same account.
1054
- - If any command returns a 401 error, re-run `remits-cli auth` (with the same `--base-url` if you were targeting a non-default instance).
1055
-
1056
- ### Host vs Data Mode
1057
-
1058
- Treat host selection and data mode as two separate decisions:
1059
-
1060
- - `--base-url` chooses the Remits host: localhost vs a deployed environment.
1061
- - `--data-mode` chooses the data segment on that host: `test` vs `prod`.
1062
-
1063
- Examples:
1064
-
1065
- ```bash
1066
- # Deployed prod host, but test data segment
1067
- remits-cli tools --base-url https://your-prod-host --data-mode test
1068
-
1069
- # Localhost host, but prod data segment on that localhost instance
1070
- remits-cli tool --base-url http://localhost:8080 --name mcp_account_view --data-mode prod
1071
- ```
1072
-
1073
- Do not assume `--data-mode prod` implies the deployed prod host, or that `--data-mode test` implies localhost. If host matters, read `~/.remits-cli/sessions.json` first and pass `--base-url` explicitly.
1074
-
1075
- ### Tool Execution Lifecycle
1076
-
1077
- `remits-cli tool` supports both synchronous and asynchronous execution. Use normal synchronous execution
1078
- for quick investigation tools:
1079
-
1080
- ```bash
1081
- remits-cli tool --name "mcp_account_view" --input '{"accountId": 37}' --data-mode prod
1082
- ```
1083
-
1084
- **There are two independent async mechanisms — do not confuse or stack them:**
1085
-
1086
- 1. **The tool's own async mode** (`mcp_run_action` / `mcp_run_agent`, via `executionMode:"async"` in the
1087
- tool input). The tool spawns the long work server-side and **returns immediately in the same HTTP
1088
- response** with its own run identifiers — `actionRunId` (or `agentRunId`) and, for agents, a stable
1089
- `sessionId`. You poll it with the tool's **own** status protocol (`controlAction:"status"`). This is the
1090
- preferred path for long Actions/Agents, because the run ids come back on the very first call.
1091
-
1092
- 2. **The CLI transport async** (`--async true`). This wraps *any* tool call in a background server task and
1093
- returns a CLI-level `callId` immediately, which you poll with `remits-cli tool status --call-id`. Use it
1094
- for long tools that do **not** have their own async mode. Its start response carries `callId`,
1095
- `status:"running"`, `threadGroupingId`, `accountId`, `branchName`, and `dataMode` — but **not** any
1096
- tool-specific ids, because the tool has not run yet; those arrive inside the `result` of the polled
1097
- completed status.
1098
-
1099
- For `mcp_run_action` / `mcp_run_agent`, prefer mechanism (1) alone — it already makes the call non-blocking
1100
- **and** returns the run ids up front. Pass your own `actionRunId`/`agentRunId` so you can poll it
1101
- deterministically:
1102
-
1103
- ```bash
1104
- remits-cli tool --name "mcp_run_action" --input '{"accountId":49,"actionId":200,"executionMode":"async","actionRunId":"my-stable-run-id","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
1105
- ```
1106
-
1107
- Every tool response is saved to `./.remits-cli/tool-responses/<callId>.json`.
1108
-
1109
- ### Hierarchy-scoped tool reads
1110
-
1111
- For read/discovery tools, the account resolved from the checkout/session is the **scope root**, not proof
1112
- that the business record is owned by that account. This closes the common support loop where you know a
1113
- precise document, object, event, or indexed source id but do not yet know which child account owns it.
1114
-
1115
- Use the tool flags rather than editing JSON by hand:
1116
-
1117
- ```bash
1118
- remits-cli tool --name mcp_firestore_search \
1119
- --input '{"collection":"statements","documentId":"1234"}' \
1120
- --scope children --data-mode prod
1121
- ```
1122
-
1123
- Available flags:
1124
-
1125
- | Flag | Meaning |
1126
- |---|---|
1127
- | `--scope self|children|hierarchy` | Expand from the repo/session account for read/discovery. Exact-id lookups usually default to `children`; broad searches default to `self` unless widened. |
1128
- | `--target-account-id ID` | Exact owner/execution account when already known. Required by mutating tools. |
1129
- | `--account-ids 1,2,3` | Explicit bounded owner list. The platform verifies every id against the scope root. |
1130
- | `--anchor-account-id ID` | Path-disambiguation anchor for multi-parent account relationships. |
1131
-
1132
- The CLI merges these into `--input`; a value already present in `--input` wins. Tool responses echo
1133
- `scopeRootAccountId`, `scope`, `accountIds`, and, when an exact owner is discovered,
1134
- `resolvedTargetAccountId`. Feed that returned owner to `mcp_firestore_patch`, action/test runs, and browser
1135
- tokens. Mutating tools do not infer or fan out writes.
1136
-
1137
- The implicit account ceiling is intentionally different by shape: broad searches stay capped at 100 accounts
1138
- unless the tool says otherwise, while exact-id discovery may span up to 1000 accounts by default. If a broad
1139
- tool returns `scopeTooBroad`, narrow with `--target-account-id`, `--account-ids`, or a smaller `--scope`.
1140
-
1141
- `mcp_firestore_search` handles exact Firestore document ids. `mcp_index_search` handles fuzzy/semantic
1142
- lookup through Vertex. `mcp_bigquery_query` handles warehouse lookup/query with server-resolved
1143
- `{table_current}`/`{table}` placeholders and the scoped `{account_filter}` predicate. BigQuery is a prod
1144
- analytics surface, so use `--data-mode prod` when you intend to query it.
1145
-
1146
- Poll a CLI-transport async call (mechanism 2) by call id:
1147
-
1148
- ```bash
1149
- remits-cli tool status --call-id <callId> --data-mode prod
1150
- ```
1151
-
1152
- Pass `--wait true` to have the CLI process poll locally until the transport call completes (short polling
1153
- requests instead of one long HTTP connection):
1154
-
1155
- ```bash
1156
- remits-cli tool --name "some_long_tool_without_its_own_async" --async true --wait true --input '{...}' --data-mode prod
1157
- ```
1158
-
1159
- Stacking both (`--async true` **and** `executionMode:"async"`) works but is redundant: the tool's
1160
- `actionRunId`/`agentRunId`/`sessionId` then appear only in the polled completed `result`, not in the CLI
1161
- start response — which is why the start response looks "incomplete." Pick one mechanism.
1162
-
1163
- `--timeout-ms <ms>` controls the per-request HTTP timeout. Prefer async execution over a large timeout for
1164
- multi-minute work so the server task is not tied to one HTTP connection.
1165
-
1166
- ### Data Mode
1167
-
1168
- Controls whether you work with test data or production data. **Default is `test`.**
1169
-
1170
- ```bash
1171
- remits-cli data-mode # Show current mode
1172
- remits-cli data-mode set prod # Switch to prod for investigations
1173
- remits-cli data-mode set test # Switch back to test for development
1174
- ```
1175
-
1176
- ## Development Workflow
1177
-
1178
- ### The Golden Rule: Writing Code Is Not Finishing the Job
1179
-
1180
- **A change is not complete until it is verified.** Writing the component is the first step, not the last.
1181
- Two ways to prove it:
1182
-
1183
- 1. **A Test component** (preferred) — it exercises the change *and* becomes permanent regression
1184
- protection. Run it with `remits-cli test run`.
1185
- 2. **Visual verification** — `remits-cli token` for a browser URL, then drive it with `playwright-cli`.
1186
- This is how most users think about verification: "let me see it working."
1187
-
1188
- Use a Test when the behavior can be asserted programmatically, a browser when the change is visual.
1189
- Often both.
1190
-
1191
- **Never skip verification.** "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
1192
- cannot verify — no Test component, no relevant embeddable — say what you would need and ask how the user
1193
- wants to proceed rather than reporting the work as done.
1194
-
1195
- > The *design* rule that pairs with this — never solve interpretive problems with regex cascades, keyword
1196
- > lists, or layout-specific branching when the platform's AI surface is the right tool — is in the account
1197
- > repo's `CLAUDE.md` and, in depth, in `features/ai-strategy.md`.
1198
-
1199
- ### The Development Fast Loop
1200
-
1201
- This is how every development task should flow:
1202
-
1203
- #### Step 1: Understand the Request
1204
- Read the user's request. If you may need a repo other than the current one, read `~/.remits-cli/account-repos.json` first. Then review `account-info.json` and `README.md` to understand what components exist and how they relate. Read the source of any component you'll modify before changing it.
1205
-
1206
- **Establish the account's shape too, not just its components.** Read the `resolution` block in
1207
- `account-info.json` (or `mcp_account_view`): the account `type` decides whether this repo is even the right
1208
- place to change code, `resolution.relationships` shows whether the account has more than one parent (and
1209
- which link carries a branch/namespace/host), and `resolvedDatabaseName` tells you where its data actually
1210
- lands. See "Account Targeting Model" and `features/account-management.md` (`mcp_get_guide`).
1211
-
1212
- **Also establish which world you are working in.** `remits-cli components status` reports whether the
1213
- working tree is a **trunk** checkout or a **variant branch** checkout — which decides both what your test
1214
- runs resolve and what a sync writes. If `account-info.json` carries a `componentBranches` section, branch
1215
- variants of these components exist: editing an origin component will drift them, so check
1216
- `remits-cli components branches` before changing shared code. See "Branched Component Variants".
1217
-
1218
- #### Step 2: Make the Change
1219
- Edit component files under `components/`. This is local file editing — the platform doesn't know about your changes yet.
1220
-
1221
- **Creating a component that does not exist yet.** Files are named `<id>_<Name>.<ext>`, where the numeric
1222
- prefix is the platform's component id. A new component has no id, so name its files with the **`new_`
1223
- prefix** and let the sync assign one (it then renames the files to that id):
1224
-
1225
- ```
1226
- components/embeddables/new_MerchantPortal.groovy # source
1227
- components/embeddables/new_MerchantPortal.html # markup
1228
- components/embeddables/new_MerchantPortal.js # client script
1229
- components/embeddables/new_MerchantPortal.meta.yml # metadata sidecar
1230
- components/prompts/new_PricingReviewPrompt.md # standalone Prompt body
1231
- components/prompts/new_PricingReviewPrompt.meta.yml # standalone Prompt metadata
1232
- ```
1233
-
1234
- **Do NOT put an `id:` in a new component's sidecar.** `id` is what links a sidecar to an *existing*
1235
- component and is parsed as a number, so a placeholder (`id: new`, `id: TBD`) fails the **entire**
1236
- stage/sync request with a `NumberFormatException` — not just that one file, and the error does not name
1237
- the file. Omit the key; the platform fills it in on sync:
1238
-
1239
- ```yaml
1240
- # components/embeddables/new_MerchantPortal.meta.yml — no `id:` yet
1241
- name: Merchant Portal
1242
- summary: One-line statement of what this component is for. This is the compact text account-info.json uses first.
1243
- description: |
1244
- Longer technical description with line-number references to the key logic.
1245
- path: /page/merchant-portal # Readers and Embeddables only
1246
- injectionType: DIRECT # Embeddables only: DIRECT or IFRAME
1247
- category: default
1248
- auxiliary: false # `true` means the sync SKIPS the file entirely — see Auxiliary
1249
- mermaid: |
1250
- graph TD
1251
- A[Request] --> B[Load documents]
1252
- ```
1253
-
1254
- For `components/prompts/new_*.meta.yml`, include `description` and `purpose: CUSTOM`; missing
1255
- `description` fails trunk validation, and missing/mismatched `purpose` leaves a post-promotion Prompt
1256
- overlay instead of pruning cleanly.
1257
-
1258
- > **Staging creates nothing in the database, so a `new_` component has no id yet — address it BY NAME.**
1259
- > `remits-cli test run --test "My Suite"`, not `--test <id>`. Component-to-component resolution and
1260
- > request-level addressing are name-based too; the component guides cover those. After a trunk sync the
1261
- > component has a real id and either form works.
1262
-
1263
- #### Step 3: Stage to Platform
1264
-
1265
- ```bash
1266
- remits-cli components stage
1267
- ```
1268
-
1269
- This uploads your local file changes to the platform's staging cache (Redis, 240-minute TTL). It does NOT commit anything. The platform cannot see your local edits until you stage them.
1270
-
1271
- **THE STAGE-BEFORE-RUN RULE:** You MUST run `remits-cli components stage` after EVERY file edit and BEFORE any test run or verification. The platform executes whatever version is in the staging cache at the moment the test starts. If you edit a file and run a test without staging first, the test runs the OLD code — not your changes. This is the single most common mistake. Never skip staging. The sequence is always: **edit → stage → run**.
1272
-
1273
- This applies to:
1274
- - Creating new components (the platform won't find them until staged)
1275
- - Editing existing components (the platform runs the previously staged version until you re-stage)
1276
- - Every iteration of the fix loop — every edit requires a fresh stage before the next test run
1277
-
1278
- #### Step 4: Verify the Change
1279
-
1280
- **Option A — Run Tests** (if Test components exist for this area):
1281
-
1282
- ```bash
1283
- remits-cli test run --test <TEST_ID_OR_NAME>
1284
- remits-cli test run --test "Invoice Tests" --names "specific test case"
1285
- ```
1286
-
1287
- Tests run on the platform against your staged snapshot. They stream results in real-time. If they fail, fix the code, re-stage, and re-run.
1288
-
1289
- Important test-runner constraints:
1290
- - `remits-cli test run` now defaults to `test` dataMode unless you explicitly pass `--data-mode prod`.
1291
- - **`--names` is delimited by `|`, and may be repeated.** A comma still splits a single `--names` value
1292
- (legacy behaviour), which is why a case name containing a comma used to be cut in half and match
1293
- nothing. Prefer `|` or repetition whenever a name might contain punctuation:
1294
- ```bash
1295
- remits-cli test run --test 13 --names "a case, with a comma|another case"
1296
- remits-cli test run --test 13 --names "a case, with a comma" --names "another case"
1297
- ```
1298
- - **A selector that matches no case FAILS the run.** It used to report `0 passed, 0 failed`,
1299
- `completed`, and exit 0 — indistinguishable from a suite where everything passed. The run now names
1300
- the unmatched selectors and lists the cases the suite actually declared, and exits non-zero.
1301
-
1302
- If no relevant Test component exists yet, consider creating one. Test components live in `components/tests/` and follow the same component structure. They provide permanent regression protection — every test you write today saves debugging time tomorrow.
1303
-
1304
- New test files use the `new_` prefix (e.g., `new_MyTest.groovy`) and no `id:` in the sidecar — see "Creating a component that does not exist yet" in Step 2. Run them **by name** (`remits-cli test run --test "My Test"`) until a sync assigns an id and renames the file.
1305
-
1306
- **How to write the Test itself is not a CLI concern** — what a suite can assert, how mocks behave across HTTP/relay boundaries, driving an embeddable in-process, and the front-stage-only rule all live in `guides/components/test-components.md`. Read that before authoring a suite.
1307
-
1308
- **Option B — Visual verification with Playwright** (for UI changes or when the user wants to "see it"):
1309
-
1310
- ```bash
1311
- # Generate a browser-accessible URL for an embeddable
1312
- remits-cli token --path embeddable/index/<EMBEDDABLE_ID>
1313
- ```
1314
-
1315
- This returns an `embeddableUrl`. Use Playwright to open and interact with it:
1316
-
1317
- ```bash
1318
- # Open the embeddable in a headed browser
1319
- playwright-cli open --headed "<embeddableUrl>"
1320
-
1321
- # Take a snapshot to see the current state
1322
- playwright-cli snapshot
1323
-
1324
- # Interact with elements
1325
- playwright-cli click "text=Submit"
1326
- playwright-cli fill "#amount" "500.00"
1327
-
1328
- # Verify specific content
1329
- playwright-cli eval "() => document.querySelector('.total-amount').textContent"
1330
- ```
1331
-
1332
- The `testMode` metadata confirms you're testing against staged changes, not production.
1333
-
1334
- **Two different token keys come back, for two different jobs.** When `--path` resolves to an
1335
- Embeddable, the response carries an `embedTokenKey` and a paste-ready `embedSnippet` alongside the usual
1336
- `tokenKey` / `embeddableUrl`:
1337
-
1338
- | Field | Use it for |
1339
- |---|---|
1340
- | `tokenKey` / `embeddableUrl` | Opening the page in a browser (Playwright, or clicking the link) |
1341
- | `embedTokenKey` / `embedSnippet` | The `<script>` embed loader — verifying the page as a HOST SITE embeds it |
1342
-
1343
- They are not interchangeable. The loader's request carries **no path**, so it resolves the component
1344
- purely from the embeddable-scoped token key's persisted context. The browser `tokenKey` names the account
1345
- preview URL; `embedTokenKey` names the host-loader credential. The response also echoes `injectionType` /
1346
- `renderMode` / `headMode`, which decide what a host actually receives
1347
- (`guides/components/embeddable-components.md`).
1348
-
1349
- **This works for a `new_` component that has never been synced.** The embed token key carries the
1350
- component NAME as well as its id, so a staged, id-less Embeddable is loader-addressable — you do not
1351
- have to sync it, or borrow another component's id, just to verify a host embed.
1352
-
1353
- **Option C — Use investigation tools** (for backend/data changes):
1354
-
1355
- For changes to Readers, Actions, or Rules that process data rather than display UI, verify by examining the data they produce:
1356
-
1357
- ```bash
1358
- # After triggering the component (via test or manual action), check the result
1359
- remits-cli tool --name "mcp_firestore_search" --input '{"accountId": <ID>, "collection": "<collection>", "limit": 5, "sort": [{"field": "_lastModifiedAt", "direction": "DESC"}]}'
1360
- ```
1361
-
1362
- For long-running backend verification, use the Action runner's own async mode (`executionMode:"async"`) and
1363
- poll by `actionRunId` rather than holding a single request open (see "Tool Execution Lifecycle" for why not
1364
- to also stack the CLI `--async` flag):
1365
-
1366
- ```bash
1367
- remits-cli tool --name "mcp_run_action" --input '{"accountId": <ID>, "actionId": <ACTION_ID>, "executionMode": "async", "actionInput": {...}}' --data-mode test
1368
- # then poll: {"controlAction":"status","accountId": <ID>, "actionRunId":"<actionRunId>"}
1369
- ```
1370
-
1371
- #### Step 5: Iterate If Needed
1372
-
1373
- If verification reveals issues, repeat the loop: **edit → stage → run**. Every iteration must include a fresh `remits-cli components stage` after your edits and before the next test run. Never run a test immediately after editing without staging first — the platform will execute the previous version, not your latest changes.
1374
-
1375
- Don't ask the user for permission to re-iterate — just do it. Only stop to ask if you're stuck or unsure about the intended behavior.
1376
-
1377
- If the work is tied to a support ticket:
1378
- - Use `remits-cli ticket status --status in_progress` once you have started substantive work.
1379
- - If a new reply arrives, re-read the ticket and incorporate the reply into your current plan.
1380
-
1381
- #### Step 6: Update Documentation
1382
-
1383
- Before committing, update metadata so the next session understands what changed:
1384
-
1385
- 1. **`.meta.yml` sidecars** — Update `summary`, `description`, and `mermaid` for each modified component. `summary` is what drives the compact component description in generated `account-info.json`; `description` is the fallback when no summary is set and is capped in that file.
1386
- 2. **`README.md`** — If the change affects account-level capabilities or workflows.
1387
- 3. **New components** — Always fill in `.meta.yml` immediately.
1388
-
1389
- `account-info.json` is read-only — never edit it. It regenerates automatically after sync. Component
1390
- entries prefer `summary`, fall back to capped `description`, and cap `mermaid`; relationships remain as
1391
- generated. On a trunk sync it describes the owning repo account. On a subscriber-initiated variant sync it
1392
- describes the subscribing account reached through the branch edge, even though the component files still
1393
- belong to the owner's repo.
1394
-
1395
- #### Temporary Experiment Workflow
1396
-
1397
- Use this when you need to prove a guard or assertion by temporarily making a local component fail. The staged
1398
- Redis cache can affect later test/tool runs, so always clear it after restoring the file:
1399
-
1400
- ```bash
1401
- # make temporary local edit
1402
- remits-cli components stage
1403
- remits-cli test run --test <id-or-name> --names "<case name>"
1404
- git restore <file>
1405
- remits-cli components clear --all
1406
- remits-cli components status
1407
- ```
1408
-
1409
- For narrower cleanup when only one staged component should be cleared:
1410
-
1411
- ```bash
1412
- remits-cli components clear --component-type Action --component-id 25
1413
- ```
1414
-
1415
- #### Step 7: Commit and Durable Sync
1416
-
1417
- Once verified and documented, create a normal git commit first:
1418
-
1419
- ```bash
1420
- git add -A
1421
- git commit -m "description of what changed and why"
1422
- git push origin <branch>
1423
- ```
1424
-
1425
- This separates the local failure boundaries cleanly:
1426
- 1. Local git commit
1427
- 2. Remote push
1428
-
1429
- After the push, run the **Component Integrity Rules** safety checks before any durable platform sync. Do not run
1430
- `remits-cli components sync` when local files, `account-info.json`, and live inventory disagree about component
1431
- IDs or when unexpected deletes/renumbers are present.
1432
-
1433
- Only after those checks pass, and only when the user intends to promote the repo to the platform database:
1434
-
1435
- ```bash
1436
- remits-cli components sync
1437
- git pull --ff-only origin <branch>
1438
- ```
1439
-
1440
- `remits-cli components sync` is the authoritative platform-sync step. It does not perform local git operations,
1441
- and it is capable of reconciling creates/deletes/renames from the remote repository into the database. Treat it
1442
- as a gated promote/reconciliation command, not as an exploratory command or fallback.
1443
-
1444
- The CLI now returns the post-sync branch SHA from the platform and verifies that your `git fetch` and final `git pull --ff-only` land on that exact commit. If that SHA does not match `origin/<branch>` or local `HEAD`, stop immediately and investigate the race or branch drift instead of guessing.
1445
-
1446
- `remits-cli components commit` still exists as a convenience wrapper, but agents should not call unsupported
1447
- subcommand help variants such as `remits-cli components commit --help` to discover behavior. Consult this skill,
1448
- the CLI source, or `remits-cli components` documentation instead. If an exploratory or commit command behaves
1449
- unexpectedly, stop and inspect the repo-local session log before running any mutating follow-up command.
1450
-
1451
- **Git is required for durable sync.** The platform syncs by pulling from the git remote (`GitHubClient.syncFromRepository`). If `git push` fails, the server has nothing new to sync. You can still **stage** and **test** without git — only durable sync requires it.
1452
-
1453
- #### Step 8: Close the Ticket
1454
-
1455
- If the request came from a support ticket, the task is not complete until you update the ticket lifecycle yourself:
1456
-
1457
- 1. Re-read the ticket if needed to confirm the latest state and replies.
1458
- 2. If the work is done and verified, call `remits-cli ticket complete` and include a concise resolution summary.
1459
- 3. If you cannot finish, use `remits-cli ticket status` or `remits-cli ticket release` with clear notes so the next agent can continue.
1460
- 4. Do this automatically. The human user should not need to instruct you to update the ticket.
1461
-
1462
- Options:
1463
- ```bash
1464
- --message "commit msg" # Commit message (default: auto-generated timestamp)
1465
- --allow-empty true # Allow empty git commits
1466
- ```
1467
-
1468
- ### User Confirmation Preferences
1469
-
1470
- Some users want to review every change before staging. Others want you to move fast and only stop if something breaks. **Pay attention to how the user communicates:**
1471
-
1472
- - If they say "just fix it" or "go ahead" — move through the loop without asking for confirmation at each step. Stage, verify, commit.
1473
- - If they say "show me first" or "wait before committing" — pause at the appropriate step.
1474
- - If they say "you don't need to ask me" or "stop asking" — remember this preference and work autonomously through the full loop.
1475
-
1476
- The default should be: make the change, stage it, verify it, and present the results. Only block on the user when you're genuinely unsure about intent.
1477
-
1478
- ## Component Resolution: Staging Cache vs DB (which "version" actually runs)
1479
-
1480
- When the platform executes a component it resolves the source from one of two places, then compiles it
1481
- behind an in-memory cache. Understanding this is the difference between "my change isn't working" guesses
1482
- and a precise diagnosis.
1483
-
1484
- ### The three source layers + the compile cache
1485
-
1486
- 1. **CLI staging cache (Redis, 240-min TTL).** Branch + user + account scoped overrides written by
1487
- `remits-cli components stage`. These shadow the layers below **only during CLI/test-mode execution**
1488
- (see "When staged overrides apply" below).
1489
- 2. **Committed branch variants (`ComponentVariant`, MySQL).** Durable, branch-scoped overlays of a
1490
- component. Unlike staging these are **not** user-scoped, do **not** expire, and **do** apply to normal
1491
- production traffic — for the accounts that subscribe to that branch. See
1492
- "Branched Component Variants" below. Most accounts have none, in which case this layer is inert.
1493
- 3. **Database trunk row (the committed live component).** What `mcp_component_view` reads, what an
1494
- unsubscribed prod run uses, and what a trunk `commit` writes to.
1495
-
1496
- Resolution order is **staged → variant → trunk**, and each layer *layers over* the one beneath it rather
1497
- than replacing it: a payload that only carries `source` inherits `path`, `objectType`, `inputSchema` etc.
1498
- from the layer below. A staged edit made on a variant branch therefore layers over **that variant**, not
1499
- over trunk.
1500
-
1501
- Plus the compile cache:
1502
-
1503
- - **Compiled-closure cache (`BaseClosureDomain.CLOSURE_CACHE`).** An in-memory, **per-JVM-instance** Guava
1504
- cache of the parsed closure, keyed by `(componentId, type, compileSignature)`. This is why a change that
1505
- is correctly in the DB can still execute stale on a running instance — see "Stale after sync" below.
1506
-
1507
- ### Staging cache key format
1508
-
1509
- ```
1510
- # default lane (no workspace)
1511
- account:<accountId>:cli:<cliUserId>:components:<branch>:<family>:id:<componentId>
1512
- account:<accountId>:cli:<cliUserId>:components:<branch>:<family>:name:<normalizedName>
1513
-
1514
- # workspace lane
1515
- account:<accountId>:cli:<cliUserId>:components:<branch>:ws:<workspace>:<family>:id:<componentId>
1516
- ```
1517
-
1518
- The staging scope is therefore **account + cli user + branch + workspace**. `workspace` is optional and
1519
- absent by default; see "Working alongside other agents: the staging WORKSPACE" above.
1520
-
1521
- `<family>` is the lowercased component family (`reader`, `action`, `test`, `embeddable`, ...). Both an
1522
- `id:` and a `name:` key are written per stage. The entry value carries: `kind` (the family), `type` (the
1523
- component's OWN type enum such as `ObjectType`/`RuleType`, or absent — **never** the family), `hash`,
1524
- `updatedAt`, the staged content field(s) (`source`/`prompt`/`html`/`javascript`/`schema`/
1525
- `inputSchema`/`previewData`), and `.meta.yml` metadata fields such as `description`, `summary`, `mermaid`,
1526
- `path`, Embeddable `injectionType`, `category`, and Schema flags (`enableTrigger`, `enableFullText`, `enableRAG`, `enableRevisions`,
1527
- `enableBigQuerySync`, `enableRules`, `anchor`, `auxiliary`).
1528
-
1529
- ### How the platform picks staged vs DB (the compile signature)
1530
-
1531
- At compile time the platform computes a **signature** that tells you which layer won:
1532
-
1533
- - Staged override present → `compileSignature = "cli:<hash>"` (the staged content hash).
1534
- - Committed branch variant → `compileSignature = "variant:<variantId>:<hash12>"`.
1535
- - Neither → `compileSignature = "version:<N>:<sourceHash12>"` (the DB row version plus a source hash
1536
- prefix, so source changes cannot reuse a stale compile entry on the same instance).
1537
-
1538
- The three namespaces are distinct on purpose: a component's staged, variant, and trunk closures coexist in
1539
- the compile cache without colliding.
1540
-
1541
- That signature is logged. Querying for it is the single most reliable way to know what ran:
1542
-
1543
- ```bash
1544
- remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","timeRange":"1h","filter":"Using Cached BCD"}' --data-mode prod
1545
- ```
1546
-
1547
- > **Pass the bare phrase — never hand-write a `textPayload:` filter.** In production the platform's
1548
- > logback encoder writes every `log.*` line to **`jsonPayload.message`**; only `println`/stdout lands in
1549
- > `textPayload`. A `textPayload:"..."` filter therefore matches **zero** rows for nearly every platform
1550
- > log line, and zero rows is indistinguishable from "it never happened" — which has already caused a real
1551
- > misdiagnosis. `mcp_system_logs` now widens a bare phrase (and any `textPayload:"..."` clause) to cover
1552
- > both shapes, and echoes the executed filter back as `filterApplied`. A filter that names `jsonPayload`
1553
- > explicitly is passed through untouched.
1554
-
1555
- `Using Cached BCD [ID: 230, Type: Action, Signature: cli:08cc...]` → ran a **staged** override.
1556
- `...Signature: variant:14:9f2c1a...]` → ran a **committed branch variant**.
1557
- `...Signature: version:37:abc123def456]` → ran the **committed trunk** version.
1558
-
1559
- ### When staged overrides apply
1560
-
1561
- Staged overrides resolve whenever the execution carries a **CLI-scoped TestMode** — i.e.
1562
- `TestMode.branchName` and `TestMode.cliUserId` are set. That includes:
1563
-
1564
- - `remits-cli test run`
1565
- - `remits-cli tool`
1566
- - `remits-cli tools`
1567
- - tokenized runs minted with a branch-aware CLI token
1568
-
1569
- For `remits-cli tool`, the CLI TestMode now stays active for the **entire tool execution**, not just the
1570
- top-level tool lookup. That means nested `reader()`, `action()`, `utility()`, `account.getTool()`, and
1571
- similar component resolution inside the tool also see the staged branch context.
1572
-
1573
- `dataMode` is separate from staged resolution:
1574
-
1575
- - `branchName` + `cliUserId` decide whether staged components can resolve.
1576
- - `dataMode:test|prod` decides which data surface the tool/test/token runs against.
1577
-
1578
- So a `remits-cli tool --data-mode prod` call can intentionally execute **staged code against prod data**
1579
- for investigation or recall testing, while a normal live webhook / non-CLI runtime path with no CLI
1580
- TestMode still uses the committed DB source. Staging remains a dev/verification surface, not a deploy.
1581
-
1582
- ### Diagnosing which version is in play
1583
-
1584
- - **See staging metadata for a component:** `mcp_component_view` (omit `fieldName`) returns `staging` /
1585
- `stagedFields`, telling you whether a staged entry exists and which fields are staged.
1586
- - **Inspect the raw staged entry + TTL in Redis:** use `mcp_cache`.
1587
- ```bash
1588
- # find staged entries for one component
1589
- remits-cli tool --name mcp_cache --input '{"action":"scan","pattern":"account:52:cli:*:components:*:reader:id:181","includeValuePreview":true}' --data-mode prod
1590
- # dump one exact key
1591
- remits-cli tool --name mcp_cache --input '{"action":"inspect","key":"account:52:cli:23:components:main:reader:id:181"}' --data-mode prod
1592
- ```
1593
- The preview shows `kind`/`type`/`hash`/`updatedAt` + a source snippet — confirm it's your content and
1594
- that `type` is NOT the family (a family value in `type` is a tool bug that crashes hydration, e.g.
1595
- `No enum constant ObjectType.reader`).
1596
- - **Confirm the DB version:** `mcp_component_view` reads the live DB source directly (no staging, no compile
1597
- cache), so it is the source of truth for "what was committed."
1598
- - **Ask the CLI what is staged:** `remits-cli components status` lists this lane's staged entries,
1599
- including staged fields, aliases, hashes, and TTLs. The default terminal output is concise; pass `--json` or
1600
- `--verbose` when you need the full staged-entry payload. `remits-cli components clear` removes those entries
1601
- when you intentionally want to fall back to DB source.
1602
-
1603
- ### Working alongside other agents: the staging WORKSPACE
1604
-
1605
- Staging is scoped by `(account, cli user, branch, workspace)`. Account and user are fixed for a repo, so
1606
- **without a workspace the git branch is the only isolation axis** — and `components stage` uploads the
1607
- ENTIRE repo and REPLACES the lane rather than merging into it. Two agents on one branch therefore
1608
- overwrite each other, and a `components commit` clears the lane out from under the other one.
1609
-
1610
- If more than one agent is working on the same branch, give each its own workspace:
1611
-
1612
- ```bash
1613
- git worktree add ../repo-agent-a forked # one checkout per agent, same branch
1614
- cd ../repo-agent-a
1615
- remits-cli workspace use --auto # names the lane after this directory
1616
- remits-cli components stage # isolated: nobody else sees it, nobody overwrites it
1617
- remits-cli test run --test 42
1618
- remits-cli token --path /page/whatever
1619
- ```
1620
-
1621
- A workspace narrows STAGING and nothing else. A commit still targets the same branch and the same owner
1622
- account, and the run still resolves whatever committed variant branch the account subscribes to — so it
1623
- does **not** have the side effects of inventing a throwaway git branch per agent (which would make a
1624
- commit write `ComponentVariant` overlays for a branch nobody subscribes to).
1625
-
1626
- - `.remits-cli/workspace` is per-checkout and gitignored, so each worktree keeps its own lane.
1627
- - Precedence: `--workspace NAME` > `REMITS_WORKSPACE` > `.remits-cli/workspace` > shared default lane.
1628
- - `--no-workspace` targets the shared lane for one command without clearing the file.
1629
- - Every stage / test run / token / clear prints its `Staging lane:` — if a change seems to have had no
1630
- effect, check that line FIRST. A mismatched lane resolves committed source, which looks identical to
1631
- "the stage did not work".
1632
- - `remits-cli components status` lists every lane staged on the branch, so you can see whether another
1633
- agent is working alongside you.
1634
- - `remits-cli components clear --all` is scoped to YOUR lane and never touches another agent's.
1635
-
1636
- ### Stage / sync / clear with remits-cli
1637
-
1638
- - `remits-cli components stage` writes local file changes into the Redis staging cache for the current
1639
- branch/user/workspace scope. This is the normal edit/test loop.
1640
- - `remits-cli components stage --changed-only` stages just the components this working tree edited. It
1641
- does NOT reconcile, so entries for components it did not mention are left alone rather than deleted.
1642
- Useful on a large repo; a full stage is still the default and the safest.
1643
- - `remits-cli components status` shows which branch/variant world the checkout resolves, plus staged entries,
1644
- staged fields, aliases, hashes, and TTLs. Use `--json` or `--verbose` for the full staged-entry payload.
1645
- - **A full `stage` makes the `.meta.yml` AUTHORITATIVE.** Staging layers a payload over what is already
1646
- staged, which is what lets `mcp_component_edit` write a single field without blanking the others. But a
1647
- `remits-cli components stage` sends the whole sidecar, so a key you DELETE from a sidecar is removed from
1648
- the staged entry rather than lingering — "restore the file and re-stage" restores the staged state, which
1649
- is the only mental model that is safe to have. Content fields still layer (they come from separate files),
1650
- so a partial stage is unaffected.
1651
- - `remits-cli components clear` drops staged entries when you intentionally want to fall back to committed DB
1652
- source. An empty staging scope is clean state, not a failure.
1653
- - `remits-cli components sync` syncs the DB from the pushed git remote and then clears staged entries for the
1654
- synced components, so a clean promotion leaves a clean staging cache. Because sync reconciles the remote repo
1655
- into the live component database, it must pass the Component Integrity Rules first. Never use sync only to
1656
- clear staging, to recover from a mismatched ID, or to retry after an unexpected create/delete/rename response.
1657
-
1658
- ### Stale after sync / commit (the in-memory compile cache)
1659
-
1660
- After a `git sync` or a `commit` updates the DB source, a **running instance can keep executing the
1661
- previously-compiled closure** until the version-keyed signature changes and that instance's
1662
- `CLOSURE_CACHE` misses (or the instance recycles). Symptoms: `mcp_component_view` shows the new source, but
1663
- behaviour (or a freshly-staged entry produced by an edited *tool*) still reflects the old code. This is the
1664
- standard Grails no-hot-reload caveat — it is environmental, not a code defect. Verify the live entry/source
1665
- with `mcp_cache` / `mcp_component_view`, and if a platform/tool source change must take effect immediately,
1666
- the platform owner recycles the instance.
1667
-
1668
- ## Account Resolution: how a request travels the account graph
1669
-
1670
- Component inheritance, branch variants, and where data physically lives are all decided by **how the
1671
- current request reached the executing account**. Read this before debugging *"my subscriber isn't picking
1672
- up the branch"* or *"why is this account reading the wrong collection"* — those are almost always
1673
- resolution questions, not component bugs.
1674
-
1675
- > The model behind it — the linear inheritance walk, the ambiguity rule, the three orthogonal edge
1676
- > properties, and path-scoped storage namespaces — is in **`platform-overview.md` → *Account Structure***,
1677
- > which is already loaded. What follows is how to *observe* it from the CLI.
1678
-
1679
- **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id`, stamped onto
1680
- the `Object` / `Event` / `Alert` / `ObjectLog` records a run creates, so async workers re-resolve on the
1681
- same branch. A **null** anchor means "walk the structural chain". Entry points that already know the
1682
- branch supply an anchor; from the CLI you supply it explicitly with `--as-account`, `--variant-branch`, or
1683
- by using the edge's own host.
1684
-
1685
- ### Seeing an account's edges
1686
-
1687
- ```bash
1688
- remits-cli tool --name mcp_account_view --input '{"accountId": 101}' --data-mode prod
1689
- # then read: resolution.relationships, resolution.resolvedDatabaseName, resolution.componentBranch
1690
- ```
1691
-
1692
- `resolution.relationships` returns one entry per structural link, primary first, each carrying its own
1693
- `branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so
1694
- this block is the **only** place that answers "how many parents does this account really have, and which
1695
- link carries what?"
1696
-
1697
- ### When a subscription "doesn't work"
1698
-
1699
- 1. Does the account have a primary parent, or is it membership-only? Count `resolution.relationships` and
1700
- see which is `primary: true`. (Membership-only **and** several edges ⇒ ambiguous ⇒ trunk, by design.)
1701
- 2. Which **edge** carries the `branchName`? `remits-cli components branch <name> --subscribers` prints
1702
- `via primary|membership edge -> parent N`.
1703
- 3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
1704
- account's own edge selects the branch, exactly as production would.
1705
- 4. Check the resolved layer, not the source text: `testComponentSource`, or the `variant:<id>:<hash>`
1706
- compile signature (see "Diagnosing a variant").
1707
-
1708
- ### The same block answers the non-branch questions
1709
-
1710
- - *"Why are this account's documents not where I expect?"* → compare `databaseName` vs
1711
- `resolvedDatabaseName`, and check for a `databaseName` on one of the `relationships` edges — it applies
1712
- to everything below that edge.
1713
- - *"Why does this hostname land on the wrong account?"* → compare `domainName` vs `resolvedDomainName` and
1714
- the edge `domainName`. An **edge** host wins over the account's own, and additionally supplies the path
1715
- travelled — which is what makes that edge's branch variants apply.
1716
-
1717
- **Users are not part of this graph** (see `platform-overview.md`). Use `mcp_account_user_admin`
1718
- (`action:'users'`, `action:'user'`, or `action:'user_update'`) for user membership and account-scoped user
1719
- fields. Drop to `mcp_sql_query` against `user` / `user_account` only for raw join-table evidence.
1720
-
1721
- ## Branched Component Variants (per-account component overrides)
1722
-
1723
- When one account — often a customer nested several levels down — needs *slightly* different behavior from
1724
- a component owned by its platform or product account, the answer is a **branch variant**: a durable,
1725
- branch-scoped overlay of that component, resolved only by accounts subscribed to that branch. The wrong
1726
- answer is per-account `if/then` logic inside the origin component.
1727
-
1728
- > **`features/subscriber-branch-promotions.md` (`mcp_get_guide`) is the guide for this.** It owns the
1729
- > mental model, the five scenarios you will actually meet, the full promotion procedure, what happens to a
1730
- > `new_` component across a promotion, merge-conflict resolution file by file, removals, and the approval
1731
- > gates for running a promotion as a coding agent. **Load it before promoting anything.** This section
1732
- > covers only the CLI surface and the traps that bite at the command line.
1733
-
1734
- Three facts that everything else follows from:
1735
-
1736
- - **A branch is not an account.** Subscription is many-to-many, it applies to the subscribing account
1737
- *and its descendants*, and a subscribing account still has its own trunk components which merge on top.
1738
- - **A variant keeps the origin's component id** — it is an overlay, not a copy. That is what makes drift
1739
- computable.
1740
- - **Sparseness is computed at sync time, not declared.** A git branch physically contains every file; only
1741
- the ones whose content *differs from trunk* become variants. A file that is absent becomes a
1742
- **tombstone**; a file whose id prefix is not a live trunk id (`new_Foo.groovy`) becomes a **branch-only**
1743
- component keyed by name.
1744
-
1745
- ### Which world does your working tree resolve? (read this before you run anything)
1746
-
1747
- You will work from **two different checkouts of the same repo**, and they behave differently on both ends
1748
- of the loop. The rule turns entirely on **trunk vs non-trunk**:
1749
-
1750
- | Working tree | Staging scope | A run resolves | `components sync`/`commit` writes |
1751
- |---|---|---|---|
1752
- | **trunk** (`main`, or whatever `account-info.json` says) | that branch | trunk + **each account's subscribed** variant branch (production semantics) | the **live component rows** — full reconcile, creates/updates/**deletes** |
1753
- | **any other branch** (`feature_branch`) | that branch | trunk + **`feature_branch`** overlays | **`ComponentVariant` overlays on that branch only** — never touches trunk rows |
1754
-
1755
- Do not infer this from the branch name. Ask:
1756
-
1757
- ```bash
1758
- remits-cli components status
1759
- ```
1760
-
1761
- ```
1762
- Working tree: VARIANT BRANCH "feature_branch" (trunk is "main")
1763
- runs resolve: trunk + the 'feature_branch' variant overlays
1764
- commit writes: ComponentVariant overlays on 'feature_branch' (never touches trunk rows)
1765
- variants stored on this branch: 3
1766
- subscribing accounts: 101 (Acme Child)
1767
- ```
1768
-
1769
- **The precedence trap that costs the most time:** `variantBranch` **outranks every account's
1770
- subscription**. So running a Test suite that asserts *production* semantics from a **variant checkout**
1771
- pins every account in that suite — including fixture accounts subscribed to their own generated branches —
1772
- to your branch, where they have no variants, and they all read trunk. The suite fails in a way that looks
1773
- exactly like a resolution regression. (Live example: a branch-variant suite scored 4/15 from a variant
1774
- checkout and 15/15 from trunk, with no code difference.) **Run branch-variant suites from trunk, or pass
1775
- `--variant-branch none`.** Before concluding "variant resolution is broken", re-run from trunk.
1776
-
1777
- **Two other things differ from trunk while you work on a branch:**
1778
-
1779
- - **Keep the origin id in the filename.** `50_ExtractInvoice.groovy` on the branch overlays Action 50.
1780
- That is what preserves component identity and lets drift be computed against the origin.
1781
- - **Nothing is renamed.** A variant sync never renames files and never repoints the account's trunk
1782
- branch. A `new_*` file stays `new_*`.
1783
-
1784
- **Branch-local `account-info.json` can describe a subscriber.** On a variant branch the repository is
1785
- still the OWNER's component repo — files overlay that owner's trunk ids and sync writes overlays owned by
1786
- that owner. But when the checkout was synced from a subscribing account reached through an
1787
- `AccountRelationship` edge, and the branch has **exactly one** subscriber, the branch's
1788
- `account-info.json` is intentionally rooted at that subscriber. With **more than one** subscriber the
1789
- refresh is skipped (the sync result says so under `accountInfo.skipped`/`reason`) and the file keeps
1790
- describing the owner, which is at least true for all of them. Overlay ownership is unaffected either way.
1791
-
1792
- Read `resolution` before acting from any checkout — in particular `resolution.accountId` (the account this
1793
- checkout should be treated as; **read this, not the top-level `id`**), `resolution.role`,
1794
- `resolution.componentOwnerAccountId` (whose trunk ids the files overlay and whose overlays a sync writes),
1795
- and `resolution.componentBranch` / `resolution.scopeAccountId` (the branch and the path anchor that made
1796
- this subscriber resolution possible). If a variant checkout's `account-info.json` is stale
1797
- and still names the owner, run the first repair sync with an explicit subscriber target
1798
- (`remits-cli components sync --account-id 101`), then `git pull --ff-only`.
1799
-
1800
- ### Two levers, two different questions
1801
-
1802
- - **`--as-account <id>`** changes **who** the run executes as, so that account's own edge picks the branch.
1803
- Answers *"what does customer X actually get?"* Only narrows downward (the target must be reachable from
1804
- an account you already have access to). It works for a `parentId`-less, membership-only subscriber too:
1805
- the anchor is derived from the branch you name, or from the account's single branch subscription. It
1806
- **refuses to guess** when an account has several edges each carrying a different branch — the run then
1807
- resolves trunk, and you must name the branch.
1808
- - **`--variant-branch <name>`** changes **which branch**, from any checkout. Answers *"what does branch Y
1809
- look like?"* — most useful for verifying a freshly committed branch **before** any edge subscribes to
1810
- it. `--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the
1811
- branch.
1812
-
1813
- Both work on `remits-cli test run` and `remits-cli token`; `--variant-branch` also works on
1814
- `remits-cli tools` and `remits-cli tool`, so tests, browser URLs, tool discovery, and tool execution can
1815
- all inspect the same committed variant world.
1816
-
1817
- The strongest end-to-end proof for a UI-visible variant is a token, not a log line:
1818
-
1819
- ```bash
1820
- remits-cli token --path embeddable/index/50 --as-account 101 # subscriber -> variant
1821
- remits-cli token --path embeddable/index/50 # owner -> trunk
1822
- ```
1823
-
1824
- Each answer carries a **`resolution`** block for the account the token executes as — `role`,
1825
- `componentBranch` and its owner, `resolvedDatabaseName`, `resolvedDomainName`, `scopeAccountId`, and a
1826
- one-sentence `summary`. Read it before opening the URL: `accountId` alone does not say whether a branch
1827
- overlay applies or which storage namespace the page will read, and those are exactly what
1828
- `--as-account` is being used to change.
1829
-
1830
- Separate branch fields, because these questions answer differently and can disagree:
1831
-
1832
- | Field | Answers |
1833
- |---|---|
1834
- | `componentBranch` | what **this token** will resolve |
1835
- | `componentBranchSource` | `probe` (an explicit `--variant-branch`), `subscription` (the account's edge), or `trunk` |
1836
- | `subscribedComponentBranch` | what the **account graph** says, independent of this token |
1837
- | `componentBranchAnchored` | whether **this resolution actually reached** that subscription |
1838
-
1839
- A single `componentBranch` field would have reported `trunk` under `--variant-branch X`, for a token that
1840
- resolves `X`.
1841
-
1842
- **Subscribing to a branch and resolving through it are different facts.** An account subscribes on an
1843
- edge; a *request* resolves through that edge only when something named the path (`--as-account`, the
1844
- edge's own host, an explicit `--variant-branch`). An account with no `parentId` and several upward links
1845
- resolves trunk by design — the resolver refuses to guess a path. So this is a normal, explainable state:
1846
-
1847
- ```
1848
- "componentBranch": null, // this token resolves TRUNK
1849
- "componentBranchSource": "trunk",
1850
- "subscribedComponentBranch": "forked", // ...but the account does subscribe
1851
- "componentBranchAnchored": false // ...and nothing anchored this request to it
1852
- ```
1853
-
1854
- Reading `componentBranch` alone there tells you the account is on trunk, which is true — and leads you to
1855
- conclude it has no branch, which is false. If several edges carry branches, none is picked for you:
1856
- `subscribedComponentBranch` is `null` and `subscribedComponentBranches` lists them.
1857
-
1858
- Under a probe, `componentOwnerAccountId` is the **nearest account on the token's resolution path that owns
1859
- variant rows for X** — not an edge lookup, which answers `null` for the ordinary case of a variant
1860
- committed before anything subscribes to it. `null` means no account on that path owns rows for X, and what
1861
- that implies depends on the account, so read `summary`:
1862
-
1863
- - **on trunk** — the page resolves what it would without the probe; the branch is not committed yet.
1864
- - **resolving through a subscription** — the probe **suppresses** it. `variantBranch` outranks the
1865
- subscription before it is consulted, so the page resolves **trunk**, not the overlays that account
1866
- normally gets. Drop `--variant-branch` to see the subscription.
1867
- - **subscribed but not anchored** — it was already resolving trunk before you probed. Dropping
1868
- `--variant-branch` will *not* by itself show you the branch; name the path as well.
1869
-
1870
- The page itself then states the same facts. Its hidden `remits-session-info` line — which appears in an
1871
- accessibility snapshot with no script — carries `Account` (what it runs as), `Addressed Account` (what
1872
- the URL/token named), `Data Lane`, `Component Branch`, `Variant Applied`, and `Scope Account`.
1873
-
1874
- **`Component Branch` is the branch SELECTED; `Variant Applied` is what actually overlaid.** They are two
1875
- fields because a branch can be selected and overlay nothing: `--variant-branch missing_branch` reports
1876
- `Component Branch: missing_branch` while the page renders trunk components. So read `Variant Applied` —
1877
- the `ComponentVariant` row id this page's component resolved through, or `none` — when the question is
1878
- "did my variant apply?". That is a far sharper signal than inspecting the rendered markup for a style you
1879
- expected.
1880
-
1881
- If `components status` shows staged entries that you cannot safely clear, isolate verification in an
1882
- unused staging namespace instead of deleting someone else's cache:
1883
-
1884
- ```bash
1885
- remits-cli test run --test "Invoice Tests" --branch promotion-check-empty --variant-branch none --as-account 101
1886
- remits-cli token --path embeddable/index/50 --branch promotion-check-empty --variant-branch none --as-account 101
1887
- ```
1888
-
1889
- Here `--branch` is only the CLI staging-cache namespace, and `--variant-branch none` keeps runtime
1890
- resolution on production/subscription semantics. Do **not** use this pattern with `components sync` or
1891
- `components commit`: for those commands `--branch` is the GitHub branch to reconcile.
1892
-
1893
- ### The SDLC is identical on a variant branch
1894
-
1895
- ```bash
1896
- git checkout feature_branch # or: git checkout -b feature_branch
1897
- # edit components/actions/50_ExtractInvoice.groovy (KEEP the trunk id)
1898
- remits-cli components stage # Redis, scoped to this branch — same as always
1899
- remits-cli test run --test "Invoice Tests" # the feature_branch world
1900
- remits-cli test run --test "Invoice Tests" --as-account 101 # ...as the real subscriber
1901
- git add -A && git commit -m "..." && git push origin feature_branch
1902
- remits-cli components sync --dry-run # inspect overrides/additions/tombstones without writes
1903
- remits-cli components sync # writes ComponentVariant overlays ONLY
1904
- ```
1905
-
1906
- Promotion back to trunk is a **git** operation followed by a **trunk** sync — the platform merges nothing
1907
- for you, and merging a branch promotes its **deletions** as hard deletes. Do not improvise it: follow
1908
- `features/subscriber-branch-promotions.md`. For a real trunk promotion with many `new_` files, use
1909
- `remits-cli components sync --summary --timeout-ms 300000`; the platform may need longer than the default
1910
- 60 seconds to create rows, push rename/meta commits, and regenerate account metadata.
1911
-
1912
- ### Where am I in the promotion loop?
1913
-
1914
- ```bash
1915
- remits-cli components promotion # the branch you are standing on
1916
- remits-cli components promotion --branch forked --json
1917
- ```
1918
-
1919
- **Run this before every promotion step and after every one.** It reports only — no git, no writes — and it
1920
- reads the remote (which is what the platform syncs from, not your working tree). It works from either
1921
- checkout, because it resolves the branch's owner itself. It exits non-zero while blockers remain, so treat
1922
- that as "do not proceed".
1923
-
1924
- | Phase | Meaning | Next |
1925
- |---|---|---|
1926
- | `converged` | branch == trunk | nothing to do; this is also what a **finished** promotion looks like |
1927
- | `ready` | ahead of trunk, current with it | promote |
1928
- | `diverged` | behind trunk | `git merge <trunk>` into the branch, re-sync, re-read the plan |
1929
- | `awaiting-merge-back` | fully contained in trunk, trunk has moved on | **steps 4–5 are owed** — merge trunk back and re-sync |
1930
-
1931
- Two things it tells you that nothing else does:
1932
-
1933
- - **Which commit the stored counts describe, on BOTH axes.** An overlay is stored only while a file
1934
- differs from trunk, so the stored set is a statement about a (branch, trunk) pair and either side moving
1935
- makes it stale: `[STALE — does not match the branch HEAD]` (the branch was pushed since) or
1936
- `[STALE — matches the branch HEAD, but trunk has moved since]`. The second is the easy one to miss —
1937
- measured live, a branch at its own synced HEAD reported 2 stored overlays against a real plan of 21.
1938
- - **That a promotion is unfinished.** `awaiting-merge-back` is what a promotion that *looked* successful
1939
- leaves behind: trunk is correct and its tests pass, while the subscriber silently keeps resolving its
1940
- pre-promotion overlays — including for any fix made afterwards. **A trunk sync succeeding is step 2 of 5,
1941
- not completion.**
1942
-
1943
- ### Subscribing, unsubscribing, retiring
1944
-
1945
- ```bash
1946
- remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>] [--dry-run] [--confirm-primary-edge]
1947
- remits-cli components branch <name> --unsubscribe <accountId>
1948
- remits-cli components branch <name> --retire [--force]
1949
- ```
1950
-
1951
- Branch administration commands act **as the account you are running from**, which is treated as the branch
1952
- **owner** — run them from the owner's trunk checkout. Authorization is downward-only.
1953
-
1954
- - **`--subscribe` sets the branch on an edge that must already exist — it cannot create one.** Failure
1955
- reads *"Account N has no relationship edge to subscribe; add a membership edge first"*. Create it with
1956
- `mcp_account_user_admin` `action:'edge_add'` (`targetAccountId` = the child, `parentAccountId` = the
1957
- owner), which also accepts `branchName` so you can create and subscribe in one call.
1958
- - **Many older accounts have no relationship row at all** — the edge table was added after the fact and
1959
- never backfilled, so an account whose parent link is only `Account.parentId` resolves fine but has
1960
- nothing to attach to. `mcp_account_user_admin` `action:'reparent'` with the account's *current* parent
1961
- is the one-call repair: it creates the missing primary edge and changes nothing else.
1962
- - **When the account has several parents, say which edge you mean** with `--parent-account <ownerId>`.
1963
- Without it the CLI picks the edge to the owner whose branch you are managing, then falls back to the
1964
- account's primary edge — which may not be the one you intended. `--subscribers` prints
1965
- `via primary|membership edge -> parent N` so you can confirm.
1966
- - **Primary-edge subscriptions are structural.** If the selected edge is the account's `parentId` edge,
1967
- subscribing it makes that account and descendants resolve the branch by default even though `parentId`
1968
- still points at the same parent. The server refuses this write unless you pass
1969
- `--confirm-primary-edge`; run `--dry-run` first and prefer a membership edge for fork/pilot
1970
- subscriptions.
1971
- - **Retiring is explicit.** Deleting the *git* branch does **not** remove its overlays; subscribers would
1972
- keep resolving a branch that no longer exists. `--retire` refuses while subscribers remain unless you
1973
- pass `--force`.
1974
-
1975
- **Every component kind can be varied** — `schema`, `reader`, `action`, `embeddable`, `i18n`, `htmltemplate`,
1976
- `rule`, `test`, `utility` (agent), `tool`, `prompt` — including tombstones and branch-only additions. A
1977
- **schema** variant deserves the same care as a trunk schema change: it can change the JSON schema *and*
1978
- `collectionName`, so it changes validation and where the subscriber's documents physically land. A
1979
- branch-only agent (a `new_*` file under `components/agents/`) defaults to `type: AI` so agent lookups
1980
- find it.
1981
-
1982
- A variant speaks the same `.meta.yml` vocabulary trunk does; keys outside that set are ignored on purpose,
1983
- because trunk cannot express them either — allowing them would mean a branch behaves one way and silently
1984
- loses that behavior the moment it is promoted. **One documented exception: an agent's `tools:` list cannot
1985
- be overridden by a variant.** It maps to a GORM association that `agent()` populates from the trunk row, so
1986
- a `tools:` list on a branch is ignored. Change trunk, or have the agent select tools at runtime.
1987
-
1988
- ### Danger profile on a variant branch (different, not absent)
1989
-
1990
- The Component Integrity Rules' worst case — *"a missing file hard-deletes a live component"* — **does not
1991
- apply on a variant branch.** Variant sync writes overlay rows only; it cannot create, delete, rename, or
1992
- overwrite a trunk component. That makes a variant branch a genuinely safer place to iterate.
1993
-
1994
- The analogous hazard is different, and you must still respect it:
1995
-
1996
- - **A trunk component with no file on the branch becomes a TOMBSTONE**, which *hides* that component from
1997
- every subscriber. It is reversible (restore the file, re-sync) and never touches trunk — but to a
1998
- subscribing account it looks exactly like the component was deleted. Confirm every omission is
1999
- deliberate before syncing.
2000
- - **Deleting a file means "remove the component", not "stop overriding it."** To withdraw an override,
2001
- make the file identical to trunk again — the sync then removes the variant row.
2002
- - **Keep the branch rebased.** A branch physically carries every file, so anything trunk added *after* the
2003
- branch was cut is absent from it. The sync now asks git which of those absences are real deletions
2004
- (comparing against the merge base) and refuses to tombstone the rest, reporting them as
2005
- `skipped: absent from the branch but never deleted on it`. Treat any such entry as "merge trunk in" —
2006
- if an older sync already stored one of those absences as a tombstone, a non-dry-run sync prunes it and a
2007
- dry run reports `wouldPrune: true`. The classification falls back to a conservative ratio guard when
2008
- GitHub's compare is unavailable or its file list comes back truncated, which the sync output says
2009
- explicitly.
2010
- - **Use `components sync --dry-run` before risky variant syncs.** It reports `overridden`, `added`,
2011
- `removed`, `unchanged`, `skipped`, and `errors` without writing rows, caching the sync SHA, or clearing
2012
- staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means the
2013
- branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
2014
- `components commit --dry-run` is unsupported because `commit` performs local git writes before syncing.
2015
- - **The sync refuses a wholesale removal.** Above roughly a third of a kind — or **100% of a kind at any
2016
- size** — it aborts that kind, reports why, and points at a rebase. Rebasing is almost always the real
2017
- fix. Only when the removals are genuinely deliberate, re-run with `--force-tombstones`.
2018
- - **Deleting a trunk component cascades**: its overlays on every branch are removed with it.
2019
- - **A removal you did not author usually means TRUNK is drifted**, not that the branch deleted something.
2020
- A component that exists in the DB with **no file on trunk** is missing from every branch too, so it
2021
- shows up as a phantom `removed` in *every* branch preview — while the trunk sync separately tries to
2022
- hard-delete it on every run. Check whether the file exists on trunk before "fixing" it on the branch;
2023
- the repair is to mirror the live DB source back into the trunk repo (Component Integrity Rules).
2024
-
2025
- ### Inspecting branches and drift
2026
-
2027
- ```bash
2028
- remits-cli components branches # branches with variants, counts, drift
2029
- remits-cli components branch feature_branch # overridden / added / removed + subscribers
2030
- remits-cli components branch feature_branch --diff 50 --component-type action
2031
- remits-cli components branch feature_branch --subscribers
2032
- remits-cli components branch feature_branch --json # the stored overlay content
2033
- ```
2034
-
2035
- **Drift is the number to watch.** Each variant records the trunk content hash at the moment it was cut
2036
- (`originHash`). When the origin component later changes, the variant is reported **DRIFTED** — the branch
2037
- is now based on a stale version and someone should reconcile it. Before editing an origin component, check
2038
- whether variants of it exist: the owner account's `account-info.json` carries a `componentBranches`
2039
- summary, and `components branches` gives the live view. A change to the origin silently drifts every
2040
- branch that overlays it.
2041
-
2042
- **`components branches` only lists branches that already have overlays.** A branch you just pushed is
2043
- invisible here until its first sync — that is not an error. Preview it by name (`components sync --dry-run`
2044
- from that checkout).
2045
-
2046
- **Trunk moving also invalidates a branch.** Variant sparseness compares branch content against *current*
2047
- trunk, so a trunk change can make an overlay obsolete without the branch changing at all. A trunk sync
2048
- drops the affected branches' cached sync verdicts, so the next `components sync` on the branch really
2049
- re-evaluates instead of answering *"No changes detected"*. After promoting anything, re-sync each live
2050
- branch.
2051
-
2052
- **The admin UI has the same surface**, which is what to point a non-CLI user at: the *owner* account page's
2053
- **Component branches** box (per-branch counts, drift, subscribers, and Preview / Sync / Retire), and the
2054
- *subscriber* account page's **Branch variants** box plus **GitHub → Fetch `<branch>` variants**. Both open
2055
- the same result panel — grouped plan, subscribers, a Monaco diff of trunk vs branch per changed field, and
2056
- a confirm-gated override when the removal guard refuses. Preview there is the same `--dry-run`.
2057
-
2058
- ### Diagnosing a variant
2059
-
2060
- - `remits-cli components branch <name> --json` — the stored overlay content and what it overrides.
2061
- - The compile signature `variant:<id>:<hash>` in `Using Cached BCD` logs — proves a variant actually ran.
2062
- - A test/tool response reports `testComponentSource` as `staged` | `variant` | `db`, so you can see which
2063
- layer the run resolved without reading logs.
2064
-
2065
- > **A populated staging cache makes a variant look broken.** Anything carrying a CLI `TestMode` — a
2066
- > `remits-cli token` URL, `/s/<tokenKey>/...`, an `X-Auth-Token` request, a script-loader embed — resolves
2067
- > the STAGED layer, which outranks the variant. So with components staged under the same branch/user, a
2068
- > tokenized page reports `Component Branch: <branch>` but `Variant Applied: none` and renders trunk, while
2069
- > an anonymous `?account_id=` request to the same page reports the variant. That is the documented
2070
- > precedence (staged -> variant -> trunk) working correctly, and it reads exactly like "tokens break
2071
- > variant resolution". **Run `remits-cli components clear --all` before verifying variant resolution
2072
- > through any tokenized entry point**, or check `remits-cli components status` first.
2073
-
2074
- ## Production Support Workflow
2075
-
2076
- Switch to prod mode for investigations:
2077
-
2078
- ```bash
2079
- remits-cli data-mode set prod
2080
- ```
2081
-
2082
- ### Investigation Strategy
2083
-
2084
- Before starting an investigation outside the confirmed current repo:
2085
- 1. Read `~/.remits-cli/account-repos.json`
2086
- 2. Switch to the best local repo candidate
2087
- 3. Read that repo's `account-info.json`
2088
- 4. Confirm whether you are in `CLIENT`, `PLATFORM`, or `PRODUCT` context
2089
- 5. Then continue with the investigation flow below
2090
-
2091
- **Document-First** (most common — user reports a data issue):
2092
- 1. `mcp_account_view` — understand the account's schemas and components.
2093
- 2. `mcp_firestore_search` — find the document, capture its `object_id`.
2094
- 3. If the issue involves inbound or outbound HTTP behavior and the account has `enableHttpAudits`, query `http-audits/http-audits-YYYY-MM/entries` with `mcp_firestore_search`.
2095
- 4. `mcp_object_activity` — scan the timeline for warnings, errors, unexpected events.
2096
- 5. `mcp_record_listing` — search or filter alerts, events, object logs, or objects when you need to find the suspicious record first.
2097
- 6. `mcp_record_view` — drill into suspicious entries for full content.
2098
- 7. `mcp_ai_session_search` — if the workflow involves AI, inspect session groupings, prompts, tool definitions, and responses in human-readable form.
2099
- 8. `mcp_user_activity` — for "user X is slow right now" reports, list sessions by `userId`/`accountId`, open the session story, and use the returned beat pivots.
2100
- 9. `mcp_performance_trace` — for slow/sluggish reports, open the beat `traceId` with `action:"trace"`; use `action:"slowest"` when you only have a broad time window.
2101
- 10. `mcp_system_logs` — correlate via `threadGroupingId` for raw log context when the trace needs supporting log lines.
2102
- 11. `mcp_component_view`/`mcp_component_grep` — explain how the responsible component works.
2103
-
2104
- **Slow / sluggish user report:**
2105
-
2106
- 1. Resolve the reporting user/account with `mcp_account_user_admin` if you only have an email/name.
2107
- 2. Call `mcp_user_activity` with `action:"sessions"` and `userId` or `accountId`.
2108
- 3. Open the likely row with `action:"story"` and inspect beat labels, status, `ms`, `node`, and `traceId`.
2109
- 4. Open slow or failed beat pivots with `mcp_performance_trace` before querying raw logs.
2110
- 5. Use the returned `mcp_system_logs` pivot only when the trace needs surrounding log lines.
2111
- 6. If there is no live session, call `mcp_user_activity` `action:"watch"` for the user/account, ask for reproduction, then read `sessions`/`story` again. Focused sessions retain sanitized request detail and emit archived `REMITS_ACTIVITY` log lines.
2112
-
2113
- **Error or Alert Investigation:**
2114
- 1. `mcp_record_listing` — search by alert type, content, error text, action, status, `threadGroupingId`, or other exact-match record properties when you do not yet know the record ID.
2115
- 2. `mcp_record_view` — inspect the chosen record/event/alert/object in full once you have its ID.
2116
- 3. Use `object_id` + `threadGroupingId` to pull full timeline and logs.
2117
- 4. Cross-check Firestore document state.
2118
- 5. Identify `source_bcd` and any `upstream_source_bcd`.
2119
- 6. Explain the record in terms of the workflow step that produced it, not as a generic JSON blob.
2120
- 7. If the context looks missing, redacted, or truncated, consider sanitization rules before concluding data was never present.
2121
- 8. If AI behavior is part of the symptom, use `mcp_ai_session_search` and compare the persisted session content against the Agent component implementation and `features/ai-support.md`.
2122
-
2123
- **Stuck / failed / recovered Event:**
2124
-
2125
- Do **not** open the Action source first. The platform records each attempt's delivery envelope — which
2126
- queue delivered it, which delivery attempt this was, and how long it was ever allowed to run — and
2127
- classifies the failure for you.
2128
-
2129
- ```bash
2130
- remits-cli tool --name mcp_event_diagnostics --input '{"accountId":49,"eventId":18838}' --data-mode prod
2131
- ```
2132
-
2133
- The same classifier is available from a Test or any component as `eventDiagnostics(18838)`.
2134
-
2135
- **Read `classification` before anything else** — only `APPLICATION_FAILURE` means the bug is in the
2136
- component. The full classification table, what each `abandonmentCause` implies, and the returned `pivots`
2137
- are under **`mcp_event_diagnostics`** in the Tool Reference. One thing to check every time:
2138
- `delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
2139
- non-idempotent side effect may have run more than once — look for duplicate records before concluding the
2140
- component "ran twice for no reason".
2141
-
2142
- Full detail: `features/observability.md` and `features/events-builder-guide.md` (`mcp_get_guide`).
2143
-
2144
- ### Presenting Findings
2145
-
2146
- Users are not engineers. When reporting investigation results:
2147
- - Lead with what happened in plain language.
2148
- - Show the evidence (document values, timeline events, log excerpts).
2149
- - Explain why it happened if you can determine the cause.
2150
- - Recommend what to do next — in terms the user can act on.
2151
-
2152
- ### Verifying a Production Issue Fix
2153
-
2154
- When a bug is reported from production, use this pattern:
2155
-
2156
- 1. Investigate the live issue in **prod mode** and identify the exact affected document IDs, collection names, account IDs, and component path.
2157
- 2. Make the code change in the owning `PLATFORM` or `PRODUCT` repo when the defect is in shared implementation.
2158
- 3. Verify in **test mode**, not prod.
2159
- 4. Prefer a **Test component** when the behavior can be asserted programmatically, because that creates a durable regression suite and lets you explicitly construct the necessary data, operations, and assertions.
2160
- 5. Use `remits-cli token` plus `playwright-cli` when the proof is visual or interaction-driven.
2161
- 6. If useful, create or update a dedicated embeddable "playground" in test mode to reproduce the scenario in a controlled way.
2162
-
2163
- Do not move production customer data into another account's test collection as a routine verification strategy. If you cannot verify with a Test component, Playwright flow, or controlled test-mode embeddable, explain the gap clearly instead of improvising with live production validation.
2164
-
2165
- ## Tool Reference
2166
-
2167
- ### Execute a Tool
2168
-
2169
- ```bash
2170
- remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collection": "invoices"}' --data-mode prod
2171
- ```
2172
-
2173
- Response saved to `./.remits-cli/tool-responses/<callId>.json`. Read the file to see results.
2174
-
2175
- **"Tool call succeeded" means DISPATCHED, not that the tool did what you asked.** A tool that runs
2176
- and refuses — an unmet precondition, a rejected enum value, a failed validation — returns HTTP 200
2177
- with its own `success: false` inside `result`. The CLI now prints `Tool call FAILED — the tool ran
2178
- and returned an error.` plus a `Tool error:` line and exits non-zero, and the response envelope
2179
- carries `toolSuccess` / `toolMessage`. **For any MUTATING call, confirm the tool's own verdict before
2180
- reporting the work as done** — do not grep the terminal output for "succeeded":
2181
-
2182
- ```bash
2183
- F=$(remits-cli tool --name mcp_support_ticket --input "$(cat payload.json)" --data-mode prod 2>&1 \
2184
- | grep -o '[^ ]*tool-responses/[a-f0-9-]*\.json' | tail -1)
2185
- python3 -c "import json;r=json.load(open('$F'))['result'];print(r.get('success'), r.get('message'))"
2186
- ```
2187
-
2188
- Build non-trivial JSON into a file (e.g. with `python3 -c 'json.dumps(...)'`) and pass it as
2189
- `--input "$(cat payload.json)"`. Long inline single-quoted JSON intermittently produces no response
2190
- file at all.
2191
-
2192
- For long-running Action/Agent runners, use the tool's own async mode (`executionMode:"async"`), which returns
2193
- the `actionRunId`/`agentRunId` (and, for agents, `sessionId`) immediately:
2194
-
2195
- ```bash
2196
- remits-cli tool --name "mcp_run_action" --input '{"accountId":37,"actionName":"Rebuild Invoice","executionMode":"async","actionInput":{"invoiceId":"abc"}}' --data-mode prod
2197
- # poll by run id: {"controlAction":"status","accountId":37,"actionRunId":"<actionRunId>"}
2198
- ```
2199
-
2200
- For long tools that lack their own async mode, use the CLI transport async (`--async true`), optionally with
2201
- `--wait true` to poll locally, and `remits-cli tool status --call-id <callId>`. Do not stack both mechanisms
2202
- (see "Tool Execution Lifecycle"). Use `--timeout-ms <ms>` only to adjust the per-request client timeout; it is
2203
- not a replacement for async mode on multi-minute workflows.
2204
-
2205
- **Account-id precedence for tool calls.** When the CLI and the tool input both carry an account id, the server resolves them in this order:
2206
-
2207
- 1. Explicit `--account-id <ID>` flag — always wins. Use this when you want to be certain the tool runs against a specific account (and the user's session covers it).
2208
- 2. `input.accountId` (or `input.account_id`) — the tool's per-call execution target.
2209
- 3. The current repo's `account-info.json` / active session — the default fallback.
2210
-
2211
- So from inside a parent account's repo you can target a child account just by setting `input.accountId`, or force it with `--account-id` if you need it to override whatever the tool input says. Verify with the `accountId` field in the response envelope.
2212
-
2213
- ### `mcp_account_view`
2214
- Returns complete account structure — schemas, components, relationships.
2215
-
2216
- Also returns two blocks that explain how the account RESOLVES, which is what you need before comparing
2217
- its behavior against component source:
2218
-
2219
- - `resolution` — `role` (`OWNER` = resolves its own component trunk, `SUBSCRIBER` = resolves another
2220
- account's components through a branch edge) and a one-sentence `summary`; `accountId` (the account
2221
- described — the top level of this shape is the hierarchy ROOT, so do not read identity from there);
2222
- `databaseName` (the account's own override, often null) vs `resolvedDatabaseName` (the storage namespace
2223
- actually in effect); `branchName` (the repo sync branch) and `lastRepoSync`; `domainName` vs
2224
- `resolvedDomainName` (the custom host in effect, which differs when reached through an edge host);
2225
- `authPath` / `targetPath` (login and post-login landing routes); `editMode`; and — when a component
2226
- branch is in effect — `componentBranch` plus `componentOwnerAccountId` / `componentOwnerAccountName`.
2227
- - `resolution.relationships` — **every structural link upward**, primary first: `parentAccountId` /
2228
- `parentAccountName` / `parentAccountCode` / `parentAccountType`, `primary` (true for the one link that
2229
- mirrors the account's primary parent), `active`, and the three independent link-scoped properties
2230
- `branchName` (which component-variant code runs), `databaseName` (where data lives), `domainName` (which
2231
- host reaches the account through this link). The hierarchy tree flattens all links into one shape, so this
2232
- is the **only** place that answers "does this account have more than one parent, and which link carries the
2233
- branch/namespace/host?" More than one entry ⇒ this account can resolve differently depending on the path a
2234
- request travelled — establish which one a failing request used before comparing behavior.
2235
- - `componentBranches` — the variant branches this account OWNS, with override/add/remove, subscriber, and
2236
- drift counts. Same summary the owner's `account-info.json` carries.
2237
-
2238
- Note: this tool does **not** return users, and it returns EVERYTHING about one account. For users, for the
2239
- deep account tree, or for small account/user updates, use `mcp_account_user_admin`.
2240
-
2241
- | Parameter | Required | Description |
2242
- |-----------|----------|-------------|
2243
- | `accountId` | yes | Account ID |
2244
-
2245
- ### `mcp_account_user_admin`
2246
- The account-graph and user surface: the middle ground between `account-info.json` (which states structure
2247
- compactly, because it is read into your context every session) and `mcp_account_view` (everything about one
2248
- account).
2249
-
2250
- - `action: 'hierarchy'` (default) — the descendant tree trimmed to `depth` (1-10, default 2; nodes cut off
2251
- are marked `truncated` and still report their child count) and/or the anchored ancestor chain plus every
2252
- edge (`direction: 'down'|'up'|'both'`). Nodes include `testAccount`. **This is how you get the deep tree
2253
- account-info.json omits.**
2254
- - `action: 'account'` — one account's `resolution` block, including `testAccount`, plus masked
2255
- configuration fields, without paying for the component inventory.
2256
- - `action: 'users'` — an account's users at a hierarchy `scope` (`self`/`children`/`parents`/`hierarchy`),
2257
- optional `email` substring filter. Rows include `testUser`. Extension fields are omitted here on purpose:
2258
- they are stored **per account** and these users are bound to their own.
2259
- - `action: 'user'` — one user (`userId` or `email`) with roles, account memberships, and extension fields
2260
- **correctly scoped to the requested account**, plus `testUser`. It never grants membership as a side
2261
- effect of a read, and tells you when the fields shown belong to a different account.
2262
- - `action: 'account_update'` — write Account-schema configuration `fields`, and/or `name`/`status`
2263
- (`ACTIVE`/`ON_HOLD`/`PENDING`).
2264
- - `action: 'user_update'` — write User-schema `fields` under the named account, plus `name`/`enabled` and
2265
- membership add/remove. Refused unless the user is a member or you pass `addAccount: true`, because the
2266
- write would otherwise land on another account. If an email does not exist globally, `addAccount: true`
2267
- intentionally creates that user first, then binds them to the named account before writing fields.
2268
-
2269
- **Building an account hierarchy** (the structural writes — this is how a coding agent provisions accounts
2270
- without a browser):
2271
-
2272
- **Data-lane rule for provisioning:** `remits-cli tool` defaults to `--data-mode test`. That is correct for
2273
- fixtures and rehearsals, but it means `mcp_account_user_admin` `action:'account_create'` creates test-lane
2274
- accounts unless the command explicitly passes `--data-mode prod`. For real platform/product/customer
2275
- provisioning, always dry-run in prod first, check the response's `dataMode`, then run the write in prod:
2276
-
2277
- ```bash
2278
- remits-cli tool --name mcp_account_user_admin --data-mode prod --input '{"action":"account_create","parentAccountId":4,"name":"Freto","type":"PRODUCT","dryRun":true}'
2279
- ```
2280
-
2281
- After the real write, verify the response (or re-read `action:'account'`) shows the command `dataMode` you
2282
- intended and `testAccount:false` for real provisioning. `testAccount:true` means you created a test-data
2283
- account, even if the name and structure look correct.
2284
-
2285
- For a test rehearsal, make the opposite assertion explicit: the response should show `dataMode:'test'` and
2286
- `testAccount:true` for **newly created** accounts (or `testUser:true` for created users).
2287
-
2288
- Read `reusedExisting` before reading anything into the flag. `account_create` is find-or-create, and a
2289
- **test**-lane create can legitimately match a **real** account: real accounts are visible in both lanes,
2290
- so a rehearsal for a name that already exists in prod returns `reusedExisting:true` /
2291
- `testAccount:false` and changes nothing. That is correct reuse, not a lane error. Only
2292
- `reusedExisting:false` with `testAccount:false` in a test rehearsal means the lane was not the one you
2293
- intended. (The prod direction is not symmetrical: a prod-lane create never resolves onto a test
2294
- CLIENT/PROVIDER account, so the same name can exist once per lane.)
2295
-
2296
- **A test-lane account does not get the `code` the prod one will.** `code` is derived from `name` and is
2297
- globally unique, so a test-lane create with no explicit `code` is assigned `test_<code>_<parentId>`.
2298
- A namespace resolves as `databaseName ?: platform.code ?: code`, so a rehearsal **does not prove the
2299
- storage namespace** the real create will land in unless you set `databaseName` explicitly. Conversely,
2300
- passing an explicit `code` in a test rehearsal opts out of the prefix, and the later prod create then
2301
- fails on `code unique:true` — as it also will against a test account created before this rule existed.
2302
- Check the existing account's `code` before assuming a name is free.
2303
-
2304
- Account/User schema `fields` are Firestore-backed extension fields. Their physical storage follows the
2305
- same data lane as the tool call: `--data-mode test` writes under `testing/<resolvedDatabaseName>/...`, while
2306
- `--data-mode prod` writes under `accounts/<resolvedDatabaseName>/...` (for modern segmented accounts). If a
2307
- test-lane rehearsal should become real provisioning, rerun the create/update in prod mode; do not assume the
2308
- test-lane Firestore fields moved.
2309
-
2310
- - `action: 'account_create'` — create a child under `parentAccountId`, with its **primary relationship edge**,
2311
- applying `type` / `databaseName` / `domainName` / `authPath` / `targetPath` / `code` /
2312
- `repositoryNameOverride` / `branchName` / `editMode` / … **at birth**. That ordering matters: the storage
2313
- namespace is resolved from those properties, and the parent's cascaded schema fields are written into it
2314
- during creation. Idempotent — an existing same-name account under that parent comes back with
2315
- `reusedExisting: true`, unchanged. The response includes `testAccount`.
2316
- - `action: 'account_structure'` — change those properties on an existing account, including account-level
2317
- custom host, login path, and landing path.
2318
- - `action: 'edge_add'` / `'edge_update'` / `'edge_remove'` — manage a membership `AccountRelationship` edge to
2319
- `parentAccountId`, including the three independent edge properties `branchName` (which component code runs),
2320
- `databaseName` (a path-scoped storage-namespace override — **live**, and inherited by everything below
2321
- that edge) and `domainName` (which host reaches the account through that edge). Cycles, self-edges, and
2322
- removing the primary edge are all refused.
2323
- - `action: 'reparent'` — move the account's **primary** edge (and `Account.parentId`) to `parentAccountId`.
2324
-
2325
- > **The namespace guard.** An account's storage namespace resolves as
2326
- > `databaseName ?: platform.code ?: code`, and `Account.setName` **regenerates `code`** — so renaming an
2327
- > account whose namespace falls through to its own code silently repoints its storage. Any write that would
2328
- > move the resolved namespace is **refused** unless you pass `confirmSegmentChange: true`, and the refusal
2329
- > names both namespaces. To rename without moving storage, pin `code` to the old value in the same call.
2330
- >
2331
- > Because the namespace follows the **path** (see *Account Structure* in `platform-overview.md`), an
2332
- > `edge_update` that sets `databaseName` repoints storage for **everything below that edge**, and the
2333
- > answer is path-specific — the same account reached through a different parent can resolve a different
2334
- > namespace. Dry-run these.
2335
-
2336
- Every write supports `dryRun: true`, which reports each `from -> to` without writing. Not here by design:
2337
- component-branch subscription reporting and drift (use `remits-cli components branches` /
2338
- `remits-cli components branch <name>`), and account/user **deletion** (admin only, so the
2339
- destructive-teardown contract applies).
2340
-
2341
- **Provisioning recipe** — a new product account under a platform, running its own component branch, with
2342
- client accounts beneath it:
2343
-
2344
- ```
2345
- 1. mcp_account_user_admin action:'account_create' parentAccountId:<platform> name:'Adyen'
2346
- type:'PLATFORM'|'PRODUCT' [databaseName:'...']
2347
- 2. git branch + push in the OWNER's repo, then `remits-cli components sync` from that checkout
2348
- (non-trunk => writes ComponentVariant overlays only)
2349
- 3. mcp_account_user_admin action:'edge_update' targetAccountId:<new> parentAccountId:<platform>
2350
- branchName:'<branch>'
2351
- 4. mcp_account_user_admin action:'account_create' parentAccountId:<new> name:'<business unit>'
2352
- type:'CLIENT' # inherits the branch automatically
2353
- ```
2354
-
2355
- > **Put the branch on the edge that is UNAMBIGUOUSLY on the account's path up — primary or membership.**
2356
- > Inheritance walks `parentId` and, at an account that has no `parentId`, continues through its *single*
2357
- > active membership edge. So a subscriber shell with **no `parentId` and one membership edge** to its owner
2358
- > is a fully supported shape: it resolves the branch, **and so do all of its descendants**. You do not need
2359
- > to make the subscriber a structural child of its owner.
2360
- >
2361
- > What is NOT resolved by default is genuine **ambiguity** — an account with *several* upward links, where
2362
- > the platform refuses to guess which product it was reached through. That account (and its descendants)
2363
- > resolve trunk until a request names the path: `--as-account`, `--variant-branch`, or the edge's own host.
2364
- > An account that has a `parentId` **and** a separate membership edge carrying the branch is this case: the
2365
- > `parentId` wins, so put the branch on the link the account actually inherits through.
2366
- >
2367
- > Either way, descendants inherit the branch down the chain automatically, which is what makes step 4 free.
2368
-
2369
- | Parameter | Required | Description |
2370
- |-----------|----------|-------------|
2371
- | `action` | no | `hierarchy` (default), `account`, `users`, `user`, `account_update`, `user_update` |
2372
- | `accountId` / `targetAccountId` | no | Account to act on; `targetAccountId` targets another account without moving tool resolution |
2373
- | `depth` / `direction` | no | `hierarchy` only |
2374
- | `scope` | no | `users` only |
2375
- | `userId` / `email` | no | Identify the user (`user`, `user_update`); `email` is a filter for `users` |
2376
- | `fields` / `name` / `status` / `enabled` | no | The update payload |
2377
- | `addAccount` / `removeAccount` | no | Membership changes for `user_update` |
2378
- | `dryRun` | no | Report the change without writing |
2379
-
2380
- ### `mcp_firestore_search`
2381
- Query Firestore documents.
2382
-
2383
- | Parameter | Required | Description |
2384
- |-----------|----------|-------------|
2385
- | `accountId` | yes | Account ID |
2386
- | `collection` | yes | Collection name (snake_case plural, e.g., `invoices`) |
2387
- | `documentId` | no | Fetch single document by ID |
2388
- | `filters` | no | `[{field, operation, value}]`. Operations: `EQUALS` (or `==`), `NOT_EQUALS` (or `!=`), `GREATER_THAN` (or `>`), `GREATER_THAN_EQUALS` (or `>=`), `LESS_THAN` (or `<`), `LESS_THAN_EQUALS` (or `<=`), `IN`, `NOT_IN`, `ARRAY_CONTAINS`, `ARRAY_CONTAINS_ANY`, `IS_NULL`, `IS_NOT_NULL`. `op` is accepted as alias for `operation`. |
2389
- | `sort` | no | `[{field, direction}]` — `ASC`/`DESC`. Also accepts top-level `orderBy` + `orderDirection`. |
2390
- | `pagination` | no | `{limit, offset}`. Default limit=25, max=200. Also accepts top-level `limit`/`offset`. |
2391
- | `fields` | no | Field names to return. If omitted, auto-selects up to 20 fields. |
2392
- | `aggregation` | no | `{sum: [...], avg: [...], min: [...], max: [...], count: true}` |
2393
- | `dateRanges` | no | `[{field, startDate, endDate}]` (yyyy-MM-dd) |
2394
- | `textSearch` | no | `[{field, prefix}]` for prefix matching |
2395
- | `fallbackOnMissingIndex` | no | When `true`, a sorted read that fails on a missing composite index retries without sort and returns `warning`, `missingIndexUrl`, and `sortApplied:false`. Use when an unsorted first page is still useful. |
2396
-
2397
- **HTTP audits use this same tool** — they are Firestore documents in monthly collections
2398
- (`http-audits/http-audits-YYYY-MM/entries`). Which filters to reach for, and when audits are the right
2399
- evidence at all, is in *The Investigation Model → HTTP audits*.
2400
-
2401
- ```bash
2402
- remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collection": "http-audits/http-audits-2026-05/entries", "filters": [{"field": "direction", "operation": "EQUALS", "value": "OUTBOUND"}, {"field": "request.path", "operation": "EQUALS", "value": "/api/orders"}, {"field": "success", "operation": "EQUALS", "value": false}], "sort": [{"field": "timestamp", "direction": "DESC"}], "pagination": {"limit": 10}}' --data-mode prod
2403
- ```
2404
-
2405
- ### `mcp_firestore_patch`
2406
- Guarded exact-document Firestore patch tool for bounded repairs. It defaults to `dryRun:true` and refuses broad
2407
- updates, wildcard collections, delete/remove operations, protected identity fields, and `_lastModified*` audit
2408
- fields.
2409
-
2410
- Use it when the desired repair is mechanical and smaller than rerunning an expensive Action, for example copying
2411
- canonical fields into stale UI mirror fields. Always dry-run first and include preconditions:
2412
-
2413
- ```bash
2414
- remits-cli tool --name mcp_firestore_patch --data-mode prod --input '{
2415
- "accountId":743,
2416
- "collection":"statements",
2417
- "documentId":"20958",
2418
- "dryRun":true,
2419
- "preconditions":[
2420
- {"field":"interchangeOptimization.status","equals":"Calculated"},
2421
- {"field":"interchangeOptimizationChecked","equals":false}
2422
- ],
2423
- "patch":{
2424
- "interchangeOptimizationChecked":true,
2425
- "feeBreakdown.interchangeOptimization":{"$copyFrom":"interchangeOptimization"},
2426
- "feeBreakdown.interchange.optimization":{"$copyFrom":"interchangeOptimization"}
2427
- }
2428
- }'
2429
- ```
2430
-
2431
- Response fields include `dataMode`, `accountId`, `collection`, `documentId`, `dryRun`, `patchedFields`, and
2432
- `diff`. Switch to `"dryRun":false` only after the diff and preconditions are exactly what you intended.
2433
-
2434
- ### `mcp_object_activity`
2435
- Object lifecycle timeline — metadata + recent activity.
2436
-
2437
- | Parameter | Required | Description |
2438
- |-----------|----------|-------------|
2439
- | `accountId` | yes | Account ID |
2440
- | `objectId` | yes | Object ID (from `object_id` in documents) |
2441
- | `dataMode` | no | Explicit lane: `prod` or `test`. Response echoes `dataMode`. |
2442
- | `activityOptions` | no | `{limit, offset, types, start, end, order}`. Default: limit=5, order=desc. Types: `OBJECT_LOG`, `EVENT`, `ALERT`. |
2443
-
2444
- Response fields include `dataMode`, `object.testMode`, and `testMode` on Event/Alert timeline entries.
2445
-
2446
- ### `mcp_record_listing`
2447
- List and search lifecycle records when you do not already know the record ID.
2448
-
2449
- Typical use:
2450
- - find active error alerts by `type` or `status`
2451
- - search alert/event/object_log content for an error phrase from an inbound support email
2452
- - narrow candidate records before switching to `mcp_record_view`
2453
-
2454
- Tool ID: `88`
2455
-
2456
- | Parameter | Required | Description |
2457
- |-----------|----------|-------------|
2458
- | `accountId` | yes | Account ID used for tenant scoping |
2459
- | `recordType` | yes | `object`, `object_log`, `event`, or `alert` |
2460
- | `filters` | no | Exact-match domain-property filters following the admin `listData` model, for example `status`, `type`, `active`, `threadGroupingId`, `action`, or enum fields using `_enum` |
2461
- | `ids` | no | Exact ID filter. Accepts a comma-separated string or array of numeric IDs |
2462
- | `query` | no | Lightweight text query over key searchable fields such as alert type/content/error text or object_log description/content |
2463
- | `page` | no | 1-based page number. Default: `1` |
2464
- | `pageSize` | no | Records per page. Default: `10`, max: `100` |
2465
- | `sort` | no | Domain property to sort by. Default: `id` |
2466
- | `order` | no | Sort direction: `asc` or `desc`. Default: `desc` |
2467
- | `includeChildren` | no | When `true`, include the specified account and child accounts |
2468
- | `scanLimit` | no | When using `query`, number of filtered candidate records to scan before text matching. Default: `200`, max: `500` |
2469
- | `maxPreviewChars` | no | Override preview length for returned content/body snippets. Max: `2048` |
2470
-
2471
- ### `mcp_record_view`
2472
- Inspect individual lifecycle records with line-range or grep.
2473
-
2474
- | Parameter | Required | Description |
2475
- |-----------|----------|-------------|
2476
- | `accountId` | yes | Account ID |
2477
- | `recordType` | yes | `object`, `object_log`, `event`, or `alert` |
2478
- | `recordId` | yes | Record primary key |
2479
- | `field` | no | `content` (default) or `body` (objects only) |
2480
- | `revisionId` | no | Envers revision ID (not for object_log) |
2481
- | `lineRange` | no | `{start, end}` (1-based inclusive) |
2482
- | `grep` | no | `{pattern, caseSensitive, contextBefore, contextAfter}` |
2483
-
2484
- ### `mcp_ai_session_search`
2485
- Search AI session groupings and export grouping detail in human-readable form.
2486
-
2487
- Typical use:
2488
- - find AI sessions by agent, user, account, session ID, or grouping ID
2489
- - inspect the exact prompts, system messages, tools, and responses used in a prior run
2490
- - identify tuning opportunities in Agent behavior by comparing session output with the front-stage guides and component source
2491
-
2492
- Front-stage references:
2493
- - `components/agent-components.md`
2494
- - `features/ai-support.md`
2495
-
2496
- | Parameter | Required | Description |
2497
- |-----------|----------|-------------|
2498
- | `action` | no | `search` (default), `detail`, or session control `pause`/`unpause`/`interrupt` |
2499
- | `dataMode` | no | Explicit execution lane: `prod` or `test`. Response echoes `dataMode`, but persisted groupings do not have durable per-row lane flags. |
2500
- | `search` | no | Broad text match against session IDs and grouping IDs |
2501
- | `sessionId` | no | Session ID filter in search mode, or grouping/session key in detail mode |
2502
- | `groupingId` | no | Grouping ID filter in search mode, or grouping key in detail mode |
2503
- | `groupingKey` | no | Preferred explicit grouping key for detail mode |
2504
- | `user` | no | User filter |
2505
- | `account` | no | Account filter |
2506
- | `agent` | no | Agent filter |
2507
- | `scope` | no | `all`, `agents`, or `internal` |
2508
- | `status` | no | Search mode: filter by live runtime status, comma-separated (e.g. `paused,interrupted`) |
2509
- | `scanLimit` | no | Search mode: window scanned when `status` is set. Default `100`, max `500` |
2510
- | `page` | no | 1-based page number. Default: `1` |
2511
- | `pageSize` | no | Results per page. Default: `25`, max: `100` |
2512
- | `summaryOnly` | no | Detail mode: return the MAP (record index + stats + timeline) with no payloads. Same as `parts:["index"]` |
2513
- | `parts` | no | Detail parts: `index`, `stats`, `transcript`, `tool_calls`, and record sections `full`, `conversation_messages`, `system`, `response`, `tools` |
2514
- | `sections` | no | Alias for `parts` |
2515
- | `recordIds` | no | Detail mode: open only these request/response record IDs |
2516
- | `toolCallIds` | no | Detail mode: return the FULL exact input/result from `ai_tool_call` for these tool-call ids (what the tool PRODUCED — see the lens caveat) |
2517
- | `consolidateContext` | no | When `true`, collapses repeated XML-like prompt context into a consolidated section |
2518
-
2519
- Audit flow: `action:"search"` to find the grouping → `action:"detail"` + `summaryOnly:true` for the MAP → re-call detail with `recordIds`/`toolCallIds` + `parts` to open exactly what you need. Prefer the map → open flow over a full-detail dump. Remember the two-lens rule: `tool_calls`/`toolCallIds` is what the tool PRODUCED; `conversation_messages`/`transcript` is what the AI CONSUMED (after any `_offload`/`_hideResult`/`_message`/supersede/evict transform).
2520
-
2521
- ### `mcp_run_action`
2522
- Run an Action on a target account, with explicit prod/test data mode, optional staged branch resolution, and
2523
- staged-vs-DB provenance in the result.
2524
-
2525
- Describe the Action first when the input shape is not obvious. This does not execute the Action:
2526
-
2527
- ```bash
2528
- remits-cli tool --name mcp_run_action --input '{"controlAction":"describe","accountId":743,"actionId":25,"includeInputSchema":true}' --data-mode prod
2529
- ```
2530
-
2531
- The describe response reports `hasInputSchema`, optional `inputSchema`, `inferredInputKeys`, and component
2532
- provenance. If `hasInputSchema:false`, treat `inferredInputKeys` as a best-effort static scan, not a contract.
2533
-
2534
- Use direct mode only for quick Actions:
2535
-
2536
- ```bash
2537
- remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"direct","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
2538
- ```
2539
-
2540
- Use the tool's own async mode for long-running Action execution — it returns immediately with an
2541
- `actionRunId`. Pass your **own** `actionRunId` so you can poll deterministically without first parsing it out
2542
- of the start response. Do **not** also pass the CLI `--async` flag; that only buries these ids behind the
2543
- transport layer:
2544
-
2545
- ```bash
2546
- remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"async","actionRunId":"my-stable-run-id","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
2547
- ```
2548
-
2549
- Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
2550
- `accountId` + `actionRunId`:
2551
-
2552
- ```bash
2553
- remits-cli tool --name mcp_run_action --input '{"controlAction":"status","accountId":49,"actionRunId":"my-stable-run-id"}' --data-mode prod
2554
- ```
2555
-
2556
- > This poll is a normal `remits-cli tool --name mcp_run_action` call — **not** `remits-cli tool status`,
2557
- > which polls the CLI-transport `--async` `callId` (a different mechanism). Use `controlAction:"status"`
2558
- > (rather than `command:"status"`) inside the input so it is never conflated with the transport-level status.
2559
- > If you started the run with a different `userId`, include that same `userId` in the poll (the run's status
2560
- > is keyed by account + user + `actionRunId`; it otherwise defaults to the current user).
2561
-
2562
- For job-style Actions only (`Action.job == true`), prefer `executionMode:"event"` when you want the durable
2563
- Event lifecycle, Event status, and platform recovery behavior. Event mode is inherently async; poll it the
2564
- same way (`controlAction:"status"` + `actionRunId`) — the status resolves the backing Event's terminal state:
2565
-
2566
- ```bash
2567
- remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"event","actionRunId":"my-stable-run-id","actionInput":{}}' --data-mode prod
2568
- ```
2569
-
2570
- Returned fields on the async/event start: `actionRunId`, `status:"running"`, `executionMode`,
2571
- `threadGroupingId`, `componentSource`, `componentSignature`, and (event mode) `eventId`/`eventStatus`. The
2572
- `status` poll adds `result` on completion, or `message`/`error` on failure.
2573
-
2574
- > **In event mode the EVENT is the source of truth, not the promise.** `status` is driven by the Event's
2575
- > own state; the value the dispatch call returned is reported separately as `dispatchResult` and is **not**
2576
- > the Action's result. Read `eventStatus` for the outcome.
2577
-
2578
- #### Stopping a run — `controlAction:'interrupt'`
2579
-
2580
- The tool counterpart of the **Interrupt** button on the admin Events page. Use it when an investigation
2581
- turns up a run that is consuming resources and should not finish — the case this exists for is finding a
2582
- `PROCESSING` event that has been running far too long.
2583
-
2584
- ```bash
2585
- # the usual path: you found the event in mcp_record_listing / mcp_object_activity
2586
- remits-cli tool --name mcp_run_action --data-mode prod --input '{"controlAction":"interrupt","accountId":49,"eventId":19102,"reason":"runaway extraction, 45min"}'
2587
-
2588
- # or stop a run you started yourself (executionMode:'event' only)
2589
- remits-cli tool --name mcp_run_action --data-mode prod --input '{"controlAction":"interrupt","accountId":4,"actionRunId":"my-run-id"}'
2590
- ```
2591
-
2592
- Aliases `cancel` / `stop` / `kill` all work. What you need to know before using it:
2593
-
2594
- - **It is COOPERATIVE cancellation, not a thread kill.** It sets a flag the running work observes at its
2595
- next checkpoint, then throws. Checkpoints are dense across everything that matters — every Firestore
2596
- read/write, outbound HTTP call, AI turn, and front-stage DSL call — so a normal run stops promptly. A
2597
- run blocked inside a *single* long call (one slow AI turn) stops when that call returns, not instantly.
2598
- - **Work already committed is NOT rolled back.** This stops further work; it does not undo what has run.
2599
- - **Only `PENDING`/`QUEUED`/`PROCESSING` can be interrupted.** A terminal event is reported back with its
2600
- status rather than being silently reported as "interrupted".
2601
- - **Event-scoped.** A `direct` or `async` run has no Event and cannot be stopped this way.
2602
- - Tenant-scoped: you cannot interrupt another account's event.
2603
- - The response returns `previousStatus`, `eventStatus`, and `threadGroupingId`, so you can pivot straight
2604
- into `mcp_performance_trace` / `mcp_system_logs` to see what it was doing when you stopped it.
2605
-
2606
- **AI sessions are stopped separately** with `mcp_ai_session_search` (`action:'interrupt'`, plus
2607
- `pause`/`unpause`) — that controls an agent's conversation loop, whereas this controls an Action's Event.
2608
-
2609
- ### `mcp_run_agent`
2610
- Run one real Agent turn on a target account. The Agent hooks and tools execute for real against the requested
2611
- `dataMode`; pass `dataMode:"test"` for safer tuning.
2612
-
2613
- Use direct mode only for short turns:
2614
-
2615
- ```bash
2616
- remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Summarize this invoice context","executionMode":"direct"}' --data-mode prod
2617
- ```
2618
-
2619
- Use the tool's own async mode for autonomous or long Agent turns. It returns immediately with both an
2620
- `agentRunId` and a single, stable `sessionId` — the **same** id the running session uses, so you can inspect
2621
- it right away. Pass your own `agentRunId` for deterministic polling. Do **not** also pass the CLI `--async`
2622
- flag (that only delays these ids into a polled result):
2623
-
2624
- ```bash
2625
- remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Audit this invoice","executionMode":"async","agentRunId":"my-stable-run-id","context":{"invoiceId":"..."}}' --data-mode prod
2626
- ```
2627
-
2628
- Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
2629
- `accountId` + `agentRunId` (this is a normal `mcp_run_agent` call, **not** `remits-cli tool status`):
2630
-
2631
- ```bash
2632
- remits-cli tool --name mcp_run_agent --input '{"controlAction":"status","accountId":49,"agentRunId":"my-stable-run-id"}' --data-mode prod
2633
- ```
2634
-
2635
- Inspect the live/persisted AI session at any time using the `sessionId` returned by the start call (it is the
2636
- run's real, canonical session id):
2637
-
2638
- ```bash
2639
- remits-cli tool --name mcp_ai_session_search --input '{"action":"detail","sessionId":"<sessionId>","summaryOnly":true}' --data-mode prod
2640
- ```
2641
-
2642
- Key returned fields: `agentRunId`, `sessionId`, `status`, `threadGroupingId`, runtime pause/interruption
2643
- hints, `lastAssistantMessage`, `toolCallCount`, `result`, `message`, and `error`.
2644
-
2645
- #### Controlling a live agent — `pause` / `unpause` / `interrupt`
2646
-
2647
- ```bash
2648
- remits-cli tool --name mcp_run_agent --data-mode prod --input '{"controlAction":"pause","accountId":49,"agentRunId":"my-run-id"}'
2649
- remits-cli tool --name mcp_run_agent --data-mode prod --input '{"controlAction":"interrupt","accountId":49,"sessionId":"<sessionId>"}'
2650
- ```
2651
-
2652
- Target the session with the `agentRunId` an async start returned, or the `sessionId` directly.
2653
-
2654
- - **`interrupt` is TERMINAL** (aliases `stop`/`cancel`/`kill`). It clears any pending resume and pending
2655
- guardrails and persists a terminal lifecycle status. An interrupted session **cannot be unpaused** —
2656
- attempting it is refused with that reason rather than silently doing nothing. Use `pause` if you intend
2657
- to resume.
2658
- - If the session is not resident on the serving node, it is rehydrated by agent name — so pass `agentName`
2659
- (or an `agentRunId`, which carries it) when controlling a session you did not just start.
2660
- - The same controls remain available on `mcp_ai_session_search`, which is the right tool when you are
2661
- *searching* for the session; this is the right one when you *started* the run.
2662
-
2663
- ### `mcp_system_logs`
2664
- Query Cloud Run service logs.
2665
-
2666
- | Parameter | Required | Description |
2667
- |-----------|----------|-------------|
2668
- | `node` | * | Node name (e.g., `remitsAdmin-east5`). Auto-resolves to serviceName+region. |
2669
- | `serviceName` | * | Cloud Run service. Not needed if `node` provided. |
2670
- | `region` | * | Cloud Run region. Not needed if `node` provided. |
2671
- | `timeRange` | * | Relative time: `1h`, `4h`, `30m`, `7d`. Auto-calculates startTime. |
2672
- | `startTime` | * | ISO 8601 timestamp. Not needed if `timeRange` provided. |
2673
- | `endTime` | no | ISO 8601 upper bound (defaults to now) |
2674
- | `severity` | no | Minimum: `INFO`, `WARNING`, `ERROR`, etc. |
2675
- | `threadGroupingId` | no | Filter by processing chain ID |
2676
- | `filter` | no | Additional Cloud Logging filter (LQL). **To search message text, pass the bare phrase** — it is widened automatically to match both `jsonPayload.message` (all `log.*` output) and `textPayload` (`println`/stdout). See the warning under "Diagnosing which version is in play". |
2677
- | `maxPreviewChars` | no | Per-entry truncation width. Default 512, max 8000. Raise it when an entry carries a structured payload (a serialized `RemitsTrace`, a long stack frame) that the default cuts mid-JSON. |
2678
- | `pageSize` | no | Default 25, max 100. Also accepts `limit`. |
2679
-
2680
- *Provide either `node` or `serviceName`+`region`. Provide either `timeRange` or `startTime`.
2681
-
2682
- **Example:**
2683
- ```json
2684
- {"node": "remitsAdmin-east5", "timeRange": "4h", "severity": "ERROR"}
2685
- ```
2686
-
2687
- ### `mcp_user_activity`
2688
- Read live user activity sessions and control focused capture. Use this before trace/log spelunking when
2689
- the report is user-centric, for example "User abc is reporting slow responses." It wraps the same
2690
- Redis-backed store as System → Activity and returns ready pivots to traces, logs, and component source.
2691
-
2692
- Common flows:
2693
-
2694
- ```bash
2695
- # Find what one user is doing right now
2696
- remits-cli tool --name mcp_user_activity --input '{"action":"sessions","userId":3,"limit":10}' --data-mode prod
2697
-
2698
- # Open a returned sessionKey and inspect its beats
2699
- remits-cli tool --name mcp_user_activity --input '{"action":"story","sessionKey":"c_46ee68bba67003a6","limit":50}' --data-mode prod
2700
-
2701
- # Arm focused capture, ask the user to reproduce, then read the story again
2702
- remits-cli tool --name mcp_user_activity --input '{"action":"watch","userId":3,"minutes":30}' --data-mode prod
2703
- ```
2704
-
2705
- | Parameter | Required | Description |
2706
- |-----------|----------|-------------|
2707
- | `action` | no | `sessions`, `story`, `watch`, `unwatch`, `forget`, `status`, or `archive`. Default: `sessions`. |
2708
- | `userId` / `accountId` | no | Filter sessions/archive or choose the focus subject for `watch`/`unwatch`. One is required for `watch`/`unwatch`. |
2709
- | `sessionKey` | for `story`/`forget` | Salted activity session key returned by `sessions`; not a raw browser/session credential. |
2710
- | `focusedOnly` | no | For `sessions`, return only focused/watched sessions. |
2711
- | `sinceMs` | no | For `sessions`, lower bound on last-seen epoch milliseconds. Defaults to the activity TTL window. |
2712
- | `limit` | no | Session/story/archive row cap. Defaults: sessions=100, story=200, archive=50. |
2713
- | `minutes` | no | Watch TTL for `watch`; defaults to `activity.focus.ttl.minutes`. |
2714
- | `traceId` / `threadGroupingId` | no | For `archive`, narrow focused activity log pivot to one trace. |
2715
- | `lookbackHours` | no | For `archive`, Cloud Logging window in the returned `mcp_system_logs` pivot. Default 24, max 168. |
2716
- | `node` / `serviceName` / `region` | no | For `archive`, target for the returned `mcp_system_logs` pivot. `node` defaults to `remitsAdmin-east5`. |
2717
-
2718
- Reading rule: use `sessions` → `story` to build the behavioral timeline, then open a slow or failed beat's
2719
- `mcp_performance_trace` pivot. Use `watch` when the user can reproduce and no live story exists. Use
2720
- `archive` only for watched/focused sessions; ordinary activity lives in Redis and expires with the activity
2721
- TTL.
2722
-
2723
- ### `mcp_performance_trace`
2724
- Read Remits request traces through the same `traces(...)` DSL that powers the admin Diagnostics "Request
2725
- traces" panel. Use this before raw log spelunking for slow or sluggish requests because it returns profiled
2726
- span rollups, component annotations, and retained slow-request summaries directly.
2727
-
2728
- Common flows:
2729
-
2730
- ```bash
2731
- # User only knows it was slow this afternoon
2732
- remits-cli tool --name mcp_performance_trace --input '{"action":"slowest","lookbackHours":4,"accountId":52,"minMs":2000,"limit":10}' --data-mode prod
2733
-
2734
- # You have the Diagnostics/request/Object/Event/Alert threadGroupingId
2735
- remits-cli tool --name mcp_performance_trace --input '{"action":"trace","traceId":"msf1y65n-001","lookbackHours":6,"format":"markdown"}' --data-mode prod
2736
-
2737
- # Same local-node data as the Diagnostics table
2738
- remits-cli tool --name mcp_performance_trace --input '{"action":"snapshot","limit":100}' --data-mode prod
2739
- ```
2740
-
2741
- | Parameter | Required | Description |
2742
- |-----------|----------|-------------|
2743
- | `action` | no | `snapshot`, `slowest`, or `trace`. Default: `snapshot`. |
2744
- | `traceId` / `threadGroupingId` | for `trace` | Request/grouping id to open across local ring and Cloud Logging. |
2745
- | `lookbackHours` | no | Cloud Logging window for `trace`/`slowest`. Defaults are 6 and 4 hours. |
2746
- | `accountId` | no | Account filter for `slowest`. |
2747
- | `kind` | no | Operation kind filter for `slowest`. **Rarely what you want** — see the note below. |
2748
- | `componentType` | no | Filter `slowest` by the component that did the work: `Action`, `Reader`, `Rule`, `Embeddable`, `Tool`, `Test`. **This is the right axis for "which Actions/Rules are slow".** |
2749
- | `componentId` / `componentName` | no | Narrow `slowest` to one component. |
2750
-
2751
- > **Filter by `componentType`, not `kind`.** `kind` is set by whoever OPENS the trace. An Action delivered
2752
- > by Cloud Tasks arrives over HTTP, so the trace is `kind:'web'` and the Action is a `component.Action`
2753
- > *span inside it*; a Rule fired during a request and a Tool invoked by an agent are the same. So
2754
- > `kind:'action'` matches almost nothing in production. Every component execution annotates
2755
- > `componentType`/`componentId`/`componentName` — filter on those.
2756
- | `minMs` | no | Minimum duration for `slowest`. Default: 1500. |
2757
- | `limit` | no | Result limit. |
2758
- | `format` / `markdown` | no | Set `format:"markdown"` or `markdown:true` for a rendered trace report. |
2759
-
2760
- > **Tools are components, so availability is per ACCOUNT.** `Tool not found: mcp_performance_trace` does
2761
- > not mean the tool is broken or that tracing is off — it means that tool has not been synced to the
2762
- > account you are resolving against. This bites most often on **localhost** (a Test Account that has not
2763
- > pulled the System Account's tool set) and on **client accounts**. Run `remits-cli tools` for the account
2764
- > in question, or re-run against an account that owns the tool (`--account-id 4` for the System Account).
2765
- > The same is true of every `mcp_*` tool, including the component tools noted below.
2766
-
2767
- Reading rule: first use `slowest` to get candidate trace ids, then call `trace` on the suspicious id, then use
2768
- `mcp_system_logs` only if you need surrounding log lines. A trace dominated by `firestore.*`, `http.*`,
2769
- `gorm.save.*`, or component spans points you at the relevant platform seam or front-stage component. Large
2770
- unaccounted wall time is itself a finding: check cold compile, queueing, blocking I/O, or missing
2771
- `measure(...)` instrumentation.
2772
-
2773
- ### `mcp_event_diagnostics`
2774
- Diagnose one Event's infrastructure outcome through the same `eventDiagnostics(eventId)` DSL described in
2775
- `features/observability.md`. Use this before opening Action source when an Event is stuck,
2776
- recovered, timed out, retried, or appears to have been killed.
2777
-
2778
- ```bash
2779
- remits-cli tool --name mcp_event_diagnostics --input '{"accountId":49,"eventId":18838}' --data-mode prod
2780
- ```
2781
-
2782
- | Parameter | Required | Description |
2783
- |-----------|----------|-------------|
2784
- | `accountId` | yes | Tenant scope. The Event must belong to this account unless `includeChildren:true`. |
2785
- | `eventId` / `id` | yes | Event primary key to diagnose. |
2786
- | `includeChildren` | no | Allow the Event to belong to the requested account or one of its child accounts. Default: `false`. |
2787
-
2788
- Read `classification` first:
2789
-
2790
- - `APPLICATION_FAILURE` — the Action failed in application code; read `event.errorMessage`, correlated
2791
- alerts, and the producing component.
2792
- - `ORPHANED_*` / `RECOVERED_*` — the attempt was abandoned; read `abandonmentCause`.
2793
- - `REQUEST_TIMEOUT_LIKELY` — it used its whole deadline (`timing.deadlineUsed` near `1.0`). The unit of
2794
- work is too big for one event; the fix is resumable batches, not component logic.
2795
- - `PROCESS_TERMINATED_LIKELY` — it stopped well inside its deadline (`timing.deadlineUsed` near `0`), so
2796
- the worker was killed (memory pressure, restart). Not a logic bug; inspect JVM/node health and
2797
- container lifecycle logs.
2798
- - `UNKNOWN_NO_DEADLINE_EVIDENCE` — no deadline was recorded, so the cause is genuinely unknown. Use the
2799
- returned `logQuery` filters; do not assume. Events predating delivery-envelope capture always look
2800
- like this.
2801
- - `AWAITING_DELIVERY` — the Event was never claimed; check queue delivery and action-node health.
2802
- - `IN_FLIGHT_HEALTHY` — the Event is still heartbeating. A long Action is not a stuck one; wait, and
2803
- inspect trace/logs before interrupting.
2804
-
2805
- `delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
2806
- non-idempotent side effect may have run more than once.
2807
-
2808
- The response returns `delivery.threadGroupingId` — the same id everything else uses — plus the full
2809
- `result` map, `diagnosisHints`, and `pivots` carrying ready-to-run inputs for `mcp_performance_trace`,
2810
- `mcp_system_logs`, `mcp_record_listing`, and `mcp_object_activity` when those handles are present.
2811
- `logQuery` carries ready-made Cloud Logging filters, including the container-lifecycle and 504 queries.
2812
- For a performance question, open the `mcp_performance_trace` pivot next; for raw failure context, open
2813
- logs and records by `threadGroupingId`.
2814
-
2815
- ### `mcp_component_view`
2816
- Read component field content with line numbers.
2817
-
2818
- In a normal `remits-cli` coding workflow, prefer local repo files for source reads. Use this tool when the
2819
- local repo is unavailable, when confirming live DB source, or when you need staging / variant metadata that
2820
- is not present in the working tree.
2821
-
2822
- | Parameter | Required | Description |
2823
- |-----------|----------|-------------|
2824
- | `accountId` | yes | Account ID |
2825
- | `componentType` | yes | `Schema`, `Reader`, `Action`, `Embeddable`, `HtmlTemplate`, `Rule`, `Agent`, `Test`, `Tool`, `Prompt` |
2826
- | `componentId` | yes | Component ID |
2827
- | `fieldName` | no | `source`, `html`, `javascript`, `css`, `schema`, `description`, `mermaid`. Also accepts `field`. Omit for metadata. |
2828
- | `offset` | no | Start line (1-indexed). Also accepts `startLine`. |
2829
- | `limit` | no | Number of lines to return |
2830
-
2831
- Returns `componentVariants` when the component has committed branch variants — the branches, their state
2832
- (`current` / `drifted` / `removed`), and a warning. Non-null means some accounts run a different version
2833
- than the source you are reading, and trunk promotions can drift those variants.
2834
- `mcp_component_grep` searches trunk, so it will not match text that exists only in a variant.
2835
-
2836
- ### `mcp_component_grep`
2837
- Search component source code with regex.
2838
-
2839
- | Parameter | Required | Description |
2840
- |-----------|----------|-------------|
2841
- | `accountId` | yes | Account ID |
2842
- | `componentType` | yes | Component type |
2843
- | `pattern` | yes | Regex to search. Also accepts `searchTerm`, `query`, `search`. |
2844
- | `fieldName` | no | Field to search (default: `source`). Also accepts `field`. |
2845
- | `componentId` | no | Specific component. If omitted, searches ALL of the type. |
2846
- | `context` | no | Lines before AND after each match. Also accepts `contextLines`. |
2847
- | `caseSensitive` | no | Default: true |
2848
-
2849
- ### `mcp_support_ticket`
2850
- Create and manage the full lifecycle of account-relative `support_tickets`.
2851
-
2852
- | Parameter | Required | Description |
2853
- |-----------|----------|-------------|
2854
- | `accountId` | conditional | The account that owns the support ticket. **Required only for `create`.** For every other action it is optional — the tool resolves the owning account from `ticketId` (the ticket's anchor id) and returns it. Pass it only to override/disambiguate. |
2855
- | `action` | yes | `create`, `read`, `accept`, `update_status`, `complete`, `release`, `record_progress`, `add_artifact`, or `get_attachment` |
2856
- | `ticketId` | conditional | Required for every action **except** `create` (which returns the new ticket ID). Alone it is sufficient to resolve the ticket and its owning account. |
2857
- | `subject` | conditional | Short title. Required for `create`. |
2858
- | `type` | conditional | Required for `create`: `enhancement`, `defect`, `question`, `task`, or `incident` |
2859
- | `priority` | no | `low`/`medium`/`high`/`critical` for `create` (default `medium`) |
2860
- | `description` | no | Longer description of the request/issue for `create` |
2861
- | `affectedComponent` | no | Component or platform area affected (`create`) |
2862
- | `implementationAccountId` / `implementationAccountName` | no | Owning `PLATFORM`/`PRODUCT` account when the ticket concerns shared implementation (e.g. a back-stage platform fix) |
2863
- | `stepsToReproduce` / `acceptanceCriteria` / `tags` | no | Extra `create` fields for defect/enhancement tickets |
2864
- | `assignee` | no | Required for `accept`. The agent's own name by convention — `claude`, `codex`, `gemini` |
2865
- | `status` | no | Required for `update_status`. Valid values: `in_progress`, `pending_review`. **The ticket must be `accept`ed first** — otherwise the call is rejected with *"Ticket must be accepted before updating status"* |
2866
- | `resolution` | no | Required for `complete` |
2867
- | `category` / `summary` / `details` / `findings` / `nextStep` | no | Worklog fields for `record_progress` (`category` + `summary` required). `category` is a **fixed enum** — `triage`, `investigation`, `reproduction`, `fix`, `verification`, `handoff`, `other` — and any other value fails the whole call. `findings` is a **list of strings**, not a paragraph |
2868
- | `artifactType` / `artifactLabel` / `contentBase64` / `gcsPath` / `url` | no | Evidence fields for `add_artifact` (screenshot/trace/log/test_result/link) |
2869
- | `notes` | no | Optional lifecycle note stored with the ticket activity |
2870
- | `attachmentIndex` | no | Zero-based index of the attachment to download. Used with `get_attachment`. |
2871
-
2872
- **Opening a ticket for your own work (`create`).** When you are asked to do work — or you discover a Remits **back-stage** defect while building front stage — and you were **not** handed an existing ticket, open one with `action:'create'` so the work is tracked end-to-end. For a platform fix, use `type:'defect'` (or `'enhancement'` for a gap), describe the seam and evidence, reference the fix PR, and set `implementationAccountId`/`implementationAccountName` to the owning `PLATFORM`/`PRODUCT` account.
2873
-
2874
- **Attachments:** Support emails may include file attachments (screenshots, logs, documents). These are automatically extracted and stored in GCS when the email is ingested. The `read` action returns an `attachments` array on the ticket with metadata for each file (`index`, `filename`, `contentType`, `size`, `messageId`, `uploadedAt`). To retrieve the actual file content:
2875
-
2876
- 1. Use `read` to see the attachments list and their indices
2877
- 2. Use `get_attachment` with the desired `attachmentIndex` to download the file content (returned base64-encoded)
2878
- 3. If called without `attachmentIndex`, `get_attachment` lists all attachments with their indices
2879
-
2880
- This keeps file retrieval self-contained — no separate download endpoint is needed.
2881
-
2882
- **Recommended flow** — `accountId` is optional throughout; `ticketId` resolves the owning account:
2883
- 1. `read` — check ticket state and any attachments
2884
- - If `read` returns `ticket.mirrorOnly:true`, use the mirrored fields for triage context, restore the backing document first, then claim/update/complete it.
2885
- 2. **`accept`** (with `assignee`) — this is a **hard precondition for `update_status`**, not just etiquette
2886
- 3. `get_attachment` if attachments are present and relevant to the investigation
2887
- 4. `update_status` — `in_progress` while working, `pending_review` when the fix is done but not yet deployed
2888
- 5. `record_progress` as you go — one `investigation` entry for the root cause, one `verification` entry for the proof
2889
- 6. investigate/fix/verify on the owning ticket account or its implementation account as appropriate
2890
- 7. `complete` (with `resolution`) or `release` if handing off
2891
-
2892
- **Check each mutation actually landed.** Every action above is a write that can be refused while the
2893
- CLI still reports the *call* as fine — see "Execute a Tool" for why, and read `result.success` from
2894
- the response file. A silent no-op here means telling the user a ticket moved when it did not.
2895
-
2896
- **Duplicates are common.** The same defect is often filed twice — once against the `CLIENT`/subscriber
2897
- account where it was observed and once against the owning `PLATFORM`/`PRODUCT` account. Before
2898
- starting, check `mcp_support_ticket_queue` for the same subject or affected component. Close the
2899
- duplicate with a `resolution` naming the ticket that carries the real work, rather than investigating
2900
- it twice.
2901
-
2902
- **Automation rule:** If a ticket is involved, you should usually:
2903
- - `read` at the start
2904
- - `accept` before substantive work
2905
- - `get_attachment` if there are attachments relevant to the issue (screenshots, error logs, etc.)
2906
- - `update_status` when actively working or blocked
2907
- - `complete` after verification
2908
- - `release` if you are handing it off or cannot continue
2909
-
2910
- ### Component branches
2911
- Use `remits-cli components branches` / `remits-cli components branch <name>` to inspect committed branch
2912
- variants, subscribers, and drift from a local checkout.
2913
-
2914
- Common uses:
2915
- - `remits-cli components branches` — list branches this owner has variants on.
2916
- - `remits-cli components branch <name>` — show overridden / added / removed components on that branch.
2917
- - `remits-cli components branch <name> --diff <id> --component-type <kind>` — compare one variant against
2918
- current trunk.
2919
- - `remits-cli components branch <name> --subscribers` — list accounts resolving that branch.
2920
-
2921
- ### `mcp_cache`
2922
- Bounded read-only investigation of the platform Redis keyspace — the way to see exactly what a staged
2923
- entry holds (and its TTL) or any other cache key. Read-only: no delete (use `remits-cli components clear`
2924
- to remove staged component entries).
2925
-
2926
- | Parameter | Required | Description |
2927
- |-----------|----------|-------------|
2928
- | `action` | yes | `summary` (overview of matching keys), `scan` (paginated key list; add `includeValuePreview:true`), or `inspect` (one exact `key`) |
2929
- | `pattern` | no | Redis glob for summary/scan (e.g. `account:52:cli:*:components:*:reader:id:181`). Alias: `keyPattern`/`query` |
2930
- | `key` | no | Exact key for `action:'inspect'` |
2931
- | `pageSize`/`sampleSize`/`previewChars` | no | Bounding controls |
2932
-
2933
- ### `mcp_sql_query`
2934
- Read-only, bounded SQL against the platform database. This is the **catch-all investigation surface** for
2935
- questions the purpose-built tools do not model — above all **users and account membership**, for which there
2936
- is no dedicated tool.
2937
-
2938
- | Parameter | Required | Description |
2939
- |-----------|----------|-------------|
2940
- | `query` | yes* | One read-only statement. Alias: `sql`. Must start with `SELECT`, `WITH`, `SHOW`, `DESCRIBE`/`DESC`, or `EXPLAIN`; mutation, DDL, locking, and filesystem constructs are rejected. |
2941
- | `queries` | yes* | Batch of up to 10 read-only queries (strings, or `{query, params}` objects) |
2942
- | `params` / `parameters` | no | Positional parameters — **use these instead of interpolating values** |
2943
- | `maxRows` / `limit` | no | Rows per query. Default 100, max 500. |
2944
- | `maxValueChars` | no | Truncation width per string value. Default 2000, max 20000. |
2945
- | `redact` | no | Redact secret-like columns (`password`, `token`, `secret`, `authorization`, …). **Default true — leave it on.** |
2946
-
2947
- *Provide `query`/`sql` or `queries`.
2948
-
2949
- Useful shapes:
2950
-
2951
- ```sql
2952
- -- who has access to a client account (and through which membership rows)
2953
- SELECT u.id, u.username, u.enabled FROM user u
2954
- JOIN user_account ua ON ua.user_id = u.id WHERE ua.account_id = ?;
2955
-
2956
- -- every account a user can reach directly
2957
- SELECT a.id, a.name, a.type FROM account a
2958
- JOIN user_account ua ON ua.account_id = a.id WHERE ua.user_id = ?;
2959
-
2960
- -- the account's structural links, including membership edges (`primary` is column `is_primary`)
2961
- SELECT parent_id, is_primary, branch_name, database_name, domain_name, active
2962
- FROM account_relationship WHERE account_id = ?;
2963
- ```
2964
-
2965
- **This tool is lane-blind — filter the data lane yourself.** Every other record surface segments test from
2966
- prod data for you; raw SQL does not. `object`, `event`, and `alert` carry a `test_mode` column, `user`
2967
- carries `test_user`, and `account` carries `test_account`, and an unfiltered query returns **both lanes
2968
- mixed** — so "who has access to this account" silently includes throwaway test users, and a row count
2969
- silently includes test fixtures. Add the predicate explicitly:
2970
-
2971
- ```sql
2972
- -- prod lane only (legacy rows predate the column, so NULL counts as prod)
2973
- SELECT id, name, status FROM object
2974
- WHERE account_id = ? AND (test_mode IS NULL OR test_mode = 0);
2975
-
2976
- -- real users of an account, excluding test fixtures
2977
- SELECT u.id, u.username, u.enabled FROM user u
2978
- JOIN user_account ua ON ua.user_id = u.id
2979
- WHERE ua.account_id = ? AND (u.test_user IS NULL OR u.test_user = 0);
2980
- ```
2981
-
2982
- Prefer the purpose-built tools when one fits — they apply account scoping, data-mode segmentation, and
2983
- resolution awareness that raw SQL does not. Reach for SQL when nothing else models the question.
2984
-
2985
- ### `mcp_index_search`
2986
- Query and **diagnose** the Vertex AI Search index through the platform's Vertex DSL. Use it to check what a
2987
- RAG/search-backed component actually retrieves before blaming the component.
2988
-
2989
- | Parameter | Required | Description |
2990
- |-----------|----------|-------------|
2991
- | `accountId` | yes | Tenant scoping |
2992
- | `action` | no | `search` (default, `index_search()`), `facets` (`index_facets()` — taxonomy/distinct values), `diagnose` (explain why strict filtering dropped matches), `inspect` (what is actually indexed for given `sourceDocumentIds`) |
2993
- | `query` | conditional | Required for `search`/`diagnose` |
2994
- | `schema` / `projectionType(s)` / `projectionSource` | no | Scope to a schema's Vertex projections |
2995
- | `filters` | no | Vertex-style structured filters on indexed metadata |
2996
- | `pageSize` / `maxResults` / `pageToken` | no | Paging and local trimming |
2997
- | `searchProfile` | no | `ai`/`agent`/`strict` (precision) vs `admin`/`default` (recall) |
2998
- | `includeMatchDiagnostics` | no | Return the applied filter/query plus the pre-strict-filter candidate set |
2999
- | `accountIds` | no | Explicit multi-account search |
3000
- | `dataMode` | no | `test` (default) or `prod` |
3001
-
3002
- When a component "can't find" an obviously-present document, run `action:'inspect'` on its source document id
3003
- first — that shows what was indexed, which is usually the answer.
3004
-
3005
- ### `mcp_get_guide`
3006
- Load the packaged front-stage guides — the same `docs/guides/` set `remits-cli` syncs into a repo. Use it when
3007
- you are working **outside a repo** (or the repo's `guides/` is stale) and need the authoritative guidance
3008
- before writing a component.
3009
-
3010
- | Parameter | Required | Description |
3011
- |-----------|----------|-------------|
3012
- | `guide` | conditional | Short name (`agent-components`), relative path (`features/account-management`), or full path |
3013
- | `list` | no | List available guides instead of loading one |
3014
- | `directory` | no | Scope a list to `components` or `features` |
3015
- | `contains` | no | Substring filter when listing |
3016
-
3017
- Every guide is delivered with a table of contents whose entries carry **real line numbers**
3018
- (`- L412 Querying Alerts`), resolved at delivery so they are never stale. Several of these guides are
3019
- over a thousand lines: read the head, pick the sections you need, and offset-read those rather than
3020
- loading the whole file. The entry text is the heading verbatim, so it also greps.
3021
-
3022
- ### `mcp_test_fixture`
3023
- Seed and remove schema-backed fixture documents in the **forced test** data segment. This is how you construct
3024
- realistic conditions for verification without copying live customer data.
3025
-
3026
- | Parameter | Required | Description |
3027
- |-----------|----------|-------------|
3028
- | `accountId` | yes | Account owning the target schema/collection |
3029
- | `action` | no | `create` (fails on existing id), `upsert`, or `delete` |
3030
- | `schemaName` / `collection` | conditional | Schema display name or collection name |
3031
- | `documentId` / `documentIds` | no | Explicit Firestore ids for single/batch operations |
3032
- | `data` / `documents` | conditional | Single payload, or a batch array |
3033
- | `dataMode` | no | Must be `test` — **prod-mode writes are rejected** |
3034
-
3035
- ### `mcp_playwright_replay`
3036
- Hosted browser automation (the `playwright-relay` service) for visual verification when local `playwright-cli`
3037
- is unavailable — e.g. an agent running remotely. Returns an accessibility snapshot and interactive refs on
3038
- every command, so you navigate iteratively.
3039
-
3040
- | Parameter | Required | Description |
3041
- |-----------|----------|-------------|
3042
- | `command` | yes | `open`, `goto`, `snapshot`, `click`, `dblclick`, `hover`, `fill`, `select`, `check`, `uncheck`, `eval`, `run-code`, `screenshot`, `pdf`, `console`, `network`, `go-back`, `go-forward`, `reload`, `tab-*`, `close`, `close-all` |
3043
- | `sessionId` | conditional | Reuse an existing session. `open`/`goto` without one starts a session. |
3044
- | `url` / `target` / `value` / `expression` / `code` | conditional | Per-command inputs (`target` is an element ref or selector) |
3045
- | `compact` | no | Default true — omits bulky raw relay payloads |
3046
- | `includeSnapshot` / `includeAccessibility` / `includeRefs` / `includePage` / `includeResult` / `includeRaw` | no | Response shaping |
3047
- | `pattern` / `level` / `limit` | no | Filters for `console` / `network` |
3048
- | `ticketId` / `artifactType` / `artifactLabel` / `artifactNotes` | no | Attach a screenshot/pdf/trace/video to a support ticket |
3049
-
3050
- ### `mcp_jvm_spike_triage`
3051
- Production-aware triage bundle for a Cloud Run service showing a latency/memory spike. Safe by construction:
3052
- optional class-histogram sampling, **no heap dump and no JFR**.
3053
-
3054
- | Parameter | Required | Description |
3055
- |-----------|----------|-------------|
3056
- | `serviceName` | yes | e.g. `remits`, `remits-actions` |
3057
- | `region` | yes | e.g. `us-east5`, `us-east1` |
3058
- | `sampleHeap` | no | Default true — one class histogram + heap composition sample (brief stop-the-world) |
3059
- | `top` | no | Top classes to return. Default 20, max 100. |
3060
- | `includeThreadDump` / `threadLimit` | no | Fuller thread dump beyond the built-in top-thread preview |
3061
- | `includeLogs` / `logLookbackMinutes` / `logLimit` | no | Recent WARNING+ log signals for the same service |
3062
-
3063
- ### `mcp_support_ticket_queue`
3064
- List and filter tickets **across an account and its descendants** so you can choose what to work on. Lifecycle
3065
- actions stay on `mcp_support_ticket`.
3066
-
3067
- Queue rows are built from support-ticket anchor mirrors by default. A row in this list means a
3068
- support-ticket anchor exists; it does not guarantee the full Firestore document is healthy. Open the
3069
- ticket with `mcp_support_ticket read` before lifecycle work and honor `mirrorOnly` / `documentState` if present.
3070
-
3071
- | Parameter | Required | Description |
3072
- |-----------|----------|-------------|
3073
- | `action` | no | `list` (default) |
3074
- | `accountId` / `accountIds` / `includeChildren` / `includeRoot` | no | Queue scope. Defaults to the current account **and its descendants**. |
3075
- | `statuses` / `status` | no | Defaults to `open`, `accepted`, `in_progress`, `pending_review`. `['all']` disables filtering. |
3076
- | `priorities` / `types` / `sources` / `tags` | no | Additional filters |
3077
- | `assignedTo` / `unassigned` | no | Assignment filters |
3078
- | `implementationAccountId` | no | Filter by owning `PLATFORM`/`PRODUCT` account |
3079
- | `workstream` / `plannedIn` / `boardStage` / `size` / `blockedBy` | no | SDLC-agnostic planning filters; exact matches on compact queue fields |
3080
- | `rank` / `minRank` / `maxRank` | no | Exact or inclusive range filters for numeric planning rank |
3081
- | `search` | no | Case-insensitive across subject, description, account, affected component, sender, tags, workstream, and planning fields |
3082
- | `sortBy` | no | `triage` (default-style: critical/high unassigned first), `updated`, `priority`, `status`, `account`, `plannedIn`, `boardStage`, `rank`, `size` |
3083
- | `sortDirection` | no | `asc` / `desc`, and it means the same thing on every axis. Natural order is `desc` for `triage`/`updated`/`priority` and `asc` for `status`/`account`/`rank`/`plannedIn`/`boardStage`/`size`, so pass it only to invert one |
3084
- | `limit` / `offset` / `scanLimitPerAccount` | no | Paging and scan bounds |
3085
- | `dataMode` | no | Normal agent work uses the **prod** ticket queue |
3086
-
3087
- ## Multi-Session Support
3088
-
3089
- 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.
3090
-
3091
- ```bash
3092
- # Authenticate for an account (run from its repo, or pass --account-id)
3093
- remits-cli auth
3094
- remits-cli auth --account-id 42
3095
- remits-cli auth --account-id 42 --base-url http://localhost:8080
3096
-
3097
- # List all active sessions
3098
- remits-cli sessions list
3099
-
3100
- # Remove a session (removes all sessions for the account, or narrow with --base-url / --data-mode)
3101
- remits-cli sessions remove --account-id 42
3102
- remits-cli sessions remove --account-id 42 --base-url http://localhost:8080
3103
- ```
3104
-
3105
- 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.
3106
-
3107
- Use `remits-cli whoami` when you need a compact proof of the active target before a sensitive operation.
3108
- It prints the resolved Account ID, User ID, current git branch, data mode, and base URL. `remits-cli status`
3109
- prints the same session tuple after the service/dashboard status. Pass `--base-url`, `--account-id`, and
3110
- `--data-mode` when host or lane matters; do not infer those values from the repo directory or account name.
3111
-
3112
- **The reported data mode describes the next `tool` / `tools` / `token` call, not `test run`.** It falls back
3113
- to the stored session lane, whereas `remits-cli test run` deliberately ignores that and defaults to `test`
3114
- unless you pass `--data-mode prod` explicitly. So `whoami` can read `prod` while a test run goes to the test
3115
- lane — which is the safe direction, but not the one you would predict from the output alone. `whoami` says
3116
- so in its own output; when a test run genuinely needs prod data, pass the flag.
3117
-
3118
- ## Registering This Session As An Agent
3119
-
3120
- `remits-cli agent` is how a terminal session makes itself available to work support tickets. It is
3121
- independent of everything else — no background service is required, and every tab is its own agent.
3122
-
3123
- ```bash
3124
- remits-cli agent serve # routable AND autonomously working tickets — the usual choice
3125
- remits-cli agent workers # what this session is running right now
3126
- remits-cli agent list # who else is available across the accounts you cover
3127
- remits-cli agent release # stop serving and stop receiving tickets right now
3128
-
3129
- remits-cli agent register # presence ONLY — nothing starts on its own
3130
- remits-cli agent work --wait 300 # ask for tickets routed here (manual loop)
3131
- remits-cli agent status --state working --ticket 22454 --activity "reproducing"
3132
- ```
3133
-
3134
- `serve` is `register` plus a supervisor. Everything below about registration applies to both.
3135
-
3136
- **Defaults exist so the user does not have to type them.** With no flags, `register` targets the
3137
- production platform (`REMITS_BASE_URL` when set) and the **prod** lane, and claims every account repo
3138
- indexed on this machine. That is a deliberate exception to the CLI's test-first default: registering
3139
- presence mutates no business data, and a test-lane agent is silently useless for support — it appears
3140
- online and can never be routed a production ticket. Every command after `register` reuses the
3141
- platform, lane and identity it registered with.
3142
-
3143
- ### What registration actually does
3144
-
3145
- - Mints an `agentId` for this session and tells the platform which account repos this machine has
3146
- checked out — read from the machine-wide index (`~/.remits-cli/account-repos.json`), which is why
3147
- the working directory does not matter. The platform **verifies** those claims against what your user
3148
- can access and returns the ones it refused, so "I registered but never get tickets for account 52"
3149
- is answerable from the registration output alone.
3150
- - Starts a small detached heartbeat process **anchored to the agent process that owns this terminal**
3151
- (it walks up the process tree to find `claude`/`codex`/`gemini`, then an interactive shell). When
3152
- that process ends, the heartbeat ends and the session stops being routable within a couple of
3153
- minutes. Closing the tab is a valid way to go offline; `agent release` just makes it immediate.
3154
- - Registers in the session's **data lane**. A `test`-lane run only ever reaches a `test`-lane agent,
3155
- which is what keeps fixture traffic away from a production terminal.
3156
- - Declares **capacity** — how many tickets this session can genuinely run at once (`--max-concurrent`,
3157
- default 1). The router will not exceed it.
3158
-
3159
- ### What `serve` adds
3160
-
3161
- The same detached process that maintains presence also polls for work. On each poll it publishes the
3162
- tickets it currently has running, renews their claims, and takes at most enough new ones to fill its
3163
- capacity. For each new ticket it launches a headless worker:
3164
-
3165
- | | headless invocation | edit mode | investigate mode |
3166
- |---|---|---|---|
3167
- | `codex` | `codex exec --cd <repo>` | `--sandbox workspace-write` | `--sandbox read-only` |
3168
- | `claude` | `claude -p --add-dir <repo>` | `--permission-mode acceptEdits` | `--permission-mode plan` |
3169
- | `gemini` | `gemini -p` in `<repo>` | `--approval-mode auto_edit` | `--approval-mode plan` |
3170
-
3171
- **Which of the three it launches:** `--worker-agent` if you pass it, otherwise **whatever agent this
3172
- session is** (detected from the process the agent anchored to — which finds nothing in a plain tab),
3173
- otherwise `~/.remits-cli/config.json`'s `agent` (set it with `remits-cli config set --agent NAME`),
3174
- otherwise `claude`. **It prints which it chose and why on startup**, because in the intended
3175
- 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
3176
- without a flag, and the label the operator sees (`codex@repo`) is resolved the same way — the label
3177
- and the worker are one answer, not two.
3178
-
3179
- The brief always arrives on **stdin**, never in argv — it is a page of prose and argv has a hard
3180
- length limit, so an argv brief would fail on exactly the detailed tickets that most need the detail.
3181
- The brief itself is generated by the platform, so all three worker types are told the same thing.
3182
-
3183
- **Following a run.** `remits-cli start`'s control center lists every worker; **Follow live** streams
3184
- that run's transcript into the page, rendering commands with their exit codes, file edits, and the
3185
- agent's own messages as distinct things. It keeps working after the worker exits — a finished run is
3186
- usually the one worth reading. For a terminal instead, each worker prints a `tail -f` for its log in
3187
- `~/.remits-cli/workers/`.
3188
-
3189
- **There is no terminal to attach to, by design.** A worker is spawned with pipes and runs
3190
- non-interactively (`codex exec`, `claude -p`), so there is no tty and no prompt to type at — the
3191
- whole point is that it needs no supervision. Following the transcript is the way to watch, and it is
3192
- strictly better than a terminal would be: the output is structured, so it can be read as events
3193
- rather than scraped back out of ANSI text.
3194
-
3195
- **No worker ever commits.** The brief forbids `git commit`/`git push` and nothing in the supervisor
3196
- runs git — a worker leaves its changes in the working tree and says so on the ticket, for a human to
3197
- review.
3198
-
3199
- ### Resolving which agent a command means
3200
-
3201
- Order: `--agent-id`, then `REMITS_AGENT_ID`, then the single agent registered for this working
3202
- directory. With several registered and no way to tell them apart, the command **fails and lists the
3203
- candidates** rather than guessing — routing work to the wrong tab is silent, and a message is not.
3204
-
3205
- ### Which agent gets a ticket
3206
-
3207
- The platform walks a ladder — the ticket's implementation account first (a defect seen on a client is
3208
- usually fixed in the platform repo above it), then the ticket's own account, then its
3209
- `PLATFORM`/`PRODUCT` ancestors — and takes the first account with an available agent. Among those it
3210
- prefers an **idle** agent, then the one that has waited longest, so work spreads across your tabs
3211
- instead of piling onto whichever one most recently ran a command. A `paused` agent stays visible and
3212
- is never routed to, and so is one **at capacity** — those are different facts and stay separate:
3213
- `paused` means a human stopped this session, at-capacity means its workers are all busy.
3214
-
3215
- ### Why a routed ticket might not have started
3216
-
3217
- In order of likelihood:
3218
-
3219
- 1. **The session registered but never served.** `agent register` starts nothing. `agent workers`
3220
- showing none while a ticket is routed here is this.
3221
- 2. **At capacity.** Check `agent workers`; the ticket starts when one finishes.
3222
- 3. **Another session is running it.** A claim held by a different agent blocks it until that claim is
3223
- dropped or expires.
3224
- 4. **No local checkout.** The worker still starts, and its brief tells it to locate the repo rather
3225
- than guess — but if the account genuinely is not on this machine it will say so and stop.
3226
- 5. **It failed twice already** and was released back to the queue. The transcripts in
3227
- `~/.remits-cli/workers/` say why.
3228
-
3229
- ## Background Service and Control Center
3230
-
3231
- `remits-cli start` runs an optional background service for the **human** watching:
3232
-
3233
- - Maintains persistent WebSocket connections to the Remits platform
3234
- - Hosts a localhost browser **control center** (typically `http://127.0.0.1:8787/`)
3235
-
3236
- ```bash
3237
- remits-cli start
3238
- remits-cli start --foreground true
3239
- remits-cli status
3240
- remits-cli whoami
3241
- remits-cli stop
3242
- ```
3243
-
3244
- Compatibility aliases still exist:
3245
-
3246
- ```bash
3247
- remits-cli listen
3248
- remits-cli listen status
3249
- remits-cli listen stop
3250
- ```
3251
-
3252
- **The service is not part of ticket delivery.** Agents register and collect work on their own, so the
3253
- dashboard being down never stops a ticket reaching an agent. What the service adds is visibility: it
3254
- scans for `account-info.json` files to rebuild the repo index, keeps one WebSocket per platform URL,
3255
- and holds a PID lock (`~/.remits-cli/listener.pid`) so only one runs per machine.
3256
-
3257
- ### Control Center
3258
-
3259
- The control center shows, in one browser view:
3260
- - which agent sessions are registered, what each is doing right now, and its recent activity
3261
- - the open support-ticket queue, and which agent each ticket is routed to
3262
- - a control to route a ticket at a specific available agent
3263
- - websocket connection and topic health
3264
- - the discovered account repo index and the important global/per-repo files
3265
-
3266
- **What it shows is what the agents see.** The ticket queue comes from the platform's own generic
3267
- endpoint, not from any product's tool, so the control center reads the same on every Remits platform
3268
- and never implies a workflow that a particular product does not have. Product-specific views belong
3269
- in that product's Embeddables.
3270
-
3271
- When a user asks a broad or vague question about remits-cli behavior, prefer reasoning from the
3272
- control center state before spelunking individual files.
3273
-
3274
- ### Configuring the Preferred Agent
3275
-
3276
- ```bash
3277
- remits-cli config # Show current config
3278
- remits-cli config set --agent claude # Use Claude Code (default)
3279
- remits-cli config set --agent codex # Use OpenAI Codex CLI
3280
- remits-cli config set --agent gemini # Use Gemini CLI
3281
- ```
3282
-
3283
- The agent preference is stored in `~/.remits-cli/config.json` and applies globally.
3284
-
3285
- ## Local State Files
3286
-
3287
- **Global** (`~/.remits-cli/`):
3288
- - `sessions.json`
3289
- - Source of truth for authenticated account sessions, keyed by `accountId + dataMode + baseUrl` so the same account can hold separate sessions for different environments (e.g., localhost vs production).
3290
- - Each entry contains auth token, user info, websocket topic, data mode, base URL, and timestamp.
3291
- - Read this when a question involves auth, account selection, websocket topic coverage, or which session a command should resolve.
3292
- - `config.json`
3293
- - Global CLI preferences.
3294
- - Currently most important for preferred agent selection, but treat it as the general machine-level config file.
3295
- - `service-state.json`
3296
- - Runtime snapshot for the currently running remits-cli service.
3297
- - Includes dashboard URL, chosen port, repo-discovery summary, and websocket connection state.
3298
- - This is the first file to read when the user asks "is remits-cli running?", "what port is the dashboard on?", or "why isn't the browser page showing my connections?"
3299
- - `account-repos.json`
3300
- - Index of all known account repositories on this machine.
3301
- - Built from `account-info.json` discovery plus best-effort updates when commands run inside an account repo.
3302
- - This is the repo-resolution file. Read it before deciding that a repo is unavailable locally.
3303
- - `listener.pid`
3304
- - PID of the background remits-cli service process.
3305
- - Use it only to confirm process presence; use `service-state.json` for richer service details.
3306
- - `agents.json`
3307
- - The agent sessions registered from this machine, each with the process it is anchored to.
3308
- - Read this when a command asks for `--agent-id`, or when an agent appears registered locally but
3309
- is missing from `remits-cli agent list` (that gap IS the "why am I not getting tickets" answer).
3310
- - `activity.log`
3311
- - Human-readable chronological event log for service lifecycle, websocket events, agent
3312
- registration/heartbeat, and ticket routing.
3313
- - This is usually the best forensic file for "what happened?" questions.
3314
-
3315
- **Per-repo** (`./.remits-cli/`):
3316
- - `tools/tools.json`
3317
- - Cached tool definitions for this repo context.
3318
- - Read this when tool availability or input shape is unclear.
3319
- - `sessions/<name>.jsonl`
3320
- - Repo-local HTTP request/response log for `/cli/*` calls.
3321
- - Tokens are redacted and large content fields are summarized.
3322
- - This is the first file to inspect when the question is "what exactly did remits-cli send or receive from this repo?"
3323
- - `tool-responses/<callId>.json`
3324
- - Full payload for `remits-cli tool` responses that were too large for the session log.
3325
- - Prefer this over terminal summaries when investigating tool behavior.
3326
- - `current-session.txt`
3327
- - Pointer to the active repo-local session log name.
3328
- - Always read this before opening `sessions/<name>.jsonl`.
3329
-
3330
- Reading rules:
3331
- - Read `current-session.txt` before opening a session log by name.
3332
- - When a tool call says the response was stored externally, open `tool-responses/<callId>.json` instead of inferring from the terminal summary.
3333
- - If a question spans both global and repo-local behavior, inspect both layers and explain which facts came from which layer.
3334
-
3335
- ## Command Reference
3336
-
3337
- ```
3338
- remits-cli auth [--base-url URL] [--account-id ID] [--data-mode test|prod]
3339
- remits-cli sessions [list|remove] [--account-id ID]
3340
- remits-cli config [set] [--agent claude|codex|gemini]
3341
- remits-cli agent serve [--worker-agent claude|codex|gemini] [--max-concurrent N] [--mode edit|investigate] [--label NAME] [--data-mode test|prod]
3342
- remits-cli agent workers [--json]
3343
- remits-cli agent register [--label NAME] [--data-mode test|prod] [--account-id ID] [--max-concurrent N]
3344
- remits-cli agent work [--wait SECONDS] [--json]
3345
- remits-cli agent status [--state idle|working|paused] [--ticket ID] [--activity "..."] [--step "..."]
3346
- remits-cli agent list [--account-id ID] [--json]
3347
- remits-cli agent map [--account-ids 1,4] [--json] # who is EDITING which repository, from which checkout/branch/staging lane
3348
- remits-cli agent release
3349
- remits-cli ticket read|accept|status|progress|complete|release|reopen|assign|planning --ticket ID [--status S] [--resolution "..."] [--summary "..."] [--category C] [--assignee EMAIL] [--workstream VALUE] [--planned-in VALUE] [--board-stage VALUE] [--rank N] [--size VALUE] [--blocked-by VALUE] [--notes "..."]
3350
- remits-cli ticket lease|unlease|force-unlease --ticket ID [--reason "..."] # force-unlease breaks SOMEBODY ELSE'S lease; operator only
3351
- remits-cli ticket where --ticket ID # WHERE this work is: repo account, checkout, branch, staging lane, live lease + holder's location, claim, your phase
3352
- remits-cli ticket ask --ticket ID --question "..." [--context "..."] [--to WHO] # park on a human decision
3353
- remits-cli ticket answer --ticket ID --message "..." [--route false] # answer it (alias: reply); re-routes by default
3354
- remits-cli ticket message --ticket ID --message "..." [--to a@x,b@y] [--subject "..."] [--channel email] # PUBLIC — the requester reads it
3355
- remits-cli ticket note --ticket ID --message "..." # INTERNAL — the next worker and a reviewer read it
3356
- remits-cli ticket deliver --ticket ID --channel email --to a@x [--template T] [--subject "..."] [--idempotency-key K] # ask for it to be SENT
3357
- remits-cli ticket deliveries --ticket ID [--channel email] # what is queued to go out, and what already went
3358
- remits-cli ticket delivered --ticket ID --delivery-id ID | ticket delivery-failed --ticket ID --delivery-id ID --reason "..."
3359
- remits-cli ticket tag --ticket ID --tags a,b # make a cluster of near-identical tickets visible as one
3360
- remits-cli ticket artifact --ticket ID --type TYPE --label "..." [--url U | --content "..."]
3361
- remits-cli ticket participant --ticket ID --email E [--role watcher|requester|agent]
3362
- remits-cli ticket field --ticket ID --key K --value V # the ORGANIZATION's own field, outside the planning slots
3363
- remits-cli ticket reclaim [--ticket ID | --account-id ID] [--stale-hours 24] [--apply] # OPERATOR: take back an agent's abandoned ticket
3364
- remits-cli ticket queue --account-id ID [--status ...] [--unrouted] [--unassigned] [--awaiting-response] [--workstream W] [--board-stage S] [--planned-in P] [--search "..."] [--sort-by ...] [--json]
3365
- remits-cli ticket create --account-id ID --subject "..." --type defect|question|task|incident|enhancement [--priority P] [--description "..."] [--tags a,b] [--workstream W] [--affected-component C] [--implementation-account-id ID] [--reference-id KEY]
3366
- remits-cli start [--foreground true] [--port 8787]
3367
- remits-cli stop
3368
- remits-cli status [--base-url URL] [--account-id ID] [--data-mode test|prod] [--json]
3369
- remits-cli whoami [--base-url URL] [--account-id ID] [--data-mode test|prod] [--json]
3370
- remits-cli listen [stop|status] [--foreground true] # compatibility alias
3371
- remits-cli data-mode [set test|prod]
3372
- remits-cli components stage [--branch <name>] [--workspace <name>] [--changed-only] [--data-mode test|prod] [--json|--verbose]
3373
- remits-cli workspace [show | use <name> | use --auto | clear]
3374
- remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]
3375
- remits-cli components clear [--branch <name>] [--component-type <type>] [--component-id <id>] [--all] [--json|--verbose] # id alone scopes to one component when unambiguous (ids are type-local; add --component-type if the same id is staged in multiple families); no filter clears the whole branch scope; --all forces the full wipe
3376
- remits-cli components sync [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--summary] [--changed-only [--changed-since <ref>]] # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
3377
- remits-cli components commit [--message "msg"] [--data-mode test|prod] [--force-tombstones]
3378
- remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
3379
- remits-cli components branch <name> [--json] # one branch: overridden / added / removed, drift flags, subscribers
3380
- remits-cli components branch <name> --diff <componentId> --component-type <kind> [--json]
3381
- remits-cli components branch <name> --subscribers [--json]
3382
- remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>] [--dry-run] [--confirm-primary-edge] # make an account resolve this branch
3383
- remits-cli components branch <name> --unsubscribe <accountId> # return that account to trunk
3384
- remits-cli components branch <name> --retire [--force] # delete the branch's overlays
3385
- remits-cli test run --test <id|name> [--branch <stagingScope>] [--names "a|b"] [--watch true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
3386
- remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
3387
- remits-cli token inspect --token <token|tokenKey|URL> # inspect token metadata, safety/dataMode evidence, and full context
3388
- remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
3389
- remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
3390
- remits-cli tool status --call-id <callId> [--data-mode test|prod]
3391
- ```
3392
-
3393
- For tests specifically:
3394
- - If `--data-mode` is omitted, `remits-cli test run` uses `test`.
3395
- - `--names` is `|`-delimited (a comma still splits a single value) and may be repeated; an unmatched
3396
- selector fails the run instead of reporting zero cases as success.
3397
- - `--as-account <ID>` runs AS a descendant subscriber so its edge selects the component branch
3398
- (*"what does customer X get?"*); `--variant-branch <name>` probes a branch from any checkout
3399
- (*"what does branch Y look like?"*), and `--variant-branch none` forces production/subscription semantics.
3400
- Omit both and the working tree decides — see "Branched Component Variants".
3401
- - `--branch <stagingScope>` on `test run` selects the Redis staging namespace only. It is useful with
3402
- `--variant-branch none` when an existing staged cache on the real git branch would shadow committed trunk
3403
- or variant rows. On `components sync` / `commit`, `--branch` is different: it names the GitHub branch to
3404
- reconcile.
3405
- - `--force-tombstones` is only for non-trunk variant syncs, when missing trunk component files are known,
3406
- intentional tombstone overrides. It is rejected on trunk.
3407
- - `--dry-run` is only for `components sync` on non-trunk variant branches. It reports the variant write
3408
- plan without writing rows, caching the sync SHA, or clearing staging.
3409
- - `--summary` on `components sync --dry-run` prints compact counts, removals/tombstones, skipped items, errors,
3410
- and warnings instead of the full override/add/remove payload.
3411
- - **Fail-closed sync gates.** On non-trunk variant branches these flags now force a server dry-run first,
3412
- evaluate that plan before any overlay row is written, and only then run the mutating sync when the plan
3413
- passes. On trunk, there is no safe dry-run plan, so do not treat these as scoped commit controls:
3414
- - `--changed-only` — fail unless every planned write is a component **this checkout actually changed**.
3415
- This is the strongest guard against a sync that quietly rewrites components you never touched. It
3416
- also fails when the checkout is not a git working tree, because "git could not answer" must never
3417
- be read as "nothing changed".
3418
- > **Pair it with `--changed-since <ref>` after you have committed.** On its own `--changed-only`
3419
- > reads UNCOMMITTED edits, and the documented flow commits and pushes *before* syncing (sync reads
3420
- > the pushed remote, so it cannot see uncommitted work at all) — so the changed set is empty at
3421
- > exactly the moment the gate runs, and it refuses the whole plan. `--changed-since origin/main`
3422
- > (or the commit you branched from) makes the changed set the components your commits touched.
3423
- > The refusal message says this when it detects the empty-set case.
3424
- - `--fail-on-removed` — fail if the plan removes or tombstones anything.
3425
- - `--expected-removed <type:id>` — whitelist the removals you intend (repeatable, or comma-delimited,
3426
- e.g. `--expected-removed action:5,reader:9`). It **implies** `--fail-on-removed`, so any removal you
3427
- did not name fails the sync.
3428
- - `--fail-on-errors` — fail if the server reported any per-component sync error.
3429
- - `--names-only` — dry-run and print only `BUCKET type:id name` lines for the planned writes, then stop
3430
- without writing overlays.
3431
-
3432
- A good default for an unattended promotion is:
3433
- `remits-cli components sync --summary --changed-only --fail-on-errors`
3434
-
3435
- ### Prod banners and retryable failures
3436
-
3437
- - Every command that can touch production (`tool`, `test run`, `components sync`) prints a `PROD DATA`
3438
- banner naming the operation, the resolved account, and the host — and distinguishes a live **WRITE**
3439
- from a live **READ** and from a **DRY RUN**. `remits-cli tool` also prints an explicit **TEST DATA WRITE**
3440
- banner for mutating tool calls in the test lane, including multi-action tools such as
3441
- `mcp_account_user_admin` where the write is signaled by `input.action` (`account_create`, `user_update`,
3442
- `edge_update`, etc.). If you see a WRITE banner you did not intend, stop.
3443
- - A tool call that fails **in the platform runtime** rather than in the tool (Groovy reflective dispatch
3444
- of a runtime-compiled component, an empty connection pool, a Redis reconnect, a lock-wait timeout)
3445
- now comes back as HTTP `503` with `failureClass: "transient_infrastructure"` and `retryable: true`,
3446
- and the CLI prints `TRANSIENT INFRASTRUCTURE FAILURE (retryable)`. Retry that **once**; prefer an
3447
- idempotent input if the tool has side effects. A genuine tool error stays HTTP `500` with
3448
- `failureClass: "tool_error"` — do **not** retry it, fix the input or the component.
3449
-
3450
- ## Troubleshooting
3451
-
3452
- | Symptom | Fix |
3453
- |---|---|
3454
- | 401 or `Not authenticated` | Run `remits-cli auth` |
3455
- | `account-info.json not found` | Run from the account repo root |
3456
- | Test not found (404) | New component not staged yet. Run `remits-cli components stage` first. For new tests (no ID), run by name not ID. |
3457
- | Stage shows 0 updated | No changes since last stage (hash dedup) |
3458
- | Test runs old code after edit | You forgot to stage, or you are looking at DB while a staged entry is still active. Run `remits-cli components status`; then `remits-cli components stage` to update staged code or `remits-cli components clear` to fall back to DB. |
3459
- | "Git credentials" or `git push` error | Git auth is required for commit (server pulls from git remote). Fix git authentication (SSH keys or HTTPS credentials) before retrying. Staging and testing still work without git. |
3460
- | Need to learn command behavior | Read this skill, repo docs, or CLI source. Do not run unsupported mutating subcommand variants such as `remits-cli components commit --help`; if a command unexpectedly mutates or commits, stop and inspect the repo-local session log before doing anything else. |
3461
- | Sync response reports unexpected deletes, creates, renames, uniqueness errors, or component ID drift | Stop immediately. Do not rerun sync, do not create replacement components to paper over the mismatch, and do not promote more changes. Preserve session logs/tool responses, compare `account-info.json`, local filenames, live inventory, and git history, then prepare a repair/escalation summary. |
3462
- | 500 / `Internal Server Error` from Remits | Stop normal task work. Capture the exact command, error, and response artifact, then escalate to a Remits system admin. Do not invent a workaround. |
3463
- | Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
3464
- | Tool response missing | Check `./.remits-cli/tool-responses/` |
3465
- | Long-running tool times out | For `mcp_run_action`/`mcp_run_agent`, use the tool's own `executionMode:"async"` and poll with a `controlAction:"status"` call (see "Tool Execution Lifecycle"). For other long tools without their own async, use `remits-cli tool --async true` and poll `remits-cli tool status --call-id <callId>`. `--timeout-ms` only adjusts the per-request HTTP timeout; it is not a substitute for async. |
3466
- | Need to continue tracking a long Action or Agent after terminal disconnect | Read `./.remits-cli/tool-responses/<callId>.json` for the returned `actionRunId`/`agentRunId`/`sessionId`/`threadGroupingId`. Re-poll the run with a `controlAction:"status"` call carrying that `actionRunId`/`agentRunId`, or inspect the agent session via `mcp_ai_session_search` with the `sessionId`. |
3467
- | Staged change has no effect in a live (non-CLI) run | Staged overrides resolve only under a CLI TestMode (`branchName`+`cliUserId`). Live webhooks and other non-CLI runtime paths still use the DB (trunk, or the account's subscribed variant). `commit` to make it durable. See "Component Resolution". |
3468
- | Need to know an account's shape (role, type, parents, namespace, branch, host/login routes) | Read `resolution` — from the repo's `account-info.json`, or `mcp_account_user_admin` `action:'account'` (cheap), or `mcp_account_view` (full inventory): `role`/`summary`, `type`, `resolvedDatabaseName`, `domainName`/`resolvedDomainName`, `authPath`/`targetPath`, `relationships`, `componentBranch`, plus top-level `componentBranches`. Never infer structure from the account's name. |
3469
- | Need the account tree below an account, or its users | In a local repo, read `account-hierarchy.json` for the generated tree. For live data, use `mcp_account_user_admin` (`action:'hierarchy'` with a `depth`, or `action:'users'`). `account-info.json` deliberately omits the tree. |
3470
- | Need to prove whether an account/user/record is test data | Check explicit flags: `resolution.testAccount` / hierarchy `testAccount`, `mcp_account_user_admin` `testAccount` / `testUser`, token inspect owner flags, and `mcp_record_listing` / `mcp_record_view` `testMode` for object/event/alert rows. Do not infer from names or branch labels. |
3471
- | Need account configuration values | In a local repo, read `account-configurations.json`. For live data, use `mcp_account_user_admin` (`action:'account'`) or `mcp_account_view`. `account-info.json` deliberately omits configurations. |
3472
- | An account has two parents and you don't know which one a run used | `resolution.relationships` lists every link with its own `branchName`/`databaseName`/`domainName`. A membership-only account with SEVERAL edges resolves **trunk and inherits nothing** until a path is named (`--as-account`, `--variant-branch`, or an edge host) — that is by design, not a bug. With exactly ONE membership edge it inherits normally, descendants included. |
3473
- | Documents missing / written to the wrong place | Compare `resolution.databaseName` (the account's own override) with `resolution.resolvedDatabaseName` (what is actually in effect), and check for a `databaseName` on one of the `relationships` edges. Data does not inherit; components do. |
3474
- | A custom hostname resolves to an unexpected account | Compare `resolution.domainName` with `resolvedDomainName` and the edge `domainName`s. An **edge** host wins over the account's own host and additionally supplies the path travelled (which is what makes that edge's branch variants apply). |
3475
- | Need users of an account, accounts of a user, or account-scoped user fields | Use `mcp_account_user_admin` (`action:'users'`, `action:'user'`, or `action:'user_update'`). Use `mcp_sql_query` on `user` / `user_account` only for raw join-table investigation. Remember user custom fields are stored **per bound account**, so the same person can differ per account. |
3476
- | One account behaves differently from its siblings on the same component | It probably subscribes to a **branch variant**. Check `remits-cli components branches` and `remits-cli components branch <name> --subscribers`, and reproduce with `remits-cli test run --as-account <ID>`. Do NOT "fix" this by adding per-account logic to the origin component. |
3477
- | Edits on a feature branch seem to run against trunk code | You are likely on the **trunk** branch, or passed `--variant-branch none`. Run `remits-cli components status` — it states which world the working tree resolves. |
3478
- | A component vanished for one account after a variant-branch sync | Its file is missing from that branch, so the sync created a **tombstone** that hides it from subscribers. Restore the file on the branch and re-sync. Trunk is unaffected. |
3479
- | A variant Test suite fails wholesale, asserting trunk where you expect a variant | You are almost certainly running it from a **variant checkout**: `variantBranch` outranks every subscription, so the suite's own fixture accounts resolve YOUR branch. Re-run from trunk or with `--variant-branch none` before treating it as a regression. |
3480
- | `components sync` on a branch says "No changes detected" but trunk has moved | Re-run it; a trunk sync now invalidates the branch's cached verdict. If it still skips, the branch genuinely matches trunk - check `components branch <name>` for what is actually stored. |
3481
- | A sync summary reports `skipped: true` with `NO COMPONENTS WERE SYNCED` | The branch head matches the cached sync SHA, so the branch was never re-read — this is NOT an empty plan. It matters when something else changed the overlays at that same SHA: an ordinary re-sync then answers "nothing to do" forever. `--dry-run` always re-reads the branch and shows the real plan; `--force-tombstones` re-reads and applies it. |
3482
- | A branch sync stores overlays for components you never edited on the branch | **Trunk moved.** Sparseness compares the branch against CURRENT trunk, so editing a component on trunk without merging trunk into the branch turns it into a branch overlay on the next branch sync. The branch did not change. Merge trunk in, push, re-sync — the overlays prune. Check `components promotion` for the phase. |
3483
- | After promoting a `new_*` component to trunk, the branch still shows it as `added` | You have not re-synced the branch since the trunk sync. Merge trunk into the branch and `components sync`: the file adopts the promoted id and, if unchanged, removes its own overlay. If the branch carries BOTH `new_Foo.*` and `<id>_Foo.*`, the id file wins and the `new_` one is reported `skipped: superseded` - delete it. |
3484
- | After promoting a standalone Prompt, the branch still shows `OVERRIDDEN prompt:<id>` with only `purpose` changed | The trunk Prompt row does not match repo metadata. Ensure the Prompt sidecar has `description` and `purpose: CUSTOM`, run a platform build that imports Prompt `purpose`, re-sync trunk, merge back, and re-sync the branch. |
3485
- | A branch preview reports a `removed` component nobody deleted | Check whether that component has a file on **trunk**. A DB row with no trunk file is missing from every branch, so it reads as a removal everywhere (and the trunk sync tries to hard-delete it each run). Repair trunk, not the branch. |
3486
- | `--as-account <id>` resolves trunk, or 404s | The account probably has several edges each carrying a branch, so the anchor is ambiguous and the platform refuses to guess. Name the branch with `--variant-branch <name>`, and confirm the edge with `components branch <name> --subscribers`. |
3487
- | A branch variant is reported DRIFTED | The origin component changed after the variant was cut, so the branch is based on a stale version. `remits-cli components branch <name> --diff <id> --component-type <kind>` to compare, then reconcile the branch. |
3488
- | New source shown by `mcp_component_view` but old behavior persists after sync/commit | The compile cache (`CLOSURE_CACHE`) is keyed by `version:<N>:<sourceHash12>`, so a source change on the same version now invalidates it automatically — a run right after sync/commit picks up the new source. If old behavior still persists, confirm the run actually hit the synced instance and that no staged override is still shadowing DB (`remits-cli components status`). |
3489
- | Staged Reader test run throws `No enum constant ObjectType.<family>` | The staged entry has the component family in `type` (should be `kind`). Clear + re-stage; if it persists, inspect the staged payload with `mcp_cache` and escalate as a CLI/platform staging bug. |
3490
- | Unsure whether a run used staged vs DB source | Query `mcp_system_logs` for `Using Cached BCD` — `Signature: cli:<hash>` = staged, `version:<N>:<sourceHash12>` = DB. CLI/MCP tool results also report `componentSource` and `componentSignature` when available. |
3491
- | Service already running | Run `remits-cli status` to get the dashboard URL, or `remits-cli stop` before restarting. |
3492
- | Control center URL unknown | Read `~/.remits-cli/service-state.json` or run `remits-cli status` |
3493
- | Dashboard missing repos | Rebuild `~/.remits-cli/account-repos.json` with `remits-cli start` or the control center rescan action |
3494
- | Agent dispatch to wrong directory | Check `~/.remits-cli/account-repos.json` has the correct directory and that you resolved the account type correctly. A `CLIENT` ticket may still belong to a parent `PLATFORM` or `PRODUCT` repo for code changes. |
3495
-
3496
- ### When Something Doesn't Work as Expected
3497
-
3498
- All `remits-cli` capabilities — staging, test execution, committing, tool calls — are known to work. The
3499
- most common cause is an operational mistake, above all forgetting to stage before running a test.
3500
-
3501
- If you have followed the loop (**edit → stage → run**) and something still misbehaves after 2-3 attempts,
3502
- **stop trying workarounds** and decide which kind of problem it is:
3503
-
3504
- - **A `remits-cli` / tooling operational issue** — staging, sync, dispatch, the listener, or the CLI itself
3505
- misbehaving → use the **Escalation Bundle** below and ask the user to escalate to a Remits system admin.
3506
- - **A Remits back-stage platform defect or limitation** — the component *runtime* behaves wrong:
3507
- Hibernate/session/optimistic-locking errors, detached-entity surprises, brittle lifecycle or tool
3508
- behavior, a DSL method diverging from its guide → follow the **Back-Stage Escalation Workflow** in
3509
- `features/front-stage-debugging-strategy.md` (`mcp_get_guide`), which owns it end to end: analyze the
3510
- platform seam locally, open a PR on a feature branch (never merge it yourself), raise a
3511
- `mcp_support_ticket`, and tell the user. **Do not normalize it into a front-stage workaround.**
3512
-
3513
- The local clone of the core platform repo that workflow needs is tracked in
3514
- `~/.remits-cli/account-repos.json` under the reserved **`platform`** entry (default `~/remits`, override
3515
- with `REMITS_PLATFORM_DIR`); `remits-cli` clones it on first authenticated run if it is missing.
3516
-
3517
- Apply the boy-scout rule to the guides themselves: **whenever you only solved the problem by reading the
3518
- core back stage because a front-stage guide was unclear or missing — even when there was no platform
3519
- defect at all — open a guide-only PR** updating the relevant guide in the platform repo's `docs/guides/`,
3520
- which is the source served to every account repo. Better guides over time are an explicit goal.
3521
-
3522
- #### Escalation Bundle (tooling/operational issue)
3523
-
3524
- 1. **Create a single escalation bundle under `~/.remits-cli/issues/`.**
3525
- Use a directory name like:
3526
- - `~/.remits-cli/issues/<timestamp>-account-<accountId>-<short-slug>/`
3527
-
3528
- Populate that directory with enough information that the user or a Remits system admin can continue without re-running your work. Include at minimum:
3529
- - `summary.md`
3530
- - What you were trying to do
3531
- - Why you were trying to do it
3532
- - The expected behavior
3533
- - The actual behavior
3534
- - The exact commands you ran, in order
3535
- - The key error messages or unexpected outputs
3536
- - Whether the failure blocks staging, testing, sync, ticket routing, listener dispatch, or production investigation
3537
- - `context.json`
3538
- - `cwd`
3539
- - target repo directory
3540
- - `accountId`
3541
- - account name
3542
- - account `type`
3543
- - branch name
3544
- - current data mode
3545
- - ticket ID if applicable
3546
- - component names / IDs involved
3547
- - Copies or references for the relevant supporting artifacts:
3548
- - `./.remits-cli/current-session.txt`
3549
- - the active repo session log from `./.remits-cli/sessions/`
3550
- - any `./.remits-cli/tool-responses/<callId>.json` files involved
3551
- - `~/.remits-cli/account-repos.json` if repo resolution may be relevant
3552
- - `~/.remits-cli/activity.log` if service / websocket / agent-routing behavior may be relevant
3553
-
3554
- 2. **Use the global Remits CLI state to make the bundle self-contained.**
3555
- - Read `./.remits-cli/current-session.txt` to identify the active repo session log.
3556
- - Record the exact repo directory and account context from `account-info.json`.
3557
- - If repo selection or account targeting may be part of the issue, include the relevant entry from `~/.remits-cli/account-repos.json`.
3558
- - If the problem involves support-ticket routing, agent registration, or websocket events, include the relevant lines from `~/.remits-cli/activity.log`.
3559
- - If a tool call stored its full response externally, include that file path and summarize the important fields in `summary.md`.
3560
-
3561
- 3. **Stop and document the issue clearly for the user.**
3562
- Tell the user where the escalation bundle lives and summarize:
3563
- - what was attempted
3564
- - what should have happened
3565
- - what actually happened
3566
- - why this appears to require a Remits system admin
3567
-
3568
- 4. **Ask the user to escalate to a Remits system admin.** The system admin has access to the Remits platform codebase and the `remits-cli` source code, and can diagnose and fix platform-level issues directly.
3569
-
3570
- 5. **Do not attempt creative workarounds** (renaming components, duplicating files, bypassing the CLI with raw API calls, etc.). If the platform has a real bug, workarounds mask the problem and make it harder to diagnose. It is better to have the issue fixed at the source than to build fragile workarounds around it.
148
+ - For investigations, follow the shortest path: identify the exact document/ticket/object first, then
149
+ drill in. Avoid exploratory "maybe this" queries across large collections.
150
+ - For ticket replies, read the current ticket state and the new reply, then continue from existing
151
+ context instead of re-loading broad account state from scratch.
152
+ - Verification should be decisive. Avoid repeated identical test/status/tool calls when no new
153
+ information is likely.
154
+
155
+ ## When something doesn't work
156
+
157
+ All `remits-cli` capabilities are known to work. The most common cause is an operational mistake, above
158
+ all forgetting to stage before running a test. If you have followed **edit → stage → run** and something
159
+ still misbehaves after two or three attempts, stop trying workarounds and open `troubleshooting.md`: it
160
+ carries the symptom table, separates a CLI/tooling issue from a back-stage platform defect, and owns the
161
+ escalation bundle for each.