@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 +2 -0
- package/index.js +555 -87
- package/package.json +1 -1
- package/skills/remits-cli/references/branch-variants.md +10 -4
- package/skills/remits-cli/references/command-reference.md +27 -1
- package/skills/remits-cli/references/development-loop.md +19 -0
- package/skills/remits-cli/references/tool-reference.md +4 -3
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
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
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
|
-
|
|
1420
|
-
|
|
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
|
|
1456
|
-
|
|
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
|
|
1505
|
-
if (explicit
|
|
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
|
-
|
|
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
|
|
1686
|
-
//
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
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
|
-
//
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
'.
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
7295
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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('
|
|
8790
|
+
console.log('Checkout world:', checkoutWorldLine);
|
|
8502
8791
|
if (!active) {
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
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('
|
|
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
|
|
8836
|
+
console.log('Verification envelope cleared for this checkout world.');
|
|
8535
8837
|
printLocalCommandContext(cwd, flags, { accountId, branchName, workspace, dataMode });
|
|
8536
|
-
console.log('
|
|
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:',
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
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('
|
|
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
|
@@ -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`)
|
|
174
|
-
|
|
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
|
|
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
|
-
>
|
|
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
|
-
>
|
|
241
|
-
>
|
|
242
|
-
>
|
|
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
|
>
|