@remits/remits-cli 0.1.95 → 0.1.97

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,231 +7,164 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
7
7
 
8
8
  ## Table of Contents
9
9
 
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 2260: `mcp_event_diagnostics`
99
- - Line 2263: `mcp_component_view`
100
- - Line 2286: `mcp_component_grep`
101
- - Line 2299: `mcp_support_ticket`
102
- - Line 2359: `mcp_run_test`
103
- - Line 2368: `mcp_component_edit`
104
- - Line 2383: `mcp_component_create`
105
- - Line 2397: `mcp_component_commit`
106
- - Line 2405: `mcp_component_branches`
107
- - Line 2424: `mcp_cache`
108
- - Line 2436: `mcp_sql_query`
109
- - Line 2471: `mcp_index_search`
110
- - Line 2491: `mcp_get_guide`
111
- - Line 2503: `mcp_test_fixture`
112
- - Line 2516: `mcp_embeddable_test_url`
113
- - Line 2528: `mcp_playwright_replay`
114
- - Line 2543: `mcp_jvm_spike_triage`
115
- - Line 2556: `mcp_support_ticket_queue`
116
- - Line 2573: Multi-Session Support
117
- - Line 2593: Persistent Service, Control Center, and Agent Dispatch
118
- - Line 2602: Starting the Service
119
- - Line 2628: Control Center
120
- - Line 2643: Agent Dispatch
121
- - Line 2664: Configuring the Preferred Agent
122
- - Line 2675: Local State Files
123
- - Line 2723: Command Reference
124
- - Line 2783: Prod banners and retryable failures
125
- - Line 2795: Troubleshooting
126
- - Line 2837: When Something Doesn't Work as Expected
127
- - Line 2848: Back-Stage Escalation Workflow (platform defect/limitation)
128
- - Line 2857: Escalation Bundle (tooling/operational issue)
129
- `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.
10
+ - [Account Targeting Model](#account-targeting-model)
11
+ - [Read the shape first](#read-the-shape-first)
12
+ - [Which repo does the work belong in](#which-repo-does-the-work-belong-in)
13
+ - [Repo selection rules](#repo-selection-rules)
14
+ - [Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)](#component-integrity-rules-repo--database-reconciliation-read-before-any-synccommit)
15
+ - [How the platform reconciles the repo into the database (the mechanism you must understand)](#how-the-platform-reconciles-the-repo-into-the-database-the-mechanism-you-must-understand)
16
+ - [The surfaces and their intended behavior](#the-surfaces-and-their-intended-behavior)
17
+ - [Intended workflows](#intended-workflows)
18
+ - [Pre-sync safety check (confirm ALL before `components sync` or `components commit`)](#pre-sync-safety-check-confirm-all-before-components-sync-or-components-commit)
19
+ - [If something looks wrong — stop, don't paper over](#if-something-looks-wrong--stop-dont-paper-over)
20
+ - [Required Local Index Reads](#required-local-index-reads)
21
+ - [Big Picture: How remits-cli State Is Organized](#big-picture-how-remits-cli-state-is-organized)
22
+ - [Support Ticket Mental Model](#support-ticket-mental-model)
23
+ - [Efficiency Rules](#efficiency-rules)
24
+ - [Account Repository Index](#account-repository-index)
25
+ - [Two Workflows](#two-workflows)
26
+ - [Test Mode vs Prod Mode](#test-mode-vs-prod-mode)
27
+ - [The Investigation Model](#the-investigation-model)
28
+ - [Which tool reads which record](#which-tool-reads-which-record)
29
+ - [Correlation keys](#correlation-keys)
30
+ - [Reading a record's `content` — persisted context, not a memory dump](#reading-a-records-content--persisted-context-not-a-memory-dump)
31
+ - [HTTP audits](#http-audits)
32
+ - [AI activity](#ai-activity)
33
+ - [Node Reference Table](#node-reference-table)
34
+ - [Getting Started](#getting-started)
35
+ - [Authentication](#authentication)
36
+ - [Host vs Data Mode](#host-vs-data-mode)
37
+ - [Tool Execution Lifecycle](#tool-execution-lifecycle)
38
+ - [Data Mode](#data-mode)
39
+ - [Development Workflow](#development-workflow)
40
+ - [The Golden Rule: Writing Code Is Not Finishing the Job](#the-golden-rule-writing-code-is-not-finishing-the-job)
41
+ - [The Development Fast Loop](#the-development-fast-loop)
42
+ - [Step 1: Understand the Request](#step-1-understand-the-request)
43
+ - [Step 2: Make the Change](#step-2-make-the-change)
44
+ - [Step 3: Stage to Platform](#step-3-stage-to-platform)
45
+ - [Step 4: Verify the Change](#step-4-verify-the-change)
46
+ - [Step 5: Iterate If Needed](#step-5-iterate-if-needed)
47
+ - [Step 6: Update Documentation](#step-6-update-documentation)
48
+ - [Temporary Experiment Workflow](#temporary-experiment-workflow)
49
+ - [Step 7: Commit and Durable Sync](#step-7-commit-and-durable-sync)
50
+ - [Step 8: Close the Ticket](#step-8-close-the-ticket)
51
+ - [User Confirmation Preferences](#user-confirmation-preferences)
52
+ - [Component Resolution: Staging Cache vs DB (which "version" actually runs)](#component-resolution-staging-cache-vs-db-which-version-actually-runs)
53
+ - [The three source layers + the compile cache](#the-three-source-layers--the-compile-cache)
54
+ - [Staging cache key format](#staging-cache-key-format)
55
+ - [How the platform picks staged vs DB (the compile signature)](#how-the-platform-picks-staged-vs-db-the-compile-signature)
56
+ - [When staged overrides apply](#when-staged-overrides-apply)
57
+ - [Diagnosing which version is in play](#diagnosing-which-version-is-in-play)
58
+ - [Stage / sync / clear with remits-cli](#stage--sync--clear-with-remits-cli)
59
+ - [Stale after sync / commit (the in-memory compile cache)](#stale-after-sync--commit-the-in-memory-compile-cache)
60
+ - [Account Resolution: how a request travels the account graph](#account-resolution-how-a-request-travels-the-account-graph)
61
+ - [Seeing an account's edges](#seeing-an-accounts-edges)
62
+ - [When a subscription "doesn't work"](#when-a-subscription-doesnt-work)
63
+ - [The same block answers the non-branch questions](#the-same-block-answers-the-non-branch-questions)
64
+ - [Branched Component Variants (per-account component overrides)](#branched-component-variants-per-account-component-overrides)
65
+ - [Which world does your working tree resolve? (read this before you run anything)](#which-world-does-your-working-tree-resolve-read-this-before-you-run-anything)
66
+ - [Two levers, two different questions](#two-levers-two-different-questions)
67
+ - [The SDLC is identical on a variant branch](#the-sdlc-is-identical-on-a-variant-branch)
68
+ - [Subscribing, unsubscribing, retiring](#subscribing-unsubscribing-retiring)
69
+ - [Danger profile on a variant branch (different, not absent)](#danger-profile-on-a-variant-branch-different-not-absent)
70
+ - [Inspecting branches and drift](#inspecting-branches-and-drift)
71
+ - [Diagnosing a variant](#diagnosing-a-variant)
72
+ - [Production Support Workflow](#production-support-workflow)
73
+ - [Investigation Strategy](#investigation-strategy)
74
+ - [Presenting Findings](#presenting-findings)
75
+ - [Verifying a Production Issue Fix](#verifying-a-production-issue-fix)
76
+ - [Tool Reference](#tool-reference)
77
+ - [Execute a Tool](#execute-a-tool)
78
+ - [`mcp_account_view`](#mcp_account_view)
79
+ - [`mcp_account_user_admin`](#mcp_account_user_admin)
80
+ - [`mcp_firestore_search`](#mcp_firestore_search)
81
+ - [`mcp_firestore_patch`](#mcp_firestore_patch)
82
+ - [`mcp_object_activity`](#mcp_object_activity)
83
+ - [`mcp_record_listing`](#mcp_record_listing)
84
+ - [`mcp_record_view`](#mcp_record_view)
85
+ - [`mcp_ai_session_search`](#mcp_ai_session_search)
86
+ - [`mcp_run_action`](#mcp_run_action)
87
+ - [Stopping a run — `controlAction:'interrupt'`](#stopping-a-run--controlactioninterrupt)
88
+ - [`mcp_run_agent`](#mcp_run_agent)
89
+ - [Controlling a live agent — `pause` / `unpause` / `interrupt`](#controlling-a-live-agent--pause--unpause--interrupt)
90
+ - [`mcp_system_logs`](#mcp_system_logs)
91
+ - [`mcp_performance_trace`](#mcp_performance_trace)
92
+ - [`mcp_event_diagnostics`](#mcp_event_diagnostics)
93
+ - [`mcp_component_view`](#mcp_component_view)
94
+ - [`mcp_component_grep`](#mcp_component_grep)
95
+ - [`mcp_support_ticket`](#mcp_support_ticket)
96
+ - [Component branches](#component-branches)
97
+ - [`mcp_cache`](#mcp_cache)
98
+ - [`mcp_sql_query`](#mcp_sql_query)
99
+ - [`mcp_index_search`](#mcp_index_search)
100
+ - [`mcp_get_guide`](#mcp_get_guide)
101
+ - [`mcp_test_fixture`](#mcp_test_fixture)
102
+ - [`mcp_playwright_replay`](#mcp_playwright_replay)
103
+ - [`mcp_jvm_spike_triage`](#mcp_jvm_spike_triage)
104
+ - [`mcp_support_ticket_queue`](#mcp_support_ticket_queue)
105
+ - [Multi-Session Support](#multi-session-support)
106
+ - [Persistent Service, Control Center, and Agent Dispatch](#persistent-service-control-center-and-agent-dispatch)
107
+ - [Starting the Service](#starting-the-service)
108
+ - [Control Center](#control-center)
109
+ - [Agent Dispatch](#agent-dispatch)
110
+ - [Configuring the Preferred Agent](#configuring-the-preferred-agent)
111
+ - [Local State Files](#local-state-files)
112
+ - [Command Reference](#command-reference)
113
+ - [Prod banners and retryable failures](#prod-banners-and-retryable-failures)
114
+ - [Troubleshooting](#troubleshooting)
115
+ - [When Something Doesn't Work as Expected](#when-something-doesnt-work-as-expected)
116
+ - [Escalation Bundle (tooling/operational issue)](#escalation-bundle-toolingoperational-issue)
130
117
 
131
118
  ## Account Targeting Model
132
119
 
133
120
  **Establish the account's shape before you touch anything.** Which repo you work in, which account you run
134
- against, where a fix belongs, and why one account behaves unlike its siblings are all answered by the account
135
- structure — never by the account's name. Read it from `account-info.json` (in the repo) or `mcp_account_view`
136
- (remotely). Both return the same thing.
121
+ against, and where a fix belongs are answered by the account's structure — never by its name.
137
122
 
138
- ### Account types
123
+ The account model itself — types, component inheritance, primary vs membership edges, the three independent
124
+ edge properties, the user model — is in the always-loaded `platform-overview.md` and in depth in
125
+ `features/account-management.md` (`mcp_get_guide`). What follows is only what changes **what you type**.
139
126
 
140
- | Type | What it is | Components | Business data |
141
- |---|---|---|---|
142
- | `PLATFORM` | Top of a use-case tree. Owns the shared implementation and the git repository. | **Owned here** | Rarely — mostly configuration |
143
- | `PRODUCT` | A product/use case under a platform. May own shared components too. | **Owned here or above** | Rarely |
144
- | `CLIENT` | An actual customer. Where an issue is observed. | Usually **inherited**, not owned | **Here** — the customer's documents and lifecycle records |
145
- | `PROVIDER` | A partner/vendor participant in a use case | Rarely | Sometimes |
146
-
147
- - **Feature, enhancement, component change, shared behavior fix** → the implementation target is usually the
148
- owning `PLATFORM` or `PRODUCT` account, **not** the `CLIENT` that reported it.
149
- - **Production investigation or client-specific data issue** → start with the affected `CLIENT` account's
150
- data and runtime history.
151
- - **Both** → confirm symptoms on the `CLIENT`, then move to the owning `PLATFORM`/`PRODUCT` repo to change code.
152
-
153
- ### How accounts connect (and why it changes what runs)
127
+ ### Read the shape first
154
128
 
155
- Components are **inherited downward** and de-duplicated **by component name, nearest owner wins** — so a
156
- client can run an Action owned three levels up, and can override it just by owning one with the same name.
157
- Business data does **not** inherit; it belongs to the account that wrote it, in that account's storage
158
- namespace.
129
+ `account-info.json` (in a repo) and `mcp_account_view` (remotely) both carry a `resolution` block — the one
130
+ place these facts appear. Field-by-field detail is under **`mcp_account_view`** in the Tool Reference. The
131
+ four that decide a CLI action:
159
132
 
160
- An account is linked upward in two ways, and they coexist:
133
+ | Read | To decide |
134
+ |---|---|
135
+ | `role` + `summary` | `OWNER` (the files here **are** its components) vs `SUBSCRIBER` (it resolves another account's components under a variant branch). Read this before you touch anything. |
136
+ | `type` | whether this repo is where the change belongs — see below |
137
+ | `resolvedDatabaseName` | where its data actually lands. **Check this first when documents are "missing".** |
138
+ | `relationships` | every link upward, primary first, each with its own `branchName` / `databaseName` / `domainName`. **More than one entry means the account can legitimately resolve differently depending on the path a request travelled** — establish which one a failing request used before comparing behavior. |
161
139
 
162
- - **The primary parent** — one per account. The overwhelming majority of accounts have only this.
163
- - **Membership edges** — extra links letting the *same account* be reached through **more than one** parent
164
- (a customer in two product lines; a branch deployment reaching the same owner).
140
+ Two more, easily confused: top-level **`componentBranches`** lists the variant branches this account
141
+ **owns**, with drift and subscriber counts — check it before editing a shared component. And
142
+ `resolution.branchName` is the **repo's trunk sync branch**, *not* a component-variant branch.
165
143
 
166
- Any link can independently carry three things. They are **orthogonal** — never infer one from another:
144
+ ### Which repo does the work belong in
167
145
 
168
- | Edge property | Means |
146
+ | Situation | Target |
169
147
  |---|---|
170
- | `branchName` | **Which code runs** — the component-variant branch this account subscribes to (see "Branched Component Variants") |
171
- | `databaseName` | **Where data lives** — a storage-namespace override scoped to this link |
172
- | `domainName` | **Which host reaches it** — a custom hostname that resolves this account *and* this link's parent as the path travelled |
173
-
174
- Two consequences that generate most "this account is weird" tickets:
175
-
176
- 1. **The same account can legitimately resolve differently depending on how the request reached it** — a
177
- different component version, a different namespace, a different host. Establish which path a failing
178
- request travelled before comparing behavior. (Full detail in "Account Resolution: how a request travels
179
- the account graph".)
180
- 2. **An account with no primary parent and SEVERAL membership edges inherits nothing and runs trunk** until
181
- a path is named — the platform refuses to guess between equally valid parents. That is by design. With
182
- exactly ONE membership edge there is nothing to guess, so it (and everything below it) inherits normally
183
- — a missing `parentId` is not itself a problem.
184
-
185
- ### Users are a separate model — don't reason about them as a tree
186
-
187
- - A user is **global, keyed by email**. There is no per-account copy of a person.
188
- - A user **belongs to many accounts** (a membership list, not a parent pointer).
189
- - A user's **custom fields are per-account**: the same person can be `role: 'Admin'` on one account and
190
- `role: 'Viewer'` on another. A field written while bound to the wrong account lands on the wrong account.
191
- - **Access reach is a union over every link** — a user credentialed on a parent can act on an account below
192
- it through any link, which is deliberately broader than the single-path component inheritance.
193
-
194
- There is no dedicated user MCP tool. For access questions ("who can see this client account?", "why does
195
- this user see the wrong data?") use `mcp_sql_query` against `user` / `user_account`, or `account.users([scope:
196
- 'children'])` from inside a component. See `features/account-management.md` (`mcp_get_guide`).
197
-
198
- ### Reading the shape
199
-
200
- `account-info.json` / `mcp_account_view` carry a `resolution` block — the ONE place these facts appear
201
- (nothing in it is repeated elsewhere in the file). Read it in this order:
202
-
203
- - **`role`** + **`summary`** — `OWNER` (resolves its own component trunk: the files here ARE its components)
204
- or `SUBSCRIBER` (resolves ANOTHER account's components with a variant branch applied). `summary` says it
205
- in one sentence, naming the owner. Read this before you touch anything.
206
- - **`accountId`** — the account described. In a repo export the top-level `id` is the same account; in a
207
- legacy export it is the hierarchy ROOT, so prefer `resolution.accountId`.
208
- - **`type`** — decide repo/ownership per the table above.
209
- - **`resolvedDatabaseName`** vs **`databaseName`** — the namespace actually in effect vs the account's own
210
- override. Check this first when documents are "missing".
211
- - **`relationships`** — every link upward, primary first, each with its own `branchName` / `databaseName` /
212
- `domainName`. **This is where you see that an account has two parents, and which link carries what.**
213
- - **`componentBranch`** (+ `componentOwnerAccountId` / `componentOwnerAccountName`) — the component-variant
214
- branch in effect for this resolution and who owns it. A `SUBSCRIBER` with no `componentBranch` subscribes
215
- through an edge that nothing anchored this resolution to (multi-parent, no path named) — `summary` says so.
216
- - **`branchName`** — the account **repo's** trunk sync branch. Not a component-variant branch. Do not confuse
217
- these two.
218
- - **`parents`** / **`children`** — the anchored ancestor chain and a DIRECT-child summary with counts (repo
219
- exports only; the default `mcp_account_view` shape carries the full tree instead). The deep tree is a
220
- lookup — `mcp_account_user_admin` with `action:'hierarchy'` and a `depth` — never a committed artifact.
221
- - **`componentBranches`** (top level) — variant branches this account **owns**, with drift and subscriber
222
- counts. Check it before editing a shared component.
148
+ | Feature, enhancement, shared behavior fix | usually the owning `PLATFORM` / `PRODUCT` account — **not** the `CLIENT` that reported it |
149
+ | Production investigation, client-specific data issue | the affected `CLIENT` account's data and runtime history |
150
+ | Both | confirm the symptom on the `CLIENT`, then move to the owning repo to change code |
223
151
 
224
152
  ### Repo selection rules
225
153
 
226
- - **Inside the target implementation repo**: read `account-info.json` and inspect `/components` directly.
227
- - **Inside one repo but supporting a different account**: switch to the correct repo for that account if
228
- available; otherwise use `mcp_account_view` / `mcp_component_view` / `mcp_component_grep`.
229
- - **Outside any repo**: rely on the tools for account structure and component source.
230
- - **Never create a new local repo/directory just because a ticket references an account name.** First resolve
231
- the account type and parent hierarchy. Only work from an existing indexed repo unless the user explicitly
232
- asks you to create or clone one.
154
+ - **Inside the target implementation repo**: read `account-info.json` and inspect `components/` directly.
155
+ - **Inside one repo but supporting a different account**: switch to that account's repo if it exists;
156
+ otherwise use `mcp_account_view` / `mcp_component_view` / `mcp_component_grep`.
157
+ - **Outside any repo**: rely on the tools, and `mcp_get_guide` for the front-stage guides.
158
+ - **Never create a new local repo/directory just because a ticket references an account name.** Resolve the
159
+ account type and parent hierarchy first, and work only from an existing indexed repo unless the user
160
+ explicitly asks you to clone one.
161
+
162
+ There is no dedicated user MCP tool. For access questions — "who can see this client account?", "why does
163
+ this user see the wrong data?" — use `mcp_sql_query` against `user` / `user_account`. Remember a user's
164
+ custom fields are stored **per bound account**, so the same person can differ per account.
233
165
 
234
- Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens, committing work, and diagnosing production issues.
166
+ Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens,
167
+ committing work, and diagnosing production issues.
235
168
 
236
169
  ## Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
237
170
 
@@ -303,15 +236,14 @@ every live component present at its real id, and nothing extra.
303
236
 
304
237
  ### The surfaces and their intended behavior
305
238
 
306
- | Command / tool | What it touches | Danger |
239
+ | Command | What it touches | Danger |
307
240
  |---|---|---|
308
241
  | `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
309
- | `mcp_component_edit` `mode:'stage'` | Redis staging cache for one component/field. | none |
310
- | `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) |
242
+ | `remits-cli components status` | Reads the local branch/user staging scope and branch resolution. | none |
243
+ | `remits-cli components clear` | Clears the local branch/user staging cache without changing DB or git. | none |
311
244
  | `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** |
312
245
  | `remits-cli components sync` **on a variant branch** | Writes `ComponentVariant` overlays for that branch only. Never touches trunk rows or the account's trunk branch. When the checkout identifies a subscribing account, the branch-local `account-info.json` is refreshed for that subscriber; `--dry-run` reports the plan without writes. | medium (a missing file becomes a **tombstone** that hides the component from subscribers) |
313
246
  | `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** |
314
- | `mcp_component_create` | Writes a **new** component row **directly to the DB** (immediate, new id). | medium (can create duplicates) |
315
247
 
316
248
  Key implications:
317
249
  - **`stage` is always safe** — stage and test as much as you want; it never reconciles or deletes.
@@ -337,9 +269,8 @@ Key implications:
337
269
  `git add -A && git commit && git push`, `remits-cli components sync`, `git pull --ff-only`.
338
270
 
339
271
  **Create a new component:** add `new_Name.groovy` (+ `.json` / `.meta.yml` as applicable). Sync assigns the
340
- durable id and renames the files. `mcp_component_create` is the direct-DB alternative for no-repo or
341
- remote-account work — do not use it to work around an id/name mismatch, and never to replace a component that
342
- was unexpectedly deleted or renumbered.
272
+ durable id and renames the files. Do not create a direct database row to work around an id/name mismatch, and
273
+ never create a replacement for a component that was unexpectedly deleted or renumbered.
343
274
 
344
275
  **Delete a component (deliberate only):** remove **all** of that component's files from the repo, confirm via
345
276
  `git status` that only those files are gone, then sync — the delete phase removes exactly that DB row. Deletion
@@ -362,9 +293,9 @@ file is catastrophic.
362
293
  ### If something looks wrong — stop, don't paper over
363
294
 
364
295
  If sync reports unexpected `deleted` / `created` / `renamed`, uniqueness errors, or missing components — or you
365
- discover id drift — **stop. Do not re-run sync, do not `components commit`, and do not use
366
- `mcp_component_create` to "make ids line up" or to replace a deleted component.** Those actions compound the
367
- corruption. Preserve the repo-local session log and tool responses, and reconcile source-of-truth first.
296
+ discover id drift — **stop. Do not re-run sync, do not `components commit`, and do not create replacement
297
+ components to "make ids line up" or replace a deleted component.** Those actions compound the corruption.
298
+ Preserve the repo-local session log and tool responses, and reconcile source-of-truth first.
368
299
 
369
300
  **Safe recovery pattern (repo ↔ DB drift):**
370
301
  1. Establish DB truth: `mcp_account_view` for the full live inventory; `mcp_component_view` to confirm and read
@@ -559,182 +490,107 @@ The right model is:
559
490
 
560
491
  Never treat "it looks right in prod data inspection" as sufficient proof that a code change is verified.
561
492
 
562
- ## Platform Mental Model: Front Stage vs Back Stage
563
-
564
- | Layer | What It Contains | Purpose |
565
- |-------|------------------|---------|
566
- | **Front Stage** | Schemas, Readers, Actions, Embeddables, Rules, HtmlTemplates, Agents, Tests. Firestore **documents** are the business data (invoices, payments, vendors). | What the user sees and interacts with. Components are the building blocks; documents are the data. |
567
- | **Back Stage** | Lifecycle **records**: Objects, ObjectLogs, Events, Alerts. Cloud Logging traces. | Audit trail of everything the platform executed. Used for debugging and investigation. |
568
-
569
- ### Documents vs Records
570
-
571
- - **Documents** (Firestore) = business data. Always have `account_id`, `object_id`, `_lastModifiedAt`, `_lastModifiedBy`, `_lastModifiedOn`. Query with `mcp_firestore_search`.
572
- - **Records** (MySQL) = execution audit trail, tied to `object_id`:
573
- - **Object** — ingestion anchor (file, HTTP request). Has name, status, body/content.
574
- - **ObjectLog** — log entries from Readers/Actions. Has `type`, `description`, `content`, `threadGroupingId`.
575
- - **Event** — scheduled or executed actions. Has `action`, `status`, `eventDate`, `threadGroupingId`.
576
- - **Alert** — notices (info/warning/error). Has `status`, `type`, `active`, `threadGroupingId`.
577
- - Use `mcp_record_listing` when you need to find candidate records first, and `mcp_record_view` when you already have the exact record ID.
578
-
579
- ### HTTP Audits
580
-
581
- HTTP Audits are a first-class Firestore investigation surface for raw HTTP traffic, but they are **opt-in** per account.
582
-
583
- - The feature is controlled by `Account.enableHttpAudits`.
584
- - If `enableHttpAudits` is `false`, do not expect Firestore HTTP audit documents to exist for that account.
585
- - If `enableHttpAudits` is `true`, the platform writes normalized inbound and outbound HTTP audit documents to Firestore and also emits websocket `"http"` payloads using the same normalized shape.
586
-
587
- What gets captured when enabled:
588
-
589
- - **Inbound** HTTP handled by Readers
590
- - **Outbound** HTTP made from front-stage code through the `rest(...)` DSL
591
-
592
- Storage contract:
593
-
594
- - HTTP audits live in monthly Firestore collections, not in one global collection.
595
- - Collection path format is:
596
- - `http-audits/http-audits-YYYY-MM/entries`
597
- - Always start with the month that matches when the request likely ran.
598
- - If the run may have crossed a month boundary, check the adjacent month collection too.
599
-
600
- Normalized audit shape:
601
-
602
- - top-level `timestamp`, `month`, `direction`, `transport`, `success`, `component`, `request`, `response`
603
- - optional `error`, `durationMs`, `referenceId`, `category`
604
- - `direction` is `INBOUND` or `OUTBOUND`
605
- - `request` commonly contains `method`, `url`, `uri`, `path`, `queryString`, `headers`, `params`, `body`, `contentType`
606
- - `response` commonly contains `statusCode`, `contentType`, `headers`, `body`
493
+ ## The Investigation Model
607
494
 
608
- Investigation rules:
495
+ Front stage — Schemas, Readers, Actions, Embeddables, Rules, HtmlTemplates, Agents, Tests, and the Firestore
496
+ **documents** that hold business data — is described in `platform-overview.md`. This section is only about
497
+ the **back-stage lifecycle records** an investigation actually reads, and how to read them.
609
498
 
610
- - Use `mcp_firestore_search` to query HTTP audits directly from Firestore when the account has `enableHttpAudits`.
611
- - Prefer tight filters and low limits. Good filters include:
612
- - `direction`
613
- - `success`
614
- - `request.method`
615
- - `request.path`
616
- - `component.name`
617
- - `response.statusCode`
618
- - Treat HTTP audits as the best source when the question is:
619
- - what exact HTTP request came in
620
- - what exact outbound request was sent
621
- - what status code or response body came back
622
- - whether a Reader or Action actually made the expected HTTP call
623
- - Do not confuse HTTP audits with business documents. They are audit records stored in Firestore for investigation, not customer data collections like `invoices` or `freight_contracts`.
624
- - Do not assume every HTTP call will appear. The platform intentionally excludes some paths from HTTP audit persistence, such as configured audit-exclusion endpoints.
499
+ ### Which tool reads which record
625
500
 
626
- How to combine them with the rest of the investigation stack:
501
+ The model — Documents (Firestore business data) vs Objects and their record trail (MySQL), linked by
502
+ `object_id` — is in `platform-overview.md` → *Objects, Documents, and the Record Trail*. What matters
503
+ here is the tool and the filterable fields:
627
504
 
628
- - Use `mcp_firestore_search` on business collections to find the affected document.
629
- - Use `object_id` to pivot into `mcp_object_activity` and lifecycle records.
630
- - Use `mcp_firestore_search` on the monthly HTTP audit collection to inspect the raw HTTP request/response evidence around the same time.
631
- - Use component source plus the HTTP audit payload to explain whether the problem is in request formation, partner response, or downstream document processing.
632
-
633
- ### Record `content` / Context Mental Model
634
-
635
- When investigating lifecycle records, treat `content` as the **persisted workflow context**, not as a raw dump of everything that was in memory during execution.
636
-
637
- - Runtime component context is the live data a component has available while it is executing.
638
- - Persisted record context is the sanitized subset written to `Alert.content`, `Event.content`, `Object.content`, and `ObjectLog.content`.
639
- - Persisted `content` should be assumed to be the platform's intentional investigation-friendly snapshot of that workflow step.
640
-
641
- Understand record context this way:
642
-
643
- - **Object.content** captures ingestion/request/file context at the time the `Object` was created or updated.
644
- - **ObjectLog.content** captures `contextForSaving()` from the component that logged the object, so it reflects that component's point-in-time view of the workflow.
645
- - **Event.content** captures `contextForSaving()` from the component that scheduled the event, plus explicit event options.
646
- - **Alert.content** captures `contextForSaving()` from the component that raised the alert, plus explicit alert body data.
647
-
648
- This is the key investigation question:
649
-
650
- - not just "what keys are present?"
651
- - but "what did the producing component know at that exact workflow step, and what did the platform intentionally keep for persistence?"
505
+ | Record | Query it with | Fields worth filtering on |
506
+ |---|---|---|
507
+ | document | `mcp_firestore_search` | any schema field, plus `account_id`, `object_id`, `_lastModifiedAt` |
508
+ | `object` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `name`, `referenceId` |
509
+ | `object_log` | `mcp_record_listing` / `mcp_record_view` | `type`, `description`, `content`, `threadGroupingId` |
510
+ | `event` | `mcp_record_listing` / `mcp_record_view` | `action`, `status`, `eventDate`, `threadGroupingId` |
511
+ | `alert` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `active`, `threadGroupingId` |
652
512
 
653
- ### Provenance Fields
513
+ `mcp_record_listing` finds candidates when you do not know the id; `mcp_record_view` opens an exact one;
514
+ `mcp_object_activity` returns one Object's whole timeline in order.
654
515
 
655
- Persisted record context should be read with provenance in mind:
516
+ **Check the data lane before concluding a record does not exist** — `--data-mode test` and `prod` read
517
+ different lanes (see `platform-overview.md` → *Test and Production Data Lanes*).
656
518
 
657
- - **`source_bcd`** = the immediate component that produced the persisted record context. Example: `[Action:18] DataPlus Invoice Posting`
658
- - **`upstream_source_bcd`** = the prior workflow hop, when the current component inherited context that already had a `source_bcd`
659
- - **`object_id`** = execution anchor linking documents and records
660
- - **`threadGroupingId`** = processing-chain correlation key across records and system logs
519
+ ### Correlation keys
661
520
 
662
- Use these fields to reconstruct the path:
521
+ - **`object_id`** — links documents to records. Pivot `mcp_firestore_search` → `mcp_object_activity`.
522
+ - **`threadGroupingId`** — groups every record and log line from one processing chain, and is also the
523
+ request's trace id. Pivot into `mcp_system_logs` and `mcp_performance_trace`.
524
+ - **`node`** — resolves to `serviceName` + `region` for log queries (table below).
525
+ - **`sessionId`** — pivots into persisted AI activity via `mcp_ai_session_search`.
663
526
 
664
- 1. Identify the record you are inspecting.
665
- 2. Read `source_bcd` to determine the immediate producing component.
666
- 3. If present, read `upstream_source_bcd` to understand the prior workflow hop.
667
- 4. Use `object_id` to open the full object timeline.
668
- 5. Use `threadGroupingId` to correlate related events, alerts, object logs, and system logs.
669
- 6. Open the relevant component source to explain why that component saved that particular context.
527
+ ### Reading a record's `content` — persisted context, not a memory dump
670
528
 
671
- ### Why Persisted Context Looks Different From Runtime Context
529
+ `content` on an `object`, `object_log`, `event`, or `alert` is the **sanitized snapshot** the platform
530
+ deliberately kept of what the producing component knew at that workflow step. It is not everything that was
531
+ in memory.
672
532
 
673
- Do not assume missing fields mean the component never had them in memory. Persisted context is deliberately sanitized before save:
533
+ - `Object.content` — ingestion/request/file context when the Object was created or updated.
534
+ - `ObjectLog.content` — the logging component's point-in-time view.
535
+ - `Event.content` — the scheduling component's view, plus the explicit event options.
536
+ - `Alert.content` — the raising component's view, plus the explicit alert body.
674
537
 
675
- - non-serializable runtime objects are removed
676
- - configured removal keys such as `requestBody`, `params`, and `token` are removed recursively
677
- - large strings may be summarized instead of stored in full
678
- - oversized maps/collections may be pruned by serialization limits
679
- - sensitive-looking keys such as `password`, `secret`, `authorization`, `cookie`, `clientSecret`, and similar values may be redacted
680
- - Account/User schema fields marked `sensitive: true` may also be redacted before persistence
538
+ **Missing or `[REDACTED]` does not mean the component never had it.** Before persisting, the platform drops
539
+ non-serializable objects, strips `requestBody` / `params` / `token` recursively, summarizes very large
540
+ strings, prunes oversized maps and collections, and redacts secret-looking keys (`password`, `secret`,
541
+ `authorization`, `cookie`, `clientSecret`, …) plus any Account/User schema field marked `sensitive: true`.
542
+ When explaining a record, distinguish what the workflow *had* at runtime from what the platform *kept*.
681
543
 
682
- So if a user asks "why isn't the full payload here?" or "why is this value `[REDACTED]`?", the right answer is usually:
544
+ **Provenance travels in the context itself:**
683
545
 
684
- - the workflow may have had the full value at runtime
685
- - the persisted record intentionally stores only the safe, investigation-friendly subset
546
+ - **`source_bcd`** — the immediate component that produced this record, e.g. `[Action:18] DataPlus Invoice Posting`
547
+ - **`upstream_source_bcd`** — the prior workflow hop, when the context was inherited from one
686
548
 
687
- When explaining a record, always distinguish between:
549
+ **Never interpret `content` in isolation.** Pair it with the record type, `source_bcd`, any
550
+ `upstream_source_bcd`, the `object_id` timeline, the `threadGroupingId` chain, and the producing
551
+ component's source. The goal is not to find a suspicious record — it is to explain **why the context looks
552
+ exactly the way it does relative to the workflow step that produced it**.
688
553
 
689
- - what the workflow likely had at runtime
690
- - what the platform intentionally kept in persistent `content`
554
+ ### HTTP audits
691
555
 
692
- ### Investigation Rule for Record Context
556
+ Raw inbound (Reader) and outbound (`rest(...)`) HTTP is persisted to Firestore — but **only for accounts
557
+ with `Account.enableHttpAudits`**. When it is off, the absence of audit documents proves nothing.
693
558
 
694
- Never interpret record `content` in isolation.
559
+ Audits live in **monthly** collections, not one global collection:
695
560
 
696
- Always pair it with:
561
+ ```
562
+ http-audits/http-audits-YYYY-MM/entries
563
+ ```
697
564
 
698
- - the record type (`object`, `object_log`, `event`, `alert`)
699
- - the immediate producer (`source_bcd`)
700
- - the upstream producer when present (`upstream_source_bcd`)
701
- - the object timeline (`object_id`)
702
- - the execution chain (`threadGroupingId`)
703
- - the producing component source (`mcp_component_view`, `mcp_component_grep`, or local `/components`)
565
+ Start with the month the request ran in, and check the adjacent month if the run may have crossed a
566
+ boundary. Query them with `mcp_firestore_search` like any other collection; the filters worth reaching for
567
+ are `direction` (`INBOUND` / `OUTBOUND`), `success`, `request.method`, `request.path`, `component.name`, and
568
+ `response.statusCode`. Some paths are deliberately excluded from persistence, so a missing audit is not
569
+ proof a call was never made.
704
570
 
705
- The goal of an investigation is not only to find a suspicious record. It is to explain **why the context looks exactly the way it does relative to the workflow step that produced it**.
571
+ Reach for audits when the question is *what exact request went out, what came back, and did this component
572
+ actually make the call* — then use the component source plus the payload to place the fault in request
573
+ formation, the partner's response, or downstream processing. Full captured shape, websocket behavior, and
574
+ embeddable patterns: `features/http-audits.md` (`mcp_get_guide`).
706
575
 
707
- ### AI Request Response Records
708
- - AI activity is also persisted as `AI Request Response` records keyed by `sessionId`.
709
- - In component code, use `ai_request_response([sessionId: id])` to load the stored request/response history for a session.
710
- - Pass `first: true` or `last: true` when you need a single record.
711
- - These records are especially useful during investigations, prompt tuning, and any workflow where you need to review a prior AI exchange or replay a stored provider response in tests.
712
- - If a document or agent state already stores a session ID, treat that as a direct pivot into persisted AI session activity.
576
+ ### AI activity
713
577
 
714
- ### AI Session Groupings
578
+ Every `ai()` call and Agent turn is persisted. Two ways in:
715
579
 
716
- - For front-stage investigation and tuning work, use `mcp_ai_session_search` to search persisted AI session groupings and audit grouping detail. A grouping spans every related session that shares one grouping ID — the agent turns plus its guardrail and internal `ai()` calls.
717
- - This tool mirrors the admin AI Groupings UI:
718
- - search filters: `search`, `sessionId`, `groupingId`, `user`, `account`, `agent`, `scope`, `status`
719
- - detail parts: `index`, `stats`, `transcript`, `tool_calls`, plus the record sections `full`, `conversation_messages`, `system`, `response`, `tools`
720
- - Audit a grouping with a **map → open** workflow, not a whole-detail dump (which re-sends the same cumulative prompt on every record and burns tokens):
721
- 1. `action:"detail"` + `summaryOnly:true` (or `parts:["index"]`) → the MAP, no payloads: a per-request record list (id, callType, agent, model, tokens, raw `finishReason`, `toolCallsRequested`, `responsePreview`) + a timeline of interleaved tool-call ids. Same rows the admin grouping detail shows.
722
- 2. Then open only what you need: `recordIds:[<id>]` + `parts:["conversation_messages"|"system"|"response"|"tools"|"full"]` for one record; `parts:["transcript"]` for the deduplicated end-to-end conversation (each message once, in order); `parts:["tool_calls"]` (then `toolCallIds:[<id>]`) for the exact input/result a tool produced.
723
- - **Tool-result lens — do not conflate the two:** `parts:["tool_calls"]`/`toolCallIds` returns what a tool **PRODUCED** (the exact `ai_tool_call` result). That is **not** necessarily what the model saw next turn — front-stage control keys (`_offload`/`_offloadSynopsis`, `_hideResult`, `_message`, `_supersedes`/`_evict*`, `_stop`, …) can offload, hide, summarize, or collapse the re-provided value. To see what the AI **CONSUMED**, read the tool_result inside `conversation_messages`/`transcript`. (`resultChars` in the tool_calls index flags the large results most likely to have been transformed.)
724
- - Use it when you need to understand:
725
- - what prompts, system instructions, tools, and responses were actually sent for a session
726
- - how an Agent behaved across one grouping, including prompt-tuning and tool-usage opportunities
727
- - whether a suspicious answer was caused by prompt design, tool definitions, missing context, or model behavior
728
- - This is a front-stage builder tool, so interpret it alongside the front-stage guides:
729
- - `docs/guides/components/agent-components.md` for how Agents are authored and structured
730
- - `docs/guides/features/ai-support.md` for the platform AI surface (`ai()`, agent tooling, model behavior, and related patterns)
731
- - Investigation rule: do not read raw AI session output in isolation. Pair it with the owning Agent component source and the AI support guide so you can explain why the session looked the way it did and where tuning should happen.
580
+ - **`mcp_ai_session_search`** — search groupings and open their detail. A grouping spans every session
581
+ sharing one grouping id: the agent turns plus its guardrail and internal `ai()` calls. Use the
582
+ **map → open** flow described under its Tool Reference entry, never a whole-detail dump.
583
+ - **`ai_request_response([sessionId: id])`** — from inside component code, loads the stored
584
+ request/response history for a session (`first: true` / `last: true` for one record). This is also how
585
+ you replay a stored provider response in a test.
732
586
 
733
- ### Correlation Keys
587
+ If a document or agent state already carries a session id, that is a direct pivot into persisted AI
588
+ activity.
734
589
 
735
- - **`object_id`** — links documents to records. Pivot from `mcp_firestore_search` → `mcp_object_activity`.
736
- - **`threadGroupingId`** — groups all records from the same processing chain. Use with `mcp_system_logs`.
737
- - **`node`** → resolves to `serviceName` + `region` for log queries.
590
+ **Before tuning any prompt, read the session.** `features/ai-session-investigation.md` owns the method —
591
+ per-turn forensics, what each layer proves, and the rule that what a tool **PRODUCED** is not necessarily
592
+ what the model **CONSUMED**. `features/ai-strategy.md` owns what to change once you know. Changing a prompt
593
+ before reading the persisted request/response is guessing.
738
594
 
739
595
  ### Node Reference Table
740
596
 
@@ -746,13 +602,6 @@ The goal of an investigation is not only to find a suspicious record. It is to e
746
602
 
747
603
  `mcp_system_logs` accepts `node` directly and resolves it automatically.
748
604
 
749
- ### Repository vs External Context
750
- | Scenario | Preferred data source | When to call account/component tools |
751
- |----------|----------------------|--------------------------------------|
752
- | In the target account repo | Local `account-info.json` and `/components` | Only if files are missing or you need live prod insight not present locally |
753
- | In Account A repo but question is for Account B | Switch to Account B repo if available; otherwise read its files from disk | If Account B repo isn't available, use `mcp_account_view`, `mcp_component_view`, `mcp_component_grep` for that account |
754
- | Outside any repo | N/A | Always use the tools to load structure and source |
755
-
756
605
  ## Getting Started
757
606
 
758
607
  ### Authentication
@@ -855,28 +704,24 @@ remits-cli data-mode set test # Switch back to test for development
855
704
 
856
705
  ### The Golden Rule: Writing Code Is Not Finishing the Job
857
706
 
858
- **A change is not complete until it is verified.** Writing or modifying a component is only the first step. You must always confirm the change actually works before telling the user it's done. There are two ways to verify:
859
-
860
- 1. **Test components** (preferred) — Write or update a Remits Test component that exercises the change. This creates a permanent regression check that protects against future breakage. Run it with `remits-cli test run`.
707
+ **A change is not complete until it is verified.** Writing the component is the first step, not the last.
708
+ Two ways to prove it:
861
709
 
862
- 2. **Visual verification with Playwright** — Generate a browser URL with `remits-cli token`, then use `playwright-cli` to open the embeddable and verify the behavior visually. This is how most users think about verification — "let me see it working."
710
+ 1. **A Test component** (preferred) — it exercises the change *and* becomes permanent regression
711
+ protection. Run it with `remits-cli test run`.
712
+ 2. **Visual verification** — `remits-cli token` for a browser URL, then drive it with `playwright-cli`.
713
+ This is how most users think about verification: "let me see it working."
863
714
 
864
- Both are valid. Use Test components when the behavior can be asserted programmatically. Use Playwright when the change is visual or when the user wants to see it. Often you'll do both.
715
+ Use a Test when the behavior can be asserted programmatically, a browser when the change is visual.
716
+ Often both.
865
717
 
866
- **Never skip verification.** If the user says "just do it" or "that's fine, commit it" — verify anyway. Silent bugs erode trust. If you can't verify because there's no Test component and no relevant embeddable, tell the user what you'd need to verify and ask how they'd like to proceed.
718
+ **Never skip verification.** "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
719
+ cannot verify — no Test component, no relevant embeddable — say what you would need and ask how the user
720
+ wants to proceed rather than reporting the work as done.
867
721
 
868
- ### Avoid Brittle Front-Stage Intelligence
869
-
870
- When a Remits component must interpret, classify, extract, reconcile, route, match, or otherwise make judgment calls over variable real-world data, do not implement that intelligence as hard-coded helper methods, regex cascades, keyword lists, filename/layout assumptions, or overly specific branching.
871
-
872
- Use the platform's AI-first pattern instead:
873
- - deterministic code bounds inputs, normalizes obvious protocol details, validates schema shape, performs math, and persists authoritative results
874
- - `ai()`, agents, tools, prompts, and `index_search()` handle fuzzy interpretation and variable document/data understanding
875
- - prompts live in Prompt files when non-trivial and are explicit, schema-grounded, and testable
876
- - tool calls are preferred over scraping JSON out of model text when state needs to change
877
- - deterministic validation checks AI output before writes and provides safe fallbacks
878
-
879
- Regex and narrow helper methods are acceptable for mechanical parsing, exact validation, stable protocol handling, and guardrails. They are not acceptable as the primary strategy for messy document understanding, classification, matching, or other intelligence-like behavior. If a component guide and `docs/guides/features/ai-support.md` point to an AI-supported capability, follow that surface before inventing brittle code.
722
+ > The *design* rule that pairs with this — never solve interpretive problems with regex cascades, keyword
723
+ > lists, or layout-specific branching when the platform's AI surface is the right tool — is in the account
724
+ > repo's `CLAUDE.md` and, in depth, in `features/ai-strategy.md`.
880
725
 
881
726
  ### The Development Fast Loop
882
727
 
@@ -1130,8 +975,8 @@ and a precise diagnosis.
1130
975
  ### The three source layers + the compile cache
1131
976
 
1132
977
  1. **CLI staging cache (Redis, 240-min TTL).** Branch + user + account scoped overrides written by
1133
- `remits-cli components stage` and by the `mcp_component_edit` tool (`mode:'stage'`). These shadow the
1134
- layers below **only during CLI/test-mode execution** (see "When staged overrides apply" below).
978
+ `remits-cli components stage`. These shadow the layers below **only during CLI/test-mode execution**
979
+ (see "When staged overrides apply" below).
1135
980
  2. **Committed branch variants (`ComponentVariant`, MySQL).** Durable, branch-scoped overlays of a
1136
981
  component. Unlike staging these are **not** user-scoped, do **not** expire, and **do** apply to normal
1137
982
  production traffic — for the accounts that subscribe to that branch. See
@@ -1203,7 +1048,6 @@ Staged overrides resolve whenever the execution carries a **CLI-scoped TestMode*
1203
1048
  - `remits-cli test run`
1204
1049
  - `remits-cli tool`
1205
1050
  - `remits-cli tools`
1206
- - `mcp_run_test`
1207
1051
  - tokenized runs minted with a branch-aware CLI token
1208
1052
 
1209
1053
  For `remits-cli tool`, the CLI TestMode now stays active for the **entire tool execution**, not just the
@@ -1240,29 +1084,18 @@ TestMode still uses the committed DB source. Staging remains a dev/verification
1240
1084
  `--verbose` when you need the full staged-entry payload. `remits-cli components clear` removes those entries
1241
1085
  when you intentionally want to fall back to DB source.
1242
1086
 
1243
- ### Stage / commit / clear with the MCP tools
1244
-
1245
- - `mcp_component_edit` `mode:'stage'` → writes the staging cache (Redis). `mode:'commit'` → writes the field
1246
- to the **live DB** and **clears** that component's staging entries. `editMode:'replace'` swaps the whole
1247
- field; `editMode:'targeted'` does anchor-verified line edits.
1248
- - **A `mode:'commit'` promotes ALL of that component's staged fields in one save, not just the named field.**
1249
- Committing `source` also flushes a staged `path` (etc.) and then clears the whole staging entry. The result
1250
- reports the full set in `persist.committedFields` + `persist.stagingCleared` — trust that, and do **not**
1251
- try to "finish" by re-committing a sibling field that was already flushed.
1252
- - `mcp_component_commit` (`read`/`commit`/`clear`) enumerates staged entries and can flush or clear them.
1253
- - **An empty staging scope is the clean, already-promoted state — not a failure.** A `commit`/`clear` against a
1254
- component (or scope) with nothing staged returns a benign success (`alreadyClean:true`, `committedCount:0`),
1255
- not an error. "No staged changes found" means *already promoted*, never *partial commit*. The staging cache
1256
- is a dev-override artifact; its emptiness is the goal. Verify what's actually in the DB with
1257
- `mcp_component_view` / `mcp_integration_validate` — never gate a commit/verification on staging-cache state.
1258
- - **Commit bypasses the staged-override path entirely** (it writes the DB and clears staging), so committing
1259
- is the way to make a change durable and to stop a stale staged entry from shadowing the live component in
1260
- later test runs.
1261
- - `remits-cli components sync` (the `commit`/`sync` mode on the `components` endpoint) syncs the DB from the
1262
- git remote and then **also clears the staged entries** for the synced components, so a clean commit leaves a
1263
- clean staging cache on both the CLI and MCP-tool surfaces. Because sync reconciles the remote repo into the
1264
- live component database, it must pass the Component Integrity Rules first. Never use sync to clear staging, to
1265
- recover from a mismatched ID, or to retry after an unexpected create/delete/rename response.
1087
+ ### Stage / sync / clear with remits-cli
1088
+
1089
+ - `remits-cli components stage` writes local file changes into the Redis staging cache for the current
1090
+ branch/user scope. This is the normal edit/test loop.
1091
+ - `remits-cli components status` shows which branch/variant world the checkout resolves, plus staged entries,
1092
+ staged fields, aliases, hashes, and TTLs. Use `--json` or `--verbose` for the full staged-entry payload.
1093
+ - `remits-cli components clear` drops staged entries when you intentionally want to fall back to committed DB
1094
+ source. An empty staging scope is clean state, not a failure.
1095
+ - `remits-cli components sync` syncs the DB from the pushed git remote and then clears staged entries for the
1096
+ synced components, so a clean promotion leaves a clean staging cache. Because sync reconciles the remote repo
1097
+ into the live component database, it must pass the Component Integrity Rules first. Never use sync only to
1098
+ clear staging, to recover from a mismatched ID, or to retry after an unexpected create/delete/rename response.
1266
1099
 
1267
1100
  ### Stale after sync / commit (the in-memory compile cache)
1268
1101
 
@@ -1277,207 +1110,89 @@ the platform owner recycles the instance.
1277
1110
  ## Account Resolution: how a request travels the account graph
1278
1111
 
1279
1112
  Component inheritance, branch variants, and where data physically lives are all decided by **how the
1280
- current request reached the executing account**. Read this before debugging "my subscriber isn't picking
1281
- up the branch" or "why is this account reading the wrong collection" — those are almost always
1113
+ current request reached the executing account**. Read this before debugging *"my subscriber isn't picking
1114
+ up the branch"* or *"why is this account reading the wrong collection"* — those are almost always
1282
1115
  resolution questions, not component bugs.
1283
1116
 
1284
- **Two kinds of structural edge.**
1285
-
1286
- - **`Account.parentId`** — the legacy primary parent, and still the primary structural edge. An ordinary
1287
- single-parent account with no branch subscription resolves exactly as it did before relationships
1288
- existed. Most production accounts are this shape.
1289
- - **`AccountRelationship` edges** — a join table letting one account be reached through **more than one**
1290
- parent. Exactly one edge per account is `primary` (kept in lockstep with `parentId`); the rest are
1291
- **membership** edges. Each edge can independently carry:
1292
- - `branchName` — the component-variant branch this account subscribes to (see the next section);
1293
- - `databaseName` — a branch-scoped data-segment override;
1294
- - `domainName` — a branch-scoped custom host that reaches this account **through this edge**.
1295
- These are **orthogonal**: subscribing to a component branch never moves an account's data, setting a
1296
- segment override never changes which code runs, and a custom host changes neither. Do not reason about
1297
- one from the others.
1298
-
1299
- **Custom hosts resolve the anchor, not just the account.** An account can have its own
1300
- `Account.domainName`, and an edge can carry one too. A request arriving on an **edge** host resolves the
1301
- edge's *child* as the execution account **and** the edge's *parent* as the branch anchor — which is what
1302
- makes that edge's branch variants apply. An account-level host resolves the account with **no** anchor
1303
- (today's behavior). Edge wins, then the account's own host, so removing an edge degrades cleanly instead
1304
- of taking the hostname offline. Hosts are stored as bare lowercase hostnames; a full URL is normalized on
1305
- the way in. This is what lets a branch deployment get its own domain without a separate account
1306
- tree — `branch.example.com` and `app.example.com` can serve the same owner's components, one overlaid
1307
- with a branch.
1308
-
1309
- **Two traversals, deliberately different.** Confusing them is the usual source of wrong conclusions:
1310
-
1311
- | | Used for | Shape |
1312
- |---|---|---|
1313
- | **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 |
1314
- | **Union reachability** | authorization, "may this caller act on that account" (`--as-account`, tokens) | **permissive** union over `parentId` *and* every active edge |
1315
-
1316
- **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id` (stamped
1317
- onto the `Object`/`Event`/`Alert`/`ObjectLog` records a run creates, so async workers re-resolve on the
1318
- same branch). A **null** anchor means "walk the structural chain" — see below.
1117
+ > The model behind it — the linear inheritance walk, the ambiguity rule, the three orthogonal edge
1118
+ > properties, and path-scoped storage namespaces — is in **`platform-overview.md` → *Account Structure***,
1119
+ > which is already loaded. What follows is how to *observe* it from the CLI.
1319
1120
 
1320
- **How the chain is walked, and the #1 gotcha.** The walk follows `Account.parentId`, and at an account with
1321
- **no** `parentId` it continues through that account's *single* active membership edge. Applied at every hop:
1121
+ **The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id`, stamped onto
1122
+ the `Object` / `Event` / `Alert` / `ObjectLog` records a run creates, so async workers re-resolve on the
1123
+ same branch. A **null** anchor means "walk the structural chain". Entry points that already know the
1124
+ branch supply an anchor; from the CLI you supply it explicitly with `--as-account`, `--variant-branch`, or
1125
+ by using the edge's own host.
1322
1126
 
1323
- | Account shape at a hop | What the walk does |
1324
- |---|---|
1325
- | has a `parentId` | follow it (legacy, unchanged) |
1326
- | no `parentId`, **exactly one** membership edge | **continue through that edge** |
1327
- | no `parentId`, **multiple** membership edges | **stop** — ambiguous, refuses to guess |
1328
- | no `parentId`, no edges | stop — a true root |
1329
-
1330
- **Primary-vs-membership does not gate inheritance; only ambiguity does.** A `parentId`-less subscriber shell
1331
- linked to its owner by one membership edge inherits that owner's components and branch — **and so does
1332
- everything beneath it**. You do not need to give it a real `parentId`.
1333
-
1334
- But a `parentId`-less account with **two** membership edges resolves **trunk and inherits nothing** until an
1335
- anchor is supplied. That is correct-by-design (the inheritance path must stay linear, or component
1336
- name-dedupe would pick an arbitrary winner between two sibling products), not a bug. Entry points that
1337
- already know the branch disambiguate it by branch name; from the CLI you supply it explicitly.
1338
-
1339
- > Before 2026-08 the continuation was applied **only to the account a lookup started from**, so a
1340
- > membership-only account truncated the chain for all of its descendants — they inherited nothing and
1341
- > resolved trunk while the subscriber itself looked fine. If you are reading an older investigation that
1342
- > "fixed" this by reparenting a subscriber under its owner, that workaround is no longer needed.
1343
-
1344
- **Same account, two parents, two answers.** An account reached through Product A versus Product B can
1345
- legitimately resolve a different component variant *and* a different data segment. When you investigate
1346
- such an account, always establish which anchor the failing request used before comparing behavior.
1347
-
1348
- **How to actually see an account's edges.** `mcp_account_view` (and the repo's `account-info.json`) returns
1349
- `resolution.relationships` — one entry per structural link, primary first, each carrying its own
1350
- `branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so this
1351
- block is the only place that answers "how many parents does this account really have, and which link carries
1352
- what?" Start here for any resolution question:
1127
+ ### Seeing an account's edges
1353
1128
 
1354
1129
  ```bash
1355
1130
  remits-cli tool --name mcp_account_view --input '{"accountId": 101}' --data-mode prod
1356
1131
  # then read: resolution.relationships, resolution.resolvedDatabaseName, resolution.componentBranch
1357
1132
  ```
1358
1133
 
1359
- **Practical checklist when a subscription "doesn't work":**
1134
+ `resolution.relationships` returns one entry per structural link, primary first, each carrying its own
1135
+ `branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so
1136
+ this block is the **only** place that answers "how many parents does this account really have, and which
1137
+ link carries what?"
1138
+
1139
+ ### When a subscription "doesn't work"
1360
1140
 
1361
- 1. Does the account have a primary parent, or is it membership-only? Count the entries in
1362
- `resolution.relationships` and check which one is `primary: true`. (Membership-only + multiple edges ⇒
1363
- ambiguous ⇒ trunk.)
1364
- 2. Which **edge** carries the `branchName` — it is on the edge in `resolution.relationships`, and
1365
- `remits-cli components branch <name> --subscribers` prints `via primary|membership edge -> parent N`.
1141
+ 1. Does the account have a primary parent, or is it membership-only? Count `resolution.relationships` and
1142
+ see which is `primary: true`. (Membership-only **and** several edges ⇒ ambiguous ⇒ trunk, by design.)
1143
+ 2. Which **edge** carries the `branchName`? `remits-cli components branch <name> --subscribers` prints
1144
+ `via primary|membership edge -> parent N`.
1366
1145
  3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
1367
1146
  account's own edge selects the branch, exactly as production would.
1368
- 4. Check the resolved layer, not the source text: `testComponentSource` / the `variant:<id>:<hash>`
1147
+ 4. Check the resolved layer, not the source text: `testComponentSource`, or the `variant:<id>:<hash>`
1369
1148
  compile signature (see "Diagnosing a variant").
1370
1149
 
1371
- **The same block answers the non-branch resolution questions too:** "why are this account's documents not
1372
- where I expect?" → compare `databaseName` vs `resolvedDatabaseName` and any edge `databaseName`. "Why does
1373
- this hostname land on the wrong account?" → `domainName` vs `resolvedDomainName` and the edge `domainName`
1374
- (an edge host wins over the account's own, and additionally supplies the path travelled).
1150
+ ### The same block answers the non-branch questions
1375
1151
 
1376
- **Users are not part of this graph.** A user is global, belongs to many accounts, and can reach an account
1377
- below one they are credentialed on through **any** link — a deliberately broader rule than the single-path
1378
- component inheritance above. Their custom fields (`role`, `team`, …) are stored **per account**, so the same
1379
- person can differ per account. There is no user MCP tool: use `mcp_sql_query` against `user` /
1380
- `user_account`, and read `features/account-management.md` (`mcp_get_guide`) for the model.
1152
+ - *"Why are this account's documents not where I expect?"* → compare `databaseName` vs
1153
+ `resolvedDatabaseName`, and check for a `databaseName` on one of the `relationships` edges — it applies
1154
+ to everything below that edge.
1155
+ - *"Why does this hostname land on the wrong account?"* → compare `domainName` vs `resolvedDomainName` and
1156
+ the edge `domainName`. An **edge** host wins over the account's own, and additionally supplies the path
1157
+ travelled — which is what makes that edge's branch variants apply.
1158
+
1159
+ **Users are not part of this graph** (see `platform-overview.md`). There is no user MCP tool — use
1160
+ `mcp_sql_query` against `user` / `user_account`.
1381
1161
 
1382
1162
  ## Branched Component Variants (per-account component overrides)
1383
1163
 
1384
- Sometimes one account — often a customer nested several levels down a hierarchy — needs *slightly*
1385
- different behavior from a component owned by its platform or product account. The wrong answer is
1386
- per-account `if/then` logic inside the origin component. The right answer is a **branch variant**: a
1387
- durable, branch-scoped overlay of that component, which only the accounts subscribed to that branch
1388
- resolve.
1389
-
1390
- ### The model (three moving parts)
1391
-
1392
- 1. **The origin account owns the component and the branch.** Say platform account 1 owns
1393
- `Extract Invoice` (Action 50). Its repo `remits-<name>` has trunk branch `main` and a second git branch
1394
- `feature_branch`.
1395
- 2. **A `ComponentVariant` row is the overlay.** Committing on `feature_branch` stores rows owned by
1396
- **account 1**, on branch `feature_branch`, for the components whose content **differs from trunk**. A git
1397
- branch physically contains every file; only the *differing* ones become variants. That is computed at
1398
- sync time — you never declare it.
1399
- 3. **A child account subscribes via its relationship edge.** Account 101's `AccountRelationship` edge
1400
- carries `branchName = 'feature_branch'`. Resolution then walks 101's inheritance chain and applies
1401
- account 1's `feature_branch` overlays.
1402
-
1403
- Consequences worth internalizing:
1404
-
1405
- - **A branch is not an account.** Subscription is many-to-many: five accounts can share one branch, and a
1406
- subscribing account still has its own trunk components, which merge on top as usual.
1407
- - **Nested hierarchies work.** The overlay applies to the subscribing account *and its descendants*, until a
1408
- nearer edge overrides it. Resolution consults every owner on the inheritance path, so a deeply nested
1409
- client picks up a platform-owned variant.
1410
- - **Component identity is shared.** A variant keeps the origin's component id — it is an overlay, not a
1411
- copy. That is what makes drift detectable and what distinguishes this from just duplicating the component
1412
- onto the child account.
1413
- - **Subscribing an account** attaches the branch to that account's relationship edge:
1414
- `remits-cli components branch <name> --subscribe <accountId> [--domain <host>]` (and `--unsubscribe <accountId>` to return it
1415
- to trunk). It is also editable per-edge on the admin account page. Authorization is downward-only: you can
1416
- only subscribe an account reachable from one you already have access to.
1417
- - **`--subscribe` sets the branch on an edge that must already exist — it cannot create one.** If the
1418
- account has no relationship edge to the owner you will get *"Account N has no relationship edge to
1419
- subscribe; add a membership edge first"*. Create it with
1420
- `mcp_account_user_admin` `action:'edge_add'` (`targetAccountId` = the child, `parentAccountId` = the
1421
- owner), which also accepts `branchName` so you can create and subscribe in one call. The admin account
1422
- page does the same thing.
1423
- - **Many older accounts have no relationship row at all.** The edge table was introduced after the fact
1424
- and never backfilled, so an account whose parent link is only `Account.parentId` resolves fine but has
1425
- nothing for `--subscribe` to attach to. `mcp_account_user_admin` `action:'reparent'` with the account's
1426
- *current* parent is the one-call repair: it creates the missing primary edge and changes nothing else.
1427
- - **When the account has several parents, say which edge you mean:**
1428
- `--subscribe <accountId> --parent-account <ownerId>`. Without it the CLI picks the edge to the owner
1429
- whose branch you are managing, then falls back to the account's primary edge — which may not be the
1430
- edge you intended. `--subscribers` prints `via primary|membership edge -> parent N` so you can confirm.
1431
- - **Retiring a branch** is explicit: `remits-cli components branch <name> --retire`. Deleting the *git*
1432
- branch does **not** remove its overlays — subscribers would keep resolving a branch that no longer exists.
1433
- Retire refuses while the branch still has subscribers unless you pass `--force`.
1434
-
1435
- A branch can also **add** a component (a file whose prefix is not a live trunk id — e.g. `new_Foo.groovy` —
1436
- becomes a *branch-only* component identified by name) and **remove** one (a trunk component with no file on
1437
- the branch becomes a *tombstone*, hiding it from subscribers).
1438
-
1439
- **Two sharp edges worth knowing before you edit a branch:**
1440
-
1441
- - **Deleting a file means "remove the component", not "stop overriding it."** To withdraw an override, make
1442
- the file identical to trunk again — the sync then removes the variant row. Deleting it creates a tombstone
1443
- and hides the component from every subscriber.
1444
- - **Keep the branch rebased.** A branch physically carries every file, so anything trunk added *after* the
1445
- branch was cut looks like a deliberate deletion. The sync refuses a wholesale removal (more than ~a third
1446
- of a kind) and tells you to rebase, but a small stale gap will tombstone silently. Rebase onto trunk before
1447
- you commit a branch you have not touched in a while.
1164
+ When one account — often a customer nested several levels down — needs *slightly* different behavior from
1165
+ a component owned by its platform or product account, the answer is a **branch variant**: a durable,
1166
+ branch-scoped overlay of that component, resolved only by accounts subscribed to that branch. The wrong
1167
+ answer is per-account `if/then` logic inside the origin component.
1448
1168
 
1449
- **Every component kind can be varied** — `schema`, `reader`, `action`, `embeddable`, `htmltemplate`,
1450
- `rule`, `test`, `utility` (agent), `tool`, `prompt` — including tombstones and branch-only additions. A
1451
- schema variant is worth calling out: it can change the JSON schema itself *and* `collectionName`, so it
1452
- changes validation and where the subscriber's documents physically land. Treat schema variants with the
1453
- same care as a trunk schema change.
1454
-
1455
- **What a branch may override.** A variant speaks the same `.meta.yml` vocabulary trunk does — `name`,
1456
- `description`, `summary`, `mermaid`, `category`, `type`, `path`, `injectionType`, `collectionName`, `job`, `model`,
1457
- `agentTimeout`, `mcp`, `cli`, `global`, `purpose`, `auxiliary`, plus Schema flags (`enableTrigger`,
1458
- `enableFullText`, `enableRAG`, `enableRevisions`, `enableBigQuerySync`, `enableRules`, `anchor`) — and the
1459
- component's content files. Keys outside that set are ignored, deliberately: trunk cannot express them either,
1460
- so allowing them would mean a branch behaves one way and silently loses that behavior the moment it is
1461
- promoted to trunk.
1169
+ > **`features/subscriber-branch-promotions.md` (`mcp_get_guide`) is the guide for this.** It owns the
1170
+ > mental model, the five scenarios you will actually meet, the full promotion procedure, what happens to a
1171
+ > `new_` component across a promotion, merge-conflict resolution file by file, removals, and the approval
1172
+ > gates for running a promotion as a coding agent. **Load it before promoting anything.** This section
1173
+ > covers only the CLI surface and the traps that bite at the command line.
1174
+
1175
+ Three facts that everything else follows from:
1176
+
1177
+ - **A branch is not an account.** Subscription is many-to-many, it applies to the subscribing account
1178
+ *and its descendants*, and a subscribing account still has its own trunk components which merge on top.
1179
+ - **A variant keeps the origin's component id** — it is an overlay, not a copy. That is what makes drift
1180
+ computable.
1181
+ - **Sparseness is computed at sync time, not declared.** A git branch physically contains every file; only
1182
+ the ones whose content *differs from trunk* become variants. A file that is absent becomes a
1183
+ **tombstone**; a file whose id prefix is not a live trunk id (`new_Foo.groovy`) becomes a **branch-only**
1184
+ component keyed by name.
1462
1185
 
1463
1186
  ### Which world does your working tree resolve? (read this before you run anything)
1464
1187
 
1465
- You will work from **two different checkouts of the same repo**, and they behave differently on both ends of
1466
- the loop. The rule turns entirely on **trunk vs non-trunk**:
1188
+ You will work from **two different checkouts of the same repo**, and they behave differently on both ends
1189
+ of the loop. The rule turns entirely on **trunk vs non-trunk**:
1467
1190
 
1468
1191
  | Working tree | Staging scope | A run resolves | `components sync`/`commit` writes |
1469
1192
  |---|---|---|---|
1470
1193
  | **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** |
1471
1194
  | **any other branch** (`feature_branch`) | that branch | trunk + **`feature_branch`** overlays | **`ComponentVariant` overlays on that branch only** — never touches trunk rows |
1472
1195
 
1473
- **The precedence trap that costs the most time:** `variantBranch` OUTRANKS every account's subscription. So
1474
- running a Test suite that asserts *production* semantics from a **variant checkout** pins every account in
1475
- that suite — including fixture accounts that subscribe to their own generated branches — to your branch,
1476
- where they have no variants, and they all read trunk. The suite fails in a way that looks exactly like a
1477
- resolution regression. (Live example: the branch-variant suite scored 4/15 from a variant checkout and 15/15
1478
- from trunk, with no code difference.) **Run branch-variant suites from trunk, or pass `--variant-branch
1479
- none`.** Before concluding "variant resolution is broken", re-run from trunk.
1480
-
1481
1196
  Do not infer this from the branch name. Ask:
1482
1197
 
1483
1198
  ```bash
@@ -1492,152 +1207,117 @@ Working tree: VARIANT BRANCH "feature_branch" (trunk is "main")
1492
1207
  subscribing accounts: 101 (Acme Child)
1493
1208
  ```
1494
1209
 
1495
- **Branch-local `account-info.json` can describe a subscriber.** On a variant branch, the same git repository
1496
- is still the OWNER's component repo: files under `components/` overlay that owner's trunk component ids, and
1497
- `components sync` writes `ComponentVariant` rows owned by that parent account. But if the checkout was synced
1498
- from a subscribing account reached through an `AccountRelationship` edge, the branch's `account-info.json`
1499
- is intentionally rooted at that subscriber and refreshed from the subscriber's resolved account information.
1500
- That is what lets a branch checkout answer "who am I acting as?" while preserving "who owns these component
1501
- variants?"
1502
-
1503
- Read the `resolution` block before acting from any checkout — it is the ONE place account-info states this
1504
- (nothing in it is repeated elsewhere in the file):
1505
-
1506
- - `resolution.role` — `OWNER` for a trunk/owner export, `SUBSCRIBER` for an account reached through an
1507
- `AccountRelationship` branch edge. `resolution.summary` says the same thing in one sentence.
1508
- - `resolution.accountId` — the account the local checkout should be treated as. **This, not the top-level
1509
- `id`, is the key to read**: an `account-info.json` predating this shape is rooted at the hierarchy root.
1510
- - `resolution.componentOwnerAccountId` — the account whose trunk component ids the files overlay and whose
1511
- `ComponentVariant` rows sync writes. Equals `resolution.accountId` for an `OWNER`.
1512
- - `resolution.componentBranch` / `resolution.scopeAccountId` — the branch and the path anchor that made this
1513
- subscriber resolution possible.
1514
- - `resolution.parents` / `resolution.children` / `resolution.relationships` — the account graph, stated once
1515
- and compactly. For quick live hierarchy checks, prefer `mcp_account_user_admin` with `action:"account"` or
1516
- `action:"hierarchy"`; use `mcp_account_view` only when component inventory or detailed configuration is also
1517
- needed. The full descendant tree is a lookup, not a committed artifact.
1518
-
1519
- **One branch, one `account-info.json`.** There is exactly one git branch, so when a branch has MORE THAN ONE
1520
- subscriber the refresh is skipped (the sync result says so under `accountInfo.skipped`/`reason`) and the file
1521
- keeps describing the owner — which is at least true for all of them. Otherwise each subscriber's sync would
1522
- rewrite the file as its own and hand the other subscriber's agent a file naming the wrong account. Overlay
1523
- ownership is unaffected either way: variants always belong to the owner account.
1524
-
1525
- Consequence: if a variant checkout's `account-info.json` is stale and still names the owner, run the first
1526
- repair sync with an explicit subscriber target, for example `remits-cli components sync --account-id 101`.
1527
- After that sync and `git pull --ff-only`, normal commands from that checkout should resolve the subscriber
1528
- from `account-info.json` and continue writing variants under the owner selected by the branch edge.
1210
+ **The precedence trap that costs the most time:** `variantBranch` **outranks every account's
1211
+ subscription**. So running a Test suite that asserts *production* semantics from a **variant checkout**
1212
+ pins every account in that suite — including fixture accounts subscribed to their own generated branches —
1213
+ to your branch, where they have no variants, and they all read trunk. The suite fails in a way that looks
1214
+ exactly like a resolution regression. (Live example: a branch-variant suite scored 4/15 from a variant
1215
+ checkout and 15/15 from trunk, with no code difference.) **Run branch-variant suites from trunk, or pass
1216
+ `--variant-branch none`.** Before concluding "variant resolution is broken", re-run from trunk.
1217
+
1218
+ **Two other things differ from trunk while you work on a branch:**
1219
+
1220
+ - **Keep the origin id in the filename.** `50_ExtractInvoice.groovy` on the branch overlays Action 50.
1221
+ That is what preserves component identity and lets drift be computed against the origin.
1222
+ - **Nothing is renamed.** A variant sync never renames files and never repoints the account's trunk
1223
+ branch. A `new_*` file stays `new_*`.
1224
+
1225
+ **Branch-local `account-info.json` can describe a subscriber.** On a variant branch the repository is
1226
+ still the OWNER's component repo — files overlay that owner's trunk ids and sync writes overlays owned by
1227
+ that owner. But when the checkout was synced from a subscribing account reached through an
1228
+ `AccountRelationship` edge, and the branch has **exactly one** subscriber, the branch's
1229
+ `account-info.json` is intentionally rooted at that subscriber. With **more than one** subscriber the
1230
+ refresh is skipped (the sync result says so under `accountInfo.skipped`/`reason`) and the file keeps
1231
+ describing the owner, which is at least true for all of them. Overlay ownership is unaffected either way.
1232
+
1233
+ Read `resolution` before acting from any checkout — in particular `resolution.accountId` (the account this
1234
+ checkout should be treated as; **read this, not the top-level `id`**), `resolution.role`,
1235
+ `resolution.componentOwnerAccountId` (whose trunk ids the files overlay and whose overlays a sync writes),
1236
+ and `resolution.componentBranch` / `resolution.scopeAccountId` (the branch and the path anchor that made
1237
+ this subscriber resolution possible). If a variant checkout's `account-info.json` is stale
1238
+ and still names the owner, run the first repair sync with an explicit subscriber target
1239
+ (`remits-cli components sync --account-id 101`), then `git pull --ff-only`.
1529
1240
 
1530
1241
  ### Two levers, two different questions
1531
1242
 
1532
1243
  - **`--as-account <id>`** changes **who** the run executes as, so that account's own edge picks the branch.
1533
- Answers *"what does customer X actually get?"* Only narrows downward (the target must be reachable from an
1534
- account you already have access to).
1244
+ Answers *"what does customer X actually get?"* Only narrows downward (the target must be reachable from
1245
+ an account you already have access to). It works for a `parentId`-less, membership-only subscriber too:
1246
+ the anchor is derived from the branch you name, or from the account's single branch subscription. It
1247
+ **refuses to guess** when an account has several edges each carrying a different branch — the run then
1248
+ resolves trunk, and you must name the branch.
1535
1249
  - **`--variant-branch <name>`** changes **which branch**, from any checkout. Answers *"what does branch Y
1536
- look like?"* — most useful for verifying a freshly committed branch **before** any edge subscribes to it.
1537
- `--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the branch.
1538
- The flag is available on `test run`, `token`, `tools`, and `tool`, so tests, browser URLs, tool discovery,
1539
- and tool execution can all inspect the same committed variant world.
1250
+ look like?"* — most useful for verifying a freshly committed branch **before** any edge subscribes to
1251
+ it. `--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the
1252
+ branch.
1540
1253
 
1541
1254
  Both work on `remits-cli test run` and `remits-cli token`; `--variant-branch` also works on
1542
- `remits-cli tools` and `remits-cli tool` for branch-specific tool discovery and execution.
1255
+ `remits-cli tools` and `remits-cli tool`, so tests, browser URLs, tool discovery, and tool execution can
1256
+ all inspect the same committed variant world.
1543
1257
 
1544
- ### The SDLC is identical on a variant branch
1258
+ The strongest end-to-end proof for a UI-visible variant is a token, not a log line:
1259
+
1260
+ ```bash
1261
+ remits-cli token --path embeddable/index/50 --as-account 101 # subscriber -> variant
1262
+ remits-cli token --path embeddable/index/50 # owner -> trunk
1263
+ ```
1545
1264
 
1546
- The loop does not change shape — `edit → stage → verify → commit`:
1265
+ ### The SDLC is identical on a variant branch
1547
1266
 
1548
1267
  ```bash
1549
1268
  git checkout feature_branch # or: git checkout -b feature_branch
1550
1269
  # edit components/actions/50_ExtractInvoice.groovy (KEEP the trunk id)
1551
1270
  remits-cli components stage # Redis, scoped to this branch — same as always
1552
- remits-cli test run --test "Invoice Tests" # resolves the feature_branch world
1271
+ remits-cli test run --test "Invoice Tests" # the feature_branch world
1553
1272
  remits-cli test run --test "Invoice Tests" --as-account 101 # ...as the real subscriber
1554
- # promote:
1555
1273
  git add -A && git commit -m "..." && git push origin feature_branch
1556
1274
  remits-cli components sync --dry-run # inspect overrides/additions/tombstones without writes
1557
1275
  remits-cli components sync # writes ComponentVariant overlays ONLY
1558
1276
  ```
1559
1277
 
1560
- Two things that differ from trunk:
1561
-
1562
- - **Keep the origin id in the filename.** `50_ExtractInvoice.groovy` on the branch overlays Action 50. That
1563
- is what preserves component identity and lets drift be computed against the origin.
1564
- - **Nothing is renamed.** A variant sync never renames files and never repoints the account's trunk branch.
1565
- When launched from a subscribing account, it may refresh that branch's `account-info.json` so the checkout
1566
- describes the account reached through the branch edge. A `new_*` file stays `new_*` and becomes a
1567
- branch-only component keyed by name.
1568
-
1569
- ### Promotion: getting the branch back into trunk
1570
-
1571
- The branch is the cheap half. Promotion is where the sharp edges are, and it is a **git** operation followed
1572
- by a **trunk** sync — the platform does not merge anything for you.
1573
-
1574
- ```bash
1575
- # 1. merge the branch into trunk (review the diff FIRST - see the deletion hazard below)
1576
- git checkout main && git merge feature_branch && git push origin main
1577
- # 2. trunk sync: creates real rows for new_* files, assigns ids, RENAMES those files on trunk
1578
- remits-cli components sync
1579
- git pull --ff-only origin main
1580
- # 3. bring trunk back into the branch so the two stop diverging
1581
- git checkout feature_branch && git merge origin/main && git push origin feature_branch
1582
- remits-cli components sync # reconciles the branch's overlays
1583
- ```
1584
-
1585
- **What step 3 reconciles, and why you must run it.** A branch-only `new_Foo.groovy` becomes trunk component
1586
- `123` and is renamed `123_Foo.groovy` **on trunk only**. The branch still holds the pre-promotion file, so
1587
- the sync handles both shapes automatically:
1588
-
1589
- | Branch state after promotion | What the sync does |
1590
- |---|---|
1591
- | only `new_Foo.*` | **adopts** trunk id 123 -> identical content -> the overlay is **removed** (`unchanged`, `pruned:true`) |
1592
- | 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 |
1593
-
1594
- Skip step 3 and the branch keeps a **branch-only** overlay for a component that now has a trunk row: it can
1595
- never converge (there is no trunk id to compare against), so subscribers stay pinned to the promoted copy
1596
- forever and every later trunk improvement is invisible to them.
1597
-
1598
- > **A tombstone is a DELETED FILE, so merging a variant branch into trunk promotes its removals.** On the
1599
- > branch, a missing file only *hides* a component from subscribers. Merged into trunk and synced, that same
1600
- > missing file **hard-deletes the component for everyone**. Always read
1601
- > `git diff --stat origin/main HEAD -- components/` before pushing a promotion and confirm every deletion is
1602
- > intended. To promote only part of a branch, restore the files you are not promoting
1603
- > (`git checkout origin/main -- <path>`) before the trunk sync.
1604
-
1605
- **Trunk moving also invalidates the branch.** Variant sparseness compares branch content against *current*
1606
- trunk, so a trunk change can make a branch overlay obsolete without the branch changing at all. A trunk sync
1607
- now drops the affected branches' cached sync verdicts, so the next `components sync` on the branch really
1608
- re-evaluates instead of answering *"No changes detected"*. After promoting anything, re-sync each live
1609
- branch.
1610
-
1611
- ### Verifying as the subscriber
1612
-
1613
- `--as-account <id>` runs as the subscriber so its own edge selects the branch. It works for a
1614
- `parentId`-less, membership-only subscriber too: the anchor is derived from the branch you name
1615
- (`--variant-branch`) or, failing that, from the account's single branch subscription.
1278
+ Promotion back to trunk is a **git** operation followed by a **trunk** sync — the platform merges nothing
1279
+ for you, and merging a branch promotes its **deletions** as hard deletes. Do not improvise it: follow
1280
+ `features/subscriber-branch-promotions.md`.
1616
1281
 
1617
- It **refuses to guess** when an account has several edges each carrying a different branch - the run then
1618
- resolves trunk. Name the branch with `--variant-branch <name>` to disambiguate, or check the edges with
1619
- `components branch <name> --subscribers` (it prints `via primary|membership edge -> parent N`).
1620
-
1621
- The strongest end-to-end proof for a UI-visible variant is a token, not a log line:
1282
+ ### Subscribing, unsubscribing, retiring
1622
1283
 
1623
1284
  ```bash
1624
- remits-cli token --path embeddable/index/50 --as-account 101 # subscriber -> variant
1625
- remits-cli token --path embeddable/index/50 # owner -> trunk
1626
- ```
1627
-
1628
- Open both; if the branch changes anything visible, you will see it immediately.
1629
-
1630
- ### Agents (Utility) on a variant branch
1631
-
1632
- A `Utility` is an **Agent** in practice — the repo directory is `components/agents/` and there is no
1633
- `components/utilities/`. Agent variants work, with two things to know:
1285
+ remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>]
1286
+ remits-cli components branch <name> --unsubscribe <accountId>
1287
+ remits-cli components branch <name> --retire [--force]
1288
+ ```
1289
+
1290
+ Branch administration commands act **as the account you are running from**, which is treated as the branch
1291
+ **owner** — run them from the owner's trunk checkout. Authorization is downward-only.
1292
+
1293
+ - **`--subscribe` sets the branch on an edge that must already exist — it cannot create one.** Failure
1294
+ reads *"Account N has no relationship edge to subscribe; add a membership edge first"*. Create it with
1295
+ `mcp_account_user_admin` `action:'edge_add'` (`targetAccountId` = the child, `parentAccountId` = the
1296
+ owner), which also accepts `branchName` so you can create and subscribe in one call.
1297
+ - **Many older accounts have no relationship row at all** — the edge table was added after the fact and
1298
+ never backfilled, so an account whose parent link is only `Account.parentId` resolves fine but has
1299
+ nothing to attach to. `mcp_account_user_admin` `action:'reparent'` with the account's *current* parent
1300
+ is the one-call repair: it creates the missing primary edge and changes nothing else.
1301
+ - **When the account has several parents, say which edge you mean** with `--parent-account <ownerId>`.
1302
+ Without it the CLI picks the edge to the owner whose branch you are managing, then falls back to the
1303
+ account's primary edge — which may not be the one you intended. `--subscribers` prints
1304
+ `via primary|membership edge -> parent N` so you can confirm.
1305
+ - **Retiring is explicit.** Deleting the *git* branch does **not** remove its overlays; subscribers would
1306
+ keep resolving a branch that no longer exists. `--retire` refuses while subscribers remain unless you
1307
+ pass `--force`.
1308
+
1309
+ **Every component kind can be varied** — `schema`, `reader`, `action`, `embeddable`, `i18n`, `htmltemplate`,
1310
+ `rule`, `test`, `utility` (agent), `tool`, `prompt` — including tombstones and branch-only additions. A
1311
+ **schema** variant deserves the same care as a trunk schema change: it can change the JSON schema *and*
1312
+ `collectionName`, so it changes validation and where the subscriber's documents physically land. A
1313
+ branch-only agent (a `new_*` file under `components/agents/`) defaults to `type: AI` so agent lookups
1314
+ find it.
1634
1315
 
1635
- - **Override the sidecar the same way you would on trunk.** `components/agents/19_Bob.meta.yml` keys
1636
- (`model:`, `agentTimeout:`, `type:`) and the `.md` prompt sidecar all overlay correctly, as does the
1637
- `.groovy` source. A branch-only agent (a `new_*` file) defaults to `type: AI` so agent lookups find it.
1638
- - **An agent's TOOL LIST cannot be overridden by a variant.** `tools:` in the sidecar maps to a GORM
1639
- association, and `agent()` populates it from the **trunk** row. A `tools:` list on a variant branch is
1640
- ignored. If a branch needs a different tool set, change trunk or have the agent choose tools at runtime.
1316
+ A variant speaks the same `.meta.yml` vocabulary trunk does; keys outside that set are ignored on purpose,
1317
+ because trunk cannot express them either — allowing them would mean a branch behaves one way and silently
1318
+ loses that behavior the moment it is promoted. **One documented exception: an agent's `tools:` list cannot
1319
+ be overridden by a variant.** It maps to a GORM association that `agent()` populates from the trunk row, so
1320
+ a `tools:` list on a branch is ignored. Change trunk, or have the agent select tools at runtime.
1641
1321
 
1642
1322
  ### Danger profile on a variant branch (different, not absent)
1643
1323
 
@@ -1645,27 +1325,30 @@ The Component Integrity Rules' worst case — *"a missing file hard-deletes a li
1645
1325
  apply on a variant branch.** Variant sync writes overlay rows only; it cannot create, delete, rename, or
1646
1326
  overwrite a trunk component. That makes a variant branch a genuinely safer place to iterate.
1647
1327
 
1648
- The analogous hazard is different and you must still respect it:
1328
+ The analogous hazard is different, and you must still respect it:
1649
1329
 
1650
1330
  - **A trunk component with no file on the branch becomes a TOMBSTONE**, which *hides* that component from
1651
- every subscriber. It is reversible (restore the file and re-sync) and it never touches trunk — but to a
1652
- subscribing account it looks exactly like the component was deleted. Before syncing a variant branch,
1653
- confirm every omission is deliberate.
1331
+ every subscriber. It is reversible (restore the file, re-sync) and never touches trunk — but to a
1332
+ subscribing account it looks exactly like the component was deleted. Confirm every omission is
1333
+ deliberate before syncing.
1334
+ - **Deleting a file means "remove the component", not "stop overriding it."** To withdraw an override,
1335
+ make the file identical to trunk again — the sync then removes the variant row.
1336
+ - **Keep the branch rebased.** A branch physically carries every file, so anything trunk added *after* the
1337
+ branch was cut looks like a deliberate deletion. A small stale gap tombstones silently.
1654
1338
  - **Use `components sync --dry-run` before risky variant syncs.** It reports `overridden`, `added`,
1655
- `removed`, `unchanged`, `skipped`, and `errors` without writing variant rows, caching the sync SHA, or
1656
- clearing staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means
1657
- the branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
1658
- `components commit --dry-run` is unsupported because `commit` performs local git writes before server sync.
1339
+ `removed`, `unchanged`, `skipped`, and `errors` without writing rows, caching the sync SHA, or clearing
1340
+ staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means the
1341
+ branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
1342
+ `components commit --dry-run` is unsupported because `commit` performs local git writes before syncing.
1343
+ - **The sync refuses a wholesale removal.** Above roughly a third of a kind — or **100% of a kind at any
1344
+ size** — it aborts that kind, reports why, and points at a rebase. Rebasing is almost always the real
1345
+ fix. Only when the removals are genuinely deliberate, re-run with `--force-tombstones`.
1659
1346
  - **Deleting a trunk component cascades**: its overlays on every branch are removed with it.
1660
- - **A removal you did not author usually means TRUNK is drifted, not that the branch deleted something.** A
1661
- component that exists in the DB with **no file on trunk** is missing from every branch too, so it shows up
1662
- as a phantom `removed` in *every* branch preview - while the trunk sync separately tries to hard-delete it
1663
- on every run. Check whether the file exists on trunk before "fixing" it on the branch; the repair is to
1664
- mirror the live DB source back into the trunk repo (Component Integrity Rules), not to touch the branch.
1665
- - **The sync refuses a wholesale removal.** Above ~a third of a kind - or **100% of a kind at any size** - it
1666
- aborts that kind, reports why, and points at a rebase. Rebasing onto trunk is almost always the real fix; a
1667
- branch not rebased in a while is missing everything trunk has added since it was cut. Only when the
1668
- removals are genuinely deliberate, re-run with `--force-tombstones`.
1347
+ - **A removal you did not author usually means TRUNK is drifted**, not that the branch deleted something.
1348
+ A component that exists in the DB with **no file on trunk** is missing from every branch too, so it
1349
+ shows up as a phantom `removed` in *every* branch preview — while the trunk sync separately tries to
1350
+ hard-delete it on every run. Check whether the file exists on trunk before "fixing" it on the branch;
1351
+ the repair is to mirror the live DB source back into the trunk repo (Component Integrity Rules).
1669
1352
 
1670
1353
  ### Inspecting branches and drift
1671
1354
 
@@ -1674,35 +1357,36 @@ remits-cli components branches # branches with
1674
1357
  remits-cli components branch feature_branch # overridden / added / removed + subscribers
1675
1358
  remits-cli components branch feature_branch --diff 50 --component-type action
1676
1359
  remits-cli components branch feature_branch --subscribers
1360
+ remits-cli components branch feature_branch --json # the stored overlay content
1677
1361
  ```
1678
1362
 
1679
1363
  **Drift is the number to watch.** Each variant records the trunk content hash at the moment it was cut
1680
- (`originHash`). When the origin component later changes, the variant is reported **DRIFTED** — the branch is
1681
- now based on a stale version of the origin and someone should reconcile it. `branches` shows a per-branch
1682
- drift count; `branch <name>` flags each drifted component; `--diff` shows variant vs current trunk.
1683
-
1684
- Before editing an origin component, check whether variants of it exist — the owner account's
1685
- `account-info.json` carries a `componentBranches` summary, `components branches` gives the live view, and
1686
- the admin edit forms show a branch-variant notice for trunk components that already have variants. A change
1687
- to the origin silently drifts every branch that overlays it.
1364
+ (`originHash`). When the origin component later changes, the variant is reported **DRIFTED** — the branch
1365
+ is now based on a stale version and someone should reconcile it. Before editing an origin component, check
1366
+ whether variants of it exist: the owner account's `account-info.json` carries a `componentBranches`
1367
+ summary, and `components branches` gives the live view. A change to the origin silently drifts every
1368
+ branch that overlays it.
1688
1369
 
1689
1370
  **`components branches` only lists branches that already have overlays.** A branch you just pushed is
1690
- invisible here until its first sync - that is not an error. Preview it by name
1691
- (`components sync --dry-run` from that checkout), or from the admin GitHub menu's branch picker.
1692
-
1693
- **The admin UI has the same surface**, which is what to point a user at:
1694
- - *owner account page* -> **Component branches** box: per-branch counts, drift, subscribers, and
1695
- **Preview / Sync / Retire**;
1696
- - *subscriber account page* -> **Branch variants** box (what THIS account resolves) and **GitHub -> Fetch
1697
- `<branch>` variants**;
1698
- - both open the same result panel - grouped plan, subscribers, a **Monaco diff of trunk vs branch** per
1699
- changed field, and a confirm-gated override when the removal guard refuses.
1700
- Preview there is the same `--dry-run`, so it is safe to hand to a non-CLI user.
1371
+ invisible here until its first sync — that is not an error. Preview it by name (`components sync --dry-run`
1372
+ from that checkout).
1373
+
1374
+ **Trunk moving also invalidates a branch.** Variant sparseness compares branch content against *current*
1375
+ trunk, so a trunk change can make an overlay obsolete without the branch changing at all. A trunk sync
1376
+ drops the affected branches' cached sync verdicts, so the next `components sync` on the branch really
1377
+ re-evaluates instead of answering *"No changes detected"*. After promoting anything, re-sync each live
1378
+ branch.
1379
+
1380
+ **The admin UI has the same surface**, which is what to point a non-CLI user at: the *owner* account page's
1381
+ **Component branches** box (per-branch counts, drift, subscribers, and Preview / Sync / Retire), and the
1382
+ *subscriber* account page's **Branch variants** box plus **GitHub → Fetch `<branch>` variants**. Both open
1383
+ the same result panel — grouped plan, subscribers, a Monaco diff of trunk vs branch per changed field, and
1384
+ a confirm-gated override when the removal guard refuses. Preview there is the same `--dry-run`.
1701
1385
 
1702
1386
  ### Diagnosing a variant
1703
1387
 
1704
1388
  - `remits-cli components branch <name> --json` — the stored overlay content and what it overrides.
1705
- - The compile signature (`variant:<id>:<hash>`) in `Using Cached BCD` logs — proves a variant actually ran.
1389
+ - The compile signature `variant:<id>:<hash>` in `Using Cached BCD` logs — proves a variant actually ran.
1706
1390
  - A test/tool response reports `testComponentSource` as `staged` | `variant` | `db`, so you can see which
1707
1391
  layer the run resolved without reading logs.
1708
1392
 
@@ -1743,48 +1427,28 @@ Before starting an investigation outside the confirmed current repo:
1743
1427
  5. Identify `source_bcd` and any `upstream_source_bcd`.
1744
1428
  6. Explain the record in terms of the workflow step that produced it, not as a generic JSON blob.
1745
1429
  7. If the context looks missing, redacted, or truncated, consider sanitization rules before concluding data was never present.
1746
- 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`.
1430
+ 8. If AI behavior is part of the symptom, use `mcp_ai_session_search` and compare the persisted session content against the Agent component implementation and `features/ai-support.md`.
1747
1431
 
1748
1432
  **Stuck / failed / recovered Event:**
1749
1433
 
1750
- Do **not** open the Action source first. The platform records each attempt's delivery envelope (which
1751
- 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 remits-cli / MCP, use `mcp_event_diagnostics` first:
1434
+ Do **not** open the Action source first. The platform records each attempt's delivery envelope — which
1435
+ queue delivered it, which delivery attempt this was, and how long it was ever allowed to run — and
1436
+ classifies the failure for you.
1752
1437
 
1753
1438
  ```bash
1754
1439
  remits-cli tool --name mcp_event_diagnostics --input '{"accountId":49,"eventId":18838}' --data-mode prod
1755
1440
  ```
1756
1441
 
1757
- From a Test or any component, the same platform classifier is available directly:
1758
-
1759
- ```groovy
1760
- eventDiagnostics(18838)
1761
- ```
1762
-
1763
- Read `classification` before anything else:
1764
-
1765
- - `APPLICATION_FAILURE` — **the only one that means the bug is in the component.** Read `errorMessage`
1766
- and the correlated `alerts`, then investigate the component normally.
1767
- - `ORPHANED_*` / `RECOVERED_*` — the attempt was abandoned. Read `abandonmentCause`:
1768
- - `REQUEST_TIMEOUT_LIKELY` — used its whole deadline (`timing.deadlineUsed` near `1.0`). The unit of
1769
- work is too big for one event; the fix is resumable batches, not component logic.
1770
- - `PROCESS_TERMINATED_LIKELY` — stopped well inside its deadline (`deadlineUsed` near `0`). The worker
1771
- was killed (memory pressure, restart). Not a logic bug.
1772
- - `UNKNOWN_NO_DEADLINE_EVIDENCE` — no deadline recorded, so the cause is genuinely unknown. Use
1773
- `logQuery`; do not assume. Events predating delivery-envelope capture always look like this.
1774
- - `AWAITING_DELIVERY` — never claimed. A queue/delivery problem.
1775
- - `IN_FLIGHT_HEALTHY` — still running with a fresh heartbeat. A long Action is not a stuck one; wait.
1442
+ The same classifier is available from a Test or any component as `eventDiagnostics(18838)`.
1776
1443
 
1444
+ **Read `classification` before anything else** — only `APPLICATION_FAILURE` means the bug is in the
1445
+ component. The full classification table, what each `abandonmentCause` implies, and the returned `pivots`
1446
+ are under **`mcp_event_diagnostics`** in the Tool Reference. One thing to check every time:
1777
1447
  `delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
1778
- non-idempotent side effect may have run more than once — check for duplicate records before concluding
1779
- the component "ran twice for no reason".
1448
+ non-idempotent side effect may have run more than once — look for duplicate records before concluding the
1449
+ component "ran twice for no reason".
1780
1450
 
1781
- `mcp_event_diagnostics` returns the same classification, `delivery.threadGroupingId`, and ready-to-run
1782
- `pivots` for `mcp_performance_trace`, `mcp_system_logs`, `mcp_record_listing`, and `mcp_object_activity`.
1783
- `delivery.threadGroupingId` is the same id everything else uses, so you can pivot straight into
1784
- `mcp_performance_trace` (`action:"trace"`) or `mcp_system_logs` with it. `logQuery` in the response
1785
- carries ready-made Cloud Logging filters, including the container-lifecycle and 504 queries.
1786
-
1787
- Full detail: `docs/guides/features/observability.md` §5b and `events-builder-guide.md`.
1451
+ Full detail: `features/observability.md` and `features/events-builder-guide.md` (`mcp_get_guide`).
1788
1452
 
1789
1453
  ### Presenting Findings
1790
1454
 
@@ -1921,9 +1585,9 @@ without a browser):
1921
1585
  - `action: 'account_structure'` — change those properties on an existing account.
1922
1586
  - `action: 'edge_add'` / `'edge_update'` / `'edge_remove'` — manage a membership `AccountRelationship` edge to
1923
1587
  `parentAccountId`, including the three independent edge properties `branchName` (which component code runs),
1924
- `databaseName` (branch-scoped namespace — stored, but inert until segment inheritance is enabled) and
1925
- `domainName` (which host reaches the account through that edge). Cycles, self-edges, and removing the primary
1926
- edge are all refused.
1588
+ `databaseName` (a path-scoped storage-namespace override — **live**, and inherited by everything below
1589
+ that edge) and `domainName` (which host reaches the account through that edge). Cycles, self-edges, and
1590
+ removing the primary edge are all refused.
1927
1591
  - `action: 'reparent'` — move the account's **primary** edge (and `Account.parentId`) to `parentAccountId`.
1928
1592
 
1929
1593
  > **The namespace guard.** An account's storage namespace resolves as
@@ -1932,16 +1596,15 @@ without a browser):
1932
1596
  > move the resolved namespace is **refused** unless you pass `confirmSegmentChange: true`, and the refusal
1933
1597
  > names both namespaces. To rename without moving storage, pin `code` to the old value in the same call.
1934
1598
  >
1935
- > The namespace follows the **path**: the nearest `databaseName` walking up from the account — its own
1936
- > first, then the edge it was reached through, then the same two questions at each account above — falling
1937
- > back to the nearest `PLATFORM` ancestor's `code`. So a namespace set anywhere above is inherited by
1938
- > everything below it until a nearer account or edge overrides it, and an edge answer is path-specific (the
1939
- > same account reached through a different parent can resolve a different namespace).
1599
+ > Because the namespace follows the **path** (see *Account Structure* in `platform-overview.md`), an
1600
+ > `edge_update` that sets `databaseName` repoints storage for **everything below that edge**, and the
1601
+ > answer is path-specific — the same account reached through a different parent can resolve a different
1602
+ > namespace. Dry-run these.
1940
1603
 
1941
1604
  Every write supports `dryRun: true`, which reports each `from -> to` without writing. Not here by design:
1942
- component-branch subscription reporting and drift (`mcp_component_branches` — though `edge_add`/`edge_update`
1943
- set the branch directly), and account/user **deletion** (admin only, so the destructive-teardown contract
1944
- applies).
1605
+ component-branch subscription reporting and drift (use `remits-cli components branches` /
1606
+ `remits-cli components branch <name>`), and account/user **deletion** (admin only, so the
1607
+ destructive-teardown contract applies).
1945
1608
 
1946
1609
  **Provisioning recipe** — a new product account under a platform, running its own component branch, with
1947
1610
  client accounts beneath it:
@@ -1952,7 +1615,7 @@ client accounts beneath it:
1952
1615
  2. git branch + push in the OWNER's repo, then `remits-cli components sync` from that checkout
1953
1616
  (non-trunk => writes ComponentVariant overlays only)
1954
1617
  3. mcp_account_user_admin action:'edge_update' targetAccountId:<new> parentAccountId:<platform>
1955
- branchName:'<branch>' # or mcp_component_branches action:'subscribe'
1618
+ branchName:'<branch>'
1956
1619
  4. mcp_account_user_admin action:'account_create' parentAccountId:<new> name:'<business unit>'
1957
1620
  type:'CLIENT' # inherits the branch automatically
1958
1621
  ```
@@ -1999,28 +1662,14 @@ Query Firestore documents.
1999
1662
  | `textSearch` | no | `[{field, prefix}]` for prefix matching |
2000
1663
  | `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. |
2001
1664
 
2002
- HTTP audit usage notes:
2003
-
2004
- - Use this same tool for monthly HTTP audit collections:
2005
- - `http-audits/http-audits-YYYY-MM/entries`
2006
- - Example investigation query:
1665
+ **HTTP audits use this same tool** — they are Firestore documents in monthly collections
1666
+ (`http-audits/http-audits-YYYY-MM/entries`). Which filters to reach for, and when audits are the right
1667
+ evidence at all, is in *The Investigation Model → HTTP audits*.
2007
1668
 
2008
1669
  ```bash
2009
1670
  remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collection": "http-audits/http-audits-2026-05/entries", "filters": [{"field": "direction", "operation": "EQUALS", "value": "OUTBOUND"}, {"field": "request.path", "operation": "EQUALS", "value": "/api/orders"}, {"field": "success", "operation": "EQUALS", "value": false}], "sort": [{"field": "timestamp", "direction": "DESC"}], "pagination": {"limit": 10}}' --data-mode prod
2010
1671
  ```
2011
1672
 
2012
- - For Reader webhook investigations, commonly filter on:
2013
- - `direction = INBOUND`
2014
- - `request.method`
2015
- - `request.path`
2016
- - `response.statusCode`
2017
- - For outbound integration investigations, commonly filter on:
2018
- - `direction = OUTBOUND`
2019
- - `component.name`
2020
- - `request.path` or `request.url`
2021
- - `response.statusCode`
2022
- - `success`
2023
-
2024
1673
  ### `mcp_firestore_patch`
2025
1674
  Guarded exact-document Firestore patch tool for bounded repairs. It defaults to `dryRun:true` and refuses broad
2026
1675
  updates, wildcard collections, delete/remove operations, protected identity fields, and `_lastModified*` audit
@@ -2106,8 +1755,8 @@ Typical use:
2106
1755
  - identify tuning opportunities in Agent behavior by comparing session output with the front-stage guides and component source
2107
1756
 
2108
1757
  Front-stage references:
2109
- - `docs/guides/components/agent-components.md`
2110
- - `docs/guides/features/ai-support.md`
1758
+ - `components/agent-components.md`
1759
+ - `features/ai-support.md`
2111
1760
 
2112
1761
  | Parameter | Required | Description |
2113
1762
  |-----------|----------|-------------|
@@ -2351,7 +2000,7 @@ unaccounted wall time is itself a finding: check cold compile, queueing, blockin
2351
2000
 
2352
2001
  ### `mcp_event_diagnostics`
2353
2002
  Diagnose one Event's infrastructure outcome through the same `eventDiagnostics(eventId)` DSL described in
2354
- `docs/guides/features/observability.md`. Use this before opening Action source when an Event is stuck,
2003
+ `features/observability.md`. Use this before opening Action source when an Event is stuck,
2355
2004
  recovered, timed out, retried, or appears to have been killed.
2356
2005
 
2357
2006
  ```bash
@@ -2369,26 +2018,34 @@ Read `classification` first:
2369
2018
  - `APPLICATION_FAILURE` — the Action failed in application code; read `event.errorMessage`, correlated
2370
2019
  alerts, and the producing component.
2371
2020
  - `ORPHANED_*` / `RECOVERED_*` — the attempt was abandoned; read `abandonmentCause`.
2372
- - `REQUEST_TIMEOUT_LIKELY` means the work likely used its whole deadline, so split it into resumable batches.
2373
- - `PROCESS_TERMINATED_LIKELY` means the worker likely disappeared before its deadline; inspect JVM/node
2374
- health and container lifecycle logs.
2375
- - `UNKNOWN_NO_DEADLINE_EVIDENCE` means the platform refuses to guess; use the returned `logQuery` filters.
2376
- - `AWAITING_DELIVERY` means the Event has not been claimed; check queue delivery and action-node health.
2377
- - `IN_FLIGHT_HEALTHY` means the Event is still heartbeating; inspect trace/logs before interrupting.
2378
-
2379
- The response returns `threadGroupingId`, the full `result` map, `diagnosisHints`, and `pivots` containing
2380
- ready-to-run inputs for `mcp_performance_trace`, `mcp_system_logs`, `mcp_record_listing`, and
2381
- `mcp_object_activity` when those handles are present. For a performance question, open the returned
2382
- `mcp_performance_trace` pivot next; for raw failure context, open logs and records by `threadGroupingId`.
2021
+ - `REQUEST_TIMEOUT_LIKELY` — it used its whole deadline (`timing.deadlineUsed` near `1.0`). The unit of
2022
+ work is too big for one event; the fix is resumable batches, not component logic.
2023
+ - `PROCESS_TERMINATED_LIKELY` — it stopped well inside its deadline (`timing.deadlineUsed` near `0`), so
2024
+ the worker was killed (memory pressure, restart). Not a logic bug; inspect JVM/node health and
2025
+ container lifecycle logs.
2026
+ - `UNKNOWN_NO_DEADLINE_EVIDENCE` — no deadline was recorded, so the cause is genuinely unknown. Use the
2027
+ returned `logQuery` filters; do not assume. Events predating delivery-envelope capture always look
2028
+ like this.
2029
+ - `AWAITING_DELIVERY` — the Event was never claimed; check queue delivery and action-node health.
2030
+ - `IN_FLIGHT_HEALTHY` — the Event is still heartbeating. A long Action is not a stuck one; wait, and
2031
+ inspect trace/logs before interrupting.
2032
+
2033
+ `delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
2034
+ non-idempotent side effect may have run more than once.
2035
+
2036
+ The response returns `delivery.threadGroupingId` — the same id everything else uses — plus the full
2037
+ `result` map, `diagnosisHints`, and `pivots` carrying ready-to-run inputs for `mcp_performance_trace`,
2038
+ `mcp_system_logs`, `mcp_record_listing`, and `mcp_object_activity` when those handles are present.
2039
+ `logQuery` carries ready-made Cloud Logging filters, including the container-lifecycle and 504 queries.
2040
+ For a performance question, open the `mcp_performance_trace` pivot next; for raw failure context, open
2041
+ logs and records by `threadGroupingId`.
2383
2042
 
2384
2043
  ### `mcp_component_view`
2385
2044
  Read component field content with line numbers.
2386
2045
 
2387
- Note: component source-management tools such as `mcp_component_view`, `mcp_component_grep`,
2388
- `mcp_component_edit`, `mcp_component_create`, and `mcp_component_commit` are primarily for MCP-only
2389
- clients that do not have a local account repo. In a normal `remits-cli` coding workflow, prefer local
2390
- repo files plus `remits-cli components stage` / `remits-cli components sync`; if these component tools
2391
- are absent from `remits-cli tools`, that is expected.
2046
+ In a normal `remits-cli` coding workflow, prefer local repo files for source reads. Use this tool when the
2047
+ local repo is unavailable, when confirming live DB source, or when you need staging / variant metadata that
2048
+ is not present in the working tree.
2392
2049
 
2393
2050
  | Parameter | Required | Description |
2394
2051
  |-----------|----------|-------------|
@@ -2401,7 +2058,7 @@ are absent from `remits-cli tools`, that is expected.
2401
2058
 
2402
2059
  Returns `componentVariants` when the component has committed branch variants — the branches, their state
2403
2060
  (`current` / `drifted` / `removed`), and a warning. Non-null means some accounts run a different version
2404
- than the source you are reading, and editing here changes trunk only and drifts those variants.
2061
+ than the source you are reading, and trunk promotions can drift those variants.
2405
2062
  `mcp_component_grep` searches trunk, so it will not match text that exists only in a variant.
2406
2063
 
2407
2064
  ### `mcp_component_grep`
@@ -2477,103 +2134,21 @@ it twice.
2477
2134
  - `complete` after verification
2478
2135
  - `release` if you are handing it off or cannot continue
2479
2136
 
2480
- ### `mcp_run_test`
2481
- Execute a Test component.
2137
+ ### Component branches
2138
+ Use `remits-cli components branches` / `remits-cli components branch <name>` to inspect committed branch
2139
+ variants, subscribers, and drift from a local checkout.
2482
2140
 
2483
- | Parameter | Required | Description |
2484
- |-----------|----------|-------------|
2485
- | `accountId` | yes | Account ID |
2486
- | `testId` | yes | Test component ID |
2487
- | `testNames` | no | Array of specific test case names |
2488
- | `taskId` | no | Stable id for the run. **Declare your own if you may need to stop it** — see below. Echoed back either way. |
2489
- | `controlAction` | no | `interrupt` (aliases `stop`/`cancel`/`kill`) stops a running suite identified by `taskId`. |
2490
-
2491
- #### Stopping a running suite
2492
-
2493
- A suite that loops dozens of cases — or sits in one slow AI/HTTP call — used to have to be waited out.
2494
- It can now be stopped, from the admin test runner's **Stop** button or from here.
2495
-
2496
- ```bash
2497
- # declare the id when you start, so the run is addressable while it is still going
2498
- remits-cli tool --name mcp_run_test --data-mode test --input '{"accountId":1,"testId":7,"taskId":"my-run"}'
2499
-
2500
- # ...then from another call/session:
2501
- remits-cli tool --name mcp_run_test --data-mode test --input '{"controlAction":"interrupt","accountId":1,"taskId":"my-run"}'
2502
- ```
2503
-
2504
- > **This tool runs the suite SYNCHRONOUSLY**, so you cannot stop a run you are yourself blocked on —
2505
- > which is exactly why you declare `taskId` up front. Without one, a run gets a generated id you never see.
2506
-
2507
- Semantics, same as everywhere else in the platform:
2508
-
2509
- - **Cooperative.** The suite ends at its next checkpoint — between cases, or mid-case at any
2510
- Firestore/HTTP/AI/DSL call. A case stuck in one long external call stops when that call returns.
2511
- - **Cases already completed keep their results**, and committed work is **not** rolled back.
2512
- - An interrupted run returns `interrupted: true` and its partial results. A stop is reported as
2513
- **INTERRUPTED, never as a test failure** — so it can't be mistaken for a broken suite.
2514
- - Keyed per RUN, not per Test: the same Test can be running concurrently (different users, branches, or
2515
- data modes), and stopping one never stops another.
2516
-
2517
- ### `mcp_component_edit`
2518
- Edit a component field, server-side, with stage or commit semantics. The agent counterpart to local file
2519
- edit → `remits-cli components stage`. Useful for components that don't live in the local repo (e.g.
2520
- auxiliary components). See "Component Resolution" for stage-vs-DB behavior.
2521
-
2522
- | Parameter | Required | Description |
2523
- |-----------|----------|-------------|
2524
- | `accountId` | yes | Account ID of the component |
2525
- | `componentType` | yes | `Schema`/`Reader`/`Action`/`Embeddable`/`HtmlTemplate`/`Rule`/`Agent`/`Test`/`Tool`/`Prompt` |
2526
- | `componentId` | yes | Component ID |
2527
- | `fieldName` | yes | Field to edit (`source`, `html`, `javascript`, `schema`, `inputSchema`, `previewData`, `path`; metadata fields like `description`/`mermaid` require `mode:'commit'`) |
2528
- | `mode` | no | `stage` (default, Redis 240-min TTL) or `commit` (write live DB + clear staging) |
2529
- | `editMode` | no | `targeted` (default, anchor-verified line edit via `lineStart`/`lineEnd`/`anchorContent`/`newContent`) or `replace` (full-field `newContent`) |
2530
- | `auxiliary`/`category`/`partnerSlug` | no | Companion metadata applied alongside the edit |
2531
-
2532
- ### `mcp_component_create`
2533
- Create a new component row directly in the database. This is a direct-DB operation, not the normal repo-backed
2534
- component creation workflow.
2535
-
2536
- Use it only when:
2537
- - there is no local account repo available, or
2538
- - the user explicitly asks for direct remote creation, or
2539
- - an emergency repair plan intentionally creates a new row after live inventory and git history have been
2540
- reconciled.
2541
-
2542
- In a repo-backed account, prefer `new_` component files plus stage/test/git/sync after the Component Integrity
2543
- Rules pass. Never use `mcp_component_create` to compensate for an unexpected deletion, a renamed numeric file,
2544
- or a local/live ID mismatch.
2545
-
2546
- ### `mcp_component_commit`
2547
- Inspect, flush, or clear the CLI staging cache for components.
2548
-
2549
- | Parameter | Required | Description |
2550
- |-----------|----------|-------------|
2551
- | `accountId` | yes | Account ID |
2552
- | `action` | yes | `read` (list staged entries), `commit` (flush staged → DB), or `clear` (drop staged entries without touching the DB) |
2553
-
2554
- ### `mcp_component_branches`
2555
- The MCP counterpart of `remits-cli components branches` / `components branch <name>` — use it when you are
2556
- driving the platform **remotely** (no local repo/checkout) and need to know whether a component already has
2557
- committed branch variants, who subscribes, and whether they have drifted.
2558
-
2559
- | Parameter | Required | Description |
2560
- |-----------|----------|-------------|
2561
- | `action` | no | `list` (default) — every branch this owner has variants on, with drift + subscriber counts; `status` — one branch's overridden/added/removed; `diff` — one component's variant beside current trunk; `subscribers` — accounts resolving a branch; `subscribe`/`unsubscribe` — set/clear the branch on a subscriber's existing relationship edge; `retire` — delete a branch's overlays |
2562
- | `ownerAccountId` | no | The account whose branches are inspected. **Outranks `accountId`**, because `accountId` also selects which account the tool is resolved on — so `accountId` alone cannot target another account's branches. |
2563
- | `branchName` | conditional | Required for everything except `list`. Alias: `branch`. |
2564
- | `componentId` / `componentName` / `componentType` | conditional | Address the component for `diff` |
2565
- | `targetAccountId` | conditional | The account to subscribe/unsubscribe |
2566
- | `parentAccountId` | no | Which relationship edge to set the branch on. Without it: the edge toward the branch owner, then the target's primary edge. |
2567
- | `force` | no | `retire` only — proceed while subscribers remain |
2568
-
2569
- Same rules as the CLI: `subscribe` only **sets the branch on an edge that already exists** (creating a
2570
- membership edge is an admin operation), authorization is downward-only, and `retire` refuses while
2571
- subscribers remain unless forced.
2141
+ Common uses:
2142
+ - `remits-cli components branches` — list branches this owner has variants on.
2143
+ - `remits-cli components branch <name>` — show overridden / added / removed components on that branch.
2144
+ - `remits-cli components branch <name> --diff <id> --component-type <kind>` — compare one variant against
2145
+ current trunk.
2146
+ - `remits-cli components branch <name> --subscribers` — list accounts resolving that branch.
2572
2147
 
2573
2148
  ### `mcp_cache`
2574
2149
  Bounded read-only investigation of the platform Redis keyspace — the way to see exactly what a staged
2575
- entry holds (and its TTL) or any other cache key. Read-only: no delete (use `mcp_component_commit clear`
2576
- to remove staged entries).
2150
+ entry holds (and its TTL) or any other cache key. Read-only: no delete (use `remits-cli components clear`
2151
+ to remove staged component entries).
2577
2152
 
2578
2153
  | Parameter | Required | Description |
2579
2154
  |-----------|----------|-------------|
@@ -2649,6 +2224,11 @@ before writing a component.
2649
2224
  | `directory` | no | Scope a list to `components` or `features` |
2650
2225
  | `contains` | no | Substring filter when listing |
2651
2226
 
2227
+ Every guide is delivered with a table of contents whose entries carry **real line numbers**
2228
+ (`- L412 Querying Alerts`), resolved at delivery so they are never stale. Several of these guides are
2229
+ over a thousand lines: read the head, pick the sections you need, and offset-read those rather than
2230
+ loading the whole file. The entry text is the heading verbatim, so it also greps.
2231
+
2652
2232
  ### `mcp_test_fixture`
2653
2233
  Seed and remove schema-backed fixture documents in the **forced test** data segment. This is how you construct
2654
2234
  realistic conditions for verification without copying live customer data.
@@ -2662,18 +2242,6 @@ realistic conditions for verification without copying live customer data.
2662
2242
  | `data` / `documents` | conditional | Single payload, or a batch array |
2663
2243
  | `dataMode` | no | Must be `test` — **prod-mode writes are rejected** |
2664
2244
 
2665
- ### `mcp_embeddable_test_url`
2666
- Mint an authenticated browser URL for an embeddable — the MCP counterpart of `remits-cli token --path
2667
- embeddable/index/<id>`. Returns the URL plus branch/variant/data-mode hints so you know which world the page
2668
- will render.
2669
-
2670
- | Parameter | Required | Description |
2671
- |-----------|----------|-------------|
2672
- | `accountId` | yes | Account owning the embeddable |
2673
- | `embeddableId` / `embeddableName` | conditional | Provide one |
2674
- | `userId` | no | User to mint for. Defaults to the current user. |
2675
- | `variantBranch` | no | Probe a committed component-variant branch. Usually inherited from the caller. |
2676
-
2677
2245
  ### `mcp_playwright_replay`
2678
2246
  Hosted browser automation (the `playwright-relay` service) for visual verification when local `playwright-cli`
2679
2247
  is unavailable — e.g. an agent running remotely. Returns an accessibility snapshot and interactive refs on
@@ -2912,9 +2480,9 @@ For tests specifically:
2912
2480
  plan without writing rows, caching the sync SHA, or clearing staging.
2913
2481
  - `--summary` on `components sync --dry-run` prints compact counts, removals/tombstones, skipped items, errors,
2914
2482
  and warnings instead of the full override/add/remove payload.
2915
- - **Fail-closed sync gates.** The row below ("Sync response reports unexpected deletes...") tells you to
2916
- stop *after* an unexpected sync. These flags make sync refuse it up front instead, each exiting
2917
- non-zero rather than printing a wall of JSON you have to read carefully:
2483
+ - **Fail-closed sync gates.** On non-trunk variant branches these flags now force a server dry-run first,
2484
+ evaluate that plan before any overlay row is written, and only then run the mutating sync when the plan
2485
+ passes. On trunk, there is no safe dry-run plan, so do not treat these as scoped commit controls:
2918
2486
  - `--changed-only` — fail unless every planned write is a component **this checkout actually edited**.
2919
2487
  This is the strongest guard against a sync that quietly rewrites components you never touched. It
2920
2488
  also fails when the checkout is not a git working tree, because "git could not answer" must never
@@ -2924,10 +2492,11 @@ For tests specifically:
2924
2492
  e.g. `--expected-removed action:5,reader:9`). It **implies** `--fail-on-removed`, so any removal you
2925
2493
  did not name fails the sync.
2926
2494
  - `--fail-on-errors` — fail if the server reported any per-component sync error.
2927
- - `--names-only` — print only `BUCKET type:id name` lines for the planned writes.
2495
+ - `--names-only` — dry-run and print only `BUCKET type:id name` lines for the planned writes, then stop
2496
+ without writing overlays.
2928
2497
 
2929
2498
  A good default for an unattended promotion is:
2930
- `remits-cli components sync --dry-run --summary --changed-only --fail-on-errors`
2499
+ `remits-cli components sync --summary --changed-only --fail-on-errors`
2931
2500
 
2932
2501
  ### Prod banners and retryable failures
2933
2502
 
@@ -2952,7 +2521,7 @@ For tests specifically:
2952
2521
  | Test runs old code after edit | You forgot to stage, or you are looking at DB while a staged entry is still active. Run `remits-cli components status`; then `remits-cli components stage` to update staged code or `remits-cli components clear` to fall back to DB. |
2953
2522
  | "Git credentials" or `git push` error | Git auth is required for commit (server pulls from git remote). Fix git authentication (SSH keys or HTTPS credentials) before retrying. Staging and testing still work without git. |
2954
2523
  | Need to learn command behavior | Read this skill, repo docs, or CLI source. Do not run unsupported mutating subcommand variants such as `remits-cli components commit --help`; if a command unexpectedly mutates or commits, stop and inspect the repo-local session log before doing anything else. |
2955
- | Sync response reports unexpected deletes, creates, renames, uniqueness errors, or component ID drift | Stop immediately. Do not rerun sync, do not patch around it with `mcp_component_create`, and do not promote more changes. Preserve session logs/tool responses, compare `account-info.json`, local filenames, live inventory, and git history, then prepare a repair/escalation summary. |
2524
+ | Sync response reports unexpected deletes, creates, renames, uniqueness errors, or component ID drift | Stop immediately. Do not rerun sync, do not create replacement components to paper over the mismatch, and do not promote more changes. Preserve session logs/tool responses, compare `account-info.json`, local filenames, live inventory, and git history, then prepare a repair/escalation summary. |
2956
2525
  | 500 / `Internal Server Error` from Remits | Stop normal task work. Capture the exact command, error, and response artifact, then escalate to a Remits system admin. Do not invent a workaround. |
2957
2526
  | Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
2958
2527
  | Tool response missing | Check `./.remits-cli/tool-responses/` |
@@ -2960,7 +2529,7 @@ For tests specifically:
2960
2529
  | 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`. |
2961
2530
  | 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". |
2962
2531
  | 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. |
2963
- | 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. |
2532
+ | Need the account tree below an account, or its users | In a local repo, read `account-hierarchy.json` for the generated tree. For live data, use `mcp_account_user_admin` (`action:'hierarchy'` with a `depth`, or `action:'users'`). `account-info.json` deliberately omits the tree. |
2964
2533
  | 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. |
2965
2534
  | 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. |
2966
2535
  | 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). |
@@ -2975,7 +2544,7 @@ For tests specifically:
2975
2544
  | `--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`. |
2976
2545
  | 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. |
2977
2546
  | 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`). |
2978
- | 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`. |
2547
+ | Staged Reader test run throws `No enum constant ObjectType.<family>` | The staged entry has the component family in `type` (should be `kind`). Clear + re-stage; if it persists, inspect the staged payload with `mcp_cache` and escalate as a CLI/platform staging bug. |
2979
2548
  | 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. |
2980
2549
  | Service already running | Run `remits-cli status` to get the dashboard URL, or `remits-cli stop` before restarting. |
2981
2550
  | tmux not installed | Install tmux (`brew install tmux` on macOS). Required for agent dispatch. |
@@ -2985,23 +2554,29 @@ For tests specifically:
2985
2554
 
2986
2555
  ### When Something Doesn't Work as Expected
2987
2556
 
2988
- All `remits-cli` capabilities (staging, test execution, committing, tool calls) have been tested and confirmed to work correctly. The most common issues are operational mistakes like forgetting to stage before running tests.
2989
-
2990
- However, the Remits **platform itself** may have bugs or gaps that prevent certain behavior from working. If you've followed the correct workflow (edit → stage → run) and something still doesn't behave as expected after 2-3 attempts, **do not keep trying workarounds.** First decide which kind of problem it is:
2991
-
2992
- - **A `remits-cli` / tooling operational issue** (staging, sync, dispatch, listener, the CLI itself misbehaving) → use the escalation-bundle path below and ask the user to escalate to a Remits system admin.
2993
- - **A Remits back-stage platform defect or limitation** (the component runtime behaves wrong: Hibernate/session/optimistic-locking errors, detached-entity surprises, brittle lifecycle/tool behavior, a DSL method diverging from its guide) → run the **Back-Stage Escalation Workflow** below. Do **not** normalize it into a front-stage workaround.
2994
-
2995
- Also apply the boy-scout rule to the guides themselves: **whenever you only solved the problem by reading the core back stage because a front-stage guide was unclear or missing — even if there was no platform defect at all — open a guide-only PR** updating the relevant `docs/guides/` guide so the next agent can succeed from the front stage. Better guides over time are an explicit goal.
2996
-
2997
- #### Back-Stage Escalation Workflow (platform defect/limitation)
2998
-
2999
- The core Remits platform repo is cloned on this machine and tracked in `~/.remits-cli/account-repos.json` under the reserved **`platform`** entry (default `~/remits`, or `REMITS_PLATFORM_DIR`). Use it:
3000
-
3001
- 1. **Analyze the back stage locally.** Open the platform repo and read every seam on the failing path — `src/main/groovy/remits/domain/BaseClosureDomain.groovy`, `src/main/groovy/remits/GormUtils.groovy`, the relevant controller/service/client, and `docs/back-stage-session-resilience.md` / `CLAUDE.md`. Map the runtime-compiled class name in any stack trace (`{componentType}_{componentId}_...`) back to the failing line, then trace it into the platform code to find the true root cause.
3002
- 2. **Propose the fix as a PR — and improve the guides (boy-scout rule).** Create a **feature branch** in the platform repo and open a **pull request** with the recommended back-stage fix that honors the resiliency contract (front stage stays clean; add/extend a reproducing spec or Test Account suite where it applies). The front-stage guides live in `docs/guides/` in that same repo and are the source of truth served to every account repo via `/cli/guides`, so the PR should **also** update the relevant component/feature guide whenever you had to read the back stage because the guides didn't make the answer obvious — document the new behavior for a real limitation, or fix the unclear/missing guidance even when no platform code changed (a guide bug is still a bug). **Do not merge it yourself.**
3003
- 3. **Raise a support ticket** with `mcp_support_ticket` `action:'create'` (`type:'defect'` for brittleness or `'enhancement'` for a gap). Include the subject, the seam, the evidence, and the PR; set `affectedComponent`, `priority`, and `implementationAccountId`/`implementationAccountName` for the owning `PLATFORM`/`PRODUCT` account. Use `record_progress`/`add_artifact` to attach the diagnosis and supporting evidence.
3004
- 4. **Tell the user about the PR** and the ticket so a Remits engineer can review and decide whether to approve and merge.
2557
+ All `remits-cli` capabilities — staging, test execution, committing, tool calls — are known to work. The
2558
+ most common cause is an operational mistake, above all forgetting to stage before running a test.
2559
+
2560
+ If you have followed the loop (**edit → stage → run**) and something still misbehaves after 2-3 attempts,
2561
+ **stop trying workarounds** and decide which kind of problem it is:
2562
+
2563
+ - **A `remits-cli` / tooling operational issue** — staging, sync, dispatch, the listener, or the CLI itself
2564
+ misbehaving → use the **Escalation Bundle** below and ask the user to escalate to a Remits system admin.
2565
+ - **A Remits back-stage platform defect or limitation** — the component *runtime* behaves wrong:
2566
+ Hibernate/session/optimistic-locking errors, detached-entity surprises, brittle lifecycle or tool
2567
+ behavior, a DSL method diverging from its guide → follow the **Back-Stage Escalation Workflow** in
2568
+ `features/front-stage-debugging-strategy.md` (`mcp_get_guide`), which owns it end to end: analyze the
2569
+ platform seam locally, open a PR on a feature branch (never merge it yourself), raise a
2570
+ `mcp_support_ticket`, and tell the user. **Do not normalize it into a front-stage workaround.**
2571
+
2572
+ The local clone of the core platform repo that workflow needs is tracked in
2573
+ `~/.remits-cli/account-repos.json` under the reserved **`platform`** entry (default `~/remits`, override
2574
+ with `REMITS_PLATFORM_DIR`); `remits-cli` clones it on first authenticated run if it is missing.
2575
+
2576
+ Apply the boy-scout rule to the guides themselves: **whenever you only solved the problem by reading the
2577
+ core back stage because a front-stage guide was unclear or missing — even when there was no platform
2578
+ defect at all — open a guide-only PR** updating the relevant guide in the platform repo's `docs/guides/`,
2579
+ which is the source served to every account repo. Better guides over time are an explicit goal.
3005
2580
 
3006
2581
  #### Escalation Bundle (tooling/operational issue)
3007
2582