@remits/remits-cli 0.1.118 → 0.1.120

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
@@ -71,9 +71,10 @@ remits-cli install --skills --overwrite true
71
71
  - An **empty workset never clears a lane**: `--workset` on a clean working tree stages nothing and leaves the lane as it is. Pass `--empty-workset clear` to opt in, or use `components clear --all`.
72
72
  - A **deleted component file cannot be represented in Redis staging** — clearing a staged entry falls back to the committed row, so the component still resolves. Those changes are reported as NOT REPRESENTABLE. On a non-trunk variant branch, prove a deletion through `components sync --dry-run --summary --fail-on-errors`; on trunk there is no dry-run plan, so use the full pre-sync safety check before any mutating reconcile.
73
73
  - Schema `.meta.yml` sidecars can stage/sync the schema flags: `enableTrigger`, `enableFullText`, `enableRAG`, `enableRevisions`, `enableBigQuerySync`, `enableRules`, `anchor`, and `auxiliary`.
74
- - `components status` shows the branch/user staging entries that can shadow DB components during CLI-scoped test-mode execution.
74
+ - `components status` shows the branch/user staging entries that can shadow DB components during CLI-scoped test-mode execution, plus the other live lanes on the account.
75
+ - `components lanes` lists every indexed staging lane on the account. `components entries --lane-id <id>` reads the authoritative staged files for one lane without switching your workspace, clearing anything, or using SQL.
75
76
  - `components clear` clears staged entries. Scope it with `--component-type` and/or `--component-id`. Component ids are type-local, so an id alone clears that one component when the id is staged in only one family; if the same id is staged across multiple families it returns an ambiguity error asking you to add `--component-type`. With no filter it clears every staged entry for the current branch; pass `--all` to force the full-branch wipe explicitly.
76
- - `components stage`, `components status`, and `components clear` print concise summaries by default. Add `--json` or `--verbose` to print the full server response.
77
+ - `components stage`, `components status`, `components lanes`, `components entries`, and `components clear` print concise summaries by default. Add `--json` or `--verbose` to print the full server response.
77
78
  - `components stage` and `components status` report three separate numbers, and they answer three different questions: the **workset** (components git reports this working tree changed), what was **submitted**, and the materialized **overlay** the lane now holds — which is what a run resolves. A number that could not be established prints as `unknown`, never as `0`.
78
79
  - `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.
79
80
  - 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.
@@ -83,6 +84,7 @@ remits-cli install --skills --overwrite true
83
84
  - `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.
84
85
  - 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.
85
86
  - Auth sessions are stored per `accountId + dataMode + baseUrl`, so the same account can stay authenticated against both localhost and production without overwriting the other session.
87
+ - Active verification envelopes are scoped per `baseUrl + accountId + branch + workspace + dataMode`, so a localhost envelope does not silently capture evidence from a production command. Pass `--verify-envelope <id>` for an intentional cross-context attachment.
86
88
  - `--base-url` and `--data-mode` are independent. `--base-url` chooses the Remits host (`http://localhost:8080` vs deployed prod), while `--data-mode` chooses the data segment on that host (`test` vs `prod`). Do not assume `--data-mode prod` means the deployed prod host, or that `--data-mode test` means localhost.
87
89
  - `remits-cli start` scans the machine for `account-info.json` files and rebuilds `~/.remits-cli/account-repos.json` before bringing up the background service.
88
90
  - Front-stage guides are **not** bundled with the CLI npm package. For account-work commands (`components`, `test`, `token`, `tools`, `tool`) and on `auth`, `remits-cli` downloads the latest guides from the authenticated `/cli/guides` endpoint and writes them into the current account repo: root `platform-overview.md` / `development-guide.md`, `guides/*.md`, `guides/features/*.md`, and the agent-guidance files `CLAUDE.md` / `AGENTS.md` / `GEMINI.md` (from `docs/front-stage/remits-components-zip-readme.md`).
package/index.js CHANGED
@@ -546,6 +546,7 @@ function localStatePaths(cwd) {
546
546
  toolResponsesDir: path.join(base, 'tool-responses'),
547
547
  verificationDir: path.join(base, 'verification'),
548
548
  activeVerificationFile: path.join(base, 'verification', 'active'),
549
+ activeVerificationContextsFile: path.join(base, 'verification', 'active-contexts.json'),
549
550
  currentSessionFile: path.join(base, 'current-session.txt'),
550
551
  workspaceFile: path.join(base, 'workspace')
551
552
  };
@@ -771,7 +772,49 @@ function verificationPaths(cwd, envelopeId) {
771
772
  };
772
773
  }
773
774
 
774
- function readActiveVerificationEnvelope(cwd) {
775
+ function verificationContextKey(context = {}) {
776
+ const baseUrl = normalizeBaseUrl(context.baseUrl || context.host || DEFAULT_BASE_URL);
777
+ const accountId = context.accountId != null ? String(context.accountId) : 'unknown-account';
778
+ const branchName = context.branchName || context.branch || 'unknown-branch';
779
+ const workspace = normalizeWorkspace(context.workspace) || 'shared';
780
+ const dataMode = normalizeDataMode(context.dataMode);
781
+ return [baseUrl, accountId, branchName, workspace, dataMode].join('|');
782
+ }
783
+
784
+ function readActiveVerificationContexts(cwd) {
785
+ const file = localStatePaths(cwd).activeVerificationContextsFile;
786
+ if (!fs.existsSync(file)) return {};
787
+ try {
788
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
789
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
790
+ } catch (_) {
791
+ return {};
792
+ }
793
+ }
794
+
795
+ function writeActiveVerificationContexts(cwd, contexts) {
796
+ const paths = ensureLocalState(cwd);
797
+ atomicWriteFile(paths.activeVerificationContextsFile, JSON.stringify(contexts || {}, null, 2));
798
+ }
799
+
800
+ function activeVerificationContext(cwd, flags = {}, session = null, accountId = null) {
801
+ return {
802
+ baseUrl: normalizeBaseUrl(flags['base-url'] || (session && session.baseUrl) || DEFAULT_BASE_URL),
803
+ accountId,
804
+ branchName: flags.branch || currentBranch(cwd),
805
+ workspace: resolveWorkspace(cwd, flags),
806
+ dataMode: resolveDataMode(flags, session)
807
+ };
808
+ }
809
+
810
+ function readActiveVerificationEnvelope(cwd, context = null) {
811
+ if (context) {
812
+ const contexts = readActiveVerificationContexts(cwd);
813
+ const entry = contexts[verificationContextKey(context)];
814
+ if (entry && typeof entry === 'object') return entry.envelopeId || null;
815
+ if (typeof entry === 'string') return entry || null;
816
+ return null;
817
+ }
775
818
  const file = verificationPaths(cwd).activeFile;
776
819
  try {
777
820
  if (!fs.existsSync(file)) return null;
@@ -782,8 +825,25 @@ function readActiveVerificationEnvelope(cwd) {
782
825
  }
783
826
  }
784
827
 
785
- function writeActiveVerificationEnvelope(cwd, envelopeId) {
828
+ function writeActiveVerificationEnvelope(cwd, envelopeId, context = null) {
786
829
  const paths = verificationPaths(cwd);
830
+ if (context) {
831
+ const contexts = readActiveVerificationContexts(cwd);
832
+ const key = verificationContextKey(context);
833
+ if (!envelopeId) {
834
+ delete contexts[key];
835
+ } else {
836
+ contexts[key] = {
837
+ envelopeId: String(envelopeId),
838
+ updatedAt: new Date().toISOString(),
839
+ context: Object.assign({}, context, {
840
+ baseUrl: normalizeBaseUrl(context.baseUrl || context.host || DEFAULT_BASE_URL),
841
+ workspace: normalizeWorkspace(context.workspace)
842
+ })
843
+ };
844
+ }
845
+ writeActiveVerificationContexts(cwd, contexts);
846
+ }
787
847
  if (!envelopeId) {
788
848
  try { if (fs.existsSync(paths.activeFile)) fs.unlinkSync(paths.activeFile); } catch (_) { /* best-effort */ }
789
849
  return null;
@@ -792,11 +852,11 @@ function writeActiveVerificationEnvelope(cwd, envelopeId) {
792
852
  return String(envelopeId);
793
853
  }
794
854
 
795
- function verificationEnvelopeIdForCommand(cwd, flags = {}) {
855
+ function verificationEnvelopeIdForCommand(cwd, flags = {}, context = null) {
796
856
  if (flagEnabled(flags['no-verify-envelope']) || flagEnabled(flags.noVerifyEnvelope)) return null;
797
857
  const explicit = flags['verify-envelope'] || flags.verifyEnvelope || flags.envelope;
798
858
  if (explicit !== undefined && explicit !== null && explicit !== true) return String(explicit).trim() || null;
799
- return readActiveVerificationEnvelope(cwd);
859
+ return context ? readActiveVerificationEnvelope(cwd, context) : readActiveVerificationEnvelope(cwd);
800
860
  }
801
861
 
802
862
  function writeLocalVerificationEnvelope(cwd, envelope) {
@@ -903,7 +963,8 @@ async function postVerificationCommand(api, cwd, session, accountId, command, pa
903
963
  }
904
964
 
905
965
  async function appendVerificationPacket(api, cwd, session, accountId, flags, packet, options = {}) {
906
- const envelopeId = verificationEnvelopeIdForCommand(cwd, flags);
966
+ const context = activeVerificationContext(cwd, flags, session, accountId);
967
+ const envelopeId = verificationEnvelopeIdForCommand(cwd, flags, context);
907
968
  if (!envelopeId) return null;
908
969
  const finalPacket = Object.assign({
909
970
  packetId: crypto.randomUUID(),
@@ -929,6 +990,52 @@ async function appendVerificationPacket(api, cwd, session, accountId, flags, pac
929
990
  }
930
991
  }
931
992
 
993
+ function formatTime(value) {
994
+ if (!value) return 'unknown';
995
+ const n = Number(value);
996
+ const date = Number.isFinite(n) ? new Date(n) : new Date(value);
997
+ if (Number.isNaN(date.getTime())) return String(value);
998
+ return date.toISOString();
999
+ }
1000
+
1001
+ function printVerificationSummaryLine(env) {
1002
+ const health = env.health ? ' health=' + env.health : '';
1003
+ const failures = env.currentFailureCount ? ' currentFailures=' + env.currentFailureCount : (env.failedCount ? ' failed=' + env.failedCount : '');
1004
+ const required = ' required=' + (env.satisfiedCount || 0) + '/' + (env.requiredCount || 0);
1005
+ const noManifest = env.noRequiredEvidence ? ' no-manifest' : '';
1006
+ const lane = [env.branchName || 'unknown-branch', env.workspace ? 'ws:' + env.workspace : 'shared', env.dataMode || null, env.sourceLayer || null]
1007
+ .filter(Boolean).join(' | ');
1008
+ console.log('- ' + (env.envelopeId || '(no id)') + ' ' + (env.status || 'unknown') + health + failures + required + noManifest);
1009
+ if (env.summary) console.log(' ' + env.summary);
1010
+ console.log(' ' + lane + ' packets=' + (env.packetCount || 0) + ' updated=' + formatTime(env.updatedAtMs || env.latestPacketAtMs));
1011
+ }
1012
+
1013
+ function printVerificationEnvelope(envelope, fallbackEnvelopeId) {
1014
+ console.log('Verification envelope:', envelope.envelopeId || fallbackEnvelopeId);
1015
+ if (envelope.summary) console.log('Summary:', envelope.summary);
1016
+ console.log('Status:', envelope.status || 'unknown');
1017
+ const evaluation = envelope.evaluation || {};
1018
+ if (evaluation.health) console.log('Health:', evaluation.health);
1019
+ if (evaluation.noRequiredEvidence) {
1020
+ console.log('Required evidence: none declared - evidence packets do not prove a complete acceptance contract.');
1021
+ } else {
1022
+ console.log('Required evidence:', (evaluation.satisfiedCount || 0) + '/' + (evaluation.requiredCount || 0));
1023
+ }
1024
+ if (evaluation.failedCount) {
1025
+ console.log('Failed packets:', evaluation.failedCount, 'current failures:', evaluation.currentFailureCount || 0);
1026
+ }
1027
+ const packets = Array.isArray(envelope.packets) ? envelope.packets : [];
1028
+ console.log('Packets:', packets.length);
1029
+ packets.slice(-20).forEach((packet) => {
1030
+ const status = packet.stale ? ' stale' : (packet.success === false ? ' failed' : '');
1031
+ const claim = packet.claim ? ' - ' + packet.claim : '';
1032
+ console.log('- ' + (packet.type || 'unknown') + ' ' + (packet.packetId || '') + status + claim);
1033
+ });
1034
+ if (packets.length > 20) {
1035
+ console.log(' ...' + (packets.length - 20) + ' earlier packet(s); rerun with --json for the full envelope');
1036
+ }
1037
+ }
1038
+
932
1039
  function buildCommandWorld(response, fallback = {}) {
933
1040
  const source = (response && response.staging) || {};
934
1041
  const resolution = (response && response.resolution) || {};
@@ -2752,8 +2859,8 @@ async function pushComponentsCommand(flags) {
2752
2859
  world: buildCommandWorld(response, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl), sourceLayer: 'staged' }),
2753
2860
  revision: collectVerificationSource(cwd, flags),
2754
2861
  assertions: [
2755
- { category: 'stage', stageMode },
2756
- { category: 'staging_lane', value: response.stagingLane }
2862
+ { id: 'stage', stageMode },
2863
+ { id: 'staging_lane', value: response.stagingLane }
2757
2864
  ],
2758
2865
  evidenceCategories: ['stage', 'staging_lane'],
2759
2866
  rawRefs: { command: 'components stage' },
@@ -2995,6 +3102,9 @@ function printStageSummary(response, flags) {
2995
3102
  }
2996
3103
 
2997
3104
  printUnrepresentableChanges(response.unrepresentableChanges || (stage.unrepresentable || []));
3105
+ if (typeof printAccountLanes === 'function') {
3106
+ printAccountLanes(response);
3107
+ }
2998
3108
  printComponentCommandResponse('Components stage', response, flags);
2999
3109
  }
3000
3110
 
@@ -3040,6 +3150,61 @@ function printStatusSummary(response, flags) {
3040
3150
  printComponentCommandResponse('Components staging', response, flags);
3041
3151
  }
3042
3152
 
3153
+ function printLanesSummary(response, flags) {
3154
+ printBranchContext(response);
3155
+ const lanes = Array.isArray(response.lanes) ? response.lanes : (Array.isArray(response.accountLanes) ? response.accountLanes : []);
3156
+ console.log('Staging lanes for account ' + response.accountId + ':', lanes.length);
3157
+ if (!lanes.length) {
3158
+ console.log('No staged lanes are currently indexed for this account.');
3159
+ return;
3160
+ }
3161
+ lanes.forEach((lane) => printStagingLaneRow(lane));
3162
+ console.log('');
3163
+ console.log('Inspect a lane: remits-cli components entries --lane-id <laneId>');
3164
+ console.log('Or by scope: remits-cli components entries --lane-account-id <id> --cli-user-id <id> --branch <name> [--workspace <name>]');
3165
+ printComponentCommandResponse('Components lanes', response, flags);
3166
+ }
3167
+
3168
+ function printStagingLaneRow(lane) {
3169
+ const world = lane.onTrunk === true ? 'trunk'
3170
+ : lane.onTrunk === false ? 'variant'
3171
+ : 'unknown-world';
3172
+ const ws = lane.workspace ? ' [ws:' + lane.workspace + ']' : ' [shared]';
3173
+ const workset = lane.worksetKnown && lane.worksetCountAsOf != null
3174
+ ? ' workset ' + lane.worksetCountAsOf
3175
+ : ' workset unknown';
3176
+ const shared = lane.sharedLane === true ? ' SHARED' : '';
3177
+ const ttl = lane.expiresInSeconds != null ? ' expires ~' + Math.round(lane.expiresInSeconds / 60) + 'm' : '';
3178
+ console.log('- ' + (lane.laneId || lane.stagingLane || '(no lane id)'));
3179
+ console.log(' ' + lane.branchName + ws + ' ' + world + shared + ' overlay ' + (lane.stagedCountAsOf || lane.stagedCount || 0) + workset + ttl);
3180
+ if (lane.userEmail || lane.cliUserId) console.log(' owner: ' + (lane.userEmail || 'user #' + lane.cliUserId) + ' cliUserId=' + lane.cliUserId);
3181
+ if (lane.typeCounts) console.log(' types: ' + Object.entries(lane.typeCounts).map(([k, v]) => k + ':' + v).join(', '));
3182
+ }
3183
+
3184
+ function printEntriesSummary(response, flags) {
3185
+ console.log('Staging lane:', response.stagingLane || response.laneId);
3186
+ console.log('Account:', response.accountId, 'CLI user:', response.cliUserId);
3187
+ console.log('Branch:', response.branchName, 'Workspace:', response.workspace || 'shared');
3188
+ printLaneSummary(response);
3189
+ printComponentTypeCounts(response.entries || []);
3190
+ const entries = Array.isArray(response.entries) ? response.entries : [];
3191
+ if (entries.length) {
3192
+ console.log('');
3193
+ entries.slice(0, flagEnabled(flags.verbose) ? entries.length : 40).forEach((entry) => {
3194
+ const id = entry.componentId != null ? '#' + entry.componentId : '(new)';
3195
+ const name = entry.componentName || entry.name || '';
3196
+ const pathText = entry.path ? ' ' + entry.path : '';
3197
+ const fields = Array.isArray(entry.stagedFields) && entry.stagedFields.length ? ' fields=' + entry.stagedFields.join(',') : '';
3198
+ const inWorkset = entry.stageInWorkset === true ? ' workset' : (entry.stageInWorkset === false ? ' retained' : '');
3199
+ console.log('- ' + (entry.type || entry.componentType || 'component') + ' ' + id + (name ? ' ' + name : '') + inWorkset + fields + pathText);
3200
+ });
3201
+ if (!flagEnabled(flags.verbose) && entries.length > 40) {
3202
+ console.log(' ...' + (entries.length - 40) + ' more; rerun with --verbose or --json');
3203
+ }
3204
+ }
3205
+ printComponentCommandResponse('Components entries', response, flags);
3206
+ }
3207
+
3043
3208
  /**
3044
3209
  * What THIS lane holds, separated into the work in flight and the overlay carried along with it.
3045
3210
  *
@@ -3269,6 +3434,85 @@ async function statusComponentsCommand(flags) {
3269
3434
  return response;
3270
3435
  }
3271
3436
 
3437
+ async function lanesComponentsCommand(flags) {
3438
+ const cwd = process.cwd();
3439
+ ensureLocalState(cwd);
3440
+ const sessionContext = resolveSessionContext(cwd, flags);
3441
+ const { session, accountId } = sessionContext;
3442
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
3443
+ const branchName = flags.branch || currentBranch(cwd);
3444
+ const workspace = resolveWorkspace(cwd, flags);
3445
+ const dataMode = resolveDataMode(flags, session);
3446
+ const api = buildAxios(baseUrl, session.token);
3447
+
3448
+ const response = await loggedPost(api, cwd, '/cli/components', {
3449
+ token: session.token,
3450
+ accountId,
3451
+ branchName,
3452
+ workspace,
3453
+ dataMode,
3454
+ mode: 'lanes'
3455
+ }).then((r) => r.data);
3456
+
3457
+ if (!response.success) {
3458
+ throw new Error(response.message || 'Staging lanes failed');
3459
+ }
3460
+
3461
+ if (flagEnabled(flags.json)) {
3462
+ console.log(JSON.stringify(response, null, 2));
3463
+ return response;
3464
+ }
3465
+
3466
+ printSessionResolutionWarning(sessionContext);
3467
+ printResolvedBaseUrl(baseUrl);
3468
+ console.log('Data mode:', response.dataMode || dataMode);
3469
+ printLanesSummary(response, flags);
3470
+ return response;
3471
+ }
3472
+
3473
+ async function entriesComponentsCommand(flags) {
3474
+ const cwd = process.cwd();
3475
+ ensureLocalState(cwd);
3476
+ const sessionContext = resolveSessionContext(cwd, flags);
3477
+ const { session, accountId } = sessionContext;
3478
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
3479
+ const branchName = flags.branch || currentBranch(cwd);
3480
+ const workspace = resolveWorkspace(cwd, flags);
3481
+ const dataMode = resolveDataMode(flags, session);
3482
+ const api = buildAxios(baseUrl, session.token);
3483
+
3484
+ const response = await loggedPost(api, cwd, '/cli/components', {
3485
+ token: session.token,
3486
+ accountId,
3487
+ branchName,
3488
+ workspace,
3489
+ dataMode,
3490
+ mode: 'entries',
3491
+ laneId: flags['lane-id'] || flags.lane,
3492
+ laneAccountId: flags['lane-account-id'] || flags.ownerAccountId || flags['owner-account-id'],
3493
+ cliUserId: flags['cli-user-id'] || flags.userId || flags['user-id'],
3494
+ laneBranchName: flags['lane-branch'] || flags.targetBranch || flags['target-branch'],
3495
+ laneWorkspace: flags['lane-workspace'] || flags.targetWorkspace || flags['target-workspace'],
3496
+ componentType: flags['component-type'] || flags.type,
3497
+ componentId: flags['component-id'] || flags.id
3498
+ }).then((r) => r.data);
3499
+
3500
+ if (!response.success) {
3501
+ throw new Error(response.message || 'Staging entries failed');
3502
+ }
3503
+
3504
+ if (flagEnabled(flags.json)) {
3505
+ console.log(JSON.stringify(response, null, 2));
3506
+ return response;
3507
+ }
3508
+
3509
+ printSessionResolutionWarning(sessionContext);
3510
+ printResolvedBaseUrl(baseUrl);
3511
+ console.log('Data mode:', response.dataMode || dataMode);
3512
+ printEntriesSummary(response, flags);
3513
+ return response;
3514
+ }
3515
+
3272
3516
  async function clearComponentsCommand(flags) {
3273
3517
  const cwd = process.cwd();
3274
3518
  ensureLocalState(cwd);
@@ -4560,6 +4804,9 @@ async function testCommand(flags) {
4560
4804
  printSessionResolutionWarning(sessionContext);
4561
4805
  printResolvedBaseUrl(baseUrl);
4562
4806
  printStagingLane(branchName, workspace, workspaceSource(cwd, flags));
4807
+ if (start.staging && Array.isArray(start.staging.accountLanes)) {
4808
+ printAccountLanes({ accountLanes: start.staging.accountLanes, branchName, workspace });
4809
+ }
4563
4810
  console.log('Test run started:', start.taskId);
4564
4811
  runtimeState.currentTestTaskId = start.taskId;
4565
4812
 
@@ -4687,6 +4934,7 @@ async function tokenCommand(flags) {
4687
4934
  variantBranch: data.variantBranch,
4688
4935
  workspace: data.workspace !== undefined ? data.workspace : workspace,
4689
4936
  stagingLane: data.stagingLane,
4937
+ accountLanes: data.accountLanes,
4690
4938
  dataMode: data.dataMode || dataMode,
4691
4939
  shortBasePath: data.shortBasePath,
4692
4940
  embeddableUrl
@@ -5059,10 +5307,26 @@ async function verifyCommand(flags, subcommand) {
5059
5307
  ensureLocalState(cwd);
5060
5308
  const sub = String(subcommand || (flags._ && flags._[1]) || 'status').toLowerCase();
5061
5309
 
5310
+ const sessionContext = resolveSessionContext(cwd, flags);
5311
+ const { session, accountId } = sessionContext;
5312
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5313
+ const dataMode = resolveDataMode(flags, session);
5314
+ const branchName = flags.branch || currentBranch(cwd);
5315
+ const workspace = resolveWorkspace(cwd, flags);
5316
+ const api = buildAxios(baseUrl, session.token);
5317
+ const activeContext = activeVerificationContext(cwd, flags, session, accountId);
5318
+
5062
5319
  if (sub === 'current') {
5063
- const active = readActiveVerificationEnvelope(cwd);
5320
+ const active = readActiveVerificationEnvelope(cwd, activeContext);
5321
+ printSessionResolutionWarning(sessionContext);
5322
+ printResolvedBaseUrl(baseUrl);
5323
+ console.log('Context:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
5064
5324
  if (!active) {
5065
- console.log('No active verification envelope in this checkout.');
5325
+ const legacy = readActiveVerificationEnvelope(cwd);
5326
+ console.log('No active verification envelope for this context.');
5327
+ if (legacy) {
5328
+ console.log('Legacy active envelope ignored for this context:', legacy);
5329
+ }
5066
5330
  return;
5067
5331
  }
5068
5332
  console.log('Active verification envelope:', active);
@@ -5074,25 +5338,25 @@ async function verifyCommand(flags, subcommand) {
5074
5338
  if (sub === 'use') {
5075
5339
  const envelopeId = flags._ && flags._[2] || flags.envelope || flags['verify-envelope'];
5076
5340
  if (!envelopeId) throw new Error('Usage: remits-cli verify use <envelopeId>');
5077
- writeActiveVerificationEnvelope(cwd, envelopeId);
5341
+ writeActiveVerificationEnvelope(cwd, envelopeId, activeContext);
5078
5342
  console.log('Active verification envelope:', envelopeId);
5343
+ console.log('Context:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
5079
5344
  return;
5080
5345
  }
5081
5346
 
5082
5347
  if (sub === 'clear') {
5083
- writeActiveVerificationEnvelope(cwd, null);
5084
- console.log('Verification envelope cleared for this checkout.');
5348
+ if (flagEnabled(flags.all)) {
5349
+ writeActiveVerificationEnvelope(cwd, null);
5350
+ writeActiveVerificationContexts(cwd, {});
5351
+ console.log('All active verification envelope pointers cleared for this checkout.');
5352
+ } else {
5353
+ writeActiveVerificationEnvelope(cwd, null, activeContext);
5354
+ console.log('Verification envelope cleared for this context.');
5355
+ console.log('Context:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
5356
+ }
5085
5357
  return;
5086
5358
  }
5087
5359
 
5088
- const sessionContext = resolveSessionContext(cwd, flags);
5089
- const { session, accountId } = sessionContext;
5090
- const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5091
- const dataMode = resolveDataMode(flags, session);
5092
- const branchName = flags.branch || currentBranch(cwd);
5093
- const workspace = resolveWorkspace(cwd, flags);
5094
- const api = buildAxios(baseUrl, session.token);
5095
-
5096
5360
  if (sub === 'start') {
5097
5361
  const summary = flags.summary || flags._ && flags._.slice(2).join(' ');
5098
5362
  if (!summary) throw new Error('verify start requires --summary "..."');
@@ -5129,14 +5393,29 @@ async function verifyCommand(flags, subcommand) {
5129
5393
  });
5130
5394
  const envelope = response.envelope;
5131
5395
  writeLocalVerificationEnvelope(cwd, envelope);
5132
- writeActiveVerificationEnvelope(cwd, envelope.envelopeId);
5396
+ writeActiveVerificationEnvelope(cwd, envelope.envelopeId, activeContext);
5133
5397
  console.log('Verification envelope started:', envelope.envelopeId);
5134
5398
  console.log('Target:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
5135
5399
  console.log('Local mirror:', verificationPaths(cwd, envelope.envelopeId).base);
5136
5400
  return envelope;
5137
5401
  }
5138
5402
 
5139
- const envelopeId = flags.envelope || flags['verify-envelope'] || flags.id || readActiveVerificationEnvelope(cwd);
5403
+ if (sub === 'list' || sub === 'summaries') {
5404
+ const response = await postVerificationCommand(api, cwd, session, accountId, 'list', {
5405
+ max: flags.max || flags.limit || 50
5406
+ });
5407
+ if (flagEnabled(flags.json)) {
5408
+ console.log(JSON.stringify(response, null, 2));
5409
+ return response;
5410
+ }
5411
+ printSessionResolutionWarning(sessionContext);
5412
+ printResolvedBaseUrl(baseUrl);
5413
+ console.log('Verification envelopes:', (response.envelopes || []).length);
5414
+ (response.envelopes || []).forEach((env) => printVerificationSummaryLine(env));
5415
+ return response;
5416
+ }
5417
+
5418
+ const envelopeId = flags.envelope || flags['verify-envelope'] || flags.id || readActiveVerificationEnvelope(cwd, activeContext);
5140
5419
  if (!envelopeId) {
5141
5420
  throw new Error('No verification envelope selected. Run `remits-cli verify start --summary "..."` or pass --envelope <id>.');
5142
5421
  }
@@ -5168,14 +5447,7 @@ async function verifyCommand(flags, subcommand) {
5168
5447
  return response.envelope || response;
5169
5448
  }
5170
5449
  const envelope = response.envelope || {};
5171
- console.log('Verification envelope:', envelope.envelopeId || envelopeId);
5172
- if (envelope.summary) console.log('Summary:', envelope.summary);
5173
- console.log('Status:', envelope.status || 'unknown');
5174
- const packets = Array.isArray(envelope.packets) ? envelope.packets : [];
5175
- console.log('Packets:', packets.length);
5176
- packets.forEach((packet) => {
5177
- console.log('- ' + (packet.type || 'unknown') + ' ' + (packet.packetId || '') + (packet.success === false ? ' failed' : ''));
5178
- });
5450
+ printVerificationEnvelope(envelope, envelopeId);
5179
5451
  return envelope;
5180
5452
  }
5181
5453
 
@@ -5268,6 +5540,24 @@ async function verifyCommand(flags, subcommand) {
5268
5540
  return response.envelope;
5269
5541
  }
5270
5542
 
5543
+ if (sub === 'abandon' || sub === 'close') {
5544
+ const reason = flags.reason || flags.message || flags._ && flags._.slice(2).join(' ') || 'abandoned';
5545
+ const response = await postVerificationCommand(api, cwd, session, accountId, 'abandon', { envelopeId, reason });
5546
+ writeLocalVerificationEnvelope(cwd, response.envelope);
5547
+ console.log('Envelope abandoned:', envelopeId);
5548
+ return response.envelope;
5549
+ }
5550
+
5551
+ if (sub === 'supersede') {
5552
+ const supersededBy = flags['superseded-by'] || flags.supersededBy || flags.next || flags._ && flags._[2];
5553
+ if (!supersededBy) throw new Error('verify supersede requires --superseded-by <envelopeId>');
5554
+ const reason = flags.reason || flags.message || 'superseded by a newer verification envelope';
5555
+ const response = await postVerificationCommand(api, cwd, session, accountId, 'supersede', { envelopeId, supersededBy, reason });
5556
+ writeLocalVerificationEnvelope(cwd, response.envelope);
5557
+ console.log('Envelope superseded:', envelopeId, '->', supersededBy);
5558
+ return response.envelope;
5559
+ }
5560
+
5271
5561
  if (sub === 'status' || sub === 'report') {
5272
5562
  let statusPacket = null;
5273
5563
  try {
@@ -10825,6 +11115,23 @@ function printComponentsHelp(subcommand) {
10825
11115
  console.log('whether another agent is staging alongside you.');
10826
11116
  return;
10827
11117
  }
11118
+ if (subcommand === 'lanes' || subcommand === 'lane') {
11119
+ console.log('Usage: remits-cli components lanes [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--json]');
11120
+ console.log('');
11121
+ console.log('Lists every indexed staging lane on the account, across users, branches, and workspaces.');
11122
+ console.log('This is read-only and uses the same lane registry that powers the admin Platforms view.');
11123
+ console.log('Use `components entries --lane-id <id>` to inspect the actual staged files in a lane.');
11124
+ return;
11125
+ }
11126
+ if (subcommand === 'entries' || subcommand === 'entry' || subcommand === 'staged') {
11127
+ console.log('Usage: remits-cli components entries --lane-id ID [--base-url URL] [--account-id ID] [--json|--verbose]');
11128
+ console.log(' remits-cli components entries --lane-account-id ID --cli-user-id ID --branch BRANCH [--workspace NAME]');
11129
+ console.log('');
11130
+ console.log('Reads the authoritative staged entries for one lane from Redis staging keys.');
11131
+ console.log('Use this for review/audit: it does not switch your workspace, impersonate another user,');
11132
+ console.log('clear anything, or affect what your own runs resolve.');
11133
+ return;
11134
+ }
10828
11135
  if (subcommand === 'clear') {
10829
11136
  console.log('Usage: remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
10830
11137
  console.log('');
@@ -10846,12 +11153,14 @@ function printComponentsHelp(subcommand) {
10846
11153
  console.log('`remits-cli components sync --safe`.');
10847
11154
  return;
10848
11155
  }
10849
- console.log('Usage: remits-cli components <stage|status|clear|sync|commit|promotion|branches|branch>');
11156
+ console.log('Usage: remits-cli components <stage|status|lanes|entries|clear|sync|commit|promotion|branches|branch>');
10850
11157
  console.log('');
10851
11158
  console.log('All staging subcommands accept --workspace NAME to isolate a lane on the same branch,');
10852
11159
  console.log('so several agents can iterate at once. See: remits-cli workspace --help');
10853
11160
  console.log(' remits-cli components stage [--workset|--changed-only] [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--json|--verbose]');
10854
11161
  console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID] [--json|--verbose]');
11162
+ console.log(' remits-cli components lanes [--base-url URL] [--account-id ID] [--json]');
11163
+ console.log(' remits-cli components entries --lane-id ID [--base-url URL] [--account-id ID] [--json|--verbose]');
10855
11164
  console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
10856
11165
  console.log(' remits-cli components sync [--safe] [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--summary]');
10857
11166
  console.log(' remits-cli components commit [--safe] [--message "msg"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod] [--force-tombstones]');
@@ -10931,12 +11240,16 @@ function printVerifyHelp() {
10931
11240
  console.log('');
10932
11241
  console.log('Envelope lifecycle:');
10933
11242
  console.log(' remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID]');
11243
+ console.log(' remits-cli verify list [--max 50] [--json]');
10934
11244
  console.log(' remits-cli verify use <envelopeId>');
10935
11245
  console.log(' remits-cli verify current');
10936
- console.log(' remits-cli verify clear');
11246
+ console.log(' remits-cli verify clear [--all]');
10937
11247
  console.log(' remits-cli verify show [--envelope ID] [--json]');
10938
11248
  console.log(' remits-cli verify status [--envelope ID]');
10939
11249
  console.log(' remits-cli verify report [--envelope ID]');
11250
+ console.log(' remits-cli verify abandon --envelope ID --reason "..."');
11251
+ console.log(' remits-cli verify supersede --envelope ID --superseded-by ID [--reason "..."]');
11252
+ console.log(' Active envelopes are scoped by host, account, branch, workspace, and data mode.');
10940
11253
  console.log('');
10941
11254
  console.log('Manifest and manual evidence:');
10942
11255
  console.log(' remits-cli verify manifest --file file.json [--envelope ID]');
@@ -11034,9 +11347,11 @@ async function main() {
11034
11347
  console.log(' remits-cli tool status --call-id <callId> [--base-url URL] [--account-id ID] [--data-mode test|prod]');
11035
11348
  console.log(' remits-cli workspace [show|use <name>|use --auto|clear] # isolate staging when several agents share a repo');
11036
11349
  console.log(' remits-cli verify start --summary "..." [--manifest file.json] # start a verification envelope');
11037
- console.log(' remits-cli verify [current|use|clear|status|report|attach|test|token|stage|sync|tool]');
11350
+ console.log(' remits-cli verify [current|list|use|clear|show|status|report|attach|abandon|supersede|test|token|stage|sync|tool]');
11038
11351
  console.log(' remits-cli components stage [--workset|--changed-only] [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--data-mode test|prod] [--json|--verbose]');
11039
11352
  console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--component-type TYPE --component-id ID] [--json|--verbose]');
11353
+ console.log(' remits-cli components lanes [--base-url URL] [--account-id ID] [--json]');
11354
+ console.log(' remits-cli components entries --lane-id ID [--base-url URL] [--account-id ID] [--json|--verbose]');
11040
11355
  console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
11041
11356
  console.log(' remits-cli components sync [--safe] [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--summary]');
11042
11357
  console.log(' remits-cli components commit [--message \"msg\"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod] [--force-tombstones]');
@@ -11139,6 +11454,16 @@ async function main() {
11139
11454
  return;
11140
11455
  }
11141
11456
 
11457
+ if (command === 'components' && (subcommand === 'lanes' || subcommand === 'lane')) {
11458
+ await lanesComponentsCommand(args);
11459
+ return;
11460
+ }
11461
+
11462
+ if (command === 'components' && (subcommand === 'entries' || subcommand === 'entry' || subcommand === 'staged')) {
11463
+ await entriesComponentsCommand(args);
11464
+ return;
11465
+ }
11466
+
11142
11467
  if (command === 'components' && subcommand === 'clear') {
11143
11468
  await clearComponentsCommand(args);
11144
11469
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remits/remits-cli",
3
- "version": "0.1.118",
3
+ "version": "0.1.120",
4
4
  "description": "Local CLI for auth, component sync, and live test execution against Remits",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -101,6 +101,10 @@ reference named after it.
101
101
  holds — what a run resolves. The workset is what git says you changed. A full stage makes them differ
102
102
  by the size of the repo, and `--changed-only` merges, so it can never shrink an overlay it inherited.
103
103
  `components status` prints all three numbers; so does the console. (`component-resolution.md`)
104
+ - **Review other lanes with read-only commands, not SQL.** `components lanes` lists every indexed lane on
105
+ the account, and `components entries --lane-id <id>` shows the actual staged files in one lane without
106
+ changing your workspace or clearing anything. Use these when another agent's workstream is relevant.
107
+ (`component-resolution.md`, `command-reference.md`)
104
108
  - **Host and data mode are two independent decisions.** `--base-url` picks the Remits host,
105
109
  `--data-mode` picks the data segment on it. Neither implies the other. (`command-reference.md`)
106
110
  - **`test run` ignores the stored session data lane.** It defaults to `test` even when `whoami` shows
@@ -194,6 +194,8 @@ remits-cli data-mode [set test|prod]
194
194
  remits-cli components stage [--workset | --changed-only] [--branch <name>] [--workspace <name>] [--empty-workset clear] [--data-mode test|prod] [--json|--verbose] # default = FULL SNAPSHOT of the repo; --workset = only what git says changed, lane reconciled to it
195
195
  remits-cli workspace [show | use <name> | use --auto | clear]
196
196
  remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]
197
+ remits-cli components lanes [--json] # every indexed staging lane on the account
198
+ remits-cli components entries --lane-id <id> [--json|--verbose] # authoritative staged files for one lane
197
199
  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
198
200
  remits-cli components sync [--safe [--yes]] [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--summary] [--changed-only [--changed-since <ref>]] # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
199
201
  remits-cli components commit [--safe] [--message "msg"] [--data-mode test|prod] [--force-tombstones] # --safe gates phase 2; it cannot un-push phase 1
@@ -211,9 +213,10 @@ remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <na
211
213
  remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
212
214
  remits-cli tool status --call-id <callId> [--data-mode test|prod]
213
215
  remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID]
216
+ remits-cli verify list [--max 50] [--json]
214
217
  remits-cli verify use <envelopeId>
215
218
  remits-cli verify current
216
- remits-cli verify clear
219
+ remits-cli verify clear [--all]
217
220
  remits-cli verify show [--envelope ID] [--json]
218
221
  remits-cli verify manifest --file file.json [--envelope ID]
219
222
  remits-cli verify manifest --print [--envelope ID]
@@ -230,6 +233,8 @@ remits-cli verify sync --safe
230
233
  remits-cli verify tool --name <toolName> [--input "{...}"]
231
234
  remits-cli verify status [--envelope ID]
232
235
  remits-cli verify report [--envelope ID]
236
+ remits-cli verify abandon --envelope ID --reason "..."
237
+ remits-cli verify supersede --envelope ID --superseded-by <envelopeId> [--reason "..."]
233
238
  ```
234
239
 
235
240
  ### Verification envelopes
@@ -245,8 +250,11 @@ remits-cli components status
245
250
  remits-cli verify start --summary "Hosted upload updates an existing profile" --manifest acceptance.json
246
251
  ```
247
252
 
248
- An active envelope is stored in `./.remits-cli/verification/active`. Stage/test/token/tool/sync commands
249
- attach evidence automatically while an envelope is active. The explicit wrappers do the same thing and
253
+ An active envelope is stored per command world: `baseUrl + accountId + branch + workspace + dataMode`.
254
+ The legacy `./.remits-cli/verification/active` pointer is still understood for compatibility, but normal
255
+ auto-attachment uses `./.remits-cli/verification/active-contexts.json`, so a localhost envelope does not
256
+ silently capture evidence from a production command. Stage/test/token/tool/sync commands attach evidence
257
+ automatically while an envelope is active for that context. The explicit wrappers do the same thing and
250
258
  make the intent visible in terminal history:
251
259
 
252
260
  ```bash
@@ -260,6 +268,10 @@ remits-cli verify report
260
268
  Existing commands also accept `--verify-envelope <id>` to attach to a specific envelope and
261
269
  `--no-verify-envelope` to suppress automatic attachment for one command.
262
270
 
271
+ Use `remits-cli verify list` to review open envelopes for the account without SQL. If a newer envelope
272
+ replaces an older one, use `verify supersede`; if a duplicate or abandoned attempt should no longer read
273
+ as live work, use `verify abandon`. Both keep the evidence history.
274
+
263
275
  The manifest is the proof contract. It should name the world being exercised: repo account, host,
264
276
  git/component branch, workspace, data mode, source layer, user journeys, artifacts and hashes, and the
265
277
  evidence categories required before the final response may claim the work is done. If the manifest
@@ -153,9 +153,16 @@ TestMode still uses the committed DB source. Staging remains a dev/verification
153
153
  - **Confirm the DB version:** `mcp_component_view` reads the live DB source directly (no staging, no compile
154
154
  cache), so it is the source of truth for "what was committed."
155
155
  - **Ask the CLI what is staged:** `remits-cli components status` lists this lane's staged entries,
156
- including staged fields, aliases, hashes, and TTLs. The default terminal output is concise; pass `--json` or
157
- `--verbose` when you need the full staged-entry payload. `remits-cli components clear` removes those entries
158
- when you intentionally want to fall back to DB source.
156
+ including staged fields, aliases, hashes, and TTLs. It also reports the other indexed lanes on the
157
+ account so you can see parallel work before assuming you are alone. The default terminal output is
158
+ concise; pass `--json` or `--verbose` when you need the full staged-entry payload.
159
+ - **Inspect another lane without impersonating it:** `remits-cli components lanes` lists every indexed
160
+ lane on the account; `remits-cli components entries --lane-id <id>` reads the authoritative staged
161
+ files for one lane. These are read-only review surfaces. They do not switch your workspace, clear
162
+ anything, or change what your own test/token/tool runs resolve.
163
+ - **Clear only your own lane:** `remits-cli components clear` removes entries when you intentionally want
164
+ to fall back to DB source. `--all` is scoped to the command's account/user/branch/workspace lane, not
165
+ every lane another agent may be using.
159
166
 
160
167
  ### Working alongside other agents: the staging WORKSPACE
161
168
 
@@ -198,9 +205,11 @@ commit write `ComponentVariant` overlays for a branch nobody subscribes to).
198
205
  - Every stage / test run / token / clear prints its `Staging lane:` — if a change seems to have had no
199
206
  effect, check that line FIRST. A mismatched lane resolves committed source, which looks identical to
200
207
  "the stage did not work".
201
- - `remits-cli components status` lists every lane staged on the branch AND every lane on the account, so
202
- you can see whether another agent is working alongside you. Each row names its world (trunk or variant
203
- branch), its overlay, its workset where known, and whether it is the SHARED lane.
208
+ - `remits-cli components status`, `components stage`, `test run`, and `token` include the current
209
+ account's lane context in their responses. A shared current lane is printed loudly; sibling lanes are
210
+ listed when they matter.
211
+ - `remits-cli components lanes` is the review view across users/branches/workspaces, and
212
+ `remits-cli components entries --lane-id <id>` is the drill-down into actual staged files.
204
213
  - `remits-cli components clear --all` is scoped to YOUR lane and never touches another agent's.
205
214
 
206
215
  ### A lane holds an OVERLAY; your workset is a different number
@@ -270,6 +270,22 @@ interchangeable:
270
270
  A missing answer is printed as `unknown`, never as `0`: "git could not answer" and "git says nothing
271
271
  changed" are different facts and only one of them is a number.
272
272
 
273
+ ##### Review lanes without changing them
274
+
275
+ `components status` is still the normal first read: it tells you which world this checkout resolves,
276
+ what this lane holds, and whether other account lanes are live. For review/debugging, use the dedicated
277
+ read-only surfaces:
278
+
279
+ ```bash
280
+ remits-cli components lanes
281
+ remits-cli components entries --lane-id <id>
282
+ ```
283
+
284
+ `components lanes` shows every indexed lane on the account across users, branches and workspaces.
285
+ `components entries` reads the authoritative staged files for one lane. It does not switch your
286
+ workspace, clear entries, or affect what your own runs resolve. Use it before assuming a shared lane is
287
+ safe to overwrite, and when reviewing another agent's workstream from the CLI.
288
+
273
289
  **THE STAGE-BEFORE-RUN RULE:** You MUST run `remits-cli components stage` after EVERY file edit and BEFORE any test run or verification. The platform executes whatever version is in the staging cache at the moment the test starts. If you edit a file and run a test without staging first, the test runs the OLD code — not your changes. This is the single most common mistake. Never skip staging. The sequence is always: **edit → stage → run**.
274
290
 
275
291
  This applies to: