@remits/remits-cli 0.1.132 → 0.1.135

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/index.js CHANGED
@@ -6,16 +6,16 @@
6
6
  - L22 Runtime Bootstrap And Shared State
7
7
  - L119 Sessions, Account Resolution, And Production Guards
8
8
  - L603 Local State, Workspaces, And Verification Evidence
9
- - L2155 Account Repos, Guide Sync, And Platform Repo
10
- - L2813 Component Discovery And HTTP Logging
11
- - L3389 Skill Delivery And TOC Resolution
12
- - L3699 Auth And Component Staging
13
- - L4312 Component Summaries, Status, And Sync Gates
14
- - L6375 Branches, Promotion, Commit, And Test Runs
15
- - L8036 Tokens, Tools, Verification, And Config
16
- - L9251 Service Dashboard And WebSocket Listener
17
- - L11367 Agent And Ticket Workflows
18
- - L14559 Help, Auto Update, And Command Dispatch
9
+ - L2474 Account Repos, Guide Sync, And Platform Repo
10
+ - L3282 Component Discovery And HTTP Logging
11
+ - L3923 Skill Delivery And TOC Resolution
12
+ - L4233 Auth And Component Staging
13
+ - L4848 Component Summaries, Status, And Sync Gates
14
+ - L6917 Branches, Promotion, Commit, And Test Runs
15
+ - L8600 Tokens, Tools, Verification, And Config
16
+ - L9980 Service Dashboard And WebSocket Listener
17
+ - L12101 Agent And Ticket Workflows
18
+ - L15573 Help, Auto Update, And Command Dispatch
19
19
  */
20
20
 
21
21
  /*
@@ -621,6 +621,7 @@ function localStatePaths(cwd) {
621
621
  actorDir,
622
622
  sharedDir: path.join(base, 'shared'),
623
623
  toolsDir: path.join(base, 'shared', 'tools'),
624
+ workstreamsDir: path.join(base, 'shared', 'workstreams'),
624
625
  sessionsDir: path.join(actorDir, 'sessions'),
625
626
  toolResponsesDir: path.join(actorDir, 'tool-responses'),
626
627
  verificationDir: path.join(actorDir, 'verification'),
@@ -924,6 +925,112 @@ function localStateSummary(cwd, flags = {}) {
924
925
  };
925
926
  }
926
927
 
928
+ function normalizeWorkstreamId(value) {
929
+ if (value === undefined || value === null) return null;
930
+ const trimmed = String(value).trim();
931
+ if (!trimmed) return null;
932
+ let normalized = trimmed
933
+ .toLowerCase()
934
+ .replace(/[^a-z0-9._-]/g, '-')
935
+ .replace(/-+/g, '-')
936
+ .replace(/^[-.]+/, '')
937
+ .replace(/[-.]+$/, '');
938
+ return normalized ? normalized.slice(0, 96).replace(/[-.]+$/, '') : null;
939
+ }
940
+
941
+ // The LOCAL proof-campaign id: the thing that groups several actor directories working one goal. It is not
942
+ // the same `workstream` a support ticket carries — that one is an account's own routing label, matched by
943
+ // `agent serve --serves`. They share a flag name and nothing else; keep the distinction in any text that
944
+ // mentions either.
945
+ function resolveWorkstreamId(cwd, flags = {}, context = {}) {
946
+ return normalizeWorkstreamId(flags.workstream || flags['workstream-id'] || process.env.REMITS_WORKSTREAM_ID ||
947
+ context.workstream || context.workspace || resolveWorkspace(cwd, flags));
948
+ }
949
+
950
+ const WORKSTREAM_ROLE_KEYS = ['repoAccountId', 'accountId', 'executionAccountId', 'targetAccountId',
951
+ 'branchName', 'componentBranch', 'workspace', 'dataMode'];
952
+
953
+ function workstreamRoleSignature(world) {
954
+ if (!world || typeof world !== 'object') return null;
955
+ const signature = {};
956
+ WORKSTREAM_ROLE_KEYS.forEach((key) => {
957
+ if (world[key] !== undefined && world[key] !== null) signature[key] = world[key];
958
+ });
959
+ return Object.keys(signature).length ? signature : null;
960
+ }
961
+
962
+ function workstreamWorldParts(world) {
963
+ const roles = world || {};
964
+ return [
965
+ roles.repoAccountId ? 'repo ' + roles.repoAccountId : null,
966
+ roles.accountId ? 'account ' + roles.accountId : null,
967
+ roles.executionAccountId ? 'exec ' + roles.executionAccountId : null,
968
+ roles.targetAccountId ? 'target ' + roles.targetAccountId : null,
969
+ roles.branchName ? 'branch ' + roles.branchName : null,
970
+ roles.componentBranch ? 'componentBranch ' + roles.componentBranch : null,
971
+ roles.workspace ? 'ws:' + roles.workspace : null,
972
+ roles.dataMode ? 'data ' + roles.dataMode : null
973
+ ].filter(Boolean);
974
+ }
975
+
976
+ function dedupeWorkstreamWorlds(worlds) {
977
+ const seen = new Set();
978
+ const out = [];
979
+ (worlds || []).forEach((world) => {
980
+ if (!world || !Object.keys(world).length) return;
981
+ const key = JSON.stringify(WORKSTREAM_ROLE_KEYS.map((k) => world[k] === undefined ? null : world[k]));
982
+ if (seen.has(key)) return;
983
+ seen.add(key);
984
+ out.push(world);
985
+ });
986
+ return out.slice(-12);
987
+ }
988
+
989
+ function workstreamFile(cwd, workstreamId) {
990
+ const paths = ensureLocalState(cwd);
991
+ ensureDir(paths.workstreamsDir);
992
+ return path.join(paths.workstreamsDir, normalizeWorkstreamId(workstreamId) + '.json');
993
+ }
994
+
995
+ function recordWorkstreamArtifact(cwd, flags = {}, artifact = {}) {
996
+ const workstreamId = resolveWorkstreamId(cwd, flags, artifact);
997
+ if (!workstreamId) return null;
998
+ const file = workstreamFile(cwd, workstreamId);
999
+ let existing = {};
1000
+ try {
1001
+ if (fs.existsSync(file)) existing = JSON.parse(fs.readFileSync(file, 'utf8')) || {};
1002
+ } catch (_) {
1003
+ existing = {};
1004
+ }
1005
+ const now = new Date().toISOString();
1006
+ const actor = localStatePaths(cwd).actor.id;
1007
+ const item = Object.assign({
1008
+ actor,
1009
+ recordedAt: now
1010
+ }, artifact || {});
1011
+ const artifacts = Array.isArray(existing.artifacts) ? existing.artifacts : [];
1012
+ const dedupeKey = [item.type, item.callId, item.envelopeId, item.packetId, item.path].filter(Boolean).join('|');
1013
+ const filtered = dedupeKey
1014
+ ? artifacts.filter((row) => [row.type, row.callId, row.envelopeId, row.packetId, row.path].filter(Boolean).join('|') !== dedupeKey)
1015
+ : artifacts;
1016
+ // The LAST world, never a merge of all of them. Merging produced a "World:" line that no single run ever
1017
+ // executed in — a leftover targetAccountId from one command printed next to the executionAccountId of
1018
+ // another. A campaign that legitimately spans worlds is reported as several worlds, not as one blend.
1019
+ const roles = workstreamRoleSignature(artifact.world) || existing.roles || {};
1020
+ const priorWorlds = Array.isArray(existing.worlds) ? existing.worlds : (existing.roles ? [existing.roles] : []);
1021
+ const worlds = dedupeWorkstreamWorlds(priorWorlds.concat([roles]));
1022
+ const next = Object.assign({}, existing, {
1023
+ workstreamId,
1024
+ updatedAt: now,
1025
+ actors: Array.from(new Set([].concat(existing.actors || [], actor))).sort(),
1026
+ roles,
1027
+ worlds,
1028
+ artifacts: filtered.concat([item]).slice(-200)
1029
+ });
1030
+ atomicWriteFile(file, JSON.stringify(next, null, 2));
1031
+ return file;
1032
+ }
1033
+
927
1034
  // ---------------------------------------------------------------------------------------------------
928
1035
  // Staging workspace
929
1036
  // ---------------------------------------------------------------------------------------------------
@@ -1463,6 +1570,40 @@ function writeActiveVerificationContexts(cwd, contexts) {
1463
1570
  atomicWriteFile(paths.activeVerificationContextsFile, JSON.stringify(contexts || {}, null, 2));
1464
1571
  }
1465
1572
 
1573
+ function describeVerificationWorld(context = {}) {
1574
+ return ['account ' + (context.accountId != null ? context.accountId : '?'),
1575
+ 'branch ' + (context.branchName || context.branch || '?'),
1576
+ 'workspace ' + (normalizeWorkspace(context.workspace) || 'shared'),
1577
+ context.dataMode ? 'data ' + context.dataMode : null].filter(Boolean).join(', ');
1578
+ }
1579
+
1580
+ /**
1581
+ * Active envelope pointers this actor holds for a DIFFERENT checkout world than the one running now. Used
1582
+ * only to explain a silence: an agent that started an envelope on `main` and then worked `--branch forked`
1583
+ * otherwise gets no envelope, no detach message, and no reason.
1584
+ */
1585
+ function otherWorldVerificationEnvelopes(cwd, context) {
1586
+ try {
1587
+ const currentKey = verificationContextKey(context);
1588
+ const contexts = readActiveVerificationContexts(cwd);
1589
+ return Object.keys(contexts)
1590
+ .filter((key) => key !== currentKey && !legacyVerificationContextKeys(currentKey).includes(key))
1591
+ .map((key) => {
1592
+ const entry = typeof contexts[key] === 'string' ? { envelopeId: contexts[key] } : contexts[key];
1593
+ if (!entry || !entry.envelopeId) return null;
1594
+ return { envelopeId: String(entry.envelopeId), updatedAt: entry.updatedAt, context: entry.context || {} };
1595
+ })
1596
+ .filter(Boolean)
1597
+ // Only pointers for THIS host and account: an envelope for another account is not a missing binding,
1598
+ // it is different work.
1599
+ .filter((entry) => String(entry.context.accountId) === String(context.accountId) &&
1600
+ normalizeBaseUrl(entry.context.baseUrl || '') === normalizeBaseUrl(context.baseUrl || ''))
1601
+ .sort((left, right) => String(right.updatedAt || '').localeCompare(String(left.updatedAt || '')));
1602
+ } catch (_) {
1603
+ return [];
1604
+ }
1605
+ }
1606
+
1466
1607
  function activeVerificationContext(cwd, flags = {}, session = null, accountId = null) {
1467
1608
  return {
1468
1609
  baseUrl: normalizeBaseUrl(flags['base-url'] || (session && session.baseUrl) || DEFAULT_BASE_URL),
@@ -1768,7 +1909,25 @@ async function verificationPreflight(api, cwd, session, accountId, flags = {}, p
1768
1909
  const label = options.command || 'this command';
1769
1910
  const context = activeVerificationContext(cwd, flags, session, accountId);
1770
1911
  const envelopeId = verificationEnvelopeIdForCommand(cwd, flags, context);
1771
- if (!envelopeId) return { envelopeId: null, verdict: 'none', evidenceFlags: flags };
1912
+ if (!envelopeId) {
1913
+ // An envelope is bound per CHECKOUT WORLD, so `verify start` on one branch/workspace and the work on
1914
+ // another leaves the agent with an active envelope it cannot see and a command that says nothing at all:
1915
+ // it did the work, attached no evidence, and reported neither. Silence is only correct when there is no
1916
+ // envelope to speak of.
1917
+ if (!options.quiet) {
1918
+ const orphan = otherWorldVerificationEnvelopes(cwd, context);
1919
+ if (orphan.length) {
1920
+ console.error('No verification envelope is active for this world (' + describeVerificationWorld(context) + '), ' +
1921
+ 'so ' + label + ' attaches no evidence. You have ' + orphan.length + ' active envelope(s) bound to other worlds:');
1922
+ orphan.slice(0, 3).forEach((entry) => {
1923
+ console.error(' - ' + entry.envelopeId + ' ' + describeVerificationWorld(entry.context));
1924
+ });
1925
+ console.error(' Start one here with `remits-cli verify start --summary "..."`, adopt one with ' +
1926
+ '`remits-cli verify use <id>`, or re-run in the world that envelope was started in.');
1927
+ }
1928
+ }
1929
+ return { envelopeId: null, verdict: 'none', evidenceFlags: flags };
1930
+ }
1772
1931
  const pinned = Object.assign({}, flags, { 'verify-envelope': envelopeId });
1773
1932
  let result;
1774
1933
  try {
@@ -1851,7 +2010,23 @@ async function appendVerificationPacket(api, cwd, session, accountId, flags, pac
1851
2010
  }, packet || {});
1852
2011
  finalPacket.envelopeId = envelopeId;
1853
2012
  stampPacketRepoAccount(finalPacket, accountId);
2013
+ // The workstream id travels WITH the evidence. Without it the local index and the platform's
2014
+ // `activity inspect --scope related` share no identifier, so two agents on one campaign — the case this
2015
+ // whole surface exists for — could never be shown the same set of work.
2016
+ const packetWorkstreamId = resolveWorkstreamId(cwd, flags, finalPacket.world || {});
2017
+ if (packetWorkstreamId) {
2018
+ if (!finalPacket.world || typeof finalPacket.world !== 'object') finalPacket.world = {};
2019
+ if (!finalPacket.world.workstreamId) finalPacket.world.workstreamId = packetWorkstreamId;
2020
+ }
1854
2021
  writeLocalVerificationPacket(cwd, envelopeId, finalPacket);
2022
+ recordWorkstreamArtifact(cwd, flags, {
2023
+ type: 'verification_packet',
2024
+ envelopeId,
2025
+ packetId: finalPacket.packetId,
2026
+ packetType: finalPacket.type,
2027
+ world: finalPacket.world || {},
2028
+ workspace: finalPacket.world && finalPacket.world.workspace
2029
+ });
1855
2030
  try {
1856
2031
  const response = await postVerificationCommand(api, cwd, session, accountId, 'packet', {
1857
2032
  envelopeId,
@@ -1934,6 +2109,107 @@ function printVerificationEnvelope(envelope, fallbackEnvelopeId) {
1934
2109
  }
1935
2110
  }
1936
2111
 
2112
+ function slimVerificationPacket(packet, options = {}) {
2113
+ const p = packet || {};
2114
+ const world = p.world || {};
2115
+ const hasSuccess = Object.prototype.hasOwnProperty.call(p, 'success');
2116
+ return {
2117
+ packetId: p.packetId || p.id || null,
2118
+ type: p.type || 'unknown',
2119
+ success: hasSuccess ? p.success !== false : options.defaultSuccess !== false,
2120
+ stale: Boolean(p.stale),
2121
+ claim: p.claim || p.summary || null,
2122
+ taskId: p.taskId || (p.testRun && p.testRun.taskId) || null,
2123
+ testName: p.testName || (p.testRun && p.testRun.testName) || null,
2124
+ accountId: world.accountId || p.accountId || null,
2125
+ dataMode: world.dataMode || p.dataMode || null,
2126
+ branchName: world.branchName || p.branchName || null,
2127
+ workspace: world.workspace || p.workspace || null,
2128
+ recordedAt: formatTime(p.recordedAt || p.createdAt || p.at || p.timestamp),
2129
+ message: p.message || p.error || null
2130
+ };
2131
+ }
2132
+
2133
+ function compactVerificationEnvelope(envelope, fallbackEnvelopeId) {
2134
+ const env = envelope || {};
2135
+ const evaluation = env.evaluation || {};
2136
+ const packets = Array.isArray(env.packets) ? env.packets : [];
2137
+ const failures = Array.isArray(evaluation.currentFailures)
2138
+ ? evaluation.currentFailures
2139
+ : packets.filter((packet) => packet && packet.success === false && !packet.stale);
2140
+ const stale = Array.isArray(evaluation.stale) ? evaluation.stale : packets.filter((packet) => packet && packet.stale);
2141
+ const requiredEvidence = ((env.acceptance || {}).requiredEvidence || evaluation.requiredEvidence || []);
2142
+ const latestByType = evaluation.latestByType || env.latestByType || {};
2143
+ const nextActions = [];
2144
+
2145
+ if (evaluation.noRequiredEvidence) {
2146
+ nextActions.push('Attach a manifest or required evidence contract; packets are context, not proof of completion.');
2147
+ }
2148
+ if (failures.length) {
2149
+ nextActions.push('Inspect and replace the current failing evidence packet(s).');
2150
+ }
2151
+ if (stale.length) {
2152
+ nextActions.push('Re-run stale evidence after the requirement/source change.');
2153
+ }
2154
+
2155
+ return {
2156
+ envelopeId: env.envelopeId || fallbackEnvelopeId || null,
2157
+ summary: env.summary || null,
2158
+ status: env.status || 'unknown',
2159
+ health: evaluation.health || null,
2160
+ acceptanceStatus: evaluation.acceptanceStatus || env.acceptanceStatus || null,
2161
+ requiredEvidence: {
2162
+ satisfiedCount: evaluation.satisfiedCount || 0,
2163
+ requiredCount: evaluation.requiredCount || requiredEvidence.length || 0,
2164
+ missingCount: evaluation.missingCount || 0,
2165
+ staleCount: evaluation.staleCount || stale.length || 0,
2166
+ noRequiredEvidence: Boolean(evaluation.noRequiredEvidence),
2167
+ declared: requiredEvidence.slice(0, 10).map((entry) => ({
2168
+ type: entry && entry.type || entry && entry.evidenceType || null,
2169
+ category: entry && entry.category || null,
2170
+ claim: entry && entry.claim || entry && entry.description || null
2171
+ }))
2172
+ },
2173
+ packets: {
2174
+ count: packets.length,
2175
+ failedCount: evaluation.failedCount || failures.length || 0,
2176
+ currentFailureCount: evaluation.currentFailureCount || failures.length || 0,
2177
+ lastPacketAt: formatTime(env.lastPacketAtMs || env.latestPacketAtMs || env.updatedAtMs),
2178
+ recent: packets.slice(-5).map(slimVerificationPacket)
2179
+ },
2180
+ latestByType,
2181
+ currentFailures: failures.slice(0, 10).map((packet) => slimVerificationPacket(packet, { defaultSuccess: false })),
2182
+ stale: stale.slice(0, 10).map(slimVerificationPacket),
2183
+ warnings: (env.warnings || []).slice(0, 10),
2184
+ nextActions
2185
+ };
2186
+ }
2187
+
2188
+ function printCompactVerificationEnvelope(compact) {
2189
+ console.log('Verification envelope:', compact.envelopeId || '(no id)');
2190
+ if (compact.summary) console.log('Summary:', compact.summary);
2191
+ console.log('Status:', compact.status + (compact.health ? ' health=' + compact.health : ''));
2192
+ if (compact.requiredEvidence.noRequiredEvidence) {
2193
+ console.log('Required evidence: none declared');
2194
+ } else {
2195
+ console.log('Required evidence:', compact.requiredEvidence.satisfiedCount + '/' + compact.requiredEvidence.requiredCount +
2196
+ (compact.requiredEvidence.staleCount ? ' stale=' + compact.requiredEvidence.staleCount : ''));
2197
+ }
2198
+ console.log('Packets:', compact.packets.count + ' current failures=' + compact.packets.currentFailureCount);
2199
+ if (compact.currentFailures.length) {
2200
+ console.log('Current failures:');
2201
+ compact.currentFailures.forEach((packet) => {
2202
+ console.log(' - ' + packet.type + ' ' + (packet.packetId || '') + (packet.claim ? ' - ' + packet.claim : '') +
2203
+ (packet.message ? ' (' + packet.message + ')' : ''));
2204
+ });
2205
+ }
2206
+ if (compact.nextActions.length) {
2207
+ console.log('Next actions:');
2208
+ compact.nextActions.forEach((action) => console.log(' - ' + action));
2209
+ }
2210
+ console.log('Full envelope: remits-cli verify show --json');
2211
+ }
2212
+
1937
2213
  function printAcceptanceWarnings(envelope) {
1938
2214
  const warnings = envelope && envelope.acceptance && Array.isArray(envelope.acceptance.warnings)
1939
2215
  ? envelope.acceptance.warnings
@@ -1996,16 +2272,35 @@ async function readVerificationPacketsForDiagnostics(api, cwd, session, accountI
1996
2272
  }
1997
2273
 
1998
2274
  function buildCommandWorld(response, fallback = {}) {
2275
+ if (response && response.world && typeof response.world === 'object') {
2276
+ return Object.assign({}, response.world, {
2277
+ host: response.world.host || fallback.host,
2278
+ dataMode: response.world.dataMode || response.dataMode || fallback.dataMode,
2279
+ branchName: response.world.branchName || response.branchName || fallback.branchName,
2280
+ workspace: response.world.workspace !== undefined ? response.world.workspace : fallback.workspace
2281
+ });
2282
+ }
1999
2283
  const source = (response && response.staging) || {};
2000
2284
  const resolution = (response && response.resolution) || {};
2001
2285
  const result = (response && response.result) || {};
2002
2286
  const laneHash = laneContentHashFrom(response) || fallback.laneContentHash || fallback.stagedOverlayHash;
2003
- const executionAccountId = response && (response.executionAccountId || result.accountId || source.executionAccountId || response.accountId) ||
2287
+ const addressedAccountId = response && (response.repoAccountId || response.checkoutAccountId || response.scopeRootAccountId ||
2288
+ response.accountId || response.ownerAccountId) ||
2289
+ fallback.repoAccountId || fallback.checkoutAccountId || fallback.scopeRootAccountId || fallback.addressedAccountId || fallback.accountId;
2290
+ const executionAccountId = response && (response.executionAccountId || result.accountId || source.executionAccountId) ||
2004
2291
  resolution.executionAccountId ||
2005
- fallback.executionAccountId;
2292
+ fallback.executionAccountId ||
2293
+ // Legacy/fallback callers historically supplied only `accountId`, and for all non-split worlds that
2294
+ // value is both repo and execution. Never let a top-level response.accountId (usually addressed/repo) be
2295
+ // the execution account unless the caller intentionally passed it as the fallback.
2296
+ fallback.accountId;
2006
2297
  return {
2007
- accountId: response && (response.accountId || response.ownerAccountId) || fallback.accountId,
2298
+ accountId: executionAccountId,
2299
+ repoAccountId: response && response.repoAccountId || fallback.repoAccountId || addressedAccountId,
2300
+ scopeRootAccountId: response && response.scopeRootAccountId || fallback.scopeRootAccountId,
2301
+ checkoutAccountId: response && response.checkoutAccountId || fallback.checkoutAccountId || addressedAccountId,
2008
2302
  executionAccountId,
2303
+ targetAccountId: response && response.targetAccountId || fallback.targetAccountId,
2009
2304
  addressedAccountId: resolution.addressedAccountId || fallback.addressedAccountId,
2010
2305
  host: fallback.host,
2011
2306
  dataMode: response && response.dataMode || fallback.dataMode,
@@ -2022,10 +2317,34 @@ function buildCommandWorld(response, fallback = {}) {
2022
2317
  fallback.componentBranch,
2023
2318
  variantBranchSource: response && (response.variantBranchSource || (response.variantWorld && response.variantWorld.variantBranchSource)) || fallback.variantBranchSource,
2024
2319
  variantId: resolution.variantId || fallback.variantId,
2320
+ componentSource: response && response.componentSource || fallback.componentSource,
2321
+ componentSignature: response && response.componentSignature || fallback.componentSignature,
2025
2322
  sourceLayer: fallback.sourceLayer || (source.testComponentSource === 'staged' ? 'staged' : undefined)
2026
2323
  };
2027
2324
  }
2028
2325
 
2326
+ function buildToolStatusWorld(data, stored = {}, fallback = {}) {
2327
+ const response = data && typeof data === 'object' ? data : {};
2328
+ const previous = stored && typeof stored === 'object' ? stored : {};
2329
+ return buildCommandWorld(response, {
2330
+ repoAccountId: response.repoAccountId || previous.repoAccountId || fallback.repoAccountId || previous.checkoutAccountId || previous.accountId || fallback.accountId,
2331
+ scopeRootAccountId: response.scopeRootAccountId || previous.scopeRootAccountId || fallback.scopeRootAccountId,
2332
+ checkoutAccountId: response.checkoutAccountId || previous.checkoutAccountId || fallback.checkoutAccountId,
2333
+ accountId: response.executionAccountId || previous.executionAccountId || fallback.executionAccountId || fallback.accountId,
2334
+ executionAccountId: response.executionAccountId || previous.executionAccountId || fallback.executionAccountId,
2335
+ targetAccountId: response.targetAccountId || previous.targetAccountId || fallback.targetAccountId,
2336
+ dataMode: response.dataMode || previous.dataMode || fallback.dataMode,
2337
+ branchName: response.branchName || previous.branchName || fallback.branchName,
2338
+ workspace: response.workspace !== undefined ? response.workspace : (previous.workspace !== undefined ? previous.workspace : fallback.workspace),
2339
+ host: fallback.host,
2340
+ componentSource: response.componentSource || previous.componentSource || fallback.componentSource,
2341
+ componentSignature: response.componentSignature || previous.componentSignature || fallback.componentSignature,
2342
+ componentBranch: response.componentBranch || previous.componentBranch || fallback.componentBranch,
2343
+ variantBranch: response.variantBranch || previous.variantBranch || fallback.variantBranch,
2344
+ variantBranchSource: response.variantBranchSource || previous.variantBranchSource || fallback.variantBranchSource
2345
+ });
2346
+ }
2347
+
2029
2348
  // A variant sync writes overlays on exactly one component branch; say so in the packet world, where a manifest's
2030
2349
  // componentBranch is compared. Without it no sync_mutation could satisfy a manifest that declares one.
2031
2350
  function syncPacketWorld(response, world, branchName) {
@@ -2344,6 +2663,120 @@ async function downloadAndSyncGuides(cwd, ctx) {
2344
2663
  return writeGuidePayloadIntoRepo(cwd, payload);
2345
2664
  }
2346
2665
 
2666
+ function guideSyncSummaryLine(summary) {
2667
+ if (!summary) {
2668
+ return 'guide sync skipped';
2669
+ }
2670
+ const repoCount = Number(summary.repoCount || 0);
2671
+ const updatedRepos = Number(summary.updatedRepos || 0);
2672
+ const updatedFiles = Number(summary.updatedFiles || 0);
2673
+ const skipped = (summary.skipped || []).length;
2674
+ return updatedRepos + '/' + repoCount + ' repo(s) updated, ' + updatedFiles +
2675
+ ' file(s) changed' + (skipped ? ', ' + skipped + ' skipped' : '');
2676
+ }
2677
+
2678
+ function guideSyncSessions(requestedBaseUrl = null) {
2679
+ return listSessionsFromStore(readAllSessions()).filter((session) => {
2680
+ if (!session || !session.token) return false;
2681
+ if (!requestedBaseUrl) return true;
2682
+ return normalizeBaseUrl(session.baseUrl || DEFAULT_BASE_URL) === normalizeBaseUrl(requestedBaseUrl);
2683
+ });
2684
+ }
2685
+
2686
+ function guideSessionForRepo(entry, sessions) {
2687
+ const accountId = Number(entry && entry.accountId);
2688
+ return sessions.find((session) => Number(session.accountId) === accountId) || sessions[0] || null;
2689
+ }
2690
+
2691
+ // Refresh every indexed account checkout from the authenticated platform guide payload. This is the
2692
+ // operator-level repair path for "agents are reading stale front-stage docs": it does not wait for each
2693
+ // checkout to happen to run a component command.
2694
+ async function syncGuidesAcrossIndexedRepos(options = {}) {
2695
+ if (options.rescan !== false) {
2696
+ try { discoverAccountRepos(); } catch (_) { /* use the current index */ }
2697
+ }
2698
+
2699
+ const sessions = guideSyncSessions(options.baseUrl || null);
2700
+ const entries = listAccountRepoEntries(readAccountRepoIndex())
2701
+ .sort((a, b) => Number(a.accountId) - Number(b.accountId));
2702
+ const result = {
2703
+ repoCount: entries.length,
2704
+ updatedRepos: 0,
2705
+ unchangedRepos: 0,
2706
+ updatedFiles: 0,
2707
+ removedFiles: 0,
2708
+ skipped: [],
2709
+ repos: []
2710
+ };
2711
+
2712
+ if (!sessions.length) {
2713
+ result.skipped.push({
2714
+ reason: 'no_authenticated_session',
2715
+ message: 'Run remits-cli auth before syncing guides.'
2716
+ });
2717
+ return result;
2718
+ }
2719
+
2720
+ for (const entry of entries) {
2721
+ const repo = {
2722
+ accountId: Number(entry.accountId),
2723
+ name: entry.name || '',
2724
+ directory: entry.directory,
2725
+ updated: [],
2726
+ removed: []
2727
+ };
2728
+ if (!entry.directory || !isAccountCheckout(entry.directory)) {
2729
+ repo.skipped = true;
2730
+ repo.reason = 'not_a_git_checkout';
2731
+ result.skipped.push(repo);
2732
+ result.repos.push(repo);
2733
+ continue;
2734
+ }
2735
+
2736
+ const session = guideSessionForRepo(entry, sessions);
2737
+ if (!session) {
2738
+ repo.skipped = true;
2739
+ repo.reason = 'no_session_for_repo';
2740
+ result.skipped.push(repo);
2741
+ result.repos.push(repo);
2742
+ continue;
2743
+ }
2744
+
2745
+ const baseUrl = normalizeBaseUrl(session.baseUrl || options.baseUrl || DEFAULT_BASE_URL);
2746
+ const summary = await downloadAndSyncGuides(entry.directory, {
2747
+ session,
2748
+ baseUrl,
2749
+ accountId: Number(entry.accountId) || session.accountId
2750
+ });
2751
+ if (!summary) {
2752
+ repo.skipped = true;
2753
+ repo.reason = 'guide_payload_unavailable';
2754
+ result.skipped.push(repo);
2755
+ result.repos.push(repo);
2756
+ continue;
2757
+ }
2758
+
2759
+ repo.updated = summary.updated || [];
2760
+ repo.removed = summary.removed || [];
2761
+ result.updatedFiles += repo.updated.length;
2762
+ result.removedFiles += repo.removed.length;
2763
+ if (repo.updated.length || repo.removed.length) {
2764
+ result.updatedRepos += 1;
2765
+ } else {
2766
+ result.unchangedRepos += 1;
2767
+ }
2768
+ result.repos.push(repo);
2769
+ }
2770
+
2771
+ appendGlobalActivityLog('guides', 'sync_all', {
2772
+ repoCount: result.repoCount,
2773
+ updatedRepos: result.updatedRepos,
2774
+ updatedFiles: result.updatedFiles,
2775
+ skipped: result.skipped.length
2776
+ });
2777
+ return result;
2778
+ }
2779
+
2347
2780
  // Ensure the current account repo has the latest guides. Authenticates first
2348
2781
  // (interactive terminals only) so guides are only ever delivered to users who
2349
2782
  // can authenticate to the platform.
@@ -2809,6 +3242,42 @@ function resolveSessionContext(cwd, flags) {
2809
3242
  };
2810
3243
  }
2811
3244
 
3245
+ function hasExplicitAccountIdFlag(flags) {
3246
+ const value = Number(flags && flags['account-id']);
3247
+ return Number.isFinite(value) && value > 0;
3248
+ }
3249
+
3250
+ function platformRepoEntryMatchesCwd(cwd) {
3251
+ const entry = getPlatformRepoEntry();
3252
+ if (!entry || !entry.directory || !cwd) return false;
3253
+ try {
3254
+ return path.resolve(entry.directory) === path.resolve(cwd);
3255
+ } catch (_) {
3256
+ return false;
3257
+ }
3258
+ }
3259
+
3260
+ function platformRepoAccountCommandProblem(cwd, flags, commandName, sessionContext) {
3261
+ if (hasExplicitAccountIdFlag(flags)) return null;
3262
+ try {
3263
+ if (loadAccountInfo(cwd)) return null;
3264
+ } catch (_) {
3265
+ // No account-info.json means the command would fall back to session state.
3266
+ }
3267
+ if (!looksLikePlatformRepo(cwd) && !platformRepoEntryMatchesCwd(cwd)) return null;
3268
+
3269
+ const account = sessionContext && sessionContext.accountId
3270
+ ? 'account ' + sessionContext.accountId
3271
+ : 'the last authenticated account';
3272
+ return 'Refusing ' + commandName + ' from the Remits platform repo. This checkout is back-stage platform source, not an account repository, so the command would target ' +
3273
+ account + ' from session state. Run it from that account repo, or pass --account-id <id> explicitly if you really intend to inspect that account from here.';
3274
+ }
3275
+
3276
+ function requireAccountRepoContext(cwd, flags, commandName, sessionContext) {
3277
+ const problem = platformRepoAccountCommandProblem(cwd, flags, commandName, sessionContext);
3278
+ if (problem) throw new Error(problem);
3279
+ }
3280
+
2812
3281
  /*
2813
3282
  ## Component Discovery And HTTP Logging
2814
3283
  */
@@ -3299,11 +3768,76 @@ function sandboxNetworkHint(err) {
3299
3768
  return ' Network access to ' + baseUrl + ' appears unavailable from the current sandbox. If remits-cli is being run by a local AI agent (for example Codex or Claude Code), retry the command with escalated permissions or outside the sandbox.';
3300
3769
  }
3301
3770
 
3771
+ // The tool inventory is ONE file per checkout, shared by every actor in it, and every refresh replaces it
3772
+ // wholesale. That is fine for one agent and wrong for several: `--as-account`, `--branch` and `--data-mode`
3773
+ // each produce a different inventory, so a second agent's refresh silently becomes the first agent's
3774
+ // "current tools". The file cannot be per-world without breaking the documented path, so instead: write it
3775
+ // atomically (a concurrent reader never sees a half-written file) and return the world it REPLACED, so the
3776
+ // caller can say out loud that it just overwrote somebody else's.
3777
+ function toolsSnapshotWorldSignature(snapshot) {
3778
+ if (!snapshot || typeof snapshot !== 'object') return null;
3779
+ return {
3780
+ accountId: snapshot.accountId,
3781
+ executionAccountId: snapshot.executionAccountId,
3782
+ branchName: snapshot.branchName,
3783
+ variantBranch: snapshot.variantBranch || null,
3784
+ dataMode: snapshot.dataMode
3785
+ };
3786
+ }
3787
+
3302
3788
  function saveToolsSnapshot(cwd, data) {
3303
3789
  const paths = ensureLocalState(cwd);
3304
3790
  const file = path.join(paths.toolsDir, 'tools.json');
3305
- fs.writeFileSync(file, JSON.stringify(data, null, 2));
3306
- return file;
3791
+ let previous = null;
3792
+ try {
3793
+ if (fs.existsSync(file)) previous = JSON.parse(fs.readFileSync(file, 'utf8'));
3794
+ } catch (_) {
3795
+ previous = null;
3796
+ }
3797
+ atomicWriteFile(file, JSON.stringify(data, null, 2));
3798
+ const before = toolsSnapshotWorldSignature(previous);
3799
+ const after = toolsSnapshotWorldSignature(data);
3800
+ const replaced = before && JSON.stringify(before) !== JSON.stringify(after) ? before : null;
3801
+ return { file, replaced };
3802
+ }
3803
+
3804
+ // The other half of the shared-snapshot problem: the writer is warned, but the agent that READS
3805
+ // .remits-cli/shared/tools/tools.json — to pick a tool name, or an input shape — gets no signal that the
3806
+ // file describes somebody else's account, branch or data lane. Answered at the moment it matters: the call.
3807
+ function staleToolsSnapshotWarning(cwd, world) {
3808
+ try {
3809
+ const file = path.join(localStatePaths(cwd).toolsDir, 'tools.json');
3810
+ if (!fs.existsSync(file)) return null;
3811
+ const snapshot = JSON.parse(fs.readFileSync(file, 'utf8'));
3812
+ const snapshotExec = snapshot.executionAccountId || snapshot.accountId;
3813
+ const mismatch = [];
3814
+ if (snapshotExec != null && String(snapshotExec) !== String(world.executionAccountId)) {
3815
+ mismatch.push('account ' + snapshotExec + ' (this call: ' + world.executionAccountId + ')');
3816
+ }
3817
+ if (snapshot.branchName && String(snapshot.branchName) !== String(world.branchName)) {
3818
+ mismatch.push('branch ' + snapshot.branchName + ' (this call: ' + world.branchName + ')');
3819
+ }
3820
+ if (snapshot.dataMode && String(snapshot.dataMode) !== String(world.dataMode)) {
3821
+ mismatch.push('data ' + snapshot.dataMode + ' (this call: ' + world.dataMode + ')');
3822
+ }
3823
+ if (!mismatch.length) return null;
3824
+ return {
3825
+ code: 'tools_snapshot_other_world',
3826
+ message: '.remits-cli/shared/tools/tools.json was last refreshed for ' + mismatch.join(', ') +
3827
+ '. It is one file per checkout, so it may be another agent\'s. Run `remits-cli tools` with your own ' +
3828
+ 'flags before trusting the tool list or an input schema read from it.'
3829
+ };
3830
+ } catch (_) {
3831
+ return null;
3832
+ }
3833
+ }
3834
+
3835
+ function describeToolsSnapshotWorld(world) {
3836
+ if (!world) return 'unknown world';
3837
+ return ['account ' + (world.executionAccountId || world.accountId || '?'),
3838
+ 'branch ' + (world.branchName || '?'),
3839
+ world.variantBranch ? "variant '" + world.variantBranch + "'" : null,
3840
+ 'data ' + (world.dataMode || '?')].filter(Boolean).join(', ');
3307
3841
  }
3308
3842
 
3309
3843
  async function loggedPost(api, cwd, endpoint, payload, options = {}) {
@@ -3843,14 +4377,15 @@ async function authCommand(flags) {
3843
4377
  dataMode: session.dataMode
3844
4378
  }).then((r) => r.data);
3845
4379
  if (toolsData && toolsData.success) {
3846
- const toolsFile = saveToolsSnapshot(cwd, {
4380
+ const toolsSnapshot = saveToolsSnapshot(cwd, {
3847
4381
  accountId: session.accountId,
4382
+ executionAccountId: session.accountId,
3848
4383
  branchName,
3849
4384
  dataMode: toolsData.dataMode || session.dataMode,
3850
4385
  fetchedAt: new Date().toISOString(),
3851
4386
  tools: toolsData.tools || []
3852
4387
  });
3853
- console.log('Tools cached at:', toolsFile);
4388
+ console.log('Tools cached at:', toolsSnapshot.file);
3854
4389
  }
3855
4390
  } catch (err) {
3856
4391
  console.log('Warning: failed to refresh tools after auth:', err.message);
@@ -3892,6 +4427,7 @@ async function pushComponentsCommand(flags) {
3892
4427
  const cwd = process.cwd();
3893
4428
  ensureLocalState(cwd);
3894
4429
  const sessionContext = resolveSessionContext(cwd, flags);
4430
+ requireAccountRepoContext(cwd, flags, 'components stage', sessionContext);
3895
4431
  const { session, accountId } = sessionContext;
3896
4432
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
3897
4433
  const branchName = flags.branch || currentBranch(cwd);
@@ -4015,7 +4551,7 @@ async function pushComponentsCommand(flags) {
4015
4551
  const api = buildAxios(baseUrl, session.token, stageTimeoutMs(components, flags));
4016
4552
  const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
4017
4553
  packetType: 'stage',
4018
- world: { accountId, dataMode, branchName, workspace }
4554
+ world: { repoAccountId: accountId, accountId, executionAccountId: accountId, dataMode, branchName, workspace }
4019
4555
  }, { command: 'components stage' });
4020
4556
  const stagePayload = {
4021
4557
  token: session.token,
@@ -4823,7 +5359,8 @@ function printBranchContext(response) {
4823
5359
  console.log(' commit writes: ' + ctx.commitWrites);
4824
5360
  if (Object.prototype.hasOwnProperty.call(ctx, 'lastSyncedSha')) {
4825
5361
  // null is "unknown" (never synced, expired, or the last sync had errors) — never "nothing landed".
4826
- console.log(' last platform sync of this branch: ' + (ctx.lastSyncedSha ? short(ctx.lastSyncedSha) : 'unknown'));
5362
+ const source = ctx.lastSyncedShaSource ? ' [' + ctx.lastSyncedShaSource + ']' : '';
5363
+ console.log(' last platform sync of this branch: ' + (ctx.lastSyncedSha ? short(ctx.lastSyncedSha) : 'unknown') + source);
4827
5364
  }
4828
5365
  if (ctx.repository) {
4829
5366
  console.log(' sync reads repository: ' + ctx.repository +
@@ -5014,6 +5551,7 @@ async function statusComponentsCommand(flags) {
5014
5551
  const cwd = process.cwd();
5015
5552
  ensureLocalState(cwd);
5016
5553
  const sessionContext = resolveSessionContext(cwd, flags);
5554
+ requireAccountRepoContext(cwd, flags, 'components status', sessionContext);
5017
5555
  const { session, accountId } = sessionContext;
5018
5556
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5019
5557
  const branchName = flags.branch || currentBranch(cwd);
@@ -5082,6 +5620,7 @@ async function lanesComponentsCommand(flags) {
5082
5620
  const cwd = process.cwd();
5083
5621
  ensureLocalState(cwd);
5084
5622
  const sessionContext = resolveSessionContext(cwd, flags);
5623
+ requireAccountRepoContext(cwd, flags, 'components lanes', sessionContext);
5085
5624
  const { session, accountId } = sessionContext;
5086
5625
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5087
5626
  const branchName = flags.branch || currentBranch(cwd);
@@ -5173,6 +5712,7 @@ async function entriesComponentsCommand(flags) {
5173
5712
  const cwd = process.cwd();
5174
5713
  ensureLocalState(cwd);
5175
5714
  const sessionContext = resolveSessionContext(cwd, flags);
5715
+ requireAccountRepoContext(cwd, flags, 'components entries', sessionContext);
5176
5716
  const { session, accountId } = sessionContext;
5177
5717
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5178
5718
  const branchName = flags.branch || currentBranch(cwd);
@@ -5216,6 +5756,7 @@ async function clearComponentsCommand(flags) {
5216
5756
  const cwd = process.cwd();
5217
5757
  ensureLocalState(cwd);
5218
5758
  const sessionContext = resolveSessionContext(cwd, flags);
5759
+ requireAccountRepoContext(cwd, flags, 'components clear', sessionContext);
5219
5760
  const { session, accountId } = sessionContext;
5220
5761
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5221
5762
  const branchName = flags.branch || currentBranch(cwd);
@@ -5275,6 +5816,7 @@ async function syncComponentsCommand(rawFlags) {
5275
5816
  const flags = safe ? expandSafeSyncFlags(rawFlags) : rawFlags;
5276
5817
  ensureLocalState(cwd);
5277
5818
  const sessionContext = resolveSessionContext(cwd, flags);
5819
+ requireAccountRepoContext(cwd, flags, 'components sync', sessionContext);
5278
5820
  const { session, accountId } = sessionContext;
5279
5821
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
5280
5822
  const branchName = flags.branch || currentBranch(cwd);
@@ -5291,7 +5833,7 @@ async function syncComponentsCommand(rawFlags) {
5291
5833
 
5292
5834
  const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
5293
5835
  packetType: dryRun || namesOnly ? 'sync_dry_run' : 'sync_mutation',
5294
- world: { accountId, dataMode, branchName, workspace }
5836
+ world: { repoAccountId: accountId, accountId, executionAccountId: accountId, dataMode, branchName, workspace }
5295
5837
  }, { command: 'components sync' });
5296
5838
 
5297
5839
  printProdDataBanner({
@@ -6382,6 +6924,7 @@ async function branchesComponentsCommand(flags) {
6382
6924
  const cwd = process.cwd();
6383
6925
  ensureLocalState(cwd);
6384
6926
  const sessionContext = resolveSessionContext(cwd, flags);
6927
+ requireAccountRepoContext(cwd, flags, 'components branches', sessionContext);
6385
6928
  const { session, accountId } = sessionContext;
6386
6929
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
6387
6930
  const api = buildAxios(baseUrl, session.token);
@@ -6579,6 +7122,7 @@ async function promotionComponentsCommand(flags) {
6579
7122
  const cwd = process.cwd();
6580
7123
  ensureLocalState(cwd);
6581
7124
  const sessionContext = resolvePromotionSessionContext(cwd, flags);
7125
+ requireAccountRepoContext(cwd, flags, 'components promotion', sessionContext);
6582
7126
  const { session, accountId } = sessionContext;
6583
7127
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
6584
7128
  const api = buildAxios(baseUrl, session.token);
@@ -6744,7 +7288,9 @@ function shortSignature(signature) {
6744
7288
  async function commitComponentsCommand(flags) {
6745
7289
  const cwd = process.cwd();
6746
7290
  ensureLocalState(cwd);
6747
- const { session, accountId } = resolveSessionContext(cwd, flags);
7291
+ const sessionContext = resolveSessionContext(cwd, flags);
7292
+ requireAccountRepoContext(cwd, flags, 'components commit', sessionContext);
7293
+ const { session, accountId } = sessionContext;
6748
7294
  const branchName = flags.branch || currentBranch(cwd);
6749
7295
  const commitMessage = String(flags.message || ('remits-cli commit sync ' + new Date().toISOString()));
6750
7296
  const allowEmpty = flags['allow-empty'] === true || flags['allow-empty'] === 'true';
@@ -6764,7 +7310,7 @@ async function commitComponentsCommand(flags) {
6764
7310
  buildAxios(flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL, session.token),
6765
7311
  cwd, session, accountId, Object.assign({}, flags, { branch: branchName }), {
6766
7312
  packetType: 'sync_mutation',
6767
- world: { accountId, dataMode: resolveDataMode(flags, session), branchName, workspace: resolveWorkspace(cwd, flags) }
7313
+ world: { repoAccountId: accountId, accountId, executionAccountId: accountId, dataMode: resolveDataMode(flags, session), branchName, workspace: resolveWorkspace(cwd, flags) }
6768
7314
  }, { command: 'components commit' });
6769
7315
 
6770
7316
  // Landing is serial and nothing can make it concurrent: `git add -A` sweeps a shared checkout, and the
@@ -7467,17 +8013,31 @@ function runWorldLines(world, context = {}) {
7467
8013
  if (!world || typeof world !== 'object') return [];
7468
8014
  const lines = ['World:'];
7469
8015
  if (context.host) lines.push(' host: ' + context.host);
8016
+ const scopeAccount = world.scopeRootAccountId || world.repoAccountId || world.checkoutAccountId || context.scopeRootAccountId;
8017
+ if (scopeAccount) lines.push(' scoped from: account ' + scopeAccount);
7470
8018
  const accounts = ['account ' + (world.executionAccountId || context.accountId || 'unknown')];
7471
8019
  if (world.checkoutAccountId && world.checkoutAccountId !== world.executionAccountId) accounts.push('checkout ' + world.checkoutAccountId);
7472
8020
  if (world.componentOwnerAccountId && world.componentOwnerAccountId !== world.executionAccountId) accounts.push('component owner ' + world.componentOwnerAccountId);
7473
8021
  lines.push(' runs as: ' + accounts.join(', '));
8022
+ if (world.targetAccountId && world.targetAccountId !== world.executionAccountId) {
8023
+ lines.push(' targets: account ' + world.targetAccountId);
8024
+ }
7474
8025
  lines.push(' data lane: ' + (world.dataMode || context.dataMode || 'unknown'));
7475
8026
  const contextVariantWorld = context.variantWorld && typeof context.variantWorld === 'object' ? context.variantWorld : {};
7476
8027
  const subscribedComponentBranch = world.subscribedComponentBranch || contextVariantWorld.subscribedComponentBranch;
7477
8028
  const componentBranch = world.componentBranch || subscribedComponentBranch || contextVariantWorld.componentBranch || world.variantBranch;
7478
- const branchSource = componentBranch && subscribedComponentBranch && String(componentBranch) === String(subscribedComponentBranch)
7479
- ? 'subscription'
7480
- : (world.componentBranchSource || contextVariantWorld.componentBranchSource || world.variantBranchSource || contextVariantWorld.variantBranchSource);
8029
+ // The platform owns this answer (remits.cli.CliVariantWorld). Reporting a locally re-derived source is how
8030
+ // a line ends up contradicting itself: an --variant-branch probe REPLACES the subscription, and a variant
8031
+ // branch's working tree decides the world before any edge is consulted, but both resolve to a branch the
8032
+ // account often also subscribes to — so "componentBranch == subscribedComponentBranch" cannot mean
8033
+ // "the subscription chose it". Only fill the source in when the platform reports the TRUNK case (the git
8034
+ // branch named no world, so the account's own subscription decided) or reports nothing at all.
8035
+ const resolvedBranchSource = world.componentBranchSource || contextVariantWorld.componentBranchSource ||
8036
+ world.variantBranchSource || contextVariantWorld.variantBranchSource;
8037
+ const subscriptionDecided = componentBranch && subscribedComponentBranch &&
8038
+ String(componentBranch) === String(subscribedComponentBranch) &&
8039
+ (!resolvedBranchSource || resolvedBranchSource === 'trunk' || resolvedBranchSource === 'no-branch');
8040
+ const branchSource = subscriptionDecided ? 'subscription' : resolvedBranchSource;
7481
8041
  const branchLabel = componentBranch
7482
8042
  ? "componentBranch=" + componentBranch + (branchSource ? ' via ' + branchSource : '')
7483
8043
  : (branchSource || '');
@@ -7502,6 +8062,9 @@ function runWorldLines(world, context = {}) {
7502
8062
  lines.push(' test component: ' + world.testComponentSource + (world.testComponentSignature ? ' ' + shortSignature(world.testComponentSignature) : '') +
7503
8063
  (world.testComponentBranch ? " variant '" + world.testComponentBranch + "'" : ''));
7504
8064
  }
8065
+ if (world.componentSource) {
8066
+ lines.push(' component: ' + world.componentSource + (world.componentSignature ? ' ' + shortSignature(world.componentSignature) : ''));
8067
+ }
7505
8068
  lines.push(...stagedMaskedByVariantLines(world.stagedMaskedByVariant, world.executionAccountId || context.accountId));
7506
8069
  return lines;
7507
8070
  }
@@ -7731,7 +8294,8 @@ async function testCommand(flags) {
7731
8294
  const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
7732
8295
  packetType: 'test_run',
7733
8296
  world: {
7734
- accountId,
8297
+ repoAccountId: accountId,
8298
+ accountId: asAccountId ? Number(asAccountId) : Number(accountId),
7735
8299
  executionAccountId: asAccountId ? Number(asAccountId) : Number(accountId),
7736
8300
  dataMode,
7737
8301
  branchName,
@@ -8061,7 +8625,8 @@ async function tokenCommand(flags) {
8061
8625
  const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
8062
8626
  packetType: 'token_inspect',
8063
8627
  world: {
8064
- accountId,
8628
+ repoAccountId: accountId,
8629
+ accountId: tokenAsAccountId ? Number(tokenAsAccountId) : Number(accountId),
8065
8630
  executionAccountId: tokenAsAccountId ? Number(tokenAsAccountId) : Number(accountId),
8066
8631
  dataMode,
8067
8632
  branchName,
@@ -8103,6 +8668,12 @@ async function tokenCommand(flags) {
8103
8668
  baseUrl: normalizeBaseUrl(baseUrl),
8104
8669
  tokenKey: data.tokenKey,
8105
8670
  accountId: data.accountId,
8671
+ executionAccountId: data.accountId,
8672
+ // The account the command was addressed to, i.e. the repo/checkout account. Distinct from `accountId`
8673
+ // (which account the URL EXECUTES as, moved by --as-account) and from
8674
+ // `resolution.componentOwnerAccountId` (whose components run). Three different accounts on a subscriber
8675
+ // token, and the response used to carry only one of them.
8676
+ repoAccountId: data.ownerAccountId,
8106
8677
  branchName: data.branchName,
8107
8678
  variantBranch: data.variantBranch,
8108
8679
  // Why the variant branch is (or is not) set — the platform's one rule, so a null is explainable.
@@ -8146,6 +8717,29 @@ async function tokenCommand(flags) {
8146
8717
  printSessionResolutionWarning(sessionContext);
8147
8718
  printLocalCommandContext(cwd, flags, { accountId: output.accountId || accountId, branchName, workspace: output.workspace, dataMode: output.dataMode || dataMode });
8148
8719
  printLocalStateWarnings(cwd, flags);
8720
+ // The same World block `tools`, `tool` and `test run` print. A token is the surface that hands a browser
8721
+ // a whole resolved world, and it was the one that made the reader dig it out of a JSON dump — including
8722
+ // the `resolution.summary` sentence that says, in words, which account's components the page will run.
8723
+ runWorldLines({
8724
+ accountId: output.accountId,
8725
+ executionAccountId: output.accountId,
8726
+ checkoutAccountId: output.repoAccountId,
8727
+ repoAccountId: output.repoAccountId,
8728
+ componentOwnerAccountId: output.resolution && output.resolution.componentOwnerAccountId,
8729
+ dataMode: output.dataMode || dataMode,
8730
+ branchName: output.branchName || branchName,
8731
+ workspace: output.workspace,
8732
+ stagingLane: output.stagingLane,
8733
+ componentBranch: output.variantWorld && output.variantWorld.componentBranch,
8734
+ subscribedComponentBranch: output.variantWorld && output.variantWorld.subscribedComponentBranch,
8735
+ variantBranch: output.variantBranch,
8736
+ variantBranchSource: output.variantBranchSource,
8737
+ resolves: output.variantWorld && output.variantWorld.resolves,
8738
+ stagedMaskedByVariant: output.stagedMaskedByVariant
8739
+ }, { host: normalizeBaseUrl(baseUrl) }).forEach((line) => console.log(line));
8740
+ if (output.resolution && output.resolution.summary) {
8741
+ console.log(' resolves as: ' + output.resolution.summary);
8742
+ }
8149
8743
  }
8150
8744
  console.log(JSON.stringify(output, null, 2));
8151
8745
 
@@ -8153,7 +8747,16 @@ async function tokenCommand(flags) {
8153
8747
  type: 'token_inspect',
8154
8748
  success: true,
8155
8749
  claim: 'Verification token minted',
8156
- world: buildCommandWorld(output, { accountId: output.accountId || accountId, dataMode: output.dataMode || dataMode, branchName, workspace: output.workspace, host: normalizeBaseUrl(baseUrl) }),
8750
+ world: buildCommandWorld(output, {
8751
+ repoAccountId: output.repoAccountId || accountId,
8752
+ checkoutAccountId: output.repoAccountId || accountId,
8753
+ accountId: output.executionAccountId || output.accountId || accountId,
8754
+ executionAccountId: output.executionAccountId || output.accountId || accountId,
8755
+ dataMode: output.dataMode || dataMode,
8756
+ branchName,
8757
+ workspace: output.workspace,
8758
+ host: normalizeBaseUrl(baseUrl)
8759
+ }),
8157
8760
  revision: collectVerificationSource(cwd, flags),
8158
8761
  browser: {
8159
8762
  tokenKeyHash: output.tokenKey ? sha256(String(output.tokenKey)) : null,
@@ -8222,6 +8825,7 @@ async function toolsCommand(flags) {
8222
8825
  const data = await loggedPost(api, cwd, '/cli/tools', {
8223
8826
  token: session.token,
8224
8827
  accountId,
8828
+ asAccountId: flags['as-account'] || flags['as-account-id'],
8225
8829
  variantBranch,
8226
8830
  branchName,
8227
8831
  workspace,
@@ -8232,12 +8836,17 @@ async function toolsCommand(flags) {
8232
8836
  throw new Error('Failed to fetch tools');
8233
8837
  }
8234
8838
 
8235
- const toolsFile = saveToolsSnapshot(cwd, {
8236
- accountId,
8839
+ const toolsSnapshot = saveToolsSnapshot(cwd, {
8840
+ accountId: data.accountId || accountId,
8841
+ repoAccountId: data.repoAccountId || accountId,
8842
+ checkoutAccountId: data.checkoutAccountId || accountId,
8843
+ executionAccountId: data.executionAccountId || data.accountId || accountId,
8237
8844
  baseUrl: normalizeBaseUrl(baseUrl),
8238
- branchName,
8845
+ branchName: data.branchName || branchName,
8239
8846
  variantBranch: data.variantBranch || variantBranch,
8240
8847
  dataMode: data.dataMode || dataMode,
8848
+ world: data.world,
8849
+ staging: data.staging,
8241
8850
  fetchedAt: new Date().toISOString(),
8242
8851
  tools: data.tools || []
8243
8852
  });
@@ -8245,10 +8854,17 @@ async function toolsCommand(flags) {
8245
8854
  printSessionResolutionWarning(sessionContext);
8246
8855
  printResolvedBaseUrl(baseUrl);
8247
8856
  console.log('Tools refreshed.');
8857
+ if (data.world) runWorldLines(data.world, { host: normalizeBaseUrl(baseUrl), gitHead: safeGitValue(cwd, 'git rev-parse HEAD') }).forEach((line) => console.log(line));
8248
8858
  console.log('Data mode:', data.dataMode || dataMode);
8249
8859
  if (data.variantBranch || variantBranch) console.log('Variant branch:', data.variantBranch || variantBranch);
8250
- console.log('Tools file:', toolsFile);
8860
+ console.log('Tools file:', toolsSnapshot.file);
8251
8861
  console.log('Tool count:', (data.tools || []).length);
8862
+ if (toolsSnapshot.replaced) {
8863
+ console.error('Shared snapshot warning: this refresh replaced the tool inventory for ' +
8864
+ describeToolsSnapshotWorld(toolsSnapshot.replaced) + '. ' +
8865
+ '.remits-cli/shared/tools/tools.json is one file per checkout, so another agent reading it now sees ' +
8866
+ 'YOUR world. Re-run `remits-cli tools` with your own flags before trusting it.');
8867
+ }
8252
8868
  }
8253
8869
 
8254
8870
  function parseToolInput(flags) {
@@ -8282,10 +8898,18 @@ function applyScopeFlags(input, flags) {
8282
8898
  merged.scope = normalized;
8283
8899
  }
8284
8900
 
8285
- const targetAccountId = flags && (flags['target-account-id'] || flags.targetAccountId);
8901
+ const targetAccountId = flags && (flags['target-account'] || flags.targetAccount || flags['target-account-id'] || flags.targetAccountId);
8286
8902
  if (targetAccountId && merged.targetAccountId === undefined) {
8287
8903
  merged.targetAccountId = String(targetAccountId).trim();
8288
8904
  }
8905
+ // ...and as `accountId`, because that is the key the platform's account-targeting tools actually read
8906
+ // (mcp_run_action resolves `input.accountId`, falling back to the RUN SCOPE when it is absent). Without
8907
+ // this, `--target-account 36` printed "targets: account 36" in the World block while the tool ran against
8908
+ // the scope account — a flag that reads like a target and silently is not. An explicit `accountId` in
8909
+ // --input still wins, like every other scope flag.
8910
+ if (targetAccountId && merged.accountId === undefined && merged.account_id === undefined) {
8911
+ merged.accountId = String(targetAccountId).trim();
8912
+ }
8289
8913
 
8290
8914
  const anchorAccountId = flags && (flags['anchor-account-id'] || flags.anchorAccountId);
8291
8915
  if (anchorAccountId && merged.anchorAccountId === undefined) {
@@ -8330,6 +8954,8 @@ async function toolCommand(flags) {
8330
8954
  const branchName = flags.branch || currentBranch(cwd);
8331
8955
  const dataMode = resolveDataMode(flags, session);
8332
8956
  const variantBranch = flags['variant-branch'];
8957
+ const asAccountId = flags['as-account'] || flags['as-account-id'] || flags.asAccountId;
8958
+ const targetAccountId = flags['target-account'] || flags.targetAccount || flags['target-account-id'] || flags.targetAccountId;
8333
8959
  const timeoutMs = parsePositiveInt(flags['timeout-ms'] || flags.timeoutMs, 60000);
8334
8960
  const callId = String(flags['call-id'] || flags.callId || crypto.randomUUID());
8335
8961
  const asyncMode = flagEnabled(flags.async) || flagEnabled(input.async);
@@ -8365,6 +8991,24 @@ async function toolCommand(flags) {
8365
8991
  data.responseFile = statusResponse.responseFile;
8366
8992
  data.sessionLog = sessionJsonlFile(cwd);
8367
8993
  data.localState = localStateSummary(cwd, flags);
8994
+ const statusBranchName = flags.branch || data.branchName || (stored && stored.branchName) || branchName;
8995
+ const statusWorkspace = data.workspace !== undefined ? data.workspace : resolveWorkspace(cwd, flags);
8996
+ const statusWorld = buildToolStatusWorld(data, stored, {
8997
+ repoAccountId: accountId,
8998
+ accountId: statusAccountId,
8999
+ checkoutAccountId: accountId,
9000
+ dataMode: data.dataMode || dataMode,
9001
+ branchName: statusBranchName,
9002
+ workspace: statusWorkspace,
9003
+ host: normalizeBaseUrl(baseUrl)
9004
+ });
9005
+ recordWorkstreamArtifact(cwd, flags, {
9006
+ type: 'tool_status',
9007
+ callId: requestedCallId,
9008
+ path: statusResponse.responseFile,
9009
+ world: statusWorld,
9010
+ workspace: data.workspace
9011
+ });
8368
9012
  // A completed run can still carry a tool-level refusal — see toolResultFailed.
8369
9013
  const polledFailed = data.toolSuccess === false || toolResultFailed(data.result);
8370
9014
  if (polledFailed) {
@@ -8374,13 +9018,13 @@ async function toolCommand(flags) {
8374
9018
  // Same question as the call that started it: a poll must not attach where the call itself would not.
8375
9019
  const statusVerification = await verificationPreflight(api, cwd, session, accountId, flags, {
8376
9020
  packetType: 'tool_call',
8377
- world: { accountId: statusAccountId, dataMode: data.dataMode || dataMode, branchName: flags.branch || (stored && stored.branchName) || branchName, workspace: resolveWorkspace(cwd, flags) }
9021
+ world: statusWorld
8378
9022
  }, { command: 'tool status', neverRefuse: true, quiet: jsonOutput });
8379
9023
  await appendVerificationPacket(api, cwd, session, accountId, statusVerification.evidenceFlags, {
8380
9024
  type: 'tool_call',
8381
9025
  success: data.status !== 'failed' && !polledFailed,
8382
9026
  claim: 'Tool status ' + requestedCallId,
8383
- world: buildCommandWorld(data, { accountId: statusAccountId, dataMode: data.dataMode || dataMode, branchName: flags.branch || (stored && stored.branchName) || branchName, host: normalizeBaseUrl(baseUrl) }),
9027
+ world: statusWorld,
8384
9028
  revision: collectVerificationSource(cwd, flags),
8385
9029
  evidenceCategories: ['tool_call'],
8386
9030
  rawRefs: { toolResponsePath: statusResponse.responseFile, callId: requestedCallId },
@@ -8395,6 +9039,7 @@ async function toolCommand(flags) {
8395
9039
  printLocalCommandContext(cwd, flags, { accountId: statusAccountId, branchName: flags.branch || (stored && stored.branchName) || branchName, dataMode: data.dataMode || dataMode });
8396
9040
  printLocalStateWarnings(cwd, flags);
8397
9041
  console.log('Tool call status:', data.status);
9042
+ if (data.world) runWorldLines(data.world, { host: normalizeBaseUrl(baseUrl) }).forEach((line) => console.log(line));
8398
9043
  console.log('Call ID:', requestedCallId);
8399
9044
  console.log('Data mode:', data.dataMode || dataMode);
8400
9045
  if (data.threadGroupingId) console.log('Thread grouping ID:', data.threadGroupingId);
@@ -8417,15 +9062,49 @@ async function toolCommand(flags) {
8417
9062
  });
8418
9063
 
8419
9064
  const toolWorkspace = resolveWorkspace(cwd, flags);
9065
+ const legacyInputAccountId = input && (input.accountId || input.account_id);
9066
+ // A STRUCTURED warning, not only a printed one: agents run with --json, and `input.accountId` no longer
9067
+ // decides the execution account, so the one signal that the command's component resolution belongs to a
9068
+ // different account than the tool's data must survive into the payload the agent actually reads.
9069
+ const toolWarnings = [];
9070
+ if (legacyInputAccountId && !asAccountId && !targetAccountId && String(legacyInputAccountId) !== String(accountId)) {
9071
+ toolWarnings.push({
9072
+ code: 'legacy_input_account_id',
9073
+ inputAccountId: String(legacyInputAccountId),
9074
+ scopeAccountId: String(accountId),
9075
+ message: 'input.accountId=' + legacyInputAccountId +
9076
+ ' is treated as a tool target while the command stays scoped from account ' + accountId +
9077
+ '. Use --as-account ' + legacyInputAccountId + ' for execution-account semantics, or --target-account ' + legacyInputAccountId + ' to make the target explicit.'
9078
+ });
9079
+ }
9080
+ const snapshotWarning = staleToolsSnapshotWarning(cwd, {
9081
+ executionAccountId: asAccountId || accountId,
9082
+ branchName,
9083
+ dataMode
9084
+ });
9085
+ if (snapshotWarning) toolWarnings.push(snapshotWarning);
9086
+ // stderr always: it never corrupts --json stdout, and suppressing it there hid the warning from exactly
9087
+ // the callers that cannot see the human output.
9088
+ toolWarnings.forEach((warning) => console.error('Warning: ' + warning.message));
8420
9089
  const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
8421
9090
  packetType: 'tool_call',
8422
- world: { accountId, dataMode, branchName, workspace: toolWorkspace }
9091
+ world: {
9092
+ repoAccountId: accountId,
9093
+ accountId: asAccountId || accountId,
9094
+ executionAccountId: asAccountId || accountId,
9095
+ targetAccountId: targetAccountId || legacyInputAccountId,
9096
+ dataMode,
9097
+ branchName,
9098
+ workspace: toolWorkspace
9099
+ }
8423
9100
  }, { command: 'tool --name ' + String(toolName) });
8424
9101
 
8425
9102
  const response = await loggedPost(api, cwd, '/cli/tool', {
8426
9103
  token: session.token,
8427
9104
  accountId,
8428
9105
  accountIdExplicit,
9106
+ asAccountId,
9107
+ targetAccountId: targetAccountId || undefined,
8429
9108
  // The repo/session account, sent as a FIRST-CLASS value rather than left to be inferred from
8430
9109
  // `accountId`: a read tool may reinterpret its own `input.accountId`, and the scope root — what bounds
8431
9110
  // what the call may discover — has to survive that. The server validates it; it is never authorization.
@@ -8455,6 +9134,15 @@ async function toolCommand(flags) {
8455
9134
  data.responseFile = response.responseFile;
8456
9135
  data.sessionLog = sessionJsonlFile(cwd);
8457
9136
  data.localState = localStateSummary(cwd, flags);
9137
+ if (toolWarnings.length) data.warnings = (data.warnings || []).concat(toolWarnings);
9138
+ recordWorkstreamArtifact(cwd, flags, {
9139
+ type: asyncMode ? 'tool_async_start' : 'tool_call',
9140
+ callId,
9141
+ path: response.responseFile,
9142
+ tool: String(toolName),
9143
+ world: buildCommandWorld(data, { repoAccountId: accountId, accountId: data.executionAccountId || asAccountId || accountId, dataMode: data.dataMode || dataMode, branchName, workspace: toolWorkspace, host: normalizeBaseUrl(baseUrl) }),
9144
+ workspace: data.workspace || toolWorkspace
9145
+ });
8458
9146
  if (toolFailureMessage && !data.toolMessage) data.toolMessage = toolFailureMessage;
8459
9147
 
8460
9148
  let evidenceData = data;
@@ -8467,6 +9155,7 @@ async function toolCommand(flags) {
8467
9155
  printLocalStateWarnings(cwd, flags);
8468
9156
  console.log(asyncMode ? 'Tool call started.'
8469
9157
  : (toolFailed ? 'Tool call FAILED — the tool ran and returned an error.' : 'Tool call succeeded.'));
9158
+ if (data.world) runWorldLines(data.world, { host: normalizeBaseUrl(baseUrl), gitHead: safeGitValue(cwd, 'git rev-parse HEAD') }).forEach((line) => console.log(line));
8470
9159
  if (toolFailed && toolFailureMessage) console.log('Tool error:', toolFailureMessage);
8471
9160
  console.log('Call ID:', callId);
8472
9161
  console.log('Data mode:', data.dataMode || dataMode);
@@ -8499,10 +9188,26 @@ async function toolCommand(flags) {
8499
9188
 
8500
9189
  finalStatus.data.responseFile = finalStatus.responseFile;
8501
9190
  finalStatus.data.sessionLog = sessionJsonlFile(cwd);
9191
+ recordWorkstreamArtifact(cwd, flags, {
9192
+ type: 'tool_async_final',
9193
+ callId,
9194
+ path: finalStatus.responseFile,
9195
+ tool: String(toolName),
9196
+ world: buildCommandWorld(finalStatus.data, { repoAccountId: accountId, accountId: finalStatus.data.executionAccountId || asAccountId || accountId, dataMode: finalStatus.data.dataMode || dataMode, branchName: finalStatus.data.branchName || branchName, workspace: toolWorkspace, host: normalizeBaseUrl(baseUrl) }),
9197
+ workspace: finalStatus.data.workspace || toolWorkspace
9198
+ });
8502
9199
  evidenceData = finalStatus.data;
8503
9200
  evidenceResponseFile = finalStatus.responseFile;
8504
9201
  if (!jsonOutput) {
8505
9202
  console.log('Final status:', finalStatus.data.status);
9203
+ if (finalStatus.data.world) {
9204
+ runWorldLines(finalStatus.data.world, { host: normalizeBaseUrl(baseUrl), gitHead: safeGitValue(cwd, 'git rev-parse HEAD') }).forEach((line) => console.log(line));
9205
+ const startWorldText = JSON.stringify(data.world || {});
9206
+ const finalWorldText = JSON.stringify(finalStatus.data.world || {});
9207
+ if (data.world && startWorldText !== finalWorldText) {
9208
+ console.log('World changed since start: inspect the final response file for the exact resolved execution world.');
9209
+ }
9210
+ }
8506
9211
  printToolResponseFileLine('Tool response file', finalStatus.responseFile);
8507
9212
  }
8508
9213
  if (finalStatus.data.status !== 'completed') {
@@ -8516,7 +9221,20 @@ async function toolCommand(flags) {
8516
9221
  success: asyncMode && !waitForAsync ? null : (!toolFailed && (!asyncMode || process.exitCode !== 1)),
8517
9222
  pending: asyncMode && !waitForAsync,
8518
9223
  claim: 'Tool call ' + String(toolName),
8519
- world: buildCommandWorld(evidenceData, { accountId: evidenceData.accountId || accountId, dataMode: evidenceData.dataMode || dataMode, branchName: evidenceData.branchName || branchName, workspace: resolveWorkspace(cwd, flags), host: normalizeBaseUrl(baseUrl), sourceLayer: evidenceData.componentSource }),
9224
+ world: buildCommandWorld(evidenceData, {
9225
+ accountId: evidenceData.executionAccountId || asAccountId || evidenceData.accountId || accountId,
9226
+ repoAccountId: accountId,
9227
+ checkoutAccountId: accountId,
9228
+ executionAccountId: evidenceData.executionAccountId || asAccountId || accountId,
9229
+ targetAccountId: evidenceData.targetAccountId || targetAccountId || legacyInputAccountId,
9230
+ dataMode: evidenceData.dataMode || dataMode,
9231
+ branchName: evidenceData.branchName || branchName,
9232
+ workspace: resolveWorkspace(cwd, flags),
9233
+ host: normalizeBaseUrl(baseUrl),
9234
+ componentSource: evidenceData.componentSource,
9235
+ componentSignature: evidenceData.componentSignature,
9236
+ sourceLayer: evidenceData.componentSource
9237
+ }),
8520
9238
  revision: Object.assign(collectVerificationSource(cwd, flags), {
8521
9239
  compileSignatures: evidenceData.componentSignature ? [evidenceData.componentSignature] : []
8522
9240
  }),
@@ -8916,7 +9634,11 @@ async function verifyCommand(flags, subcommand) {
8916
9634
  return response;
8917
9635
  }
8918
9636
 
8919
- const envelopeId = flags.envelope || flags['verify-envelope'] || flags.id || readActiveVerificationEnvelope(cwd, activeContext);
9637
+ const positionalEnvelopeId = flags._ && flags._[2];
9638
+ const showLikeSubcommands = ['show', 'read', 'get', 'packets', 'status', 'report'];
9639
+ const explicitEnvelopeId = flags.envelope || flags['verify-envelope'] || flags.id ||
9640
+ (showLikeSubcommands.includes(sub) ? positionalEnvelopeId : null);
9641
+ const envelopeId = explicitEnvelopeId || readActiveVerificationEnvelope(cwd, activeContext);
8920
9642
  if (!envelopeId) {
8921
9643
  throw new Error('No verification envelope selected. Run `remits-cli verify start --summary "..."` or pass --envelope <id>.');
8922
9644
  }
@@ -8945,11 +9667,18 @@ async function verifyCommand(flags, subcommand) {
8945
9667
  if (sub === 'show' || sub === 'read' || sub === 'get' || sub === 'packets') {
8946
9668
  const response = await postVerificationCommand(api, cwd, session, accountId, 'show', { envelopeId });
8947
9669
  if (response.envelope) writeLocalVerificationEnvelope(cwd, response.envelope);
9670
+ const envelope = response.envelope || {};
9671
+ const compact = flagEnabled(flags.summary) || flagEnabled(flags.compact);
8948
9672
  if (flagEnabled(flags.json)) {
8949
- console.log(JSON.stringify(response.envelope || response, null, 2));
8950
- return response.envelope || response;
9673
+ const payload = compact ? compactVerificationEnvelope(envelope, envelopeId) : (response.envelope || response);
9674
+ console.log(JSON.stringify(payload, null, 2));
9675
+ return payload;
9676
+ }
9677
+ if (compact) {
9678
+ const payload = compactVerificationEnvelope(envelope, envelopeId);
9679
+ printCompactVerificationEnvelope(payload);
9680
+ return payload;
8951
9681
  }
8952
- const envelope = response.envelope || {};
8953
9682
  printVerificationEnvelope(envelope, envelopeId);
8954
9683
  return envelope;
8955
9684
  }
@@ -9068,7 +9797,7 @@ async function verifyCommand(flags, subcommand) {
9068
9797
  // report would list as wrong-world evidence on every status check.
9069
9798
  const statusVerification = await verificationPreflight(api, cwd, session, accountId, { 'verify-envelope': envelopeId }, {
9070
9799
  packetType: 'component_status',
9071
- world: { accountId, dataMode, branchName, workspace }
9800
+ world: { repoAccountId: accountId, accountId, executionAccountId: accountId, dataMode, branchName, workspace }
9072
9801
  }, { command: 'verify status', quiet: true, neverRefuse: true });
9073
9802
  const componentStatus = await loggedPost(api, cwd, '/cli/components', {
9074
9803
  token: session.token,
@@ -10437,6 +11166,7 @@ function renderDashboardHtml(snapshot) {
10437
11166
  '<div class="action-row">' +
10438
11167
  '<button class="action-btn" data-action="export-issues">Export Issue Bundle</button>' +
10439
11168
  '<button class="action-btn" data-action="rescan-repos">Rescan Account Repos</button>' +
11169
+ '<button class="action-btn" data-action="sync-guides">Update Guides In All Repos</button>' +
10440
11170
  '<button class="action-btn" data-action="reconnect-websockets">Reconnect Websockets</button>' +
10441
11171
  '<button class="action-btn" data-action="refresh-agents">Refresh Agents</button>' +
10442
11172
  '</div>' +
@@ -10509,7 +11239,7 @@ function renderDashboardHtml(snapshot) {
10509
11239
  'summaryItem("Available Agents",String(agentList.length)),' +
10510
11240
  'summaryItem("Routed Tickets",String(ticketCounts.routed||0))' +
10511
11241
  '].join("");}' +
10512
- 'function renderLastAction(state){const target=document.getElementById("last-action");if(!state.lastAction){target.className="empty";target.textContent="No dashboard actions have been triggered yet.";return;}target.className="summary-item";target.innerHTML=\'<span class="k">Last Action</span><span class="v">\'+escapeHtml(state.lastAction.action||"unknown")+\'</span><div class="muted" style="margin-top:8px;">\'+escapeHtml(formatTime(state.lastAction.at))+\'</div>\'+(state.lastAction.bundlePath?\'<div class="muted" style="margin-top:8px;word-break:break-word;"><strong>Bundle:</strong> \'+escapeHtml(state.lastAction.bundlePath)+\'</div>\':"");}' +
11242
+ 'function renderLastAction(state){const target=document.getElementById("last-action");if(!state.lastAction){target.className="empty";target.textContent="No dashboard actions have been triggered yet.";return;}target.className="summary-item";target.innerHTML=\'<span class="k">Last Action</span><span class="v">\'+escapeHtml(state.lastAction.action||"unknown")+\'</span><div class="muted" style="margin-top:8px;">\'+escapeHtml(formatTime(state.lastAction.at))+\'</div>\'+(state.lastAction.summary?\'<div class="muted" style="margin-top:8px;">\'+escapeHtml(state.lastAction.summary)+\'</div>\':"")+(state.lastAction.bundlePath?\'<div class="muted" style="margin-top:8px;word-break:break-word;"><strong>Bundle:</strong> \'+escapeHtml(state.lastAction.bundlePath)+\'</div>\':"");}' +
10513
11243
  'function renderIssues(state){const target=document.getElementById("issues-section");const issues=state.issues||[];if(!issues.length){target.innerHTML=\'<div class="empty">No automatic issue signals are currently detected. If something still feels wrong, you can still click "Export Issue Bundle" to prepare a package for a Remits system admin.</div>\';return;}target.innerHTML=issues.map(function(issue,index){const tone=issue.severity==="bad"?"bad":"warn";const meta=badge(issue.severity==="bad"?"critical":"warning",tone)+\' <span class="muted">\'+escapeHtml(issue.scope+(issue.repoAccountId?(" account "+issue.repoAccountId):""))+\'</span>\';const evidence=(issue.evidence||[]).map(function(line){return "<li>"+escapeHtml(line)+"</li>";}).join("");const body=\'<div class="stack"><div class="muted">\'+escapeHtml(issue.summary||"")+\'</div><div class="mini-card"><strong>Recommendation</strong>\'+escapeHtml(issue.recommendation||"") + \'</div><div><strong style="display:block;margin-bottom:8px;">Evidence</strong><ul style="margin:0;padding-left:18px;display:grid;gap:8px;">\'+(evidence||"<li>No evidence lines captured.</li>")+\'</ul></div></div>\';return detailsBlock("issue:"+issue.id,(index+1)+". "+issue.title,meta,body,index===0);}).join("");}' +
10514
11244
  'function renderSupportTickets(state){const target=document.getElementById("support-ticket-section");const support=state.supportTickets||{};const parts=support.compartments||[];const errors=support.errors||[];let html="";if(errors.length){html+=detailsBlock("support:errors","Queue fetch errors",badge(String(errors.length)+" error(s)","warn"),codeBlock("support-errors",JSON.stringify(errors,null,2),"log"),false);}if(!parts.length&&!errors.length){target.innerHTML=html+\'<div class="empty">No authenticated support-ticket queue is available yet.</div>\';return;}html+=parts.map(function(part,pi){const lane=(part.dataMode||"prod");const meta=badge(lane+" lane",lane==="test"?"warn":"ok")+\' \'+badge(String(part.open||0)+" open","ok")+\' <span class="muted">\'+escapeHtml(String(part.queueCount||0))+\' account queue(s)\'+(part.overlappingQueuesDropped?", "+part.overlappingQueuesDropped+" overlapping dropped":"")+\'</span>\';const caveat=part.scopeUnknown?\'<div class="mini-card"><strong>Note</strong>A queue did not report which accounts it scanned, so this total may count a ticket more than once.</div>\':"";const cards=\'<div class="mini-grid"><div class="mini-card"><strong>Open</strong>\'+escapeHtml(String(part.open||0))+\'</div><div class="mini-card"><strong>Routed To An Agent</strong>\'+escapeHtml(String(part.routed||0))+\'</div><div class="mini-card"><strong>Unrouted</strong>\'+escapeHtml(String(part.unrouted||0))+\'</div><div class="mini-card"><strong>Awaiting A Reply</strong>\'+escapeHtml(String(part.awaitingReplyOnPage||0))+\' <span class="muted">of \'+escapeHtml(String((part.tickets||[]).length))+\' shown</span></div></div>\'+caveat;const rows=(part.tickets||[]).length?(part.tickets||[]).map(function(ticket,index){const routed=!!ticket.routedAgentId;const waiting=!!ticket.awaitingResponseSince;const meta2=badge(ticket.priority||"priority",ticket.priority==="critical"||ticket.priority==="high"?"bad":"warn")+\' \'+badge(ticket.status||"status",ticket.status==="open"?"ok":"warn")+\' \'+badge(routed?("routed: "+(ticket.routedAgentLabel||ticket.routedAgentId)):"unrouted",routed?"ok":"warn")+(waiting?\' \'+badge("awaiting reply","warn"):"")+\' <span class="muted">\'+escapeHtml(ticket.accountName||ticket.accountId||"unknown account")+\'</span>\';return detailsBlock("support-ticket:"+pi+":"+ticket.ticketId,String(ticket.ticketId||"ticket")+" "+(ticket.subject||""),meta2,supportTicketDetails(state,ticket,"support-ticket:"+pi+":"+index),false);}).join(""):\'<div class="empty">No open support tickets in this compartment.</div>\';return detailsBlock("compartment:"+part.compartment,part.baseUrl||part.compartment,meta,cards+\'<div class="accordion">\'+rows+\'</div>\',pi===0);}).join("");html+=\'<div class="muted" style="margin-top:10px;">Each platform and data lane is counted separately. A localhost test-lane queue is full of suite fixtures and is not comparable to a production queue — summing them is what made this panel report 583 open tickets against a production queue of 24.</div>\';target.innerHTML=html;}' +
10515
11245
  'function supportTicketDetails(state,ticket,key){const agents=((state.agents||{}).registered)||[];const ticketAccountId=String(ticket.accountId||"");const eligible=agents.filter(function(a){if(ticket.baseUrl&&a.baseUrl&&a.baseUrl!==ticket.baseUrl)return false;if(ticket.dataMode&&a.dataMode&&a.dataMode!==ticket.dataMode)return false;return (a.accounts||[]).some(function(acc){return String(acc.accountId)===ticketAccountId||acc.routingScope==="self_and_descendants";});});const options=[\'<option value="">- unrouted -</option>\'].concat(eligible.map(function(a){const sel=a.agentId===ticket.routedAgentId?" selected":"";return \'<option value="\'+escapeHtml(a.agentId)+\'"\'+sel+\'>\'+escapeHtml((a.label||a.userName||a.agentId)+" - "+(a.state||"idle")+(a.activity?" ("+a.activity+")":""))+\'</option>\';})).join("");const routeControl=eligible.length?(\'<div class="mini-card"><strong>Route To Agent</strong><div style="display:flex;gap:8px;margin-top:6px;"><select data-route-ticket="\'+escapeHtml(String(ticket.ticketId||""))+\'" data-route-account="\'+escapeHtml(ticketAccountId)+\'" data-route-base="\'+escapeHtml(ticket.baseUrl||"")+\'" data-route-mode="\'+escapeHtml(ticket.dataMode||"")+\'" style="flex:1;min-width:0;">\'+options+\'</select><button class="action-btn" data-action="route-ticket" data-ticket="\'+escapeHtml(String(ticket.ticketId||""))+\'">Route</button></div></div>\'):\'<div class="mini-card"><strong>Route To Agent</strong>No registered agent covers this account.</div>\';const lane=ticket.dataMode||"prod";const planning=[ticket.workstream?("Workstream "+ticket.workstream):"",ticket.plannedIn?("Planned "+ticket.plannedIn):"",ticket.boardStage?("Board "+ticket.boardStage):"",ticket.rank!==undefined&&ticket.rank!==null&&ticket.rank!==""?("Rank "+ticket.rank):"",ticket.size?("Size "+ticket.size):"",ticket.blockedBy?("Blocked by "+ticket.blockedBy):""].filter(Boolean).join(" / ");const command="remits-cli ticket read --ticket "+String(ticket.ticketId||"")+" --data-mode "+lane+"\\nremits-cli ticket accept --ticket "+String(ticket.ticketId||"")+" --data-mode "+lane;return \'<div class="stack"><div class="mini-grid"><div class="mini-card"><strong>Ticket</strong>\'+escapeHtml(ticket.ticketId||"n/a")+\'</div><div class="mini-card"><strong>Account</strong>\'+escapeHtml((ticket.accountName||"n/a")+" ("+(ticket.accountId||"n/a")+")")+\'</div><div class="mini-card"><strong>Type</strong>\'+escapeHtml(ticket.type||"n/a")+\'</div><div class="mini-card"><strong>Owned By</strong>\'+escapeHtml(ticket.assignedTo||"unassigned")+\'</div><div class="mini-card"><strong>Routed To</strong>\'+escapeHtml(ticket.routedAgentLabel||ticket.routedAgentId||"nobody")+\'</div><div class="mini-card"><strong>Implementation</strong>\'+escapeHtml((ticket.implementationAccountName||"n/a")+" ("+(ticket.implementationAccountId||"n/a")+")")+\'</div><div class="mini-card"><strong>Planning</strong>\'+escapeHtml(planning||"No planning fields")+\'</div></div>\'+routeControl+\'<div class="muted">\'+escapeHtml(ticket.affectedComponent||"No affected component recorded.")+\'</div>\'+codeBlock(key+":cmd",command)+\'</div>\';}' +
@@ -10900,6 +11630,10 @@ function startDashboardServer(preferredPort) {
10900
11630
  } else if (action === 'rescan-repos') {
10901
11631
  recordDiscoverySummary(discoverAccountRepos());
10902
11632
  supportTicketSnapshotCache = { fetchedAtMs: 0, value: null };
11633
+ } else if (action === 'sync-guides') {
11634
+ actionResult = await syncGuidesAcrossIndexedRepos({ rescan: true });
11635
+ runtimeState.lastAction.summary = guideSyncSummaryLine(actionResult);
11636
+ recordDiscoverySummary(discoverAccountRepos());
10903
11637
  } else if (action === 'refresh-agents') {
10904
11638
  agentSnapshotCache = { fetchedAtMs: 0, value: null };
10905
11639
  supportTicketSnapshotCache = { fetchedAtMs: 0, value: null };
@@ -11997,6 +12731,11 @@ function resolveAgentId(flags, options = {}) {
11997
12731
  * agent is silently useless for support: it registers, appears online, and can never be routed a
11998
12732
  * production ticket. Ask for `--data-mode test` when you want a fixture agent.
11999
12733
  */
12734
+ /**
12735
+ * The host and lane an agent command runs against: the flag, then this agent's own prior registration, then
12736
+ * the PRODUCTION platform and lane. That last default is deliberate — `agent register` joins the support
12737
+ * fleet, and a fleet member on a test lane appears online and can never be routed a real ticket.
12738
+ */
12000
12739
  function agentDefaults(flags, entry) {
12001
12740
  return {
12002
12741
  baseUrl: normalizeBaseUrl(
@@ -12009,10 +12748,29 @@ function agentDefaults(flags, entry) {
12009
12748
  function agentSessionFor(flags, entry) {
12010
12749
  const cwd = (entry && entry.cwd) || process.cwd();
12011
12750
  const defaults = agentDefaults(flags, entry);
12012
- const context = resolveSessionContext(cwd, Object.assign({}, flags, {
12013
- 'base-url': defaults.baseUrl,
12014
- 'data-mode': defaults.dataMode
12015
- }));
12751
+ const pinnedByCaller = Boolean((flags && flags['base-url']) || (entry && entry.baseUrl));
12752
+ let context;
12753
+ try {
12754
+ context = resolveSessionContext(cwd, Object.assign({}, flags, {
12755
+ 'base-url': defaults.baseUrl,
12756
+ 'data-mode': defaults.dataMode
12757
+ }));
12758
+ } catch (err) {
12759
+ // The prod default is intentional, and invisible: every OTHER command in this checkout follows the
12760
+ // active session, so "not authenticated at <some prod url>" reads as a broken session rather than as
12761
+ // "this command deliberately went somewhere else". Say which host it chose and why.
12762
+ if (!pinnedByCaller) {
12763
+ const local = readSession({}) || {};
12764
+ const refusal = new Error('Agent commands join the SUPPORT FLEET, which lives on the production ' +
12765
+ 'platform, so this one targeted ' + defaults.baseUrl + ' — not the session this checkout uses' +
12766
+ (local.baseUrl ? ' (' + normalizeBaseUrl(local.baseUrl) + ')' : '') + '. ' +
12767
+ 'Authenticate there with `remits-cli auth --base-url ' + defaults.baseUrl + '`, or point this ' +
12768
+ 'command at another platform with `--base-url <url>`.');
12769
+ refusal.cause = err;
12770
+ throw refusal;
12771
+ }
12772
+ throw err;
12773
+ }
12016
12774
  if (!context.session || !context.session.token) {
12017
12775
  throw new Error('No authenticated session for ' + defaults.baseUrl +
12018
12776
  '. Run: remits-cli auth --base-url ' + defaults.baseUrl);
@@ -12593,7 +13351,7 @@ async function postAgentStatus(context, agentId, update) {
12593
13351
  * the same thing. Only the local supervisor can add machine-local repository hints; the worker owns the
12594
13352
  * final grounding step, including creating or choosing a git worktree before it edits.
12595
13353
  */
12596
- function spawnTicketWorker(context, ctx, ticket) {
13354
+ async function spawnTicketWorker(context, ctx, ticket) {
12597
13355
  const { spawn } = require('child_process');
12598
13356
  const agentId = ctx.agentId;
12599
13357
  const ticketId = String(ticket.id || ticket.ticketId);
@@ -12612,6 +13370,32 @@ function spawnTicketWorker(context, ctx, ticket) {
12612
13370
  const targetBranch = repositoryWork ? ticketTargetBranch(ticket, baseWorkingDirectory) : gitBranchNameInDirectory(baseWorkingDirectory);
12613
13371
  const suggestedWorktree = repositoryWork && baseWorkingDirectory ? ticketWorktreePath(baseWorkingDirectory, ticketId) : null;
12614
13372
  const workingDirectory = baseWorkingDirectory;
13373
+ if (workingDirectory) {
13374
+ try {
13375
+ const guideSummary = await downloadAndSyncGuides(workingDirectory, {
13376
+ session: context.session,
13377
+ baseUrl: context.baseUrl,
13378
+ accountId: Number(ticket.accountId) || context.accountId
13379
+ });
13380
+ if (guideSummary && ((guideSummary.updated || []).length || (guideSummary.removed || []).length)) {
13381
+ appendGlobalActivityLog('guides', 'worker_repo_synced', {
13382
+ agentId,
13383
+ ticketId,
13384
+ accountId: Number(ticket.accountId) || null,
13385
+ directory: workingDirectory,
13386
+ updated: (guideSummary.updated || []).length,
13387
+ removed: (guideSummary.removed || []).length
13388
+ });
13389
+ }
13390
+ } catch (err) {
13391
+ appendGlobalActivityLog('guides', 'worker_repo_sync_failed', {
13392
+ agentId,
13393
+ ticketId,
13394
+ directory: workingDirectory,
13395
+ error: describeError(err)
13396
+ }, 'error');
13397
+ }
13398
+ }
12615
13399
  // The brief is rendered server-side without the local path (the platform cannot know it), so the
12616
13400
  // resolved local repository hint is appended here. When nothing resolved, the server-rendered brief already
12617
13401
  // tells the worker to find it rather than guess — do not paper over that with a default.
@@ -12642,6 +13426,7 @@ function spawnTicketWorker(context, ctx, ticket) {
12642
13426
  'git -C ' + shellQuote(workingDirectory) + ' worktree add --force ' + shellQuote(suggestedWorktree) +
12643
13427
  (targetBranch ? ' ' + targetBranch : ' <branch>') + '\n' +
12644
13428
  'cd ' + shellQuote(suggestedWorktree) + '\n' +
13429
+ 'remits-cli guides sync\n' +
12645
13430
  (targetBranch
12646
13431
  ? 'git pull --ff-only origin ' + shellQuote(targetBranch) + '\n' +
12647
13432
  'git rev-list --left-right --count HEAD...origin/' + shellQuote(targetBranch) + ' # must print: 0 0\n'
@@ -13015,7 +13800,7 @@ async function agentHeartbeatDaemonCommand(flags) {
13015
13800
  if (!ticketId || inFlight.has(ticketId)) {
13016
13801
  continue;
13017
13802
  }
13018
- const started = spawnTicketWorker(context, ctx, ticket);
13803
+ const started = await spawnTicketWorker(context, ctx, ticket);
13019
13804
  inFlight.set(ticketId, { pid: started.pid, accountId: ticket.accountId });
13020
13805
  started.done.then(() => {
13021
13806
  inFlight.delete(ticketId);
@@ -13864,14 +14649,24 @@ async function activityInspectCommand(flags) {
13864
14649
  dataMode: resolveDataMode(flags, session),
13865
14650
  userId: flags['user-id'] || flags.userId || flags['cli-user-id'],
13866
14651
  userEmail: flags['user-email'] || flags.email,
14652
+ workspace: flags.workspace,
14653
+ branchName: flags.branch || flags['branch-name'],
14654
+ variantBranch: flags['variant-branch'],
14655
+ // The local proof-campaign id, so the platform-side related view and `workstream status` can be asked
14656
+ // the same question. Never defaulted from the workspace here: an unasked-for filter that silently
14657
+ // narrows an investigation is worse than no filter.
14658
+ workstreamId: normalizeWorkstreamId(flags.workstream || flags['workstream-id']),
13867
14659
  limit: flags.limit || flags.max || 20,
13868
14660
  envelopeLimit: flags['envelope-limit'] || flags.envelopeLimit
13869
14661
  }).then((r) => r.data);
13870
14662
 
13871
14663
  if (!response.success) throw new Error(response.message || 'Activity inspection failed');
13872
14664
  if (flagEnabled(flags.json)) {
13873
- console.log(JSON.stringify(response, null, 2));
13874
- return response;
14665
+ const payload = (flagEnabled(flags.summary) || flagEnabled(flags.compact))
14666
+ ? compactActivityInspect(response)
14667
+ : response;
14668
+ console.log(JSON.stringify(payload, null, 2));
14669
+ return payload;
13875
14670
  }
13876
14671
 
13877
14672
  printSessionResolutionWarning(sessionContext);
@@ -13880,14 +14675,134 @@ async function activityInspectCommand(flags) {
13880
14675
  return response;
13881
14676
  }
13882
14677
 
14678
+ function smellSeverityRank(smell) {
14679
+ const severity = String((smell && smell.severity) || 'info').toLowerCase();
14680
+ if (severity === 'critical') return 0;
14681
+ if (severity === 'high') return 1;
14682
+ if (severity === 'medium') return 2;
14683
+ if (severity === 'low') return 3;
14684
+ return 4;
14685
+ }
14686
+
14687
+ function sortedActivitySmells(smells) {
14688
+ return (Array.isArray(smells) ? smells.slice() : []).sort((left, right) => {
14689
+ const severity = smellSeverityRank(left) - smellSeverityRank(right);
14690
+ if (severity) return severity;
14691
+ return String(right.updatedAt || right.at || '').localeCompare(String(left.updatedAt || left.at || ''));
14692
+ });
14693
+ }
14694
+
14695
+ function slimActivitySmell(smell) {
14696
+ const s = smell || {};
14697
+ return {
14698
+ severity: s.severity || 'info',
14699
+ type: s.type || 'signal',
14700
+ title: s.title || s.envelopeSummary || s.testName || null,
14701
+ summary: s.summary || null,
14702
+ status: s.status || s.health || null,
14703
+ currentFailureCount: s.currentFailureCount == null ? null : s.currentFailureCount,
14704
+ accountId: s.accountId || null,
14705
+ laneId: s.laneId || null,
14706
+ envelopeId: s.envelopeId || null,
14707
+ taskId: s.taskId || null,
14708
+ testName: s.testName || null,
14709
+ branchName: s.branchName || null,
14710
+ workspace: s.workspace || null,
14711
+ updatedAt: formatTime(s.updatedAtMs || s.updatedAt || s.at || s.createdAt)
14712
+ };
14713
+ }
14714
+
14715
+ function compactActivityInspect(response) {
14716
+ const counts = response.counts || {};
14717
+ const smells = sortedActivitySmells(response.smells);
14718
+ const lanes = Array.isArray(response.lanes) ? response.lanes : [];
14719
+ const envelopes = Array.isArray(response.envelopes) ? response.envelopes : [];
14720
+ const testRuns = Array.isArray(response.testRuns) ? response.testRuns : [];
14721
+ const nextActions = smells
14722
+ .filter((smell) => smellSeverityRank(smell) <= 2)
14723
+ .slice(0, 5)
14724
+ .map((smell) => {
14725
+ const title = smell.title || smell.envelopeSummary || smell.testName;
14726
+ return (smell.type || 'signal') + (title ? ' (' + title + ')' : '') + ': ' + (smell.summary || 'inspect this signal');
14727
+ });
14728
+
14729
+ return {
14730
+ success: response.success,
14731
+ accountId: response.accountId,
14732
+ accountName: response.accountName || null,
14733
+ scope: response.scope || 'self',
14734
+ dataMode: response.dataMode,
14735
+ accounts: response.accountIds || [],
14736
+ counts: {
14737
+ agents: counts.agents || 0,
14738
+ lanes: counts.lanes || lanes.length || 0,
14739
+ verificationEnvelopes: counts.verificationEnvelopes || envelopes.length || 0,
14740
+ testRuns: counts.testRuns || testRuns.length || 0,
14741
+ corpusResults: counts.corpusResults || 0,
14742
+ smells: smells.length
14743
+ },
14744
+ topSmells: smells.slice(0, 10).map(slimActivitySmell),
14745
+ currentLanes: lanes.slice(0, 10).map((lane) => ({
14746
+ laneId: lane.laneId || null,
14747
+ accountId: lane.accountId || null,
14748
+ branchName: lane.branchName || null,
14749
+ workspace: lane.workspace || null,
14750
+ stagedCount: lane.stagedCountAsOf || lane.stagedCount || 0,
14751
+ worksetCount: lane.worksetCountAsOf == null ? null : lane.worksetCountAsOf,
14752
+ userEmail: lane.userEmail || null,
14753
+ expiresInSeconds: lane.expiresInSeconds == null ? null : lane.expiresInSeconds
14754
+ })),
14755
+ recentEnvelopes: envelopes.slice(0, 8).map((env) => ({
14756
+ envelopeId: env.envelopeId || null,
14757
+ status: env.status || 'unknown',
14758
+ summary: env.summary || null,
14759
+ packetCount: env.packetCount || 0,
14760
+ currentFailureCount: env.currentFailureCount || 0,
14761
+ health: env.health || null,
14762
+ updatedAt: formatTime(env.updatedAtMs || env.lastPacketAtMs || env.createdAt)
14763
+ })),
14764
+ recentTestRuns: testRuns.slice(0, 8).map((run) => ({
14765
+ taskId: run.taskId || null,
14766
+ testName: run.testName || null,
14767
+ passed: run.passed || 0,
14768
+ total: run.total || 0,
14769
+ branchName: run.branchName || null,
14770
+ workspace: run.workspace || null,
14771
+ ai: corpusAiLabel(run),
14772
+ completedAt: formatTime(run.completedAt || run.startedAt)
14773
+ })),
14774
+ nextActions
14775
+ };
14776
+ }
14777
+
13883
14778
  function printActivityInspectSummary(response) {
13884
14779
  const subject = response.subjectUser
13885
14780
  ? ' for ' + (response.subjectUser.email || response.subjectUser.name || ('user ' + response.subjectUser.id))
13886
14781
  : '';
13887
14782
  console.log('Activity inspection' + subject + ' — ' + response.dataMode + ' lane');
14783
+ // A scope of several hundred accounts printed as one line is not a scope statement, it is a wall. Say how
14784
+ // many, show the head, and point at --json for the rest.
14785
+ const scopeIds = response.accountIds || [];
14786
+ const scopeIdText = scopeIds.length > 12
14787
+ ? scopeIds.slice(0, 12).join(', ') + ' … (+' + (scopeIds.length - 12) + ' more, --json for all)'
14788
+ : (scopeIds.join(', ') || String(response.accountId));
13888
14789
  console.log('Scope: ' + (response.accountName || ('account ' + response.accountId)) +
13889
14790
  ' (' + response.accountId + '), ' + (response.scope || 'self') +
13890
- ' · accounts ' + ((response.accountIds || []).join(', ') || response.accountId));
14791
+ ' · ' + (scopeIds.length || 1) + ' account(s): ' + scopeIdText);
14792
+ if (response.filters && (response.filters.workspace || response.filters.branchName || response.filters.workstreamId)) {
14793
+ console.log('Filters: ' + [
14794
+ response.filters.workspace ? 'workspace ' + response.filters.workspace : null,
14795
+ response.filters.branchName ? 'branch ' + response.filters.branchName : null,
14796
+ response.filters.workstreamId ? 'workstream ' + response.filters.workstreamId : null
14797
+ ].filter(Boolean).join(' · ') + ' (evidence below is narrowed to it)');
14798
+ }
14799
+ if (Array.isArray(response.relatedAccounts) && response.relatedAccounts.length) {
14800
+ const related = response.relatedAccounts;
14801
+ const shown = related.slice(0, 12).map((row) =>
14802
+ row.id + ' ' + (row.name || '') + (row.role ? ' (' + row.role + ')' : '')).join('; ');
14803
+ console.log('Related accounts: ' + shown +
14804
+ (related.length > 12 ? '; … (+' + (related.length - 12) + ' more, --json for all)' : ''));
14805
+ }
13891
14806
  const counts = response.counts || {};
13892
14807
  console.log('Counts: ' +
13893
14808
  (counts.agents || 0) + ' agent(s), ' +
@@ -13896,12 +14811,12 @@ function printActivityInspectSummary(response) {
13896
14811
  (counts.testRuns || 0) + ' test run(s), ' +
13897
14812
  (counts.corpusResults || 0) + ' corpus result(s)');
13898
14813
 
13899
- const smells = response.smells || [];
14814
+ const smells = sortedActivitySmells(response.smells);
13900
14815
  console.log('');
13901
14816
  if (!smells.length) {
13902
14817
  console.log('Smells: none detected in this slice.');
13903
14818
  } else {
13904
- console.log('Smells (' + smells.length + '):');
14819
+ console.log('Top smells (' + smells.length + '):');
13905
14820
  smells.slice(0, 20).forEach((smell) => {
13906
14821
  console.log(' [' + (smell.severity || 'info') + '] ' + (smell.type || 'signal') + ': ' + smell.summary);
13907
14822
  const pivots = [
@@ -13916,6 +14831,33 @@ function printActivityInspectSummary(response) {
13916
14831
  if (pivots.length) console.log(' ' + pivots.join(' · '));
13917
14832
  });
13918
14833
  if (smells.length > 20) console.log(' ...' + (smells.length - 20) + ' more; rerun with --json');
14834
+ const actions = compactActivityInspect(response).nextActions;
14835
+ if (actions.length) {
14836
+ console.log('');
14837
+ console.log('Next actions:');
14838
+ actions.forEach((action) => console.log(' - ' + action));
14839
+ }
14840
+ }
14841
+
14842
+ // "2 agent(s)" with no names is the shape of the question, not the answer: an agent asking who else is in
14843
+ // this repo right now could read the count and still have to go find them. The payload already carries the
14844
+ // directory, branch, workspace and last-seen of each.
14845
+ const liveAgents = response.agents || [];
14846
+ if (liveAgents.length) {
14847
+ console.log('');
14848
+ console.log('Live agents here:');
14849
+ liveAgents.slice(0, 10).forEach((agent) => {
14850
+ const who = agent.label || agent.userEmail || agent.agentId || 'unknown agent';
14851
+ const where = [agent.branch ? 'branch ' + agent.branch : null,
14852
+ 'workspace ' + (agent.workspace || 'shared'),
14853
+ agent.dataMode ? 'data ' + agent.dataMode : null].filter(Boolean).join(' · ');
14854
+ const seen = agent.lastSeenAtMs ? ' · seen ' + formatTime(agent.lastSeenAtMs) : '';
14855
+ console.log(' - ' + who + ' [' + (agent.state || 'unknown') + ']' +
14856
+ (agent.source === 'implicit' ? ' (implicit)' : '') + seen);
14857
+ console.log(' ' + where);
14858
+ if (agent.workingDirectory) console.log(' ' + agent.workingDirectory);
14859
+ });
14860
+ if (liveAgents.length > 10) console.log(' ...' + (liveAgents.length - 10) + ' more agent(s)');
13919
14861
  }
13920
14862
 
13921
14863
  const lanes = response.lanes || [];
@@ -13978,9 +14920,81 @@ function printActivityInspectSummary(response) {
13978
14920
  });
13979
14921
  }
13980
14922
  console.log('');
14923
+ console.log('Compact JSON: remits-cli activity inspect --summary --json');
13981
14924
  console.log('Full structured payload: remits-cli activity inspect --json');
13982
14925
  }
13983
14926
 
14927
+ function readWorkstreamIndex(cwd, workstreamId) {
14928
+ const id = normalizeWorkstreamId(workstreamId);
14929
+ if (!id) return null;
14930
+ const file = workstreamFile(cwd, id);
14931
+ if (!fs.existsSync(file)) return null;
14932
+ try {
14933
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
14934
+ } catch (_) {
14935
+ return null;
14936
+ }
14937
+ }
14938
+
14939
+ function listWorkstreamIndexes(cwd) {
14940
+ const paths = ensureLocalState(cwd);
14941
+ if (!fs.existsSync(paths.workstreamsDir)) return [];
14942
+ return fs.readdirSync(paths.workstreamsDir, { withFileTypes: true })
14943
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
14944
+ .map((entry) => readWorkstreamIndex(cwd, entry.name.replace(/\.json$/, '')))
14945
+ .filter(Boolean)
14946
+ .sort((a, b) => String(b.updatedAt || '').localeCompare(String(a.updatedAt || '')));
14947
+ }
14948
+
14949
+ async function workstreamCommand(flags, subcommand) {
14950
+ const cwd = process.cwd();
14951
+ ensureLocalState(cwd);
14952
+ const action = subcommand || 'status';
14953
+ if (action !== 'status') throw new Error('Unknown workstream subcommand: ' + action);
14954
+ const requested = resolveWorkstreamId(cwd, flags, { workspace: flags.workspace });
14955
+ let rows = requested ? [readWorkstreamIndex(cwd, requested)].filter(Boolean) : listWorkstreamIndexes(cwd);
14956
+ if (flags.workspace) {
14957
+ const normalizedWorkspace = normalizeWorkspace(flags.workspace);
14958
+ rows = rows.filter((row) => {
14959
+ if (normalizeWorkspace(row.roles && row.roles.workspace) === normalizedWorkspace) return true;
14960
+ return (row.artifacts || []).some((artifact) => normalizeWorkspace(artifact.workspace || (artifact.world && artifact.world.workspace)) === normalizedWorkspace);
14961
+ });
14962
+ }
14963
+ if (flagEnabled(flags.json)) {
14964
+ console.log(JSON.stringify({ success: true, workstreams: rows }, null, 2));
14965
+ return;
14966
+ }
14967
+ if (!rows.length) {
14968
+ console.log('No local workstream index found.');
14969
+ console.log('Start one implicitly with a workspace, or pass --workstream <id> / REMITS_WORKSTREAM_ID while running tool or verify commands.');
14970
+ return;
14971
+ }
14972
+ rows.forEach((row) => {
14973
+ console.log('Workstream:', row.workstreamId);
14974
+ console.log('Updated:', row.updatedAt || 'unknown');
14975
+ console.log('Actors:', (row.actors || []).join(', ') || 'none recorded');
14976
+ const roleParts = workstreamWorldParts(row.roles);
14977
+ if (roleParts.length) console.log('Latest world:', roleParts.join(' · '));
14978
+ // A campaign that spanned several worlds says so. One blended line would describe a run that never
14979
+ // happened, and "which world proved this?" is the question the whole surface exists to answer.
14980
+ const worlds = Array.isArray(row.worlds) ? row.worlds : [];
14981
+ if (worlds.length > 1) {
14982
+ console.log('Worlds in this workstream: ' + worlds.length);
14983
+ worlds.slice(-6).forEach((world) => {
14984
+ const parts = workstreamWorldParts(world);
14985
+ if (parts.length) console.log(' - ' + parts.join(' · '));
14986
+ });
14987
+ }
14988
+ const artifacts = Array.isArray(row.artifacts) ? row.artifacts.slice(-12) : [];
14989
+ console.log('Artifacts:', artifacts.length ? '' : 'none recorded');
14990
+ artifacts.forEach((artifact) => {
14991
+ const label = [artifact.type, artifact.tool, artifact.callId, artifact.envelopeId, artifact.packetType].filter(Boolean).join(' ');
14992
+ console.log(' - ' + (artifact.recordedAt || '') + ' ' + label);
14993
+ if (artifact.path) console.log(' ' + artifact.path);
14994
+ });
14995
+ });
14996
+ }
14997
+
13984
14998
  /**
13985
14999
  * The live work map: which repositories are being edited, by whom, and who else is around.
13986
15000
  *
@@ -14910,12 +15924,16 @@ function printCorpusHelp() {
14910
15924
  }
14911
15925
 
14912
15926
  function printToolHelp() {
14913
- console.log('Usage: remits-cli tool --name <toolName> [--base-url URL] [--account-id ID] [--branch BRANCH] [--input "{...}"|--input-file file.json] [--data-mode test|prod] [--variant-branch NAME|none] [--timeout-ms 60000] [--async true --wait true]');
15927
+ console.log('Usage: remits-cli tool --name <toolName> [--base-url URL] [--account-id ID] [--as-account ID] [--target-account ID] [--branch BRANCH] [--input "{...}"|--input-file file.json] [--data-mode test|prod] [--variant-branch NAME|none] [--timeout-ms 60000] [--async true --wait true]');
14914
15928
  console.log(' remits-cli tool status --call-id <callId> [--base-url URL] [--account-id ID] [--data-mode test|prod]');
14915
15929
  console.log('');
14916
15930
  console.log('Hierarchy scope (read/discovery tools) — you do NOT need the owning child account id:');
14917
15931
  console.log(' --scope self|children|hierarchy expand from the repo account when searching');
14918
15932
  console.log(' --target-account-id ID the exact account to act on (required by writes)');
15933
+ console.log(' --as-account ID execute as this reachable account; subscriber branch edges apply');
15934
+ console.log(' --target-account ID the account the tool acts ON (sent as input.accountId and');
15935
+ console.log(' input.targetAccountId when --input omits them). It does NOT change');
15936
+ console.log(' component resolution — add --as-account ID for that.');
14919
15937
  console.log(' --account-ids 1,2,3 an explicit bounded list of owner accounts');
14920
15938
  console.log(' --anchor-account-id ID disambiguate the path for a multi-parent account');
14921
15939
  console.log(' These are merged into --input; a value already in --input wins.');
@@ -14923,17 +15941,68 @@ function printToolHelp() {
14923
15941
  console.log('Examples:');
14924
15942
  console.log(' remits-cli tool --name mcp_firestore_search --input-file query.json --data-mode prod');
14925
15943
  console.log(' remits-cli tool --name mcp_firestore_search --input \'{"collection":"statements","documentId":"1234"}\' --scope children');
14926
- console.log(' remits-cli tool --name mcp_run_action --input \'{"accountId":49,"actionId":200,"executionMode":"async"}\' --data-mode prod');
15944
+ console.log(' remits-cli tool --name mcp_run_action --account-id 21 --as-account 36 --target-account 36 --input \'{"actionId":200,"executionMode":"async"}\' --data-mode prod');
14927
15945
  console.log('');
14928
15946
  console.log('For mcp_run_action and mcp_run_agent, prefer the tool\'s own executionMode:"async" and poll with that tool\'s controlAction:"status".');
14929
15947
  }
14930
15948
 
14931
15949
  function printToolsHelp() {
14932
- console.log('Usage: remits-cli tools [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--variant-branch NAME|none]');
15950
+ console.log('Usage: remits-cli tools [--base-url URL] [--account-id ID] [--as-account ID] [--branch BRANCH] [--data-mode test|prod] [--variant-branch NAME|none]');
14933
15951
  console.log('');
14934
15952
  console.log('Refreshes .remits-cli/tools/tools.json for the current account/branch/data-mode context.');
14935
15953
  }
14936
15954
 
15955
+ function printGuidesHelp() {
15956
+ console.log('Usage: remits-cli guides sync [--all] [--base-url URL] [--json]');
15957
+ console.log('');
15958
+ console.log('Refreshes front-stage guides from the authenticated platform.');
15959
+ console.log(' --all updates every indexed account checkout on this machine.');
15960
+ console.log(' without --all, run from an account repo to refresh only that checkout.');
15961
+ }
15962
+
15963
+ async function guidesCommand(flags, subcommand) {
15964
+ const mode = subcommand || 'sync';
15965
+ if (mode === 'help' || mode === '--help' || flagEnabled(flags.help)) {
15966
+ printGuidesHelp();
15967
+ return;
15968
+ }
15969
+ if (mode !== 'sync' && mode !== 'refresh' && mode !== 'update') {
15970
+ throw new Error('Unknown guides command: ' + mode + '. Use: remits-cli guides sync [--all]');
15971
+ }
15972
+
15973
+ if (flagEnabled(flags.all)) {
15974
+ const result = await syncGuidesAcrossIndexedRepos({
15975
+ baseUrl: flags['base-url'] || null,
15976
+ rescan: !flagEnabled(flags['no-rescan'])
15977
+ });
15978
+ if (flagEnabled(flags.json)) {
15979
+ console.log(JSON.stringify(result, null, 2));
15980
+ return;
15981
+ }
15982
+ console.log('Guides sync: ' + guideSyncSummaryLine(result));
15983
+ for (const repo of result.repos || []) {
15984
+ if (repo.skipped) {
15985
+ console.log(' skipped account ' + (repo.accountId || '?') + ': ' + repo.reason + ' ' + repo.directory);
15986
+ } else if ((repo.updated || []).length || (repo.removed || []).length) {
15987
+ console.log(' updated account ' + repo.accountId + ': ' +
15988
+ ((repo.updated || []).length + (repo.removed || []).length) + ' file(s) ' + repo.directory);
15989
+ }
15990
+ }
15991
+ return;
15992
+ }
15993
+
15994
+ if (!loadAccountInfo(process.cwd())) {
15995
+ throw new Error('Run `remits-cli guides sync` from an account repo, or pass --all.');
15996
+ }
15997
+ const before = Date.now();
15998
+ await ensureGuidesSynced(process.cwd(), flags);
15999
+ if (flagEnabled(flags.json)) {
16000
+ console.log(JSON.stringify({ success: true, directory: process.cwd(), elapsedMs: Date.now() - before }, null, 2));
16001
+ return;
16002
+ }
16003
+ console.log('Guides synced for ' + process.cwd());
16004
+ }
16005
+
14937
16006
  function printTokenHelp() {
14938
16007
  console.log('Usage: remits-cli token [--base-url URL] [--branch BRANCH] [--path embeddable/path] [--data-mode test|prod] [--as-account ID] [--variant-branch NAME|none]');
14939
16008
  console.log(' remits-cli token inspect --token <token|tokenKey|URL> [--base-url URL] [--account-id ID]');
@@ -14952,7 +16021,7 @@ function printVerifyHelp() {
14952
16021
  console.log(' remits-cli verify use <envelopeId>');
14953
16022
  console.log(' remits-cli verify current');
14954
16023
  console.log(' remits-cli verify clear [--all]');
14955
- console.log(' remits-cli verify show [--envelope ID] [--json]');
16024
+ console.log(' remits-cli verify show [--envelope ID] [--summary|--compact] [--json]');
14956
16025
  console.log(' remits-cli verify status [--envelope ID]');
14957
16026
  console.log(' remits-cli verify report [--envelope ID]');
14958
16027
  console.log(' remits-cli verify abandon --envelope ID --reason "..."');
@@ -15020,8 +16089,13 @@ async function main() {
15020
16089
  try { updateAccountRepoIndex(process.cwd()); } catch (_) { /* not in account repo */ }
15021
16090
  // Auto-start the background service if not already running.
15022
16091
  // Skip for lifecycle subcommands and help.
15023
- const isLifecycleCmd = command === 'listen' || command === 'start' || command === 'stop' || command === 'status' || command === 'whoami' || command === 'doctor' || command === 'agent' || command === 'ticket';
16092
+ const isLifecycleCmd = command === 'listen' || command === 'start' || command === 'stop' || command === 'status' || command === 'whoami' || command === 'doctor' || command === 'agent' || command === 'ticket' || command === 'guides';
15024
16093
  const isHelpCmd = !command || command === 'help' || command === '--help' || wantsHelp;
16094
+ if (!wantsJson && !isHelpCmd && command === 'components') {
16095
+ const commandName = 'components ' + (subcommand || 'command');
16096
+ const problem = platformRepoAccountCommandProblem(process.cwd(), args, commandName, null);
16097
+ if (problem) throw new Error(problem);
16098
+ }
15025
16099
  if (!wantsJson && !isHelpCmd && !isLifecycleCmd && !isListenerRunning()) {
15026
16100
  try {
15027
16101
  const dashboardUrl = await autoStartServiceIfNeeded();
@@ -15052,18 +16126,20 @@ async function main() {
15052
16126
  console.log(' remits-cli agent status [--state idle|working|paused] [--ticket ID] [--activity "..."]');
15053
16127
  console.log(' remits-cli agent list [--account-id ID] # who else is available');
15054
16128
  console.log(' remits-cli agent map [--account-ids 1,4] [--json] # who is EDITING what, where, right now');
15055
- console.log(' remits-cli activity inspect [--account-id ID] [--scope self|children] [--user-email E] [--json]');
16129
+ console.log(' remits-cli activity inspect [--account-id ID] [--scope self|children|related] [--workspace NAME] [--workstream ID] [--user-email E] [--summary|--compact] [--json]');
16130
+ console.log(' remits-cli workstream status [--workstream ID|--workspace NAME] [--json]');
15056
16131
  console.log(' remits-cli agent release # stop receiving tickets now');
15057
16132
  console.log(' remits-cli start [--foreground true] [--port 8787] # human dashboard in the browser');
15058
16133
  console.log(' remits-cli stop');
15059
16134
  console.log(' remits-cli status [--base-url URL] [--account-id ID] [--data-mode test|prod] [--json]');
15060
16135
  console.log(' remits-cli doctor local-state [--json] # actor/workspace/local state hygiene');
15061
16136
  console.log(' remits-cli whoami [--base-url URL] [--account-id ID] [--data-mode test|prod] [--json]');
16137
+ console.log(' remits-cli guides sync [--all] [--base-url URL] [--json] # refresh front-stage guides');
15062
16138
  console.log(' remits-cli listen [stop|status] [--foreground true] # compatibility alias');
15063
16139
  console.log(' remits-cli data-mode [set test|prod]');
15064
16140
  console.log(' remits-cli install --skills [--target codex|claude|gemini|all] [--overwrite true]');
15065
- console.log(' remits-cli tools [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--variant-branch NAME|none]');
15066
- console.log(' remits-cli tool --name <toolName> [--base-url URL] [--branch BRANCH] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod] [--scope self|children|hierarchy] [--target-account-id ID] [--account-ids 1,2,3] [--variant-branch NAME|none] [--timeout-ms 60000] [--async true --wait true]');
16141
+ console.log(' remits-cli tools [--base-url URL] [--account-id ID] [--as-account ID] [--branch BRANCH] [--data-mode test|prod] [--variant-branch NAME|none]');
16142
+ console.log(' remits-cli tool --name <toolName> [--base-url URL] [--account-id ID] [--as-account ID] [--target-account ID] [--branch BRANCH] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod] [--scope self|children|hierarchy] [--account-ids 1,2,3] [--variant-branch NAME|none] [--timeout-ms 60000] [--async true --wait true]');
15067
16143
  console.log(' remits-cli tool status --call-id <callId> [--base-url URL] [--account-id ID] [--data-mode test|prod]');
15068
16144
  console.log(' remits-cli workspace [show|use <name>|use --auto|clear] # isolate staging when several agents share a repo');
15069
16145
  console.log(' remits-cli verify start --summary "..." [--manifest file.json] # start a verification envelope');
@@ -15103,7 +16179,7 @@ async function main() {
15103
16179
  console.log('A WORKSPACE is an optional second isolation axis for staging, alongside the git branch.');
15104
16180
  console.log('Staging is keyed by (account, cli user, branch, workspace) — and account and user are fixed');
15105
16181
  console.log('for a given repo, so without a workspace the branch is the ONLY axis. That is why two coding');
15106
- console.log('agents in two worktrees of the same branch overwrite each other: `components stage` uploads');
16182
+ console.log('agents in two checkouts without their own workspace overwrite each other: `components stage` uploads');
15107
16183
  console.log('the whole repo and replaces the lane wholesale.');
15108
16184
  console.log('');
15109
16185
  console.log('A workspace isolates staging WITHOUT changing anything else:');
@@ -15111,13 +16187,13 @@ async function main() {
15111
16187
  console.log(' - the run still resolves whatever committed ComponentVariant branch the account subscribes to');
15112
16188
  console.log(' - only which staged entries shadow them changes');
15113
16189
  console.log('');
15114
- console.log('Typical setup, one worktree per agent, all on the same branch:');
15115
- console.log(' git fetch origin');
16190
+ console.log('Typical setup, one clone per agent, all on the same branch:');
16191
+ console.log(' git clone <repo-url> ../ms-agent-a');
16192
+ console.log(' cd ../ms-agent-a');
15116
16193
  console.log(' git switch forked');
15117
16194
  console.log(' git pull --ff-only origin forked');
15118
16195
  console.log(' git rev-list --left-right --count HEAD...origin/forked # must print: 0 0');
15119
- console.log(' git worktree add --force ../ms-agent-a forked');
15120
- console.log(' cd ../ms-agent-a && remits-cli workspace use --auto');
16196
+ console.log(' remits-cli workspace use --auto');
15121
16197
  console.log(' remits-cli components status # branch world + staging lane + other lanes');
15122
16198
  console.log(' remits-cli components stage # isolated lane; nobody else sees it');
15123
16199
  console.log(' remits-cli test run --test 42 # resolves this lane over the forked variants');
@@ -15126,7 +16202,8 @@ async function main() {
15126
16202
  console.log('Precedence: --workspace > REMITS_WORKSPACE > .remits-cli/workspace > shared default lane');
15127
16203
  console.log('Use --no-workspace on a single command to target the shared lane without clearing the file.');
15128
16204
  console.log('');
15129
- console.log('`.remits-cli/workspace` is per-checkout and gitignored, so a worktree keeps its own lane.');
16205
+ console.log('`.remits-cli/workspace` is per-checkout and gitignored, so each clone keeps its own lane.');
16206
+ console.log('Avoid same-branch worktrees for concurrent agents: they share the branch ref, so one pull moves HEAD under the others, and `components commit` refuses there.');
15130
16207
  process.exit(0);
15131
16208
  }
15132
16209
 
@@ -15145,6 +16222,11 @@ async function main() {
15145
16222
  process.exit(0);
15146
16223
  }
15147
16224
 
16225
+ if (wantsHelp && command === 'guides') {
16226
+ printGuidesHelp();
16227
+ process.exit(0);
16228
+ }
16229
+
15148
16230
  if (wantsHelp && command === 'token') {
15149
16231
  printTokenHelp();
15150
16232
  process.exit(0);
@@ -15165,6 +16247,11 @@ async function main() {
15165
16247
  return;
15166
16248
  }
15167
16249
 
16250
+ if (command === 'guides') {
16251
+ await guidesCommand(args, subcommand);
16252
+ return;
16253
+ }
16254
+
15168
16255
  if (command === 'components' && (subcommand === 'push' || subcommand === 'stage')) {
15169
16256
  if (subcommand === 'push' && !args.mode) {
15170
16257
  args.mode = 'push';
@@ -15277,11 +16364,16 @@ async function main() {
15277
16364
 
15278
16365
  if (command === 'activity') {
15279
16366
  if (wantsHelp || !subcommand) {
15280
- console.log('Usage: remits-cli activity inspect [--account-id ID] [--scope self|children] [--user-id ID|--user-email EMAIL] [--limit N] [--json]');
16367
+ console.log('Usage: remits-cli activity inspect [--account-id ID] [--scope self|children|related] [--workspace NAME] [--branch NAME] [--workstream ID] [--user-id ID|--user-email EMAIL] [--limit N] [--summary|--compact] [--json]');
15281
16368
  console.log('');
15282
16369
  console.log('Read-only workstream inspection for agent/operator review. Joins current staging lanes,');
15283
16370
  console.log('verification envelopes, durable test runs, corpus measurements, live agent presence,');
15284
16371
  console.log('and heuristic smell signals without changing any lane or checkout.');
16372
+ console.log('');
16373
+ console.log('--scope related also follows component owners, staging lanes and verification packet worlds');
16374
+ console.log('out of the hierarchy. --workspace / --branch / --workstream NARROW it: the account set becomes');
16375
+ console.log('the accounts with matching evidence (plus this account and its component owners), and the');
16376
+ console.log('lanes, envelopes, test runs and corpus results are filtered to that workstream.');
15285
16377
  return;
15286
16378
  }
15287
16379
  if (subcommand === 'inspect') {
@@ -15291,6 +16383,23 @@ async function main() {
15291
16383
  throw new Error('Unknown activity subcommand: ' + subcommand);
15292
16384
  }
15293
16385
 
16386
+ if (command === 'workstream') {
16387
+ if (wantsHelp) {
16388
+ console.log('Usage: remits-cli workstream status [--workstream ID|--workspace NAME] [--json]');
16389
+ console.log('');
16390
+ console.log('Shows the local logical proof campaign index across actor-scoped state. It references');
16391
+ console.log('tool response files, verification packets, actors and account/world roles without copying payloads.');
16392
+ console.log('');
16393
+ console.log('The id is stamped onto every verification packet world, so the same value works server-side:');
16394
+ console.log(' remits-cli activity inspect --scope related --workstream <id>');
16395
+ console.log('This is NOT a support ticket\'s --workstream, which is an account\'s own routing label matched');
16396
+ console.log('by `agent serve --serves`. Same flag name, unrelated namespaces.');
16397
+ return;
16398
+ }
16399
+ await workstreamCommand(args, subcommand);
16400
+ return;
16401
+ }
16402
+
15294
16403
  if (command === 'ticket') {
15295
16404
  if (wantsHelp || !subcommand) {
15296
16405
  printTicketHelp();