@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.
- package/README.md +7 -1
- package/index.js +271 -11
- package/package.json +3 -2
- package/skills/remits-cli/SKILL.md +945 -122
|
@@ -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
|
|
11
|
-
- Line
|
|
12
|
-
- Line
|
|
13
|
-
- Line
|
|
14
|
-
- Line
|
|
15
|
-
- Line
|
|
16
|
-
- Line
|
|
17
|
-
- Line
|
|
18
|
-
- Line
|
|
19
|
-
- Line
|
|
20
|
-
- Line
|
|
21
|
-
- Line
|
|
22
|
-
- Line
|
|
23
|
-
- Line
|
|
24
|
-
- Line
|
|
25
|
-
- Line
|
|
26
|
-
- Line
|
|
27
|
-
- Line
|
|
28
|
-
- Line
|
|
29
|
-
- Line
|
|
30
|
-
- Line
|
|
31
|
-
- Line
|
|
32
|
-
- Line
|
|
33
|
-
- Line
|
|
34
|
-
- Line
|
|
35
|
-
- Line
|
|
36
|
-
- Line
|
|
37
|
-
- Line
|
|
38
|
-
- Line
|
|
39
|
-
- Line
|
|
40
|
-
- Line
|
|
41
|
-
- Line
|
|
42
|
-
- Line
|
|
43
|
-
- Line
|
|
44
|
-
- Line
|
|
45
|
-
- Line
|
|
46
|
-
- Line
|
|
47
|
-
- Line
|
|
48
|
-
- Line
|
|
49
|
-
- Line
|
|
50
|
-
- Line
|
|
51
|
-
- Line
|
|
52
|
-
- Line
|
|
53
|
-
- Line
|
|
54
|
-
- Line
|
|
55
|
-
- Line
|
|
56
|
-
- Line
|
|
57
|
-
- Line
|
|
58
|
-
- Line
|
|
59
|
-
- Line
|
|
60
|
-
- Line
|
|
61
|
-
- Line
|
|
62
|
-
- Line
|
|
63
|
-
- Line
|
|
64
|
-
- Line
|
|
65
|
-
- Line
|
|
66
|
-
- Line
|
|
67
|
-
- Line
|
|
68
|
-
- Line
|
|
69
|
-
- Line
|
|
70
|
-
- Line
|
|
71
|
-
- Line
|
|
72
|
-
- Line
|
|
73
|
-
- Line
|
|
74
|
-
- Line
|
|
75
|
-
- Line
|
|
76
|
-
- Line
|
|
77
|
-
- Line
|
|
78
|
-
- Line
|
|
79
|
-
- Line
|
|
80
|
-
- Line
|
|
81
|
-
- Line
|
|
82
|
-
- Line
|
|
83
|
-
- Line
|
|
84
|
-
- Line
|
|
85
|
-
- Line
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
100
|
-
|
|
101
|
-
-
|
|
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
|
-
|
|
148
|
+
### How accounts connect (and why it changes what runs)
|
|
104
149
|
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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`.
|
|
122
|
-
over the database.** It reads the **remote repo ZIP — not your
|
|
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
|
|
137
|
-
hard-delete the original at the old id**. If a component's files are missing from the repo at sync
|
|
138
|
-
component is **hard-deleted from the DB**. This is exactly how a prior session deleted live schemas
|
|
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.**
|
|
142
|
-
|
|
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
|
|
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
|
|
164
|
-
scope. This is the expected clean state: old Redis aliases should not keep shadowing
|
|
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`)
|
|
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
|
|
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
|
|
895
|
-
|
|
896
|
-
2. **
|
|
897
|
-
and
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
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`,
|
|
913
|
-
`inputSchema`/`previewData
|
|
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
|
-
-
|
|
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:
|
|
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
|
|
1635
|
-
remits-cli components commit [--message "msg"] [--data-mode test|prod]
|
|
1636
|
-
remits-cli
|
|
1637
|
-
remits-cli
|
|
1638
|
-
remits-cli
|
|
1639
|
-
remits-cli
|
|
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. |
|