@remits/remits-cli 0.1.85 → 0.1.87

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.
@@ -7,105 +7,212 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
7
7
 
8
8
  ## Table of Contents
9
9
 
10
- - Line 89: Account Targeting Model
11
- - Line 112: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
12
- - Line 118: How the platform reconciles the repo into the database (the mechanism you must understand)
13
- - Line 144: The surfaces and their intended behavior
14
- - Line 166: Intended workflows
15
- - Line 185: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
16
- - Line 195: If something looks wrong — stop, don't paper over
17
- - Line 219: Required Local Index Reads
18
- - Line 253: Big Picture: How remits-cli State Is Organized
19
- - Line 283: Support Ticket Mental Model
20
- - Line 319: Efficiency Rules
21
- - Line 331: Account Repository Index
22
- - Line 356: Two Workflows
23
- - Line 363: Test Mode vs Prod Mode
24
- - Line 395: Platform Mental Model: Front Stage vs Back Stage
25
- - Line 402: Documents vs Records
26
- - Line 412: HTTP Audits
27
- - Line 466: Record `content` / Context Mental Model
28
- - Line 486: Provenance Fields
29
- - Line 504: Why Persisted Context Looks Different From Runtime Context
30
- - Line 525: Investigation Rule for Record Context
31
- - Line 540: AI Request Response Records
32
- - Line 547: AI Session Groupings
33
- - Line 562: Correlation Keys
34
- - Line 568: Node Reference Table
35
- - Line 578: Repository vs External Context
36
- - Line 585: Getting Started
37
- - Line 587: Authentication
38
- - Line 600: Host vs Data Mode
39
- - Line 619: Tool Execution Lifecycle
40
- - Line 660: Data Mode
41
- - Line 670: Development Workflow
42
- - Line 672: The Golden Rule: Writing Code Is Not Finishing the Job
43
- - Line 684: The Development Fast Loop
44
- - Line 843: User Confirmation Preferences
45
- - Line 853: Component Resolution: Staging Cache vs DB (which "version" actually runs)
46
- - Line 859: The two source layers + the compile cache
47
- - Line 870: Staging cache key format
48
- - Line 883: How the platform picks staged vs DB (the compile signature)
49
- - Line 900: When staged overrides apply
50
- - Line 924: Diagnosing which version is in play
51
- - Line 945: Stage / commit / clear with the MCP tools
52
- - Line 969: Stale after sync / commit (the in-memory compile cache)
53
- - Line 979: Production Support Workflow
54
- - Line 987: Investigation Strategy
55
- - Line 1017: Presenting Findings
56
- - Line 1025: Verifying a Production Issue Fix
57
- - Line 1038: Tool Reference
58
- - Line 1040: Execute a Tool
59
- - Line 1067: `mcp_account_view`
60
- - Line 1074: `mcp_firestore_search`
61
- - Line 1112: `mcp_object_activity`
62
- - Line 1121: `mcp_record_listing`
63
- - Line 1146: `mcp_record_view`
64
- - Line 1159: `mcp_ai_session_search`
65
- - Line 1191: `mcp_run_action`
66
- - Line 1229: `mcp_run_agent`
67
- - Line 1267: `mcp_system_logs`
68
- - Line 1290: `mcp_component_view`
69
- - Line 1308: `mcp_component_grep`
70
- - Line 1321: `mcp_support_ticket`
71
- - Line 1370: `mcp_run_test`
72
- - Line 1379: `mcp_component_edit`
73
- - Line 1394: `mcp_component_create`
74
- - Line 1408: `mcp_component_commit`
75
- - Line 1416: `mcp_cache`
76
- - Line 1428: Multi-Session Support
77
- - Line 1448: Persistent Service, Control Center, and Agent Dispatch
78
- - Line 1457: Starting the Service
79
- - Line 1483: Control Center
80
- - Line 1498: Agent Dispatch
81
- - Line 1519: Configuring the Preferred Agent
82
- - Line 1530: Local State Files
83
- - Line 1578: Command Reference
84
- - Line 1605: Troubleshooting
85
- - Line 1632: When Something Doesn't Work as Expected
10
+ - Line 126: Account Targeting Model
11
+ - Line 133: Account types
12
+ - Line 148: How accounts connect (and why it changes what runs)
13
+ - Line 178: Users are a separate model — don't reason about them as a tree
14
+ - Line 191: Reading the shape
15
+ - Line 207: Repo selection rules
16
+ - Line 219: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
17
+ - Line 225: How the platform reconciles the repo into the database (the mechanism you must understand)
18
+ - Line 280: The surfaces and their intended behavior
19
+ - Line 304: Intended workflows
20
+ - Line 323: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
21
+ - Line 336: If something looks wrong — stop, don't paper over
22
+ - Line 360: Required Local Index Reads
23
+ - Line 394: Big Picture: How remits-cli State Is Organized
24
+ - Line 424: Support Ticket Mental Model
25
+ - Line 460: Efficiency Rules
26
+ - Line 472: Account Repository Index
27
+ - Line 497: Two Workflows
28
+ - Line 504: Test Mode vs Prod Mode
29
+ - Line 536: Platform Mental Model: Front Stage vs Back Stage
30
+ - Line 543: Documents vs Records
31
+ - Line 553: HTTP Audits
32
+ - Line 607: Record `content` / Context Mental Model
33
+ - Line 627: Provenance Fields
34
+ - Line 645: Why Persisted Context Looks Different From Runtime Context
35
+ - Line 666: Investigation Rule for Record Context
36
+ - Line 681: AI Request Response Records
37
+ - Line 688: AI Session Groupings
38
+ - Line 707: Correlation Keys
39
+ - Line 713: Node Reference Table
40
+ - Line 723: Repository vs External Context
41
+ - Line 730: Getting Started
42
+ - Line 732: Authentication
43
+ - Line 745: Host vs Data Mode
44
+ - Line 764: Tool Execution Lifecycle
45
+ - Line 818: Data Mode
46
+ - Line 828: Development Workflow
47
+ - Line 830: The Golden Rule: Writing Code Is Not Finishing the Job
48
+ - Line 842: Avoid Brittle Front-Stage Intelligence
49
+ - Line 855: The Development Fast Loop
50
+ - Line 859: Step 1: Understand the Request
51
+ - Line 874: Step 2: Make the Change
52
+ - Line 877: Step 3: Stage to Platform
53
+ - Line 892: Step 4: Verify the Change
54
+ - Line 955: Step 5: Iterate If Needed
55
+ - Line 965: Step 6: Update Documentation
56
+ - Line 975: Step 7: Commit and Durable Sync
57
+ - Line 1013: Step 8: Close the Ticket
58
+ - Line 1028: User Confirmation Preferences
59
+ - Line 1038: Component Resolution: Staging Cache vs DB (which "version" actually runs)
60
+ - Line 1044: The three source layers + the compile cache
61
+ - Line 1067: Staging cache key format
62
+ - Line 1082: How the platform picks staged vs DB (the compile signature)
63
+ - Line 1104: When staged overrides apply
64
+ - Line 1128: Diagnosing which version is in play
65
+ - Line 1149: Stage / commit / clear with the MCP tools
66
+ - Line 1173: Stale after sync / commit (the in-memory compile cache)
67
+ - Line 1183: Account Resolution: how a request travels the account graph
68
+ - Line 1276: Branched Component Variants (per-account component overrides)
69
+ - Line 1284: The model (three moving parts)
70
+ - Line 1351: Which world does your working tree resolve? (read this before you run anything)
71
+ - Line 1383: Two levers, two different questions
72
+ - Line 1397: The SDLC is identical on a variant branch
73
+ - Line 1421: Promotion: getting the branch back into trunk
74
+ - Line 1463: Verifying as the subscriber
75
+ - Line 1482: Agents (Utility) on a variant branch
76
+ - Line 1494: Danger profile on a variant branch (different, not absent)
77
+ - Line 1522: Inspecting branches and drift
78
+ - Line 1554: Diagnosing a variant
79
+ - Line 1561: Production Support Workflow
80
+ - Line 1569: Investigation Strategy
81
+ - Line 1599: Presenting Findings
82
+ - Line 1607: Verifying a Production Issue Fix
83
+ - Line 1620: Tool Reference
84
+ - Line 1622: Execute a Tool
85
+ - Line 1651: `mcp_account_view`
86
+ - Line 1680: `mcp_firestore_search`
87
+ - Line 1718: `mcp_object_activity`
88
+ - Line 1727: `mcp_record_listing`
89
+ - Line 1752: `mcp_record_view`
90
+ - Line 1765: `mcp_ai_session_search`
91
+ - Line 1801: `mcp_run_action`
92
+ - Line 1845: `mcp_run_agent`
93
+ - Line 1881: `mcp_system_logs`
94
+ - Line 1904: `mcp_component_view`
95
+ - Line 1927: `mcp_component_grep`
96
+ - Line 1940: `mcp_support_ticket`
97
+ - Line 1989: `mcp_run_test`
98
+ - Line 1998: `mcp_component_edit`
99
+ - Line 2013: `mcp_component_create`
100
+ - Line 2027: `mcp_component_commit`
101
+ - Line 2035: `mcp_component_branches`
102
+ - Line 2054: `mcp_cache`
103
+ - Line 2066: `mcp_sql_query`
104
+ - Line 2101: `mcp_index_search`
105
+ - Line 2121: `mcp_get_guide`
106
+ - Line 2133: `mcp_test_fixture`
107
+ - Line 2146: `mcp_embeddable_test_url`
108
+ - Line 2158: `mcp_playwright_replay`
109
+ - Line 2173: `mcp_jvm_spike_triage`
110
+ - Line 2186: `mcp_support_ticket_queue`
111
+ - Line 2203: Multi-Session Support
112
+ - Line 2223: Persistent Service, Control Center, and Agent Dispatch
113
+ - Line 2232: Starting the Service
114
+ - Line 2258: Control Center
115
+ - Line 2273: Agent Dispatch
116
+ - Line 2294: Configuring the Preferred Agent
117
+ - Line 2305: Local State Files
118
+ - Line 2353: Command Reference
119
+ - Line 2395: Troubleshooting
120
+ - Line 2436: When Something Doesn't Work as Expected
121
+ - Line 2447: Back-Stage Escalation Workflow (platform defect/limitation)
122
+ - Line 2456: Escalation Bundle (tooling/operational issue)
86
123
 
87
124
  `remits-cli` is the brainstem for both **development** and **production support**. You might run it inside an account repository (where `account-info.json` and `/components` already exist) or from outside any repo when a user needs help on another account. Your users are business professionals — they think in terms of what they can see in a browser.
88
125
 
89
126
  ## Account Targeting Model
90
127
 
91
- Before deciding which repo or account context to use, read the target account's `type` from `account-info.json` or `Account.getInformation` / `mcp_account_view`.
128
+ **Establish the account's shape before you touch anything.** Which repo you work in, which account you run
129
+ against, where a fix belongs, and why one account behaves unlike its siblings are all answered by the account
130
+ structure — never by the account's name. Read it from `account-info.json` (in the repo) or `mcp_account_view`
131
+ (remotely). Both return the same thing.
92
132
 
93
- - **`PLATFORM`** = a use-case/platform implementation account. This is often where the shared Remits components live.
94
- - **`PRODUCT`** = a product/use-case account under a platform. This can also hold shared implementation components.
95
- - **`CLIENT`** = an actual customer account. This is usually where production data lives and where an issue is observed, but not necessarily where the shared implementation is authored.
133
+ ### Account types
96
134
 
97
- Do not guess from the account name alone.
135
+ | Type | What it is | Components | Business data |
136
+ |---|---|---|---|
137
+ | `PLATFORM` | Top of a use-case tree. Owns the shared implementation and the git repository. | **Owned here** | Rarely — mostly configuration |
138
+ | `PRODUCT` | A product/use case under a platform. May own shared components too. | **Owned here or above** | Rarely |
139
+ | `CLIENT` | An actual customer. Where an issue is observed. | Usually **inherited**, not owned | **Here** — the customer's documents and lifecycle records |
140
+ | `PROVIDER` | A partner/vendor participant in a use case | Rarely | Sometimes |
98
141
 
99
- - If the work is a **feature, enhancement, component change, or shared behavior fix**, the implementation target is usually the relevant `PLATFORM` or `PRODUCT` account, not the `CLIENT` account that reported the issue.
100
- - If the work is a **production investigation or client-specific data issue**, start with the affected `CLIENT` account's data and runtime history.
101
- - If the request spans both, investigate in the `CLIENT` account first to confirm symptoms, then move to the owning `PLATFORM` or `PRODUCT` repo before making code changes.
142
+ - **Feature, enhancement, component change, shared behavior fix** → the implementation target is usually the
143
+ owning `PLATFORM` or `PRODUCT` account, **not** the `CLIENT` that reported it.
144
+ - **Production investigation or client-specific data issue** → start with the affected `CLIENT` account's
145
+ data and runtime history.
146
+ - **Both** → confirm symptoms on the `CLIENT`, then move to the owning `PLATFORM`/`PRODUCT` repo to change code.
102
147
 
103
- Apply these rules before making remote tool calls:
148
+ ### How accounts connect (and why it changes what runs)
104
149
 
105
- - **Inside the target implementation repo**: read `account-info.json` (the same data that `mcp_account_view` returns) and inspect `/components` directly.
106
- - **Inside one repo but supporting a different account**: switch to the correct repo for that account type if available; otherwise use `mcp_account_view` / `mcp_component_view` / `mcp_component_grep`.
150
+ Components are **inherited downward** and de-duplicated **by component name, nearest owner wins** — so a
151
+ client can run an Action owned three levels up, and can override it just by owning one with the same name.
152
+ Business data does **not** inherit; it belongs to the account that wrote it, in that account's storage
153
+ namespace.
154
+
155
+ An account is linked upward in two ways, and they coexist:
156
+
157
+ - **The primary parent** — one per account. The overwhelming majority of accounts have only this.
158
+ - **Membership edges** — extra links letting the *same account* be reached through **more than one** parent
159
+ (a customer in two product lines; a forked deployment reaching the same owner).
160
+
161
+ Any link can independently carry three things. They are **orthogonal** — never infer one from another:
162
+
163
+ | Edge property | Means |
164
+ |---|---|
165
+ | `branchName` | **Which code runs** — the component-variant branch this account subscribes to (see "Branched Component Variants") |
166
+ | `databaseName` | **Where data lives** — a storage-namespace override scoped to this link |
167
+ | `domainName` | **Which host reaches it** — a custom hostname that resolves this account *and* this link's parent as the path travelled |
168
+
169
+ Two consequences that generate most "this account is weird" tickets:
170
+
171
+ 1. **The same account can legitimately resolve differently depending on how the request reached it** — a
172
+ different component version, a different namespace, a different host. Establish which path a failing
173
+ request travelled before comparing behavior. (Full detail in "Account Resolution: how a request travels
174
+ the account graph".)
175
+ 2. **An account with no primary parent and several membership edges inherits nothing and runs trunk** until
176
+ a path is named — the platform refuses to guess between equally valid parents. That is by design.
177
+
178
+ ### Users are a separate model — don't reason about them as a tree
179
+
180
+ - A user is **global, keyed by email**. There is no per-account copy of a person.
181
+ - A user **belongs to many accounts** (a membership list, not a parent pointer).
182
+ - A user's **custom fields are per-account**: the same person can be `role: 'Admin'` on one account and
183
+ `role: 'Viewer'` on another. A field written while bound to the wrong account lands on the wrong account.
184
+ - **Access reach is a union over every link** — a user credentialed on a parent can act on an account below
185
+ it through any link, which is deliberately broader than the single-path component inheritance.
186
+
187
+ There is no dedicated user MCP tool. For access questions ("who can see this client account?", "why does
188
+ this user see the wrong data?") use `mcp_sql_query` against `user` / `user_account`, or `account.users([scope:
189
+ 'children'])` from inside a component. See `features/account-management.md` (`mcp_get_guide`).
190
+
191
+ ### Reading the shape
192
+
193
+ `account-info.json` / `mcp_account_view` carry a `resolution` block. Read it in this order:
194
+
195
+ - **`type`** — decide repo/ownership per the table above.
196
+ - **`resolvedDatabaseName`** vs **`databaseName`** — the namespace actually in effect vs the account's own
197
+ override. Check this first when documents are "missing".
198
+ - **`relationships`** — every link upward, primary first, each with its own `branchName` / `databaseName` /
199
+ `domainName`. **This is where you see that an account has two parents, and which link carries what.**
200
+ - **`componentBranch`** (+ `componentBranchOwnerAccountId`) — the component-variant branch this account
201
+ subscribes to, if any. Present only when subscribed.
202
+ - **`branchName`** — the account **repo's** trunk sync branch. Not a component-variant branch. Do not confuse
203
+ these two.
204
+ - **`componentBranches`** (top level) — variant branches this account **owns**, with drift and subscriber
205
+ counts. Check it before editing a shared component.
206
+
207
+ ### Repo selection rules
208
+
209
+ - **Inside the target implementation repo**: read `account-info.json` and inspect `/components` directly.
210
+ - **Inside one repo but supporting a different account**: switch to the correct repo for that account if
211
+ available; otherwise use `mcp_account_view` / `mcp_component_view` / `mcp_component_grep`.
107
212
  - **Outside any repo**: rely on the tools for account structure and component source.
108
- - **Never create a new local repo/directory just because a ticket references an account name.** First resolve the account type and parent hierarchy. Only work from an existing indexed repo unless the user explicitly asks you to create or clone one.
213
+ - **Never create a new local repo/directory just because a ticket references an account name.** First resolve
214
+ the account type and parent hierarchy. Only work from an existing indexed repo unless the user explicitly
215
+ asks you to create or clone one.
109
216
 
110
217
  Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens, committing work, and diagnosing production issues.
111
218
 
@@ -117,13 +224,25 @@ different implementations. These rules override the normal fast loop whenever th
117
224
 
118
225
  ### How the platform reconciles the repo into the database (the mechanism you must understand)
119
226
 
227
+ > **First, check which branch you are on.** Everything in this section describes a **TRUNK** sync. Syncing
228
+ > from a **non-trunk branch** is a different, much safer operation — it writes `ComponentVariant` overlays
229
+ > only and can never create, delete, rename, or overwrite a live component row. Run
230
+ > `remits-cli components status` to see which mode your working tree is in, and read
231
+ > "Branched Component Variants" for the variant-branch rules (which have their own hazard: tombstones).
232
+
120
233
  `remits-cli components sync` (and the sync phase of `components commit`) calls
121
- `GitHubClient.syncFromRepository`. It is a **full two-way reconcile in which the GitHub remote is authoritative
122
- over the database.** It reads the **remote repo ZIP — not your local working tree** — and:
234
+ `GitHubClient.syncFromRepository`. On the account's **trunk branch** it is a **full two-way reconcile in
235
+ which the GitHub remote is authoritative over the database.** It reads the **remote repo ZIP — not your
236
+ local working tree** — and:
123
237
 
124
238
  - **Match / update:** each component is matched to a DB row by the **numeric id prefix of its files**
125
239
  (`58_x.groovy` → component 58), not by name. Matching files overwrite that component's DB fields
126
240
  (source/schema/html/etc.).
241
+ - **Rename:** changing the component's `name:` in `.meta.yml` is supported as a normal update **as long as
242
+ the numeric id prefix stays the same**. On staging, the CLI updates the id/name cache aliases and prunes
243
+ stale old-name aliases for that id. On trunk sync, the platform canonicalizes the repo filenames to the
244
+ current component name (`58_OldName.groovy` with `name: New Name` becomes `58_NewName.groovy`, plus
245
+ sidecars) and reports those moves in `syncResults.renamed`.
127
246
  - **Create:** a file whose id prefix is **not** a live component on the account — including any `new_*` file —
128
247
  is created as a **brand-new DB row with a fresh server-assigned id**, and the platform renames the repo files
129
248
  to that id (the `Rename X→Y after component creation` / `Delete old file` commits).
@@ -132,14 +251,38 @@ over the database.** It reads the **remote repo ZIP — not your local working t
132
251
  "looks healthy" (contains `account-info.json` or `README.md`). Exempt from deletion: `auxiliary` components,
133
252
  README-purpose prompts, and AGENT-purpose prompts.
134
253
 
254
+ > ### `auxiliary: true` opts a component OUT of the repo entirely — in BOTH directions
255
+ >
256
+ > This is a silent trap, so know it before you author a sidecar. `auxiliary: true` does not merely
257
+ > "de-emphasize" a component:
258
+ >
259
+ > - **Repo → platform:** the sync **skips the file outright**. A `new_*` component whose `.meta.yml` says
260
+ > `auxiliary: true` is never created, so it **never gets a real id** and the file is never renamed. It
261
+ > looks like the sync silently ignored your work — because it did.
262
+ > - **Platform → repo:** the component's save hooks skip pushing source to GitHub, and repo initialization
263
+ > omits it.
264
+ > - It is also excluded from `getInformation()` (so AI agents do not discover it) and exempt from the
265
+ > deletion pass above.
266
+ >
267
+ > **Use `auxiliary: true` only for genuinely throwaway components** — ad-hoc reports, experiments, and the
268
+ > ephemeral fixtures a Test creates and deletes at runtime (those are created in Groovy with
269
+ > `auxiliary: true` and must never touch the repo).
270
+ >
271
+ > **Use `auxiliary: false` for anything durable** — above all a Test suite that is a regression guard. If you
272
+ > want it versioned in git, addressable by a stable id, or discoverable by another agent, it is not
273
+ > auxiliary. Symptom to recognize: *"I added `new_Foo.groovy`, synced, and it neither appeared on the account
274
+ > nor got renamed."* Check the sidecar's `auxiliary` flag first.
275
+
135
276
  **The single most important consequence:** the id in a component's **filename is load-bearing**. If a file's id
136
- no longer matches its DB row (a rename/renumber), the next sync will **create a duplicate at the new id and
137
- hard-delete the original at the old id**. If a component's files are missing from the repo at sync time, that
138
- component is **hard-deleted from the DB**. This is exactly how a prior session deleted live schemas and an
139
- embeddable.
277
+ no longer matches its DB row (a renumber or move across ids), the next sync will **create a duplicate at the
278
+ new id and hard-delete the original at the old id**. If a component's files are missing from the repo at sync
279
+ time, that component is **hard-deleted from the DB**. This is exactly how a prior session deleted live schemas
280
+ and an embeddable.
140
281
 
141
- **Therefore: never renumber, rename-across-ids, or remove component files as a side effect.** Before any sync,
142
- the repo must already mirror the live DB: every live component present at its real id, and nothing extra.
282
+ **Therefore: never renumber, rename-across-ids, or remove component files as a side effect.** Name-only
283
+ renames are fine when every file keeps the same numeric id; either rename the local filename stem yourself or
284
+ let trunk sync canonicalize it from `.meta.yml`. Before any sync, the repo must already mirror the live DB:
285
+ every live component present at its real id, and nothing extra.
143
286
 
144
287
  ### The surfaces and their intended behavior
145
288
 
@@ -148,8 +291,9 @@ the repo must already mirror the live DB: every live component present at its re
148
291
  | `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
149
292
  | `mcp_component_edit` `mode:'stage'` | Redis staging cache for one component/field. | none |
150
293
  | `mcp_component_edit` `mode:'commit'` / `mcp_component_commit` `commit` | Writes the field(s) to the **live DB** for that exact existing component and clears its staging. Does not create/delete other components. | low (scoped to one known component) |
151
- | `remits-cli components sync` | **Server-side git→DB reconcile of the whole account** (create/update/**delete**/rename). Reads the pushed remote; ignores local files. | **high** |
152
- | `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. | **highest** |
294
+ | `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** |
295
+ | `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) |
296
+ | `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** |
153
297
  | `mcp_component_create` | Writes a **new** component row **directly to the DB** (immediate, new id). | medium (can create duplicates) |
154
298
 
155
299
  Key implications:
@@ -160,14 +304,17 @@ Key implications:
160
304
  `git commit → git push → components sync → git pull` sequence so each phase can be inspected.
161
305
  - **`components sync` acts on the pushed remote**, so local edits are invisible to it until committed **and
162
306
  pushed**, and a drifted **remote** is dangerous even when your local tree looks fine.
163
- - After a successful `components sync` / `components commit`, the server clears the full branch/user staging
164
- scope. This is the expected clean state: old Redis aliases should not keep shadowing the newly synced DB rows.
307
+ - After a successful non-dry-run `components sync` / `components commit`, the server clears the full
308
+ branch/user staging scope. This is the expected clean state: old Redis aliases should not keep shadowing
309
+ the newly synced DB rows. `components sync --dry-run` intentionally leaves staging untouched.
165
310
 
166
311
  ### Intended workflows
167
312
 
168
313
  **Change existing components (normal path):**
169
314
  1. Edit files under `components/` **keeping each component's existing numeric id** (use `new_*` only for
170
- genuinely new components).
315
+ genuinely new components). To rename a component, update `name:` in its `.meta.yml`; keep the id prefix
316
+ fixed. Renaming the file stem is optional before trunk sync because the platform will canonicalize it, but
317
+ doing it locally keeps the working tree easier to read.
171
318
  2. `remits-cli components stage` → verify in test mode. Iterate (edit → stage → run).
172
319
  3. When ready to promote: pass the pre-sync safety check below, then
173
320
  `git add -A && git commit && git push`, `remits-cli components sync`, `git pull --ff-only`.
@@ -184,6 +331,9 @@ file is catastrophic.
184
331
 
185
332
  ### Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
186
333
 
334
+ - **You know which sync mode this branch selects.** `remits-cli components status` states it outright. On a
335
+ variant branch the id/delete/renumber checks below apply to the **overlay set** instead: confirm every
336
+ component absent from the branch is *meant* to be tombstoned for subscribers.
187
337
  - The user intends durable platform promotion now — not just local edits, staging, or verification.
188
338
  - `git status --short` shows only intended changes; every rename/delete is explained. **No component file has
189
339
  been renumbered to a different id.**
@@ -718,9 +868,56 @@ This is how every development task should flow:
718
868
  #### Step 1: Understand the Request
719
869
  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.
720
870
 
871
+ **Establish the account's shape too, not just its components.** Read the `resolution` block in
872
+ `account-info.json` (or `mcp_account_view`): the account `type` decides whether this repo is even the right
873
+ place to change code, `resolution.relationships` shows whether the account has more than one parent (and
874
+ which link carries a branch/namespace/host), and `resolvedDatabaseName` tells you where its data actually
875
+ lands. See "Account Targeting Model" and `features/account-management.md` (`mcp_get_guide`).
876
+
877
+ **Also establish which world you are working in.** `remits-cli components status` reports whether the
878
+ working tree is a **trunk** checkout or a **variant branch** checkout — which decides both what your test
879
+ runs resolve and what a sync writes. If `account-info.json` carries a `componentBranches` section, branch
880
+ variants of these components exist: editing an origin component will drift them, so check
881
+ `remits-cli components branches` before changing shared code. See "Branched Component Variants".
882
+
721
883
  #### Step 2: Make the Change
722
884
  Edit component files under `components/`. This is local file editing — the platform doesn't know about your changes yet.
723
885
 
886
+ **Creating a component that does not exist yet.** Files are named `<id>_<Name>.<ext>`, where the numeric
887
+ prefix is the platform's component id. A new component has no id, so name its files with the **`new_`
888
+ prefix** and let the sync assign one (it then renames the files to that id):
889
+
890
+ ```
891
+ components/embeddables/new_MerchantPortal.groovy # source
892
+ components/embeddables/new_MerchantPortal.html # markup
893
+ components/embeddables/new_MerchantPortal.js # client script
894
+ components/embeddables/new_MerchantPortal.meta.yml # metadata sidecar
895
+ ```
896
+
897
+ **Do NOT put an `id:` in a new component's sidecar.** `id` is what links a sidecar to an *existing*
898
+ component and is parsed as a number, so a placeholder (`id: new`, `id: TBD`) fails the **entire**
899
+ stage/sync request with a `NumberFormatException` — not just that one file, and the error does not name
900
+ the file. Omit the key; the platform fills it in on sync:
901
+
902
+ ```yaml
903
+ # components/embeddables/new_MerchantPortal.meta.yml — no `id:` yet
904
+ name: Merchant Portal
905
+ summary: One-line statement of what this component is for.
906
+ description: |
907
+ Longer technical description with line-number references to the key logic.
908
+ path: /page/merchant-portal # Readers and Embeddables only
909
+ category: default
910
+ auxiliary: false # `true` means the sync SKIPS the file entirely — see Auxiliary
911
+ mermaid: |
912
+ graph TD
913
+ A[Request] --> B[Load documents]
914
+ ```
915
+
916
+ > **Staging creates nothing in the database, so a `new_` component has no id yet — address it BY NAME.**
917
+ > `remits-cli test run --test "My Suite"`, not `--test <id>`. Component-to-component resolution and
918
+ > request-level addressing are name-based too; the component guides cover those. After a trunk sync the
919
+ > component has a real id and either form works.
920
+
724
921
  #### Step 3: Stage to Platform
725
922
 
726
923
  ```bash
@@ -753,7 +950,9 @@ Important test-runner constraints:
753
950
 
754
951
  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.
755
952
 
756
- New test files use the `new_` prefix (e.g., `new_MyTest.groovy`). New tests have no database ID, so you must run them **by name**: `remits-cli test run --test "My Test"`. After committing, the platform assigns an ID and renames the file (e.g., `4_MyTest.groovy`) — then you can run by either ID or name.
953
+ 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.
954
+
955
+ **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.
757
956
 
758
957
  **Option B — Visual verification with Playwright** (for UI changes or when the user wants to "see it"):
759
958
 
@@ -817,7 +1016,9 @@ Before committing, update metadata so the next session understands what changed:
817
1016
  2. **`README.md`** — If the change affects account-level capabilities or workflows.
818
1017
  3. **New components** — Always fill in `.meta.yml` immediately.
819
1018
 
820
- `account-info.json` is read-only — never edit it. It regenerates automatically after sync.
1019
+ `account-info.json` is read-only — never edit it. It regenerates automatically after sync. On a trunk sync it
1020
+ describes the owning repo account. On a subscriber-initiated variant sync it describes the subscribing
1021
+ account reached through the branch edge, even though the component files still belong to the owner's repo.
821
1022
 
822
1023
  #### Step 7: Commit and Durable Sync
823
1024
 
@@ -888,16 +1089,28 @@ When the platform executes a component it resolves the source from one of two pl
888
1089
  behind an in-memory cache. Understanding this is the difference between "my change isn't working" guesses
889
1090
  and a precise diagnosis.
890
1091
 
891
- ### The two source layers + the compile cache
1092
+ ### The three source layers + the compile cache
892
1093
 
893
1094
  1. **CLI staging cache (Redis, 240-min TTL).** Branch + user + account scoped overrides written by
894
- `remits-cli components stage` and by the `mcp_component_edit` tool (`mode:'stage'`). These shadow the DB
895
- source **only during CLI/test-mode execution** (see "When staged overrides apply" below).
896
- 2. **Database (the committed live component).** What `mcp_component_view` reads, what a pure prod run uses,
897
- and what `commit` writes to.
898
- 3. **Compiled-closure cache (`BaseClosureDomain.CLOSURE_CACHE`).** An in-memory, **per-JVM-instance** Guava
899
- cache of the parsed closure, keyed by `(componentId, type, compileSignature)`. This is why a change that
900
- is correctly in the DB can still execute stale on a running instance — see "Stale after sync" below.
1095
+ `remits-cli components stage` and by the `mcp_component_edit` tool (`mode:'stage'`). These shadow the
1096
+ layers below **only during CLI/test-mode execution** (see "When staged overrides apply" below).
1097
+ 2. **Committed branch variants (`ComponentVariant`, MySQL).** Durable, branch-scoped overlays of a
1098
+ component. Unlike staging these are **not** user-scoped, do **not** expire, and **do** apply to normal
1099
+ production traffic — for the accounts that subscribe to that branch. See
1100
+ "Branched Component Variants" below. Most accounts have none, in which case this layer is inert.
1101
+ 3. **Database trunk row (the committed live component).** What `mcp_component_view` reads, what an
1102
+ unsubscribed prod run uses, and what a trunk `commit` writes to.
1103
+
1104
+ Resolution order is **staged → variant → trunk**, and each layer *layers over* the one beneath it rather
1105
+ than replacing it: a payload that only carries `source` inherits `path`, `objectType`, `inputSchema` etc.
1106
+ from the layer below. A staged edit made on a variant branch therefore layers over **that variant**, not
1107
+ over trunk.
1108
+
1109
+ Plus the compile cache:
1110
+
1111
+ - **Compiled-closure cache (`BaseClosureDomain.CLOSURE_CACHE`).** An in-memory, **per-JVM-instance** Guava
1112
+ cache of the parsed closure, keyed by `(componentId, type, compileSignature)`. This is why a change that
1113
+ is correctly in the DB can still execute stale on a running instance — see "Stale after sync" below.
901
1114
 
902
1115
  ### Staging cache key format
903
1116
 
@@ -909,17 +1122,23 @@ account:<accountId>:cli:<cliUserId>:components:<branch>:<family>:name:<normalize
909
1122
  `<family>` is the lowercased component family (`reader`, `action`, `test`, `embeddable`, ...). Both an
910
1123
  `id:` and a `name:` key are written per stage. The entry value carries: `kind` (the family), `type` (the
911
1124
  component's OWN type enum such as `ObjectType`/`RuleType`, or absent — **never** the family), `hash`,
912
- `updatedAt`, and the staged content field(s) (`source`/`prompt`/`html`/`javascript`/`schema`/
913
- `inputSchema`/`previewData`/`path`).
1125
+ `updatedAt`, the staged content field(s) (`source`/`prompt`/`html`/`javascript`/`schema`/
1126
+ `inputSchema`/`previewData`), and `.meta.yml` metadata fields such as `description`, `summary`, `mermaid`,
1127
+ `path`, `category`, and Schema flags (`enableTrigger`, `enableFullText`, `enableRAG`, `enableRevisions`,
1128
+ `enableBigQuerySync`, `enableRules`, `anchor`, `auxiliary`).
914
1129
 
915
1130
  ### How the platform picks staged vs DB (the compile signature)
916
1131
 
917
1132
  At compile time the platform computes a **signature** that tells you which layer won:
918
1133
 
919
1134
  - Staged override present → `compileSignature = "cli:<hash>"` (the staged content hash).
920
- - No staged override → `compileSignature = "version:<N>:<sourceHash12>"` (the DB row version plus a source hash
1135
+ - Committed branch variant → `compileSignature = "variant:<variantId>:<hash12>"`.
1136
+ - Neither → `compileSignature = "version:<N>:<sourceHash12>"` (the DB row version plus a source hash
921
1137
  prefix, so source changes cannot reuse a stale compile entry on the same instance).
922
1138
 
1139
+ The three namespaces are distinct on purpose: a component's staged, variant, and trunk closures coexist in
1140
+ the compile cache without colliding.
1141
+
923
1142
  That signature is logged. Querying for it is the single most reliable way to know what ran:
924
1143
 
925
1144
  ```bash
@@ -927,7 +1146,8 @@ remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","tim
927
1146
  ```
928
1147
 
929
1148
  `Using Cached BCD [ID: 230, Type: Action, Signature: cli:08cc...]` → ran a **staged** override.
930
- `...Signature: version:37:abc123def456]` → ran the **committed DB** version.
1149
+ `...Signature: variant:14:9f2c1a...]` → ran a **committed branch variant**.
1150
+ `...Signature: version:37:abc123def456]` → ran the **committed trunk** version.
931
1151
 
932
1152
  ### When staged overrides apply
933
1153
 
@@ -1008,6 +1228,397 @@ standard Grails no-hot-reload caveat — it is environmental, not a code defect.
1008
1228
  with `mcp_cache` / `mcp_component_view`, and if a platform/tool source change must take effect immediately,
1009
1229
  the platform owner recycles the instance.
1010
1230
 
1231
+ ## Account Resolution: how a request travels the account graph
1232
+
1233
+ Component inheritance, branch variants, and where data physically lives are all decided by **how the
1234
+ current request reached the executing account**. Read this before debugging "my subscriber isn't picking
1235
+ up the branch" or "why is this account reading the wrong collection" — those are almost always
1236
+ resolution questions, not component bugs.
1237
+
1238
+ **Two kinds of structural edge.**
1239
+
1240
+ - **`Account.parentId`** — the legacy primary parent, and still the primary structural edge. An ordinary
1241
+ single-parent account with no branch subscription resolves exactly as it did before relationships
1242
+ existed. Most production accounts are this shape.
1243
+ - **`AccountRelationship` edges** — a join table letting one account be reached through **more than one**
1244
+ parent. Exactly one edge per account is `primary` (kept in lockstep with `parentId`); the rest are
1245
+ **membership** edges. Each edge can independently carry:
1246
+ - `branchName` — the component-variant branch this account subscribes to (see the next section);
1247
+ - `databaseName` — a branch-scoped data-segment override;
1248
+ - `domainName` — a branch-scoped custom host that reaches this account **through this edge**.
1249
+ These are **orthogonal**: subscribing to a component branch never moves an account's data, setting a
1250
+ segment override never changes which code runs, and a custom host changes neither. Do not reason about
1251
+ one from the others.
1252
+
1253
+ **Custom hosts resolve the anchor, not just the account.** An account can have its own
1254
+ `Account.domainName`, and an edge can carry one too. A request arriving on an **edge** host resolves the
1255
+ edge's *child* as the execution account **and** the edge's *parent* as the branch anchor — which is what
1256
+ makes that edge's branch variants apply. An account-level host resolves the account with **no** anchor
1257
+ (today's behavior). Edge wins, then the account's own host, so removing an edge degrades cleanly instead
1258
+ of taking the hostname offline. Hosts are stored as bare lowercase hostnames; a full URL is normalized on
1259
+ the way in. This is what lets a forked/branch deployment get its own domain without a separate account
1260
+ tree — `forked.example.com` and `app.example.com` can serve the same owner's components, one overlaid
1261
+ with a branch.
1262
+
1263
+ **Two traversals, deliberately different.** Confusing them is the usual source of wrong conclusions:
1264
+
1265
+ | | Used for | Shape |
1266
+ |---|---|---|
1267
+ | **Anchored path** | component inheritance, branch selection, data-segment roll-up | **linear** `[self … anchor … root]` — so component name-dedupe never sees two sibling products at once |
1268
+ | **Union reachability** | authorization, "may this caller act on that account" (`--as-account`, tokens) | **permissive** union over `parentId` *and* every active edge |
1269
+
1270
+ **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id` (stamped
1271
+ onto the `Object`/`Event`/`Alert`/`ObjectLog` records a run creates, so async workers re-resolve on the
1272
+ same branch). A **null** anchor means "walk the primary `parentId` chain" — today's legacy behavior.
1273
+
1274
+ **Default-anchor derivation is deliberately conservative, and this is the #1 gotcha:**
1275
+
1276
+ | Account shape | Derived anchor |
1277
+ |---|---|
1278
+ | has a `parentId` | `null` → primary chain (legacy, unchanged) |
1279
+ | no `parentId`, **exactly one** membership edge | that edge's parent |
1280
+ | no `parentId`, **multiple** membership edges | `null` — **ambiguous, refuses to guess** |
1281
+
1282
+ So a `parentId`-less account with two membership edges resolves **trunk and inherits nothing** until an
1283
+ anchor is supplied. That is correct-by-design, not a bug. Entry points that already know the branch
1284
+ disambiguate it by branch name; from the CLI you supply it explicitly.
1285
+
1286
+ **Same account, two parents, two answers.** An account reached through Product A versus Product B can
1287
+ legitimately resolve a different component variant *and* a different data segment. When you investigate
1288
+ such an account, always establish which anchor the failing request used before comparing behavior.
1289
+
1290
+ **How to actually see an account's edges.** `mcp_account_view` (and the repo's `account-info.json`) returns
1291
+ `resolution.relationships` — one entry per structural link, primary first, each carrying its own
1292
+ `branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so this
1293
+ block is the only place that answers "how many parents does this account really have, and which link carries
1294
+ what?" Start here for any resolution question:
1295
+
1296
+ ```bash
1297
+ remits-cli tool --name mcp_account_view --input '{"accountId": 101}' --data-mode prod
1298
+ # then read: resolution.relationships, resolution.resolvedDatabaseName, resolution.componentBranch
1299
+ ```
1300
+
1301
+ **Practical checklist when a subscription "doesn't work":**
1302
+
1303
+ 1. Does the account have a primary parent, or is it membership-only? Count the entries in
1304
+ `resolution.relationships` and check which one is `primary: true`. (Membership-only + multiple edges ⇒
1305
+ ambiguous ⇒ trunk.)
1306
+ 2. Which **edge** carries the `branchName` — it is on the edge in `resolution.relationships`, and
1307
+ `remits-cli components branch <name> --subscribers` prints `via primary|membership edge -> parent N`.
1308
+ 3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
1309
+ account's own edge selects the branch, exactly as production would.
1310
+ 4. Check the resolved layer, not the source text: `testComponentSource` / the `variant:<id>:<hash>`
1311
+ compile signature (see "Diagnosing a variant").
1312
+
1313
+ **The same block answers the non-branch resolution questions too:** "why are this account's documents not
1314
+ where I expect?" → compare `databaseName` vs `resolvedDatabaseName` and any edge `databaseName`. "Why does
1315
+ this hostname land on the wrong account?" → `domainName` vs `resolvedDomainName` and the edge `domainName`
1316
+ (an edge host wins over the account's own, and additionally supplies the path travelled).
1317
+
1318
+ **Users are not part of this graph.** A user is global, belongs to many accounts, and can reach an account
1319
+ below one they are credentialed on through **any** link — a deliberately broader rule than the single-path
1320
+ component inheritance above. Their custom fields (`role`, `team`, …) are stored **per account**, so the same
1321
+ person can differ per account. There is no user MCP tool: use `mcp_sql_query` against `user` /
1322
+ `user_account`, and read `features/account-management.md` (`mcp_get_guide`) for the model.
1323
+
1324
+ ## Branched Component Variants (per-account component overrides)
1325
+
1326
+ Sometimes one account — often a customer nested several levels down a hierarchy — needs *slightly*
1327
+ different behavior from a component owned by its platform or product account. The wrong answer is
1328
+ per-account `if/then` logic inside the origin component. The right answer is a **branch variant**: a
1329
+ durable, branch-scoped overlay of that component, which only the accounts subscribed to that branch
1330
+ resolve.
1331
+
1332
+ ### The model (three moving parts)
1333
+
1334
+ 1. **The origin account owns the component and the branch.** Say platform account 1 owns
1335
+ `Extract Invoice` (Action 50). Its repo `remits-<name>` has trunk branch `main` and a second git branch
1336
+ `feature_forked`.
1337
+ 2. **A `ComponentVariant` row is the overlay.** Committing on `feature_forked` stores rows owned by
1338
+ **account 1**, on branch `feature_forked`, for the components whose content **differs from trunk**. A git
1339
+ branch physically contains every file; only the *differing* ones become variants. That is computed at
1340
+ sync time — you never declare it.
1341
+ 3. **A child account subscribes via its relationship edge.** Account 101's `AccountRelationship` edge
1342
+ carries `branchName = 'feature_forked'`. Resolution then walks 101's inheritance chain and applies
1343
+ account 1's `feature_forked` overlays.
1344
+
1345
+ Consequences worth internalizing:
1346
+
1347
+ - **A branch is not an account.** Subscription is many-to-many: five accounts can share one branch, and a
1348
+ subscribing account still has its own trunk components, which merge on top as usual.
1349
+ - **Nested hierarchies work.** The overlay applies to the subscribing account *and its descendants*, until a
1350
+ nearer edge overrides it. Resolution consults every owner on the inheritance path, so a deeply nested
1351
+ client picks up a platform-owned variant.
1352
+ - **Component identity is shared.** A variant keeps the origin's component id — it is an overlay, not a
1353
+ copy. That is what makes drift detectable and what distinguishes this from just duplicating the component
1354
+ onto the child account.
1355
+ - **Subscribing an account** attaches the branch to that account's relationship edge:
1356
+ `remits-cli components branch <name> --subscribe <accountId> [--domain <host>]` (and `--unsubscribe <accountId>` to return it
1357
+ to trunk). It is also editable per-edge on the admin account page. Authorization is downward-only: you can
1358
+ only subscribe an account reachable from one you already have access to.
1359
+ - **`--subscribe` sets the branch on an edge that must already exist — it cannot create one.** If the
1360
+ account has no relationship edge to the owner you will get *"Account N has no relationship edge to
1361
+ subscribe; add a membership edge first"*. Creating a membership edge is an **admin** operation (the
1362
+ account page), not a CLI one.
1363
+ - **When the account has several parents, say which edge you mean:**
1364
+ `--subscribe <accountId> --parent-account <ownerId>`. Without it the CLI picks the edge to the owner
1365
+ whose branch you are managing, then falls back to the account's primary edge — which may not be the
1366
+ edge you intended. `--subscribers` prints `via primary|membership edge -> parent N` so you can confirm.
1367
+ - **Retiring a branch** is explicit: `remits-cli components branch <name> --retire`. Deleting the *git*
1368
+ branch does **not** remove its overlays — subscribers would keep resolving a branch that no longer exists.
1369
+ Retire refuses while the branch still has subscribers unless you pass `--force`.
1370
+
1371
+ A branch can also **add** a component (a file whose prefix is not a live trunk id — e.g. `new_Foo.groovy` —
1372
+ becomes a *branch-only* component identified by name) and **remove** one (a trunk component with no file on
1373
+ the branch becomes a *tombstone*, hiding it from subscribers).
1374
+
1375
+ **Two sharp edges worth knowing before you edit a branch:**
1376
+
1377
+ - **Deleting a file means "remove the component", not "stop overriding it."** To withdraw an override, make
1378
+ the file identical to trunk again — the sync then removes the variant row. Deleting it creates a tombstone
1379
+ and hides the component from every subscriber.
1380
+ - **Keep the branch rebased.** A branch physically carries every file, so anything trunk added *after* the
1381
+ branch was cut looks like a deliberate deletion. The sync refuses a wholesale removal (more than ~a third
1382
+ of a kind) and tells you to rebase, but a small stale gap will tombstone silently. Rebase onto trunk before
1383
+ you commit a branch you have not touched in a while.
1384
+
1385
+ **Every component kind can be varied** — `schema`, `reader`, `action`, `embeddable`, `htmltemplate`,
1386
+ `rule`, `test`, `utility` (agent), `tool`, `prompt` — including tombstones and branch-only additions. A
1387
+ schema variant is worth calling out: it can change the JSON schema itself *and* `collectionName`, so it
1388
+ changes validation and where the subscriber's documents physically land. Treat schema variants with the
1389
+ same care as a trunk schema change.
1390
+
1391
+ **What a branch may override.** A variant speaks the same `.meta.yml` vocabulary trunk does — `name`,
1392
+ `description`, `summary`, `mermaid`, `category`, `type`, `path`, `collectionName`, `job`, `model`,
1393
+ `agentTimeout`, `mcp`, `cli`, `global`, `purpose`, `auxiliary`, plus Schema flags (`enableTrigger`,
1394
+ `enableFullText`, `enableRAG`, `enableRevisions`, `enableBigQuerySync`, `enableRules`, `anchor`) — and the
1395
+ component's content files. Keys outside that set are ignored, deliberately: trunk cannot express them either,
1396
+ so allowing them would mean a branch behaves one way and silently loses that behavior the moment it is
1397
+ promoted to trunk.
1398
+
1399
+ ### Which world does your working tree resolve? (read this before you run anything)
1400
+
1401
+ You will work from **two different checkouts of the same repo**, and they behave differently on both ends of
1402
+ the loop. The rule turns entirely on **trunk vs non-trunk**:
1403
+
1404
+ | Working tree | Staging scope | A run resolves | `components sync`/`commit` writes |
1405
+ |---|---|---|---|
1406
+ | **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** |
1407
+ | **any other branch** (`feature_forked`) | that branch | trunk + **`feature_forked`** overlays | **`ComponentVariant` overlays on that branch only** — never touches trunk rows |
1408
+
1409
+ **The precedence trap that costs the most time:** `variantBranch` OUTRANKS every account's subscription. So
1410
+ running a Test suite that asserts *production* semantics from a **variant checkout** pins every account in
1411
+ that suite — including fixture accounts that subscribe to their own generated branches — to your branch,
1412
+ where they have no variants, and they all read trunk. The suite fails in a way that looks exactly like a
1413
+ resolution regression. (Live example: the branch-variant suite scored 4/15 from a variant checkout and 15/15
1414
+ from trunk, with no code difference.) **Run branch-variant suites from trunk, or pass `--variant-branch
1415
+ none`.** Before concluding "variant resolution is broken", re-run from trunk.
1416
+
1417
+ Do not infer this from the branch name. Ask:
1418
+
1419
+ ```bash
1420
+ remits-cli components status
1421
+ ```
1422
+
1423
+ ```
1424
+ Working tree: VARIANT BRANCH "feature_forked" (trunk is "main")
1425
+ runs resolve: trunk + the 'feature_forked' variant overlays
1426
+ commit writes: ComponentVariant overlays on 'feature_forked' (never touches trunk rows)
1427
+ variants stored on this branch: 3
1428
+ subscribing accounts: 101 (Acme Child)
1429
+ ```
1430
+
1431
+ **Branch-local `account-info.json` can describe a subscriber.** On a variant branch, the same git repository
1432
+ is still the OWNER's component repo: files under `components/` overlay that owner's trunk component ids, and
1433
+ `components sync` writes `ComponentVariant` rows owned by that parent account. But if the checkout was synced
1434
+ from a subscribing account reached through an `AccountRelationship` edge, the branch's `account-info.json`
1435
+ is intentionally refreshed from the subscriber's resolved account information. That is what lets a forked
1436
+ checkout answer "who am I acting as?" while preserving "who owns these component variants?"
1437
+
1438
+ Consequence: if a variant checkout's `account-info.json` is stale and still names the owner, run the first
1439
+ repair sync with an explicit subscriber target, for example `remits-cli components sync --account-id 101`.
1440
+ After that sync and `git pull --ff-only`, normal commands from that checkout should resolve the subscriber
1441
+ from `account-info.json` and continue writing variants under the owner selected by the branch edge.
1442
+
1443
+ ### Two levers, two different questions
1444
+
1445
+ - **`--as-account <id>`** changes **who** the run executes as, so that account's own edge picks the branch.
1446
+ Answers *"what does customer X actually get?"* Only narrows downward (the target must be reachable from an
1447
+ account you already have access to).
1448
+ - **`--variant-branch <name>`** changes **which branch**, from any checkout. Answers *"what does branch Y
1449
+ look like?"* — most useful for verifying a freshly committed branch **before** any edge subscribes to it.
1450
+ `--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the branch.
1451
+ The flag is available on `test run`, `token`, `tools`, and `tool`, so tests, browser URLs, tool discovery,
1452
+ and tool execution can all inspect the same committed variant world.
1453
+
1454
+ Both work on `remits-cli test run` and `remits-cli token`; `--variant-branch` also works on
1455
+ `remits-cli tools` and `remits-cli tool` for branch-specific tool discovery and execution.
1456
+
1457
+ ### The SDLC is identical on a variant branch
1458
+
1459
+ The loop does not change shape — `edit → stage → verify → commit`:
1460
+
1461
+ ```bash
1462
+ git checkout feature_forked # or: git checkout -b feature_forked
1463
+ # edit components/actions/50_ExtractInvoice.groovy (KEEP the trunk id)
1464
+ remits-cli components stage # Redis, scoped to this branch — same as always
1465
+ remits-cli test run --test "Invoice Tests" # resolves the feature_forked world
1466
+ remits-cli test run --test "Invoice Tests" --as-account 101 # ...as the real subscriber
1467
+ # promote:
1468
+ git add -A && git commit -m "..." && git push origin feature_forked
1469
+ remits-cli components sync --dry-run # inspect overrides/additions/tombstones without writes
1470
+ remits-cli components sync # writes ComponentVariant overlays ONLY
1471
+ ```
1472
+
1473
+ Two things that differ from trunk:
1474
+
1475
+ - **Keep the origin id in the filename.** `50_ExtractInvoice.groovy` on the branch overlays Action 50. That
1476
+ is what preserves component identity and lets drift be computed against the origin.
1477
+ - **Nothing is renamed.** A variant sync never renames files and never repoints the account's trunk branch.
1478
+ When launched from a subscribing account, it may refresh that branch's `account-info.json` so the checkout
1479
+ describes the account reached through the branch edge. A `new_*` file stays `new_*` and becomes a
1480
+ branch-only component keyed by name.
1481
+
1482
+ ### Promotion: getting the branch back into trunk
1483
+
1484
+ The branch is the cheap half. Promotion is where the sharp edges are, and it is a **git** operation followed
1485
+ by a **trunk** sync — the platform does not merge anything for you.
1486
+
1487
+ ```bash
1488
+ # 1. merge the branch into trunk (review the diff FIRST - see the deletion hazard below)
1489
+ git checkout main && git merge feature_forked && git push origin main
1490
+ # 2. trunk sync: creates real rows for new_* files, assigns ids, RENAMES those files on trunk
1491
+ remits-cli components sync
1492
+ git pull --ff-only origin main
1493
+ # 3. bring trunk back into the branch so the two stop diverging
1494
+ git checkout feature_forked && git merge origin/main && git push origin feature_forked
1495
+ remits-cli components sync # reconciles the branch's overlays
1496
+ ```
1497
+
1498
+ **What step 3 reconciles, and why you must run it.** A branch-only `new_Foo.groovy` becomes trunk component
1499
+ `123` and is renamed `123_Foo.groovy` **on trunk only**. The branch still holds the pre-promotion file, so
1500
+ the sync handles both shapes automatically:
1501
+
1502
+ | Branch state after promotion | What the sync does |
1503
+ |---|---|
1504
+ | only `new_Foo.*` | **adopts** trunk id 123 -> identical content -> the overlay is **removed** (`unchanged`, `pruned:true`) |
1505
+ | both `new_Foo.*` and `123_Foo.*` (the usual merge artifact) | the id file wins; the `new_` group is reported under `skipped` as *superseded* - delete it from the branch |
1506
+
1507
+ Skip step 3 and the branch keeps a **branch-only** overlay for a component that now has a trunk row: it can
1508
+ never converge (there is no trunk id to compare against), so subscribers stay pinned to the promoted copy
1509
+ forever and every later trunk improvement is invisible to them.
1510
+
1511
+ > **A tombstone is a DELETED FILE, so merging a variant branch into trunk promotes its removals.** On the
1512
+ > branch, a missing file only *hides* a component from subscribers. Merged into trunk and synced, that same
1513
+ > missing file **hard-deletes the component for everyone**. Always read
1514
+ > `git diff --stat origin/main HEAD -- components/` before pushing a promotion and confirm every deletion is
1515
+ > intended. To promote only part of a branch, restore the files you are not promoting
1516
+ > (`git checkout origin/main -- <path>`) before the trunk sync.
1517
+
1518
+ **Trunk moving also invalidates the branch.** Variant sparseness compares branch content against *current*
1519
+ trunk, so a trunk change can make a branch overlay obsolete without the branch changing at all. A trunk sync
1520
+ now drops the affected branches' cached sync verdicts, so the next `components sync` on the branch really
1521
+ re-evaluates instead of answering *"No changes detected"*. After promoting anything, re-sync each live
1522
+ branch.
1523
+
1524
+ ### Verifying as the subscriber
1525
+
1526
+ `--as-account <id>` runs as the subscriber so its own edge selects the branch. It works for a
1527
+ `parentId`-less, membership-only subscriber too: the anchor is derived from the branch you name
1528
+ (`--variant-branch`) or, failing that, from the account's single branch subscription.
1529
+
1530
+ It **refuses to guess** when an account has several edges each carrying a different branch - the run then
1531
+ resolves trunk. Name the branch with `--variant-branch <name>` to disambiguate, or check the edges with
1532
+ `components branch <name> --subscribers` (it prints `via primary|membership edge -> parent N`).
1533
+
1534
+ The strongest end-to-end proof for a UI-visible variant is a token, not a log line:
1535
+
1536
+ ```bash
1537
+ remits-cli token --path embeddable/index/50 --as-account 101 # subscriber -> variant
1538
+ remits-cli token --path embeddable/index/50 # owner -> trunk
1539
+ ```
1540
+
1541
+ Open both; if the branch changes anything visible, you will see it immediately.
1542
+
1543
+ ### Agents (Utility) on a variant branch
1544
+
1545
+ A `Utility` is an **Agent** in practice — the repo directory is `components/agents/` and there is no
1546
+ `components/utilities/`. Agent variants work, with two things to know:
1547
+
1548
+ - **Override the sidecar the same way you would on trunk.** `components/agents/19_Bob.meta.yml` keys
1549
+ (`model:`, `agentTimeout:`, `type:`) and the `.md` prompt sidecar all overlay correctly, as does the
1550
+ `.groovy` source. A branch-only agent (a `new_*` file) defaults to `type: AI` so agent lookups find it.
1551
+ - **An agent's TOOL LIST cannot be overridden by a variant.** `tools:` in the sidecar maps to a GORM
1552
+ association, and `agent()` populates it from the **trunk** row. A `tools:` list on a variant branch is
1553
+ ignored. If a branch needs a different tool set, change trunk or have the agent choose tools at runtime.
1554
+
1555
+ ### Danger profile on a variant branch (different, not absent)
1556
+
1557
+ The Component Integrity Rules' worst case — *"a missing file hard-deletes a live component"* — **does not
1558
+ apply on a variant branch.** Variant sync writes overlay rows only; it cannot create, delete, rename, or
1559
+ overwrite a trunk component. That makes a variant branch a genuinely safer place to iterate.
1560
+
1561
+ The analogous hazard is different and you must still respect it:
1562
+
1563
+ - **A trunk component with no file on the branch becomes a TOMBSTONE**, which *hides* that component from
1564
+ every subscriber. It is reversible (restore the file and re-sync) and it never touches trunk — but to a
1565
+ subscribing account it looks exactly like the component was deleted. Before syncing a variant branch,
1566
+ confirm every omission is deliberate.
1567
+ - **Use `components sync --dry-run` before risky variant syncs.** It reports `overridden`, `added`,
1568
+ `removed`, `unchanged`, `skipped`, and `errors` without writing variant rows, caching the sync SHA, or
1569
+ clearing staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means
1570
+ the branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
1571
+ `components commit --dry-run` is unsupported because `commit` performs local git writes before server sync.
1572
+ - **Deleting a trunk component cascades**: its overlays on every branch are removed with it.
1573
+ - **A removal you did not author usually means TRUNK is drifted, not that the branch deleted something.** A
1574
+ component that exists in the DB with **no file on trunk** is missing from every branch too, so it shows up
1575
+ as a phantom `removed` in *every* branch preview - while the trunk sync separately tries to hard-delete it
1576
+ on every run. Check whether the file exists on trunk before "fixing" it on the branch; the repair is to
1577
+ mirror the live DB source back into the trunk repo (Component Integrity Rules), not to touch the branch.
1578
+ - **The sync refuses a wholesale removal.** Above ~a third of a kind - or **100% of a kind at any size** - it
1579
+ aborts that kind, reports why, and points at a rebase. Rebasing onto trunk is almost always the real fix; a
1580
+ branch not rebased in a while is missing everything trunk has added since it was cut. Only when the
1581
+ removals are genuinely deliberate, re-run with `--force-tombstones`.
1582
+
1583
+ ### Inspecting branches and drift
1584
+
1585
+ ```bash
1586
+ remits-cli components branches # branches with variants, counts, drift
1587
+ remits-cli components branch feature_forked # overridden / added / removed + subscribers
1588
+ remits-cli components branch feature_forked --diff 50 --component-type action
1589
+ remits-cli components branch feature_forked --subscribers
1590
+ ```
1591
+
1592
+ **Drift is the number to watch.** Each variant records the trunk content hash at the moment it was cut
1593
+ (`originHash`). When the origin component later changes, the variant is reported **DRIFTED** — the branch is
1594
+ now based on a stale version of the origin and someone should reconcile it. `branches` shows a per-branch
1595
+ drift count; `branch <name>` flags each drifted component; `--diff` shows variant vs current trunk.
1596
+
1597
+ Before editing an origin component, check whether variants of it exist — the owner account's
1598
+ `account-info.json` carries a `componentBranches` summary, `components branches` gives the live view, and
1599
+ the admin edit forms show a branch-variant notice for trunk components that already have variants. A change
1600
+ to the origin silently drifts every branch that overlays it.
1601
+
1602
+ **`components branches` only lists branches that already have overlays.** A branch you just pushed is
1603
+ invisible here until its first sync - that is not an error. Preview it by name
1604
+ (`components sync --dry-run` from that checkout), or from the admin GitHub menu's branch picker.
1605
+
1606
+ **The admin UI has the same surface**, which is what to point a user at:
1607
+ - *owner account page* -> **Component branches** box: per-branch counts, drift, subscribers, and
1608
+ **Preview / Sync / Retire**;
1609
+ - *subscriber account page* -> **Branch variants** box (what THIS account resolves) and **GitHub -> Fetch
1610
+ `<branch>` variants**;
1611
+ - both open the same result panel - grouped plan, subscribers, a **Monaco diff of trunk vs branch** per
1612
+ changed field, and a confirm-gated override when the removal guard refuses.
1613
+ Preview there is the same `--dry-run`, so it is safe to hand to a non-CLI user.
1614
+
1615
+ ### Diagnosing a variant
1616
+
1617
+ - `remits-cli components branch <name> --json` — the stored overlay content and what it overrides.
1618
+ - The compile signature (`variant:<id>:<hash>`) in `Using Cached BCD` logs — proves a variant actually ran.
1619
+ - A test/tool response reports `testComponentSource` as `staged` | `variant` | `db`, so you can see which
1620
+ layer the run resolved without reading logs.
1621
+
1011
1622
  ## Production Support Workflow
1012
1623
 
1013
1624
  Switch to prod mode for investigations:
@@ -1101,6 +1712,28 @@ So from inside a parent account's repo you can target a child account just by se
1101
1712
  ### `mcp_account_view`
1102
1713
  Returns complete account structure — schemas, components, relationships.
1103
1714
 
1715
+ Also returns two blocks that explain how the account RESOLVES, which is what you need before comparing
1716
+ its behavior against component source:
1717
+
1718
+ - `resolution` — `databaseName` (the account's own override, often null) vs `resolvedDatabaseName` (the
1719
+ storage namespace actually in effect), `branchName` (the repo sync branch), `domainName` vs
1720
+ `resolvedDomainName` (the custom host in effect, which differs when reached through an edge host),
1721
+ `editMode`, and — when the account subscribes to a component branch — `componentBranch` plus
1722
+ `componentBranchOwnerAccountId`.
1723
+ - `resolution.relationships` — **every structural link upward**, primary first: `parentAccountId` /
1724
+ `parentAccountName` / `parentAccountCode` / `parentAccountType`, `primary` (true for the one link that
1725
+ mirrors the account's primary parent), `active`, and the three independent link-scoped properties
1726
+ `branchName` (which component-variant code runs), `databaseName` (where data lives), `domainName` (which
1727
+ host reaches the account through this link). The hierarchy tree flattens all links into one shape, so this
1728
+ is the **only** place that answers "does this account have more than one parent, and which link carries the
1729
+ branch/namespace/host?" More than one entry ⇒ this account can resolve differently depending on the path a
1730
+ request travelled — establish which one a failing request used before comparing behavior.
1731
+ - `componentBranches` — the variant branches this account OWNS, with override/add/remove, subscriber, and
1732
+ drift counts. Same summary the owner's `account-info.json` carries.
1733
+
1734
+ Note: this tool does **not** return users. For user/access questions use `mcp_sql_query` (`user`,
1735
+ `user_account`).
1736
+
1104
1737
  | Parameter | Required | Description |
1105
1738
  |-----------|----------|-------------|
1106
1739
  | `accountId` | yes | Account ID |
@@ -1347,6 +1980,11 @@ are absent from `remits-cli tools`, that is expected.
1347
1980
  | `offset` | no | Start line (1-indexed). Also accepts `startLine`. |
1348
1981
  | `limit` | no | Number of lines to return |
1349
1982
 
1983
+ Returns `componentVariants` when the component has committed branch variants — the branches, their state
1984
+ (`current` / `drifted` / `removed`), and a warning. Non-null means some accounts run a different version
1985
+ than the source you are reading, and editing here changes trunk only and drifts those variants.
1986
+ `mcp_component_grep` searches trunk, so it will not match text that exists only in a variant.
1987
+
1350
1988
  ### `mcp_component_grep`
1351
1989
  Search component source code with regex.
1352
1990
 
@@ -1455,6 +2093,25 @@ Inspect, flush, or clear the CLI staging cache for components.
1455
2093
  | `accountId` | yes | Account ID |
1456
2094
  | `action` | yes | `read` (list staged entries), `commit` (flush staged → DB), or `clear` (drop staged entries without touching the DB) |
1457
2095
 
2096
+ ### `mcp_component_branches`
2097
+ The MCP counterpart of `remits-cli components branches` / `components branch <name>` — use it when you are
2098
+ driving the platform **remotely** (no local repo/checkout) and need to know whether a component already has
2099
+ committed branch variants, who subscribes, and whether they have drifted.
2100
+
2101
+ | Parameter | Required | Description |
2102
+ |-----------|----------|-------------|
2103
+ | `action` | no | `list` (default) — every branch this owner has variants on, with drift + subscriber counts; `status` — one branch's overridden/added/removed; `diff` — one component's variant beside current trunk; `subscribers` — accounts resolving a branch; `subscribe`/`unsubscribe` — set/clear the branch on a subscriber's existing relationship edge; `retire` — delete a branch's overlays |
2104
+ | `ownerAccountId` | no | The account whose branches are inspected. **Outranks `accountId`**, because `accountId` also selects which account the tool is resolved on — so `accountId` alone cannot target another account's branches. |
2105
+ | `branchName` | conditional | Required for everything except `list`. Alias: `branch`. |
2106
+ | `componentId` / `componentName` / `componentType` | conditional | Address the component for `diff` |
2107
+ | `targetAccountId` | conditional | The account to subscribe/unsubscribe |
2108
+ | `parentAccountId` | no | Which relationship edge to set the branch on. Without it: the edge toward the branch owner, then the target's primary edge. |
2109
+ | `force` | no | `retire` only — proceed while subscribers remain |
2110
+
2111
+ Same rules as the CLI: `subscribe` only **sets the branch on an edge that already exists** (creating a
2112
+ membership edge is an admin operation), authorization is downward-only, and `retire` refuses while
2113
+ subscribers remain unless forced.
2114
+
1458
2115
  ### `mcp_cache`
1459
2116
  Bounded read-only investigation of the platform Redis keyspace — the way to see exactly what a staged
1460
2117
  entry holds (and its TTL) or any other cache key. Read-only: no delete (use `mcp_component_commit clear`
@@ -1467,6 +2124,143 @@ to remove staged entries).
1467
2124
  | `key` | no | Exact key for `action:'inspect'` |
1468
2125
  | `pageSize`/`sampleSize`/`previewChars` | no | Bounding controls |
1469
2126
 
2127
+ ### `mcp_sql_query`
2128
+ Read-only, bounded SQL against the platform database. This is the **catch-all investigation surface** for
2129
+ questions the purpose-built tools do not model — above all **users and account membership**, for which there
2130
+ is no dedicated tool.
2131
+
2132
+ | Parameter | Required | Description |
2133
+ |-----------|----------|-------------|
2134
+ | `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. |
2135
+ | `queries` | yes* | Batch of up to 10 read-only queries (strings, or `{query, params}` objects) |
2136
+ | `params` / `parameters` | no | Positional parameters — **use these instead of interpolating values** |
2137
+ | `maxRows` / `limit` | no | Rows per query. Default 100, max 500. |
2138
+ | `maxValueChars` | no | Truncation width per string value. Default 2000, max 20000. |
2139
+ | `redact` | no | Redact secret-like columns (`password`, `token`, `secret`, `authorization`, …). **Default true — leave it on.** |
2140
+
2141
+ *Provide `query`/`sql` or `queries`.
2142
+
2143
+ Useful shapes:
2144
+
2145
+ ```sql
2146
+ -- who has access to a client account (and through which membership rows)
2147
+ SELECT u.id, u.username, u.enabled FROM user u
2148
+ JOIN user_account ua ON ua.user_id = u.id WHERE ua.account_id = ?;
2149
+
2150
+ -- every account a user can reach directly
2151
+ SELECT a.id, a.name, a.type FROM account a
2152
+ JOIN user_account ua ON ua.account_id = a.id WHERE ua.user_id = ?;
2153
+
2154
+ -- the account's structural links, including membership edges (`primary` is column `is_primary`)
2155
+ SELECT parent_id, is_primary, branch_name, database_name, domain_name, active
2156
+ FROM account_relationship WHERE account_id = ?;
2157
+ ```
2158
+
2159
+ Prefer the purpose-built tools when one fits — they apply account scoping, data-mode segmentation, and
2160
+ resolution awareness that raw SQL does not. Reach for SQL when nothing else models the question.
2161
+
2162
+ ### `mcp_index_search`
2163
+ Query and **diagnose** the Vertex AI Search index through the platform's Vertex DSL. Use it to check what a
2164
+ RAG/search-backed component actually retrieves before blaming the component.
2165
+
2166
+ | Parameter | Required | Description |
2167
+ |-----------|----------|-------------|
2168
+ | `accountId` | yes | Tenant scoping |
2169
+ | `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`) |
2170
+ | `query` | conditional | Required for `search`/`diagnose` |
2171
+ | `schema` / `projectionType(s)` / `projectionSource` | no | Scope to a schema's Vertex projections |
2172
+ | `filters` | no | Vertex-style structured filters on indexed metadata |
2173
+ | `pageSize` / `maxResults` / `pageToken` | no | Paging and local trimming |
2174
+ | `searchProfile` | no | `ai`/`agent`/`strict` (precision) vs `admin`/`default` (recall) |
2175
+ | `includeMatchDiagnostics` | no | Return the applied filter/query plus the pre-strict-filter candidate set |
2176
+ | `accountIds` | no | Explicit multi-account search |
2177
+ | `dataMode` | no | `test` (default) or `prod` |
2178
+
2179
+ When a component "can't find" an obviously-present document, run `action:'inspect'` on its source document id
2180
+ first — that shows what was indexed, which is usually the answer.
2181
+
2182
+ ### `mcp_get_guide`
2183
+ Load the packaged front-stage guides — the same `docs/guides/` set `remits-cli` syncs into a repo. Use it when
2184
+ you are working **outside a repo** (or the repo's `guides/` is stale) and need the authoritative guidance
2185
+ before writing a component.
2186
+
2187
+ | Parameter | Required | Description |
2188
+ |-----------|----------|-------------|
2189
+ | `guide` | conditional | Short name (`agent-components`), relative path (`features/account-management`), or full path |
2190
+ | `list` | no | List available guides instead of loading one |
2191
+ | `directory` | no | Scope a list to `components` or `features` |
2192
+ | `contains` | no | Substring filter when listing |
2193
+
2194
+ ### `mcp_test_fixture`
2195
+ Seed and remove schema-backed fixture documents in the **forced test** data segment. This is how you construct
2196
+ realistic conditions for verification without copying live customer data.
2197
+
2198
+ | Parameter | Required | Description |
2199
+ |-----------|----------|-------------|
2200
+ | `accountId` | yes | Account owning the target schema/collection |
2201
+ | `action` | no | `create` (fails on existing id), `upsert`, or `delete` |
2202
+ | `schemaName` / `collection` | conditional | Schema display name or collection name |
2203
+ | `documentId` / `documentIds` | no | Explicit Firestore ids for single/batch operations |
2204
+ | `data` / `documents` | conditional | Single payload, or a batch array |
2205
+ | `dataMode` | no | Must be `test` — **prod-mode writes are rejected** |
2206
+
2207
+ ### `mcp_embeddable_test_url`
2208
+ Mint an authenticated browser URL for an embeddable — the MCP counterpart of `remits-cli token --path
2209
+ embeddable/index/<id>`. Returns the URL plus branch/variant/data-mode hints so you know which world the page
2210
+ will render.
2211
+
2212
+ | Parameter | Required | Description |
2213
+ |-----------|----------|-------------|
2214
+ | `accountId` | yes | Account owning the embeddable |
2215
+ | `embeddableId` / `embeddableName` | conditional | Provide one |
2216
+ | `userId` | no | User to mint for. Defaults to the current user. |
2217
+ | `variantBranch` | no | Probe a committed component-variant branch. Usually inherited from the caller. |
2218
+
2219
+ ### `mcp_playwright_replay`
2220
+ Hosted browser automation (the `playwright-relay` service) for visual verification when local `playwright-cli`
2221
+ is unavailable — e.g. an agent running remotely. Returns an accessibility snapshot and interactive refs on
2222
+ every command, so you navigate iteratively.
2223
+
2224
+ | Parameter | Required | Description |
2225
+ |-----------|----------|-------------|
2226
+ | `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` |
2227
+ | `sessionId` | conditional | Reuse an existing session. `open`/`goto` without one starts a session. |
2228
+ | `url` / `target` / `value` / `expression` / `code` | conditional | Per-command inputs (`target` is an element ref or selector) |
2229
+ | `compact` | no | Default true — omits bulky raw relay payloads |
2230
+ | `includeSnapshot` / `includeAccessibility` / `includeRefs` / `includePage` / `includeResult` / `includeRaw` | no | Response shaping |
2231
+ | `pattern` / `level` / `limit` | no | Filters for `console` / `network` |
2232
+ | `ticketId` / `artifactType` / `artifactLabel` / `artifactNotes` | no | Attach a screenshot/pdf/trace/video to a support ticket |
2233
+
2234
+ ### `mcp_jvm_spike_triage`
2235
+ Production-aware triage bundle for a Cloud Run service showing a latency/memory spike. Safe by construction:
2236
+ optional class-histogram sampling, **no heap dump and no JFR**.
2237
+
2238
+ | Parameter | Required | Description |
2239
+ |-----------|----------|-------------|
2240
+ | `serviceName` | yes | e.g. `remits`, `remits-actions` |
2241
+ | `region` | yes | e.g. `us-east5`, `us-east1` |
2242
+ | `sampleHeap` | no | Default true — one class histogram + heap composition sample (brief stop-the-world) |
2243
+ | `top` | no | Top classes to return. Default 20, max 100. |
2244
+ | `includeThreadDump` / `threadLimit` | no | Fuller thread dump beyond the built-in top-thread preview |
2245
+ | `includeLogs` / `logLookbackMinutes` / `logLimit` | no | Recent WARNING+ log signals for the same service |
2246
+
2247
+ ### `mcp_support_ticket_queue`
2248
+ List and filter tickets **across an account and its descendants** so you can choose what to work on. Lifecycle
2249
+ actions stay on `mcp_support_ticket`.
2250
+
2251
+ | Parameter | Required | Description |
2252
+ |-----------|----------|-------------|
2253
+ | `action` | no | `list` (default) |
2254
+ | `accountId` / `accountIds` / `includeChildren` / `includeRoot` | no | Queue scope. Defaults to the current account **and its descendants**. |
2255
+ | `statuses` / `status` | no | Defaults to `open`, `accepted`, `in_progress`, `pending_review`. `['all']` disables filtering. |
2256
+ | `priorities` / `types` / `sources` / `tags` | no | Additional filters |
2257
+ | `assignedTo` / `unassigned` | no | Assignment filters |
2258
+ | `implementationAccountId` | no | Filter by owning `PLATFORM`/`PRODUCT` account |
2259
+ | `search` | no | Case-insensitive across subject, description, account, affected component, sender, tags |
2260
+ | `sortBy` / `sortDirection` | no | `triage` (default-style: critical/high unassigned first), `updated`, `priority`, `status`, `account` |
2261
+ | `limit` / `offset` / `scanLimitPerAccount` | no | Paging and scan bounds |
2262
+ | `dataMode` | no | Normal agent work uses the **prod** ticket queue |
2263
+
1470
2264
  ## Multi-Session Support
1471
2265
 
1472
2266
  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.
@@ -1631,18 +2425,33 @@ remits-cli data-mode [set test|prod]
1631
2425
  remits-cli components stage [--branch <name>] [--data-mode test|prod] [--json|--verbose]
1632
2426
  remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]
1633
2427
  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
1634
- remits-cli components sync [--branch <name>] [--data-mode test|prod] # gated durable repo->DB reconciliation only
1635
- remits-cli components commit [--message "msg"] [--data-mode test|prod]
1636
- remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod]
1637
- remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod]
1638
- remits-cli tools [--branch <name>] [--data-mode test|prod]
1639
- remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--timeout-ms 60000] [--async true --wait true]
2428
+ remits-cli components sync [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
2429
+ remits-cli components commit [--message "msg"] [--data-mode test|prod] [--force-tombstones]
2430
+ remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
2431
+ remits-cli components branch <name> [--json] # one branch: overridden / added / removed, drift flags, subscribers
2432
+ remits-cli components branch <name> --diff <componentId> --component-type <kind> [--json]
2433
+ remits-cli components branch <name> --subscribers [--json]
2434
+ remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>] # make an account resolve this branch
2435
+ remits-cli components branch <name> --unsubscribe <accountId> # return that account to trunk
2436
+ remits-cli components branch <name> --retire [--force] # delete the branch's overlays
2437
+ remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
2438
+ remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
2439
+ remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
2440
+ remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
1640
2441
  remits-cli tool status --call-id <callId> [--data-mode test|prod]
1641
2442
  ```
1642
2443
 
1643
2444
  For tests specifically:
1644
2445
  - If `--data-mode` is omitted, `remits-cli test run` uses `test`.
1645
2446
  - `--names` is comma-delimited, so keep individual test names comma-free.
2447
+ - `--as-account <ID>` runs AS a descendant subscriber so its edge selects the component branch
2448
+ (*"what does customer X get?"*); `--variant-branch <name>` probes a branch from any checkout
2449
+ (*"what does branch Y look like?"*), and `--variant-branch none` forces production/subscription semantics.
2450
+ Omit both and the working tree decides — see "Branched Component Variants".
2451
+ - `--force-tombstones` is only for non-trunk variant syncs, when missing trunk component files are known,
2452
+ intentional tombstone overrides. It is rejected on trunk.
2453
+ - `--dry-run` is only for `components sync` on non-trunk variant branches. It reports the variant write
2454
+ plan without writing rows, caching the sync SHA, or clearing staging.
1646
2455
 
1647
2456
  ## Troubleshooting
1648
2457
 
@@ -1661,7 +2470,21 @@ For tests specifically:
1661
2470
  | Tool response missing | Check `./.remits-cli/tool-responses/` |
1662
2471
  | 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. |
1663
2472
  | 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`. |
1664
- | 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. `commit` to make it durable. See "Component Resolution". |
2473
+ | 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". |
2474
+ | Need to know an account's shape (type, parents, namespace, branch) | `mcp_account_view` → read `resolution` (`type`, `resolvedDatabaseName`, `relationships`, `componentBranch`) and top-level `componentBranches`. Never infer structure from the account's name. |
2475
+ | 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. |
2476
+ | 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. |
2477
+ | 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). |
2478
+ | Need users of an account, or accounts of a user | There is no user MCP tool. Use `mcp_sql_query` on `user` / `user_account` (or `account.users([scope:'children'])` inside a component). Remember user custom fields are stored **per bound account**, so the same person can differ per account. |
2479
+ | 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. |
2480
+ | 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. |
2481
+ | 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. |
2482
+ | 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. |
2483
+ | `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. |
2484
+ | 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. |
2485
+ | 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. |
2486
+ | `--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`. |
2487
+ | 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. |
1665
2488
  | 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`). |
1666
2489
  | 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, the `mcp_component_edit` tool on that instance is on an old/cached version. Inspect with `mcp_cache`. |
1667
2490
  | 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. |