@remits/remits-cli 0.1.113 → 0.1.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/index.js +933 -52
- package/package.json +3 -2
- package/skills/remits-cli/SKILL.md +163 -3549
- package/skills/remits-cli/references/account-targeting.md +174 -0
- package/skills/remits-cli/references/agent-sessions.md +222 -0
- package/skills/remits-cli/references/branch-variants.md +391 -0
- package/skills/remits-cli/references/cli-state.md +158 -0
- package/skills/remits-cli/references/command-reference.md +270 -0
- package/skills/remits-cli/references/component-integrity.md +180 -0
- package/skills/remits-cli/references/component-resolution.md +221 -0
- package/skills/remits-cli/references/development-loop.md +418 -0
- package/skills/remits-cli/references/investigation.md +254 -0
- package/skills/remits-cli/references/support-tickets.md +483 -0
- package/skills/remits-cli/references/tool-reference.md +962 -0
- package/skills/remits-cli/references/troubleshooting.md +135 -0
|
@@ -5,3566 +5,180 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
|
|
|
5
5
|
|
|
6
6
|
# remits-cli
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
##
|
|
17
|
+
## How to read this skill
|
|
135
18
|
|
|
136
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
774
|
-
|
|
|
775
|
-
|
|
776
|
-
|
|
|
777
|
-
|
|
778
|
-
`
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
- `
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
-
|
|
811
|
-
|
|
812
|
-
-
|
|
813
|
-
|
|
814
|
-
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
- **
|
|
819
|
-
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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
|
+
- **Start from a steady git baseline before the first edit.** Run `git fetch origin`; confirm
|
|
80
|
+
`git log origin/<branch>..<branch>` and `git log <branch>..origin/<branch>` are both empty; then run
|
|
81
|
+
`remits-cli components status`. On a variant branch also run `remits-cli components promotion --branch
|
|
82
|
+
<branch>`. A workspace isolates staging, not the commit your files are based on. (`development-loop.md`,
|
|
83
|
+
`branch-variants.md`)
|
|
84
|
+
- **Resolution order is staged → variant → trunk, and each layers over the one beneath.** A populated
|
|
85
|
+
staging cache makes a committed variant look broken through any tokenized entry point; clear it before
|
|
86
|
+
verifying variant resolution. (`component-resolution.md`)
|
|
87
|
+
- **Host and data mode are two independent decisions.** `--base-url` picks the Remits host,
|
|
88
|
+
`--data-mode` picks the data segment on it. Neither implies the other. (`command-reference.md`)
|
|
89
|
+
- **`test run` ignores the stored session data lane.** It defaults to `test` even when `whoami` shows
|
|
90
|
+
the session parked on prod; production Test runs require explicit prod provenance (`--data-mode prod`)
|
|
91
|
+
or Test source declaring `dataMode 'prod'` / `[dataMode:'prod']`. Check returned `dataModeSource`
|
|
92
|
+
when auditing a run. (`command-reference.md`)
|
|
93
|
+
- **A tool's `dataMode` input never widens the lane.** It may narrow `prod` → `test`, never escalate
|
|
94
|
+
`test` → `prod`. The response's `dataMode` is the truth, not your input. To reach prod data, pass
|
|
95
|
+
`--data-mode prod` on the command line. (`account-targeting.md`)
|
|
96
|
+
- **Never infer prod vs test from an account name, URL, or branch.** Read the explicit flags —
|
|
97
|
+
`testAccount`, `testUser`, `testMode`, and the echoed `dataMode`. `mcp_sql_query` is lane-blind and
|
|
98
|
+
returns both lanes mixed; filter it yourself. (`account-targeting.md`)
|
|
99
|
+
- **"Tool call succeeded" means DISPATCHED, not that the tool did what you asked.** A tool that runs and
|
|
100
|
+
refuses returns HTTP 200 with its own `success: false`. For any mutating call, read `result.success`
|
|
101
|
+
from `./.remits-cli/tool-responses/<callId>.json` before reporting the work as done.
|
|
102
|
+
(`tool-reference.md`)
|
|
103
|
+
- **Writing the code is not finishing the job.** A change is complete when it is verified — a Test
|
|
104
|
+
component (preferred, because it becomes regression protection) or a browser flow through
|
|
105
|
+
`remits-cli token`. "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
|
|
106
|
+
cannot verify, say what you would need and ask. (`development-loop.md`)
|
|
107
|
+
- **Read the component's `.meta.yml` before changing behavior.** Sidecar descriptions can be dated
|
|
108
|
+
decision records. Before changing a displayed value, helper, calculation, schema field, or prompt
|
|
109
|
+
contract, check the sidecar and either preserve its decision or explicitly supersede it.
|
|
110
|
+
(`development-loop.md`)
|
|
111
|
+
- **Read the account's `resolution` block before acting on it** — `role`, `type`,
|
|
112
|
+
`resolvedDatabaseName`, `relationships`. More than one relationship means the account can legitimately
|
|
113
|
+
resolve differently depending on the path a request travelled. Never infer an account's shape from its
|
|
114
|
+
name. (`account-targeting.md`)
|
|
115
|
+
- **Investigate in prod mode; verify in test mode.** "It looks right in prod data inspection" is not
|
|
116
|
+
proof that a code change works. And never copy live customer data into another account's test
|
|
117
|
+
collection to get realistic verification. (`development-loop.md`)
|
|
118
|
+
- **`remits-cli agent serve` is the whole answer to "work support tickets".** No flags, no setup, no need
|
|
119
|
+
to be in an account repo. `agent register` makes the session routable and starts nothing.
|
|
120
|
+
(`support-tickets.md`)
|
|
121
|
+
- **`remits-cli ticket` is the platform's own ticket surface** and works on every Remits platform.
|
|
122
|
+
`mcp_support_ticket` is one System Account's convention and refuses tickets it does not own.
|
|
123
|
+
(`support-tickets.md`)
|
|
124
|
+
- **Reading and investigating are parallel-safe; editing one account's repository is exclusive**, via a
|
|
125
|
+
lease. A refusal is not an error — carry on read-only and hand off with `ticket progress --next-step`.
|
|
126
|
+
Never wait or poll for a lease. (`support-tickets.md`)
|
|
127
|
+
- **A brief's `## Rules you will be held to` section is ENFORCED, not requested.** The platform refuses
|
|
128
|
+
the verb and the message says what to do instead — act on that sentence rather than retrying or
|
|
129
|
+
looking for another door. A `[require]` rule needs a person; hand off. (`support-tickets.md`)
|
|
130
|
+
- **`components commit` takes a short landing lease on `(account, branch)`.** Refused means somebody else
|
|
131
|
+
is landing right now — keep staging and iterating, which is lane-isolated, and retry in a minute.
|
|
132
|
+
Never loop on it. (`development-loop.md`)
|
|
133
|
+
- **If something looks wrong, stop — do not paper over it.** Unexpected deletes, creates, renames,
|
|
134
|
+
uniqueness errors, id drift, or a 500 from the platform: stop, preserve the session log and tool
|
|
135
|
+
responses, and escalate. Never create replacement components to make ids line up, and never discover
|
|
136
|
+
behavior by running unsupported mutating command variants. (`troubleshooting.md`)
|
|
137
|
+
|
|
138
|
+
## Orientation: the first four things to establish
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
remits-cli whoami # account, user, branch, data mode, host for the NEXT tool call
|
|
142
|
+
remits-cli components status # trunk or variant checkout, staging lane, what is staged
|
|
143
|
+
remits-cli tools # which tools this account actually has (tools are per-account)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
plus the repo's `account-info.json` → `resolution` block for the account's shape.
|
|
147
|
+
|
|
148
|
+
If any command returns 401, run `remits-cli auth` (with the same `--base-url` if you were targeting a
|
|
149
|
+
non-default host).
|
|
150
|
+
|
|
151
|
+
**Files that are decision inputs — read the one that governs the decision, do not rely on memory:**
|
|
152
|
+
`~/.remits-cli/account-repos.json` (every local repo, plus the reserved `platform` entry for the core
|
|
153
|
+
platform clone), `~/.remits-cli/sessions.json` (auth state and lanes), `~/.remits-cli/agents.json`
|
|
154
|
+
(agents registered here), `./.remits-cli/tool-responses/<callId>.json` (the full tool payload), and the
|
|
155
|
+
repo's `account-info.json`. `cli-state.md` maps every remaining question to its file.
|
|
156
|
+
|
|
157
|
+
## Efficiency rules
|
|
828
158
|
|
|
829
159
|
Keep support and development sessions lean:
|
|
830
160
|
|
|
831
|
-
- Prefer local repo files over remote tools whenever the target account repo exists locally.
|
|
832
|
-
|
|
833
|
-
|
|
161
|
+
- Prefer local repo files over remote component tools whenever the target account repo exists locally.
|
|
162
|
+
`mcp_component_view` / `mcp_component_grep` are fallback surfaces for agents without that checkout,
|
|
163
|
+
or for confirming what the live DB has stored after you already understand the files. For a ticket
|
|
164
|
+
with `implementationAccountId`, resolve that account's indexed repo first, pull the appropriate
|
|
165
|
+
branch when the checkout is clean, and inspect `account-info.json` + `components/` there.
|
|
166
|
+
- Do not read entire `.remits-cli/sessions/*.jsonl` or large tool response files unless you first narrow
|
|
167
|
+
to the relevant request, endpoint, tool, or ticket.
|
|
168
|
+
- Prefer targeted Firestore queries: use `documentId`, tight `filters`, narrow `fields`, and low `limit`
|
|
169
|
+
values instead of broad collection scans.
|
|
834
170
|
- 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
|
|
836
|
-
|
|
837
|
-
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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.
|
|
171
|
+
- For investigations, follow the shortest path: identify the exact document/ticket/object first, then
|
|
172
|
+
drill in. Avoid exploratory "maybe this" queries across large collections.
|
|
173
|
+
- For ticket replies, read the current ticket state and the new reply, then continue from existing
|
|
174
|
+
context instead of re-loading broad account state from scratch.
|
|
175
|
+
- Verification should be decisive. Avoid repeated identical test/status/tool calls when no new
|
|
176
|
+
information is likely.
|
|
177
|
+
|
|
178
|
+
## When something doesn't work
|
|
179
|
+
|
|
180
|
+
All `remits-cli` capabilities are known to work. The most common cause is an operational mistake, above
|
|
181
|
+
all forgetting to stage before running a test. If you have followed **edit → stage → run** and something
|
|
182
|
+
still misbehaves after two or three attempts, stop trying workarounds and open `troubleshooting.md`: it
|
|
183
|
+
carries the symptom table, separates a CLI/tooling issue from a back-stage platform defect, and owns the
|
|
184
|
+
escalation bundle for each.
|