@remits/remits-cli 0.1.138 → 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 +4 -3
- package/index.js +241 -5
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +17 -0
- package/skills/remits-cli/references/branch-variants.md +37 -1
- package/skills/remits-cli/references/command-reference.md +5 -0
- package/skills/remits-cli/references/component-integrity.md +1 -0
- package/skills/remits-cli/references/component-resolution.md +11 -0
- package/skills/remits-cli/references/development-loop.md +32 -3
- package/skills/remits-cli/references/troubleshooting.md +1 -0
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
|
|
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
|
|
104
|
-
|
|
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
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
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.
|
|
@@ -16537,11 +16765,14 @@ function printComponentsHelp(subcommand) {
|
|
|
16537
16765
|
}
|
|
16538
16766
|
if (subcommand === 'sync') {
|
|
16539
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]');
|
|
16540
16769
|
console.log('');
|
|
16541
16770
|
console.log('On trunk this reconciles the pushed repository into live component rows.');
|
|
16542
16771
|
console.log('On a variant branch this writes ComponentVariant overlays only.');
|
|
16543
16772
|
console.log('--dry-run is only supported on variant branches and writes nothing.');
|
|
16544
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.');
|
|
16545
16776
|
console.log('');
|
|
16546
16777
|
console.log('Agent safety gates (each exits non-zero instead of printing a wall of JSON):');
|
|
16547
16778
|
console.log(' --safe THE RECOMMENDED PATH on a variant branch. Expands to');
|
|
@@ -17084,6 +17315,11 @@ async function main() {
|
|
|
17084
17315
|
return;
|
|
17085
17316
|
}
|
|
17086
17317
|
|
|
17318
|
+
if (command === 'components' && subcommand === 'sync' && String(args._ && args._[2] || '').toLowerCase() === 'doctor') {
|
|
17319
|
+
await syncDoctorComponentsCommand(args);
|
|
17320
|
+
return;
|
|
17321
|
+
}
|
|
17322
|
+
|
|
17087
17323
|
if (command === 'components' && subcommand === 'sync') {
|
|
17088
17324
|
await syncComponentsCommand(args);
|
|
17089
17325
|
return;
|
package/package.json
CHANGED
|
@@ -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
|
|
@@ -169,6 +180,12 @@ reference named after it.
|
|
|
169
180
|
component (preferred, because it becomes regression protection) or a browser flow through
|
|
170
181
|
`remits-cli token`. "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
|
|
171
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`)
|
|
172
189
|
- **`remits-cli evidence` answers "what have I actually run, and in which world?"** Every stage, test,
|
|
173
190
|
token, tool and sync appends one world-stamped line automatically, per actor. Nothing to start,
|
|
174
191
|
nothing to satisfy. Read it before you re-run something, and quote it when you report what you
|
|
@@ -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
|
|
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
|
|
@@ -246,6 +246,7 @@ remits-cli components lanes [--json] # every indexed sta
|
|
|
246
246
|
remits-cli components entries --lane-id <id> [--json|--verbose] # authoritative staged files for one lane
|
|
247
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
|
|
248
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
|
|
249
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
|
|
250
251
|
remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
|
|
251
252
|
remits-cli components branch <name> [--json] # one branch: owner account, overridden / added / removed, drift flags, subscribers
|
|
@@ -579,6 +580,10 @@ For tests specifically:
|
|
|
579
580
|
- `--summary` on `components sync` prints compact counts plus named updated (with the fields that
|
|
580
581
|
changed), renamed, skipped, removal/tombstone, error, and gate details instead of the full payload. A
|
|
581
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.
|
|
582
587
|
- `--json` on `components stage`, `components sync`, `components commit`, and `tool` prints one
|
|
583
588
|
parseable JSON document to stdout. Safety banners and prose go to stderr, and sync safety-gate failures
|
|
584
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.
|
|
@@ -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
|
|
@@ -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
|
|
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. |
|