@remits/remits-cli 0.1.116 → 0.1.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +625 -1
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +7 -0
- package/skills/remits-cli/references/command-reference.md +79 -0
- package/skills/remits-cli/references/component-resolution.md +13 -0
- package/skills/remits-cli/references/development-loop.md +35 -0
- package/skills/remits-cli/references/support-tickets.md +21 -0
package/index.js
CHANGED
|
@@ -544,6 +544,8 @@ function localStatePaths(cwd) {
|
|
|
544
544
|
toolsDir: path.join(base, 'tools'),
|
|
545
545
|
sessionsDir: path.join(base, 'sessions'),
|
|
546
546
|
toolResponsesDir: path.join(base, 'tool-responses'),
|
|
547
|
+
verificationDir: path.join(base, 'verification'),
|
|
548
|
+
activeVerificationFile: path.join(base, 'verification', 'active'),
|
|
547
549
|
currentSessionFile: path.join(base, 'current-session.txt'),
|
|
548
550
|
workspaceFile: path.join(base, 'workspace')
|
|
549
551
|
};
|
|
@@ -649,6 +651,7 @@ function ensureLocalState(cwd) {
|
|
|
649
651
|
ensureDir(paths.toolsDir);
|
|
650
652
|
ensureDir(paths.sessionsDir);
|
|
651
653
|
ensureDir(paths.toolResponsesDir);
|
|
654
|
+
ensureDir(paths.verificationDir);
|
|
652
655
|
return paths;
|
|
653
656
|
}
|
|
654
657
|
|
|
@@ -755,6 +758,196 @@ function readToolResponseFile(cwd, callId) {
|
|
|
755
758
|
}
|
|
756
759
|
}
|
|
757
760
|
|
|
761
|
+
function verificationPaths(cwd, envelopeId) {
|
|
762
|
+
const paths = ensureLocalState(cwd);
|
|
763
|
+
const base = envelopeId ? path.join(paths.verificationDir, String(envelopeId)) : paths.verificationDir;
|
|
764
|
+
return {
|
|
765
|
+
root: paths.verificationDir,
|
|
766
|
+
activeFile: paths.activeVerificationFile,
|
|
767
|
+
base,
|
|
768
|
+
envelopeFile: path.join(base, 'envelope.json'),
|
|
769
|
+
manifestFile: path.join(base, 'manifest.json'),
|
|
770
|
+
packetsDir: path.join(base, 'packets')
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function readActiveVerificationEnvelope(cwd) {
|
|
775
|
+
const file = verificationPaths(cwd).activeFile;
|
|
776
|
+
try {
|
|
777
|
+
if (!fs.existsSync(file)) return null;
|
|
778
|
+
const id = fs.readFileSync(file, 'utf8').trim();
|
|
779
|
+
return id || null;
|
|
780
|
+
} catch (_) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function writeActiveVerificationEnvelope(cwd, envelopeId) {
|
|
786
|
+
const paths = verificationPaths(cwd);
|
|
787
|
+
if (!envelopeId) {
|
|
788
|
+
try { if (fs.existsSync(paths.activeFile)) fs.unlinkSync(paths.activeFile); } catch (_) { /* best-effort */ }
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
atomicWriteFile(paths.activeFile, String(envelopeId) + '\n');
|
|
792
|
+
return String(envelopeId);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function verificationEnvelopeIdForCommand(cwd, flags = {}) {
|
|
796
|
+
if (flagEnabled(flags['no-verify-envelope']) || flagEnabled(flags.noVerifyEnvelope)) return null;
|
|
797
|
+
const explicit = flags['verify-envelope'] || flags.verifyEnvelope || flags.envelope;
|
|
798
|
+
if (explicit !== undefined && explicit !== null && explicit !== true) return String(explicit).trim() || null;
|
|
799
|
+
return readActiveVerificationEnvelope(cwd);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function writeLocalVerificationEnvelope(cwd, envelope) {
|
|
803
|
+
if (!envelope || !envelope.envelopeId) return null;
|
|
804
|
+
const paths = verificationPaths(cwd, envelope.envelopeId);
|
|
805
|
+
ensureDir(paths.base);
|
|
806
|
+
ensureDir(paths.packetsDir);
|
|
807
|
+
atomicWriteFile(paths.envelopeFile, JSON.stringify(envelope, null, 2));
|
|
808
|
+
if (envelope.acceptance) {
|
|
809
|
+
atomicWriteFile(paths.manifestFile, JSON.stringify(envelope.acceptance, null, 2));
|
|
810
|
+
}
|
|
811
|
+
(envelope.packets || []).forEach((packet) => writeLocalVerificationPacket(cwd, envelope.envelopeId, packet));
|
|
812
|
+
return paths.envelopeFile;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function readLocalVerificationEnvelope(cwd, envelopeId) {
|
|
816
|
+
const paths = verificationPaths(cwd, envelopeId);
|
|
817
|
+
if (!fs.existsSync(paths.envelopeFile)) return null;
|
|
818
|
+
try {
|
|
819
|
+
return JSON.parse(fs.readFileSync(paths.envelopeFile, 'utf8'));
|
|
820
|
+
} catch (_) {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function writeLocalVerificationPacket(cwd, envelopeId, packet) {
|
|
826
|
+
if (!envelopeId || !packet) return null;
|
|
827
|
+
const paths = verificationPaths(cwd, envelopeId);
|
|
828
|
+
ensureDir(paths.packetsDir);
|
|
829
|
+
const packetId = packet.packetId || ('local-' + crypto.randomUUID());
|
|
830
|
+
const file = path.join(paths.packetsDir, packetId + '.json');
|
|
831
|
+
atomicWriteFile(file, JSON.stringify(Object.assign({}, packet, { packetId }), null, 2));
|
|
832
|
+
return file;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function parseManifestFile(file) {
|
|
836
|
+
const resolved = path.resolve(process.cwd(), String(file));
|
|
837
|
+
const text = fs.readFileSync(resolved, 'utf8');
|
|
838
|
+
if (resolved.endsWith('.yml') || resolved.endsWith('.yaml')) {
|
|
839
|
+
return YAML.parse(text);
|
|
840
|
+
}
|
|
841
|
+
return JSON.parse(text);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function sha256File(file) {
|
|
845
|
+
const resolved = path.resolve(process.cwd(), String(file));
|
|
846
|
+
const hash = crypto.createHash('sha256');
|
|
847
|
+
hash.update(fs.readFileSync(resolved));
|
|
848
|
+
const stat = fs.statSync(resolved);
|
|
849
|
+
return { path: resolved, sha256: hash.digest('hex'), size: stat.size };
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function safeGitValue(cwd, command) {
|
|
853
|
+
try {
|
|
854
|
+
return runGit(cwd, command);
|
|
855
|
+
} catch (_) {
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function collectVerificationSource(cwd, flags = {}) {
|
|
861
|
+
const branchName = flags.branch || currentBranch(cwd);
|
|
862
|
+
return {
|
|
863
|
+
workingDirectory: cwd,
|
|
864
|
+
gitBranch: branchName,
|
|
865
|
+
gitHead: safeGitValue(cwd, 'git rev-parse HEAD'),
|
|
866
|
+
originHead: safeGitValue(cwd, 'git rev-parse ' + shellQuote('origin/' + branchName)),
|
|
867
|
+
mergeBase: safeGitValue(cwd, 'git merge-base HEAD ' + shellQuote('origin/' + branchName)),
|
|
868
|
+
workspace: resolveWorkspace(cwd, flags),
|
|
869
|
+
workspaceSource: workspaceSource(cwd, flags),
|
|
870
|
+
stagingLane: branchName + ':' + (resolveWorkspace(cwd, flags) || 'shared'),
|
|
871
|
+
sourceLayer: 'unknown'
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function collectVerificationAccount(cwd, accountId, baseUrl, dataMode) {
|
|
876
|
+
const info = loadAccountInfo(cwd) || {};
|
|
877
|
+
const resolution = info.resolution || info.repoContext || {};
|
|
878
|
+
return {
|
|
879
|
+
repoAccountId: Number(accountId || accountIdFromAccountInfo(info)) || null,
|
|
880
|
+
executionAccountId: resolution.executionAccountId || resolution.accountId || accountId || null,
|
|
881
|
+
addressedAccountId: resolution.addressedAccountId || resolution.accountId || accountId || null,
|
|
882
|
+
componentOwnerAccountId: resolution.componentOwnerAccountId || resolution.ownerAccountId || null,
|
|
883
|
+
resolvedDatabaseName: resolution.resolvedDatabaseName || resolution.databaseName || null,
|
|
884
|
+
host: resolution.resolvedDomainName || resolution.host || normalizeBaseUrl(baseUrl),
|
|
885
|
+
dataMode,
|
|
886
|
+
dataModeSource: 'cli',
|
|
887
|
+
accountRole: resolution.role || null,
|
|
888
|
+
accountType: resolution.type || null,
|
|
889
|
+
testAccount: resolution.testAccount === true || info.testAccount === true
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async function postVerificationCommand(api, cwd, session, accountId, command, payload) {
|
|
894
|
+
const response = await loggedPost(api, cwd, '/cli/verification', Object.assign({
|
|
895
|
+
token: session.token,
|
|
896
|
+
accountId,
|
|
897
|
+
command
|
|
898
|
+
}, payload || {})).then((r) => r.data);
|
|
899
|
+
if (!response.success) {
|
|
900
|
+
throw new Error(response.message || 'Verification command failed');
|
|
901
|
+
}
|
|
902
|
+
return response;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
async function appendVerificationPacket(api, cwd, session, accountId, flags, packet, options = {}) {
|
|
906
|
+
const envelopeId = verificationEnvelopeIdForCommand(cwd, flags);
|
|
907
|
+
if (!envelopeId) return null;
|
|
908
|
+
const finalPacket = Object.assign({
|
|
909
|
+
packetId: crypto.randomUUID(),
|
|
910
|
+
createdAt: new Date().toISOString(),
|
|
911
|
+
success: true
|
|
912
|
+
}, packet || {});
|
|
913
|
+
finalPacket.envelopeId = envelopeId;
|
|
914
|
+
writeLocalVerificationPacket(cwd, envelopeId, finalPacket);
|
|
915
|
+
try {
|
|
916
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, 'packet', {
|
|
917
|
+
envelopeId,
|
|
918
|
+
packet: finalPacket
|
|
919
|
+
});
|
|
920
|
+
if (response.packet) writeLocalVerificationPacket(cwd, envelopeId, response.packet);
|
|
921
|
+
if (response.envelope) writeLocalVerificationEnvelope(cwd, response.envelope);
|
|
922
|
+
if (!options.quiet) console.log('Verification envelope:', envelopeId, '(packet ' + finalPacket.type + ' attached)');
|
|
923
|
+
return response;
|
|
924
|
+
} catch (err) {
|
|
925
|
+
if (!options.quiet) {
|
|
926
|
+
console.error('Verification evidence captured locally but not attached to the platform envelope:', describeError(err));
|
|
927
|
+
}
|
|
928
|
+
return { localOnly: true, envelopeId, packet: finalPacket };
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function buildCommandWorld(response, fallback = {}) {
|
|
933
|
+
const source = (response && response.staging) || {};
|
|
934
|
+
const resolution = (response && response.resolution) || {};
|
|
935
|
+
return {
|
|
936
|
+
accountId: response && (response.accountId || response.ownerAccountId) || fallback.accountId,
|
|
937
|
+
executionAccountId: response && (response.executionAccountId || response.accountId) || resolution.executionAccountId || fallback.executionAccountId,
|
|
938
|
+
addressedAccountId: resolution.addressedAccountId || fallback.addressedAccountId,
|
|
939
|
+
host: fallback.host,
|
|
940
|
+
dataMode: response && response.dataMode || fallback.dataMode,
|
|
941
|
+
dataModeSource: response && response.dataModeSource || fallback.dataModeSource,
|
|
942
|
+
branchName: response && response.branchName || fallback.branchName,
|
|
943
|
+
workspace: response && response.workspace !== undefined ? response.workspace : fallback.workspace,
|
|
944
|
+
stagingLane: response && response.stagingLane || source.stagingLane || fallback.stagingLane,
|
|
945
|
+
componentBranch: resolution.componentBranch || response && response.variantBranch || fallback.componentBranch,
|
|
946
|
+
variantId: resolution.variantId || fallback.variantId,
|
|
947
|
+
sourceLayer: fallback.sourceLayer || (source.testComponentSource === 'staged' ? 'staged' : undefined)
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
|
|
758
951
|
function normalizeName(name) {
|
|
759
952
|
return name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/\s+/g, ' ').trim();
|
|
760
953
|
}
|
|
@@ -2552,6 +2745,24 @@ async function pushComponentsCommand(flags) {
|
|
|
2552
2745
|
response.requestedStageMode = stageMode;
|
|
2553
2746
|
response.unrepresentableChanges = unstageable;
|
|
2554
2747
|
|
|
2748
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
2749
|
+
type: 'stage',
|
|
2750
|
+
success: response.success !== false,
|
|
2751
|
+
claim: 'Components staged for verification',
|
|
2752
|
+
world: buildCommandWorld(response, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl), sourceLayer: 'staged' }),
|
|
2753
|
+
revision: collectVerificationSource(cwd, flags),
|
|
2754
|
+
assertions: [
|
|
2755
|
+
{ category: 'stage', stageMode },
|
|
2756
|
+
{ category: 'staging_lane', value: response.stagingLane }
|
|
2757
|
+
],
|
|
2758
|
+
evidenceCategories: ['stage', 'staging_lane'],
|
|
2759
|
+
rawRefs: { command: 'components stage' },
|
|
2760
|
+
stage: response.stage,
|
|
2761
|
+
laneSummary: response.laneSummary,
|
|
2762
|
+
changedFromWorkingTree: response.changedFromWorkingTree,
|
|
2763
|
+
unrepresentableChanges: response.unrepresentableChanges
|
|
2764
|
+
}, { quiet: flagEnabled(flags.json) });
|
|
2765
|
+
|
|
2555
2766
|
if (flagEnabled(flags.json)) {
|
|
2556
2767
|
console.log(JSON.stringify(response, null, 2));
|
|
2557
2768
|
return response;
|
|
@@ -3034,6 +3245,17 @@ async function statusComponentsCommand(flags) {
|
|
|
3034
3245
|
throw new Error(response.message || 'Staging status failed');
|
|
3035
3246
|
}
|
|
3036
3247
|
|
|
3248
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
3249
|
+
type: 'component_status',
|
|
3250
|
+
success: true,
|
|
3251
|
+
claim: 'Component status observed',
|
|
3252
|
+
world: buildCommandWorld(response, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl) }),
|
|
3253
|
+
revision: collectVerificationSource(cwd, flags),
|
|
3254
|
+
evidenceCategories: ['component_status'],
|
|
3255
|
+
rawRefs: { command: 'components status' },
|
|
3256
|
+
componentStatus: response
|
|
3257
|
+
}, { quiet: flagEnabled(flags.json) });
|
|
3258
|
+
|
|
3037
3259
|
if (flagEnabled(flags.json)) {
|
|
3038
3260
|
console.log(JSON.stringify(response, null, 2));
|
|
3039
3261
|
return response;
|
|
@@ -3241,6 +3463,23 @@ async function syncComponentsCommand(rawFlags) {
|
|
|
3241
3463
|
const gate = preflightGate || evaluateSyncGates(response, gateFlags, changedFromWorkingTree, cwd);
|
|
3242
3464
|
summary.gates = gate.checks;
|
|
3243
3465
|
|
|
3466
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
3467
|
+
type: response.sync && response.sync.dryRun ? 'sync_dry_run' : 'sync_mutation',
|
|
3468
|
+
success: response.success !== false,
|
|
3469
|
+
claim: response.sync && response.sync.dryRun ? 'Component sync dry-run observed' : 'Component sync completed',
|
|
3470
|
+
world: buildCommandWorld(response, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl), sourceLayer: response.sync && response.sync.mode === 'variant' ? 'variant' : 'trunk' }),
|
|
3471
|
+
revision: Object.assign(collectVerificationSource(cwd, flags), {
|
|
3472
|
+
platformSyncedSha: response.sync && (response.sync.postSyncSha || response.sync.branchHeadSha)
|
|
3473
|
+
}),
|
|
3474
|
+
evidenceCategories: [response.sync && response.sync.dryRun ? 'sync_dry_run' : 'sync_mutation'],
|
|
3475
|
+
assertions: [
|
|
3476
|
+
{ category: response.sync && response.sync.dryRun ? 'sync_dry_run' : 'sync_mutation', summary }
|
|
3477
|
+
],
|
|
3478
|
+
rawRefs: { command: 'components sync' },
|
|
3479
|
+
sync: response.sync,
|
|
3480
|
+
summary
|
|
3481
|
+
}, { quiet: flagEnabled(flags.json) });
|
|
3482
|
+
|
|
3244
3483
|
if (flagEnabled(flags.json)) {
|
|
3245
3484
|
console.log(JSON.stringify(flagEnabled(flags.summary) ? summary : response, null, 2));
|
|
3246
3485
|
failOnSyncGate(gate);
|
|
@@ -4363,6 +4602,30 @@ async function testCommand(flags) {
|
|
|
4363
4602
|
} else if (status.result && status.result.failed > 0) {
|
|
4364
4603
|
process.exitCode = 1;
|
|
4365
4604
|
}
|
|
4605
|
+
|
|
4606
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
4607
|
+
type: 'test_run',
|
|
4608
|
+
success: status.status === 'completed' && !(status.result && status.result.failed > 0) && !unmatched.length,
|
|
4609
|
+
claim: 'Test run ' + String(testRef),
|
|
4610
|
+
world: buildCommandWorld(status, { accountId, dataMode: status.dataMode || dataMode, dataModeSource, branchName, workspace, host: normalizeBaseUrl(baseUrl), sourceLayer: status.staging && status.staging.testComponentSource }),
|
|
4611
|
+
revision: Object.assign(collectVerificationSource(cwd, flags), {
|
|
4612
|
+
compileSignatures: status.staging && status.staging.testComponentSignature ? [status.staging.testComponentSignature] : []
|
|
4613
|
+
}),
|
|
4614
|
+
test: {
|
|
4615
|
+
taskId: start.taskId,
|
|
4616
|
+
testId: status.test && status.test.id,
|
|
4617
|
+
testName: status.test && status.test.name,
|
|
4618
|
+
selectedCases: names,
|
|
4619
|
+
passed: status.result && status.result.passed,
|
|
4620
|
+
failed: status.result && status.result.failed,
|
|
4621
|
+
dataModeSource: status.dataModeSource || dataModeSource,
|
|
4622
|
+
unmatchedTestNames: unmatched
|
|
4623
|
+
},
|
|
4624
|
+
evidenceCategories: ['test_run'].concat(names.map((name) => 'test_run.' + name)),
|
|
4625
|
+
limitations: unmatched.length ? ['One or more requested test case selectors matched no case.'] : [],
|
|
4626
|
+
rawRefs: { testStatusKey: start.taskId },
|
|
4627
|
+
status
|
|
4628
|
+
});
|
|
4366
4629
|
}
|
|
4367
4630
|
|
|
4368
4631
|
async function tokenCommand(flags) {
|
|
@@ -4451,6 +4714,21 @@ async function tokenCommand(flags) {
|
|
|
4451
4714
|
|
|
4452
4715
|
printSessionResolutionWarning(sessionContext);
|
|
4453
4716
|
console.log(JSON.stringify(output, null, 2));
|
|
4717
|
+
|
|
4718
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
4719
|
+
type: 'token_inspect',
|
|
4720
|
+
success: true,
|
|
4721
|
+
claim: 'Verification token minted',
|
|
4722
|
+
world: buildCommandWorld(output, { accountId: output.accountId || accountId, dataMode: output.dataMode || dataMode, branchName, workspace: output.workspace, host: normalizeBaseUrl(baseUrl) }),
|
|
4723
|
+
revision: collectVerificationSource(cwd, flags),
|
|
4724
|
+
browser: {
|
|
4725
|
+
tokenKeyHash: output.tokenKey ? sha256(String(output.tokenKey)) : null,
|
|
4726
|
+
urlHash: output.embeddableUrl ? sha256(String(output.embeddableUrl)) : null,
|
|
4727
|
+
liveUrl: flagEnabled(flags['store-url']) ? output.embeddableUrl : undefined
|
|
4728
|
+
},
|
|
4729
|
+
evidenceCategories: ['token_inspect'],
|
|
4730
|
+
token: Object.assign({}, output, { tokenKey: output.tokenKey ? '[redacted]' : undefined, embedTokenKey: output.embedTokenKey ? '[redacted]' : undefined })
|
|
4731
|
+
});
|
|
4454
4732
|
}
|
|
4455
4733
|
|
|
4456
4734
|
async function tokenInspectCommand(flags) {
|
|
@@ -4480,6 +4758,19 @@ async function tokenInspectCommand(flags) {
|
|
|
4480
4758
|
printSessionResolutionWarning(sessionContext);
|
|
4481
4759
|
printResolvedBaseUrl(baseUrl);
|
|
4482
4760
|
console.log(JSON.stringify(data, null, 2));
|
|
4761
|
+
|
|
4762
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
4763
|
+
type: 'token_inspect',
|
|
4764
|
+
success: true,
|
|
4765
|
+
claim: 'Token inspected',
|
|
4766
|
+
world: buildCommandWorld(data.safety || {}, { accountId: data.context && data.context.accountId || accountId, dataMode: data.safety && data.safety.dataMode, host: normalizeBaseUrl(baseUrl) }),
|
|
4767
|
+
revision: collectVerificationSource(cwd, flags),
|
|
4768
|
+
browser: {
|
|
4769
|
+
tokenKeyHash: data.token && data.token.tokenKey ? sha256(String(data.token.tokenKey)) : null
|
|
4770
|
+
},
|
|
4771
|
+
evidenceCategories: ['token_inspect'],
|
|
4772
|
+
tokenInspect: data
|
|
4773
|
+
});
|
|
4483
4774
|
}
|
|
4484
4775
|
|
|
4485
4776
|
async function toolsCommand(flags) {
|
|
@@ -4650,6 +4941,17 @@ async function toolCommand(flags) {
|
|
|
4650
4941
|
console.log('Tool error:', data.toolMessage || toolResultMessage(data.result));
|
|
4651
4942
|
}
|
|
4652
4943
|
if (data.status === 'failed' || polledFailed) process.exitCode = 1;
|
|
4944
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
4945
|
+
type: 'tool_call',
|
|
4946
|
+
success: data.status !== 'failed' && !polledFailed,
|
|
4947
|
+
claim: 'Tool status ' + requestedCallId,
|
|
4948
|
+
world: buildCommandWorld(data, { accountId: statusAccountId, dataMode: data.dataMode || dataMode, branchName: flags.branch || (stored && stored.branchName) || branchName, host: normalizeBaseUrl(baseUrl) }),
|
|
4949
|
+
revision: collectVerificationSource(cwd, flags),
|
|
4950
|
+
evidenceCategories: ['tool_call'],
|
|
4951
|
+
rawRefs: { toolResponsePath: statusResponse.responseFile, callId: requestedCallId },
|
|
4952
|
+
tool: { callId: requestedCallId, status: data.status, name: stored && stored.name },
|
|
4953
|
+
result: data
|
|
4954
|
+
});
|
|
4653
4955
|
return;
|
|
4654
4956
|
}
|
|
4655
4957
|
|
|
@@ -4732,6 +5034,287 @@ async function toolCommand(flags) {
|
|
|
4732
5034
|
process.exitCode = 1;
|
|
4733
5035
|
}
|
|
4734
5036
|
}
|
|
5037
|
+
|
|
5038
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
5039
|
+
type: 'tool_call',
|
|
5040
|
+
success: !toolFailed && (!asyncMode || !waitForAsync || process.exitCode !== 1),
|
|
5041
|
+
claim: 'Tool call ' + String(toolName),
|
|
5042
|
+
world: buildCommandWorld(data, { accountId: data.accountId || accountId, dataMode: data.dataMode || dataMode, branchName: data.branchName || branchName, workspace: resolveWorkspace(cwd, flags), host: normalizeBaseUrl(baseUrl), sourceLayer: data.componentSource }),
|
|
5043
|
+
revision: Object.assign(collectVerificationSource(cwd, flags), {
|
|
5044
|
+
compileSignatures: data.componentSignature ? [data.componentSignature] : []
|
|
5045
|
+
}),
|
|
5046
|
+
evidenceCategories: ['tool_call'],
|
|
5047
|
+
dependencies: {
|
|
5048
|
+
mocks: [],
|
|
5049
|
+
liveHttpCalls: data.liveHttpCalls || []
|
|
5050
|
+
},
|
|
5051
|
+
rawRefs: { toolResponsePath: response.responseFile, callId },
|
|
5052
|
+
tool: { callId, name: String(toolName), async: asyncMode, status: data.status, toolSuccess: data.toolSuccess },
|
|
5053
|
+
result: data
|
|
5054
|
+
});
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
async function verifyCommand(flags, subcommand) {
|
|
5058
|
+
const cwd = process.cwd();
|
|
5059
|
+
ensureLocalState(cwd);
|
|
5060
|
+
const sub = String(subcommand || (flags._ && flags._[1]) || 'status').toLowerCase();
|
|
5061
|
+
|
|
5062
|
+
if (sub === 'current') {
|
|
5063
|
+
const active = readActiveVerificationEnvelope(cwd);
|
|
5064
|
+
if (!active) {
|
|
5065
|
+
console.log('No active verification envelope in this checkout.');
|
|
5066
|
+
return;
|
|
5067
|
+
}
|
|
5068
|
+
console.log('Active verification envelope:', active);
|
|
5069
|
+
const local = readLocalVerificationEnvelope(cwd, active);
|
|
5070
|
+
if (local && local.summary) console.log('Summary:', local.summary);
|
|
5071
|
+
return;
|
|
5072
|
+
}
|
|
5073
|
+
|
|
5074
|
+
if (sub === 'use') {
|
|
5075
|
+
const envelopeId = flags._ && flags._[2] || flags.envelope || flags['verify-envelope'];
|
|
5076
|
+
if (!envelopeId) throw new Error('Usage: remits-cli verify use <envelopeId>');
|
|
5077
|
+
writeActiveVerificationEnvelope(cwd, envelopeId);
|
|
5078
|
+
console.log('Active verification envelope:', envelopeId);
|
|
5079
|
+
return;
|
|
5080
|
+
}
|
|
5081
|
+
|
|
5082
|
+
if (sub === 'clear') {
|
|
5083
|
+
writeActiveVerificationEnvelope(cwd, null);
|
|
5084
|
+
console.log('Verification envelope cleared for this checkout.');
|
|
5085
|
+
return;
|
|
5086
|
+
}
|
|
5087
|
+
|
|
5088
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
5089
|
+
const { session, accountId } = sessionContext;
|
|
5090
|
+
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
5091
|
+
const dataMode = resolveDataMode(flags, session);
|
|
5092
|
+
const branchName = flags.branch || currentBranch(cwd);
|
|
5093
|
+
const workspace = resolveWorkspace(cwd, flags);
|
|
5094
|
+
const api = buildAxios(baseUrl, session.token);
|
|
5095
|
+
|
|
5096
|
+
if (sub === 'start') {
|
|
5097
|
+
const summary = flags.summary || flags._ && flags._.slice(2).join(' ');
|
|
5098
|
+
if (!summary) throw new Error('verify start requires --summary "..."');
|
|
5099
|
+
let manifest = {};
|
|
5100
|
+
if (flags.manifest || flags.file) {
|
|
5101
|
+
manifest = parseManifestFile(flags.manifest || flags.file);
|
|
5102
|
+
}
|
|
5103
|
+
let statusResponse = null;
|
|
5104
|
+
try {
|
|
5105
|
+
statusResponse = await loggedPost(api, cwd, '/cli/components', {
|
|
5106
|
+
token: session.token,
|
|
5107
|
+
accountId,
|
|
5108
|
+
branchName,
|
|
5109
|
+
workspace,
|
|
5110
|
+
dataMode,
|
|
5111
|
+
mode: 'status'
|
|
5112
|
+
}).then((r) => r.data);
|
|
5113
|
+
} catch (_) { /* best-effort */ }
|
|
5114
|
+
|
|
5115
|
+
const source = collectVerificationSource(cwd, flags);
|
|
5116
|
+
if (statusResponse) {
|
|
5117
|
+
source.componentBranch = statusResponse.branchContext && statusResponse.branchContext.onTrunk ? 'trunk' : branchName;
|
|
5118
|
+
source.sourceLayer = statusResponse.laneSummary && statusResponse.laneSummary.stagedCount > 0
|
|
5119
|
+
? 'staged' : (statusResponse.branchContext && statusResponse.branchContext.onTrunk ? 'trunk' : 'variant');
|
|
5120
|
+
source.stagedOverlayHash = statusResponse.laneSummary ? sha256(stableStringify(statusResponse.laneSummary)) : null;
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5123
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, 'start', {
|
|
5124
|
+
summary,
|
|
5125
|
+
ticketId: flags.ticket,
|
|
5126
|
+
account: collectVerificationAccount(cwd, accountId, baseUrl, dataMode),
|
|
5127
|
+
source,
|
|
5128
|
+
manifest
|
|
5129
|
+
});
|
|
5130
|
+
const envelope = response.envelope;
|
|
5131
|
+
writeLocalVerificationEnvelope(cwd, envelope);
|
|
5132
|
+
writeActiveVerificationEnvelope(cwd, envelope.envelopeId);
|
|
5133
|
+
console.log('Verification envelope started:', envelope.envelopeId);
|
|
5134
|
+
console.log('Target:', 'account=' + accountId + ', branch=' + branchName + ', workspace=' + (workspace || 'shared') + ', dataMode=' + dataMode);
|
|
5135
|
+
console.log('Local mirror:', verificationPaths(cwd, envelope.envelopeId).base);
|
|
5136
|
+
return envelope;
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
const envelopeId = flags.envelope || flags['verify-envelope'] || flags.id || readActiveVerificationEnvelope(cwd);
|
|
5140
|
+
if (!envelopeId) {
|
|
5141
|
+
throw new Error('No verification envelope selected. Run `remits-cli verify start --summary "..."` or pass --envelope <id>.');
|
|
5142
|
+
}
|
|
5143
|
+
|
|
5144
|
+
if (sub === 'manifest') {
|
|
5145
|
+
if (flagEnabled(flags.print)) {
|
|
5146
|
+
const local = readLocalVerificationEnvelope(cwd, envelopeId);
|
|
5147
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, 'show', { envelopeId })
|
|
5148
|
+
.catch(() => ({ envelope: local }));
|
|
5149
|
+
const manifest = response.envelope && response.envelope.acceptance;
|
|
5150
|
+
console.log(JSON.stringify(manifest || {}, null, 2));
|
|
5151
|
+
return manifest;
|
|
5152
|
+
}
|
|
5153
|
+
const file = flags.file || flags.manifest;
|
|
5154
|
+
if (!file) throw new Error('verify manifest requires --file <manifest.json> or --print');
|
|
5155
|
+
const manifest = parseManifestFile(file);
|
|
5156
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, 'manifest', { envelopeId, manifest });
|
|
5157
|
+
writeLocalVerificationEnvelope(cwd, response.envelope);
|
|
5158
|
+
console.log('Manifest attached to envelope:', envelopeId);
|
|
5159
|
+
console.log('Required evidence:', (((response.envelope || {}).acceptance || {}).requiredEvidence || []).length);
|
|
5160
|
+
return response.envelope;
|
|
5161
|
+
}
|
|
5162
|
+
|
|
5163
|
+
if (sub === 'show' || sub === 'read' || sub === 'get' || sub === 'packets') {
|
|
5164
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, 'show', { envelopeId });
|
|
5165
|
+
if (response.envelope) writeLocalVerificationEnvelope(cwd, response.envelope);
|
|
5166
|
+
if (flagEnabled(flags.json)) {
|
|
5167
|
+
console.log(JSON.stringify(response.envelope || response, null, 2));
|
|
5168
|
+
return response.envelope || response;
|
|
5169
|
+
}
|
|
5170
|
+
const envelope = response.envelope || {};
|
|
5171
|
+
console.log('Verification envelope:', envelope.envelopeId || envelopeId);
|
|
5172
|
+
if (envelope.summary) console.log('Summary:', envelope.summary);
|
|
5173
|
+
console.log('Status:', envelope.status || 'unknown');
|
|
5174
|
+
const packets = Array.isArray(envelope.packets) ? envelope.packets : [];
|
|
5175
|
+
console.log('Packets:', packets.length);
|
|
5176
|
+
packets.forEach((packet) => {
|
|
5177
|
+
console.log('- ' + (packet.type || 'unknown') + ' ' + (packet.packetId || '') + (packet.success === false ? ' failed' : ''));
|
|
5178
|
+
});
|
|
5179
|
+
return envelope;
|
|
5180
|
+
}
|
|
5181
|
+
|
|
5182
|
+
if (sub === 'attach') {
|
|
5183
|
+
const artifacts = [];
|
|
5184
|
+
let type = 'manual_observation';
|
|
5185
|
+
const label = flags.label || flags.name || 'attached evidence';
|
|
5186
|
+
if (flags.artifact) {
|
|
5187
|
+
type = 'artifact';
|
|
5188
|
+
artifacts.push(Object.assign({ label, kind: 'artifact', source: 'local_path' }, sha256File(flags.artifact)));
|
|
5189
|
+
}
|
|
5190
|
+
if (flags.screenshot) {
|
|
5191
|
+
type = 'artifact';
|
|
5192
|
+
artifacts.push(Object.assign({ label, kind: 'screenshot', source: 'local_path' }, sha256File(flags.screenshot)));
|
|
5193
|
+
}
|
|
5194
|
+
const note = flags.note || flags.message;
|
|
5195
|
+
if (!artifacts.length && !note) {
|
|
5196
|
+
throw new Error('verify attach requires --artifact <path>, --screenshot <path>, or --note "..."');
|
|
5197
|
+
}
|
|
5198
|
+
const evidenceCategories = artifacts.map((a) => a.kind === 'screenshot' ? 'screenshot' : 'artifact.sha256');
|
|
5199
|
+
const categoryFlags = []
|
|
5200
|
+
.concat(flags.category || [])
|
|
5201
|
+
.concat(flags['evidence-category'] || [])
|
|
5202
|
+
.concat(flags.evidenceCategory || []);
|
|
5203
|
+
categoryFlags
|
|
5204
|
+
.filter(Boolean)
|
|
5205
|
+
.flatMap((value) => String(value).split(','))
|
|
5206
|
+
.map((value) => value.trim())
|
|
5207
|
+
.filter(Boolean)
|
|
5208
|
+
.forEach((value) => {
|
|
5209
|
+
if (!evidenceCategories.includes(value)) evidenceCategories.push(value);
|
|
5210
|
+
});
|
|
5211
|
+
const packet = {
|
|
5212
|
+
type,
|
|
5213
|
+
success: true,
|
|
5214
|
+
claim: note || label,
|
|
5215
|
+
artifacts,
|
|
5216
|
+
evidenceCategories
|
|
5217
|
+
};
|
|
5218
|
+
const response = await appendVerificationPacket(api, cwd, session, accountId, { 'verify-envelope': envelopeId }, packet);
|
|
5219
|
+
console.log('Evidence attached:', (response.packet || packet).packetId);
|
|
5220
|
+
return response;
|
|
5221
|
+
}
|
|
5222
|
+
|
|
5223
|
+
if (sub === 'browser-start' || sub === 'browser-step' || sub === 'browser-snapshot') {
|
|
5224
|
+
const url = flags.url || flags.token || flags._ && flags._[2];
|
|
5225
|
+
const action = flags.action || (sub === 'browser-start' ? 'open' : (sub === 'browser-snapshot' ? 'snapshot' : 'observe'));
|
|
5226
|
+
const packet = {
|
|
5227
|
+
type: sub === 'browser-start' ? 'browser_session' : (sub === 'browser-snapshot' ? 'browser_snapshot' : 'browser_step'),
|
|
5228
|
+
success: !flagEnabled(flags.failed),
|
|
5229
|
+
claim: flags.claim || flags.label || action,
|
|
5230
|
+
world: buildCommandWorld(null, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl) }),
|
|
5231
|
+
browser: {
|
|
5232
|
+
action,
|
|
5233
|
+
target: flags.target,
|
|
5234
|
+
value: flags.value,
|
|
5235
|
+
urlHash: url ? sha256(String(url)) : null,
|
|
5236
|
+
liveUrl: flagEnabled(flags['store-url']) ? url : undefined,
|
|
5237
|
+
interactionKind: flags.kind || (['click', 'fill', 'select', 'upload', 'reload'].includes(String(action)) ? 'user_interaction' : 'observation')
|
|
5238
|
+
},
|
|
5239
|
+
evidenceCategories: [sub, action === 'upload' ? 'browser_session.actual_upload' : null, action === 'click' ? 'browser_session.actual_clicks' : null]
|
|
5240
|
+
.filter(Boolean)
|
|
5241
|
+
};
|
|
5242
|
+
const response = await appendVerificationPacket(api, cwd, session, accountId, { 'verify-envelope': envelopeId }, packet);
|
|
5243
|
+
console.log('Browser evidence attached:', (response.packet || packet).packetId);
|
|
5244
|
+
return response;
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
if (sub === 'test') {
|
|
5248
|
+
return await testCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
|
|
5249
|
+
}
|
|
5250
|
+
if (sub === 'token') {
|
|
5251
|
+
return await tokenCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
|
|
5252
|
+
}
|
|
5253
|
+
if (sub === 'stage') {
|
|
5254
|
+
return await pushComponentsCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
|
|
5255
|
+
}
|
|
5256
|
+
if (sub === 'sync') {
|
|
5257
|
+
return await syncComponentsCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
|
|
5258
|
+
}
|
|
5259
|
+
if (sub === 'tool') {
|
|
5260
|
+
return await toolCommand(Object.assign({}, flags, { 'verify-envelope': envelopeId }));
|
|
5261
|
+
}
|
|
5262
|
+
|
|
5263
|
+
if (sub === 'invalidate') {
|
|
5264
|
+
const reason = flags.reason || flags.message || flags._ && flags._.slice(2).join(' ') || 'manually invalidated';
|
|
5265
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, 'invalidate', { envelopeId, reason });
|
|
5266
|
+
writeLocalVerificationEnvelope(cwd, response.envelope);
|
|
5267
|
+
console.log('Envelope marked stale:', envelopeId);
|
|
5268
|
+
return response.envelope;
|
|
5269
|
+
}
|
|
5270
|
+
|
|
5271
|
+
if (sub === 'status' || sub === 'report') {
|
|
5272
|
+
let statusPacket = null;
|
|
5273
|
+
try {
|
|
5274
|
+
const componentStatus = await loggedPost(api, cwd, '/cli/components', {
|
|
5275
|
+
token: session.token,
|
|
5276
|
+
accountId,
|
|
5277
|
+
branchName,
|
|
5278
|
+
workspace,
|
|
5279
|
+
dataMode,
|
|
5280
|
+
mode: 'status'
|
|
5281
|
+
}).then((r) => r.data);
|
|
5282
|
+
statusPacket = {
|
|
5283
|
+
type: 'component_status',
|
|
5284
|
+
success: componentStatus.success !== false,
|
|
5285
|
+
claim: 'Current component status for verification status check',
|
|
5286
|
+
world: buildCommandWorld(componentStatus, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl) }),
|
|
5287
|
+
revision: collectVerificationSource(cwd, flags),
|
|
5288
|
+
assertions: [],
|
|
5289
|
+
rawRefs: { command: 'components status' },
|
|
5290
|
+
componentStatus
|
|
5291
|
+
};
|
|
5292
|
+
await appendVerificationPacket(api, cwd, session, accountId, { 'verify-envelope': envelopeId }, statusPacket, { quiet: true });
|
|
5293
|
+
} catch (err) {
|
|
5294
|
+
if (!flagEnabled(flags.json)) {
|
|
5295
|
+
console.error('Warning: could not attach component_status packet before rendering report:', describeError(err));
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
const response = await postVerificationCommand(api, cwd, session, accountId, sub, {
|
|
5299
|
+
envelopeId,
|
|
5300
|
+
currentWorld: buildCommandWorld(statusPacket && statusPacket.componentStatus, {
|
|
5301
|
+
accountId,
|
|
5302
|
+
dataMode,
|
|
5303
|
+
branchName,
|
|
5304
|
+
workspace,
|
|
5305
|
+
host: normalizeBaseUrl(baseUrl)
|
|
5306
|
+
})
|
|
5307
|
+
});
|
|
5308
|
+
if (response.envelope) writeLocalVerificationEnvelope(cwd, response.envelope);
|
|
5309
|
+
if (response.report) {
|
|
5310
|
+
console.log(response.report);
|
|
5311
|
+
} else if (response.evaluation || response.status) {
|
|
5312
|
+
console.log(JSON.stringify(response, null, 2));
|
|
5313
|
+
}
|
|
5314
|
+
return response;
|
|
5315
|
+
}
|
|
5316
|
+
|
|
5317
|
+
throw new Error('Unknown verify subcommand: ' + sub);
|
|
4735
5318
|
}
|
|
4736
5319
|
|
|
4737
5320
|
async function dataModeCommand(flags, subcommand) {
|
|
@@ -10343,6 +10926,35 @@ function printTokenHelp() {
|
|
|
10343
10926
|
console.log('Inspect decodes a persisted Remits token and prints token metadata, recognized routing fields, safety/dataMode evidence, and full context.');
|
|
10344
10927
|
}
|
|
10345
10928
|
|
|
10929
|
+
function printVerifyHelp() {
|
|
10930
|
+
console.log('Usage: remits-cli verify <subcommand>');
|
|
10931
|
+
console.log('');
|
|
10932
|
+
console.log('Envelope lifecycle:');
|
|
10933
|
+
console.log(' remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID]');
|
|
10934
|
+
console.log(' remits-cli verify use <envelopeId>');
|
|
10935
|
+
console.log(' remits-cli verify current');
|
|
10936
|
+
console.log(' remits-cli verify clear');
|
|
10937
|
+
console.log(' remits-cli verify show [--envelope ID] [--json]');
|
|
10938
|
+
console.log(' remits-cli verify status [--envelope ID]');
|
|
10939
|
+
console.log(' remits-cli verify report [--envelope ID]');
|
|
10940
|
+
console.log('');
|
|
10941
|
+
console.log('Manifest and manual evidence:');
|
|
10942
|
+
console.log(' remits-cli verify manifest --file file.json [--envelope ID]');
|
|
10943
|
+
console.log(' remits-cli verify manifest --print [--envelope ID]');
|
|
10944
|
+
console.log(' remits-cli verify attach --artifact path --label "..." [--category name] [--envelope ID]');
|
|
10945
|
+
console.log(' remits-cli verify attach --screenshot path --label "..." [--category name] [--envelope ID]');
|
|
10946
|
+
console.log(' remits-cli verify attach --note "..." [--category name] [--envelope ID]');
|
|
10947
|
+
console.log('');
|
|
10948
|
+
console.log('Wrappers around existing commands:');
|
|
10949
|
+
console.log(' remits-cli verify stage --workset');
|
|
10950
|
+
console.log(' remits-cli verify test --test "..." [--names "case"]');
|
|
10951
|
+
console.log(' remits-cli verify token --path /page');
|
|
10952
|
+
console.log(' remits-cli verify sync --safe');
|
|
10953
|
+
console.log(' remits-cli verify tool --name mcp_tool --input \'{...}\'');
|
|
10954
|
+
console.log('');
|
|
10955
|
+
console.log('Existing commands also accept --verify-envelope <id> and --no-verify-envelope.');
|
|
10956
|
+
}
|
|
10957
|
+
|
|
10346
10958
|
async function main() {
|
|
10347
10959
|
migrateSessionIfNeeded();
|
|
10348
10960
|
const originalArgv = process.argv.slice(2);
|
|
@@ -10393,7 +11005,7 @@ async function main() {
|
|
|
10393
11005
|
// sure the repo has the latest guides — authenticating first if needed — so
|
|
10394
11006
|
// only platform-authenticated users ever receive them. `auth` syncs its own
|
|
10395
11007
|
// guides, so it is excluded here.
|
|
10396
|
-
const GUIDE_SYNC_COMMANDS = new Set(['components', 'test', 'token', 'tools', 'tool']);
|
|
11008
|
+
const GUIDE_SYNC_COMMANDS = new Set(['components', 'test', 'token', 'tools', 'tool', 'verify']);
|
|
10397
11009
|
if (!wantsJson && !isHelpCmd && GUIDE_SYNC_COMMANDS.has(command)) {
|
|
10398
11010
|
try { await ensureGuidesSynced(process.cwd(), args); } catch (_) { /* best-effort */ }
|
|
10399
11011
|
}
|
|
@@ -10421,6 +11033,8 @@ async function main() {
|
|
|
10421
11033
|
console.log(' remits-cli tool --name <toolName> [--base-url URL] [--branch BRANCH] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod] [--scope self|children|hierarchy] [--target-account-id ID] [--account-ids 1,2,3] [--variant-branch NAME|none] [--timeout-ms 60000] [--async true --wait true]');
|
|
10422
11034
|
console.log(' remits-cli tool status --call-id <callId> [--base-url URL] [--account-id ID] [--data-mode test|prod]');
|
|
10423
11035
|
console.log(' remits-cli workspace [show|use <name>|use --auto|clear] # isolate staging when several agents share a repo');
|
|
11036
|
+
console.log(' remits-cli verify start --summary "..." [--manifest file.json] # start a verification envelope');
|
|
11037
|
+
console.log(' remits-cli verify [current|use|clear|status|report|attach|test|token|stage|sync|tool]');
|
|
10424
11038
|
console.log(' remits-cli components stage [--workset|--changed-only] [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--data-mode test|prod] [--json|--verbose]');
|
|
10425
11039
|
console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--component-type TYPE --component-id ID] [--json|--verbose]');
|
|
10426
11040
|
console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--workspace NAME] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
|
|
@@ -10502,6 +11116,11 @@ async function main() {
|
|
|
10502
11116
|
process.exit(0);
|
|
10503
11117
|
}
|
|
10504
11118
|
|
|
11119
|
+
if (wantsHelp && command === 'verify') {
|
|
11120
|
+
printVerifyHelp();
|
|
11121
|
+
process.exit(0);
|
|
11122
|
+
}
|
|
11123
|
+
|
|
10505
11124
|
if (command === 'auth') {
|
|
10506
11125
|
await authCommand(args);
|
|
10507
11126
|
return;
|
|
@@ -10621,6 +11240,11 @@ async function main() {
|
|
|
10621
11240
|
return;
|
|
10622
11241
|
}
|
|
10623
11242
|
|
|
11243
|
+
if (command === 'verify') {
|
|
11244
|
+
await verifyCommand(args, subcommand);
|
|
11245
|
+
return;
|
|
11246
|
+
}
|
|
11247
|
+
|
|
10624
11248
|
if (command === 'test' && subcommand === 'run') {
|
|
10625
11249
|
await testCommand(args);
|
|
10626
11250
|
return;
|
package/package.json
CHANGED
|
@@ -121,6 +121,11 @@ reference named after it.
|
|
|
121
121
|
component (preferred, because it becomes regression protection) or a browser flow through
|
|
122
122
|
`remits-cli token`. "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
|
|
123
123
|
cannot verify, say what you would need and ask. (`development-loop.md`)
|
|
124
|
+
- **For concrete user workflows, start a verification envelope before you edit.** `remits-cli verify
|
|
125
|
+
start --summary "..."` records the account/source/lane tuple and a manifest when you have one.
|
|
126
|
+
Stage, test, token, tool, and sync commands attach evidence automatically while the envelope is
|
|
127
|
+
active; finish from `remits-cli verify report`, which separates verified claims from missing or stale
|
|
128
|
+
evidence. (`development-loop.md`, `component-resolution.md`)
|
|
124
129
|
- **Read the component's `.meta.yml` before changing behavior.** Sidecar descriptions can be dated
|
|
125
130
|
decision records. Before changing a displayed value, helper, calculation, schema field, or prompt
|
|
126
131
|
contract, check the sidecar and either preserve its decision or explicitly supersede it.
|
|
@@ -162,6 +167,8 @@ remits-cli tools # which tools this account actually has (tools
|
|
|
162
167
|
```
|
|
163
168
|
|
|
164
169
|
plus the repo's `account-info.json` → `resolution` block for the account's shape.
|
|
170
|
+
For a workflow-shaped request, also run `remits-cli verify start --summary "..."` once the target tuple
|
|
171
|
+
is understood, then keep that envelope active through stage/test/token/sync.
|
|
165
172
|
|
|
166
173
|
If any command returns 401, run `remits-cli auth` (with the same `--base-url` if you were targeting a
|
|
167
174
|
non-default host).
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- [Hierarchy-scoped tool reads](#hierarchy-scoped-tool-reads)
|
|
16
16
|
- [Data Mode](#data-mode)
|
|
17
17
|
- [Command Reference](#command-reference)
|
|
18
|
+
- [Verification envelopes](#verification-envelopes)
|
|
18
19
|
- [Staging modes: workset vs full snapshot](#staging-modes-workset-vs-full-snapshot)
|
|
19
20
|
- [Prod banners and retryable failures](#prod-banners-and-retryable-failures)
|
|
20
21
|
|
|
@@ -209,8 +210,86 @@ remits-cli token inspect --token <token|tokenKey|URL> # inspect
|
|
|
209
210
|
remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
|
|
210
211
|
remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
|
|
211
212
|
remits-cli tool status --call-id <callId> [--data-mode test|prod]
|
|
213
|
+
remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID]
|
|
214
|
+
remits-cli verify use <envelopeId>
|
|
215
|
+
remits-cli verify current
|
|
216
|
+
remits-cli verify clear
|
|
217
|
+
remits-cli verify show [--envelope ID] [--json]
|
|
218
|
+
remits-cli verify manifest --file file.json [--envelope ID]
|
|
219
|
+
remits-cli verify manifest --print [--envelope ID]
|
|
220
|
+
remits-cli verify attach --artifact path --label "..." [--envelope ID]
|
|
221
|
+
remits-cli verify attach --screenshot path --label "..." [--envelope ID]
|
|
222
|
+
remits-cli verify attach --note "..." [--envelope ID]
|
|
223
|
+
remits-cli verify browser-start --url URL [--label "..."]
|
|
224
|
+
remits-cli verify browser-step [--action click|fill|upload|reload|observe|...] [--target "..."] [--value "..."] [--url URL] [--label "..."]
|
|
225
|
+
remits-cli verify browser-snapshot [--url URL] [--label "..."]
|
|
226
|
+
remits-cli verify stage --workset
|
|
227
|
+
remits-cli verify test --test <id|name> [--names "case"]
|
|
228
|
+
remits-cli verify token --path <embeddablePathOrId>
|
|
229
|
+
remits-cli verify sync --safe
|
|
230
|
+
remits-cli verify tool --name <toolName> [--input "{...}"]
|
|
231
|
+
remits-cli verify status [--envelope ID]
|
|
232
|
+
remits-cli verify report [--envelope ID]
|
|
212
233
|
```
|
|
213
234
|
|
|
235
|
+
### Verification envelopes
|
|
236
|
+
|
|
237
|
+
Use a verification envelope for workflow-shaped work: concrete user journeys, browser-facing changes,
|
|
238
|
+
branch variants, subscriber/forked accounts, production-vs-test lane questions, support tickets, and
|
|
239
|
+
multi-agent work. Start it after you know the target account/branch/workspace/data-mode tuple and before
|
|
240
|
+
the first edit:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
remits-cli workspace use --auto
|
|
244
|
+
remits-cli components status
|
|
245
|
+
remits-cli verify start --summary "Hosted upload updates an existing profile" --manifest acceptance.json
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
An active envelope is stored in `./.remits-cli/verification/active`. Stage/test/token/tool/sync commands
|
|
249
|
+
attach evidence automatically while an envelope is active. The explicit wrappers do the same thing and
|
|
250
|
+
make the intent visible in terminal history:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
remits-cli verify stage --workset
|
|
254
|
+
remits-cli verify test --test "Adyen Import Recovery" --names "browser upload recovery"
|
|
255
|
+
remits-cli verify token --path /page/pricing-config --as-account 21 --data-mode test
|
|
256
|
+
remits-cli verify sync --safe
|
|
257
|
+
remits-cli verify report
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Existing commands also accept `--verify-envelope <id>` to attach to a specific envelope and
|
|
261
|
+
`--no-verify-envelope` to suppress automatic attachment for one command.
|
|
262
|
+
|
|
263
|
+
The manifest is the proof contract. It should name the world being exercised: repo account, host,
|
|
264
|
+
git/component branch, workspace, data mode, source layer, user journeys, artifacts and hashes, and the
|
|
265
|
+
evidence categories required before the final response may claim the work is done. If the manifest
|
|
266
|
+
cannot be written because the workflow is ambiguous, ask before implementation. For machine-gated report
|
|
267
|
+
requirements, prefer structured entries like `{"id":"actual_upload","packetType":"browser_step",
|
|
268
|
+
"category":"browser_session.actual_upload"}`. Plain strings work when they match a packet type or an
|
|
269
|
+
evidence category, but structured entries are clearer when several agents collect packets for one
|
|
270
|
+
envelope.
|
|
271
|
+
|
|
272
|
+
Common packet meanings:
|
|
273
|
+
|
|
274
|
+
| Packet | It proves | It does not prove by itself |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| `stage` | Which components were uploaded to the staging lane, including workset vs overlay | The requested behavior works |
|
|
277
|
+
| `component_status` | Which source world and lane are currently staged or clean | Any behavior was exercised |
|
|
278
|
+
| `test_run` | A Test component passed in the recorded account/branch/workspace/data lane | A hosted browser journey unless that Test actually drove it |
|
|
279
|
+
| `token_inspect` | A token was minted/inspected for a specific account, source layer, and lane | A user clicked through the page |
|
|
280
|
+
| `browser_session` / `browser_step` / `browser_snapshot` | The browser journey was opened or observed with named actions | Server-side persistence unless paired with assertions or follow-up state checks |
|
|
281
|
+
| `tool_call` | A platform tool ran in the recorded world and returned the recorded result | User-visible behavior unless the tool is the requested entry point |
|
|
282
|
+
| `sync_dry_run` | The planned durable writes and safety gates | Anything was written |
|
|
283
|
+
| `sync_mutation` | Durable source moved to trunk or variant and returned a sync SHA | The committed source behaves after staging is cleared |
|
|
284
|
+
| `artifact` | A file/screenshot/note exists with path, size, and sha256 | The workflow consumed it |
|
|
285
|
+
|
|
286
|
+
Final claims should come from `remits-cli verify report`. Treat `Verified` as the acceptance boundary.
|
|
287
|
+
`Additional evidence` is useful handoff context, but it does not satisfy a missing required packet unless
|
|
288
|
+
the report lists it under `Verified`. If the report says `partially_verified`, stale, or missing evidence,
|
|
289
|
+
say that plainly instead of widening the claim. In particular, staged proof is not committed variant/trunk
|
|
290
|
+
proof, a token is not browser proof, and a direct DOM or Alpine state mutation is not the same as a user
|
|
291
|
+
click/upload/reload flow.
|
|
292
|
+
|
|
214
293
|
For tests specifically:
|
|
215
294
|
- If `--data-mode` is omitted, `remits-cli test run` uses `test` and sends `dataModeSource:"cliDefault"`.
|
|
216
295
|
An explicit `--data-mode prod` sends `dataModeSource:"explicitFlag"` so production test runs are
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
- [Component Resolution: Staging Cache vs DB (which "version" actually runs)](#component-resolution-staging-cache-vs-db-which-version-actually-runs)
|
|
12
12
|
- [The three source layers + the compile cache](#the-three-source-layers--the-compile-cache)
|
|
13
|
+
- [Verification envelopes record the source layer](#verification-envelopes-record-the-source-layer)
|
|
13
14
|
- [Staging cache key format](#staging-cache-key-format)
|
|
14
15
|
- [How the platform picks staged vs DB (the compile signature)](#how-the-platform-picks-staged-vs-db-the-compile-signature)
|
|
15
16
|
- [When staged overrides apply](#when-staged-overrides-apply)
|
|
@@ -48,6 +49,18 @@ Plus the compile cache:
|
|
|
48
49
|
cache of the parsed closure, keyed by `(componentId, type, compileSignature)`. This is why a change that
|
|
49
50
|
is correctly in the DB can still execute stale on a running instance — see "Stale after sync" below.
|
|
50
51
|
|
|
52
|
+
### Verification envelopes record the source layer
|
|
53
|
+
|
|
54
|
+
When a verification envelope is active, `components stage`, `components status`, `test run`, `token`,
|
|
55
|
+
`tool`, and `components sync` attach evidence packets with the branch/workspace/staging lane and the best
|
|
56
|
+
source-layer facts the command observed. A staged test packet is proof of the staged layer; a sync packet
|
|
57
|
+
is proof of a source transition; a token packet is proof of the browser token's resolution tuple. Those
|
|
58
|
+
are not interchangeable.
|
|
59
|
+
|
|
60
|
+
Use `remits-cli verify report` before summarizing the work. It will naturally say when the evidence only
|
|
61
|
+
covered staged source, when committed variant/trunk proof is missing, or when evidence became stale after
|
|
62
|
+
the git head, overlay, or platform sync moved.
|
|
63
|
+
|
|
51
64
|
### Staging cache key format
|
|
52
65
|
|
|
53
66
|
```
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
- [Two Workflows](#two-workflows)
|
|
12
12
|
- [Test Mode vs Prod Mode](#test-mode-vs-prod-mode)
|
|
13
|
+
- [Verification Envelopes](#verification-envelopes)
|
|
13
14
|
- [Development Workflow](#development-workflow)
|
|
14
15
|
- [The Golden Rule: Writing Code Is Not Finishing the Job](#the-golden-rule-writing-code-is-not-finishing-the-job)
|
|
15
16
|
- [The Development Fast Loop](#the-development-fast-loop)
|
|
@@ -66,6 +67,36 @@ The right model is:
|
|
|
66
67
|
|
|
67
68
|
Never treat "it looks right in prod data inspection" as sufficient proof that a code change is verified.
|
|
68
69
|
|
|
70
|
+
## Verification Envelopes
|
|
71
|
+
|
|
72
|
+
For a concrete user workflow, browser-facing change, branch-variant release, support ticket, or any loop
|
|
73
|
+
where earlier manual testing found a gap, start a verification envelope before editing:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
remits-cli verify start --summary "Hosted upload updates an existing profile" --manifest acceptance.json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The envelope records the account, host, data lane, branch, workspace, git heads, staging lane, and the
|
|
80
|
+
acceptance manifest. It is mirrored locally under `.remits-cli/verification/<envelopeId>/` and becomes
|
|
81
|
+
active for this checkout. While active, `components stage`, `components status`, `test run`, `token`,
|
|
82
|
+
`token inspect`, `tool`, and `components sync` attach evidence packets automatically; pass
|
|
83
|
+
`--verify-envelope <id>` to name one explicitly or `--no-verify-envelope` when a command should not be
|
|
84
|
+
attached.
|
|
85
|
+
|
|
86
|
+
Use the wrappers when you want the intent to be unmistakable:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
remits-cli verify stage --workset
|
|
90
|
+
remits-cli verify test --test "Suite" --names "case name"
|
|
91
|
+
remits-cli verify token --path /page/example
|
|
92
|
+
remits-cli verify sync --safe
|
|
93
|
+
remits-cli verify attach --artifact sample.zip --label "user supplied ZIP"
|
|
94
|
+
remits-cli verify report
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The report is the final-response source. It separates verified claims, missing evidence, stale packets,
|
|
98
|
+
and the source/account/lane tuple, so do not replace it with a generic "verified" sentence.
|
|
99
|
+
|
|
69
100
|
## Development Workflow
|
|
70
101
|
|
|
71
102
|
### The Golden Rule: Writing Code Is Not Finishing the Job
|
|
@@ -124,6 +155,10 @@ runs resolve and what a sync writes. If `account-info.json` carries a `component
|
|
|
124
155
|
variants of these components exist: editing an origin component will drift them, so check
|
|
125
156
|
`remits-cli components branches` before changing shared code. See `branch-variants.md`.
|
|
126
157
|
|
|
158
|
+
If the request names a journey or acceptance behavior, start the envelope here, after the target tuple is
|
|
159
|
+
understood and before editing. A manifest can be lightweight JSON; the point is that the required
|
|
160
|
+
evidence is durable before the proof is collected.
|
|
161
|
+
|
|
127
162
|
#### Step 2: Make the Change
|
|
128
163
|
Edit component files under `components/`. This is local file editing — the platform doesn't know about your changes yet.
|
|
129
164
|
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
- [Before you edit anything: where you are, and whether you may](#before-you-edit-anything-where-you-are-and-whether-you-may)
|
|
18
18
|
- [Your account's process is binding, and it is already in your brief](#your-accounts-process-is-binding-and-it-is-already-in-your-brief)
|
|
19
19
|
- [Some of that process is enforced, not requested](#some-of-that-process-is-enforced-not-requested)
|
|
20
|
+
- [Verification envelopes on tickets](#verification-envelopes-on-tickets)
|
|
20
21
|
- [Seeing the queue as a human does](#seeing-the-queue-as-a-human-does)
|
|
21
22
|
- [Agent components are workers too](#agent-components-are-workers-too)
|
|
22
23
|
- [Moving a ticket through its lifecycle](#moving-a-ticket-through-its-lifecycle)
|
|
@@ -191,6 +192,26 @@ start.
|
|
|
191
192
|
`--data-mode test` registers a fixture agent instead, which will never be routed a production ticket —
|
|
192
193
|
use it only when you are deliberately testing the routing itself.
|
|
193
194
|
|
|
195
|
+
### Verification envelopes on tickets
|
|
196
|
+
|
|
197
|
+
For ticket work that changes component behavior, especially browser workflows, branch variants, release
|
|
198
|
+
gates, or a previously failed manual loop, start an envelope tied to the ticket:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
remits-cli verify start --ticket 22454 --summary "Fix hosted upload recovery"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Keep it active while you stage, run Tests, mint browser tokens, call tools, and sync. Those commands
|
|
205
|
+
attach evidence packets to the platform envelope and mirror them locally under `.remits-cli/verification/`.
|
|
206
|
+
Before completing or handing off the ticket, run:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
remits-cli verify report
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Use that output in the ticket update. It names what was verified, what is still missing, and whether the
|
|
213
|
+
proof covered staged source or committed source.
|
|
214
|
+
|
|
194
215
|
### If you are the worker
|
|
195
216
|
|
|
196
217
|
You know you are one when `REMITS_SUPPORT_TICKET_ID` is set in your environment. Your whole job is
|