@remits/remits-cli 0.1.130 → 0.1.132

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,7 @@ remits-cli data-mode
45
45
  remits-cli data-mode set prod
46
46
  remits-cli sessions list
47
47
  remits-cli config set --agent codex
48
+ remits-cli activity inspect --account-id 4 --scope children --data-mode test
48
49
  ```
49
50
 
50
51
  ## Skill Install
@@ -78,6 +79,7 @@ remits-cli install --skills --overwrite true
78
79
  - `components lanes` lists every indexed staging lane on the account. `components entries --lane-id <id>` reads the authoritative staged files for one lane without switching your workspace, clearing anything, or using SQL.
79
80
  - `components clear` clears staged entries. Scope it with `--component-type` and/or `--component-id`. Component ids are type-local, so an id alone clears that one component when the id is staged in only one family; if the same id is staged across multiple families it returns an ambiguity error asking you to add `--component-type`. With no filter it clears every staged entry for the current branch; pass `--all` to force the full-branch wipe explicitly.
80
81
  - `components stage`, `components status`, `components lanes`, `components entries`, and `components clear` print concise summaries by default. Add `--json` or `--verbose` to print the full server response.
82
+ - `activity inspect` is a read-only workstream view for operator/agent review. It joins staging lanes, verification envelopes, Test runs, corpus measurements, live agent presence, and heuristic smell signals. Use `--scope children` from a platform/root account and `--user-email` or `--user-id` to focus on one CLI user.
81
83
  - `components stage` and `components status` report three separate numbers, and they answer three different questions: the **workset** (components git reports this working tree changed), what was **submitted**, and the materialized **overlay** the lane now holds — which is what a run resolves. A number that could not be established prints as `unknown`, never as `0`.
82
84
  - `components sync` performs a server-side sync from the git remote into the Remits platform for the selected branch. It does not run local git commands. After a successful non-dry-run sync, the platform clears the branch/user staging scope so staged aliases cannot keep shadowing the newly synced DB rows.
83
85
  - On trunk, `components sync` performs the full repo-to-DB reconcile. On a non-trunk branch, it writes `ComponentVariant` overlays only and may refresh branch-local `account-info.json`, `account-hierarchy.json`, and `account-configurations.json` for the subscribing account that initiated the sync. `--dry-run` is accepted only for non-trunk variant syncs and reports overrides/additions/tombstones without writing variants, caching the sync SHA, updating metadata, or clearing staging. Add `--summary` to dry-run output when you only need counts, removals/tombstones, errors, skipped items, and warnings. `--force-tombstones` is accepted only for non-trunk variant syncs and should be used only when missing trunk component files are intentional tombstone overrides.
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
- - L2018 Account Repos, Guide Sync, And Platform Repo
10
- - L2676 Component Discovery And HTTP Logging
11
- - L3252 Skill Delivery And TOC Resolution
12
- - L3562 Auth And Component Staging
13
- - L4171 Component Summaries, Status, And Sync Gates
14
- - L6204 Branches, Promotion, Commit, And Test Runs
15
- - L7813 Tokens, Tools, Verification, And Config
16
- - L8943 Service Dashboard And WebSocket Listener
17
- - L11059 Agent And Ticket Workflows
18
- - L14117 Help, Auto Update, And Command Dispatch
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
19
19
  */
20
20
 
21
21
  /*
@@ -1411,13 +1411,36 @@ function verificationPaths(cwd, envelopeId) {
1411
1411
  };
1412
1412
  }
1413
1413
 
1414
+ // The active envelope is bound to the checkout world an agent WORKS in: host, account, branch, workspace - the
1415
+ // same identity as its staging lane. The data lane is deliberately NOT part of it. Commands resolve their lane
1416
+ // differently (`test run` defaults to test; stage/token/tool/sync follow the session), so a lane in this key made
1417
+ // one agent see a different envelope - or none - from one command to the next: evidence silently never attached,
1418
+ // and `verify report` answered "No verification envelope selected" for the envelope it had just started. Whether
1419
+ // a command's lane fits the envelope is the platform's call (`verificationPreflight`), and it is made out loud.
1414
1420
  function verificationContextKey(context = {}) {
1415
1421
  const baseUrl = normalizeBaseUrl(context.baseUrl || context.host || DEFAULT_BASE_URL);
1416
1422
  const accountId = context.accountId != null ? String(context.accountId) : 'unknown-account';
1417
1423
  const branchName = context.branchName || context.branch || 'unknown-branch';
1418
1424
  const workspace = normalizeWorkspace(context.workspace) || 'shared';
1419
- const dataMode = normalizeDataMode(context.dataMode);
1420
- return [baseUrl, accountId, branchName, workspace, dataMode].join('|');
1425
+ return [baseUrl, accountId, branchName, workspace].join('|');
1426
+ }
1427
+
1428
+ // Pointers written while the data lane was still part of the key end in `|test` / `|prod`. They name the same
1429
+ // checkout world, so read them (newest first) rather than orphan an envelope an older CLI selected.
1430
+ function legacyVerificationContextKeys(key) {
1431
+ return [key + '|test', key + '|prod'];
1432
+ }
1433
+
1434
+ function activeVerificationContextEntry(contexts, key) {
1435
+ const normalize = (entry) => (typeof entry === 'string' ? { envelopeId: entry } : entry);
1436
+ const current = contexts && contexts[key];
1437
+ if (current) return normalize(current);
1438
+ const legacy = legacyVerificationContextKeys(key)
1439
+ .map((legacyKey) => contexts && contexts[legacyKey])
1440
+ .filter((entry) => entry && (typeof entry === 'string' || entry.envelopeId))
1441
+ .map(normalize)
1442
+ .sort((left, right) => String(right.updatedAt || '').localeCompare(String(left.updatedAt || '')));
1443
+ return legacy[0] || null;
1421
1444
  }
1422
1445
 
1423
1446
  function readActiveVerificationContexts(cwd) {
@@ -1452,11 +1475,8 @@ function activeVerificationContext(cwd, flags = {}, session = null, accountId =
1452
1475
 
1453
1476
  function readActiveVerificationEnvelope(cwd, context = null) {
1454
1477
  if (context) {
1455
- const contexts = readActiveVerificationContexts(cwd);
1456
- const entry = contexts[verificationContextKey(context)];
1457
- if (entry && typeof entry === 'object') return entry.envelopeId || null;
1458
- if (typeof entry === 'string') return entry || null;
1459
- return null;
1478
+ const entry = activeVerificationContextEntry(readActiveVerificationContexts(cwd), verificationContextKey(context));
1479
+ return entry && entry.envelopeId ? String(entry.envelopeId) : null;
1460
1480
  }
1461
1481
  const paths = verificationPaths(cwd);
1462
1482
  const candidates = [paths.activeFile, localStatePaths(cwd).legacy.activeVerificationFile];
@@ -1477,6 +1497,7 @@ function writeActiveVerificationEnvelope(cwd, envelopeId, context = null) {
1477
1497
  if (context) {
1478
1498
  const contexts = readActiveVerificationContexts(cwd);
1479
1499
  const key = verificationContextKey(context);
1500
+ legacyVerificationContextKeys(key).forEach((legacyKey) => { delete contexts[legacyKey]; });
1480
1501
  if (!envelopeId) {
1481
1502
  delete contexts[key];
1482
1503
  } else {
@@ -1499,10 +1520,15 @@ function writeActiveVerificationEnvelope(cwd, envelopeId, context = null) {
1499
1520
  return String(envelopeId);
1500
1521
  }
1501
1522
 
1523
+ function explicitVerificationEnvelopeId(flags = {}) {
1524
+ const explicit = flags['verify-envelope'] || flags.verifyEnvelope || flags.envelope;
1525
+ return explicit !== undefined && explicit !== null && explicit !== true ? (String(explicit).trim() || null) : null;
1526
+ }
1527
+
1502
1528
  function verificationEnvelopeIdForCommand(cwd, flags = {}, context = null) {
1503
1529
  if (flagEnabled(flags['no-verify-envelope']) || flagEnabled(flags.noVerifyEnvelope)) return null;
1504
- const explicit = flags['verify-envelope'] || flags.verifyEnvelope || flags.envelope;
1505
- if (explicit !== undefined && explicit !== null && explicit !== true) return String(explicit).trim() || null;
1530
+ const explicit = explicitVerificationEnvelopeId(flags);
1531
+ if (explicit) return explicit;
1506
1532
  return context ? readActiveVerificationEnvelope(cwd, context) : readActiveVerificationEnvelope(cwd);
1507
1533
  }
1508
1534
 
@@ -1511,7 +1537,10 @@ function writeLocalVerificationEnvelope(cwd, envelope) {
1511
1537
  const paths = verificationPaths(cwd, envelope.envelopeId);
1512
1538
  ensureDir(paths.base);
1513
1539
  ensureDir(paths.packetsDir);
1514
- atomicWriteFile(paths.envelopeFile, JSON.stringify(envelope, null, 2));
1540
+ // Attach and status answer with a PACKETLESS header. Layer it over the mirror instead of replacing it, or the
1541
+ // offline fallback loses the packets and evaluation the last full read stored.
1542
+ const mirrored = Array.isArray(envelope.packets) ? envelope : Object.assign({}, readLocalVerificationEnvelope(cwd, envelope.envelopeId) || {}, envelope);
1543
+ atomicWriteFile(paths.envelopeFile, JSON.stringify(mirrored, null, 2));
1515
1544
  if (envelope.acceptance) {
1516
1545
  atomicWriteFile(paths.manifestFile, JSON.stringify(envelope.acceptance, null, 2));
1517
1546
  }
@@ -1682,27 +1711,133 @@ function syncEvidencePayload(sync) {
1682
1711
  return Object.assign({}, sync, { syncResults: trimmedResults });
1683
1712
  }
1684
1713
 
1685
- // The world an envelope was STARTED in, read the same way the platform reads it
1686
- // (VerificationEnvelopeService.envelopeStartWorld / ENVELOPE_START_WORLD_KEYS). Kept field-for-field identical so
1687
- // the attach-time warning and the verify-time wrong-world verdict can never disagree.
1688
- function envelopeWorldDrift(envelope, context = {}) {
1689
- if (!envelope || typeof envelope !== 'object') return [];
1690
- const source = envelope.source && typeof envelope.source === 'object' ? envelope.source : {};
1691
- const account = envelope.account && typeof envelope.account === 'object' ? envelope.account : {};
1692
- const started = {
1693
- 'repo account': account.repoAccountId != null ? String(account.repoAccountId) : (envelope.accountId != null ? String(envelope.accountId) : null),
1694
- 'data lane': normalizeDataMode(account.dataMode || source.dataMode) || null,
1695
- 'git branch': source.gitBranch || source.branchName || null,
1696
- workspace: normalizeWorkspace(source.workspace) || null
1697
- };
1698
- const now = {
1699
- 'repo account': context.accountId != null ? String(context.accountId) : null,
1700
- 'data lane': normalizeDataMode(context.dataMode) || null,
1701
- 'git branch': context.branchName || null,
1702
- workspace: normalizeWorkspace(context.workspace) || null
1703
- };
1704
- return Object.keys(started).filter((key) => started[key] && now[key] && started[key] !== now[key])
1705
- .map((key) => key + ' ' + started[key] + ' -> ' + now[key]);
1714
+ // The flag that would put a command into the envelope's world, per mismatched field. Rendering only: WHETHER a
1715
+ // field mismatches is the platform's answer (`compatibility`), never recomputed here.
1716
+ function verificationWorldHints(diffs = []) {
1717
+ return (Array.isArray(diffs) ? diffs : []).map((diff) => {
1718
+ const expected = diff && diff.expected;
1719
+ if (expected === undefined || expected === null || expected === '') return null;
1720
+ switch (diff.field) {
1721
+ case 'dataMode': return '--data-mode ' + expected;
1722
+ case 'workspace': return '--workspace ' + expected;
1723
+ case 'repoAccountId': return '--account-id ' + expected;
1724
+ case 'accountId': return '--as-account ' + expected;
1725
+ case 'componentBranch':
1726
+ case 'variantBranch': return '--variant-branch ' + expected;
1727
+ case 'gitBranch':
1728
+ case 'branchName': return 'run from git branch ' + expected;
1729
+ default: return null;
1730
+ }
1731
+ }).filter(Boolean);
1732
+ }
1733
+
1734
+ function verificationDiffSummary(diffs = []) {
1735
+ return (Array.isArray(diffs) ? diffs : []).map((diff) =>
1736
+ diff.field + ': envelope requires ' + diff.expected + ', this command has ' + (diff.command == null ? 'none (shared lane)' : diff.command)).join('; ');
1737
+ }
1738
+
1739
+ function verificationRefusalLines(result = {}, commandLabel = 'this command') {
1740
+ const lines = [result.message || ('Evidence from ' + commandLabel + ' would not count for verification envelope ' + result.envelopeId + '.')];
1741
+ const hints = verificationWorldHints(result.diffs);
1742
+ lines.push('Nothing ran. Pick one and re-run ' + commandLabel + ':');
1743
+ if (hints.length) lines.push(" - prove it in the envelope's world: " + hints.join(' '));
1744
+ lines.push(' - run it without recording evidence: --no-verify-envelope');
1745
+ lines.push(' - it is different work: remits-cli verify start --summary "..." (selects a new envelope for this checkout)');
1746
+ lines.push(' - record it anyway as context (reported wrong-world): --allow-wrong-world-evidence');
1747
+ return lines;
1748
+ }
1749
+
1750
+ function flagsWithoutVerificationEnvelope(flags = {}) {
1751
+ return Object.assign({}, flags, { 'no-verify-envelope': true });
1752
+ }
1753
+
1754
+ // Asks the platform - by the evaluator's own world rule - whether evidence from this command would count for the
1755
+ // selected envelope, BEFORE the command runs. The CLI used to answer this with its own copy of the rule, which had
1756
+ // drifted (shared lane vs a named workspace, the execution account, packet-type exemptions) and so blessed
1757
+ // evidence the report then rejected.
1758
+ //
1759
+ // `probe` is `{ packetType, world, test? }`, with world keys as a packet records them. Resolves to
1760
+ // `{ envelopeId, verdict, evidenceFlags }`: pass `evidenceFlags` to every appendVerificationPacket of the command.
1761
+ // attach - same world: evidence attaches to exactly this envelope (pinned by id, no second lookup).
1762
+ // refuse - a requirement could use this packet but the world differs: THROWS a refusal, nothing runs.
1763
+ // detach - differs, but nothing in the manifest could use it (or the envelope is closed): runs, attaches
1764
+ // nothing, and says so once. Refusing there would cost the agent a turn and protect nothing.
1765
+ // Idempotent within one invocation: a caller that already asked passes the answer in `__verificationPreflight`.
1766
+ async function verificationPreflight(api, cwd, session, accountId, flags = {}, probe = {}, options = {}) {
1767
+ if (flags.__verificationPreflight) return flags.__verificationPreflight;
1768
+ const label = options.command || 'this command';
1769
+ const context = activeVerificationContext(cwd, flags, session, accountId);
1770
+ const envelopeId = verificationEnvelopeIdForCommand(cwd, flags, context);
1771
+ if (!envelopeId) return { envelopeId: null, verdict: 'none', evidenceFlags: flags };
1772
+ const pinned = Object.assign({}, flags, { 'verify-envelope': envelopeId });
1773
+ let result;
1774
+ try {
1775
+ result = await postVerificationCommand(api, cwd, session, accountId, 'compatibility', { envelopeId, probe });
1776
+ } catch (err) {
1777
+ const status = err && err.response && err.response.status;
1778
+ if (status === 404) {
1779
+ if (explicitVerificationEnvelopeId(flags)) {
1780
+ const refusal = new Error('Verification envelope ' + envelopeId + ' does not exist on ' + context.baseUrl +
1781
+ '. Run `remits-cli verify list` to find the right id, or drop --verify-envelope.');
1782
+ refusal.refused = true;
1783
+ throw refusal;
1784
+ }
1785
+ console.error('Verification envelope ' + envelopeId + ' (selected for this checkout) no longer exists; evidence is not attached. ' +
1786
+ 'Run `remits-cli verify list`, then `verify use <id>` or `verify start`.');
1787
+ return { envelopeId: null, verdict: 'detach', evidenceFlags: flagsWithoutVerificationEnvelope(flags) };
1788
+ }
1789
+ // A platform without `compatibility`, or one that is unreachable: attach as before. The report still judges
1790
+ // the world; this only loses the early answer.
1791
+ return { envelopeId, verdict: 'unchecked', evidenceFlags: pinned };
1792
+ }
1793
+ const verdict = result.verdict === 'refuse' && options.neverRefuse ? 'detach' : (result.verdict || 'attach');
1794
+ const allowWrongWorld = flagEnabled(flags['allow-wrong-world-evidence']) || flagEnabled(flags.allowWrongWorldEvidence);
1795
+ if (verdict === 'refuse' && !allowWrongWorld) {
1796
+ const refusal = new Error(verificationRefusalLines(result, label).join('\n'));
1797
+ refusal.refused = true;
1798
+ refusal.compatibility = result;
1799
+ throw refusal;
1800
+ }
1801
+ if (verdict === 'refuse') {
1802
+ console.error('Attaching wrong-world evidence as context (--allow-wrong-world-evidence): ' + result.message);
1803
+ return { envelopeId, verdict, compatibility: result, evidenceFlags: pinned };
1804
+ }
1805
+ if (verdict === 'detach') {
1806
+ if (!options.quiet && result.message) {
1807
+ const hints = verificationWorldHints(result.diffs);
1808
+ console.error('Verification: ' + result.message + (hints.length ? ' (It would attach, as context only, with: ' + hints.join(' ') + ')' : ''));
1809
+ }
1810
+ return { envelopeId: null, verdict, compatibility: result, evidenceFlags: flagsWithoutVerificationEnvelope(flags) };
1811
+ }
1812
+ return { envelopeId, verdict, compatibility: result, evidenceFlags: pinned };
1813
+ }
1814
+
1815
+ // "Would a plain `test run` from this checkout count for this envelope?" - printed where an agent selects or
1816
+ // inspects an envelope, so a mismatch is learned before the first run rather than from a refusal after it.
1817
+ async function printVerificationRunProbe(api, cwd, session, accountId, envelopeId, world = {}, options = {}) {
1818
+ if (!envelopeId) return null;
1819
+ let result;
1820
+ try {
1821
+ result = await postVerificationCommand(api, cwd, session, accountId, 'compatibility', {
1822
+ envelopeId,
1823
+ probe: { packetType: 'test_run', world }
1824
+ });
1825
+ } catch (_) {
1826
+ return null;
1827
+ }
1828
+ if (options.onlyMismatch && result.verdict === 'attach') return result;
1829
+ if (result.comparisonWorld) {
1830
+ console.log('Proof world (' + (result.comparisonWorldSource || 'unknown') + '):', JSON.stringify(result.comparisonWorld));
1831
+ }
1832
+ if (result.verdict === 'attach') {
1833
+ console.log('A `test run` from this checkout (dataMode=' + (world.dataMode || DEFAULT_DATA_MODE) + ') attaches to this envelope.');
1834
+ } else if (result.verdict === 'refuse' || result.verdict === 'detach') {
1835
+ const hints = verificationWorldHints(result.diffs);
1836
+ console.log('A `test run` from this checkout (dataMode=' + (world.dataMode || DEFAULT_DATA_MODE) + ') would ' +
1837
+ (result.verdict === 'refuse' ? 'be REFUSED' : 'NOT attach') + ' - ' + verificationDiffSummary(result.diffs) + '.');
1838
+ if (hints.length) console.log(' to prove it for this envelope, pass: ' + hints.join(' '));
1839
+ }
1840
+ return result;
1706
1841
  }
1707
1842
 
1708
1843
  async function appendVerificationPacket(api, cwd, session, accountId, flags, packet, options = {}) {
@@ -1720,18 +1855,20 @@ async function appendVerificationPacket(api, cwd, session, accountId, flags, pac
1720
1855
  try {
1721
1856
  const response = await postVerificationCommand(api, cwd, session, accountId, 'packet', {
1722
1857
  envelopeId,
1723
- packet: finalPacket
1858
+ packet: finalPacket,
1859
+ // Packetless answer: this runs on every command while an envelope is active.
1860
+ compact: true
1724
1861
  });
1725
1862
  if (response.packet) writeLocalVerificationPacket(cwd, envelopeId, response.packet);
1726
1863
  if (response.envelope) writeLocalVerificationEnvelope(cwd, response.envelope);
1727
1864
  if (!options.quiet) {
1728
1865
  console.log('Verification envelope:', envelopeId, '(packet ' + finalPacket.type + ' attached)');
1729
- // Say it HERE, not only at `verify` time. Evidence attached to an envelope started in a different world
1730
- // is evidence about a different world, and the reader has to learn that while they can still redo the run.
1731
- const drift = envelopeWorldDrift(response.envelope, context);
1732
- if (drift.length) {
1733
- console.log(' WARNING: this envelope was started in a different world - ' + drift.join('; ') +
1734
- '. The packet is attached, but it will be reported wrong-world at verify time.');
1866
+ // The platform's own verdict on the stored packet, so this line and `verify report` cannot disagree.
1867
+ const diffs = Array.isArray(response.worldDiffs) ? response.worldDiffs : [];
1868
+ if (diffs.length) {
1869
+ console.log(' WARNING: this packet is from a different world than the envelope requires - ' +
1870
+ diffs.map((diff) => diff.field + ' expected ' + diff.expected + ' got ' + (diff.packet == null ? 'none' : diff.packet)).join('; ') +
1871
+ '. It is attached, but it cannot satisfy a requirement.');
1735
1872
  }
1736
1873
  }
1737
1874
  return response;
@@ -3876,6 +4013,10 @@ async function pushComponentsCommand(flags) {
3876
4013
  // or, worse, conclude staging was broken and go on reading trunk. Scaled by payload size, floored at
3877
4014
  // the old default, and overridable.
3878
4015
  const api = buildAxios(baseUrl, session.token, stageTimeoutMs(components, flags));
4016
+ const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
4017
+ packetType: 'stage',
4018
+ world: { accountId, dataMode, branchName, workspace }
4019
+ }, { command: 'components stage' });
3879
4020
  const stagePayload = {
3880
4021
  token: session.token,
3881
4022
  accountId,
@@ -3919,7 +4060,7 @@ async function pushComponentsCommand(flags) {
3919
4060
  // the platform rejected it. The packet is best-effort: a failing stage must surface the STAGE error,
3920
4061
  // never an error from writing the record of it.
3921
4062
  try {
3922
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
4063
+ await appendVerificationPacket(api, cwd, session, accountId, verification.evidenceFlags, {
3923
4064
  type: 'stage',
3924
4065
  success: false,
3925
4066
  claim: 'Components refused at staging',
@@ -3956,7 +4097,7 @@ async function pushComponentsCommand(flags) {
3956
4097
  throw new Error(response.message || 'Server stage failed');
3957
4098
  }
3958
4099
 
3959
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
4100
+ await appendVerificationPacket(api, cwd, session, accountId, verification.evidenceFlags, {
3960
4101
  type: 'stage',
3961
4102
  success: response.success !== false,
3962
4103
  claim: 'Components staged for verification',
@@ -4694,6 +4835,8 @@ function printBranchContext(response) {
4694
4835
  ('this account resolves the branch through an ancestor owned by account ' + ctx.resolvingOwnerAccountId + '.')));
4695
4836
  }
4696
4837
 
4838
+ variantBranchIdentityLines(response).forEach((line) => console.log(line));
4839
+
4697
4840
  if (!ctx.onTrunk) {
4698
4841
  console.log(' variants stored on this branch: ' + (ctx.variantCount || 0));
4699
4842
  const subs = ctx.subscribers || [];
@@ -4707,6 +4850,29 @@ function printBranchContext(response) {
4707
4850
  console.log('');
4708
4851
  }
4709
4852
 
4853
+ // A variant branch whose checkout identifies as its OWNER (account-info.json names the owner) runs every command as
4854
+ // the owner, and the owner subscribes to nothing — so a feature branch cut from this checkout resolves TRUNK, and no
4855
+ // run exercises what the subscriber actually gets. With exactly one subscriber that identity is repairable, and the
4856
+ // agent should hear it here rather than infer it from a page that rendered trunk. Pure.
4857
+ function variantBranchIdentityLines(response) {
4858
+ const ctx = response && response.branchContext;
4859
+ if (!ctx || ctx.onTrunk || ctx.variantBranchSource === 'subscription-fallback') return [];
4860
+ const subs = Array.isArray(ctx.subscribers) ? ctx.subscribers : [];
4861
+ const checkoutAccountId = response.accountId;
4862
+ if (subs.length !== 1 || checkoutAccountId == null || String(subs[0].accountId) === String(checkoutAccountId)) return [];
4863
+ if (ctx.commitOwnerAccountId != null && String(ctx.commitOwnerAccountId) !== String(checkoutAccountId)) return [];
4864
+ const sub = subs[0];
4865
+ const label = sub.accountId + (sub.accountName ? ' (' + sub.accountName + ')' : '');
4866
+ return [
4867
+ '',
4868
+ ' CHECKOUT IDENTITY: this branch has one subscriber, account ' + label + ', but this checkout runs as the owner,',
4869
+ ' account ' + checkoutAccountId + ' (account-info.json names it). Feature branches cut from here run as ' + checkoutAccountId +
4870
+ ' too, which subscribes to nothing, so they resolve trunk.',
4871
+ ' run as the subscriber: --as-account ' + sub.accountId,
4872
+ ' repair the identity: remits-cli components sync --account-id ' + sub.accountId + ' then git pull --ff-only'
4873
+ ];
4874
+ }
4875
+
4710
4876
  function printClearSummary(response, flags) {
4711
4877
  console.log('Cleared keys:', response.clearedCount || 0);
4712
4878
  console.log('Remaining staged count:', response.remainingCount || 0);
@@ -5123,6 +5289,11 @@ async function syncComponentsCommand(rawFlags) {
5123
5289
  const preflightRequested = syncPreflightRequested(flags);
5124
5290
  const namesOnly = flagEnabled(flags['names-only']) || flagEnabled(flags.namesOnly);
5125
5291
 
5292
+ const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
5293
+ packetType: dryRun || namesOnly ? 'sync_dry_run' : 'sync_mutation',
5294
+ world: { accountId, dataMode, branchName, workspace }
5295
+ }, { command: 'components sync' });
5296
+
5126
5297
  printProdDataBanner({
5127
5298
  dataMode,
5128
5299
  accountId,
@@ -5334,7 +5505,7 @@ async function syncComponentsCommand(rawFlags) {
5334
5505
  summary.gates = gate.checks;
5335
5506
  summary.gateViolations = gate.violations;
5336
5507
 
5337
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
5508
+ await appendVerificationPacket(api, cwd, session, accountId, verification.evidenceFlags, {
5338
5509
  type: response.sync && response.sync.dryRun ? 'sync_dry_run' : 'sync_mutation',
5339
5510
  success: response.success !== false,
5340
5511
  claim: response.sync && response.sync.dryRun ? 'Component sync dry-run observed' : 'Component sync completed',
@@ -6573,7 +6744,7 @@ function shortSignature(signature) {
6573
6744
  async function commitComponentsCommand(flags) {
6574
6745
  const cwd = process.cwd();
6575
6746
  ensureLocalState(cwd);
6576
- const { accountId } = resolveSessionContext(cwd, flags);
6747
+ const { session, accountId } = resolveSessionContext(cwd, flags);
6577
6748
  const branchName = flags.branch || currentBranch(cwd);
6578
6749
  const commitMessage = String(flags.message || ('remits-cli commit sync ' + new Date().toISOString()));
6579
6750
  const allowEmpty = flags['allow-empty'] === true || flags['allow-empty'] === 'true';
@@ -6586,6 +6757,15 @@ async function commitComponentsCommand(flags) {
6586
6757
  if (await refuseUnsafeTrunkCommitBeforeGit(flags, accountId, branchName, skipGit)) {
6587
6758
  return;
6588
6759
  }
6760
+ // The sync's verification answer, asked BEFORE `git push`. Asked inside the sync it could refuse only after the
6761
+ // push, leaving a pushed-but-unsynced branch - the one state this command exists to avoid. The sync below
6762
+ // reuses this answer instead of asking again.
6763
+ const commitVerification = await verificationPreflight(
6764
+ buildAxios(flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL, session.token),
6765
+ cwd, session, accountId, Object.assign({}, flags, { branch: branchName }), {
6766
+ packetType: 'sync_mutation',
6767
+ world: { accountId, dataMode: resolveDataMode(flags, session), branchName, workspace: resolveWorkspace(cwd, flags) }
6768
+ }, { command: 'components commit' });
6589
6769
 
6590
6770
  // Landing is serial and nothing can make it concurrent: `git add -A` sweeps a shared checkout, and the
6591
6771
  // platform pushes a regenerated `account-info.json` back to the branch during sync, so two commits
@@ -6681,7 +6861,8 @@ async function commitComponentsCommand(flags) {
6681
6861
  const syncResponse = await withStdoutRoutedToStderr(jsonOutput, () => syncComponentsCommand({
6682
6862
  ...flags,
6683
6863
  branch: branchName,
6684
- 'account-id': accountId
6864
+ 'account-id': accountId,
6865
+ __verificationPreflight: commitVerification
6685
6866
  }));
6686
6867
  outcome.sync = syncResponse && flagEnabled(flags.summary) ? buildSyncSummary(syncResponse) : (syncResponse || null);
6687
6868
  // Stop here when the sync did not succeed. In --json mode a refused sync gate RETURNS (with a non-zero
@@ -7291,8 +7472,17 @@ function runWorldLines(world, context = {}) {
7291
7472
  if (world.componentOwnerAccountId && world.componentOwnerAccountId !== world.executionAccountId) accounts.push('component owner ' + world.componentOwnerAccountId);
7292
7473
  lines.push(' runs as: ' + accounts.join(', '));
7293
7474
  lines.push(' data lane: ' + (world.dataMode || context.dataMode || 'unknown'));
7294
- const branchSource = world.variantBranchSource ? ' [' + world.variantBranchSource + ']' : '';
7295
- lines.push(' component world: ' + (world.resolves || (world.componentBranch ? "trunk + the '" + world.componentBranch + "' variant overlays" : 'trunk')) + branchSource);
7475
+ const contextVariantWorld = context.variantWorld && typeof context.variantWorld === 'object' ? context.variantWorld : {};
7476
+ const subscribedComponentBranch = world.subscribedComponentBranch || contextVariantWorld.subscribedComponentBranch;
7477
+ 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);
7481
+ const branchLabel = componentBranch
7482
+ ? "componentBranch=" + componentBranch + (branchSource ? ' via ' + branchSource : '')
7483
+ : (branchSource || '');
7484
+ const branchSuffix = branchLabel ? ' [' + branchLabel + ']' : '';
7485
+ lines.push(' component world: ' + (world.resolves || (componentBranch ? "trunk + the '" + componentBranch + "' variant overlays" : 'trunk')) + branchSuffix);
7296
7486
  const laneParts = [world.stagingLane || (world.branchName ? world.branchName + (world.workspace ? ' [ws:' + world.workspace + ']' : '') : 'none')];
7297
7487
  if (world.sharedLane) laneParts.push('(shared lane)');
7298
7488
  if (world.laneContentHash) laneParts.push('content ' + String(world.laneContentHash).slice(0, 12));
@@ -7312,6 +7502,24 @@ function runWorldLines(world, context = {}) {
7312
7502
  lines.push(' test component: ' + world.testComponentSource + (world.testComponentSignature ? ' ' + shortSignature(world.testComponentSignature) : '') +
7313
7503
  (world.testComponentBranch ? " variant '" + world.testComponentBranch + "'" : ''));
7314
7504
  }
7505
+ lines.push(...stagedMaskedByVariantLines(world.stagedMaskedByVariant, world.executionAccountId || context.accountId));
7506
+ return lines;
7507
+ }
7508
+
7509
+ // Staged edits from a TRUNK lane that the execution account will not run, because its branch overrides those
7510
+ // components with committed variants. Production behaves the same way (the variant keeps shadowing trunk), so this
7511
+ // is drift to report, not a bug to route around. Pure.
7512
+ function stagedMaskedByVariantLines(masked, accountId) {
7513
+ if (!Array.isArray(masked) || !masked.length) return [];
7514
+ const lines = [' NOT RUN FOR THIS ACCOUNT: ' + masked.length + ' staged trunk edit(s) are shadowed by account ' +
7515
+ (accountId || '?') + "'s committed variant, exactly as in production:"];
7516
+ masked.slice(0, 10).forEach((entry) => {
7517
+ lines.push(' - ' + entry.type + ' ' + (entry.componentId != null ? entry.componentId + ' ' : '') + (entry.componentName || '') +
7518
+ " (variant " + (entry.variantId || '?') + " on '" + entry.branch + "')");
7519
+ });
7520
+ if (masked.length > 10) lines.push(' ...' + (masked.length - 10) + ' more');
7521
+ lines.push(" To change what this account runs, edit the component on its variant branch ('" + masked[0].branch +
7522
+ "') or promote the trunk change to that branch.");
7315
7523
  return lines;
7316
7524
  }
7317
7525
 
@@ -7517,6 +7725,22 @@ async function testCommand(flags) {
7517
7725
  // --variant-branch explicitly probes a committed variant branch. Normally omitted: variants resolve
7518
7726
  // from the account's subscription edge, which is what production does.
7519
7727
  const variantBranch = flags['variant-branch'];
7728
+ // Asked BEFORE the run: a run whose evidence could only fail the envelope's contract is refused here, not
7729
+ // discovered at `verify report` after the minutes it took.
7730
+ const numericTestRef = /^\d+$/.test(String(testRef));
7731
+ const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
7732
+ packetType: 'test_run',
7733
+ world: {
7734
+ accountId,
7735
+ executionAccountId: asAccountId ? Number(asAccountId) : Number(accountId),
7736
+ dataMode,
7737
+ branchName,
7738
+ workspace,
7739
+ componentBranch: variantBranch || undefined
7740
+ },
7741
+ test: numericTestRef ? { testId: Number(testRef) } : { testName: String(testRef) }
7742
+ }, { command: 'test run' });
7743
+ const activeEnvelopeId = verification.envelopeId;
7520
7744
 
7521
7745
  withStdoutRoutedToStderr(jsonOutput, () => {
7522
7746
  printProdDataBanner({
@@ -7561,6 +7785,7 @@ async function testCommand(flags) {
7561
7785
  host: normalizeBaseUrl(baseUrl),
7562
7786
  accountId: asAccountId || accountId,
7563
7787
  dataMode,
7788
+ variantWorld: start.variantWorld,
7564
7789
  gitHead: safeGitValue(cwd, 'git rev-parse HEAD')
7565
7790
  }).forEach((line) => console.log(line));
7566
7791
  console.log('Test run started:', start.taskId);
@@ -7615,8 +7840,6 @@ async function testCommand(flags) {
7615
7840
  process.exitCode = 1;
7616
7841
  }
7617
7842
 
7618
- const verificationContext = activeVerificationContext(cwd, flags, session, accountId);
7619
- const activeEnvelopeId = verificationEnvelopeIdForCommand(cwd, flags, verificationContext);
7620
7843
  const componentProvenance = testComponentProvenance(status);
7621
7844
  const sourceRevision = collectVerificationSource(cwd, flags);
7622
7845
  const priorPackets = await readVerificationPacketsForDiagnostics(api, cwd, session, accountId, activeEnvelopeId, {
@@ -7636,7 +7859,7 @@ async function testCommand(flags) {
7636
7859
  });
7637
7860
  }
7638
7861
 
7639
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
7862
+ await appendVerificationPacket(api, cwd, session, accountId, verification.evidenceFlags, {
7640
7863
  type: 'test_run',
7641
7864
  success: status.status === 'completed' && !(status.result && status.result.failed > 0) && !unmatched.length,
7642
7865
  claim: 'Test run ' + String(testRef),
@@ -7834,6 +8057,18 @@ async function tokenCommand(flags) {
7834
8057
  // Embeddable resolves that account's branch variants rather than the owner's trunk.
7835
8058
  // --variant-branch explicitly probes a committed variant branch before any edge subscribes.
7836
8059
  const variantBranch = flags['variant-branch'];
8060
+ const tokenAsAccountId = flags['as-account'] || flags['as-account-id'];
8061
+ const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
8062
+ packetType: 'token_inspect',
8063
+ world: {
8064
+ accountId,
8065
+ executionAccountId: tokenAsAccountId ? Number(tokenAsAccountId) : Number(accountId),
8066
+ dataMode,
8067
+ branchName,
8068
+ workspace,
8069
+ componentBranch: variantBranch || undefined
8070
+ }
8071
+ }, { command: 'token' });
7837
8072
  // Send the path so the server can also mint the EMBEDDABLE-SCOPED token key. `tokenKey` opens a
7838
8073
  // tokenized URL in a browser; `embedTokenKey` lets the `<script>` embed loader resolve the component
7839
8074
  // from persisted token context because the loader request sends no path.
@@ -7889,6 +8124,11 @@ async function tokenCommand(flags) {
7889
8124
  if (data.resolution) {
7890
8125
  output.resolution = data.resolution;
7891
8126
  }
8127
+ if (Array.isArray(data.stagedMaskedByVariant) && data.stagedMaskedByVariant.length) {
8128
+ output.stagedMaskedByVariant = data.stagedMaskedByVariant;
8129
+ // stderr: stdout is the JSON document. Said before the URL is opened, not discovered from what renders.
8130
+ stagedMaskedByVariantLines(data.stagedMaskedByVariant, data.accountId).forEach((line) => console.error(line.replace(/^ /, '')));
8131
+ }
7892
8132
 
7893
8133
  // Present only when the path resolved to an Embeddable. `embedTokenKey` is what a host site pastes
7894
8134
  // into the loader snippet; the render shape is echoed because it decides what that host receives.
@@ -7909,7 +8149,7 @@ async function tokenCommand(flags) {
7909
8149
  }
7910
8150
  console.log(JSON.stringify(output, null, 2));
7911
8151
 
7912
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
8152
+ await appendVerificationPacket(api, cwd, session, accountId, verification.evidenceFlags, {
7913
8153
  type: 'token_inspect',
7914
8154
  success: true,
7915
8155
  claim: 'Verification token minted',
@@ -8131,7 +8371,12 @@ async function toolCommand(flags) {
8131
8371
  data.toolMessage = data.toolMessage || toolResultMessage(data.result);
8132
8372
  }
8133
8373
  if (data.status === 'failed' || polledFailed) process.exitCode = 1;
8134
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
8374
+ // Same question as the call that started it: a poll must not attach where the call itself would not.
8375
+ const statusVerification = await verificationPreflight(api, cwd, session, accountId, flags, {
8376
+ packetType: 'tool_call',
8377
+ world: { accountId: statusAccountId, dataMode: data.dataMode || dataMode, branchName: flags.branch || (stored && stored.branchName) || branchName, workspace: resolveWorkspace(cwd, flags) }
8378
+ }, { command: 'tool status', neverRefuse: true, quiet: jsonOutput });
8379
+ await appendVerificationPacket(api, cwd, session, accountId, statusVerification.evidenceFlags, {
8135
8380
  type: 'tool_call',
8136
8381
  success: data.status !== 'failed' && !polledFailed,
8137
8382
  claim: 'Tool status ' + requestedCallId,
@@ -8171,6 +8416,12 @@ async function toolCommand(flags) {
8171
8416
  stderr: jsonOutput
8172
8417
  });
8173
8418
 
8419
+ const toolWorkspace = resolveWorkspace(cwd, flags);
8420
+ const verification = await verificationPreflight(api, cwd, session, accountId, flags, {
8421
+ packetType: 'tool_call',
8422
+ world: { accountId, dataMode, branchName, workspace: toolWorkspace }
8423
+ }, { command: 'tool --name ' + String(toolName) });
8424
+
8174
8425
  const response = await loggedPost(api, cwd, '/cli/tool', {
8175
8426
  token: session.token,
8176
8427
  accountId,
@@ -8180,7 +8431,7 @@ async function toolCommand(flags) {
8180
8431
  // what the call may discover — has to survive that. The server validates it; it is never authorization.
8181
8432
  scopeRootAccountId: accountId,
8182
8433
  branchName,
8183
- workspace: resolveWorkspace(cwd, flags),
8434
+ workspace: toolWorkspace,
8184
8435
  dataMode,
8185
8436
  variantBranch,
8186
8437
  name: String(toolName),
@@ -8260,7 +8511,7 @@ async function toolCommand(flags) {
8260
8511
  if (jsonOutput) Object.assign(data, { finalStatus: finalStatus.data });
8261
8512
  }
8262
8513
 
8263
- await appendVerificationPacket(api, cwd, session, accountId, flags, {
8514
+ await appendVerificationPacket(api, cwd, session, accountId, verification.evidenceFlags, {
8264
8515
  type: 'tool_call',
8265
8516
  success: asyncMode && !waitForAsync ? null : (!toolFailed && (!asyncMode || process.exitCode !== 1)),
8266
8517
  pending: asyncMode && !waitForAsync,
@@ -8478,6 +8729,29 @@ async function corpusCommand(flags, subcommand) {
8478
8729
  return data;
8479
8730
  }
8480
8731
 
8732
+ // `verify token` / `verify tool` say "prove this for the envelope", so without an explicit --data-mode they run
8733
+ // in the envelope's lane when that lane is TEST. A plain `token`/`tool` follows the session, and a session parked
8734
+ // on prod otherwise refuses (or silently detaches) against the test envelope the agent just started. Never
8735
+ // adopts PROD: production proof keeps requiring an explicit --data-mode prod, which the refusal names.
8736
+ async function verifyWrapperFlags(api, cwd, session, accountId, flags, envelopeId) {
8737
+ const pinned = Object.assign({}, flags, { 'verify-envelope': envelopeId });
8738
+ if (hasExplicitDataModeFlag(flags)) return pinned;
8739
+ try {
8740
+ const result = await postVerificationCommand(api, cwd, session, accountId, 'compatibility', {
8741
+ envelopeId,
8742
+ probe: { packetType: 'manual_observation', world: {} }
8743
+ });
8744
+ const lane = result.comparisonWorld && result.comparisonWorld.dataMode;
8745
+ if (lane && normalizeDataMode(lane) === 'test' && resolveDataMode(flags, session) !== 'test') {
8746
+ console.error('Data lane: test (the lane envelope ' + envelopeId + ' proves; your session is on ' + resolveDataMode(flags, session) + ')');
8747
+ return Object.assign(pinned, { 'data-mode': 'test' });
8748
+ }
8749
+ } catch (_) {
8750
+ // The command's own preflight still answers; this only saves a turn.
8751
+ }
8752
+ return pinned;
8753
+ }
8754
+
8481
8755
  async function verifyCommand(flags, subcommand) {
8482
8756
  const cwd = process.cwd();
8483
8757
  ensureLocalState(cwd);
@@ -8486,11 +8760,26 @@ async function verifyCommand(flags, subcommand) {
8486
8760
  const sessionContext = resolveSessionContext(cwd, flags);
8487
8761
  const { session, accountId } = sessionContext;
8488
8762
  const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
8489
- const dataMode = resolveDataMode(flags, session);
8763
+ // `verify start` proves in the TEST lane unless told otherwise, for the reason `test run` does: production
8764
+ // proof needs explicit provenance. It used to inherit the session's lane, so a session parked on prod started
8765
+ // prod envelopes that every default `test run` then contradicted.
8766
+ const dataMode = sub === 'start' && !hasExplicitDataModeFlag(flags)
8767
+ ? DEFAULT_DATA_MODE
8768
+ : resolveDataMode(flags, session);
8490
8769
  const branchName = flags.branch || currentBranch(cwd);
8491
8770
  const workspace = resolveWorkspace(cwd, flags);
8492
8771
  const api = buildAxios(baseUrl, session.token);
8772
+ // Selection is by checkout world only (no data lane) - see verificationContextKey.
8493
8773
  const activeContext = activeVerificationContext(cwd, flags, session, accountId);
8774
+ const checkoutWorldLine = 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared');
8775
+ // What a plain `test run` from here would record, for the "would it count" probe.
8776
+ const testRunWorld = {
8777
+ accountId,
8778
+ executionAccountId: Number(accountId),
8779
+ dataMode: hasExplicitDataModeFlag(flags) ? resolveDataMode(flags, null) : DEFAULT_DATA_MODE,
8780
+ branchName,
8781
+ workspace
8782
+ };
8494
8783
 
8495
8784
  if (sub === 'current') {
8496
8785
  const active = readActiveVerificationEnvelope(cwd, activeContext);
@@ -8498,18 +8787,30 @@ async function verifyCommand(flags, subcommand) {
8498
8787
  printResolvedBaseUrl(baseUrl);
8499
8788
  printLocalCommandContext(cwd, flags, { accountId, branchName, workspace, dataMode });
8500
8789
  printLocalStateWarnings(cwd, flags);
8501
- console.log('Context:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
8790
+ console.log('Checkout world:', checkoutWorldLine);
8502
8791
  if (!active) {
8503
- const legacy = readActiveVerificationEnvelope(cwd);
8504
- console.log('No active verification envelope for this context.');
8505
- if (legacy) {
8506
- console.log('Legacy active envelope ignored for this context:', legacy);
8792
+ console.log('No active verification envelope for this checkout world.');
8793
+ // Name the envelopes selected in SIBLING worlds (another workspace/branch of this checkout): the usual
8794
+ // cause is a workspace or branch switch, and "start a new one" is the wrong next step for that.
8795
+ const siblings = Object.values(readActiveVerificationContexts(cwd))
8796
+ .filter((entry) => entry && typeof entry === 'object' && entry.envelopeId && entry.context &&
8797
+ String(entry.context.accountId) === String(accountId))
8798
+ .sort((left, right) => String(right.updatedAt || '').localeCompare(String(left.updatedAt || '')))
8799
+ .filter((entry, index, all) => all.findIndex((other) => other.envelopeId === entry.envelopeId) === index)
8800
+ .slice(0, 3);
8801
+ siblings.forEach((entry) => {
8802
+ console.log(' selected in another world: ' + entry.envelopeId + ' (branch=' + (entry.context.branchName || '?') +
8803
+ ', workspace=' + (entry.context.workspace || 'shared') + ')');
8804
+ });
8805
+ if (siblings.length) {
8806
+ console.log(' Switch back to that workspace/branch, or `remits-cli verify use <id>` to select one here.');
8507
8807
  }
8508
8808
  return;
8509
8809
  }
8510
8810
  console.log('Active verification envelope:', active);
8511
8811
  const local = readLocalVerificationEnvelope(cwd, active);
8512
8812
  if (local && local.summary) console.log('Summary:', local.summary);
8813
+ await printVerificationRunProbe(api, cwd, session, accountId, active, testRunWorld);
8513
8814
  return;
8514
8815
  }
8515
8816
 
@@ -8520,7 +8821,8 @@ async function verifyCommand(flags, subcommand) {
8520
8821
  console.log('Active verification envelope:', envelopeId);
8521
8822
  printLocalCommandContext(cwd, flags, { accountId, branchName, workspace, dataMode });
8522
8823
  printLocalStateWarnings(cwd, flags);
8523
- console.log('Context:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
8824
+ console.log('Checkout world:', checkoutWorldLine);
8825
+ await printVerificationRunProbe(api, cwd, session, accountId, String(envelopeId), testRunWorld);
8524
8826
  return;
8525
8827
  }
8526
8828
 
@@ -8531,9 +8833,9 @@ async function verifyCommand(flags, subcommand) {
8531
8833
  console.log('All active verification envelope pointers cleared for this checkout.');
8532
8834
  } else {
8533
8835
  writeActiveVerificationEnvelope(cwd, null, activeContext);
8534
- console.log('Verification envelope cleared for this context.');
8836
+ console.log('Verification envelope cleared for this checkout world.');
8535
8837
  printLocalCommandContext(cwd, flags, { accountId, branchName, workspace, dataMode });
8536
- console.log('Context:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
8838
+ console.log('Checkout world:', checkoutWorldLine);
8537
8839
  }
8538
8840
  return;
8539
8841
  }
@@ -8586,7 +8888,13 @@ async function verifyCommand(flags, subcommand) {
8586
8888
  console.log('Verification envelope started:', envelope.envelopeId);
8587
8889
  printLocalCommandContext(cwd, flags, { accountId, branchName, workspace, dataMode });
8588
8890
  printLocalStateWarnings(cwd, flags);
8589
- console.log('Target:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
8891
+ console.log('Target:', checkoutWorldLine + ', dataMode=' + dataMode +
8892
+ (hasExplicitDataModeFlag(flags) ? '' : ' (verify start default; pass --data-mode prod to prove against production data)'));
8893
+ // Say NOW whether a plain `test run` from here will count - a prod lane or a manifest world elsewhere both
8894
+ // mean it will not - rather than after the first refusal. A PLAIN run: `--data-mode prod` on `verify start`
8895
+ // names the envelope's lane, not the run's. Silent when it attaches.
8896
+ await printVerificationRunProbe(api, cwd, session, accountId, envelope.envelopeId,
8897
+ Object.assign({}, testRunWorld, { dataMode: DEFAULT_DATA_MODE }), { onlyMismatch: true });
8590
8898
  printAcceptanceWarnings(envelope);
8591
8899
  printEnvelopeWarnings(envelope);
8592
8900
  console.log('Local mirror:', verificationPaths(cwd, envelope.envelopeId).base);
@@ -8715,7 +9023,7 @@ async function verifyCommand(flags, subcommand) {
8715
9023
  return await testCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
8716
9024
  }
8717
9025
  if (sub === 'token') {
8718
- return await tokenCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
9026
+ return await tokenCommand(await verifyWrapperFlags(api, cwd, session, accountId, flags, envelopeId));
8719
9027
  }
8720
9028
  if (sub === 'stage') {
8721
9029
  return await pushComponentsCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
@@ -8724,7 +9032,7 @@ async function verifyCommand(flags, subcommand) {
8724
9032
  return await syncComponentsCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
8725
9033
  }
8726
9034
  if (sub === 'tool') {
8727
- return await toolCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
9035
+ return await toolCommand(await verifyWrapperFlags(api, cwd, session, accountId, flags, envelopeId));
8728
9036
  }
8729
9037
 
8730
9038
  if (sub === 'invalidate') {
@@ -8756,6 +9064,12 @@ async function verifyCommand(flags, subcommand) {
8756
9064
  if (sub === 'status' || sub === 'report') {
8757
9065
  let statusPacket = null;
8758
9066
  try {
9067
+ // Only attach the status observation when it is from the envelope's world; otherwise it is noise the
9068
+ // report would list as wrong-world evidence on every status check.
9069
+ const statusVerification = await verificationPreflight(api, cwd, session, accountId, { 'verify-envelope': envelopeId }, {
9070
+ packetType: 'component_status',
9071
+ world: { accountId, dataMode, branchName, workspace }
9072
+ }, { command: 'verify status', quiet: true, neverRefuse: true });
8759
9073
  const componentStatus = await loggedPost(api, cwd, '/cli/components', {
8760
9074
  token: session.token,
8761
9075
  accountId,
@@ -8774,22 +9088,16 @@ async function verifyCommand(flags, subcommand) {
8774
9088
  rawRefs: { command: 'components status' },
8775
9089
  componentStatus
8776
9090
  };
8777
- await appendVerificationPacket(api, cwd, session, accountId, { 'verify-envelope': envelopeId }, statusPacket, { quiet: true });
9091
+ await appendVerificationPacket(api, cwd, session, accountId, statusVerification.evidenceFlags, statusPacket, { quiet: true });
8778
9092
  } catch (err) {
8779
9093
  if (!flagEnabled(flags.json)) {
8780
9094
  console.error('Warning: could not attach component_status packet before rendering report:', describeError(err));
8781
9095
  }
8782
9096
  }
8783
- const response = await postVerificationCommand(api, cwd, session, accountId, sub, {
8784
- envelopeId,
8785
- currentWorld: buildCommandWorld(statusPacket && statusPacket.componentStatus, {
8786
- accountId,
8787
- dataMode,
8788
- branchName,
8789
- workspace,
8790
- host: normalizeBaseUrl(baseUrl)
8791
- })
8792
- });
9097
+ // No `currentWorld`: status and report judge the SAME world (manifest, else the envelope's start world), so
9098
+ // they cannot give two verdicts on one envelope. Sending the session's world made every --as-account packet
9099
+ // wrong-world under `status` while `report` verified it, and made the verdict depend on the session lane.
9100
+ const response = await postVerificationCommand(api, cwd, session, accountId, sub, { envelopeId });
8793
9101
  if (response.envelope) writeLocalVerificationEnvelope(cwd, response.envelope);
8794
9102
  if (response.report) {
8795
9103
  console.log(response.report);
@@ -13542,6 +13850,137 @@ function describeRunLocation(record) {
13542
13850
  return parts.join(' ');
13543
13851
  }
13544
13852
 
13853
+ async function activityInspectCommand(flags) {
13854
+ const cwd = process.cwd();
13855
+ ensureLocalState(cwd);
13856
+ const sessionContext = resolveSessionContext(cwd, flags);
13857
+ const { session, accountId } = sessionContext;
13858
+ const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
13859
+ const api = buildAxios(baseUrl, session.token, 30000);
13860
+ const response = await loggedPost(api, cwd, '/cli/activityInspect', {
13861
+ token: session.token,
13862
+ accountId,
13863
+ scope: flags.scope || 'self',
13864
+ dataMode: resolveDataMode(flags, session),
13865
+ userId: flags['user-id'] || flags.userId || flags['cli-user-id'],
13866
+ userEmail: flags['user-email'] || flags.email,
13867
+ limit: flags.limit || flags.max || 20,
13868
+ envelopeLimit: flags['envelope-limit'] || flags.envelopeLimit
13869
+ }).then((r) => r.data);
13870
+
13871
+ if (!response.success) throw new Error(response.message || 'Activity inspection failed');
13872
+ if (flagEnabled(flags.json)) {
13873
+ console.log(JSON.stringify(response, null, 2));
13874
+ return response;
13875
+ }
13876
+
13877
+ printSessionResolutionWarning(sessionContext);
13878
+ printResolvedBaseUrl(baseUrl);
13879
+ printActivityInspectSummary(response);
13880
+ return response;
13881
+ }
13882
+
13883
+ function printActivityInspectSummary(response) {
13884
+ const subject = response.subjectUser
13885
+ ? ' for ' + (response.subjectUser.email || response.subjectUser.name || ('user ' + response.subjectUser.id))
13886
+ : '';
13887
+ console.log('Activity inspection' + subject + ' — ' + response.dataMode + ' lane');
13888
+ console.log('Scope: ' + (response.accountName || ('account ' + response.accountId)) +
13889
+ ' (' + response.accountId + '), ' + (response.scope || 'self') +
13890
+ ' · accounts ' + ((response.accountIds || []).join(', ') || response.accountId));
13891
+ const counts = response.counts || {};
13892
+ console.log('Counts: ' +
13893
+ (counts.agents || 0) + ' agent(s), ' +
13894
+ (counts.lanes || 0) + ' lane(s), ' +
13895
+ (counts.verificationEnvelopes || 0) + ' envelope(s), ' +
13896
+ (counts.testRuns || 0) + ' test run(s), ' +
13897
+ (counts.corpusResults || 0) + ' corpus result(s)');
13898
+
13899
+ const smells = response.smells || [];
13900
+ console.log('');
13901
+ if (!smells.length) {
13902
+ console.log('Smells: none detected in this slice.');
13903
+ } else {
13904
+ console.log('Smells (' + smells.length + '):');
13905
+ smells.slice(0, 20).forEach((smell) => {
13906
+ console.log(' [' + (smell.severity || 'info') + '] ' + (smell.type || 'signal') + ': ' + smell.summary);
13907
+ const pivots = [
13908
+ smell.accountId ? 'account ' + smell.accountId : null,
13909
+ smell.laneId ? 'lane ' + smell.laneId : null,
13910
+ smell.envelopeId ? 'envelope ' + smell.envelopeId : null,
13911
+ smell.taskId ? 'task ' + smell.taskId : null,
13912
+ smell.testName || null,
13913
+ smell.branchName ? 'branch ' + smell.branchName : null,
13914
+ smell.workspace ? 'ws:' + smell.workspace : null
13915
+ ].filter(Boolean);
13916
+ if (pivots.length) console.log(' ' + pivots.join(' · '));
13917
+ });
13918
+ if (smells.length > 20) console.log(' ...' + (smells.length - 20) + ' more; rerun with --json');
13919
+ }
13920
+
13921
+ const lanes = response.lanes || [];
13922
+ if (lanes.length) {
13923
+ console.log('');
13924
+ console.log('Current staging lanes:');
13925
+ lanes.slice(0, 12).forEach((lane) => {
13926
+ const ws = lane.workspace ? ' [ws:' + lane.workspace + ']' : ' [shared]';
13927
+ const workset = lane.worksetKnown && lane.worksetCountAsOf != null
13928
+ ? ' workset ' + lane.worksetCountAsOf
13929
+ : ' workset unknown';
13930
+ console.log(' - account ' + lane.accountId + ' ' + (lane.branchName || '?') + ws +
13931
+ ' overlay ' + (lane.stagedCountAsOf || 0) + workset +
13932
+ (lane.userEmail ? ' · ' + lane.userEmail : '') +
13933
+ (lane.expiresInSeconds != null ? ' · expires ~' + Math.round(lane.expiresInSeconds / 60) + 'm' : ''));
13934
+ });
13935
+ if (lanes.length > 12) console.log(' ...' + (lanes.length - 12) + ' more lane(s)');
13936
+ }
13937
+
13938
+ const envelopes = response.envelopes || [];
13939
+ if (envelopes.length) {
13940
+ console.log('');
13941
+ console.log('Recent verification envelopes:');
13942
+ envelopes.slice(0, 8).forEach((env) => {
13943
+ console.log(' - ' + (env.status || 'unknown') + ' · ' + (env.packetCount || 0) + ' packet(s) · ' +
13944
+ (env.summary || env.envelopeId) + ' · ' + formatTime(env.updatedAtMs || env.lastPacketAtMs || env.createdAt));
13945
+ if (env.currentFailureCount) console.log(' current failures: ' + env.currentFailureCount);
13946
+ });
13947
+ }
13948
+
13949
+ const runs = response.testRuns || [];
13950
+ if (runs.length) {
13951
+ console.log('');
13952
+ console.log('Recent test runs:');
13953
+ runs.slice(0, 8).forEach((run) => {
13954
+ console.log(' - ' + (run.testName || run.taskId) + ' · ' + (run.passed || 0) + '/' + (run.total || 0) +
13955
+ ' passed · ' + (run.branchName || '?') + (run.workspace ? ' [ws:' + run.workspace + ']' : '') +
13956
+ ' · ' + corpusAiLabel(run) + ' · ' + formatTime(run.completedAt || run.startedAt));
13957
+ });
13958
+ }
13959
+
13960
+ const corpus = response.corpusResults || [];
13961
+ if (corpus.length) {
13962
+ console.log('');
13963
+ console.log('Recent corpus measurements:');
13964
+ corpus.slice(0, 6).forEach((row) => {
13965
+ console.log(' - ' + (row.corpus || '?') + '/' + (row.caseKey || '?') + ' · ' +
13966
+ (row.outcome || 'unknown') + ' · ' + (row.testName || row.taskId) +
13967
+ ' · ' + formatTime(row.recordedAt));
13968
+ });
13969
+ }
13970
+
13971
+ const timeline = response.timeline || [];
13972
+ if (timeline.length) {
13973
+ console.log('');
13974
+ console.log('Timeline:');
13975
+ timeline.slice(0, 12).forEach((item) => {
13976
+ console.log(' - ' + formatTime(item.at) + ' · ' + item.type + ' · ' +
13977
+ (item.label || '') + (item.detail ? ' · ' + item.detail : ''));
13978
+ });
13979
+ }
13980
+ console.log('');
13981
+ console.log('Full structured payload: remits-cli activity inspect --json');
13982
+ }
13983
+
13545
13984
  /**
13546
13985
  * The live work map: which repositories are being edited, by whom, and who else is around.
13547
13986
  *
@@ -13815,6 +14254,7 @@ async function agentCommand(flags, subcommand) {
13815
14254
  case 'work': return agentWorkCommand(flags);
13816
14255
  case 'list': case 'ls': return agentListCommand(flags);
13817
14256
  case 'map': case 'work-map': case 'workmap': return agentWorkMapCommand(flags);
14257
+ case 'inspect': case 'activity': return activityInspectCommand(flags);
13818
14258
  case 'repos': case 'repo': return agentReposCommand(flags);
13819
14259
  case 'release': case 'deregister': case 'stop': return agentReleaseCommand(flags);
13820
14260
  case 'heartbeat-daemon': return agentHeartbeatDaemonCommand(flags);
@@ -13867,6 +14307,8 @@ function printAgentHelp() {
13867
14307
  console.log('');
13868
14308
  console.log(' list [--account-id ID] [--json] Who else is available for an account.');
13869
14309
  console.log(' map [--account-ids 1,4] [--json] Who is EDITING which repository right now.');
14310
+ console.log(' inspect [--account-id ID] [--scope self|children] [--user-email E] [--json]');
14311
+ console.log(' Read-only workstream story + smell signals for another CLI user or agent fleet.');
13870
14312
  console.log(' One agent at a time may edit an account\'s repo (the edit lease); everyone else');
13871
14313
  console.log(' investigates read-only. Check this before starting work that changes files.');
13872
14314
  console.log(' repos [--rescan] [--json] WHERE each account is checked out on this machine —');
@@ -14505,7 +14947,7 @@ function printVerifyHelp() {
14505
14947
  console.log('Usage: remits-cli verify <subcommand>');
14506
14948
  console.log('');
14507
14949
  console.log('Envelope lifecycle:');
14508
- console.log(' remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID]');
14950
+ console.log(' remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID] [--data-mode test|prod]');
14509
14951
  console.log(' remits-cli verify list [--max 50] [--json]');
14510
14952
  console.log(' remits-cli verify use <envelopeId>');
14511
14953
  console.log(' remits-cli verify current');
@@ -14515,7 +14957,8 @@ function printVerifyHelp() {
14515
14957
  console.log(' remits-cli verify report [--envelope ID]');
14516
14958
  console.log(' remits-cli verify abandon --envelope ID --reason "..."');
14517
14959
  console.log(' remits-cli verify supersede --envelope ID --superseded-by ID [--reason "..."]');
14518
- console.log(' Active envelopes are scoped by host, account, branch, workspace, and data mode.');
14960
+ console.log(' One active envelope per checkout world: host, account, git branch, workspace (not data lane).');
14961
+ console.log(' verify start proves in the test lane unless you pass --data-mode prod.');
14519
14962
  console.log('');
14520
14963
  console.log('Manifest and manual evidence:');
14521
14964
  console.log(' remits-cli verify manifest --file file.json [--envelope ID]');
@@ -14531,6 +14974,14 @@ function printVerifyHelp() {
14531
14974
  console.log(' remits-cli verify sync --safe');
14532
14975
  console.log(' remits-cli verify tool --name mcp_tool --input \'{...}\'');
14533
14976
  console.log('');
14977
+ console.log('World check (asked of the platform before stage, test run, token, tool, sync, and commit run):');
14978
+ console.log(' attach same world as the envelope requires - evidence attaches.');
14979
+ console.log(' refuse different world AND a requirement could use this evidence - nothing runs; the');
14980
+ console.log(' refusal names the flag that fixes it (e.g. --data-mode prod, --as-account 21).');
14981
+ console.log(' detach different world, nothing in the manifest could use it - runs, attaches nothing, says so.');
14982
+ console.log(' --allow-wrong-world-evidence attaches anyway as context (the report lists it wrong-world).');
14983
+ console.log(' verify token / verify tool run in the envelope\'s lane when it is test.');
14984
+ console.log('');
14534
14985
  console.log('Existing commands also accept --verify-envelope <id> and --no-verify-envelope.');
14535
14986
  }
14536
14987
 
@@ -14601,6 +15052,7 @@ async function main() {
14601
15052
  console.log(' remits-cli agent status [--state idle|working|paused] [--ticket ID] [--activity "..."]');
14602
15053
  console.log(' remits-cli agent list [--account-id ID] # who else is available');
14603
15054
  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]');
14604
15056
  console.log(' remits-cli agent release # stop receiving tickets now');
14605
15057
  console.log(' remits-cli start [--foreground true] [--port 8787] # human dashboard in the browser');
14606
15058
  console.log(' remits-cli stop');
@@ -14823,6 +15275,22 @@ async function main() {
14823
15275
  return;
14824
15276
  }
14825
15277
 
15278
+ if (command === 'activity') {
15279
+ 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]');
15281
+ console.log('');
15282
+ console.log('Read-only workstream inspection for agent/operator review. Joins current staging lanes,');
15283
+ console.log('verification envelopes, durable test runs, corpus measurements, live agent presence,');
15284
+ console.log('and heuristic smell signals without changing any lane or checkout.');
15285
+ return;
15286
+ }
15287
+ if (subcommand === 'inspect') {
15288
+ await activityInspectCommand(args);
15289
+ return;
15290
+ }
15291
+ throw new Error('Unknown activity subcommand: ' + subcommand);
15292
+ }
15293
+
14826
15294
  if (command === 'ticket') {
14827
15295
  if (wantsHelp || !subcommand) {
14828
15296
  printTicketHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remits/remits-cli",
3
- "version": "0.1.130",
3
+ "version": "0.1.132",
4
4
  "description": "Local CLI for auth, component sync, and live test execution against Remits",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -170,8 +170,9 @@ resolves `X`.
170
170
 
171
171
  **Subscribing to a branch and resolving through it are different facts.** An account subscribes on an
172
172
  edge; a *request* resolves through that edge only when something named the path (`--as-account`, the
173
- edge's own host, an explicit `--variant-branch`). An account with no `parentId` and several upward links
174
- resolves trunk by design — the resolver refuses to guess a path. So this is a normal, explainable state:
173
+ edge's own host, an explicit `--variant-branch`), or when its links leave no doubt — a single upward link, or
174
+ several with exactly one carrying a branch, which every account beneath it inherits too. An account whose
175
+ several links carry several branches refuses to guess a path. So this is a normal, explainable state:
175
176
 
176
177
  ```
177
178
  "componentBranch": null, // this token resolves TRUNK
@@ -421,9 +422,14 @@ a confirm-gated override when the removal guard refuses. Preview there is the sa
421
422
  - A test/tool response reports `testComponentSource` as `staged` | `variant` | `db`, so you can see which
422
423
  layer the run resolved without reading logs.
423
424
 
424
- > **A populated staging cache makes a variant look broken.** Anything carrying a CLI `TestMode` — a
425
+ > **A trunk lane never overrides a variant.** Staged edits from your account's trunk branch do not reach an
426
+ > account whose branch overrides that component — production behaves the same way — so `test run` and `token`
427
+ > list them under **NOT RUN FOR THIS ACCOUNT** (`stagedMaskedByVariant`). To change what that account runs, edit
428
+ > the component on its variant branch.
429
+ >
430
+ > **A populated NON-trunk staging lane makes a variant look broken.** Anything carrying a CLI `TestMode` — a
425
431
  > `remits-cli token` URL, `/s/<tokenKey>/...`, an `X-Auth-Token` request, a script-loader embed — resolves
426
- > the STAGED layer, which outranks the variant. So with components staged under the same branch/user, a
432
+ > a variant-branch or feature-branch lane's STAGED layer, which outranks the variant. So with components staged under the same branch/user, a
427
433
  > tokenized page reports `Component Branch: <branch>` but `Variant Applied: none` and renders trunk, while
428
434
  > an anonymous `?account_id=` request to the same page reports the variant. That is the documented
429
435
  > precedence (staged -> variant -> trunk) working correctly, and it reads exactly like "tokens break
@@ -173,6 +173,8 @@ remits-cli agent work [--wait SECONDS] [--json]
173
173
  remits-cli agent status [--state idle|working|paused] [--ticket ID] [--activity "..."] [--step "..."]
174
174
  remits-cli agent list [--account-id ID] [--json]
175
175
  remits-cli agent map [--account-ids 1,4] [--json] # who is EDITING which repository, from which checkout/branch/staging lane
176
+ remits-cli agent inspect [--account-id ID] [--scope self|children] [--user-id ID|--user-email EMAIL] [--limit N] [--json]
177
+ remits-cli activity inspect [--account-id ID] [--scope self|children] [--user-id ID|--user-email EMAIL] [--limit N] [--json]
176
178
  remits-cli agent release
177
179
  remits-cli ticket read|accept|status|progress|complete|release|reopen|assign|planning --ticket ID [--status S] [--resolution "..."] [--summary "..."] [--category C] [--assignee EMAIL] [--workstream VALUE] [--planned-in VALUE] [--board-stage VALUE] [--rank N] [--size VALUE] [--blocked-by VALUE] [--notes "..."]
178
180
  remits-cli ticket lease|unlease|force-unlease --ticket ID [--reason "..."] # force-unlease breaks SOMEBODY ELSE'S lease; operator only
@@ -236,7 +238,7 @@ remits-cli token inspect --token <token|tokenKey|URL> # inspect
236
238
  remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
237
239
  remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
238
240
  remits-cli tool status --call-id <callId> [--data-mode test|prod]
239
- remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID]
241
+ remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID] [--data-mode test|prod] # default: test
240
242
  remits-cli verify list [--max 50] [--json]
241
243
  remits-cli verify use <envelopeId>
242
244
  remits-cli verify current
@@ -261,6 +263,30 @@ remits-cli verify abandon --envelope ID --reason "..."
261
263
  remits-cli verify supersede --envelope ID --superseded-by <envelopeId> [--reason "..."]
262
264
  ```
263
265
 
266
+ ### Activity inspection
267
+
268
+ `remits-cli activity inspect` is a read-only operator view for understanding an agent or account
269
+ workstream without touching a checkout or staging lane. It joins the current staging-lane registry,
270
+ verification envelopes, durable Test runs, corpus measurements, and live CLI agent presence, then adds
271
+ heuristic smell signals such as shared lanes, broad overlays, retained staged entries, unfinished
272
+ verification, current evidence failures, repeated test failures, corpus failures, and tests that were run
273
+ outside a verification envelope.
274
+
275
+ Use it from the platform repo when a remote agent fleet is looping or a human reports that work is going
276
+ around in circles:
277
+
278
+ ```bash
279
+ remits-cli activity inspect --account-id 4 --scope children --data-mode test
280
+ remits-cli activity inspect --account-id 4 --scope children --user-email jason@example.com --json
281
+ remits-cli agent inspect --account-id 1742 --limit 50
282
+ ```
283
+
284
+ `--scope children` expands from the selected account to its hierarchy children, which is usually the right
285
+ shape for platform-level review. `--user-id` / `--user-email` filters the stream to one CLI user when the
286
+ platform can identify them from staged lanes, envelopes, tests, or live agent registration. `--json`
287
+ returns the full structured payload for deeper analysis; the text view is intentionally compact and
288
+ human-readable.
289
+
264
290
  ### Verification envelopes
265
291
 
266
292
  Use a verification envelope for workflow-shaped work: concrete user journeys, browser-facing changes,
@@ -84,6 +84,25 @@ command world. While active, `components stage`, `components status`, `test run`
84
84
  `--verify-envelope <id>` to name one explicitly or `--no-verify-envelope` when a command should not be
85
85
  attached.
86
86
 
87
+ `verify start` proves in the **test** lane unless you pass `--data-mode prod` (your session's lane does not
88
+ decide it). One envelope is active per checkout world (host, account, git branch, workspace); the data lane
89
+ is not part of that selection.
90
+
91
+ Before stage, test run, token, tool, sync or commit runs, the CLI asks the platform whether its evidence
92
+ would count for the active envelope:
93
+
94
+ - **attach**: same world, so the evidence attaches.
95
+ - **refuse**: different world and a required evidence item could use this packet. Nothing ran, and the
96
+ refusal names the fix (`--data-mode prod`, `--as-account 21`, `--workspace x`, ...). Apply the fix that
97
+ matches your intent. Use `--no-verify-envelope` when the run is not meant as proof, or `verify start` when
98
+ it is different work. `--allow-wrong-world-evidence` only records context; that evidence never satisfies
99
+ anything.
100
+ - **detach**: different world and nothing in the manifest could use it. The command runs and one line says
101
+ nothing was attached.
102
+
103
+ `verify current` / `verify use` print whether a plain `test run` from this checkout would attach, so you can
104
+ catch a mismatch before the first run.
105
+
87
106
  Use the wrappers when you want the intent to be unmistakable:
88
107
 
89
108
  ```bash
@@ -237,9 +237,10 @@ client accounts beneath it:
237
237
  > is a fully supported shape: it resolves the branch, **and so do all of its descendants**. You do not need
238
238
  > to make the subscriber a structural child of its owner.
239
239
  >
240
- > What is NOT resolved by default is genuine **ambiguity** — an account with *several* upward links, where
241
- > the platform refuses to guess which product it was reached through. That account (and its descendants)
242
- > resolve trunk until a request names the path: `--as-account`, `--variant-branch`, or the edge's own host.
240
+ > Several upward links are fine when exactly one carries the branch: the account and its descendants roll up
241
+ > through that subscription. What is NOT resolved by default is genuine **ambiguity** — several links and no
242
+ > single subscription among them. That account (and its descendants) resolve nothing above it until a request
243
+ > names the path: `--as-account`, `--variant-branch`, or the edge's own host.
243
244
  > An account that has a `parentId` **and** a separate membership edge carrying the branch is this case: the
244
245
  > `parentId` wins, so put the branch on the link the account actually inherits through.
245
246
  >