@remits/remits-cli 0.1.95 → 0.1.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/index.js +69 -9
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +562 -960
|
@@ -7,231 +7,165 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
|
|
|
7
7
|
|
|
8
8
|
## Table of Contents
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
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
|
+
- [Runtime node and `localMode`](#runtime-node-and-localmode)
|
|
35
|
+
- [Getting Started](#getting-started)
|
|
36
|
+
- [Authentication](#authentication)
|
|
37
|
+
- [Host vs Data Mode](#host-vs-data-mode)
|
|
38
|
+
- [Tool Execution Lifecycle](#tool-execution-lifecycle)
|
|
39
|
+
- [Data Mode](#data-mode)
|
|
40
|
+
- [Development Workflow](#development-workflow)
|
|
41
|
+
- [The Golden Rule: Writing Code Is Not Finishing the Job](#the-golden-rule-writing-code-is-not-finishing-the-job)
|
|
42
|
+
- [The Development Fast Loop](#the-development-fast-loop)
|
|
43
|
+
- [Step 1: Understand the Request](#step-1-understand-the-request)
|
|
44
|
+
- [Step 2: Make the Change](#step-2-make-the-change)
|
|
45
|
+
- [Step 3: Stage to Platform](#step-3-stage-to-platform)
|
|
46
|
+
- [Step 4: Verify the Change](#step-4-verify-the-change)
|
|
47
|
+
- [Step 5: Iterate If Needed](#step-5-iterate-if-needed)
|
|
48
|
+
- [Step 6: Update Documentation](#step-6-update-documentation)
|
|
49
|
+
- [Temporary Experiment Workflow](#temporary-experiment-workflow)
|
|
50
|
+
- [Step 7: Commit and Durable Sync](#step-7-commit-and-durable-sync)
|
|
51
|
+
- [Step 8: Close the Ticket](#step-8-close-the-ticket)
|
|
52
|
+
- [User Confirmation Preferences](#user-confirmation-preferences)
|
|
53
|
+
- [Component Resolution: Staging Cache vs DB (which "version" actually runs)](#component-resolution-staging-cache-vs-db-which-version-actually-runs)
|
|
54
|
+
- [The three source layers + the compile cache](#the-three-source-layers--the-compile-cache)
|
|
55
|
+
- [Staging cache key format](#staging-cache-key-format)
|
|
56
|
+
- [How the platform picks staged vs DB (the compile signature)](#how-the-platform-picks-staged-vs-db-the-compile-signature)
|
|
57
|
+
- [When staged overrides apply](#when-staged-overrides-apply)
|
|
58
|
+
- [Diagnosing which version is in play](#diagnosing-which-version-is-in-play)
|
|
59
|
+
- [Stage / sync / clear with remits-cli](#stage--sync--clear-with-remits-cli)
|
|
60
|
+
- [Stale after sync / commit (the in-memory compile cache)](#stale-after-sync--commit-the-in-memory-compile-cache)
|
|
61
|
+
- [Account Resolution: how a request travels the account graph](#account-resolution-how-a-request-travels-the-account-graph)
|
|
62
|
+
- [Seeing an account's edges](#seeing-an-accounts-edges)
|
|
63
|
+
- [When a subscription "doesn't work"](#when-a-subscription-doesnt-work)
|
|
64
|
+
- [The same block answers the non-branch questions](#the-same-block-answers-the-non-branch-questions)
|
|
65
|
+
- [Branched Component Variants (per-account component overrides)](#branched-component-variants-per-account-component-overrides)
|
|
66
|
+
- [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)
|
|
67
|
+
- [Two levers, two different questions](#two-levers-two-different-questions)
|
|
68
|
+
- [The SDLC is identical on a variant branch](#the-sdlc-is-identical-on-a-variant-branch)
|
|
69
|
+
- [Subscribing, unsubscribing, retiring](#subscribing-unsubscribing-retiring)
|
|
70
|
+
- [Danger profile on a variant branch (different, not absent)](#danger-profile-on-a-variant-branch-different-not-absent)
|
|
71
|
+
- [Inspecting branches and drift](#inspecting-branches-and-drift)
|
|
72
|
+
- [Diagnosing a variant](#diagnosing-a-variant)
|
|
73
|
+
- [Production Support Workflow](#production-support-workflow)
|
|
74
|
+
- [Investigation Strategy](#investigation-strategy)
|
|
75
|
+
- [Presenting Findings](#presenting-findings)
|
|
76
|
+
- [Verifying a Production Issue Fix](#verifying-a-production-issue-fix)
|
|
77
|
+
- [Tool Reference](#tool-reference)
|
|
78
|
+
- [Execute a Tool](#execute-a-tool)
|
|
79
|
+
- [`mcp_account_view`](#mcp_account_view)
|
|
80
|
+
- [`mcp_account_user_admin`](#mcp_account_user_admin)
|
|
81
|
+
- [`mcp_firestore_search`](#mcp_firestore_search)
|
|
82
|
+
- [`mcp_firestore_patch`](#mcp_firestore_patch)
|
|
83
|
+
- [`mcp_object_activity`](#mcp_object_activity)
|
|
84
|
+
- [`mcp_record_listing`](#mcp_record_listing)
|
|
85
|
+
- [`mcp_record_view`](#mcp_record_view)
|
|
86
|
+
- [`mcp_ai_session_search`](#mcp_ai_session_search)
|
|
87
|
+
- [`mcp_run_action`](#mcp_run_action)
|
|
88
|
+
- [Stopping a run — `controlAction:'interrupt'`](#stopping-a-run--controlactioninterrupt)
|
|
89
|
+
- [`mcp_run_agent`](#mcp_run_agent)
|
|
90
|
+
- [Controlling a live agent — `pause` / `unpause` / `interrupt`](#controlling-a-live-agent--pause--unpause--interrupt)
|
|
91
|
+
- [`mcp_system_logs`](#mcp_system_logs)
|
|
92
|
+
- [`mcp_performance_trace`](#mcp_performance_trace)
|
|
93
|
+
- [`mcp_event_diagnostics`](#mcp_event_diagnostics)
|
|
94
|
+
- [`mcp_component_view`](#mcp_component_view)
|
|
95
|
+
- [`mcp_component_grep`](#mcp_component_grep)
|
|
96
|
+
- [`mcp_support_ticket`](#mcp_support_ticket)
|
|
97
|
+
- [Component branches](#component-branches)
|
|
98
|
+
- [`mcp_cache`](#mcp_cache)
|
|
99
|
+
- [`mcp_sql_query`](#mcp_sql_query)
|
|
100
|
+
- [`mcp_index_search`](#mcp_index_search)
|
|
101
|
+
- [`mcp_get_guide`](#mcp_get_guide)
|
|
102
|
+
- [`mcp_test_fixture`](#mcp_test_fixture)
|
|
103
|
+
- [`mcp_playwright_replay`](#mcp_playwright_replay)
|
|
104
|
+
- [`mcp_jvm_spike_triage`](#mcp_jvm_spike_triage)
|
|
105
|
+
- [`mcp_support_ticket_queue`](#mcp_support_ticket_queue)
|
|
106
|
+
- [Multi-Session Support](#multi-session-support)
|
|
107
|
+
- [Persistent Service, Control Center, and Agent Dispatch](#persistent-service-control-center-and-agent-dispatch)
|
|
108
|
+
- [Starting the Service](#starting-the-service)
|
|
109
|
+
- [Control Center](#control-center)
|
|
110
|
+
- [Agent Dispatch](#agent-dispatch)
|
|
111
|
+
- [Configuring the Preferred Agent](#configuring-the-preferred-agent)
|
|
112
|
+
- [Local State Files](#local-state-files)
|
|
113
|
+
- [Command Reference](#command-reference)
|
|
114
|
+
- [Prod banners and retryable failures](#prod-banners-and-retryable-failures)
|
|
115
|
+
- [Troubleshooting](#troubleshooting)
|
|
116
|
+
- [When Something Doesn't Work as Expected](#when-something-doesnt-work-as-expected)
|
|
117
|
+
- [Escalation Bundle (tooling/operational issue)](#escalation-bundle-toolingoperational-issue)
|
|
130
118
|
|
|
131
119
|
## Account Targeting Model
|
|
132
120
|
|
|
133
121
|
**Establish the account's shape before you touch anything.** Which repo you work in, which account you run
|
|
134
|
-
against, where a fix belongs
|
|
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.
|
|
122
|
+
against, and where a fix belongs are answered by the account's structure — never by its name.
|
|
137
123
|
|
|
138
|
-
|
|
124
|
+
The account model itself — types, component inheritance, primary vs membership edges, the three independent
|
|
125
|
+
edge properties, the user model — is in the always-loaded `platform-overview.md` and in depth in
|
|
126
|
+
`features/account-management.md` (`mcp_get_guide`). What follows is only what changes **what you type**.
|
|
139
127
|
|
|
140
|
-
|
|
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)
|
|
128
|
+
### Read the shape first
|
|
154
129
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
namespace.
|
|
130
|
+
`account-info.json` (in a repo) and `mcp_account_view` (remotely) both carry a `resolution` block — the one
|
|
131
|
+
place these facts appear. Field-by-field detail is under **`mcp_account_view`** in the Tool Reference. The
|
|
132
|
+
four that decide a CLI action:
|
|
159
133
|
|
|
160
|
-
|
|
134
|
+
| Read | To decide |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `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. |
|
|
137
|
+
| `type` | whether this repo is where the change belongs — see below |
|
|
138
|
+
| `resolvedDatabaseName` | where its data actually lands. **Check this first when documents are "missing".** |
|
|
139
|
+
| `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
140
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
141
|
+
Two more, easily confused: top-level **`componentBranches`** lists the variant branches this account
|
|
142
|
+
**owns**, with drift and subscriber counts — check it before editing a shared component. And
|
|
143
|
+
`resolution.branchName` is the **repo's trunk sync branch**, *not* a component-variant branch.
|
|
165
144
|
|
|
166
|
-
|
|
145
|
+
### Which repo does the work belong in
|
|
167
146
|
|
|
168
|
-
|
|
|
147
|
+
| Situation | Target |
|
|
169
148
|
|---|---|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
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.
|
|
149
|
+
| Feature, enhancement, shared behavior fix | usually the owning `PLATFORM` / `PRODUCT` account — **not** the `CLIENT` that reported it |
|
|
150
|
+
| Production investigation, client-specific data issue | the affected `CLIENT` account's data and runtime history |
|
|
151
|
+
| Both | confirm the symptom on the `CLIENT`, then move to the owning repo to change code |
|
|
223
152
|
|
|
224
153
|
### Repo selection rules
|
|
225
154
|
|
|
226
|
-
- **Inside the target implementation repo**: read `account-info.json` and inspect
|
|
227
|
-
- **Inside one repo but supporting a different account**: switch to
|
|
228
|
-
|
|
229
|
-
- **Outside any repo**: rely on the tools
|
|
230
|
-
- **Never create a new local repo/directory just because a ticket references an account name.**
|
|
231
|
-
|
|
232
|
-
asks you to
|
|
155
|
+
- **Inside the target implementation repo**: read `account-info.json` and inspect `components/` directly.
|
|
156
|
+
- **Inside one repo but supporting a different account**: switch to that account's repo if it exists;
|
|
157
|
+
otherwise use `mcp_account_view` / `mcp_component_view` / `mcp_component_grep`.
|
|
158
|
+
- **Outside any repo**: rely on the tools, and `mcp_get_guide` for the front-stage guides.
|
|
159
|
+
- **Never create a new local repo/directory just because a ticket references an account name.** Resolve the
|
|
160
|
+
account type and parent hierarchy first, and work only from an existing indexed repo unless the user
|
|
161
|
+
explicitly asks you to clone one.
|
|
162
|
+
|
|
163
|
+
There is no dedicated user MCP tool. For access questions — "who can see this client account?", "why does
|
|
164
|
+
this user see the wrong data?" — use `mcp_sql_query` against `user` / `user_account`. Remember a user's
|
|
165
|
+
custom fields are stored **per bound account**, so the same person can differ per account.
|
|
233
166
|
|
|
234
|
-
Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens,
|
|
167
|
+
Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens,
|
|
168
|
+
committing work, and diagnosing production issues.
|
|
235
169
|
|
|
236
170
|
## Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
|
|
237
171
|
|
|
@@ -303,15 +237,14 @@ every live component present at its real id, and nothing extra.
|
|
|
303
237
|
|
|
304
238
|
### The surfaces and their intended behavior
|
|
305
239
|
|
|
306
|
-
| Command
|
|
240
|
+
| Command | What it touches | Danger |
|
|
307
241
|
|---|---|---|
|
|
308
242
|
| `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
|
|
309
|
-
| `
|
|
310
|
-
| `
|
|
243
|
+
| `remits-cli components status` | Reads the local branch/user staging scope and branch resolution. | none |
|
|
244
|
+
| `remits-cli components clear` | Clears the local branch/user staging cache without changing DB or git. | none |
|
|
311
245
|
| `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
246
|
| `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
247
|
| `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
248
|
|
|
316
249
|
Key implications:
|
|
317
250
|
- **`stage` is always safe** — stage and test as much as you want; it never reconciles or deletes.
|
|
@@ -337,9 +270,8 @@ Key implications:
|
|
|
337
270
|
`git add -A && git commit && git push`, `remits-cli components sync`, `git pull --ff-only`.
|
|
338
271
|
|
|
339
272
|
**Create a new component:** add `new_Name.groovy` (+ `.json` / `.meta.yml` as applicable). Sync assigns the
|
|
340
|
-
durable id and renames the files.
|
|
341
|
-
|
|
342
|
-
was unexpectedly deleted or renumbered.
|
|
273
|
+
durable id and renames the files. Do not create a direct database row to work around an id/name mismatch, and
|
|
274
|
+
never create a replacement for a component that was unexpectedly deleted or renumbered.
|
|
343
275
|
|
|
344
276
|
**Delete a component (deliberate only):** remove **all** of that component's files from the repo, confirm via
|
|
345
277
|
`git status` that only those files are gone, then sync — the delete phase removes exactly that DB row. Deletion
|
|
@@ -362,9 +294,9 @@ file is catastrophic.
|
|
|
362
294
|
### If something looks wrong — stop, don't paper over
|
|
363
295
|
|
|
364
296
|
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
|
|
366
|
-
|
|
367
|
-
|
|
297
|
+
discover id drift — **stop. Do not re-run sync, do not `components commit`, and do not create replacement
|
|
298
|
+
components to "make ids line up" or replace a deleted component.** Those actions compound the corruption.
|
|
299
|
+
Preserve the repo-local session log and tool responses, and reconcile source-of-truth first.
|
|
368
300
|
|
|
369
301
|
**Safe recovery pattern (repo ↔ DB drift):**
|
|
370
302
|
1. Establish DB truth: `mcp_account_view` for the full live inventory; `mcp_component_view` to confirm and read
|
|
@@ -414,6 +346,8 @@ These files are decision inputs. Read them when the related decision depends on
|
|
|
414
346
|
- Read when the question is about available tool names, cached schemas, or why a tool invocation shape may be invalid.
|
|
415
347
|
- `account-info.json`
|
|
416
348
|
- Read in the target repo before making component changes or assuming account ownership.
|
|
349
|
+
- `account-configurations.json`
|
|
350
|
+
- Read only when account configuration values matter. It is generated separately because configuration maps can be large and `account-info.json` deliberately omits them.
|
|
417
351
|
|
|
418
352
|
Do not rely on memory for these indexes. Read the file that governs the decision you are making.
|
|
419
353
|
|
|
@@ -559,182 +493,107 @@ The right model is:
|
|
|
559
493
|
|
|
560
494
|
Never treat "it looks right in prod data inspection" as sufficient proof that a code change is verified.
|
|
561
495
|
|
|
562
|
-
##
|
|
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`
|
|
496
|
+
## The Investigation Model
|
|
607
497
|
|
|
608
|
-
|
|
498
|
+
Front stage — Schemas, Readers, Actions, Embeddables, Rules, HtmlTemplates, Agents, Tests, and the Firestore
|
|
499
|
+
**documents** that hold business data — is described in `platform-overview.md`. This section is only about
|
|
500
|
+
the **back-stage lifecycle records** an investigation actually reads, and how to read them.
|
|
609
501
|
|
|
610
|
-
|
|
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.
|
|
502
|
+
### Which tool reads which record
|
|
625
503
|
|
|
626
|
-
|
|
504
|
+
The model — Documents (Firestore business data) vs Objects and their record trail (MySQL), linked by
|
|
505
|
+
`object_id` — is in `platform-overview.md` → *Objects, Documents, and the Record Trail*. What matters
|
|
506
|
+
here is the tool and the filterable fields:
|
|
627
507
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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?"
|
|
508
|
+
| Record | Query it with | Fields worth filtering on |
|
|
509
|
+
|---|---|---|
|
|
510
|
+
| document | `mcp_firestore_search` | any schema field, plus `account_id`, `object_id`, `_lastModifiedAt` |
|
|
511
|
+
| `object` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `name`, `referenceId` |
|
|
512
|
+
| `object_log` | `mcp_record_listing` / `mcp_record_view` | `type`, `description`, `content`, `threadGroupingId` |
|
|
513
|
+
| `event` | `mcp_record_listing` / `mcp_record_view` | `action`, `status`, `eventDate`, `threadGroupingId` |
|
|
514
|
+
| `alert` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `active`, `threadGroupingId` |
|
|
652
515
|
|
|
653
|
-
|
|
516
|
+
`mcp_record_listing` finds candidates when you do not know the id; `mcp_record_view` opens an exact one;
|
|
517
|
+
`mcp_object_activity` returns one Object's whole timeline in order.
|
|
654
518
|
|
|
655
|
-
|
|
519
|
+
**Check the data lane before concluding a record does not exist** — `--data-mode test` and `prod` read
|
|
520
|
+
different lanes (see `platform-overview.md` → *Test and Production Data Lanes*).
|
|
656
521
|
|
|
657
|
-
|
|
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
|
|
522
|
+
### Correlation keys
|
|
661
523
|
|
|
662
|
-
|
|
524
|
+
- **`object_id`** — links documents to records. Pivot `mcp_firestore_search` → `mcp_object_activity`.
|
|
525
|
+
- **`threadGroupingId`** — groups every record and log line from one processing chain, and is also the
|
|
526
|
+
request's trace id. Pivot into `mcp_system_logs` and `mcp_performance_trace`.
|
|
527
|
+
- **`node`** — resolves to `serviceName` + `region` for log queries (table below).
|
|
528
|
+
- **`sessionId`** — pivots into persisted AI activity via `mcp_ai_session_search`.
|
|
663
529
|
|
|
664
|
-
|
|
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.
|
|
530
|
+
### Reading a record's `content` — persisted context, not a memory dump
|
|
670
531
|
|
|
671
|
-
|
|
532
|
+
`content` on an `object`, `object_log`, `event`, or `alert` is the **sanitized snapshot** the platform
|
|
533
|
+
deliberately kept of what the producing component knew at that workflow step. It is not everything that was
|
|
534
|
+
in memory.
|
|
672
535
|
|
|
673
|
-
|
|
536
|
+
- `Object.content` — ingestion/request/file context when the Object was created or updated.
|
|
537
|
+
- `ObjectLog.content` — the logging component's point-in-time view.
|
|
538
|
+
- `Event.content` — the scheduling component's view, plus the explicit event options.
|
|
539
|
+
- `Alert.content` — the raising component's view, plus the explicit alert body.
|
|
674
540
|
|
|
675
|
-
|
|
676
|
-
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
- Account/User schema fields marked `sensitive: true` may also be redacted before persistence
|
|
541
|
+
**Missing or `[REDACTED]` does not mean the component never had it.** Before persisting, the platform drops
|
|
542
|
+
non-serializable objects, strips `requestBody` / `params` / `token` recursively, summarizes very large
|
|
543
|
+
strings, prunes oversized maps and collections, and redacts secret-looking keys (`password`, `secret`,
|
|
544
|
+
`authorization`, `cookie`, `clientSecret`, …) plus any Account/User schema field marked `sensitive: true`.
|
|
545
|
+
When explaining a record, distinguish what the workflow *had* at runtime from what the platform *kept*.
|
|
681
546
|
|
|
682
|
-
|
|
547
|
+
**Provenance travels in the context itself:**
|
|
683
548
|
|
|
684
|
-
- the
|
|
685
|
-
- the
|
|
549
|
+
- **`source_bcd`** — the immediate component that produced this record, e.g. `[Action:18] DataPlus Invoice Posting`
|
|
550
|
+
- **`upstream_source_bcd`** — the prior workflow hop, when the context was inherited from one
|
|
686
551
|
|
|
687
|
-
|
|
552
|
+
**Never interpret `content` in isolation.** Pair it with the record type, `source_bcd`, any
|
|
553
|
+
`upstream_source_bcd`, the `object_id` timeline, the `threadGroupingId` chain, and the producing
|
|
554
|
+
component's source. The goal is not to find a suspicious record — it is to explain **why the context looks
|
|
555
|
+
exactly the way it does relative to the workflow step that produced it**.
|
|
688
556
|
|
|
689
|
-
|
|
690
|
-
- what the platform intentionally kept in persistent `content`
|
|
557
|
+
### HTTP audits
|
|
691
558
|
|
|
692
|
-
|
|
559
|
+
Raw inbound (Reader) and outbound (`rest(...)`) HTTP is persisted to Firestore — but **only for accounts
|
|
560
|
+
with `Account.enableHttpAudits`**. When it is off, the absence of audit documents proves nothing.
|
|
693
561
|
|
|
694
|
-
|
|
562
|
+
Audits live in **monthly** collections, not one global collection:
|
|
695
563
|
|
|
696
|
-
|
|
564
|
+
```
|
|
565
|
+
http-audits/http-audits-YYYY-MM/entries
|
|
566
|
+
```
|
|
697
567
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
- the producing component source (`mcp_component_view`, `mcp_component_grep`, or local `/components`)
|
|
568
|
+
Start with the month the request ran in, and check the adjacent month if the run may have crossed a
|
|
569
|
+
boundary. Query them with `mcp_firestore_search` like any other collection; the filters worth reaching for
|
|
570
|
+
are `direction` (`INBOUND` / `OUTBOUND`), `success`, `request.method`, `request.path`, `component.name`, and
|
|
571
|
+
`response.statusCode`. Some paths are deliberately excluded from persistence, so a missing audit is not
|
|
572
|
+
proof a call was never made.
|
|
704
573
|
|
|
705
|
-
|
|
574
|
+
Reach for audits when the question is *what exact request went out, what came back, and did this component
|
|
575
|
+
actually make the call* — then use the component source plus the payload to place the fault in request
|
|
576
|
+
formation, the partner's response, or downstream processing. Full captured shape, websocket behavior, and
|
|
577
|
+
embeddable patterns: `features/http-audits.md` (`mcp_get_guide`).
|
|
706
578
|
|
|
707
|
-
### AI
|
|
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.
|
|
579
|
+
### AI activity
|
|
713
580
|
|
|
714
|
-
|
|
581
|
+
Every `ai()` call and Agent turn is persisted. Two ways in:
|
|
715
582
|
|
|
716
|
-
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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.
|
|
583
|
+
- **`mcp_ai_session_search`** — search groupings and open their detail. A grouping spans every session
|
|
584
|
+
sharing one grouping id: the agent turns plus its guardrail and internal `ai()` calls. Use the
|
|
585
|
+
**map → open** flow described under its Tool Reference entry, never a whole-detail dump.
|
|
586
|
+
- **`ai_request_response([sessionId: id])`** — from inside component code, loads the stored
|
|
587
|
+
request/response history for a session (`first: true` / `last: true` for one record). This is also how
|
|
588
|
+
you replay a stored provider response in a test.
|
|
732
589
|
|
|
733
|
-
|
|
590
|
+
If a document or agent state already carries a session id, that is a direct pivot into persisted AI
|
|
591
|
+
activity.
|
|
734
592
|
|
|
735
|
-
|
|
736
|
-
-
|
|
737
|
-
|
|
593
|
+
**Before tuning any prompt, read the session.** `features/ai-session-investigation.md` owns the method —
|
|
594
|
+
per-turn forensics, what each layer proves, and the rule that what a tool **PRODUCED** is not necessarily
|
|
595
|
+
what the model **CONSUMED**. `features/ai-strategy.md` owns what to change once you know. Changing a prompt
|
|
596
|
+
before reading the persisted request/response is guessing.
|
|
738
597
|
|
|
739
598
|
### Node Reference Table
|
|
740
599
|
|
|
@@ -746,12 +605,28 @@ The goal of an investigation is not only to find a suspicious record. It is to e
|
|
|
746
605
|
|
|
747
606
|
`mcp_system_logs` accepts `node` directly and resolves it automatically.
|
|
748
607
|
|
|
749
|
-
###
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
608
|
+
### Runtime node and `localMode`
|
|
609
|
+
|
|
610
|
+
The deployed `remits` service in `us-east5` (`remitsAdmin-east5`) runs with the platform setting
|
|
611
|
+
`localMode=true`. If someone says "localModel" in this context, confirm they mean this `localMode`
|
|
612
|
+
setting. Operationally, immediate async follow-on work stays on the same Cloud Run service/node instead of
|
|
613
|
+
being sharded to `remits-actions`:
|
|
614
|
+
|
|
615
|
+
- Pub/Sub-style follow-on messages are handled locally after commit.
|
|
616
|
+
- Near-immediate tasks are handled locally when `localMode` is enabled. Future scheduled tasks still use
|
|
617
|
+
Cloud Tasks.
|
|
618
|
+
- Local worker hops preserve the run context, including staged-source resolution, data mode,
|
|
619
|
+
`threadGroupingId`, and trace correlation.
|
|
620
|
+
- Durable boundaries such as async HTTP ingress and Events carry that same run context across the queue.
|
|
621
|
+
|
|
622
|
+
For investigations on the default deployed host (`https://remits-529558023549.us-east5.run.app`), do not
|
|
623
|
+
assume "async" means `remitsActions` / `us-east1`. Start with `node:"remitsAdmin-east5"` and the
|
|
624
|
+
`threadGroupingId`; pivot to `remitsActions` only when the Event delivery envelope, log line, or returned
|
|
625
|
+
node says the work actually ran there.
|
|
626
|
+
|
|
627
|
+
This does not change the data-lane rule: a non-null `TestMode` can exist only to carry branch/staged-source
|
|
628
|
+
resolution. Data isolation is decided by CLI `--data-mode`: a branch-scoped `--data-mode prod` run is still
|
|
629
|
+
prod data, while `--data-mode test` remains isolated test data.
|
|
755
630
|
|
|
756
631
|
## Getting Started
|
|
757
632
|
|
|
@@ -855,28 +730,24 @@ remits-cli data-mode set test # Switch back to test for development
|
|
|
855
730
|
|
|
856
731
|
### The Golden Rule: Writing Code Is Not Finishing the Job
|
|
857
732
|
|
|
858
|
-
**A change is not complete until it is verified.** Writing
|
|
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`.
|
|
861
|
-
|
|
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."
|
|
863
|
-
|
|
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.
|
|
733
|
+
**A change is not complete until it is verified.** Writing the component is the first step, not the last.
|
|
734
|
+
Two ways to prove it:
|
|
865
735
|
|
|
866
|
-
|
|
736
|
+
1. **A Test component** (preferred) — it exercises the change *and* becomes permanent regression
|
|
737
|
+
protection. Run it with `remits-cli test run`.
|
|
738
|
+
2. **Visual verification** — `remits-cli token` for a browser URL, then drive it with `playwright-cli`.
|
|
739
|
+
This is how most users think about verification: "let me see it working."
|
|
867
740
|
|
|
868
|
-
|
|
741
|
+
Use a Test when the behavior can be asserted programmatically, a browser when the change is visual.
|
|
742
|
+
Often both.
|
|
869
743
|
|
|
870
|
-
|
|
744
|
+
**Never skip verification.** "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
|
|
745
|
+
cannot verify — no Test component, no relevant embeddable — say what you would need and ask how the user
|
|
746
|
+
wants to proceed rather than reporting the work as done.
|
|
871
747
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
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.
|
|
748
|
+
> The *design* rule that pairs with this — never solve interpretive problems with regex cascades, keyword
|
|
749
|
+
> lists, or layout-specific branching when the platform's AI surface is the right tool — is in the account
|
|
750
|
+
> repo's `CLAUDE.md` and, in depth, in `features/ai-strategy.md`.
|
|
880
751
|
|
|
881
752
|
### The Development Fast Loop
|
|
882
753
|
|
|
@@ -1130,8 +1001,8 @@ and a precise diagnosis.
|
|
|
1130
1001
|
### The three source layers + the compile cache
|
|
1131
1002
|
|
|
1132
1003
|
1. **CLI staging cache (Redis, 240-min TTL).** Branch + user + account scoped overrides written by
|
|
1133
|
-
`remits-cli components stage
|
|
1134
|
-
|
|
1004
|
+
`remits-cli components stage`. These shadow the layers below **only during CLI/test-mode execution**
|
|
1005
|
+
(see "When staged overrides apply" below).
|
|
1135
1006
|
2. **Committed branch variants (`ComponentVariant`, MySQL).** Durable, branch-scoped overlays of a
|
|
1136
1007
|
component. Unlike staging these are **not** user-scoped, do **not** expire, and **do** apply to normal
|
|
1137
1008
|
production traffic — for the accounts that subscribe to that branch. See
|
|
@@ -1203,7 +1074,6 @@ Staged overrides resolve whenever the execution carries a **CLI-scoped TestMode*
|
|
|
1203
1074
|
- `remits-cli test run`
|
|
1204
1075
|
- `remits-cli tool`
|
|
1205
1076
|
- `remits-cli tools`
|
|
1206
|
-
- `mcp_run_test`
|
|
1207
1077
|
- tokenized runs minted with a branch-aware CLI token
|
|
1208
1078
|
|
|
1209
1079
|
For `remits-cli tool`, the CLI TestMode now stays active for the **entire tool execution**, not just the
|
|
@@ -1240,29 +1110,18 @@ TestMode still uses the committed DB source. Staging remains a dev/verification
|
|
|
1240
1110
|
`--verbose` when you need the full staged-entry payload. `remits-cli components clear` removes those entries
|
|
1241
1111
|
when you intentionally want to fall back to DB source.
|
|
1242
1112
|
|
|
1243
|
-
### Stage /
|
|
1244
|
-
|
|
1245
|
-
- `
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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.
|
|
1113
|
+
### Stage / sync / clear with remits-cli
|
|
1114
|
+
|
|
1115
|
+
- `remits-cli components stage` writes local file changes into the Redis staging cache for the current
|
|
1116
|
+
branch/user scope. This is the normal edit/test loop.
|
|
1117
|
+
- `remits-cli components status` shows which branch/variant world the checkout resolves, plus staged entries,
|
|
1118
|
+
staged fields, aliases, hashes, and TTLs. Use `--json` or `--verbose` for the full staged-entry payload.
|
|
1119
|
+
- `remits-cli components clear` drops staged entries when you intentionally want to fall back to committed DB
|
|
1120
|
+
source. An empty staging scope is clean state, not a failure.
|
|
1121
|
+
- `remits-cli components sync` syncs the DB from the pushed git remote and then clears staged entries for the
|
|
1122
|
+
synced components, so a clean promotion leaves a clean staging cache. Because sync reconciles the remote repo
|
|
1123
|
+
into the live component database, it must pass the Component Integrity Rules first. Never use sync only to
|
|
1124
|
+
clear staging, to recover from a mismatched ID, or to retry after an unexpected create/delete/rename response.
|
|
1266
1125
|
|
|
1267
1126
|
### Stale after sync / commit (the in-memory compile cache)
|
|
1268
1127
|
|
|
@@ -1277,207 +1136,89 @@ the platform owner recycles the instance.
|
|
|
1277
1136
|
## Account Resolution: how a request travels the account graph
|
|
1278
1137
|
|
|
1279
1138
|
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
|
|
1139
|
+
current request reached the executing account**. Read this before debugging *"my subscriber isn't picking
|
|
1140
|
+
up the branch"* or *"why is this account reading the wrong collection"* — those are almost always
|
|
1282
1141
|
resolution questions, not component bugs.
|
|
1283
1142
|
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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 |
|
|
1143
|
+
> The model behind it — the linear inheritance walk, the ambiguity rule, the three orthogonal edge
|
|
1144
|
+
> properties, and path-scoped storage namespaces — is in **`platform-overview.md` → *Account Structure***,
|
|
1145
|
+
> which is already loaded. What follows is how to *observe* it from the CLI.
|
|
1315
1146
|
|
|
1316
|
-
**The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id
|
|
1317
|
-
|
|
1318
|
-
same branch
|
|
1147
|
+
**The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id`, stamped onto
|
|
1148
|
+
the `Object` / `Event` / `Alert` / `ObjectLog` records a run creates, so async workers re-resolve on the
|
|
1149
|
+
same branch. A **null** anchor means "walk the structural chain". Entry points that already know the
|
|
1150
|
+
branch supply an anchor; from the CLI you supply it explicitly with `--as-account`, `--variant-branch`, or
|
|
1151
|
+
by using the edge's own host.
|
|
1319
1152
|
|
|
1320
|
-
|
|
1321
|
-
**no** `parentId` it continues through that account's *single* active membership edge. Applied at every hop:
|
|
1322
|
-
|
|
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:
|
|
1153
|
+
### Seeing an account's edges
|
|
1353
1154
|
|
|
1354
1155
|
```bash
|
|
1355
1156
|
remits-cli tool --name mcp_account_view --input '{"accountId": 101}' --data-mode prod
|
|
1356
1157
|
# then read: resolution.relationships, resolution.resolvedDatabaseName, resolution.componentBranch
|
|
1357
1158
|
```
|
|
1358
1159
|
|
|
1359
|
-
|
|
1160
|
+
`resolution.relationships` returns one entry per structural link, primary first, each carrying its own
|
|
1161
|
+
`branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so
|
|
1162
|
+
this block is the **only** place that answers "how many parents does this account really have, and which
|
|
1163
|
+
link carries what?"
|
|
1360
1164
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1165
|
+
### When a subscription "doesn't work"
|
|
1166
|
+
|
|
1167
|
+
1. Does the account have a primary parent, or is it membership-only? Count `resolution.relationships` and
|
|
1168
|
+
see which is `primary: true`. (Membership-only **and** several edges ⇒ ambiguous ⇒ trunk, by design.)
|
|
1169
|
+
2. Which **edge** carries the `branchName`? `remits-cli components branch <name> --subscribers` prints
|
|
1170
|
+
`via primary|membership edge -> parent N`.
|
|
1366
1171
|
3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
|
|
1367
1172
|
account's own edge selects the branch, exactly as production would.
|
|
1368
|
-
4. Check the resolved layer, not the source text: `testComponentSource
|
|
1173
|
+
4. Check the resolved layer, not the source text: `testComponentSource`, or the `variant:<id>:<hash>`
|
|
1369
1174
|
compile signature (see "Diagnosing a variant").
|
|
1370
1175
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
this
|
|
1374
|
-
|
|
1176
|
+
### The same block answers the non-branch questions
|
|
1177
|
+
|
|
1178
|
+
- *"Why are this account's documents not where I expect?"* → compare `databaseName` vs
|
|
1179
|
+
`resolvedDatabaseName`, and check for a `databaseName` on one of the `relationships` edges — it applies
|
|
1180
|
+
to everything below that edge.
|
|
1181
|
+
- *"Why does this hostname land on the wrong account?"* → compare `domainName` vs `resolvedDomainName` and
|
|
1182
|
+
the edge `domainName`. An **edge** host wins over the account's own, and additionally supplies the path
|
|
1183
|
+
travelled — which is what makes that edge's branch variants apply.
|
|
1375
1184
|
|
|
1376
|
-
**Users are not part of this graph
|
|
1377
|
-
|
|
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.
|
|
1185
|
+
**Users are not part of this graph** (see `platform-overview.md`). There is no user MCP tool — use
|
|
1186
|
+
`mcp_sql_query` against `user` / `user_account`.
|
|
1381
1187
|
|
|
1382
1188
|
## Branched Component Variants (per-account component overrides)
|
|
1383
1189
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
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.
|
|
1190
|
+
When one account — often a customer nested several levels down — needs *slightly* different behavior from
|
|
1191
|
+
a component owned by its platform or product account, the answer is a **branch variant**: a durable,
|
|
1192
|
+
branch-scoped overlay of that component, resolved only by accounts subscribed to that branch. The wrong
|
|
1193
|
+
answer is per-account `if/then` logic inside the origin component.
|
|
1448
1194
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1195
|
+
> **`features/subscriber-branch-promotions.md` (`mcp_get_guide`) is the guide for this.** It owns the
|
|
1196
|
+
> mental model, the five scenarios you will actually meet, the full promotion procedure, what happens to a
|
|
1197
|
+
> `new_` component across a promotion, merge-conflict resolution file by file, removals, and the approval
|
|
1198
|
+
> gates for running a promotion as a coding agent. **Load it before promoting anything.** This section
|
|
1199
|
+
> covers only the CLI surface and the traps that bite at the command line.
|
|
1200
|
+
|
|
1201
|
+
Three facts that everything else follows from:
|
|
1202
|
+
|
|
1203
|
+
- **A branch is not an account.** Subscription is many-to-many, it applies to the subscribing account
|
|
1204
|
+
*and its descendants*, and a subscribing account still has its own trunk components which merge on top.
|
|
1205
|
+
- **A variant keeps the origin's component id** — it is an overlay, not a copy. That is what makes drift
|
|
1206
|
+
computable.
|
|
1207
|
+
- **Sparseness is computed at sync time, not declared.** A git branch physically contains every file; only
|
|
1208
|
+
the ones whose content *differs from trunk* become variants. A file that is absent becomes a
|
|
1209
|
+
**tombstone**; a file whose id prefix is not a live trunk id (`new_Foo.groovy`) becomes a **branch-only**
|
|
1210
|
+
component keyed by name.
|
|
1462
1211
|
|
|
1463
1212
|
### Which world does your working tree resolve? (read this before you run anything)
|
|
1464
1213
|
|
|
1465
|
-
You will work from **two different checkouts of the same repo**, and they behave differently on both ends
|
|
1466
|
-
the loop. The rule turns entirely on **trunk vs non-trunk**:
|
|
1214
|
+
You will work from **two different checkouts of the same repo**, and they behave differently on both ends
|
|
1215
|
+
of the loop. The rule turns entirely on **trunk vs non-trunk**:
|
|
1467
1216
|
|
|
1468
1217
|
| Working tree | Staging scope | A run resolves | `components sync`/`commit` writes |
|
|
1469
1218
|
|---|---|---|---|
|
|
1470
1219
|
| **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
1220
|
| **any other branch** (`feature_branch`) | that branch | trunk + **`feature_branch`** overlays | **`ComponentVariant` overlays on that branch only** — never touches trunk rows |
|
|
1472
1221
|
|
|
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
1222
|
Do not infer this from the branch name. Ask:
|
|
1482
1223
|
|
|
1483
1224
|
```bash
|
|
@@ -1492,152 +1233,117 @@ Working tree: VARIANT BRANCH "feature_branch" (trunk is "main")
|
|
|
1492
1233
|
subscribing accounts: 101 (Acme Child)
|
|
1493
1234
|
```
|
|
1494
1235
|
|
|
1495
|
-
**
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
- `
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
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.
|
|
1236
|
+
**The precedence trap that costs the most time:** `variantBranch` **outranks every account's
|
|
1237
|
+
subscription**. So running a Test suite that asserts *production* semantics from a **variant checkout**
|
|
1238
|
+
pins every account in that suite — including fixture accounts subscribed to their own generated branches —
|
|
1239
|
+
to your branch, where they have no variants, and they all read trunk. The suite fails in a way that looks
|
|
1240
|
+
exactly like a resolution regression. (Live example: a branch-variant suite scored 4/15 from a variant
|
|
1241
|
+
checkout and 15/15 from trunk, with no code difference.) **Run branch-variant suites from trunk, or pass
|
|
1242
|
+
`--variant-branch none`.** Before concluding "variant resolution is broken", re-run from trunk.
|
|
1243
|
+
|
|
1244
|
+
**Two other things differ from trunk while you work on a branch:**
|
|
1245
|
+
|
|
1246
|
+
- **Keep the origin id in the filename.** `50_ExtractInvoice.groovy` on the branch overlays Action 50.
|
|
1247
|
+
That is what preserves component identity and lets drift be computed against the origin.
|
|
1248
|
+
- **Nothing is renamed.** A variant sync never renames files and never repoints the account's trunk
|
|
1249
|
+
branch. A `new_*` file stays `new_*`.
|
|
1250
|
+
|
|
1251
|
+
**Branch-local `account-info.json` can describe a subscriber.** On a variant branch the repository is
|
|
1252
|
+
still the OWNER's component repo — files overlay that owner's trunk ids and sync writes overlays owned by
|
|
1253
|
+
that owner. But when the checkout was synced from a subscribing account reached through an
|
|
1254
|
+
`AccountRelationship` edge, and the branch has **exactly one** subscriber, the branch's
|
|
1255
|
+
`account-info.json` is intentionally rooted at that subscriber. With **more than one** subscriber the
|
|
1256
|
+
refresh is skipped (the sync result says so under `accountInfo.skipped`/`reason`) and the file keeps
|
|
1257
|
+
describing the owner, which is at least true for all of them. Overlay ownership is unaffected either way.
|
|
1258
|
+
|
|
1259
|
+
Read `resolution` before acting from any checkout — in particular `resolution.accountId` (the account this
|
|
1260
|
+
checkout should be treated as; **read this, not the top-level `id`**), `resolution.role`,
|
|
1261
|
+
`resolution.componentOwnerAccountId` (whose trunk ids the files overlay and whose overlays a sync writes),
|
|
1262
|
+
and `resolution.componentBranch` / `resolution.scopeAccountId` (the branch and the path anchor that made
|
|
1263
|
+
this subscriber resolution possible). If a variant checkout's `account-info.json` is stale
|
|
1264
|
+
and still names the owner, run the first repair sync with an explicit subscriber target
|
|
1265
|
+
(`remits-cli components sync --account-id 101`), then `git pull --ff-only`.
|
|
1529
1266
|
|
|
1530
1267
|
### Two levers, two different questions
|
|
1531
1268
|
|
|
1532
1269
|
- **`--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
|
|
1534
|
-
account you already have access to).
|
|
1270
|
+
Answers *"what does customer X actually get?"* Only narrows downward (the target must be reachable from
|
|
1271
|
+
an account you already have access to). It works for a `parentId`-less, membership-only subscriber too:
|
|
1272
|
+
the anchor is derived from the branch you name, or from the account's single branch subscription. It
|
|
1273
|
+
**refuses to guess** when an account has several edges each carrying a different branch — the run then
|
|
1274
|
+
resolves trunk, and you must name the branch.
|
|
1535
1275
|
- **`--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
|
|
1537
|
-
`--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the
|
|
1538
|
-
|
|
1539
|
-
and tool execution can all inspect the same committed variant world.
|
|
1276
|
+
look like?"* — most useful for verifying a freshly committed branch **before** any edge subscribes to
|
|
1277
|
+
it. `--variant-branch none` (or `trunk`) forces production/subscription semantics without leaving the
|
|
1278
|
+
branch.
|
|
1540
1279
|
|
|
1541
1280
|
Both work on `remits-cli test run` and `remits-cli token`; `--variant-branch` also works on
|
|
1542
|
-
`remits-cli tools` and `remits-cli tool
|
|
1281
|
+
`remits-cli tools` and `remits-cli tool`, so tests, browser URLs, tool discovery, and tool execution can
|
|
1282
|
+
all inspect the same committed variant world.
|
|
1543
1283
|
|
|
1544
|
-
|
|
1284
|
+
The strongest end-to-end proof for a UI-visible variant is a token, not a log line:
|
|
1545
1285
|
|
|
1546
|
-
|
|
1286
|
+
```bash
|
|
1287
|
+
remits-cli token --path embeddable/index/50 --as-account 101 # subscriber -> variant
|
|
1288
|
+
remits-cli token --path embeddable/index/50 # owner -> trunk
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
### The SDLC is identical on a variant branch
|
|
1547
1292
|
|
|
1548
1293
|
```bash
|
|
1549
1294
|
git checkout feature_branch # or: git checkout -b feature_branch
|
|
1550
1295
|
# edit components/actions/50_ExtractInvoice.groovy (KEEP the trunk id)
|
|
1551
1296
|
remits-cli components stage # Redis, scoped to this branch — same as always
|
|
1552
|
-
remits-cli test run --test "Invoice Tests"
|
|
1297
|
+
remits-cli test run --test "Invoice Tests" # the feature_branch world
|
|
1553
1298
|
remits-cli test run --test "Invoice Tests" --as-account 101 # ...as the real subscriber
|
|
1554
|
-
# promote:
|
|
1555
1299
|
git add -A && git commit -m "..." && git push origin feature_branch
|
|
1556
1300
|
remits-cli components sync --dry-run # inspect overrides/additions/tombstones without writes
|
|
1557
1301
|
remits-cli components sync # writes ComponentVariant overlays ONLY
|
|
1558
1302
|
```
|
|
1559
1303
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
-
|
|
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.
|
|
1304
|
+
Promotion back to trunk is a **git** operation followed by a **trunk** sync — the platform merges nothing
|
|
1305
|
+
for you, and merging a branch promotes its **deletions** as hard deletes. Do not improvise it: follow
|
|
1306
|
+
`features/subscriber-branch-promotions.md`.
|
|
1568
1307
|
|
|
1569
|
-
###
|
|
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.
|
|
1308
|
+
### Subscribing, unsubscribing, retiring
|
|
1573
1309
|
|
|
1574
1310
|
```bash
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
the
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
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.
|
|
1616
|
-
|
|
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:
|
|
1622
|
-
|
|
1623
|
-
```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:
|
|
1311
|
+
remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>]
|
|
1312
|
+
remits-cli components branch <name> --unsubscribe <accountId>
|
|
1313
|
+
remits-cli components branch <name> --retire [--force]
|
|
1314
|
+
```
|
|
1315
|
+
|
|
1316
|
+
Branch administration commands act **as the account you are running from**, which is treated as the branch
|
|
1317
|
+
**owner** — run them from the owner's trunk checkout. Authorization is downward-only.
|
|
1318
|
+
|
|
1319
|
+
- **`--subscribe` sets the branch on an edge that must already exist — it cannot create one.** Failure
|
|
1320
|
+
reads *"Account N has no relationship edge to subscribe; add a membership edge first"*. Create it with
|
|
1321
|
+
`mcp_account_user_admin` `action:'edge_add'` (`targetAccountId` = the child, `parentAccountId` = the
|
|
1322
|
+
owner), which also accepts `branchName` so you can create and subscribe in one call.
|
|
1323
|
+
- **Many older accounts have no relationship row at all** — the edge table was added after the fact and
|
|
1324
|
+
never backfilled, so an account whose parent link is only `Account.parentId` resolves fine but has
|
|
1325
|
+
nothing to attach to. `mcp_account_user_admin` `action:'reparent'` with the account's *current* parent
|
|
1326
|
+
is the one-call repair: it creates the missing primary edge and changes nothing else.
|
|
1327
|
+
- **When the account has several parents, say which edge you mean** with `--parent-account <ownerId>`.
|
|
1328
|
+
Without it the CLI picks the edge to the owner whose branch you are managing, then falls back to the
|
|
1329
|
+
account's primary edge — which may not be the one you intended. `--subscribers` prints
|
|
1330
|
+
`via primary|membership edge -> parent N` so you can confirm.
|
|
1331
|
+
- **Retiring is explicit.** Deleting the *git* branch does **not** remove its overlays; subscribers would
|
|
1332
|
+
keep resolving a branch that no longer exists. `--retire` refuses while subscribers remain unless you
|
|
1333
|
+
pass `--force`.
|
|
1334
|
+
|
|
1335
|
+
**Every component kind can be varied** — `schema`, `reader`, `action`, `embeddable`, `i18n`, `htmltemplate`,
|
|
1336
|
+
`rule`, `test`, `utility` (agent), `tool`, `prompt` — including tombstones and branch-only additions. A
|
|
1337
|
+
**schema** variant deserves the same care as a trunk schema change: it can change the JSON schema *and*
|
|
1338
|
+
`collectionName`, so it changes validation and where the subscriber's documents physically land. A
|
|
1339
|
+
branch-only agent (a `new_*` file under `components/agents/`) defaults to `type: AI` so agent lookups
|
|
1340
|
+
find it.
|
|
1634
1341
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
ignored. If a branch needs a different tool set, change trunk or have the agent choose tools at runtime.
|
|
1342
|
+
A variant speaks the same `.meta.yml` vocabulary trunk does; keys outside that set are ignored on purpose,
|
|
1343
|
+
because trunk cannot express them either — allowing them would mean a branch behaves one way and silently
|
|
1344
|
+
loses that behavior the moment it is promoted. **One documented exception: an agent's `tools:` list cannot
|
|
1345
|
+
be overridden by a variant.** It maps to a GORM association that `agent()` populates from the trunk row, so
|
|
1346
|
+
a `tools:` list on a branch is ignored. Change trunk, or have the agent select tools at runtime.
|
|
1641
1347
|
|
|
1642
1348
|
### Danger profile on a variant branch (different, not absent)
|
|
1643
1349
|
|
|
@@ -1645,27 +1351,30 @@ The Component Integrity Rules' worst case — *"a missing file hard-deletes a li
|
|
|
1645
1351
|
apply on a variant branch.** Variant sync writes overlay rows only; it cannot create, delete, rename, or
|
|
1646
1352
|
overwrite a trunk component. That makes a variant branch a genuinely safer place to iterate.
|
|
1647
1353
|
|
|
1648
|
-
The analogous hazard is different and you must still respect it:
|
|
1354
|
+
The analogous hazard is different, and you must still respect it:
|
|
1649
1355
|
|
|
1650
1356
|
- **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
|
|
1652
|
-
subscribing account it looks exactly like the component was deleted.
|
|
1653
|
-
|
|
1357
|
+
every subscriber. It is reversible (restore the file, re-sync) and never touches trunk — but to a
|
|
1358
|
+
subscribing account it looks exactly like the component was deleted. Confirm every omission is
|
|
1359
|
+
deliberate before syncing.
|
|
1360
|
+
- **Deleting a file means "remove the component", not "stop overriding it."** To withdraw an override,
|
|
1361
|
+
make the file identical to trunk again — the sync then removes the variant row.
|
|
1362
|
+
- **Keep the branch rebased.** A branch physically carries every file, so anything trunk added *after* the
|
|
1363
|
+
branch was cut looks like a deliberate deletion. A small stale gap tombstones silently.
|
|
1654
1364
|
- **Use `components sync --dry-run` before risky variant syncs.** It reports `overridden`, `added`,
|
|
1655
|
-
`removed`, `unchanged`, `skipped`, and `errors` without writing
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
`components commit --dry-run` is unsupported because `commit` performs local git writes before
|
|
1365
|
+
`removed`, `unchanged`, `skipped`, and `errors` without writing rows, caching the sync SHA, or clearing
|
|
1366
|
+
staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means the
|
|
1367
|
+
branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
|
|
1368
|
+
`components commit --dry-run` is unsupported because `commit` performs local git writes before syncing.
|
|
1369
|
+
- **The sync refuses a wholesale removal.** Above roughly a third of a kind — or **100% of a kind at any
|
|
1370
|
+
size** — it aborts that kind, reports why, and points at a rebase. Rebasing is almost always the real
|
|
1371
|
+
fix. Only when the removals are genuinely deliberate, re-run with `--force-tombstones`.
|
|
1659
1372
|
- **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
|
|
1661
|
-
component that exists in the DB with **no file on trunk** is missing from every branch too, so it
|
|
1662
|
-
as a phantom `removed` in *every* branch preview
|
|
1663
|
-
on every run. Check whether the file exists on trunk before "fixing" it on the branch;
|
|
1664
|
-
mirror the live DB source back into the trunk repo (Component Integrity Rules)
|
|
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`.
|
|
1373
|
+
- **A removal you did not author usually means TRUNK is drifted**, not that the branch deleted something.
|
|
1374
|
+
A component that exists in the DB with **no file on trunk** is missing from every branch too, so it
|
|
1375
|
+
shows up as a phantom `removed` in *every* branch preview — while the trunk sync separately tries to
|
|
1376
|
+
hard-delete it on every run. Check whether the file exists on trunk before "fixing" it on the branch;
|
|
1377
|
+
the repair is to mirror the live DB source back into the trunk repo (Component Integrity Rules).
|
|
1669
1378
|
|
|
1670
1379
|
### Inspecting branches and drift
|
|
1671
1380
|
|
|
@@ -1674,35 +1383,36 @@ remits-cli components branches # branches with
|
|
|
1674
1383
|
remits-cli components branch feature_branch # overridden / added / removed + subscribers
|
|
1675
1384
|
remits-cli components branch feature_branch --diff 50 --component-type action
|
|
1676
1385
|
remits-cli components branch feature_branch --subscribers
|
|
1386
|
+
remits-cli components branch feature_branch --json # the stored overlay content
|
|
1677
1387
|
```
|
|
1678
1388
|
|
|
1679
1389
|
**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
|
|
1681
|
-
now based on a stale version
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
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.
|
|
1390
|
+
(`originHash`). When the origin component later changes, the variant is reported **DRIFTED** — the branch
|
|
1391
|
+
is now based on a stale version and someone should reconcile it. Before editing an origin component, check
|
|
1392
|
+
whether variants of it exist: the owner account's `account-info.json` carries a `componentBranches`
|
|
1393
|
+
summary, and `components branches` gives the live view. A change to the origin silently drifts every
|
|
1394
|
+
branch that overlays it.
|
|
1688
1395
|
|
|
1689
1396
|
**`components branches` only lists branches that already have overlays.** A branch you just pushed is
|
|
1690
|
-
invisible here until its first sync
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
**
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1397
|
+
invisible here until its first sync — that is not an error. Preview it by name (`components sync --dry-run`
|
|
1398
|
+
from that checkout).
|
|
1399
|
+
|
|
1400
|
+
**Trunk moving also invalidates a branch.** Variant sparseness compares branch content against *current*
|
|
1401
|
+
trunk, so a trunk change can make an overlay obsolete without the branch changing at all. A trunk sync
|
|
1402
|
+
drops the affected branches' cached sync verdicts, so the next `components sync` on the branch really
|
|
1403
|
+
re-evaluates instead of answering *"No changes detected"*. After promoting anything, re-sync each live
|
|
1404
|
+
branch.
|
|
1405
|
+
|
|
1406
|
+
**The admin UI has the same surface**, which is what to point a non-CLI user at: the *owner* account page's
|
|
1407
|
+
**Component branches** box (per-branch counts, drift, subscribers, and Preview / Sync / Retire), and the
|
|
1408
|
+
*subscriber* account page's **Branch variants** box plus **GitHub → Fetch `<branch>` variants**. Both open
|
|
1409
|
+
the same result panel — grouped plan, subscribers, a Monaco diff of trunk vs branch per changed field, and
|
|
1410
|
+
a confirm-gated override when the removal guard refuses. Preview there is the same `--dry-run`.
|
|
1701
1411
|
|
|
1702
1412
|
### Diagnosing a variant
|
|
1703
1413
|
|
|
1704
1414
|
- `remits-cli components branch <name> --json` — the stored overlay content and what it overrides.
|
|
1705
|
-
- The compile signature
|
|
1415
|
+
- The compile signature `variant:<id>:<hash>` in `Using Cached BCD` logs — proves a variant actually ran.
|
|
1706
1416
|
- A test/tool response reports `testComponentSource` as `staged` | `variant` | `db`, so you can see which
|
|
1707
1417
|
layer the run resolved without reading logs.
|
|
1708
1418
|
|
|
@@ -1743,48 +1453,28 @@ Before starting an investigation outside the confirmed current repo:
|
|
|
1743
1453
|
5. Identify `source_bcd` and any `upstream_source_bcd`.
|
|
1744
1454
|
6. Explain the record in terms of the workflow step that produced it, not as a generic JSON blob.
|
|
1745
1455
|
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 `
|
|
1456
|
+
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
1457
|
|
|
1748
1458
|
**Stuck / failed / recovered Event:**
|
|
1749
1459
|
|
|
1750
|
-
Do **not** open the Action source first. The platform records each attempt's delivery envelope
|
|
1751
|
-
queue delivered it, which delivery attempt this was, and how long it was ever allowed to run
|
|
1460
|
+
Do **not** open the Action source first. The platform records each attempt's delivery envelope — which
|
|
1461
|
+
queue delivered it, which delivery attempt this was, and how long it was ever allowed to run — and
|
|
1462
|
+
classifies the failure for you.
|
|
1752
1463
|
|
|
1753
1464
|
```bash
|
|
1754
1465
|
remits-cli tool --name mcp_event_diagnostics --input '{"accountId":49,"eventId":18838}' --data-mode prod
|
|
1755
1466
|
```
|
|
1756
1467
|
|
|
1757
|
-
|
|
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.
|
|
1468
|
+
The same classifier is available from a Test or any component as `eventDiagnostics(18838)`.
|
|
1776
1469
|
|
|
1470
|
+
**Read `classification` before anything else** — only `APPLICATION_FAILURE` means the bug is in the
|
|
1471
|
+
component. The full classification table, what each `abandonmentCause` implies, and the returned `pivots`
|
|
1472
|
+
are under **`mcp_event_diagnostics`** in the Tool Reference. One thing to check every time:
|
|
1777
1473
|
`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 —
|
|
1779
|
-
|
|
1780
|
-
|
|
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.
|
|
1474
|
+
non-idempotent side effect may have run more than once — look for duplicate records before concluding the
|
|
1475
|
+
component "ran twice for no reason".
|
|
1786
1476
|
|
|
1787
|
-
Full detail: `
|
|
1477
|
+
Full detail: `features/observability.md` and `features/events-builder-guide.md` (`mcp_get_guide`).
|
|
1788
1478
|
|
|
1789
1479
|
### Presenting Findings
|
|
1790
1480
|
|
|
@@ -1921,9 +1611,9 @@ without a browser):
|
|
|
1921
1611
|
- `action: 'account_structure'` — change those properties on an existing account.
|
|
1922
1612
|
- `action: 'edge_add'` / `'edge_update'` / `'edge_remove'` — manage a membership `AccountRelationship` edge to
|
|
1923
1613
|
`parentAccountId`, including the three independent edge properties `branchName` (which component code runs),
|
|
1924
|
-
`databaseName` (
|
|
1925
|
-
`domainName` (which host reaches the account through that edge). Cycles, self-edges, and
|
|
1926
|
-
edge are all refused.
|
|
1614
|
+
`databaseName` (a path-scoped storage-namespace override — **live**, and inherited by everything below
|
|
1615
|
+
that edge) and `domainName` (which host reaches the account through that edge). Cycles, self-edges, and
|
|
1616
|
+
removing the primary edge are all refused.
|
|
1927
1617
|
- `action: 'reparent'` — move the account's **primary** edge (and `Account.parentId`) to `parentAccountId`.
|
|
1928
1618
|
|
|
1929
1619
|
> **The namespace guard.** An account's storage namespace resolves as
|
|
@@ -1932,16 +1622,15 @@ without a browser):
|
|
|
1932
1622
|
> move the resolved namespace is **refused** unless you pass `confirmSegmentChange: true`, and the refusal
|
|
1933
1623
|
> names both namespaces. To rename without moving storage, pin `code` to the old value in the same call.
|
|
1934
1624
|
>
|
|
1935
|
-
>
|
|
1936
|
-
>
|
|
1937
|
-
>
|
|
1938
|
-
>
|
|
1939
|
-
> same account reached through a different parent can resolve a different namespace).
|
|
1625
|
+
> Because the namespace follows the **path** (see *Account Structure* in `platform-overview.md`), an
|
|
1626
|
+
> `edge_update` that sets `databaseName` repoints storage for **everything below that edge**, and the
|
|
1627
|
+
> answer is path-specific — the same account reached through a different parent can resolve a different
|
|
1628
|
+
> namespace. Dry-run these.
|
|
1940
1629
|
|
|
1941
1630
|
Every write supports `dryRun: true`, which reports each `from -> to` without writing. Not here by design:
|
|
1942
|
-
component-branch subscription reporting and drift (`
|
|
1943
|
-
|
|
1944
|
-
applies).
|
|
1631
|
+
component-branch subscription reporting and drift (use `remits-cli components branches` /
|
|
1632
|
+
`remits-cli components branch <name>`), and account/user **deletion** (admin only, so the
|
|
1633
|
+
destructive-teardown contract applies).
|
|
1945
1634
|
|
|
1946
1635
|
**Provisioning recipe** — a new product account under a platform, running its own component branch, with
|
|
1947
1636
|
client accounts beneath it:
|
|
@@ -1952,7 +1641,7 @@ client accounts beneath it:
|
|
|
1952
1641
|
2. git branch + push in the OWNER's repo, then `remits-cli components sync` from that checkout
|
|
1953
1642
|
(non-trunk => writes ComponentVariant overlays only)
|
|
1954
1643
|
3. mcp_account_user_admin action:'edge_update' targetAccountId:<new> parentAccountId:<platform>
|
|
1955
|
-
branchName:'<branch>'
|
|
1644
|
+
branchName:'<branch>'
|
|
1956
1645
|
4. mcp_account_user_admin action:'account_create' parentAccountId:<new> name:'<business unit>'
|
|
1957
1646
|
type:'CLIENT' # inherits the branch automatically
|
|
1958
1647
|
```
|
|
@@ -1999,28 +1688,14 @@ Query Firestore documents.
|
|
|
1999
1688
|
| `textSearch` | no | `[{field, prefix}]` for prefix matching |
|
|
2000
1689
|
| `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
1690
|
|
|
2002
|
-
HTTP
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
- `http-audits/http-audits-YYYY-MM/entries`
|
|
2006
|
-
- Example investigation query:
|
|
1691
|
+
**HTTP audits use this same tool** — they are Firestore documents in monthly collections
|
|
1692
|
+
(`http-audits/http-audits-YYYY-MM/entries`). Which filters to reach for, and when audits are the right
|
|
1693
|
+
evidence at all, is in *The Investigation Model → HTTP audits*.
|
|
2007
1694
|
|
|
2008
1695
|
```bash
|
|
2009
1696
|
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
1697
|
```
|
|
2011
1698
|
|
|
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
1699
|
### `mcp_firestore_patch`
|
|
2025
1700
|
Guarded exact-document Firestore patch tool for bounded repairs. It defaults to `dryRun:true` and refuses broad
|
|
2026
1701
|
updates, wildcard collections, delete/remove operations, protected identity fields, and `_lastModified*` audit
|
|
@@ -2106,8 +1781,8 @@ Typical use:
|
|
|
2106
1781
|
- identify tuning opportunities in Agent behavior by comparing session output with the front-stage guides and component source
|
|
2107
1782
|
|
|
2108
1783
|
Front-stage references:
|
|
2109
|
-
- `
|
|
2110
|
-
- `
|
|
1784
|
+
- `components/agent-components.md`
|
|
1785
|
+
- `features/ai-support.md`
|
|
2111
1786
|
|
|
2112
1787
|
| Parameter | Required | Description |
|
|
2113
1788
|
|-----------|----------|-------------|
|
|
@@ -2351,7 +2026,7 @@ unaccounted wall time is itself a finding: check cold compile, queueing, blockin
|
|
|
2351
2026
|
|
|
2352
2027
|
### `mcp_event_diagnostics`
|
|
2353
2028
|
Diagnose one Event's infrastructure outcome through the same `eventDiagnostics(eventId)` DSL described in
|
|
2354
|
-
`
|
|
2029
|
+
`features/observability.md`. Use this before opening Action source when an Event is stuck,
|
|
2355
2030
|
recovered, timed out, retried, or appears to have been killed.
|
|
2356
2031
|
|
|
2357
2032
|
```bash
|
|
@@ -2369,26 +2044,34 @@ Read `classification` first:
|
|
|
2369
2044
|
- `APPLICATION_FAILURE` — the Action failed in application code; read `event.errorMessage`, correlated
|
|
2370
2045
|
alerts, and the producing component.
|
|
2371
2046
|
- `ORPHANED_*` / `RECOVERED_*` — the attempt was abandoned; read `abandonmentCause`.
|
|
2372
|
-
- `REQUEST_TIMEOUT_LIKELY`
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
- `
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
`
|
|
2382
|
-
|
|
2047
|
+
- `REQUEST_TIMEOUT_LIKELY` — it used its whole deadline (`timing.deadlineUsed` near `1.0`). The unit of
|
|
2048
|
+
work is too big for one event; the fix is resumable batches, not component logic.
|
|
2049
|
+
- `PROCESS_TERMINATED_LIKELY` — it stopped well inside its deadline (`timing.deadlineUsed` near `0`), so
|
|
2050
|
+
the worker was killed (memory pressure, restart). Not a logic bug; inspect JVM/node health and
|
|
2051
|
+
container lifecycle logs.
|
|
2052
|
+
- `UNKNOWN_NO_DEADLINE_EVIDENCE` — no deadline was recorded, so the cause is genuinely unknown. Use the
|
|
2053
|
+
returned `logQuery` filters; do not assume. Events predating delivery-envelope capture always look
|
|
2054
|
+
like this.
|
|
2055
|
+
- `AWAITING_DELIVERY` — the Event was never claimed; check queue delivery and action-node health.
|
|
2056
|
+
- `IN_FLIGHT_HEALTHY` — the Event is still heartbeating. A long Action is not a stuck one; wait, and
|
|
2057
|
+
inspect trace/logs before interrupting.
|
|
2058
|
+
|
|
2059
|
+
`delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
|
|
2060
|
+
non-idempotent side effect may have run more than once.
|
|
2061
|
+
|
|
2062
|
+
The response returns `delivery.threadGroupingId` — the same id everything else uses — plus the full
|
|
2063
|
+
`result` map, `diagnosisHints`, and `pivots` carrying ready-to-run inputs for `mcp_performance_trace`,
|
|
2064
|
+
`mcp_system_logs`, `mcp_record_listing`, and `mcp_object_activity` when those handles are present.
|
|
2065
|
+
`logQuery` carries ready-made Cloud Logging filters, including the container-lifecycle and 504 queries.
|
|
2066
|
+
For a performance question, open the `mcp_performance_trace` pivot next; for raw failure context, open
|
|
2067
|
+
logs and records by `threadGroupingId`.
|
|
2383
2068
|
|
|
2384
2069
|
### `mcp_component_view`
|
|
2385
2070
|
Read component field content with line numbers.
|
|
2386
2071
|
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
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.
|
|
2072
|
+
In a normal `remits-cli` coding workflow, prefer local repo files for source reads. Use this tool when the
|
|
2073
|
+
local repo is unavailable, when confirming live DB source, or when you need staging / variant metadata that
|
|
2074
|
+
is not present in the working tree.
|
|
2392
2075
|
|
|
2393
2076
|
| Parameter | Required | Description |
|
|
2394
2077
|
|-----------|----------|-------------|
|
|
@@ -2401,7 +2084,7 @@ are absent from `remits-cli tools`, that is expected.
|
|
|
2401
2084
|
|
|
2402
2085
|
Returns `componentVariants` when the component has committed branch variants — the branches, their state
|
|
2403
2086
|
(`current` / `drifted` / `removed`), and a warning. Non-null means some accounts run a different version
|
|
2404
|
-
than the source you are reading, and
|
|
2087
|
+
than the source you are reading, and trunk promotions can drift those variants.
|
|
2405
2088
|
`mcp_component_grep` searches trunk, so it will not match text that exists only in a variant.
|
|
2406
2089
|
|
|
2407
2090
|
### `mcp_component_grep`
|
|
@@ -2477,103 +2160,21 @@ it twice.
|
|
|
2477
2160
|
- `complete` after verification
|
|
2478
2161
|
- `release` if you are handing it off or cannot continue
|
|
2479
2162
|
|
|
2480
|
-
###
|
|
2481
|
-
|
|
2482
|
-
|
|
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:
|
|
2163
|
+
### Component branches
|
|
2164
|
+
Use `remits-cli components branches` / `remits-cli components branch <name>` to inspect committed branch
|
|
2165
|
+
variants, subscribers, and drift from a local checkout.
|
|
2508
2166
|
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
-
|
|
2512
|
-
-
|
|
2513
|
-
|
|
2514
|
-
-
|
|
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.
|
|
2167
|
+
Common uses:
|
|
2168
|
+
- `remits-cli components branches` — list branches this owner has variants on.
|
|
2169
|
+
- `remits-cli components branch <name>` — show overridden / added / removed components on that branch.
|
|
2170
|
+
- `remits-cli components branch <name> --diff <id> --component-type <kind>` — compare one variant against
|
|
2171
|
+
current trunk.
|
|
2172
|
+
- `remits-cli components branch <name> --subscribers` — list accounts resolving that branch.
|
|
2572
2173
|
|
|
2573
2174
|
### `mcp_cache`
|
|
2574
2175
|
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 `
|
|
2576
|
-
to remove staged entries).
|
|
2176
|
+
entry holds (and its TTL) or any other cache key. Read-only: no delete (use `remits-cli components clear`
|
|
2177
|
+
to remove staged component entries).
|
|
2577
2178
|
|
|
2578
2179
|
| Parameter | Required | Description |
|
|
2579
2180
|
|-----------|----------|-------------|
|
|
@@ -2649,6 +2250,11 @@ before writing a component.
|
|
|
2649
2250
|
| `directory` | no | Scope a list to `components` or `features` |
|
|
2650
2251
|
| `contains` | no | Substring filter when listing |
|
|
2651
2252
|
|
|
2253
|
+
Every guide is delivered with a table of contents whose entries carry **real line numbers**
|
|
2254
|
+
(`- L412 Querying Alerts`), resolved at delivery so they are never stale. Several of these guides are
|
|
2255
|
+
over a thousand lines: read the head, pick the sections you need, and offset-read those rather than
|
|
2256
|
+
loading the whole file. The entry text is the heading verbatim, so it also greps.
|
|
2257
|
+
|
|
2652
2258
|
### `mcp_test_fixture`
|
|
2653
2259
|
Seed and remove schema-backed fixture documents in the **forced test** data segment. This is how you construct
|
|
2654
2260
|
realistic conditions for verification without copying live customer data.
|
|
@@ -2662,18 +2268,6 @@ realistic conditions for verification without copying live customer data.
|
|
|
2662
2268
|
| `data` / `documents` | conditional | Single payload, or a batch array |
|
|
2663
2269
|
| `dataMode` | no | Must be `test` — **prod-mode writes are rejected** |
|
|
2664
2270
|
|
|
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
2271
|
### `mcp_playwright_replay`
|
|
2678
2272
|
Hosted browser automation (the `playwright-relay` service) for visual verification when local `playwright-cli`
|
|
2679
2273
|
is unavailable — e.g. an agent running remotely. Returns an accessibility snapshot and interactive refs on
|
|
@@ -2912,9 +2506,9 @@ For tests specifically:
|
|
|
2912
2506
|
plan without writing rows, caching the sync SHA, or clearing staging.
|
|
2913
2507
|
- `--summary` on `components sync --dry-run` prints compact counts, removals/tombstones, skipped items, errors,
|
|
2914
2508
|
and warnings instead of the full override/add/remove payload.
|
|
2915
|
-
- **Fail-closed sync gates.**
|
|
2916
|
-
|
|
2917
|
-
|
|
2509
|
+
- **Fail-closed sync gates.** On non-trunk variant branches these flags now force a server dry-run first,
|
|
2510
|
+
evaluate that plan before any overlay row is written, and only then run the mutating sync when the plan
|
|
2511
|
+
passes. On trunk, there is no safe dry-run plan, so do not treat these as scoped commit controls:
|
|
2918
2512
|
- `--changed-only` — fail unless every planned write is a component **this checkout actually edited**.
|
|
2919
2513
|
This is the strongest guard against a sync that quietly rewrites components you never touched. It
|
|
2920
2514
|
also fails when the checkout is not a git working tree, because "git could not answer" must never
|
|
@@ -2924,10 +2518,11 @@ For tests specifically:
|
|
|
2924
2518
|
e.g. `--expected-removed action:5,reader:9`). It **implies** `--fail-on-removed`, so any removal you
|
|
2925
2519
|
did not name fails the sync.
|
|
2926
2520
|
- `--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
|
|
2521
|
+
- `--names-only` — dry-run and print only `BUCKET type:id name` lines for the planned writes, then stop
|
|
2522
|
+
without writing overlays.
|
|
2928
2523
|
|
|
2929
2524
|
A good default for an unattended promotion is:
|
|
2930
|
-
`remits-cli components sync --
|
|
2525
|
+
`remits-cli components sync --summary --changed-only --fail-on-errors`
|
|
2931
2526
|
|
|
2932
2527
|
### Prod banners and retryable failures
|
|
2933
2528
|
|
|
@@ -2952,7 +2547,7 @@ For tests specifically:
|
|
|
2952
2547
|
| 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
2548
|
| "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
2549
|
| 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
|
|
2550
|
+
| 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
2551
|
| 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
2552
|
| Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
|
|
2958
2553
|
| Tool response missing | Check `./.remits-cli/tool-responses/` |
|
|
@@ -2960,7 +2555,8 @@ For tests specifically:
|
|
|
2960
2555
|
| 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
2556
|
| 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
2557
|
| 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
|
|
2558
|
+
| 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. |
|
|
2559
|
+
| Need account configuration values | In a local repo, read `account-configurations.json`. For live data, use `mcp_account_user_admin` (`action:'account'`) or `mcp_account_view`. `account-info.json` deliberately omits configurations. |
|
|
2964
2560
|
| 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
2561
|
| 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
2562
|
| 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 +2571,7 @@ For tests specifically:
|
|
|
2975
2571
|
| `--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
2572
|
| 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
2573
|
| 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
|
|
2574
|
+
| 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
2575
|
| 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
2576
|
| Service already running | Run `remits-cli status` to get the dashboard URL, or `remits-cli stop` before restarting. |
|
|
2981
2577
|
| tmux not installed | Install tmux (`brew install tmux` on macOS). Required for agent dispatch. |
|
|
@@ -2985,23 +2581,29 @@ For tests specifically:
|
|
|
2985
2581
|
|
|
2986
2582
|
### When Something Doesn't Work as Expected
|
|
2987
2583
|
|
|
2988
|
-
All `remits-cli` capabilities
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
2584
|
+
All `remits-cli` capabilities — staging, test execution, committing, tool calls — are known to work. The
|
|
2585
|
+
most common cause is an operational mistake, above all forgetting to stage before running a test.
|
|
2586
|
+
|
|
2587
|
+
If you have followed the loop (**edit → stage → run**) and something still misbehaves after 2-3 attempts,
|
|
2588
|
+
**stop trying workarounds** and decide which kind of problem it is:
|
|
2589
|
+
|
|
2590
|
+
- **A `remits-cli` / tooling operational issue** — staging, sync, dispatch, the listener, or the CLI itself
|
|
2591
|
+
misbehaving → use the **Escalation Bundle** below and ask the user to escalate to a Remits system admin.
|
|
2592
|
+
- **A Remits back-stage platform defect or limitation** — the component *runtime* behaves wrong:
|
|
2593
|
+
Hibernate/session/optimistic-locking errors, detached-entity surprises, brittle lifecycle or tool
|
|
2594
|
+
behavior, a DSL method diverging from its guide → follow the **Back-Stage Escalation Workflow** in
|
|
2595
|
+
`features/front-stage-debugging-strategy.md` (`mcp_get_guide`), which owns it end to end: analyze the
|
|
2596
|
+
platform seam locally, open a PR on a feature branch (never merge it yourself), raise a
|
|
2597
|
+
`mcp_support_ticket`, and tell the user. **Do not normalize it into a front-stage workaround.**
|
|
2598
|
+
|
|
2599
|
+
The local clone of the core platform repo that workflow needs is tracked in
|
|
2600
|
+
`~/.remits-cli/account-repos.json` under the reserved **`platform`** entry (default `~/remits`, override
|
|
2601
|
+
with `REMITS_PLATFORM_DIR`); `remits-cli` clones it on first authenticated run if it is missing.
|
|
2602
|
+
|
|
2603
|
+
Apply the boy-scout rule to the guides themselves: **whenever you only solved the problem by reading the
|
|
2604
|
+
core back stage because a front-stage guide was unclear or missing — even when there was no platform
|
|
2605
|
+
defect at all — open a guide-only PR** updating the relevant guide in the platform repo's `docs/guides/`,
|
|
2606
|
+
which is the source served to every account repo. Better guides over time are an explicit goal.
|
|
3005
2607
|
|
|
3006
2608
|
#### Escalation Bundle (tooling/operational issue)
|
|
3007
2609
|
|