@remits/remits-cli 0.1.137 → 0.1.139

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 CHANGED
@@ -33,6 +33,7 @@ git add -A
33
33
  git commit -m "sync passing changes"
34
34
  git push
35
35
  remits-cli components sync --safe # variant branch: gated, dry-runs first, refuses surprises
36
+ remits-cli components sync doctor # read-only sync/promotion/staging diagnosis
36
37
  remits-cli components sync
37
38
  remits-cli components sync --branch feature_branch --dry-run
38
39
  remits-cli components sync --branch feature_branch --dry-run --summary
@@ -84,7 +85,7 @@ remits-cli install --skills --overwrite true
84
85
  - `components sync` performs a server-side sync from the git remote into the Remits platform for the selected branch. It does not run local git commands. After a successful non-dry-run sync, the platform clears the branch/user staging scope so staged aliases cannot keep shadowing the newly synced DB rows.
85
86
  - On trunk, `components sync` performs the full repo-to-DB reconcile. On a non-trunk branch, it writes `ComponentVariant` overlays only and may refresh branch-local `account-info.json`, `account-hierarchy.json`, and `account-configurations.json` for the subscribing account that initiated the sync. `--dry-run` is accepted only for non-trunk variant syncs and reports overrides/additions/tombstones without writing variants, caching the sync SHA, updating metadata, or clearing staging. Add `--summary` to dry-run output when you only need counts, removals/tombstones, errors, skipped items, and warnings. `--force-tombstones` is accepted only for non-trunk variant syncs and should be used only when missing trunk component files are intentional tombstone overrides.
86
87
  - `components commit` is a convenience wrapper that performs local git commit/push, then `components sync`, then local `git fetch`/`git pull --ff-only`.
87
- - `components sync` returns the post-sync branch SHA produced by the platform. `components commit` verifies that `origin/<branch>` and local `HEAD` both match that exact SHA after the final pull.
88
+ - `components sync` returns the post-sync branch SHA produced by the platform. `components sync doctor` is read-only and assembles local HEAD, origin HEAD, platform last-sync SHA, staging shadow risk, promotion freshness, repository mismatch, and the next safe action when sync state is confusing. `components commit` verifies that `origin/<branch>` and local `HEAD` both match the exact synced SHA after the final pull.
88
89
  - `components push` is deprecated and currently behaves the same as `components stage`.
89
90
  - `accountId` resolution for CLI commands: explicit `--account-id` flag wins, then the current repo's `account-info.json` (`resolution.accountId`, then the short-lived `repoContext.accountInfoAccountId`, then the legacy top-level id — legacy files are rooted at the hierarchy ROOT, so their top-level `id` may be an ancestor), then the active session.
90
91
  - For `remits-cli tool`, read/discovery tools treat that resolved account as the **scope root**. You do not need the owning child account id for an exact document/record lookup: pass `--scope children`, or put `scope:"children"` in `--input`, and use the returned `resolvedTargetAccountId` for follow-up writes/runs. Use `--target-account-id` when you already know the exact owner, `--account-ids` for an explicit bounded owner list, and `--anchor-account-id` only to disambiguate multi-parent paths. Mutating tools still require an exact target.
@@ -100,8 +101,8 @@ remits-cli install --skills --overwrite true
100
101
  - Branch defaults to the current local git branch.
101
102
  - Data mode defaults to `test`. For `remits-cli test run`, that default is now enforced even if the most-recent authenticated session for the account is `prod`; a production test run therefore requires an explicit `--data-mode prod` on the command line. Use `remits-cli data-mode set prod` only for production investigation.
102
103
  - If the same account is authenticated against more than one host and you omit `--base-url`, the CLI auto-resolves the best matching session and now prints the resolved host. Pass `--base-url` explicitly whenever the target host matters.
103
- - `--names` is `|`-delimited and may be repeated. A comma still splits a single value for compatibility,
104
- so a case name containing a comma should be passed with `|` or by repeating `--names`.
104
+ - `--names` is `|`-delimited and may be repeated. A comma is part of the selector, so comma-bearing case
105
+ names can be passed literally.
105
106
  - Nested help is available before required-argument validation, including `remits-cli test run --help`, `remits-cli components sync --help`, and `remits-cli tool --help`.
106
107
  - `components sync --safe` is the recommended agent path on a variant branch. It expands to `--summary --changed-only --fail-on-errors --fail-on-removed`, resolves `--changed-since` from the branch's merge base with trunk when you did not name one (local refs only; it never runs an implicit `git fetch`), and prints the planned writes before mutating unless `--yes` is passed. On trunk there is no plan to gate, so it states what a trunk reconcile does and requires `--yes`.
107
108
  - `components sync` has fail-closed safety gates for unattended/agent use. Each exits non-zero instead of printing a wall of JSON: `--changed-only` (fail unless every planned write is a component this checkout edited), `--names-only` (print only `BUCKET type:id name` lines), `--fail-on-removed`, `--fail-on-errors`, and `--expected-removed <type:id>` (repeatable or comma-delimited; implies `--fail-on-removed`, so any removal you did not name fails). `--changed-only` also fails closed when the checkout is not a git working tree, because "git could not answer" must never be read as "nothing changed".
package/index.js CHANGED
@@ -11,11 +11,11 @@
11
11
  - L4107 Skill Delivery And TOC Resolution
12
12
  - L4417 Auth And Component Staging
13
13
  - L5032 Component Summaries, Status, And Sync Gates
14
- - L7104 Branches, Promotion, Commit, And Test Runs
15
- - L9193 Tokens, Tools, Verification, And Config
16
- - L10620 Service Dashboard And WebSocket Listener
17
- - L12742 Agent And Ticket Workflows
18
- - L16337 Help, Auto Update, And Command Dispatch
14
+ - L7332 Branches, Promotion, Commit, And Test Runs
15
+ - L9421 Tokens, Tools, Verification, And Config
16
+ - L10848 Service Dashboard And WebSocket Listener
17
+ - L12971 Agent And Ticket Workflows
18
+ - L16566 Help, Auto Update, And Command Dispatch
19
19
  */
20
20
 
21
21
  /*
@@ -5994,6 +5994,234 @@ async function clearComponentsCommand(flags) {
5994
5994
  return response;
5995
5995
  }
5996
5996
 
5997
+ async function syncDoctorComponentsCommand(rawFlags) {
5998
+ const cwd = process.cwd();
5999
+ const flags = Object.assign({}, rawFlags);
6000
+ ensureLocalState(cwd);
6001
+ const sessionContext = resolveSessionContext(cwd, flags);
6002
+ requireAccountRepoContext(cwd, flags, 'components sync doctor', sessionContext);
6003
+ const { session, accountId } = sessionContext;
6004
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
6005
+ const branchName = flags.branch || currentBranch(cwd);
6006
+ const workspace = resolveWorkspace(cwd, flags);
6007
+ const dataMode = resolveDataMode(flags, session);
6008
+ const api = buildAxios(baseUrl, session.token);
6009
+
6010
+ const statusResponse = await loggedPost(api, cwd, '/cli/components', {
6011
+ token: session.token,
6012
+ accountId,
6013
+ branchName,
6014
+ workspace,
6015
+ dataMode,
6016
+ mode: 'status'
6017
+ }).then((r) => r.data);
6018
+
6019
+ if (!statusResponse.success) {
6020
+ throw new Error(statusResponse.message || 'Staging status failed');
6021
+ }
6022
+
6023
+ statusResponse.freshness = stagingFreshness({
6024
+ head: safeGitValue(cwd, 'git rev-parse HEAD'),
6025
+ originHead: safeGitValue(cwd, 'git rev-parse ' + shellQuote('origin/' + branchName)),
6026
+ lastSyncedSha: statusResponse.branchContext && statusResponse.branchContext.lastSyncedSha,
6027
+ entries: statusResponse.entries,
6028
+ laneSummary: statusResponse.laneSummary,
6029
+ isAncestor: (a, b) => gitIsAncestor(cwd, a, b)
6030
+ });
6031
+ statusResponse.repositoryCheck = repositoryCheck(cwd, statusResponse.branchContext);
6032
+
6033
+ let promotionResponse = null;
6034
+ try {
6035
+ promotionResponse = await loggedPost(api, cwd, '/cli/branches', {
6036
+ token: session.token,
6037
+ accountId,
6038
+ mode: 'promotion',
6039
+ branchName
6040
+ }, { skipSessionLog: true }).then((r) => r.data);
6041
+ } catch (err) {
6042
+ const data = err && err.response && err.response.data;
6043
+ promotionResponse = {
6044
+ success: false,
6045
+ message: data && data.message ? data.message : describeError(err)
6046
+ };
6047
+ }
6048
+
6049
+ const report = buildSyncDoctorReport({
6050
+ accountId,
6051
+ branchName,
6052
+ workspace,
6053
+ dataMode: statusResponse.dataMode || dataMode,
6054
+ baseUrl: normalizeBaseUrl(baseUrl),
6055
+ local: {
6056
+ head: safeGitValue(cwd, 'git rev-parse HEAD'),
6057
+ originHead: safeGitValue(cwd, 'git rev-parse ' + shellQuote('origin/' + branchName)),
6058
+ upstream: safeGitValue(cwd, 'git rev-parse --abbrev-ref --symbolic-full-name @{u}'),
6059
+ aheadBehind: safeGitValue(cwd, 'git rev-list --left-right --count HEAD...origin/' + shellQuote(branchName))
6060
+ },
6061
+ status: statusResponse,
6062
+ promotion: promotionResponse
6063
+ });
6064
+
6065
+ if (flagEnabled(flags.json)) {
6066
+ console.log(JSON.stringify(report, null, 2));
6067
+ return report;
6068
+ }
6069
+
6070
+ printSessionResolutionWarning(sessionContext);
6071
+ printResolvedBaseUrl(baseUrl);
6072
+ printSyncDoctorReport(report);
6073
+ return report;
6074
+ }
6075
+
6076
+ function selectedPromotionEntry(promotion, branchName) {
6077
+ if (!promotion || promotion.success === false) return null;
6078
+ const entries = Array.isArray(promotion.branches) ? promotion.branches : [promotion];
6079
+ return entries.find((entry) => String(entry && entry.branch || '') === String(branchName)) || entries[0] || null;
6080
+ }
6081
+
6082
+ function syncDoctorStagedCount(status) {
6083
+ const laneSummary = status && status.laneSummary || {};
6084
+ const candidates = [laneSummary.stagedCount, laneSummary.count, laneSummary.total, status && status.stagedCount];
6085
+ for (const value of candidates) {
6086
+ const number = Number(value);
6087
+ if (Number.isFinite(number)) return number;
6088
+ }
6089
+ return Array.isArray(status && status.entries) ? status.entries.length : 0;
6090
+ }
6091
+
6092
+ function buildSyncDoctorReport(input) {
6093
+ const status = input.status || {};
6094
+ const promotion = input.promotion || null;
6095
+ const entry = selectedPromotionEntry(promotion, input.branchName);
6096
+ const platform = entry && entry.platform || {};
6097
+ const git = entry && entry.git || {};
6098
+ const branchContext = status.branchContext || {};
6099
+ const stagedCount = syncDoctorStagedCount(status);
6100
+ const report = {
6101
+ success: status.success !== false,
6102
+ accountId: input.accountId,
6103
+ branchName: input.branchName,
6104
+ workspace: input.workspace || null,
6105
+ dataMode: input.dataMode,
6106
+ host: input.baseUrl,
6107
+ local: input.local || {},
6108
+ world: status.variantWorld || null,
6109
+ branchContext,
6110
+ repositoryCheck: status.repositoryCheck || null,
6111
+ staging: {
6112
+ laneId: status.laneSummary && status.laneSummary.laneId || status.stagingLane || null,
6113
+ stagedCount,
6114
+ contentHash: status.laneSummary && status.laneSummary.contentHash || null,
6115
+ freshness: status.freshness || null
6116
+ },
6117
+ platform: {
6118
+ lastSyncedSha: branchContext.lastSyncedSha || null,
6119
+ syncRepository: branchContext.repository || null,
6120
+ mode: branchContextIsTrunk(branchContext) ? 'trunk' : 'variant'
6121
+ },
6122
+ promotion: entry ? {
6123
+ available: true,
6124
+ branch: entry.branch,
6125
+ phase: entry.phase || null,
6126
+ summary: entry.summary || null,
6127
+ remoteBranchHeadSha: git.branchHeadSha || null,
6128
+ remoteTrunkHeadSha: git.trunkHeadSha || null,
6129
+ overlaysComputedFrom: platform.syncedSha || null,
6130
+ overlayFreshness: platform.freshness || null,
6131
+ overridden: platform.overridden,
6132
+ branchOnly: platform.added,
6133
+ tombstoned: platform.removed,
6134
+ drifted: platform.drifted,
6135
+ blockers: entry.blockers || [],
6136
+ nextSteps: entry.nextSteps || []
6137
+ } : {
6138
+ available: false,
6139
+ message: promotion && promotion.message || 'promotion status unavailable'
6140
+ }
6141
+ };
6142
+ report.recommendations = syncDoctorRecommendations(report);
6143
+ return report;
6144
+ }
6145
+
6146
+ function syncDoctorRecommendations(report) {
6147
+ const recs = [];
6148
+ const repo = report.repositoryCheck;
6149
+ if (repo && repo.matches === false) {
6150
+ recs.push('Run sync from the checkout whose origin matches the platform repository: ' + repo.platformRepository + '.');
6151
+ }
6152
+ const freshness = report.staging && report.staging.freshness || {};
6153
+ if (freshness.landedSinceHead === true) {
6154
+ recs.push('Pull the platform-synced commit before trusting this lane: git fetch origin && git pull --ff-only, then re-stage.');
6155
+ }
6156
+ if (freshness.entriesBehindLastSync > 0) {
6157
+ recs.push('Re-stage from the current checkout with `remits-cli components stage --workset`; the lane shadows rows landed after it was staged.');
6158
+ }
6159
+ if ((report.staging && report.staging.stagedCount || 0) > 0) {
6160
+ recs.push('Committed-variant verification will be shadowed by this staging lane; clear it or use an unused --branch with --variant-branch before proving committed source.');
6161
+ }
6162
+ const promo = report.promotion || {};
6163
+ if (promo.available && Array.isArray(promo.blockers) && promo.blockers.length) {
6164
+ recs.push('Do not sync yet; `components promotion` reports blockers. Follow its next steps before mutating.');
6165
+ } else if (promo.available && promo.overlayFreshness && promo.overlayFreshness !== 'current') {
6166
+ recs.push('Stored overlays are stale relative to the branch/trunk pair. Re-read promotion, merge as instructed, then run one gated sync.');
6167
+ }
6168
+ if (report.platform && report.platform.mode === 'variant') {
6169
+ recs.push('Agent landing path: push the branch, run `remits-cli components sync --safe --yes` once, then `git pull --ff-only`.');
6170
+ } else {
6171
+ recs.push('Trunk sync has no dry-run plan. Use `remits-cli components sync --safe --yes` only when you intend the authoritative trunk reconcile.');
6172
+ }
6173
+ recs.push('If a sync times out client-side, treat the result as unknown: observe with `components sync doctor` or `components promotion`; do not put `components sync` in a polling loop.');
6174
+ return Array.from(new Set(recs));
6175
+ }
6176
+
6177
+ function printSyncDoctorReport(report) {
6178
+ console.log('Sync doctor');
6179
+ console.log('Account:', report.accountId);
6180
+ console.log('Data mode:', report.dataMode);
6181
+ console.log('Git branch:', report.branchName);
6182
+ console.log('Workspace:', report.workspace || 'shared default lane');
6183
+ console.log('Mode:', report.platform.mode);
6184
+ console.log('');
6185
+ console.log('Local git:');
6186
+ console.log(' HEAD: ' + short(report.local.head));
6187
+ console.log(' origin HEAD: ' + short(report.local.originHead));
6188
+ if (report.local.upstream) console.log(' upstream: ' + report.local.upstream);
6189
+ if (report.local.aheadBehind) console.log(' HEAD...origin/' + report.branchName + ': ' + report.local.aheadBehind.trim() + ' (left=local, right=remote)');
6190
+ console.log('');
6191
+ console.log('Platform sync:');
6192
+ console.log(' repository: ' + (report.platform.syncRepository || 'unknown'));
6193
+ console.log(' last synced SHA: ' + short(report.platform.lastSyncedSha));
6194
+ if (report.repositoryCheck && report.repositoryCheck.matches === false) {
6195
+ console.log(' REPOSITORY MISMATCH: checkout origin is ' + report.repositoryCheck.checkoutOrigin);
6196
+ }
6197
+ console.log('');
6198
+ console.log('Staging lane:');
6199
+ console.log(' staged entries: ' + report.staging.stagedCount);
6200
+ if (report.staging.contentHash) console.log(' content hash: ' + report.staging.contentHash);
6201
+ printStagingFreshness(report.staging.freshness);
6202
+ console.log('');
6203
+ console.log('Promotion / variant status:');
6204
+ if (report.promotion.available) {
6205
+ console.log(' phase: ' + (report.promotion.phase || 'unknown'));
6206
+ console.log(' remote branch HEAD: ' + short(report.promotion.remoteBranchHeadSha));
6207
+ console.log(' overlays computed from:' + ' ' + short(report.promotion.overlaysComputedFrom) +
6208
+ (report.promotion.overlayFreshness ? ' [' + report.promotion.overlayFreshness + ']' : ''));
6209
+ console.log(' overlays: overridden ' + (report.promotion.overridden ?? 'unknown') +
6210
+ ', branch-only ' + (report.promotion.branchOnly ?? 'unknown') +
6211
+ ', tombstoned ' + (report.promotion.tombstoned ?? 'unknown') +
6212
+ ', drifted ' + (report.promotion.drifted ?? 'unknown'));
6213
+ if (report.promotion.blockers && report.promotion.blockers.length) {
6214
+ console.log(' blockers: ' + report.promotion.blockers.length);
6215
+ report.promotion.blockers.slice(0, 8).forEach((b) => console.log(' - ' + b.code + ': ' + b.message));
6216
+ }
6217
+ } else {
6218
+ console.log(' unavailable: ' + report.promotion.message);
6219
+ }
6220
+ console.log('');
6221
+ console.log('Recommended next action:');
6222
+ report.recommendations.forEach((line) => console.log(' - ' + line));
6223
+ }
6224
+
5997
6225
  async function syncComponentsCommand(rawFlags) {
5998
6226
  const cwd = process.cwd();
5999
6227
  // `--safe` is a NAME for the gate combination that should be the default agent path, not a new gate.
@@ -11339,6 +11567,7 @@ function buildRepoSnapshot(entry) {
11339
11567
  ? path.join(localPaths.legacy.sessionsDir, legacySessionName + '.jsonl')
11340
11568
  : null;
11341
11569
  const repoFiles = [
11570
+ { label: 'account-boot.json', path: path.join(directory, 'account-boot.json'), mode: 'json' },
11342
11571
  { label: 'account-info.json', path: entry.accountInfoPath || path.join(directory, 'account-info.json'), mode: 'json' },
11343
11572
  { label: 'account-hierarchy.json', path: path.join(directory, 'account-hierarchy.json'), mode: 'json' },
11344
11573
  { label: 'account-analytics.json', path: path.join(directory, 'account-analytics.json'), mode: 'json' },
@@ -16536,11 +16765,14 @@ function printComponentsHelp(subcommand) {
16536
16765
  }
16537
16766
  if (subcommand === 'sync') {
16538
16767
  console.log('Usage: remits-cli components sync [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--create-variant-branch] [--summary] [--json]');
16768
+ console.log(' remits-cli components sync doctor [--json]');
16539
16769
  console.log('');
16540
16770
  console.log('On trunk this reconciles the pushed repository into live component rows.');
16541
16771
  console.log('On a variant branch this writes ComponentVariant overlays only.');
16542
16772
  console.log('--dry-run is only supported on variant branches and writes nothing.');
16543
16773
  console.log('--summary prints compact counts, removals/tombstones, errors, skipped items, and warnings.');
16774
+ console.log('`sync doctor` is read-only. It assembles local HEAD, origin HEAD, platform last sync,');
16775
+ console.log('staging shadow risk, promotion freshness, repository mismatch, and the next safe action.');
16544
16776
  console.log('');
16545
16777
  console.log('Agent safety gates (each exits non-zero instead of printing a wall of JSON):');
16546
16778
  console.log(' --safe THE RECOMMENDED PATH on a variant branch. Expands to');
@@ -17083,6 +17315,11 @@ async function main() {
17083
17315
  return;
17084
17316
  }
17085
17317
 
17318
+ if (command === 'components' && subcommand === 'sync' && String(args._ && args._[2] || '').toLowerCase() === 'doctor') {
17319
+ await syncDoctorComponentsCommand(args);
17320
+ return;
17321
+ }
17322
+
17086
17323
  if (command === 'components' && subcommand === 'sync') {
17087
17324
  await syncComponentsCommand(args);
17088
17325
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remits/remits-cli",
3
- "version": "0.1.137",
3
+ "version": "0.1.139",
4
4
  "description": "Local CLI for auth, component sync, and live test execution against Remits",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -92,6 +92,12 @@ reference named after it.
92
92
  "branch" or "verified"; say what was actually proven: Test pass, browser journey, corpus measurement,
93
93
  sync mutation, or ticket lifecycle state. (`component-resolution.md`, `branch-variants.md`,
94
94
  `development-loop.md`, `support-tickets.md`)
95
+ - **Embeddable verification is browser verification.** For page, widget, form, upload, or other
96
+ browser-facing changes, mint a URL with `remits-cli token` and drive it with `playwright-cli`.
97
+ Prefer this over `curl`: `curl` can prove an HTTP response or inspect an action payload, but it does
98
+ not run JavaScript, Alpine, CSS/assets, redirects, cookies/session handling, uploads, iframes,
99
+ shadow-DOM placement, or websocket behavior. (`development-loop.md`,
100
+ `docs/front-stage/components/embeddable-components.md`, `docs/front-stage/features/web-browser-automation.md`)
95
101
  - **Land only from trunk or a real variant branch.** A per-agent/feature branch (`components status` says
96
102
  `FEATURE BRANCH … [subscription-fallback]`) is safe to stage and run from — it resolves the same world
97
103
  as its target — but `components commit`/`sync` refuse it. Merge into the branch it resolves and land
@@ -107,6 +113,11 @@ reference named after it.
107
113
  updating, renaming, and **hard-deleting** rows. Prefer the observable
108
114
  `git commit → git push → components sync → git pull`. On trunk it refuses before any git write unless
109
115
  you pass `--yes`. (`component-integrity.md`)
116
+ - **Do not hand-roll sync watchers.** A sync is a write; an observation loop must never start a fresh
117
+ `components sync`. Use `remits-cli components sync doctor` and `components promotion` to read sync /
118
+ overlay state. If a sync times out client-side, the result is unknown, not failed; observe before any
119
+ retry. Do not use `setsid`, `nohup`, `disown`, `tmux`, or shell-specific detach tricks to make sync
120
+ lifecycle work. (`development-loop.md`, `branch-variants.md`)
110
121
  - **After anyone lands, ask whether your lane is stale: `remits-cli components status`.** A landing clears
111
122
  only the lander's lane; yours keeps shadowing their new rows with content staged from the old base.
112
123
  `LANDED SINCE YOUR BASE` / `STALE OVERLAY` mean pull, re-stage, then verify — a pass against a stale
@@ -140,6 +151,12 @@ reference named after it.
140
151
  (`component-resolution.md`, `command-reference.md`)
141
152
  - **Host and data mode are two independent decisions.** `--base-url` picks the Remits host,
142
153
  `--data-mode` picks the data segment on it. Neither implies the other. (`command-reference.md`)
154
+ - **Localhost being down is not a verification blocker.** `remits-cli` can talk to any reachable Remits
155
+ host, including the deployed production host, by passing `--base-url`. If `http://localhost:8080`
156
+ refuses a connection, retry the same stage/test/tool/status command against the intended deployed host
157
+ with the same account, branch, workspace and data-mode facts; do not stop unless no reachable host or
158
+ valid session exists. A deployed host can still run test-lane verification: `--base-url` selects the
159
+ server, while `test run` still defaults to `--data-mode test`. (`command-reference.md`)
143
160
  - **`test run` ignores the stored session data lane.** It defaults to `test` even when `whoami` shows
144
161
  the session parked on prod; production Test runs require explicit prod provenance (`--data-mode prod`)
145
162
  or Test source declaring `dataMode 'prod'` / `[dataMode:'prod']`. Check returned `dataModeSource`
@@ -163,6 +180,12 @@ reference named after it.
163
180
  component (preferred, because it becomes regression protection) or a browser flow through
164
181
  `remits-cli token`. "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
165
182
  cannot verify, say what you would need and ask. (`development-loop.md`)
183
+ - **Match the proof to the claim.** A mocked Test is a seam proof; a sync packet is durable source
184
+ movement; a token/browser run is journey proof; a corpus consistency report is repeatability proof. If
185
+ the request says "same file twice", "repeatability", "consistency", "same answer", or "model variance",
186
+ start with a corpus/evaluation or other repeated live measurement under an explicit AI budget; mocked
187
+ seam suites are guardrails, not acceptance. (`development-loop.md`, `component-resolution.md`,
188
+ `docs/front-stage/components/test-components.md`)
166
189
  - **`remits-cli evidence` answers "what have I actually run, and in which world?"** Every stage, test,
167
190
  token, tool and sync appends one world-stamped line automatically, per actor. Nothing to start,
168
191
  nothing to satisfy. Read it before you re-run something, and quote it when you report what you
@@ -216,7 +239,8 @@ remits-cli components status # trunk or variant checkout, staging lane, wor
216
239
  remits-cli tools # which tools this account actually has (tools are per-account)
217
240
  ```
218
241
 
219
- plus the repo's `account-info.json` → `resolution` block for the account's shape.
242
+ plus the repo's `account-boot.json` → `resolution` block for the account's shape (`account-info.json`
243
+ only when you need full inventory detail).
220
244
  For a workflow-shaped request, rely on the automatic `remits-cli evidence` trail unless someone else needs
221
245
  a checkable verdict. Only then start `remits-cli verify start --summary "..."`, declare claims, and keep
222
246
  that envelope active through stage/test/token/sync.
@@ -228,7 +252,8 @@ non-default host).
228
252
  `~/.remits-cli/account-repos.json` (every local repo, plus the reserved `platform` entry for the core
229
253
  platform clone), `~/.remits-cli/sessions.json` (auth state and lanes), `~/.remits-cli/agents.json`
230
254
  (agents registered here), `./.remits-cli/actors/<local-agent>/tool-responses/<callId>.json` (the full
231
- tool payload), and the repo's `account-info.json`. `cli-state.md` maps every remaining question to its
255
+ tool payload), the repo's `account-boot.json`, and `account-info.json` when compact context is not enough.
256
+ `cli-state.md` maps every remaining question to its
232
257
  file, including legacy flat `.remits-cli/` fallbacks.
233
258
  Repo-local session JSONL is intentionally a bounded audit log: request payloads and ordinary response
234
259
  bodies are summarized with keys, sizes, hashes, and redaction markers. Open actor-scoped tool response
@@ -242,7 +267,7 @@ Keep support and development sessions lean:
242
267
  `mcp_component_view` / `mcp_component_grep` are fallback surfaces for agents without that checkout,
243
268
  or for confirming what the live DB has stored after you already understand the files. For a ticket
244
269
  with `implementationAccountId`, resolve that account's indexed repo first, pull the appropriate
245
- branch when the checkout is clean, and inspect `account-info.json` + `components/` there.
270
+ branch when the checkout is clean, and inspect `account-boot.json` + relevant `components/` there.
246
271
  - Do not read entire `.remits-cli/actors/<local-agent>/sessions/*.jsonl` (or legacy flat session logs) or
247
272
  large tool response files unless you first narrow to the relevant request, endpoint, tool, or ticket.
248
273
  - Prefer targeted Firestore queries: use `documentId`, tight `filters`, narrow `fields`, and low `limit`
@@ -23,13 +23,14 @@
23
23
  against, and where a fix belongs are answered by the account's structure — never by its name.
24
24
 
25
25
  The account model itself — types, component inheritance, primary vs membership edges, the three independent
26
- edge properties, the user model — is in the always-loaded `platform-overview.md` and in depth in
26
+ edge properties, the user model — is summarized in `platform-core.md` and in depth in
27
27
  `features/account-management.md` (`mcp_get_guide`). What follows is only what changes **what you type**.
28
28
 
29
29
  ### Read the shape first
30
30
 
31
- `account-info.json` (in a repo) and `mcp_account_view` (remotely) both carry a `resolution` block — the one
32
- place these facts appear. Field-by-field detail is under **`mcp_account_view`** in `tool-reference.md`. The
31
+ `account-boot.json` (in a repo), `account-info.json` (full local inventory), and `mcp_account_view`
32
+ (remotely) all carry a `resolution` block — the one place these facts appear. Field-by-field detail is
33
+ under **`mcp_account_view`** in `tool-reference.md`. The
33
34
  four that decide a CLI action:
34
35
 
35
36
  | Read | To decide |
@@ -57,7 +58,8 @@ Two more, easily confused: top-level **`componentBranches`** lists the variant b
57
58
  current before reading source or editing. Run `git fetch origin`; when the tree is clean,
58
59
  `git pull --ff-only origin <branch>`; then confirm `git log origin/<branch>..<branch>` and
59
60
  `git log <branch>..origin/<branch>` are both empty. A worktree can have an isolated staging workspace
60
- and still be based on a stale commit. Then read `account-info.json` and inspect `components/` directly.
61
+ and still be based on a stale commit. Then read `account-boot.json` and inspect relevant `components/`
62
+ directly; use `account-info.json` only for full inventory detail.
61
63
  - **Inside one repo but supporting a different account**: switch to that account's indexed repo if it
62
64
  exists. For tickets, prefer `implementationAccountId` / `implementationAccountName` over the reporting
63
65
  `accountId` when choosing that repo; a subscriber or client often reports the symptom while the
@@ -84,7 +86,7 @@ still go to the test lane. `Object.testMode` /
84
86
  `Event.testMode` / `Alert.testMode` (and `testMode` inside test-lane Audit documents) identify lifecycle
85
87
  rows in the test data lane. Agent-facing surfaces expose these fields:
86
88
 
87
- - `account-info.json`, `account-hierarchy.json`, and `mcp_account_view`: `resolution.testAccount` for the
89
+ - `account-boot.json`, `account-info.json`, `account-hierarchy.json`, and `mcp_account_view`: `resolution.testAccount` for the
88
90
  described account, and `testAccount` on returned hierarchy nodes.
89
91
  - `mcp_account_user_admin`: `testAccount` on `hierarchy`, `account`, and `account_create` results;
90
92
  `testUser` on `users` / `user` results.
@@ -12,6 +12,7 @@
12
12
  - [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)
13
13
  - [Two levers, two different questions](#two-levers-two-different-questions)
14
14
  - [The SDLC is identical on a variant branch](#the-sdlc-is-identical-on-a-variant-branch)
15
+ - [Long-running variant sync](#long-running-variant-sync)
15
16
  - [Where am I in the promotion loop?](#where-am-i-in-the-promotion-loop)
16
17
  - [Subscribing, unsubscribing, retiring](#subscribing-unsubscribing-retiring)
17
18
  - [Danger profile on a variant branch (different, not absent)](#danger-profile-on-a-variant-branch-different-not-absent)
@@ -237,7 +238,7 @@ remits-cli test run --test "Invoice Tests" # the feature_bran
237
238
  remits-cli test run --test "Invoice Tests" --as-account 101 # ...as the real subscriber
238
239
  git add -A && git commit -m "..." && git push origin feature_branch
239
240
  remits-cli components sync --dry-run # inspect overrides/additions/tombstones without writes
240
- remits-cli components sync # writes ComponentVariant overlays ONLY
241
+ remits-cli components sync --safe --yes # agent/noninteractive: writes ComponentVariant overlays ONLY
241
242
  ```
242
243
 
243
244
  `components stage` validates changed runtime-compiled source through the active branch/workspace lane,
@@ -252,6 +253,41 @@ for you, and merging a branch promotes its **deletions** as hard deletes. Do not
252
253
  `remits-cli components sync --summary --timeout-ms 300000`; the platform may need longer than the default
253
254
  60 seconds to create rows, push rename/meta commits, and regenerate account metadata.
254
255
 
256
+ ### Long-running variant sync
257
+
258
+ A variant sync writes durable `ComponentVariant` overlays only. The proof that subscribers can resolve the
259
+ new committed branch source is the overlay set's computed SHA, not the fact that your local process once
260
+ started a sync.
261
+
262
+ Use this shape for agent landings:
263
+
264
+ ```bash
265
+ git push origin <branch>
266
+ remits-cli components sync --safe --yes
267
+ git pull --ff-only origin <branch>
268
+ remits-cli components sync doctor
269
+ remits-cli components promotion --branch <branch> --no-fail
270
+ ```
271
+
272
+ `--safe --yes` skips the interactive prompt after the safe dry-run gates pass; it does not skip the gates.
273
+ If the client times out, the result is **unknown**. Do not immediately re-run `components sync`, and never
274
+ write a polling loop that invokes it. Observe with `components sync doctor` and
275
+ `components promotion --branch <branch> --no-fail`; a read loop may inspect, but only one command should
276
+ start the write.
277
+
278
+ Read the promotion report precisely:
279
+
280
+ - `branch HEAD` in the git section says what the remote branch currently points at.
281
+ - `overlays computed from` in the platform section says what subscribers resolve right now.
282
+ - A cached status or a local process exit is not proof that overlays advanced.
283
+ - Immediately after a sync, metadata refresh and compile caches can lag by a moment; committed-variant
284
+ verification still has to clear or avoid staged overlays first.
285
+
286
+ Host and account provenance are part of this proof. A subscriber can be subscribed to a branch that
287
+ currently stores zero overlays, which means the selected branch resolves trunk behavior. Before comparing
288
+ uploads or browser runs, read the token/test/tool world: host, execution account, component branch,
289
+ `variantBranchSource`, data lane, and resolved database.
290
+
255
291
  ### Where am I in the promotion loop?
256
292
 
257
293
  ```bash
@@ -42,6 +42,10 @@ Treat host selection and data mode as two separate decisions:
42
42
 
43
43
  - `--base-url` chooses the Remits host: localhost vs a deployed environment.
44
44
  - `--data-mode` chooses the data segment on that host: `test` vs `prod`.
45
+ - A connection failure to `http://localhost:8080` only says the local back-stage app is not reachable.
46
+ It does **not** mean staging, testing, tokens, or investigation are blocked. If a deployed Remits host
47
+ is the right target, pass it explicitly with `--base-url` and keep the same account/branch/workspace
48
+ and `--data-mode` facts.
45
49
 
46
50
  Examples:
47
51
 
@@ -49,11 +53,20 @@ Examples:
49
53
  # Deployed prod host, but test data segment
50
54
  remits-cli tools --base-url https://your-prod-host --data-mode test
51
55
 
56
+ # Deployed prod host, test data, staged component verification
57
+ remits-cli components stage --workset --base-url https://your-prod-host
58
+ remits-cli test run --test "Statement Reader Calculations" --base-url https://your-prod-host
59
+
52
60
  # Localhost host, but prod data segment on that localhost instance
53
61
  remits-cli tool --base-url http://localhost:8080 --name mcp_account_view --data-mode prod
54
62
  ```
55
63
 
56
- Do not assume `--data-mode prod` implies the deployed prod host, or that `--data-mode test` implies localhost. If host matters, read `~/.remits-cli/sessions.json` first and pass `--base-url` explicitly.
64
+ Do not assume `--data-mode prod` implies the deployed prod host, or that `--data-mode test` implies
65
+ localhost. Likewise, do not assume localhost is required because the current checkout is a back-stage
66
+ repo or because a previous command used localhost. If host matters, read `~/.remits-cli/sessions.json`,
67
+ `remits-cli whoami`, or the recent `remits-cli evidence` world blocks, then pass `--base-url` explicitly.
68
+ Only call verification blocked after trying the intended reachable host and finding that no authenticated
69
+ session or usable network path exists.
57
70
 
58
71
  ### Tool Execution Lifecycle
59
72
 
@@ -233,6 +246,7 @@ remits-cli components lanes [--json] # every indexed sta
233
246
  remits-cli components entries --lane-id <id> [--json|--verbose] # authoritative staged files for one lane
234
247
  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
235
248
  remits-cli components sync [--safe [--yes]] [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--create-variant-branch] [--summary] [--changed-only [--changed-since <ref>]] # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
249
+ remits-cli components sync doctor [--branch <name>] [--json] # read-only: local/remote/platform sync facts, staging shadow risk, promotion freshness, recommended next action
236
250
  remits-cli components commit [--yes] [--safe] [--allow-shared-branch] [--create-variant-branch] [--message|-m "msg"] [--data-mode test|prod] [--force-tombstones] [--json [--summary]] # phase 1 merge-stages + compile-validates changed source (never reconciles the lane); on TRUNK refuses before any git write unless --yes; --safe gates variant sync writes
237
251
  remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
238
252
  remits-cli components branch <name> [--json] # one branch: owner account, overridden / added / removed, drift flags, subscribers
@@ -566,6 +580,10 @@ For tests specifically:
566
580
  - `--summary` on `components sync` prints compact counts plus named updated (with the fields that
567
581
  changed), renamed, skipped, removal/tombstone, error, and gate details instead of the full payload. A
568
582
  trunk sync reports an untouched component under `unchanged`, not `updated`.
583
+ - `components sync doctor` does not mutate. Use it when a sync timed out, status/promotion disagree, or
584
+ you need to know whether staged entries would shadow committed-variant verification. It prints local
585
+ `HEAD`, `origin/<branch>`, platform last-sync SHA, overlay freshness, repository mismatch, and a
586
+ recommended next action.
569
587
  - `--json` on `components stage`, `components sync`, `components commit`, and `tool` prints one
570
588
  parseable JSON document to stdout. Safety banners and prose go to stderr, and sync safety-gate failures
571
589
  are reported inside `gateViolations` with a non-zero exit code instead of appending prose after the JSON.
@@ -90,6 +90,7 @@ every live component present at its real id, and nothing extra.
90
90
  | `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. A plain stage or `--workset` RECONCILES the lane (entries outside the manifest are dropped); `--changed-only` merges. | none for the DB; a lane you share with another agent is reconciled by the first two |
91
91
  | `remits-cli components status` | Reads this lane's staging scope (account/user/branch/workspace) and branch resolution, and lists every other lane on the branch. | none |
92
92
  | `remits-cli components clear` | Clears THIS lane's staging cache without changing DB or git. Never touches another workspace lane. | none |
93
+ | `remits-cli components sync doctor` | Reads local git, platform status, staging freshness, repository match, and promotion/overlay freshness. It is the safe diagnostic when sync state is confusing. | none |
93
94
  | `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** |
94
95
  | `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) |
95
96
  | `remits-cli components commit` | **One shot:** changed-source compile validation (a `--changed-only` MERGE stage, so it never reconciles the lane) + `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** |
@@ -64,6 +64,17 @@ source-layer facts the command observed. A staged test packet is proof of the st
64
64
  is proof of a source transition; a token packet is proof of the browser token's resolution tuple. Those
65
65
  are not interchangeable.
66
66
 
67
+ Use proof words narrowly:
68
+
69
+ | Evidence | What it proves | What it does NOT prove |
70
+ |---|---|---|
71
+ | `components stage --workset` | The platform accepted the staged workset and compile-checked changed runtime source | Any committed component changed, or any user journey works |
72
+ | `test run` with staged entries | The selected Test passed against this staging lane/world | The committed variant/trunk source passes |
73
+ | `components sync` / sync packet | Durable source moved from the git remote into trunk rows or variant overlays | Browser/upload behavior, or repeated live AI consistency |
74
+ | `components promotion` / `sync doctor` | Which remote SHA and overlay SHA the platform reports now | That a workflow was exercised after the overlay advanced |
75
+ | `token` / browser flow | The browser entry point resolved a host/account/lane/source tuple and the journey worked there | Persisted document fields are stable or equal across repeated runs |
76
+ | `corpus consistency` / repeated live measurement | The same artifact's recorded metrics were stable or unstable across runs in that world | A mocked seam suite caught all customer-facing variance |
77
+
67
78
  Use `remits-cli verify report` before summarizing the work. It will say when the evidence only covered
68
79
  staged source, when committed variant/trunk proof is missing, when the manifest world does not match the
69
80
  packet world, or when later packet facts made an earlier proof stale. Packets carry a lane content hash,
@@ -159,6 +159,11 @@ Two ways to prove it:
159
159
  Use a Test when the behavior can be asserted programmatically, a browser when the change is visual.
160
160
  Often both.
161
161
 
162
+ For Embeddables, prefer `playwright-cli` over `curl`. A `curl` request can confirm the server returned
163
+ something or expose a JSON/action payload, but it is not evidence that the user-facing page works:
164
+ JavaScript, Alpine initialization, assets, cookies/session state, redirects, file controls, iframe/embed
165
+ placement, and websocket updates all require a browser.
166
+
162
167
  **Never skip verification.** "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
163
168
  cannot verify — no Test component, no relevant embeddable — say what you would need and ask how the user
164
169
  wants to proceed rather than reporting the work as done.
@@ -172,7 +177,7 @@ wants to proceed rather than reporting the work as done.
172
177
  This is how every development task should flow:
173
178
 
174
179
  #### Step 1: Understand the Request
175
- 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.
180
+ 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-boot.json` and `README.md` to understand the account shape and component routing inventory. Read `account-info.json` only when the compact file lacks detail you need. Read the source of any component you'll modify before changing it.
176
181
 
177
182
  **Establish a steady git baseline before the first edit.** The platform syncs from the GitHub remote, not
178
183
  from your local files, and a worktree can be stale even when its staging lane is isolated. In the checkout
@@ -191,14 +196,14 @@ that the branch is behind trunk or that overlays were computed from an old SHA,
191
196
  code. A workspace prevents staged-cache collisions; it does not make a stale branch current.
192
197
 
193
198
  **Establish the account's shape too, not just its components.** Read the `resolution` block in
194
- `account-info.json` (or `mcp_account_view`): the account `type` decides whether this repo is even the right
199
+ `account-boot.json` (or `mcp_account_view`): the account `type` decides whether this repo is even the right
195
200
  place to change code, `resolution.relationships` shows whether the account has more than one parent (and
196
201
  which link carries a branch/namespace/host), and `resolvedDatabaseName` tells you where its data actually
197
202
  lands. See `account-targeting.md` and `features/account-management.md` (`mcp_get_guide`).
198
203
 
199
204
  **Also establish which world you are working in.** `remits-cli components status` reports whether the
200
205
  working tree is a **trunk** checkout or a **variant branch** checkout — which decides both what your test
201
- runs resolve and what a sync writes. If `account-info.json` carries a `componentBranches` section, branch
206
+ runs resolve and what a sync writes. If `account-boot.json` carries a `componentBranches` section, branch
202
207
  variants of these components exist: editing an origin component will drift them, so check
203
208
  `remits-cli components branches` before changing shared code. See `branch-variants.md`.
204
209
 
@@ -265,7 +270,7 @@ the file. Omit the key; the platform fills it in on sync:
265
270
  ```yaml
266
271
  # components/embeddables/new_MerchantPortal.meta.yml — no `id:` yet
267
272
  name: Merchant Portal
268
- summary: One-line statement of what this component is for. This is the compact text account-info.json uses first.
273
+ summary: One-line statement of what this component is for. This is the compact text account-boot.json uses first.
269
274
  description: |
270
275
  Longer technical description with line-number references to the key logic.
271
276
  path: /page/merchant-portal # Readers and Embeddables only
@@ -515,6 +520,10 @@ playwright-cli fill "#amount" "500.00"
515
520
  playwright-cli eval "() => document.querySelector('.total-amount').textContent"
516
521
  ```
517
522
 
523
+ Use this browser path for Embeddable acceptance. `curl` is acceptable for narrow diagnostics such as
524
+ checking an HTTP status or inspecting a server-rendered payload, but it does not prove the page initializes
525
+ or behaves correctly in a browser.
526
+
518
527
  The `testMode` metadata confirms you're testing against staged changes, not production.
519
528
 
520
529
  **Two different token keys come back, for two different jobs.** When `--path` resolves to an
@@ -568,7 +577,7 @@ If the work is tied to a support ticket:
568
577
 
569
578
  Before committing, update metadata so the next session understands what changed:
570
579
 
571
- 1. **`.meta.yml` sidecars** — Update `summary`, `description`, and `mermaid` for each modified component. `summary` is what drives the compact component description in generated `account-info.json`; `description` is the fallback when no summary is set and is capped in that file. Preserve or explicitly revise dated decision notes; do not delete the evidence the next agent needs.
580
+ 1. **`.meta.yml` sidecars** — Update `summary`, `description`, and `mermaid` for each modified component. `summary` is what drives the compact component description in generated `account-boot.json`; `description` is the fallback when no summary is set and is capped there. Preserve or explicitly revise dated decision notes; do not delete the evidence the next agent needs.
572
581
  2. **`README.md`** — If the change affects account-level capabilities or workflows.
573
582
  3. **New components** — Always fill in `.meta.yml` immediately.
574
583
 
@@ -630,12 +639,17 @@ IDs or when unexpected deletes/renumbers are present.
630
639
  Only after those checks pass, and only when the user intends to promote the repo to the platform database:
631
640
 
632
641
  ```bash
633
- # On a VARIANT branch — the recommended path. Dry-runs first and refuses a surprising plan.
642
+ # On a VARIANT branch in an agent/noninteractive session — dry-runs first, refuses surprises,
643
+ # and skips only the confirmation prompt, not the gates.
644
+ remits-cli components sync --safe --yes
645
+ git pull --ff-only origin <branch>
646
+
647
+ # On a VARIANT branch in a human interactive shell — same gates, then asks before writing.
634
648
  remits-cli components sync --safe
635
649
  git pull --ff-only origin <branch>
636
650
 
637
651
  # On TRUNK — there is no plan to gate, so --safe explains what a trunk reconcile does and needs --yes.
638
- remits-cli components sync
652
+ remits-cli components sync --safe --yes
639
653
  git pull --ff-only origin <branch>
640
654
  ```
641
655
 
@@ -655,14 +669,29 @@ remits-cli components sync --safe --expected-removed action:50
655
669
 
656
670
  `--force-tombstones` stays explicit and human-owned. Never pass it to get past a refusal.
657
671
 
672
+ If sync state is confusing, use the read-only diagnostic instead of building shell loops:
673
+
674
+ ```bash
675
+ remits-cli components sync doctor
676
+ remits-cli components promotion --branch <branch> --no-fail
677
+ ```
678
+
679
+ `components sync doctor` assembles local `HEAD`, `origin/<branch>`, the platform's last synced SHA,
680
+ promotion freshness, staging shadow risk, repository mismatch, and a recommended next action. A client
681
+ timeout does **not** prove the sync failed; it means the result is unknown. Observe before retrying.
682
+ Never put `components sync` inside a polling loop, and do not use shell detach tricks (`setsid`, `nohup`,
683
+ `disown`, `tmux`, ...). A loop may read status/promotion/doctor; it must never start writes.
684
+
658
685
  ##### Verifying the COMMITTED variant, not your staging
659
686
 
660
687
  After a sync, staged entries still win for CLI-scoped runs, so a test that passes may be testing your
661
- staging rather than what you just committed. Clear the lane first:
688
+ staging rather than what you just committed. Clear the lane first, then verify the subscriber/variant
689
+ world explicitly:
662
690
 
663
691
  ```bash
664
692
  remits-cli components clear --all
665
693
  remits-cli test run --test <id-or-name> --as-account <subscriber-id>
694
+ remits-cli components sync doctor
666
695
  ```
667
696
 
668
697
  `remits-cli components sync` is the authoritative platform-sync step. It does not perform local git operations,
@@ -28,6 +28,7 @@
28
28
  | Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/shared/tools/tools.json` with latest schemas. |
29
29
  | Tool response missing | Run `remits-cli doctor local-state`, then check `./.remits-cli/actors/<local-agent>/tool-responses/` and any legacy flat `./.remits-cli/tool-responses/` fallback it reports. |
30
30
  | 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 `command-reference.md` → *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. |
31
+ | `components sync` timed out, or you are unsure whether overlays advanced | Treat the result as **unknown**, not failed. Run `remits-cli components sync doctor` and `remits-cli components promotion --branch <branch> --no-fail`. Do not put `components sync` in a polling loop and do not use shell detach tricks; observation may poll, writes may not. |
31
32
  | Need to continue tracking a long Action or Agent after terminal disconnect | Read `./.remits-cli/actors/<local-agent>/tool-responses/<callId>.json` for the returned `actionRunId`/`agentRunId`/`sessionId`/`threadGroupingId` (or the legacy flat fallback if `doctor local-state` reports it there). 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`. |
32
33
  | 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.md`. |
33
34
  | Need to know an account's shape (role, type, parents, namespace, branch, host/login routes) | 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`, `domainName`/`resolvedDomainName`, `authPath`/`targetPath`, `relationships`, `componentBranch`, plus top-level `componentBranches`. Never infer structure from the account's name. |