@remits/remits-cli 0.1.85 → 0.1.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,82 +7,95 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
7
7
 
8
8
  ## Table of Contents
9
9
 
10
- - Line 89: Account Targeting Model
11
- - Line 112: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
12
- - Line 118: How the platform reconciles the repo into the database (the mechanism you must understand)
13
- - Line 144: The surfaces and their intended behavior
14
- - Line 166: Intended workflows
15
- - Line 185: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
16
- - Line 195: If something looks wrong — stop, don't paper over
17
- - Line 219: Required Local Index Reads
18
- - Line 253: Big Picture: How remits-cli State Is Organized
19
- - Line 283: Support Ticket Mental Model
20
- - Line 319: Efficiency Rules
21
- - Line 331: Account Repository Index
22
- - Line 356: Two Workflows
23
- - Line 363: Test Mode vs Prod Mode
24
- - Line 395: Platform Mental Model: Front Stage vs Back Stage
25
- - Line 402: Documents vs Records
26
- - Line 412: HTTP Audits
27
- - Line 466: Record `content` / Context Mental Model
28
- - Line 486: Provenance Fields
29
- - Line 504: Why Persisted Context Looks Different From Runtime Context
30
- - Line 525: Investigation Rule for Record Context
31
- - Line 540: AI Request Response Records
32
- - Line 547: AI Session Groupings
33
- - Line 562: Correlation Keys
34
- - Line 568: Node Reference Table
35
- - Line 578: Repository vs External Context
36
- - Line 585: Getting Started
37
- - Line 587: Authentication
38
- - Line 600: Host vs Data Mode
39
- - Line 619: Tool Execution Lifecycle
40
- - Line 660: Data Mode
41
- - Line 670: Development Workflow
42
- - Line 672: The Golden Rule: Writing Code Is Not Finishing the Job
43
- - Line 684: The Development Fast Loop
44
- - Line 843: User Confirmation Preferences
45
- - Line 853: Component Resolution: Staging Cache vs DB (which "version" actually runs)
46
- - Line 859: The two source layers + the compile cache
47
- - Line 870: Staging cache key format
48
- - Line 883: How the platform picks staged vs DB (the compile signature)
49
- - Line 900: When staged overrides apply
50
- - Line 924: Diagnosing which version is in play
51
- - Line 945: Stage / commit / clear with the MCP tools
52
- - Line 969: Stale after sync / commit (the in-memory compile cache)
53
- - Line 979: Production Support Workflow
54
- - Line 987: Investigation Strategy
55
- - Line 1017: Presenting Findings
56
- - Line 1025: Verifying a Production Issue Fix
57
- - Line 1038: Tool Reference
58
- - Line 1040: Execute a Tool
59
- - Line 1067: `mcp_account_view`
60
- - Line 1074: `mcp_firestore_search`
61
- - Line 1112: `mcp_object_activity`
62
- - Line 1121: `mcp_record_listing`
63
- - Line 1146: `mcp_record_view`
64
- - Line 1159: `mcp_ai_session_search`
65
- - Line 1191: `mcp_run_action`
66
- - Line 1229: `mcp_run_agent`
67
- - Line 1267: `mcp_system_logs`
68
- - Line 1290: `mcp_component_view`
69
- - Line 1308: `mcp_component_grep`
70
- - Line 1321: `mcp_support_ticket`
71
- - Line 1370: `mcp_run_test`
72
- - Line 1379: `mcp_component_edit`
73
- - Line 1394: `mcp_component_create`
74
- - Line 1408: `mcp_component_commit`
75
- - Line 1416: `mcp_cache`
76
- - Line 1428: Multi-Session Support
77
- - Line 1448: Persistent Service, Control Center, and Agent Dispatch
78
- - Line 1457: Starting the Service
79
- - Line 1483: Control Center
80
- - Line 1498: Agent Dispatch
81
- - Line 1519: Configuring the Preferred Agent
82
- - Line 1530: Local State Files
83
- - Line 1578: Command Reference
84
- - Line 1605: Troubleshooting
85
- - Line 1632: When Something Doesn't Work as Expected
10
+ - Line 102: Account Targeting Model
11
+ - Line 125: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
12
+ - Line 131: How the platform reconciles the repo into the database (the mechanism you must understand)
13
+ - Line 186: The surfaces and their intended behavior
14
+ - Line 210: Intended workflows
15
+ - Line 229: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
16
+ - Line 242: If something looks wrong — stop, don't paper over
17
+ - Line 266: Required Local Index Reads
18
+ - Line 300: Big Picture: How remits-cli State Is Organized
19
+ - Line 330: Support Ticket Mental Model
20
+ - Line 366: Efficiency Rules
21
+ - Line 378: Account Repository Index
22
+ - Line 403: Two Workflows
23
+ - Line 410: Test Mode vs Prod Mode
24
+ - Line 442: Platform Mental Model: Front Stage vs Back Stage
25
+ - Line 449: Documents vs Records
26
+ - Line 459: HTTP Audits
27
+ - Line 513: Record `content` / Context Mental Model
28
+ - Line 533: Provenance Fields
29
+ - Line 551: Why Persisted Context Looks Different From Runtime Context
30
+ - Line 572: Investigation Rule for Record Context
31
+ - Line 587: AI Request Response Records
32
+ - Line 594: AI Session Groupings
33
+ - Line 613: Correlation Keys
34
+ - Line 619: Node Reference Table
35
+ - Line 629: Repository vs External Context
36
+ - Line 636: Getting Started
37
+ - Line 638: Authentication
38
+ - Line 651: Host vs Data Mode
39
+ - Line 670: Tool Execution Lifecycle
40
+ - Line 724: Data Mode
41
+ - Line 734: Development Workflow
42
+ - Line 736: The Golden Rule: Writing Code Is Not Finishing the Job
43
+ - Line 748: Avoid Brittle Front-Stage Intelligence
44
+ - Line 761: The Development Fast Loop
45
+ - Line 928: User Confirmation Preferences
46
+ - Line 938: Component Resolution: Staging Cache vs DB (which "version" actually runs)
47
+ - Line 944: The three source layers + the compile cache
48
+ - Line 967: Staging cache key format
49
+ - Line 982: How the platform picks staged vs DB (the compile signature)
50
+ - Line 1004: When staged overrides apply
51
+ - Line 1028: Diagnosing which version is in play
52
+ - Line 1049: Stage / commit / clear with the MCP tools
53
+ - Line 1073: Stale after sync / commit (the in-memory compile cache)
54
+ - Line 1083: Account Resolution: how a request travels the account graph
55
+ - Line 1153: Branched Component Variants (per-account component overrides)
56
+ - Line 1161: The model (three moving parts)
57
+ - Line 1228: Which world does your working tree resolve? (read this before you run anything)
58
+ - Line 1260: Two levers, two different questions
59
+ - Line 1274: The SDLC is identical on a variant branch
60
+ - Line 1298: Promotion: getting the branch back into trunk
61
+ - Line 1340: Verifying as the subscriber
62
+ - Line 1359: Agents (Utility) on a variant branch
63
+ - Line 1371: Danger profile on a variant branch (different, not absent)
64
+ - Line 1399: Inspecting branches and drift
65
+ - Line 1431: Diagnosing a variant
66
+ - Line 1438: Production Support Workflow
67
+ - Line 1446: Investigation Strategy
68
+ - Line 1476: Presenting Findings
69
+ - Line 1484: Verifying a Production Issue Fix
70
+ - Line 1497: Tool Reference
71
+ - Line 1499: Execute a Tool
72
+ - Line 1528: `mcp_account_view`
73
+ - Line 1546: `mcp_firestore_search`
74
+ - Line 1584: `mcp_object_activity`
75
+ - Line 1593: `mcp_record_listing`
76
+ - Line 1618: `mcp_record_view`
77
+ - Line 1631: `mcp_ai_session_search`
78
+ - Line 1667: `mcp_run_action`
79
+ - Line 1711: `mcp_run_agent`
80
+ - Line 1747: `mcp_system_logs`
81
+ - Line 1770: `mcp_component_view`
82
+ - Line 1793: `mcp_component_grep`
83
+ - Line 1806: `mcp_support_ticket`
84
+ - Line 1855: `mcp_run_test`
85
+ - Line 1864: `mcp_component_edit`
86
+ - Line 1879: `mcp_component_create`
87
+ - Line 1893: `mcp_component_commit`
88
+ - Line 1901: `mcp_cache`
89
+ - Line 1913: Multi-Session Support
90
+ - Line 1933: Persistent Service, Control Center, and Agent Dispatch
91
+ - Line 1942: Starting the Service
92
+ - Line 1968: Control Center
93
+ - Line 1983: Agent Dispatch
94
+ - Line 2004: Configuring the Preferred Agent
95
+ - Line 2015: Local State Files
96
+ - Line 2063: Command Reference
97
+ - Line 2105: Troubleshooting
98
+ - Line 2141: When Something Doesn't Work as Expected
86
99
 
87
100
  `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
101
 
@@ -117,9 +130,16 @@ different implementations. These rules override the normal fast loop whenever th
117
130
 
118
131
  ### How the platform reconciles the repo into the database (the mechanism you must understand)
119
132
 
133
+ > **First, check which branch you are on.** Everything in this section describes a **TRUNK** sync. Syncing
134
+ > from a **non-trunk branch** is a different, much safer operation — it writes `ComponentVariant` overlays
135
+ > only and can never create, delete, rename, or overwrite a live component row. Run
136
+ > `remits-cli components status` to see which mode your working tree is in, and read
137
+ > "Branched Component Variants" for the variant-branch rules (which have their own hazard: tombstones).
138
+
120
139
  `remits-cli components sync` (and the sync phase of `components commit`) calls
121
- `GitHubClient.syncFromRepository`. It is a **full two-way reconcile in which the GitHub remote is authoritative
122
- over the database.** It reads the **remote repo ZIP — not your local working tree** — and:
140
+ `GitHubClient.syncFromRepository`. On the account's **trunk branch** it is a **full two-way reconcile in
141
+ which the GitHub remote is authoritative over the database.** It reads the **remote repo ZIP — not your
142
+ local working tree** — and:
123
143
 
124
144
  - **Match / update:** each component is matched to a DB row by the **numeric id prefix of its files**
125
145
  (`58_x.groovy` → component 58), not by name. Matching files overwrite that component's DB fields
@@ -132,6 +152,28 @@ over the database.** It reads the **remote repo ZIP — not your local working t
132
152
  "looks healthy" (contains `account-info.json` or `README.md`). Exempt from deletion: `auxiliary` components,
133
153
  README-purpose prompts, and AGENT-purpose prompts.
134
154
 
155
+ > ### `auxiliary: true` opts a component OUT of the repo entirely — in BOTH directions
156
+ >
157
+ > This is a silent trap, so know it before you author a sidecar. `auxiliary: true` does not merely
158
+ > "de-emphasize" a component:
159
+ >
160
+ > - **Repo → platform:** the sync **skips the file outright**. A `new_*` component whose `.meta.yml` says
161
+ > `auxiliary: true` is never created, so it **never gets a real id** and the file is never renamed. It
162
+ > looks like the sync silently ignored your work — because it did.
163
+ > - **Platform → repo:** the component's save hooks skip pushing source to GitHub, and repo initialization
164
+ > omits it.
165
+ > - It is also excluded from `getInformation()` (so AI agents do not discover it) and exempt from the
166
+ > deletion pass above.
167
+ >
168
+ > **Use `auxiliary: true` only for genuinely throwaway components** — ad-hoc reports, experiments, and the
169
+ > ephemeral fixtures a Test creates and deletes at runtime (those are created in Groovy with
170
+ > `auxiliary: true` and must never touch the repo).
171
+ >
172
+ > **Use `auxiliary: false` for anything durable** — above all a Test suite that is a regression guard. If you
173
+ > want it versioned in git, addressable by a stable id, or discoverable by another agent, it is not
174
+ > auxiliary. Symptom to recognize: *"I added `new_Foo.groovy`, synced, and it neither appeared on the account
175
+ > nor got renamed."* Check the sidecar's `auxiliary` flag first.
176
+
135
177
  **The single most important consequence:** the id in a component's **filename is load-bearing**. If a file's id
136
178
  no longer matches its DB row (a rename/renumber), the next sync will **create a duplicate at the new id and
137
179
  hard-delete the original at the old id**. If a component's files are missing from the repo at sync time, that
@@ -148,8 +190,9 @@ the repo must already mirror the live DB: every live component present at its re
148
190
  | `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
149
191
  | `mcp_component_edit` `mode:'stage'` | Redis staging cache for one component/field. | none |
150
192
  | `mcp_component_edit` `mode:'commit'` / `mcp_component_commit` `commit` | Writes the field(s) to the **live DB** for that exact existing component and clears its staging. Does not create/delete other components. | low (scoped to one known component) |
151
- | `remits-cli components sync` | **Server-side git→DB reconcile of the whole account** (create/update/**delete**/rename). Reads the pushed remote; ignores local files. | **high** |
152
- | `remits-cli components commit` | **One shot:** `git add -A` + commit + `git push` + **`components sync`** + `git pull --ff-only`. Blindly stages the *entire* working tree (including any drift) and reconciles it into prod. | **highest** |
193
+ | `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** |
194
+ | `remits-cli components sync` **on a variant branch** | Writes `ComponentVariant` overlays for that branch only. Never touches trunk rows, `account-info.json`, or the account's trunk branch. `--dry-run` reports the plan without writes. | medium (a missing file becomes a **tombstone** that hides the component from subscribers) |
195
+ | `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
196
  | `mcp_component_create` | Writes a **new** component row **directly to the DB** (immediate, new id). | medium (can create duplicates) |
154
197
 
155
198
  Key implications:
@@ -160,8 +203,9 @@ Key implications:
160
203
  `git commit → git push → components sync → git pull` sequence so each phase can be inspected.
161
204
  - **`components sync` acts on the pushed remote**, so local edits are invisible to it until committed **and
162
205
  pushed**, and a drifted **remote** is dangerous even when your local tree looks fine.
163
- - After a successful `components sync` / `components commit`, the server clears the full branch/user staging
164
- scope. This is the expected clean state: old Redis aliases should not keep shadowing the newly synced DB rows.
206
+ - After a successful non-dry-run `components sync` / `components commit`, the server clears the full
207
+ branch/user staging scope. This is the expected clean state: old Redis aliases should not keep shadowing
208
+ the newly synced DB rows. `components sync --dry-run` intentionally leaves staging untouched.
165
209
 
166
210
  ### Intended workflows
167
211
 
@@ -184,6 +228,9 @@ file is catastrophic.
184
228
 
185
229
  ### Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
186
230
 
231
+ - **You know which sync mode this branch selects.** `remits-cli components status` states it outright. On a
232
+ variant branch the id/delete/renumber checks below apply to the **overlay set** instead: confirm every
233
+ component absent from the branch is *meant* to be tombstoned for subscribers.
187
234
  - The user intends durable platform promotion now — not just local edits, staging, or verification.
188
235
  - `git status --short` shows only intended changes; every rename/delete is explained. **No component file has
189
236
  been renumbered to a different id.**
@@ -718,6 +765,12 @@ This is how every development task should flow:
718
765
  #### Step 1: Understand the Request
719
766
  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
767
 
768
+ **Also establish which world you are working in.** `remits-cli components status` reports whether the
769
+ working tree is a **trunk** checkout or a **variant branch** checkout — which decides both what your test
770
+ runs resolve and what a sync writes. If `account-info.json` carries a `componentBranches` section, branch
771
+ variants of these components exist: editing an origin component will drift them, so check
772
+ `remits-cli components branches` before changing shared code. See "Branched Component Variants".
773
+
721
774
  #### Step 2: Make the Change
722
775
  Edit component files under `components/`. This is local file editing — the platform doesn't know about your changes yet.
723
776
 
@@ -888,16 +941,28 @@ When the platform executes a component it resolves the source from one of two pl
888
941
  behind an in-memory cache. Understanding this is the difference between "my change isn't working" guesses
889
942
  and a precise diagnosis.
890
943
 
891
- ### The two source layers + the compile cache
944
+ ### The three source layers + the compile cache
892
945
 
893
946
  1. **CLI staging cache (Redis, 240-min TTL).** Branch + user + account scoped overrides written by
894
- `remits-cli components stage` and by the `mcp_component_edit` tool (`mode:'stage'`). These shadow the DB
895
- source **only during CLI/test-mode execution** (see "When staged overrides apply" below).
896
- 2. **Database (the committed live component).** What `mcp_component_view` reads, what a pure prod run uses,
897
- and what `commit` writes to.
898
- 3. **Compiled-closure cache (`BaseClosureDomain.CLOSURE_CACHE`).** An in-memory, **per-JVM-instance** Guava
899
- cache of the parsed closure, keyed by `(componentId, type, compileSignature)`. This is why a change that
900
- is correctly in the DB can still execute stale on a running instance — see "Stale after sync" below.
947
+ `remits-cli components stage` and by the `mcp_component_edit` tool (`mode:'stage'`). These shadow the
948
+ layers below **only during CLI/test-mode execution** (see "When staged overrides apply" below).
949
+ 2. **Committed branch variants (`ComponentVariant`, MySQL).** Durable, branch-scoped overlays of a
950
+ component. Unlike staging these are **not** user-scoped, do **not** expire, and **do** apply to normal
951
+ production traffic — for the accounts that subscribe to that branch. See
952
+ "Branched Component Variants" below. Most accounts have none, in which case this layer is inert.
953
+ 3. **Database trunk row (the committed live component).** What `mcp_component_view` reads, what an
954
+ unsubscribed prod run uses, and what a trunk `commit` writes to.
955
+
956
+ Resolution order is **staged → variant → trunk**, and each layer *layers over* the one beneath it rather
957
+ than replacing it: a payload that only carries `source` inherits `path`, `objectType`, `inputSchema` etc.
958
+ from the layer below. A staged edit made on a variant branch therefore layers over **that variant**, not
959
+ over trunk.
960
+
961
+ Plus the compile cache:
962
+
963
+ - **Compiled-closure cache (`BaseClosureDomain.CLOSURE_CACHE`).** An in-memory, **per-JVM-instance** Guava
964
+ cache of the parsed closure, keyed by `(componentId, type, compileSignature)`. This is why a change that
965
+ is correctly in the DB can still execute stale on a running instance — see "Stale after sync" below.
901
966
 
902
967
  ### Staging cache key format
903
968
 
@@ -909,17 +974,23 @@ account:<accountId>:cli:<cliUserId>:components:<branch>:<family>:name:<normalize
909
974
  `<family>` is the lowercased component family (`reader`, `action`, `test`, `embeddable`, ...). Both an
910
975
  `id:` and a `name:` key are written per stage. The entry value carries: `kind` (the family), `type` (the
911
976
  component's OWN type enum such as `ObjectType`/`RuleType`, or absent — **never** the family), `hash`,
912
- `updatedAt`, and the staged content field(s) (`source`/`prompt`/`html`/`javascript`/`schema`/
913
- `inputSchema`/`previewData`/`path`).
977
+ `updatedAt`, the staged content field(s) (`source`/`prompt`/`html`/`javascript`/`schema`/
978
+ `inputSchema`/`previewData`), and `.meta.yml` metadata fields such as `description`, `summary`, `mermaid`,
979
+ `path`, `category`, and Schema flags (`enableTrigger`, `enableFullText`, `enableRAG`, `enableRevisions`,
980
+ `enableBigQuerySync`, `enableRules`, `anchor`, `auxiliary`).
914
981
 
915
982
  ### How the platform picks staged vs DB (the compile signature)
916
983
 
917
984
  At compile time the platform computes a **signature** that tells you which layer won:
918
985
 
919
986
  - Staged override present → `compileSignature = "cli:<hash>"` (the staged content hash).
920
- - No staged override → `compileSignature = "version:<N>:<sourceHash12>"` (the DB row version plus a source hash
987
+ - Committed branch variant → `compileSignature = "variant:<variantId>:<hash12>"`.
988
+ - Neither → `compileSignature = "version:<N>:<sourceHash12>"` (the DB row version plus a source hash
921
989
  prefix, so source changes cannot reuse a stale compile entry on the same instance).
922
990
 
991
+ The three namespaces are distinct on purpose: a component's staged, variant, and trunk closures coexist in
992
+ the compile cache without colliding.
993
+
923
994
  That signature is logged. Querying for it is the single most reliable way to know what ran:
924
995
 
925
996
  ```bash
@@ -927,7 +998,8 @@ remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","tim
927
998
  ```
928
999
 
929
1000
  `Using Cached BCD [ID: 230, Type: Action, Signature: cli:08cc...]` → ran a **staged** override.
930
- `...Signature: version:37:abc123def456]` → ran the **committed DB** version.
1001
+ `...Signature: variant:14:9f2c1a...]` → ran a **committed branch variant**.
1002
+ `...Signature: version:37:abc123def456]` → ran the **committed trunk** version.
931
1003
 
932
1004
  ### When staged overrides apply
933
1005
 
@@ -1008,6 +1080,361 @@ standard Grails no-hot-reload caveat — it is environmental, not a code defect.
1008
1080
  with `mcp_cache` / `mcp_component_view`, and if a platform/tool source change must take effect immediately,
1009
1081
  the platform owner recycles the instance.
1010
1082
 
1083
+ ## Account Resolution: how a request travels the account graph
1084
+
1085
+ Component inheritance, branch variants, and where data physically lives are all decided by **how the
1086
+ current request reached the executing account**. Read this before debugging "my subscriber isn't picking
1087
+ up the branch" or "why is this account reading the wrong collection" — those are almost always
1088
+ resolution questions, not component bugs.
1089
+
1090
+ **Two kinds of structural edge.**
1091
+
1092
+ - **`Account.parentId`** — the legacy primary parent, and still the primary structural edge. An ordinary
1093
+ single-parent account with no branch subscription resolves exactly as it did before relationships
1094
+ existed. Most production accounts are this shape.
1095
+ - **`AccountRelationship` edges** — a join table letting one account be reached through **more than one**
1096
+ parent. Exactly one edge per account is `primary` (kept in lockstep with `parentId`); the rest are
1097
+ **membership** edges. Each edge can independently carry:
1098
+ - `branchName` — the component-variant branch this account subscribes to (see the next section);
1099
+ - `databaseName` — a branch-scoped data-segment override;
1100
+ - `domainName` — a branch-scoped custom host that reaches this account **through this edge**.
1101
+ These are **orthogonal**: subscribing to a component branch never moves an account's data, setting a
1102
+ segment override never changes which code runs, and a custom host changes neither. Do not reason about
1103
+ one from the others.
1104
+
1105
+ **Custom hosts resolve the anchor, not just the account.** An account can have its own
1106
+ `Account.domainName`, and an edge can carry one too. A request arriving on an **edge** host resolves the
1107
+ edge's *child* as the execution account **and** the edge's *parent* as the branch anchor — which is what
1108
+ makes that edge's branch variants apply. An account-level host resolves the account with **no** anchor
1109
+ (today's behavior). Edge wins, then the account's own host, so removing an edge degrades cleanly instead
1110
+ of taking the hostname offline. Hosts are stored as bare lowercase hostnames; a full URL is normalized on
1111
+ the way in. This is what lets a forked/branch deployment get its own domain without a separate account
1112
+ tree — `forked.example.com` and `app.example.com` can serve the same owner's components, one overlaid
1113
+ with a branch.
1114
+
1115
+ **Two traversals, deliberately different.** Confusing them is the usual source of wrong conclusions:
1116
+
1117
+ | | Used for | Shape |
1118
+ |---|---|---|
1119
+ | **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 |
1120
+ | **Union reachability** | authorization, "may this caller act on that account" (`--as-account`, tokens) | **permissive** union over `parentId` *and* every active edge |
1121
+
1122
+ **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id` (stamped
1123
+ onto the `Object`/`Event`/`Alert`/`ObjectLog` records a run creates, so async workers re-resolve on the
1124
+ same branch). A **null** anchor means "walk the primary `parentId` chain" — today's legacy behavior.
1125
+
1126
+ **Default-anchor derivation is deliberately conservative, and this is the #1 gotcha:**
1127
+
1128
+ | Account shape | Derived anchor |
1129
+ |---|---|
1130
+ | has a `parentId` | `null` → primary chain (legacy, unchanged) |
1131
+ | no `parentId`, **exactly one** membership edge | that edge's parent |
1132
+ | no `parentId`, **multiple** membership edges | `null` — **ambiguous, refuses to guess** |
1133
+
1134
+ So a `parentId`-less account with two membership edges resolves **trunk and inherits nothing** until an
1135
+ anchor is supplied. That is correct-by-design, not a bug. Entry points that already know the branch
1136
+ disambiguate it by branch name; from the CLI you supply it explicitly.
1137
+
1138
+ **Same account, two parents, two answers.** An account reached through Product A versus Product B can
1139
+ legitimately resolve a different component variant *and* a different data segment. When you investigate
1140
+ such an account, always establish which anchor the failing request used before comparing behavior.
1141
+
1142
+ **Practical checklist when a subscription "doesn't work":**
1143
+
1144
+ 1. Does the account have a `parentId`, or is it membership-only? (Membership-only + multiple edges ⇒
1145
+ ambiguous ⇒ trunk.)
1146
+ 2. Which **edge** carries the `branchName` — `remits-cli components branch <name> --subscribers` prints
1147
+ `via primary|membership edge -> parent N`.
1148
+ 3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
1149
+ account's own edge selects the branch, exactly as production would.
1150
+ 4. Check the resolved layer, not the source text: `testComponentSource` / the `variant:<id>:<hash>`
1151
+ compile signature (see "Diagnosing a variant").
1152
+
1153
+ ## Branched Component Variants (per-account component overrides)
1154
+
1155
+ Sometimes one account — often a customer nested several levels down a hierarchy — needs *slightly*
1156
+ different behavior from a component owned by its platform or product account. The wrong answer is
1157
+ per-account `if/then` logic inside the origin component. The right answer is a **branch variant**: a
1158
+ durable, branch-scoped overlay of that component, which only the accounts subscribed to that branch
1159
+ resolve.
1160
+
1161
+ ### The model (three moving parts)
1162
+
1163
+ 1. **The origin account owns the component and the branch.** Say platform account 1 owns
1164
+ `Extract Invoice` (Action 50). Its repo `remits-<name>` has trunk branch `main` and a second git branch
1165
+ `feature_forked`.
1166
+ 2. **A `ComponentVariant` row is the overlay.** Committing on `feature_forked` stores rows owned by
1167
+ **account 1**, on branch `feature_forked`, for the components whose content **differs from trunk**. A git
1168
+ branch physically contains every file; only the *differing* ones become variants. That is computed at
1169
+ sync time — you never declare it.
1170
+ 3. **A child account subscribes via its relationship edge.** Account 101's `AccountRelationship` edge
1171
+ carries `branchName = 'feature_forked'`. Resolution then walks 101's inheritance chain and applies
1172
+ account 1's `feature_forked` overlays.
1173
+
1174
+ Consequences worth internalizing:
1175
+
1176
+ - **A branch is not an account.** Subscription is many-to-many: five accounts can share one branch, and a
1177
+ subscribing account still has its own trunk components, which merge on top as usual.
1178
+ - **Nested hierarchies work.** The overlay applies to the subscribing account *and its descendants*, until a
1179
+ nearer edge overrides it. Resolution consults every owner on the inheritance path, so a deeply nested
1180
+ client picks up a platform-owned variant.
1181
+ - **Component identity is shared.** A variant keeps the origin's component id — it is an overlay, not a
1182
+ copy. That is what makes drift detectable and what distinguishes this from just duplicating the component
1183
+ onto the child account.
1184
+ - **Subscribing an account** attaches the branch to that account's relationship edge:
1185
+ `remits-cli components branch <name> --subscribe <accountId> [--domain <host>]` (and `--unsubscribe <accountId>` to return it
1186
+ to trunk). It is also editable per-edge on the admin account page. Authorization is downward-only: you can
1187
+ only subscribe an account reachable from one you already have access to.
1188
+ - **`--subscribe` sets the branch on an edge that must already exist — it cannot create one.** If the
1189
+ account has no relationship edge to the owner you will get *"Account N has no relationship edge to
1190
+ subscribe; add a membership edge first"*. Creating a membership edge is an **admin** operation (the
1191
+ account page), not a CLI one.
1192
+ - **When the account has several parents, say which edge you mean:**
1193
+ `--subscribe <accountId> --parent-account <ownerId>`. Without it the CLI picks the edge to the owner
1194
+ whose branch you are managing, then falls back to the account's primary edge — which may not be the
1195
+ edge you intended. `--subscribers` prints `via primary|membership edge -> parent N` so you can confirm.
1196
+ - **Retiring a branch** is explicit: `remits-cli components branch <name> --retire`. Deleting the *git*
1197
+ branch does **not** remove its overlays — subscribers would keep resolving a branch that no longer exists.
1198
+ Retire refuses while the branch still has subscribers unless you pass `--force`.
1199
+
1200
+ A branch can also **add** a component (a file whose prefix is not a live trunk id — e.g. `new_Foo.groovy` —
1201
+ becomes a *branch-only* component identified by name) and **remove** one (a trunk component with no file on
1202
+ the branch becomes a *tombstone*, hiding it from subscribers).
1203
+
1204
+ **Two sharp edges worth knowing before you edit a branch:**
1205
+
1206
+ - **Deleting a file means "remove the component", not "stop overriding it."** To withdraw an override, make
1207
+ the file identical to trunk again — the sync then removes the variant row. Deleting it creates a tombstone
1208
+ and hides the component from every subscriber.
1209
+ - **Keep the branch rebased.** A branch physically carries every file, so anything trunk added *after* the
1210
+ branch was cut looks like a deliberate deletion. The sync refuses a wholesale removal (more than ~a third
1211
+ of a kind) and tells you to rebase, but a small stale gap will tombstone silently. Rebase onto trunk before
1212
+ you commit a branch you have not touched in a while.
1213
+
1214
+ **Every component kind can be varied** — `schema`, `reader`, `action`, `embeddable`, `htmltemplate`,
1215
+ `rule`, `test`, `utility` (agent), `tool`, `prompt` — including tombstones and branch-only additions. A
1216
+ schema variant is worth calling out: it can change the JSON schema itself *and* `collectionName`, so it
1217
+ changes validation and where the subscriber's documents physically land. Treat schema variants with the
1218
+ same care as a trunk schema change.
1219
+
1220
+ **What a branch may override.** A variant speaks the same `.meta.yml` vocabulary trunk does — `name`,
1221
+ `description`, `summary`, `mermaid`, `category`, `type`, `path`, `collectionName`, `job`, `model`,
1222
+ `agentTimeout`, `mcp`, `cli`, `global`, `purpose`, `auxiliary`, plus Schema flags (`enableTrigger`,
1223
+ `enableFullText`, `enableRAG`, `enableRevisions`, `enableBigQuerySync`, `enableRules`, `anchor`) — and the
1224
+ component's content files. Keys outside that set are ignored, deliberately: trunk cannot express them either,
1225
+ so allowing them would mean a branch behaves one way and silently loses that behavior the moment it is
1226
+ promoted to trunk.
1227
+
1228
+ ### Which world does your working tree resolve? (read this before you run anything)
1229
+
1230
+ You will work from **two different checkouts of the same repo**, and they behave differently on both ends of
1231
+ the loop. The rule turns entirely on **trunk vs non-trunk**:
1232
+
1233
+ | Working tree | Staging scope | A run resolves | `components sync`/`commit` writes |
1234
+ |---|---|---|---|
1235
+ | **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** |
1236
+ | **any other branch** (`feature_forked`) | that branch | trunk + **`feature_forked`** overlays | **`ComponentVariant` overlays on that branch only** — never touches trunk rows |
1237
+
1238
+ **The precedence trap that costs the most time:** `variantBranch` OUTRANKS every account's subscription. So
1239
+ running a Test suite that asserts *production* semantics from a **variant checkout** pins every account in
1240
+ that suite — including fixture accounts that subscribe to their own generated branches — to your branch,
1241
+ where they have no variants, and they all read trunk. The suite fails in a way that looks exactly like a
1242
+ resolution regression. (Live example: the branch-variant suite scored 4/15 from a variant checkout and 15/15
1243
+ from trunk, with no code difference.) **Run branch-variant suites from trunk, or pass `--variant-branch
1244
+ none`.** Before concluding "variant resolution is broken", re-run from trunk.
1245
+
1246
+ Do not infer this from the branch name. Ask:
1247
+
1248
+ ```bash
1249
+ remits-cli components status
1250
+ ```
1251
+
1252
+ ```
1253
+ Working tree: VARIANT BRANCH "feature_forked" (trunk is "main")
1254
+ runs resolve: trunk + the 'feature_forked' variant overlays
1255
+ commit writes: ComponentVariant overlays on 'feature_forked' (never touches trunk rows)
1256
+ variants stored on this branch: 3
1257
+ subscribing accounts: 101 (Acme Child)
1258
+ ```
1259
+
1260
+ ### Two levers, two different questions
1261
+
1262
+ - **`--as-account <id>`** changes **who** the run executes as, so that account's own edge picks the branch.
1263
+ Answers *"what does customer X actually get?"* Only narrows downward (the target must be reachable from an
1264
+ account you already have access to).
1265
+ - **`--variant-branch <name>`** changes **which branch**, from any checkout. Answers *"what does branch Y
1266
+ look like?"* — most useful for verifying a freshly committed branch **before** any edge subscribes to it.
1267
+ `--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the branch.
1268
+ The flag is available on `test run`, `token`, `tools`, and `tool`, so tests, browser URLs, tool discovery,
1269
+ and tool execution can all inspect the same committed variant world.
1270
+
1271
+ Both work on `remits-cli test run` and `remits-cli token`; `--variant-branch` also works on
1272
+ `remits-cli tools` and `remits-cli tool` for branch-specific tool discovery and execution.
1273
+
1274
+ ### The SDLC is identical on a variant branch
1275
+
1276
+ The loop does not change shape — `edit → stage → verify → commit`:
1277
+
1278
+ ```bash
1279
+ git checkout feature_forked # or: git checkout -b feature_forked
1280
+ # edit components/actions/50_ExtractInvoice.groovy (KEEP the trunk id)
1281
+ remits-cli components stage # Redis, scoped to this branch — same as always
1282
+ remits-cli test run --test "Invoice Tests" # resolves the feature_forked world
1283
+ remits-cli test run --test "Invoice Tests" --as-account 101 # ...as the real subscriber
1284
+ # promote:
1285
+ git add -A && git commit -m "..." && git push origin feature_forked
1286
+ remits-cli components sync --dry-run # inspect overrides/additions/tombstones without writes
1287
+ remits-cli components sync # writes ComponentVariant overlays ONLY
1288
+ ```
1289
+
1290
+ Two things that differ from trunk:
1291
+
1292
+ - **Keep the origin id in the filename.** `50_ExtractInvoice.groovy` on the branch overlays Action 50. That
1293
+ is what preserves component identity and lets drift be computed against the origin.
1294
+ - **Nothing is renamed.** A variant sync never renames files, never regenerates `account-info.json`, and
1295
+ never repoints the account's trunk branch. A `new_*` file stays `new_*` and becomes a branch-only
1296
+ component keyed by name.
1297
+
1298
+ ### Promotion: getting the branch back into trunk
1299
+
1300
+ The branch is the cheap half. Promotion is where the sharp edges are, and it is a **git** operation followed
1301
+ by a **trunk** sync — the platform does not merge anything for you.
1302
+
1303
+ ```bash
1304
+ # 1. merge the branch into trunk (review the diff FIRST - see the deletion hazard below)
1305
+ git checkout main && git merge feature_forked && git push origin main
1306
+ # 2. trunk sync: creates real rows for new_* files, assigns ids, RENAMES those files on trunk
1307
+ remits-cli components sync
1308
+ git pull --ff-only origin main
1309
+ # 3. bring trunk back into the branch so the two stop diverging
1310
+ git checkout feature_forked && git merge origin/main && git push origin feature_forked
1311
+ remits-cli components sync # reconciles the branch's overlays
1312
+ ```
1313
+
1314
+ **What step 3 reconciles, and why you must run it.** A branch-only `new_Foo.groovy` becomes trunk component
1315
+ `123` and is renamed `123_Foo.groovy` **on trunk only**. The branch still holds the pre-promotion file, so
1316
+ the sync handles both shapes automatically:
1317
+
1318
+ | Branch state after promotion | What the sync does |
1319
+ |---|---|
1320
+ | only `new_Foo.*` | **adopts** trunk id 123 -> identical content -> the overlay is **removed** (`unchanged`, `pruned:true`) |
1321
+ | 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 |
1322
+
1323
+ Skip step 3 and the branch keeps a **branch-only** overlay for a component that now has a trunk row: it can
1324
+ never converge (there is no trunk id to compare against), so subscribers stay pinned to the promoted copy
1325
+ forever and every later trunk improvement is invisible to them.
1326
+
1327
+ > **A tombstone is a DELETED FILE, so merging a variant branch into trunk promotes its removals.** On the
1328
+ > branch, a missing file only *hides* a component from subscribers. Merged into trunk and synced, that same
1329
+ > missing file **hard-deletes the component for everyone**. Always read
1330
+ > `git diff --stat origin/main HEAD -- components/` before pushing a promotion and confirm every deletion is
1331
+ > intended. To promote only part of a branch, restore the files you are not promoting
1332
+ > (`git checkout origin/main -- <path>`) before the trunk sync.
1333
+
1334
+ **Trunk moving also invalidates the branch.** Variant sparseness compares branch content against *current*
1335
+ trunk, so a trunk change can make a branch overlay obsolete without the branch changing at all. A trunk sync
1336
+ now drops the affected branches' cached sync verdicts, so the next `components sync` on the branch really
1337
+ re-evaluates instead of answering *"No changes detected"*. After promoting anything, re-sync each live
1338
+ branch.
1339
+
1340
+ ### Verifying as the subscriber
1341
+
1342
+ `--as-account <id>` runs as the subscriber so its own edge selects the branch. It works for a
1343
+ `parentId`-less, membership-only subscriber too: the anchor is derived from the branch you name
1344
+ (`--variant-branch`) or, failing that, from the account's single branch subscription.
1345
+
1346
+ It **refuses to guess** when an account has several edges each carrying a different branch - the run then
1347
+ resolves trunk. Name the branch with `--variant-branch <name>` to disambiguate, or check the edges with
1348
+ `components branch <name> --subscribers` (it prints `via primary|membership edge -> parent N`).
1349
+
1350
+ The strongest end-to-end proof for a UI-visible variant is a token, not a log line:
1351
+
1352
+ ```bash
1353
+ remits-cli token --path embeddable/index/50 --as-account 101 # subscriber -> variant
1354
+ remits-cli token --path embeddable/index/50 # owner -> trunk
1355
+ ```
1356
+
1357
+ Open both; if the branch changes anything visible, you will see it immediately.
1358
+
1359
+ ### Agents (Utility) on a variant branch
1360
+
1361
+ A `Utility` is an **Agent** in practice — the repo directory is `components/agents/` and there is no
1362
+ `components/utilities/`. Agent variants work, with two things to know:
1363
+
1364
+ - **Override the sidecar the same way you would on trunk.** `components/agents/19_Bob.meta.yml` keys
1365
+ (`model:`, `agentTimeout:`, `type:`) and the `.md` prompt sidecar all overlay correctly, as does the
1366
+ `.groovy` source. A branch-only agent (a `new_*` file) defaults to `type: AI` so agent lookups find it.
1367
+ - **An agent's TOOL LIST cannot be overridden by a variant.** `tools:` in the sidecar maps to a GORM
1368
+ association, and `agent()` populates it from the **trunk** row. A `tools:` list on a variant branch is
1369
+ ignored. If a branch needs a different tool set, change trunk or have the agent choose tools at runtime.
1370
+
1371
+ ### Danger profile on a variant branch (different, not absent)
1372
+
1373
+ The Component Integrity Rules' worst case — *"a missing file hard-deletes a live component"* — **does not
1374
+ apply on a variant branch.** Variant sync writes overlay rows only; it cannot create, delete, rename, or
1375
+ overwrite a trunk component. That makes a variant branch a genuinely safer place to iterate.
1376
+
1377
+ The analogous hazard is different and you must still respect it:
1378
+
1379
+ - **A trunk component with no file on the branch becomes a TOMBSTONE**, which *hides* that component from
1380
+ every subscriber. It is reversible (restore the file and re-sync) and it never touches trunk — but to a
1381
+ subscribing account it looks exactly like the component was deleted. Before syncing a variant branch,
1382
+ confirm every omission is deliberate.
1383
+ - **Use `components sync --dry-run` before risky variant syncs.** It reports `overridden`, `added`,
1384
+ `removed`, `unchanged`, `skipped`, and `errors` without writing variant rows, caching the sync SHA, or
1385
+ clearing staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means
1386
+ the branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
1387
+ `components commit --dry-run` is unsupported because `commit` performs local git writes before server sync.
1388
+ - **Deleting a trunk component cascades**: its overlays on every branch are removed with it.
1389
+ - **A removal you did not author usually means TRUNK is drifted, not that the branch deleted something.** A
1390
+ component that exists in the DB with **no file on trunk** is missing from every branch too, so it shows up
1391
+ as a phantom `removed` in *every* branch preview - while the trunk sync separately tries to hard-delete it
1392
+ on every run. Check whether the file exists on trunk before "fixing" it on the branch; the repair is to
1393
+ mirror the live DB source back into the trunk repo (Component Integrity Rules), not to touch the branch.
1394
+ - **The sync refuses a wholesale removal.** Above ~a third of a kind - or **100% of a kind at any size** - it
1395
+ aborts that kind, reports why, and points at a rebase. Rebasing onto trunk is almost always the real fix; a
1396
+ branch not rebased in a while is missing everything trunk has added since it was cut. Only when the
1397
+ removals are genuinely deliberate, re-run with `--force-tombstones`.
1398
+
1399
+ ### Inspecting branches and drift
1400
+
1401
+ ```bash
1402
+ remits-cli components branches # branches with variants, counts, drift
1403
+ remits-cli components branch feature_forked # overridden / added / removed + subscribers
1404
+ remits-cli components branch feature_forked --diff 50 --component-type action
1405
+ remits-cli components branch feature_forked --subscribers
1406
+ ```
1407
+
1408
+ **Drift is the number to watch.** Each variant records the trunk content hash at the moment it was cut
1409
+ (`originHash`). When the origin component later changes, the variant is reported **DRIFTED** — the branch is
1410
+ now based on a stale version of the origin and someone should reconcile it. `branches` shows a per-branch
1411
+ drift count; `branch <name>` flags each drifted component; `--diff` shows variant vs current trunk.
1412
+
1413
+ Before editing an origin component, check whether variants of it exist — the owner account's
1414
+ `account-info.json` carries a `componentBranches` summary, `components branches` gives the live view, and
1415
+ the admin edit forms show a branch-variant notice for trunk components that already have variants. A change
1416
+ to the origin silently drifts every branch that overlays it.
1417
+
1418
+ **`components branches` only lists branches that already have overlays.** A branch you just pushed is
1419
+ invisible here until its first sync - that is not an error. Preview it by name
1420
+ (`components sync --dry-run` from that checkout), or from the admin GitHub menu's branch picker.
1421
+
1422
+ **The admin UI has the same surface**, which is what to point a user at:
1423
+ - *owner account page* -> **Component branches** box: per-branch counts, drift, subscribers, and
1424
+ **Preview / Sync / Retire**;
1425
+ - *subscriber account page* -> **Branch variants** box (what THIS account resolves) and **GitHub -> Fetch
1426
+ `<branch>` variants**;
1427
+ - both open the same result panel - grouped plan, subscribers, a **Monaco diff of trunk vs branch** per
1428
+ changed field, and a confirm-gated override when the removal guard refuses.
1429
+ Preview there is the same `--dry-run`, so it is safe to hand to a non-CLI user.
1430
+
1431
+ ### Diagnosing a variant
1432
+
1433
+ - `remits-cli components branch <name> --json` — the stored overlay content and what it overrides.
1434
+ - The compile signature (`variant:<id>:<hash>`) in `Using Cached BCD` logs — proves a variant actually ran.
1435
+ - A test/tool response reports `testComponentSource` as `staged` | `variant` | `db`, so you can see which
1436
+ layer the run resolved without reading logs.
1437
+
1011
1438
  ## Production Support Workflow
1012
1439
 
1013
1440
  Switch to prod mode for investigations:
@@ -1101,6 +1528,17 @@ So from inside a parent account's repo you can target a child account just by se
1101
1528
  ### `mcp_account_view`
1102
1529
  Returns complete account structure — schemas, components, relationships.
1103
1530
 
1531
+ Also returns two blocks that explain how the account RESOLVES, which is what you need before comparing
1532
+ its behavior against component source:
1533
+
1534
+ - `resolution` — `databaseName` (the account's own override, often null) vs `resolvedDatabaseName` (the
1535
+ storage namespace actually in effect), `branchName` (the repo sync branch), `domainName` vs
1536
+ `resolvedDomainName` (the custom host in effect, which differs when reached through an edge host),
1537
+ `editMode`, and — when the account subscribes to a component branch — `componentBranch` plus
1538
+ `componentBranchOwnerAccountId`.
1539
+ - `componentBranches` — the variant branches this account OWNS, with override/add/remove, subscriber, and
1540
+ drift counts. Same summary the owner's `account-info.json` carries.
1541
+
1104
1542
  | Parameter | Required | Description |
1105
1543
  |-----------|----------|-------------|
1106
1544
  | `accountId` | yes | Account ID |
@@ -1347,6 +1785,11 @@ are absent from `remits-cli tools`, that is expected.
1347
1785
  | `offset` | no | Start line (1-indexed). Also accepts `startLine`. |
1348
1786
  | `limit` | no | Number of lines to return |
1349
1787
 
1788
+ Returns `componentVariants` when the component has committed branch variants — the branches, their state
1789
+ (`current` / `drifted` / `removed`), and a warning. Non-null means some accounts run a different version
1790
+ than the source you are reading, and editing here changes trunk only and drifts those variants.
1791
+ `mcp_component_grep` searches trunk, so it will not match text that exists only in a variant.
1792
+
1350
1793
  ### `mcp_component_grep`
1351
1794
  Search component source code with regex.
1352
1795
 
@@ -1631,18 +2074,33 @@ remits-cli data-mode [set test|prod]
1631
2074
  remits-cli components stage [--branch <name>] [--data-mode test|prod] [--json|--verbose]
1632
2075
  remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]
1633
2076
  remits-cli components clear [--branch <name>] [--component-type <type>] [--component-id <id>] [--all] [--json|--verbose] # id alone scopes to one component when unambiguous (ids are type-local; add --component-type if the same id is staged in multiple families); no filter clears the whole branch scope; --all forces the full wipe
1634
- remits-cli components sync [--branch <name>] [--data-mode test|prod] # gated durable repo->DB reconciliation only
1635
- remits-cli components commit [--message "msg"] [--data-mode test|prod]
1636
- remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod]
1637
- remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod]
1638
- remits-cli tools [--branch <name>] [--data-mode test|prod]
1639
- remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--timeout-ms 60000] [--async true --wait true]
2077
+ 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
2078
+ remits-cli components commit [--message "msg"] [--data-mode test|prod] [--force-tombstones]
2079
+ remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
2080
+ remits-cli components branch <name> [--json] # one branch: overridden / added / removed, drift flags, subscribers
2081
+ remits-cli components branch <name> --diff <componentId> --component-type <kind> [--json]
2082
+ remits-cli components branch <name> --subscribers [--json]
2083
+ remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>] # make an account resolve this branch
2084
+ remits-cli components branch <name> --unsubscribe <accountId> # return that account to trunk
2085
+ remits-cli components branch <name> --retire [--force] # delete the branch's overlays
2086
+ remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
2087
+ remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
2088
+ remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
2089
+ remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
1640
2090
  remits-cli tool status --call-id <callId> [--data-mode test|prod]
1641
2091
  ```
1642
2092
 
1643
2093
  For tests specifically:
1644
2094
  - If `--data-mode` is omitted, `remits-cli test run` uses `test`.
1645
2095
  - `--names` is comma-delimited, so keep individual test names comma-free.
2096
+ - `--as-account <ID>` runs AS a descendant subscriber so its edge selects the component branch
2097
+ (*"what does customer X get?"*); `--variant-branch <name>` probes a branch from any checkout
2098
+ (*"what does branch Y look like?"*), and `--variant-branch none` forces production/subscription semantics.
2099
+ Omit both and the working tree decides — see "Branched Component Variants".
2100
+ - `--force-tombstones` is only for non-trunk variant syncs, when missing trunk component files are known,
2101
+ intentional tombstone overrides. It is rejected on trunk.
2102
+ - `--dry-run` is only for `components sync` on non-trunk variant branches. It reports the variant write
2103
+ plan without writing rows, caching the sync SHA, or clearing staging.
1646
2104
 
1647
2105
  ## Troubleshooting
1648
2106
 
@@ -1661,7 +2119,16 @@ For tests specifically:
1661
2119
  | Tool response missing | Check `./.remits-cli/tool-responses/` |
1662
2120
  | 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
2121
  | 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". |
2122
+ | 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". |
2123
+ | 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. |
2124
+ | 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. |
2125
+ | 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. |
2126
+ | 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. |
2127
+ | `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. |
2128
+ | 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. |
2129
+ | 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. |
2130
+ | `--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`. |
2131
+ | 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
2132
  | 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
2133
  | 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
2134
  | 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. |