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