@remits/remits-cli 0.1.90 → 0.1.92

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,121 +7,124 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
7
7
 
8
8
  ## Table of Contents
9
9
 
10
- - Line 127: Account Targeting Model
11
- - Line 134: Account types
12
- - Line 149: How accounts connect (and why it changes what runs)
13
- - Line 179: Users are a separate model — don't reason about them as a tree
14
- - Line 192: Reading the shape
15
- - Line 218: Repo selection rules
16
- - Line 230: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
17
- - Line 236: How the platform reconciles the repo into the database (the mechanism you must understand)
18
- - Line 298: The surfaces and their intended behavior
19
- - Line 322: Intended workflows
20
- - Line 343: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
21
- - Line 356: If something looks wrong — stop, don't paper over
22
- - Line 380: Required Local Index Reads
23
- - Line 414: Big Picture: How remits-cli State Is Organized
24
- - Line 444: Support Ticket Mental Model
25
- - Line 480: Efficiency Rules
26
- - Line 492: Account Repository Index
27
- - Line 517: Two Workflows
28
- - Line 524: Test Mode vs Prod Mode
29
- - Line 556: Platform Mental Model: Front Stage vs Back Stage
30
- - Line 563: Documents vs Records
31
- - Line 573: HTTP Audits
32
- - Line 627: Record `content` / Context Mental Model
33
- - Line 647: Provenance Fields
34
- - Line 665: Why Persisted Context Looks Different From Runtime Context
35
- - Line 686: Investigation Rule for Record Context
36
- - Line 701: AI Request Response Records
37
- - Line 708: AI Session Groupings
38
- - Line 727: Correlation Keys
39
- - Line 733: Node Reference Table
40
- - Line 743: Repository vs External Context
41
- - Line 750: Getting Started
42
- - Line 752: Authentication
43
- - Line 765: Host vs Data Mode
44
- - Line 784: Tool Execution Lifecycle
45
- - Line 838: Data Mode
46
- - Line 848: Development Workflow
47
- - Line 850: The Golden Rule: Writing Code Is Not Finishing the Job
48
- - Line 862: Avoid Brittle Front-Stage Intelligence
49
- - Line 875: The Development Fast Loop
50
- - Line 879: Step 1: Understand the Request
51
- - Line 894: Step 2: Make the Change
52
- - Line 932: Step 3: Stage to Platform
53
- - Line 947: Step 4: Verify the Change
54
- - Line 1012: Step 5: Iterate If Needed
55
- - Line 1022: Step 6: Update Documentation
56
- - Line 1034: Step 7: Commit and Durable Sync
57
- - Line 1072: Step 8: Close the Ticket
58
- - Line 1087: User Confirmation Preferences
59
- - Line 1097: Component Resolution: Staging Cache vs DB (which "version" actually runs)
60
- - Line 1103: The three source layers + the compile cache
61
- - Line 1126: Staging cache key format
62
- - Line 1141: How the platform picks staged vs DB (the compile signature)
63
- - Line 1163: When staged overrides apply
64
- - Line 1187: Diagnosing which version is in play
65
- - Line 1208: Stage / commit / clear with the MCP tools
66
- - Line 1232: Stale after sync / commit (the in-memory compile cache)
67
- - Line 1242: Account Resolution: how a request travels the account graph
68
- - Line 1335: Branched Component Variants (per-account component overrides)
69
- - Line 1343: The model (three moving parts)
70
- - Line 1410: Which world does your working tree resolve? (read this before you run anything)
71
- - Line 1476: Two levers, two different questions
72
- - Line 1490: The SDLC is identical on a variant branch
73
- - Line 1515: Promotion: getting the branch back into trunk
74
- - Line 1557: Verifying as the subscriber
75
- - Line 1576: Agents (Utility) on a variant branch
76
- - Line 1588: Danger profile on a variant branch (different, not absent)
77
- - Line 1616: Inspecting branches and drift
78
- - Line 1648: Diagnosing a variant
79
- - Line 1655: Production Support Workflow
80
- - Line 1663: Investigation Strategy
81
- - Line 1693: Presenting Findings
82
- - Line 1701: Verifying a Production Issue Fix
83
- - Line 1714: Tool Reference
84
- - Line 1716: Execute a Tool
85
- - Line 1745: `mcp_account_view`
86
- - Line 1777: `mcp_account_user_admin`
87
- - Line 1812: `mcp_firestore_search`
88
- - Line 1850: `mcp_object_activity`
89
- - Line 1859: `mcp_record_listing`
90
- - Line 1884: `mcp_record_view`
91
- - Line 1897: `mcp_ai_session_search`
92
- - Line 1933: `mcp_run_action`
93
- - Line 1977: `mcp_run_agent`
94
- - Line 2013: `mcp_system_logs`
95
- - Line 2036: `mcp_component_view`
96
- - Line 2059: `mcp_component_grep`
97
- - Line 2072: `mcp_support_ticket`
98
- - Line 2121: `mcp_run_test`
99
- - Line 2130: `mcp_component_edit`
100
- - Line 2145: `mcp_component_create`
101
- - Line 2159: `mcp_component_commit`
102
- - Line 2167: `mcp_component_branches`
103
- - Line 2186: `mcp_cache`
104
- - Line 2198: `mcp_sql_query`
105
- - Line 2233: `mcp_index_search`
106
- - Line 2253: `mcp_get_guide`
107
- - Line 2265: `mcp_test_fixture`
108
- - Line 2278: `mcp_embeddable_test_url`
109
- - Line 2290: `mcp_playwright_replay`
110
- - Line 2305: `mcp_jvm_spike_triage`
111
- - Line 2318: `mcp_support_ticket_queue`
112
- - Line 2335: Multi-Session Support
113
- - Line 2355: Persistent Service, Control Center, and Agent Dispatch
114
- - Line 2364: Starting the Service
115
- - Line 2390: Control Center
116
- - Line 2405: Agent Dispatch
117
- - Line 2426: Configuring the Preferred Agent
118
- - Line 2437: Local State Files
119
- - Line 2485: Command Reference
120
- - Line 2527: Troubleshooting
121
- - Line 2569: When Something Doesn't Work as Expected
122
- - Line 2580: Back-Stage Escalation Workflow (platform defect/limitation)
123
- - Line 2589: Escalation Bundle (tooling/operational issue)
124
-
10
+ - Line 130: Account Targeting Model
11
+ - Line 137: Account types
12
+ - Line 152: How accounts connect (and why it changes what runs)
13
+ - Line 184: Users are a separate model — don't reason about them as a tree
14
+ - Line 197: Reading the shape
15
+ - Line 223: Repo selection rules
16
+ - Line 235: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
17
+ - Line 241: How the platform reconciles the repo into the database (the mechanism you must understand)
18
+ - Line 303: The surfaces and their intended behavior
19
+ - Line 327: Intended workflows
20
+ - Line 348: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
21
+ - Line 361: If something looks wrong — stop, don't paper over
22
+ - Line 385: Required Local Index Reads
23
+ - Line 419: Big Picture: How remits-cli State Is Organized
24
+ - Line 449: Support Ticket Mental Model
25
+ - Line 485: Efficiency Rules
26
+ - Line 497: Account Repository Index
27
+ - Line 522: Two Workflows
28
+ - Line 529: Test Mode vs Prod Mode
29
+ - Line 561: Platform Mental Model: Front Stage vs Back Stage
30
+ - Line 568: Documents vs Records
31
+ - Line 578: HTTP Audits
32
+ - Line 632: Record `content` / Context Mental Model
33
+ - Line 652: Provenance Fields
34
+ - Line 670: Why Persisted Context Looks Different From Runtime Context
35
+ - Line 691: Investigation Rule for Record Context
36
+ - Line 706: AI Request Response Records
37
+ - Line 713: AI Session Groupings
38
+ - Line 732: Correlation Keys
39
+ - Line 738: Node Reference Table
40
+ - Line 748: Repository vs External Context
41
+ - Line 755: Getting Started
42
+ - Line 757: Authentication
43
+ - Line 770: Host vs Data Mode
44
+ - Line 789: Tool Execution Lifecycle
45
+ - Line 843: Data Mode
46
+ - Line 853: Development Workflow
47
+ - Line 855: The Golden Rule: Writing Code Is Not Finishing the Job
48
+ - Line 867: Avoid Brittle Front-Stage Intelligence
49
+ - Line 880: The Development Fast Loop
50
+ - Line 884: Step 1: Understand the Request
51
+ - Line 899: Step 2: Make the Change
52
+ - Line 938: Step 3: Stage to Platform
53
+ - Line 953: Step 4: Verify the Change
54
+ - Line 1018: Step 5: Iterate If Needed
55
+ - Line 1028: Step 6: Update Documentation
56
+ - Line 1040: Temporary Experiment Workflow
57
+ - Line 1060: Step 7: Commit and Durable Sync
58
+ - Line 1098: Step 8: Close the Ticket
59
+ - Line 1113: User Confirmation Preferences
60
+ - Line 1123: Component Resolution: Staging Cache vs DB (which "version" actually runs)
61
+ - Line 1129: The three source layers + the compile cache
62
+ - Line 1152: Staging cache key format
63
+ - Line 1167: How the platform picks staged vs DB (the compile signature)
64
+ - Line 1189: When staged overrides apply
65
+ - Line 1213: Diagnosing which version is in play
66
+ - Line 1234: Stage / commit / clear with the MCP tools
67
+ - Line 1258: Stale after sync / commit (the in-memory compile cache)
68
+ - Line 1268: Account Resolution: how a request travels the account graph
69
+ - Line 1373: Branched Component Variants (per-account component overrides)
70
+ - Line 1381: The model (three moving parts)
71
+ - Line 1454: Which world does your working tree resolve? (read this before you run anything)
72
+ - Line 1521: Two levers, two different questions
73
+ - Line 1535: The SDLC is identical on a variant branch
74
+ - Line 1560: Promotion: getting the branch back into trunk
75
+ - Line 1602: Verifying as the subscriber
76
+ - Line 1621: Agents (Utility) on a variant branch
77
+ - Line 1633: Danger profile on a variant branch (different, not absent)
78
+ - Line 1661: Inspecting branches and drift
79
+ - Line 1693: Diagnosing a variant
80
+ - Line 1700: Production Support Workflow
81
+ - Line 1708: Investigation Strategy
82
+ - Line 1770: Presenting Findings
83
+ - Line 1778: Verifying a Production Issue Fix
84
+ - Line 1791: Tool Reference
85
+ - Line 1793: Execute a Tool
86
+ - Line 1839: `mcp_account_view`
87
+ - Line 1871: `mcp_account_user_admin`
88
+ - Line 1965: `mcp_firestore_search`
89
+ - Line 2004: `mcp_firestore_patch`
90
+ - Line 2033: `mcp_object_activity`
91
+ - Line 2042: `mcp_record_listing`
92
+ - Line 2067: `mcp_record_view`
93
+ - Line 2080: `mcp_ai_session_search`
94
+ - Line 2116: `mcp_run_action`
95
+ - Line 2169: `mcp_run_agent`
96
+ - Line 2205: `mcp_system_logs`
97
+ - Line 2228: `mcp_performance_trace`
98
+ - Line 2263: `mcp_component_view`
99
+ - Line 2286: `mcp_component_grep`
100
+ - Line 2299: `mcp_support_ticket`
101
+ - Line 2359: `mcp_run_test`
102
+ - Line 2368: `mcp_component_edit`
103
+ - Line 2383: `mcp_component_create`
104
+ - Line 2397: `mcp_component_commit`
105
+ - Line 2405: `mcp_component_branches`
106
+ - Line 2424: `mcp_cache`
107
+ - Line 2436: `mcp_sql_query`
108
+ - Line 2471: `mcp_index_search`
109
+ - Line 2491: `mcp_get_guide`
110
+ - Line 2503: `mcp_test_fixture`
111
+ - Line 2516: `mcp_embeddable_test_url`
112
+ - Line 2528: `mcp_playwright_replay`
113
+ - Line 2543: `mcp_jvm_spike_triage`
114
+ - Line 2556: `mcp_support_ticket_queue`
115
+ - Line 2573: Multi-Session Support
116
+ - Line 2593: Persistent Service, Control Center, and Agent Dispatch
117
+ - Line 2602: Starting the Service
118
+ - Line 2628: Control Center
119
+ - Line 2643: Agent Dispatch
120
+ - Line 2664: Configuring the Preferred Agent
121
+ - Line 2675: Local State Files
122
+ - Line 2723: Command Reference
123
+ - Line 2783: Prod banners and retryable failures
124
+ - Line 2795: Troubleshooting
125
+ - Line 2837: When Something Doesn't Work as Expected
126
+ - Line 2848: Back-Stage Escalation Workflow (platform defect/limitation)
127
+ - Line 2857: Escalation Bundle (tooling/operational issue)
125
128
  `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.
126
129
 
127
130
  ## Account Targeting Model
@@ -173,8 +176,10 @@ Two consequences that generate most "this account is weird" tickets:
173
176
  different component version, a different namespace, a different host. Establish which path a failing
174
177
  request travelled before comparing behavior. (Full detail in "Account Resolution: how a request travels
175
178
  the account graph".)
176
- 2. **An account with no primary parent and several membership edges inherits nothing and runs trunk** until
177
- a path is named — the platform refuses to guess between equally valid parents. That is by design.
179
+ 2. **An account with no primary parent and SEVERAL membership edges inherits nothing and runs trunk** until
180
+ a path is named — the platform refuses to guess between equally valid parents. That is by design. With
181
+ exactly ONE membership edge there is nothing to guess, so it (and everything below it) inherits normally
182
+ — a missing `parentId` is not itself a problem.
178
183
 
179
184
  ### Users are a separate model — don't reason about them as a tree
180
185
 
@@ -1032,6 +1037,26 @@ Before committing, update metadata so the next session understands what changed:
1032
1037
  describes the owning repo account. On a subscriber-initiated variant sync it describes the subscribing
1033
1038
  account reached through the branch edge, even though the component files still belong to the owner's repo.
1034
1039
 
1040
+ #### Temporary Experiment Workflow
1041
+
1042
+ Use this when you need to prove a guard or assertion by temporarily making a local component fail. The staged
1043
+ Redis cache can affect later test/tool runs, so always clear it after restoring the file:
1044
+
1045
+ ```bash
1046
+ # make temporary local edit
1047
+ remits-cli components stage
1048
+ remits-cli test run --test <id-or-name> --names "<case name>"
1049
+ git restore <file>
1050
+ remits-cli components clear --all
1051
+ remits-cli components status
1052
+ ```
1053
+
1054
+ For narrower cleanup when only one staged component should be cleared:
1055
+
1056
+ ```bash
1057
+ remits-cli components clear --component-type Action --component-id 25
1058
+ ```
1059
+
1035
1060
  #### Step 7: Commit and Durable Sync
1036
1061
 
1037
1062
  Once verified and documented, create a normal git commit first:
@@ -1154,9 +1179,17 @@ the compile cache without colliding.
1154
1179
  That signature is logged. Querying for it is the single most reliable way to know what ran:
1155
1180
 
1156
1181
  ```bash
1157
- remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","timeRange":"1h","filter":"textPayload:\"Using Cached BCD\""}' --data-mode prod
1182
+ remits-cli tool --name mcp_system_logs --input '{"node":"remitsAdmin-east5","timeRange":"1h","filter":"Using Cached BCD"}' --data-mode prod
1158
1183
  ```
1159
1184
 
1185
+ > **Pass the bare phrase — never hand-write a `textPayload:` filter.** In production the platform's
1186
+ > logback encoder writes every `log.*` line to **`jsonPayload.message`**; only `println`/stdout lands in
1187
+ > `textPayload`. A `textPayload:"..."` filter therefore matches **zero** rows for nearly every platform
1188
+ > log line, and zero rows is indistinguishable from "it never happened" — which has already caused a real
1189
+ > misdiagnosis. `mcp_system_logs` now widens a bare phrase (and any `textPayload:"..."` clause) to cover
1190
+ > both shapes, and echoes the executed filter back as `filterApplied`. A filter that names `jsonPayload`
1191
+ > explicitly is passed through untouched.
1192
+
1160
1193
  `Using Cached BCD [ID: 230, Type: Action, Signature: cli:08cc...]` → ran a **staged** override.
1161
1194
  `...Signature: variant:14:9f2c1a...]` → ran a **committed branch variant**.
1162
1195
  `...Signature: version:37:abc123def456]` → ran the **committed trunk** version.
@@ -1281,19 +1314,31 @@ with a branch.
1281
1314
 
1282
1315
  **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id` (stamped
1283
1316
  onto the `Object`/`Event`/`Alert`/`ObjectLog` records a run creates, so async workers re-resolve on the
1284
- same branch). A **null** anchor means "walk the primary `parentId` chain" — today's legacy behavior.
1317
+ same branch). A **null** anchor means "walk the structural chain" — see below.
1285
1318
 
1286
- **Default-anchor derivation is deliberately conservative, and this is the #1 gotcha:**
1319
+ **How the chain is walked, and the #1 gotcha.** The walk follows `Account.parentId`, and at an account with
1320
+ **no** `parentId` it continues through that account's *single* active membership edge. Applied at every hop:
1287
1321
 
1288
- | Account shape | Derived anchor |
1322
+ | Account shape at a hop | What the walk does |
1289
1323
  |---|---|
1290
- | has a `parentId` | `null` → primary chain (legacy, unchanged) |
1291
- | no `parentId`, **exactly one** membership edge | that edge's parent |
1292
- | no `parentId`, **multiple** membership edges | `null` — **ambiguous, refuses to guess** |
1324
+ | has a `parentId` | follow it (legacy, unchanged) |
1325
+ | no `parentId`, **exactly one** membership edge | **continue through that edge** |
1326
+ | no `parentId`, **multiple** membership edges | **stop** — ambiguous, refuses to guess |
1327
+ | no `parentId`, no edges | stop — a true root |
1328
+
1329
+ **Primary-vs-membership does not gate inheritance; only ambiguity does.** A `parentId`-less subscriber shell
1330
+ linked to its owner by one membership edge inherits that owner's components and branch — **and so does
1331
+ everything beneath it**. You do not need to give it a real `parentId`.
1332
+
1333
+ But a `parentId`-less account with **two** membership edges resolves **trunk and inherits nothing** until an
1334
+ anchor is supplied. That is correct-by-design (the inheritance path must stay linear, or component
1335
+ name-dedupe would pick an arbitrary winner between two sibling products), not a bug. Entry points that
1336
+ already know the branch disambiguate it by branch name; from the CLI you supply it explicitly.
1293
1337
 
1294
- So a `parentId`-less account with two membership edges resolves **trunk and inherits nothing** until an
1295
- anchor is supplied. That is correct-by-design, not a bug. Entry points that already know the branch
1296
- disambiguate it by branch name; from the CLI you supply it explicitly.
1338
+ > Before 2026-08 the continuation was applied **only to the account a lookup started from**, so a
1339
+ > membership-only account truncated the chain for all of its descendants — they inherited nothing and
1340
+ > resolved trunk while the subscriber itself looked fine. If you are reading an older investigation that
1341
+ > "fixed" this by reparenting a subscriber under its owner, that workaround is no longer needed.
1297
1342
 
1298
1343
  **Same account, two parents, two answers.** An account reached through Product A versus Product B can
1299
1344
  legitimately resolve a different component variant *and* a different data segment. When you investigate
@@ -1466,8 +1511,9 @@ Read the `resolution` block before acting from any checkout — it is the ONE pl
1466
1511
  - `resolution.componentBranch` / `resolution.scopeAccountId` — the branch and the path anchor that made this
1467
1512
  subscriber resolution possible.
1468
1513
  - `resolution.parents` / `resolution.children` / `resolution.relationships` — the account graph, stated once
1469
- and compactly. The full descendant tree is a lookup (`mcp_account_user_admin`, `mcp_account_view`), not a
1470
- committed artifact.
1514
+ and compactly. For quick live hierarchy checks, prefer `mcp_account_user_admin` with `action:"account"` or
1515
+ `action:"hierarchy"`; use `mcp_account_view` only when component inventory or detailed configuration is also
1516
+ needed. The full descendant tree is a lookup, not a committed artifact.
1471
1517
 
1472
1518
  **One branch, one `account-info.json`.** There is exactly one git branch, so when a branch has MORE THAN ONE
1473
1519
  subscriber the refresh is skipped (the sync result says so under `accountInfo.skipped`/`reason`) and the file
@@ -1684,8 +1730,9 @@ Before starting an investigation outside the confirmed current repo:
1684
1730
  5. `mcp_record_listing` — search or filter alerts, events, object logs, or objects when you need to find the suspicious record first.
1685
1731
  6. `mcp_record_view` — drill into suspicious entries for full content.
1686
1732
  7. `mcp_ai_session_search` — if the workflow involves AI, inspect session groupings, prompts, tool definitions, and responses in human-readable form.
1687
- 8. `mcp_system_logs` — correlate via `threadGroupingId` for execution traces.
1688
- 9. `mcp_component_view`/`mcp_component_grep` — explain how the responsible component works.
1733
+ 8. `mcp_performance_trace` — for slow/sluggish reports, start with `action:"slowest"` when you only have a window, or `action:"trace"` when you have a `threadGroupingId`; this is the MCP wrapper for the Diagnostics page request-trace data.
1734
+ 9. `mcp_system_logs` — correlate via `threadGroupingId` for raw log context when the trace needs supporting log lines.
1735
+ 10. `mcp_component_view`/`mcp_component_grep` — explain how the responsible component works.
1689
1736
 
1690
1737
  **Error or Alert Investigation:**
1691
1738
  1. `mcp_record_listing` — search by alert type, content, error text, action, status, `threadGroupingId`, or other exact-match record properties when you do not yet know the record ID.
@@ -1697,6 +1744,39 @@ Before starting an investigation outside the confirmed current repo:
1697
1744
  7. If the context looks missing, redacted, or truncated, consider sanitization rules before concluding data was never present.
1698
1745
  8. If AI behavior is part of the symptom, use `mcp_ai_session_search` and compare the persisted session content against the Agent component implementation and `docs/guides/features/ai-support.md`.
1699
1746
 
1747
+ **Stuck / failed / recovered Event:**
1748
+
1749
+ Do **not** open the Action source first. The platform records each attempt's delivery envelope (which
1750
+ queue delivered it, which delivery attempt this was, and how long it was ever allowed to run) and will classify the failure for you. From a Test or any component:
1751
+
1752
+ ```groovy
1753
+ eventDiagnostics(18838)
1754
+ ```
1755
+
1756
+ Read `classification` before anything else:
1757
+
1758
+ - `APPLICATION_FAILURE` — **the only one that means the bug is in the component.** Read `errorMessage`
1759
+ and the correlated `alerts`, then investigate the component normally.
1760
+ - `ORPHANED_*` / `RECOVERED_*` — the attempt was abandoned. Read `abandonmentCause`:
1761
+ - `REQUEST_TIMEOUT_LIKELY` — used its whole deadline (`timing.deadlineUsed` near `1.0`). The unit of
1762
+ work is too big for one event; the fix is resumable batches, not component logic.
1763
+ - `PROCESS_TERMINATED_LIKELY` — stopped well inside its deadline (`deadlineUsed` near `0`). The worker
1764
+ was killed (memory pressure, restart). Not a logic bug.
1765
+ - `UNKNOWN_NO_DEADLINE_EVIDENCE` — no deadline recorded, so the cause is genuinely unknown. Use
1766
+ `logQuery`; do not assume. Events predating delivery-envelope capture always look like this.
1767
+ - `AWAITING_DELIVERY` — never claimed. A queue/delivery problem.
1768
+ - `IN_FLIGHT_HEALTHY` — still running with a fresh heartbeat. A long Action is not a stuck one; wait.
1769
+
1770
+ `delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
1771
+ non-idempotent side effect may have run more than once — check for duplicate records before concluding
1772
+ the component "ran twice for no reason".
1773
+
1774
+ `delivery.threadGroupingId` is the same id everything else uses, so you can pivot straight into
1775
+ `mcp_performance_trace` (`action:"trace"`) or `mcp_system_logs` with it. `logQuery` in the response
1776
+ carries ready-made Cloud Logging filters, including the container-lifecycle and 504 queries.
1777
+
1778
+ Full detail: `docs/guides/features/observability.md` §5b and `events-builder-guide.md`.
1779
+
1700
1780
  ### Presenting Findings
1701
1781
 
1702
1782
  Users are not engineers. When reporting investigation results:
@@ -1867,11 +1947,19 @@ client accounts beneath it:
1867
1947
  type:'CLIENT' # inherits the branch automatically
1868
1948
  ```
1869
1949
 
1870
- > **Put the branch on the account's PRIMARY edge.** Component inheritance follows the *anchored* path, which
1871
- > for an account with a `parentId` is its primary chain. An account that has both a primary edge and a
1872
- > *membership* edge carrying the branch resolves **trunk** by default — the membership edge is never
1873
- > anchored unless a request names it (`--as-account`, `--variant-branch`, or the edge's own host). Descendants
1874
- > then inherit the branch down that primary chain automatically, which is what makes step 4 free.
1950
+ > **Put the branch on the edge that is UNAMBIGUOUSLY on the account's path up — primary or membership.**
1951
+ > Inheritance walks `parentId` and, at an account that has no `parentId`, continues through its *single*
1952
+ > active membership edge. So a subscriber shell with **no `parentId` and one membership edge** to its owner
1953
+ > is a fully supported shape: it resolves the branch, **and so do all of its descendants**. You do not need
1954
+ > to make the subscriber a structural child of its owner.
1955
+ >
1956
+ > What is NOT resolved by default is genuine **ambiguity** — an account with *several* upward links, where
1957
+ > the platform refuses to guess which product it was reached through. That account (and its descendants)
1958
+ > resolve trunk until a request names the path: `--as-account`, `--variant-branch`, or the edge's own host.
1959
+ > An account that has a `parentId` **and** a separate membership edge carrying the branch is this case: the
1960
+ > `parentId` wins, so put the branch on the link the account actually inherits through.
1961
+ >
1962
+ > Either way, descendants inherit the branch down the chain automatically, which is what makes step 4 free.
1875
1963
 
1876
1964
  | Parameter | Required | Description |
1877
1965
  |-----------|----------|-------------|
@@ -1899,6 +1987,7 @@ Query Firestore documents.
1899
1987
  | `aggregation` | no | `{sum: [...], avg: [...], min: [...], max: [...], count: true}` |
1900
1988
  | `dateRanges` | no | `[{field, startDate, endDate}]` (yyyy-MM-dd) |
1901
1989
  | `textSearch` | no | `[{field, prefix}]` for prefix matching |
1990
+ | `fallbackOnMissingIndex` | no | When `true`, a sorted read that fails on a missing composite index retries without sort and returns `warning`, `missingIndexUrl`, and `sortApplied:false`. Use when an unsorted first page is still useful. |
1902
1991
 
1903
1992
  HTTP audit usage notes:
1904
1993
 
@@ -1922,6 +2011,35 @@ remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collec
1922
2011
  - `response.statusCode`
1923
2012
  - `success`
1924
2013
 
2014
+ ### `mcp_firestore_patch`
2015
+ Guarded exact-document Firestore patch tool for bounded repairs. It defaults to `dryRun:true` and refuses broad
2016
+ updates, wildcard collections, delete/remove operations, protected identity fields, and `_lastModified*` audit
2017
+ fields.
2018
+
2019
+ Use it when the desired repair is mechanical and smaller than rerunning an expensive Action, for example copying
2020
+ canonical fields into stale UI mirror fields. Always dry-run first and include preconditions:
2021
+
2022
+ ```bash
2023
+ remits-cli tool --name mcp_firestore_patch --data-mode prod --input '{
2024
+ "accountId":743,
2025
+ "collection":"statements",
2026
+ "documentId":"20958",
2027
+ "dryRun":true,
2028
+ "preconditions":[
2029
+ {"field":"interchangeOptimization.status","equals":"Calculated"},
2030
+ {"field":"interchangeOptimizationChecked","equals":false}
2031
+ ],
2032
+ "patch":{
2033
+ "interchangeOptimizationChecked":true,
2034
+ "feeBreakdown.interchangeOptimization":{"$copyFrom":"interchangeOptimization"},
2035
+ "feeBreakdown.interchange.optimization":{"$copyFrom":"interchangeOptimization"}
2036
+ }
2037
+ }'
2038
+ ```
2039
+
2040
+ Response fields include `dataMode`, `accountId`, `collection`, `documentId`, `dryRun`, `patchedFields`, and
2041
+ `diff`. Switch to `"dryRun":false` only after the diff and preconditions are exactly what you intended.
2042
+
1925
2043
  ### `mcp_object_activity`
1926
2044
  Object lifecycle timeline — metadata + recent activity.
1927
2045
 
@@ -2009,6 +2127,15 @@ Audit flow: `action:"search"` to find the grouping → `action:"detail"` + `summ
2009
2127
  Run an Action on a target account, with explicit prod/test data mode, optional staged branch resolution, and
2010
2128
  staged-vs-DB provenance in the result.
2011
2129
 
2130
+ Describe the Action first when the input shape is not obvious. This does not execute the Action:
2131
+
2132
+ ```bash
2133
+ remits-cli tool --name mcp_run_action --input '{"controlAction":"describe","accountId":743,"actionId":25,"includeInputSchema":true}' --data-mode prod
2134
+ ```
2135
+
2136
+ The describe response reports `hasInputSchema`, optional `inputSchema`, `inferredInputKeys`, and component
2137
+ provenance. If `hasInputSchema:false`, treat `inferredInputKeys` as a best-effort static scan, not a contract.
2138
+
2012
2139
  Use direct mode only for quick Actions:
2013
2140
 
2014
2141
  ```bash
@@ -2049,6 +2176,41 @@ Returned fields on the async/event start: `actionRunId`, `status:"running"`, `ex
2049
2176
  `threadGroupingId`, `componentSource`, `componentSignature`, and (event mode) `eventId`/`eventStatus`. The
2050
2177
  `status` poll adds `result` on completion, or `message`/`error` on failure.
2051
2178
 
2179
+ > **In event mode the EVENT is the source of truth, not the promise.** `status` is driven by the Event's
2180
+ > own state; the value the dispatch call returned is reported separately as `dispatchResult` and is **not**
2181
+ > the Action's result. Read `eventStatus` for the outcome.
2182
+
2183
+ #### Stopping a run — `controlAction:'interrupt'`
2184
+
2185
+ The tool counterpart of the **Interrupt** button on the admin Events page. Use it when an investigation
2186
+ turns up a run that is consuming resources and should not finish — the case this exists for is finding a
2187
+ `PROCESSING` event that has been running far too long.
2188
+
2189
+ ```bash
2190
+ # the usual path: you found the event in mcp_record_listing / mcp_object_activity
2191
+ remits-cli tool --name mcp_run_action --data-mode prod --input '{"controlAction":"interrupt","accountId":49,"eventId":19102,"reason":"runaway extraction, 45min"}'
2192
+
2193
+ # or stop a run you started yourself (executionMode:'event' only)
2194
+ remits-cli tool --name mcp_run_action --data-mode prod --input '{"controlAction":"interrupt","accountId":4,"actionRunId":"my-run-id"}'
2195
+ ```
2196
+
2197
+ Aliases `cancel` / `stop` / `kill` all work. What you need to know before using it:
2198
+
2199
+ - **It is COOPERATIVE cancellation, not a thread kill.** It sets a flag the running work observes at its
2200
+ next checkpoint, then throws. Checkpoints are dense across everything that matters — every Firestore
2201
+ read/write, outbound HTTP call, AI turn, and front-stage DSL call — so a normal run stops promptly. A
2202
+ run blocked inside a *single* long call (one slow AI turn) stops when that call returns, not instantly.
2203
+ - **Work already committed is NOT rolled back.** This stops further work; it does not undo what has run.
2204
+ - **Only `PENDING`/`QUEUED`/`PROCESSING` can be interrupted.** A terminal event is reported back with its
2205
+ status rather than being silently reported as "interrupted".
2206
+ - **Event-scoped.** A `direct` or `async` run has no Event and cannot be stopped this way.
2207
+ - Tenant-scoped: you cannot interrupt another account's event.
2208
+ - The response returns `previousStatus`, `eventStatus`, and `threadGroupingId`, so you can pivot straight
2209
+ into `mcp_performance_trace` / `mcp_system_logs` to see what it was doing when you stopped it.
2210
+
2211
+ **AI sessions are stopped separately** with `mcp_ai_session_search` (`action:'interrupt'`, plus
2212
+ `pause`/`unpause`) — that controls an agent's conversation loop, whereas this controls an Action's Event.
2213
+
2052
2214
  ### `mcp_run_agent`
2053
2215
  Run one real Agent turn on a target account. The Agent hooks and tools execute for real against the requested
2054
2216
  `dataMode`; pass `dataMode:"test"` for safer tuning.
@@ -2085,6 +2247,24 @@ remits-cli tool --name mcp_ai_session_search --input '{"action":"detail","sessio
2085
2247
  Key returned fields: `agentRunId`, `sessionId`, `status`, `threadGroupingId`, runtime pause/interruption
2086
2248
  hints, `lastAssistantMessage`, `toolCallCount`, `result`, `message`, and `error`.
2087
2249
 
2250
+ #### Controlling a live agent — `pause` / `unpause` / `interrupt`
2251
+
2252
+ ```bash
2253
+ remits-cli tool --name mcp_run_agent --data-mode prod --input '{"controlAction":"pause","accountId":49,"agentRunId":"my-run-id"}'
2254
+ remits-cli tool --name mcp_run_agent --data-mode prod --input '{"controlAction":"interrupt","accountId":49,"sessionId":"<sessionId>"}'
2255
+ ```
2256
+
2257
+ Target the session with the `agentRunId` an async start returned, or the `sessionId` directly.
2258
+
2259
+ - **`interrupt` is TERMINAL** (aliases `stop`/`cancel`/`kill`). It clears any pending resume and pending
2260
+ guardrails and persists a terminal lifecycle status. An interrupted session **cannot be unpaused** —
2261
+ attempting it is refused with that reason rather than silently doing nothing. Use `pause` if you intend
2262
+ to resume.
2263
+ - If the session is not resident on the serving node, it is rehydrated by agent name — so pass `agentName`
2264
+ (or an `agentRunId`, which carries it) when controlling a session you did not just start.
2265
+ - The same controls remain available on `mcp_ai_session_search`, which is the right tool when you are
2266
+ *searching* for the session; this is the right one when you *started* the run.
2267
+
2088
2268
  ### `mcp_system_logs`
2089
2269
  Query Cloud Run service logs.
2090
2270
 
@@ -2098,7 +2278,8 @@ Query Cloud Run service logs.
2098
2278
  | `endTime` | no | ISO 8601 upper bound (defaults to now) |
2099
2279
  | `severity` | no | Minimum: `INFO`, `WARNING`, `ERROR`, etc. |
2100
2280
  | `threadGroupingId` | no | Filter by processing chain ID |
2101
- | `filter` | no | Additional Cloud Logging filter (LQL) |
2281
+ | `filter` | no | Additional Cloud Logging filter (LQL). **To search message text, pass the bare phrase** — it is widened automatically to match both `jsonPayload.message` (all `log.*` output) and `textPayload` (`println`/stdout). See the warning under "Diagnosing which version is in play". |
2282
+ | `maxPreviewChars` | no | Per-entry truncation width. Default 512, max 8000. Raise it when an entry carries a structured payload (a serialized `RemitsTrace`, a long stack frame) that the default cuts mid-JSON. |
2102
2283
  | `pageSize` | no | Default 25, max 100. Also accepts `limit`. |
2103
2284
 
2104
2285
  *Provide either `node` or `serviceName`+`region`. Provide either `timeRange` or `startTime`.
@@ -2108,6 +2289,56 @@ Query Cloud Run service logs.
2108
2289
  {"node": "remitsAdmin-east5", "timeRange": "4h", "severity": "ERROR"}
2109
2290
  ```
2110
2291
 
2292
+ ### `mcp_performance_trace`
2293
+ Read Remits request traces through the same `traces(...)` DSL that powers the admin Diagnostics "Request
2294
+ traces" panel. Use this before raw log spelunking for slow or sluggish requests because it returns profiled
2295
+ span rollups, component annotations, and retained slow-request summaries directly.
2296
+
2297
+ Common flows:
2298
+
2299
+ ```bash
2300
+ # User only knows it was slow this afternoon
2301
+ remits-cli tool --name mcp_performance_trace --input '{"action":"slowest","lookbackHours":4,"accountId":52,"minMs":2000,"limit":10}' --data-mode prod
2302
+
2303
+ # You have the Diagnostics/request/Object/Event/Alert threadGroupingId
2304
+ remits-cli tool --name mcp_performance_trace --input '{"action":"trace","traceId":"msf1y65n-001","lookbackHours":6,"format":"markdown"}' --data-mode prod
2305
+
2306
+ # Same local-node data as the Diagnostics table
2307
+ remits-cli tool --name mcp_performance_trace --input '{"action":"snapshot","limit":100}' --data-mode prod
2308
+ ```
2309
+
2310
+ | Parameter | Required | Description |
2311
+ |-----------|----------|-------------|
2312
+ | `action` | no | `snapshot`, `slowest`, or `trace`. Default: `snapshot`. |
2313
+ | `traceId` / `threadGroupingId` | for `trace` | Request/grouping id to open across local ring and Cloud Logging. |
2314
+ | `lookbackHours` | no | Cloud Logging window for `trace`/`slowest`. Defaults are 6 and 4 hours. |
2315
+ | `accountId` | no | Account filter for `slowest`. |
2316
+ | `kind` | no | Operation kind filter for `slowest`. **Rarely what you want** — see the note below. |
2317
+ | `componentType` | no | Filter `slowest` by the component that did the work: `Action`, `Reader`, `Rule`, `Embeddable`, `Tool`, `Test`. **This is the right axis for "which Actions/Rules are slow".** |
2318
+ | `componentId` / `componentName` | no | Narrow `slowest` to one component. |
2319
+
2320
+ > **Filter by `componentType`, not `kind`.** `kind` is set by whoever OPENS the trace. An Action delivered
2321
+ > by Cloud Tasks arrives over HTTP, so the trace is `kind:'web'` and the Action is a `component.Action`
2322
+ > *span inside it*; a Rule fired during a request and a Tool invoked by an agent are the same. So
2323
+ > `kind:'action'` matches almost nothing in production. Every component execution annotates
2324
+ > `componentType`/`componentId`/`componentName` — filter on those.
2325
+ | `minMs` | no | Minimum duration for `slowest`. Default: 1500. |
2326
+ | `limit` | no | Result limit. |
2327
+ | `format` / `markdown` | no | Set `format:"markdown"` or `markdown:true` for a rendered trace report. |
2328
+
2329
+ > **Tools are components, so availability is per ACCOUNT.** `Tool not found: mcp_performance_trace` does
2330
+ > not mean the tool is broken or that tracing is off — it means that tool has not been synced to the
2331
+ > account you are resolving against. This bites most often on **localhost** (a Test Account that has not
2332
+ > pulled the System Account's tool set) and on **client accounts**. Run `remits-cli tools` for the account
2333
+ > in question, or re-run against an account that owns the tool (`--account-id 4` for the System Account).
2334
+ > The same is true of every `mcp_*` tool, including the component tools noted below.
2335
+
2336
+ Reading rule: first use `slowest` to get candidate trace ids, then call `trace` on the suspicious id, then use
2337
+ `mcp_system_logs` only if you need surrounding log lines. A trace dominated by `firestore.*`, `http.*`,
2338
+ `gorm.save.*`, or component spans points you at the relevant platform seam or front-stage component. Large
2339
+ unaccounted wall time is itself a finding: check cold compile, queueing, blocking I/O, or missing
2340
+ `measure(...)` instrumentation.
2341
+
2111
2342
  ### `mcp_component_view`
2112
2343
  Read component field content with line numbers.
2113
2344
 
@@ -2212,6 +2443,34 @@ Execute a Test component.
2212
2443
  | `accountId` | yes | Account ID |
2213
2444
  | `testId` | yes | Test component ID |
2214
2445
  | `testNames` | no | Array of specific test case names |
2446
+ | `taskId` | no | Stable id for the run. **Declare your own if you may need to stop it** — see below. Echoed back either way. |
2447
+ | `controlAction` | no | `interrupt` (aliases `stop`/`cancel`/`kill`) stops a running suite identified by `taskId`. |
2448
+
2449
+ #### Stopping a running suite
2450
+
2451
+ A suite that loops dozens of cases — or sits in one slow AI/HTTP call — used to have to be waited out.
2452
+ It can now be stopped, from the admin test runner's **Stop** button or from here.
2453
+
2454
+ ```bash
2455
+ # declare the id when you start, so the run is addressable while it is still going
2456
+ remits-cli tool --name mcp_run_test --data-mode test --input '{"accountId":1,"testId":7,"taskId":"my-run"}'
2457
+
2458
+ # ...then from another call/session:
2459
+ remits-cli tool --name mcp_run_test --data-mode test --input '{"controlAction":"interrupt","accountId":1,"taskId":"my-run"}'
2460
+ ```
2461
+
2462
+ > **This tool runs the suite SYNCHRONOUSLY**, so you cannot stop a run you are yourself blocked on —
2463
+ > which is exactly why you declare `taskId` up front. Without one, a run gets a generated id you never see.
2464
+
2465
+ Semantics, same as everywhere else in the platform:
2466
+
2467
+ - **Cooperative.** The suite ends at its next checkpoint — between cases, or mid-case at any
2468
+ Firestore/HTTP/AI/DSL call. A case stuck in one long external call stops when that call returns.
2469
+ - **Cases already completed keep their results**, and committed work is **not** rolled back.
2470
+ - An interrupted run returns `interrupted: true` and its partial results. A stop is reported as
2471
+ **INTERRUPTED, never as a test failure** — so it can't be mistaken for a broken suite.
2472
+ - Keyed per RUN, not per Test: the same Test can be running concurrently (different users, branches, or
2473
+ data modes), and stopping one never stops another.
2215
2474
 
2216
2475
  ### `mcp_component_edit`
2217
2476
  Edit a component field, server-side, with stage or commit semantics. The agent counterpart to local file
@@ -2582,7 +2841,7 @@ remits-cli data-mode [set test|prod]
2582
2841
  remits-cli components stage [--branch <name>] [--data-mode test|prod] [--json|--verbose]
2583
2842
  remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]
2584
2843
  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
2585
- 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
2844
+ remits-cli components sync [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--summary] # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
2586
2845
  remits-cli components commit [--message "msg"] [--data-mode test|prod] [--force-tombstones]
2587
2846
  remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
2588
2847
  remits-cli components branch <name> [--json] # one branch: overridden / added / removed, drift flags, subscribers
@@ -2609,6 +2868,36 @@ For tests specifically:
2609
2868
  intentional tombstone overrides. It is rejected on trunk.
2610
2869
  - `--dry-run` is only for `components sync` on non-trunk variant branches. It reports the variant write
2611
2870
  plan without writing rows, caching the sync SHA, or clearing staging.
2871
+ - `--summary` on `components sync --dry-run` prints compact counts, removals/tombstones, skipped items, errors,
2872
+ and warnings instead of the full override/add/remove payload.
2873
+ - **Fail-closed sync gates.** The row below ("Sync response reports unexpected deletes...") tells you to
2874
+ stop *after* an unexpected sync. These flags make sync refuse it up front instead, each exiting
2875
+ non-zero rather than printing a wall of JSON you have to read carefully:
2876
+ - `--changed-only` — fail unless every planned write is a component **this checkout actually edited**.
2877
+ This is the strongest guard against a sync that quietly rewrites components you never touched. It
2878
+ also fails when the checkout is not a git working tree, because "git could not answer" must never
2879
+ be read as "nothing changed".
2880
+ - `--fail-on-removed` — fail if the plan removes or tombstones anything.
2881
+ - `--expected-removed <type:id>` — whitelist the removals you intend (repeatable, or comma-delimited,
2882
+ e.g. `--expected-removed action:5,reader:9`). It **implies** `--fail-on-removed`, so any removal you
2883
+ did not name fails the sync.
2884
+ - `--fail-on-errors` — fail if the server reported any per-component sync error.
2885
+ - `--names-only` — print only `BUCKET type:id name` lines for the planned writes.
2886
+
2887
+ A good default for an unattended promotion is:
2888
+ `remits-cli components sync --dry-run --summary --changed-only --fail-on-errors`
2889
+
2890
+ ### Prod banners and retryable failures
2891
+
2892
+ - Every command that can touch production (`tool`, `test run`, `components sync`) prints a `PROD DATA`
2893
+ banner naming the operation, the resolved account, and the host — and distinguishes a live **WRITE**
2894
+ from a live **READ** and from a **DRY RUN**. If you see a WRITE banner you did not intend, stop.
2895
+ - A tool call that fails **in the platform runtime** rather than in the tool (Groovy reflective dispatch
2896
+ of a runtime-compiled component, an empty connection pool, a Redis reconnect, a lock-wait timeout)
2897
+ now comes back as HTTP `503` with `failureClass: "transient_infrastructure"` and `retryable: true`,
2898
+ and the CLI prints `TRANSIENT INFRASTRUCTURE FAILURE (retryable)`. Retry that **once**; prefer an
2899
+ idempotent input if the tool has side effects. A genuine tool error stays HTTP `500` with
2900
+ `failureClass: "tool_error"` — do **not** retry it, fix the input or the component.
2612
2901
 
2613
2902
  ## Troubleshooting
2614
2903
 
@@ -2630,7 +2919,7 @@ For tests specifically:
2630
2919
  | 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". |
2631
2920
  | Need to know an account's shape (role, type, parents, namespace, branch) | Read `resolution` — from the repo's `account-info.json`, or `mcp_account_user_admin` `action:'account'` (cheap), or `mcp_account_view` (full inventory): `role`/`summary`, `type`, `resolvedDatabaseName`, `relationships`, `componentBranch`, plus top-level `componentBranches`. Never infer structure from the account's name. |
2632
2921
  | Need the account tree below an account, or its users | `mcp_account_user_admin` (`action:'hierarchy'` with a `depth`, or `action:'users'`). `account-info.json` deliberately carries only direct children plus counts. |
2633
- | 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. |
2922
+ | An account has two parents and you don't know which one a run used | `resolution.relationships` lists every link with its own `branchName`/`databaseName`/`domainName`. A membership-only account with SEVERAL edges resolves **trunk and inherits nothing** until a path is named (`--as-account`, `--variant-branch`, or an edge host) — that is by design, not a bug. With exactly ONE membership edge it inherits normally, descendants included. |
2634
2923
  | 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. |
2635
2924
  | 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). |
2636
2925
  | 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. |