@neurcode-ai/cli 0.9.34 → 0.9.35
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 -1
- package/dist/api-client.d.ts +14 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +4 -1
- package/dist/api-client.js.map +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +15 -2
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +81 -2
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +95 -0
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/verify.d.ts +6 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +365 -56
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/change-contract.d.ts +55 -0
- package/dist/utils/change-contract.d.ts.map +1 -0
- package/dist/utils/change-contract.js +158 -0
- package/dist/utils/change-contract.js.map +1 -0
- package/dist/utils/policy-audit.d.ts +2 -2
- package/dist/utils/policy-audit.d.ts.map +1 -1
- package/dist/utils/policy-audit.js.map +1 -1
- package/dist/utils/policy-compiler.d.ts +68 -0
- package/dist/utils/policy-compiler.d.ts.map +1 -0
- package/dist/utils/policy-compiler.js +170 -0
- package/dist/utils/policy-compiler.js.map +1 -0
- package/dist/utils/project-root.d.ts +16 -0
- package/dist/utils/project-root.d.ts.map +1 -1
- package/dist/utils/project-root.js +123 -9
- package/dist/utils/project-root.js.map +1 -1
- package/dist/utils/scope-telemetry.d.ts +21 -0
- package/dist/utils/scope-telemetry.d.ts.map +1 -0
- package/dist/utils/scope-telemetry.js +35 -0
- package/dist/utils/scope-telemetry.js.map +1 -0
- package/package.json +3 -2
package/dist/commands/verify.js
CHANGED
|
@@ -43,6 +43,7 @@ const child_process_1 = require("child_process");
|
|
|
43
43
|
const git_1 = require("../utils/git");
|
|
44
44
|
const diff_parser_1 = require("@neurcode-ai/diff-parser");
|
|
45
45
|
const policy_engine_1 = require("@neurcode-ai/policy-engine");
|
|
46
|
+
const governance_runtime_1 = require("@neurcode-ai/governance-runtime");
|
|
46
47
|
const config_1 = require("../config");
|
|
47
48
|
const api_client_1 = require("../api-client");
|
|
48
49
|
const path_1 = require("path");
|
|
@@ -53,12 +54,15 @@ const box_1 = require("../utils/box");
|
|
|
53
54
|
const ignore_1 = require("../utils/ignore");
|
|
54
55
|
const project_root_1 = require("../utils/project-root");
|
|
55
56
|
const brain_context_1 = require("../utils/brain-context");
|
|
57
|
+
const scope_telemetry_1 = require("../utils/scope-telemetry");
|
|
56
58
|
const policy_packs_1 = require("../utils/policy-packs");
|
|
57
59
|
const custom_policy_rules_1 = require("../utils/custom-policy-rules");
|
|
58
60
|
const policy_exceptions_1 = require("../utils/policy-exceptions");
|
|
59
61
|
const policy_governance_1 = require("../utils/policy-governance");
|
|
60
62
|
const policy_audit_1 = require("../utils/policy-audit");
|
|
61
63
|
const governance_1 = require("../utils/governance");
|
|
64
|
+
const policy_compiler_1 = require("../utils/policy-compiler");
|
|
65
|
+
const change_contract_1 = require("../utils/change-contract");
|
|
62
66
|
const policy_1 = require("@neurcode-ai/policy");
|
|
63
67
|
// Import chalk with fallback
|
|
64
68
|
let chalk;
|
|
@@ -80,6 +84,7 @@ catch {
|
|
|
80
84
|
white: (str) => str,
|
|
81
85
|
};
|
|
82
86
|
}
|
|
87
|
+
;
|
|
83
88
|
/**
|
|
84
89
|
* Check if a file path should be excluded from verification analysis
|
|
85
90
|
* Excludes internal/system files that should not count towards plan adherence
|
|
@@ -234,6 +239,43 @@ async function buildEffectivePolicyRules(client, projectRoot, useDashboardPolici
|
|
|
234
239
|
includeDashboardPolicies: useDashboardPolicies,
|
|
235
240
|
};
|
|
236
241
|
}
|
|
242
|
+
function resolveCompiledPolicyMetadata(artifact, path) {
|
|
243
|
+
if (!artifact || !path) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
fingerprint: artifact.fingerprint,
|
|
248
|
+
deterministicRuleCount: artifact.compilation.deterministicRuleCount,
|
|
249
|
+
unmatchedStatements: artifact.compilation.unmatchedStatements.length,
|
|
250
|
+
sourcePath: path,
|
|
251
|
+
policyLockFingerprint: artifact.source.policyLockFingerprint,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function buildCompiledPolicyFromEffectiveRules(input) {
|
|
255
|
+
const policyStatements = [
|
|
256
|
+
...input.effectiveRules.customPolicies.map((policy) => policy.rule_text),
|
|
257
|
+
];
|
|
258
|
+
return (0, policy_compiler_1.buildCompiledPolicyArtifact)({
|
|
259
|
+
includeDashboardPolicies: input.effectiveRules.includeDashboardPolicies,
|
|
260
|
+
policyLockPath: input.policyLockEvaluation.lockPath,
|
|
261
|
+
policyLockFingerprint: input.policyLockEvaluation.lockPresent
|
|
262
|
+
? (0, policy_packs_1.readPolicyLockFile)(input.projectRoot).lock?.effective.fingerprint || null
|
|
263
|
+
: null,
|
|
264
|
+
policyPack: input.effectiveRules.policyPack
|
|
265
|
+
? {
|
|
266
|
+
id: input.effectiveRules.policyPack.packId,
|
|
267
|
+
name: input.effectiveRules.policyPack.packName,
|
|
268
|
+
version: input.effectiveRules.policyPack.version,
|
|
269
|
+
}
|
|
270
|
+
: null,
|
|
271
|
+
defaultRuleCount: (0, policy_engine_1.createDefaultPolicy)().rules.length,
|
|
272
|
+
policyPackRuleCount: input.effectiveRules.policyPackRules.length,
|
|
273
|
+
customRuleCount: input.effectiveRules.customRules.length,
|
|
274
|
+
effectiveRuleCount: input.effectiveRules.allRules.length,
|
|
275
|
+
intentConstraints: input.intentConstraints,
|
|
276
|
+
policyRules: policyStatements,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
237
279
|
const POLICY_AUDIT_FILE = 'neurcode.policy.audit.log.jsonl';
|
|
238
280
|
function isEnabledFlag(value) {
|
|
239
281
|
if (!value)
|
|
@@ -354,13 +396,30 @@ async function recordVerificationIfRequested(options, config, payload) {
|
|
|
354
396
|
}
|
|
355
397
|
return;
|
|
356
398
|
}
|
|
357
|
-
await reportVerification(payload.grade, payload.violations, payload.verifyResult, config.apiKey, config.apiUrl || 'https://api.neurcode.com', payload.projectId, payload.jsonMode, payload.governance);
|
|
399
|
+
await reportVerification(payload.grade, payload.violations, payload.verifyResult, config.apiKey, config.apiUrl || 'https://api.neurcode.com', payload.projectId, payload.jsonMode, payload.governance, payload.verificationSource);
|
|
358
400
|
}
|
|
359
401
|
/**
|
|
360
402
|
* Execute policy-only verification (General Governance mode)
|
|
361
403
|
* Returns the exit code to use
|
|
362
404
|
*/
|
|
363
|
-
async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner) {
|
|
405
|
+
async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, compiledPolicyMetadata, changeContractSummary) {
|
|
406
|
+
const emitPolicyOnlyJson = (payload) => {
|
|
407
|
+
console.log(JSON.stringify({
|
|
408
|
+
...payload,
|
|
409
|
+
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
410
|
+
changeContract: changeContractSummary,
|
|
411
|
+
scope: scopeTelemetry,
|
|
412
|
+
}, null, 2));
|
|
413
|
+
};
|
|
414
|
+
const policyOnlyVerificationSource = 'policy_only';
|
|
415
|
+
const recordPolicyOnlyVerification = async (payload) => recordVerificationIfRequested(options, config, {
|
|
416
|
+
...payload,
|
|
417
|
+
verificationSource: policyOnlyVerificationSource,
|
|
418
|
+
verifyResult: {
|
|
419
|
+
...payload.verifyResult,
|
|
420
|
+
verificationSource: policyOnlyVerificationSource,
|
|
421
|
+
},
|
|
422
|
+
});
|
|
364
423
|
if (!options.json) {
|
|
365
424
|
console.log(chalk.cyan('🛡️ General Governance mode (policy only, no plan linked)\n'));
|
|
366
425
|
}
|
|
@@ -376,13 +435,16 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
376
435
|
signingKeys: aiLogSigningKeys,
|
|
377
436
|
signer: aiLogSigner,
|
|
378
437
|
});
|
|
379
|
-
const governancePayload = buildGovernancePayload(governanceAnalysis, orgGovernanceSettings
|
|
438
|
+
const governancePayload = buildGovernancePayload(governanceAnalysis, orgGovernanceSettings, {
|
|
439
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
440
|
+
changeContract: changeContractSummary,
|
|
441
|
+
});
|
|
380
442
|
const contextPolicyViolations = governanceAnalysis.contextPolicy.violations.filter((item) => !ignoreFilter(item.file));
|
|
381
443
|
const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
|
|
382
444
|
if (signedLogsRequired && !governanceAnalysis.aiChangeLogIntegrity.valid) {
|
|
383
445
|
const message = `AI change-log integrity check failed: ${governanceAnalysis.aiChangeLogIntegrity.issues.join('; ') || 'unknown issue'}`;
|
|
384
446
|
if (options.json) {
|
|
385
|
-
|
|
447
|
+
emitPolicyOnlyJson({
|
|
386
448
|
grade: 'F',
|
|
387
449
|
score: 0,
|
|
388
450
|
verdict: 'FAIL',
|
|
@@ -405,13 +467,13 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
405
467
|
policyOnly: true,
|
|
406
468
|
policyOnlySource: source,
|
|
407
469
|
...governancePayload,
|
|
408
|
-
}
|
|
470
|
+
});
|
|
409
471
|
}
|
|
410
472
|
else {
|
|
411
473
|
console.log(chalk.red('❌ AI change-log integrity validation failed (policy-only mode).'));
|
|
412
474
|
console.log(chalk.red(` ${message}`));
|
|
413
475
|
}
|
|
414
|
-
await
|
|
476
|
+
await recordPolicyOnlyVerification({
|
|
415
477
|
grade: 'F',
|
|
416
478
|
violations: [
|
|
417
479
|
{
|
|
@@ -439,7 +501,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
439
501
|
|| 'Governance decision matrix returned BLOCK.';
|
|
440
502
|
const reasonCodes = governanceAnalysis.governanceDecision.reasonCodes || [];
|
|
441
503
|
if (options.json) {
|
|
442
|
-
|
|
504
|
+
emitPolicyOnlyJson({
|
|
443
505
|
grade: 'F',
|
|
444
506
|
score: 0,
|
|
445
507
|
verdict: 'FAIL',
|
|
@@ -462,7 +524,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
462
524
|
policyOnly: true,
|
|
463
525
|
policyOnlySource: source,
|
|
464
526
|
...governancePayload,
|
|
465
|
-
}
|
|
527
|
+
});
|
|
466
528
|
}
|
|
467
529
|
else {
|
|
468
530
|
console.log(chalk.red('❌ Governance decision blocked this change set (policy-only mode).'));
|
|
@@ -471,7 +533,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
471
533
|
}
|
|
472
534
|
console.log(chalk.red(` ${message}`));
|
|
473
535
|
}
|
|
474
|
-
await
|
|
536
|
+
await recordPolicyOnlyVerification({
|
|
475
537
|
grade: 'F',
|
|
476
538
|
violations: [
|
|
477
539
|
{
|
|
@@ -503,7 +565,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
503
565
|
message: item.reason,
|
|
504
566
|
}));
|
|
505
567
|
if (options.json) {
|
|
506
|
-
|
|
568
|
+
emitPolicyOnlyJson({
|
|
507
569
|
grade: 'F',
|
|
508
570
|
score: 0,
|
|
509
571
|
verdict: 'FAIL',
|
|
@@ -519,7 +581,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
519
581
|
policyOnly: true,
|
|
520
582
|
policyOnlySource: source,
|
|
521
583
|
...governancePayload,
|
|
522
|
-
}
|
|
584
|
+
});
|
|
523
585
|
}
|
|
524
586
|
else {
|
|
525
587
|
console.log(chalk.red('❌ Context policy violation detected (policy-only mode).'));
|
|
@@ -528,7 +590,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
528
590
|
});
|
|
529
591
|
console.log(chalk.dim(`\n${message}`));
|
|
530
592
|
}
|
|
531
|
-
await
|
|
593
|
+
await recordPolicyOnlyVerification({
|
|
532
594
|
grade: 'F',
|
|
533
595
|
violations: contextPolicyViolationItems,
|
|
534
596
|
verifyResult: {
|
|
@@ -617,7 +679,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
617
679
|
const message = policyLockMismatchMessage(policyLockEvaluation.mismatches);
|
|
618
680
|
const lockViolationItems = toPolicyLockViolations(policyLockEvaluation.mismatches);
|
|
619
681
|
if (options.json) {
|
|
620
|
-
|
|
682
|
+
emitPolicyOnlyJson({
|
|
621
683
|
grade: 'F',
|
|
622
684
|
score: 0,
|
|
623
685
|
verdict: 'FAIL',
|
|
@@ -639,7 +701,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
639
701
|
path: policyLockEvaluation.lockPath,
|
|
640
702
|
mismatches: policyLockEvaluation.mismatches,
|
|
641
703
|
},
|
|
642
|
-
}
|
|
704
|
+
});
|
|
643
705
|
}
|
|
644
706
|
else {
|
|
645
707
|
console.log(chalk.red('❌ Policy lock baseline mismatch.'));
|
|
@@ -649,7 +711,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
649
711
|
});
|
|
650
712
|
console.log(chalk.dim('\n If drift is intentional, regenerate baseline with `neurcode policy lock`.\n'));
|
|
651
713
|
}
|
|
652
|
-
await
|
|
714
|
+
await recordPolicyOnlyVerification({
|
|
653
715
|
grade: 'F',
|
|
654
716
|
violations: lockViolationItems,
|
|
655
717
|
verifyResult: {
|
|
@@ -763,7 +825,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
763
825
|
},
|
|
764
826
|
};
|
|
765
827
|
if (options.json) {
|
|
766
|
-
|
|
828
|
+
emitPolicyOnlyJson({
|
|
767
829
|
grade,
|
|
768
830
|
score,
|
|
769
831
|
verdict: effectiveVerdict,
|
|
@@ -797,7 +859,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
797
859
|
},
|
|
798
860
|
}
|
|
799
861
|
: {}),
|
|
800
|
-
}
|
|
862
|
+
});
|
|
801
863
|
}
|
|
802
864
|
else {
|
|
803
865
|
if (effectiveVerdict === 'PASS') {
|
|
@@ -821,7 +883,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
821
883
|
displayGovernanceInsights(governanceAnalysis, { explain: options.explain });
|
|
822
884
|
console.log(chalk.dim(`\n${message}`));
|
|
823
885
|
}
|
|
824
|
-
await
|
|
886
|
+
await recordPolicyOnlyVerification({
|
|
825
887
|
grade,
|
|
826
888
|
violations: violationsOutput,
|
|
827
889
|
verifyResult: {
|
|
@@ -839,7 +901,54 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
839
901
|
}
|
|
840
902
|
async function verifyCommand(options) {
|
|
841
903
|
try {
|
|
842
|
-
const
|
|
904
|
+
const rootResolution = (0, project_root_1.resolveNeurcodeProjectRootWithTrace)(process.cwd());
|
|
905
|
+
const projectRoot = rootResolution.projectRoot;
|
|
906
|
+
const scopeTelemetry = (0, scope_telemetry_1.buildScopeTelemetryPayload)(rootResolution);
|
|
907
|
+
const emitVerifyJson = (payload) => {
|
|
908
|
+
const jsonPayload = {
|
|
909
|
+
...payload,
|
|
910
|
+
scope: scopeTelemetry,
|
|
911
|
+
};
|
|
912
|
+
console.log(JSON.stringify(jsonPayload, null, 2));
|
|
913
|
+
};
|
|
914
|
+
const enforceChangeContract = options.enforceChangeContract === true ||
|
|
915
|
+
isEnabledFlag(process.env.NEURCODE_VERIFY_ENFORCE_CHANGE_CONTRACT);
|
|
916
|
+
const changeContractRead = (0, change_contract_1.readChangeContract)(projectRoot, options.changeContract);
|
|
917
|
+
const compiledPolicyRead = (0, policy_compiler_1.readCompiledPolicyArtifact)(projectRoot, options.compiledPolicy);
|
|
918
|
+
let compiledPolicyMetadata = resolveCompiledPolicyMetadata(compiledPolicyRead.artifact, compiledPolicyRead.exists ? compiledPolicyRead.path : null);
|
|
919
|
+
let changeContractSummary = {
|
|
920
|
+
path: changeContractRead.path,
|
|
921
|
+
exists: changeContractRead.exists,
|
|
922
|
+
enforced: enforceChangeContract,
|
|
923
|
+
valid: changeContractRead.contract ? null : changeContractRead.exists ? false : null,
|
|
924
|
+
planId: changeContractRead.contract?.planId || null,
|
|
925
|
+
contractId: changeContractRead.contract?.contractId || null,
|
|
926
|
+
violations: changeContractRead.error
|
|
927
|
+
? [
|
|
928
|
+
{
|
|
929
|
+
code: 'CHANGE_CONTRACT_PARSE_ERROR',
|
|
930
|
+
message: changeContractRead.error,
|
|
931
|
+
},
|
|
932
|
+
]
|
|
933
|
+
: [],
|
|
934
|
+
};
|
|
935
|
+
if (!options.json) {
|
|
936
|
+
(0, scope_telemetry_1.printScopeTelemetry)(chalk, scopeTelemetry, {
|
|
937
|
+
includeBlockedWarning: true,
|
|
938
|
+
});
|
|
939
|
+
if (compiledPolicyRead.error) {
|
|
940
|
+
console.log(chalk.yellow(` Compiled policy artifact unavailable (${compiledPolicyRead.error}); falling back to runtime compilation`));
|
|
941
|
+
}
|
|
942
|
+
else if (compiledPolicyRead.artifact) {
|
|
943
|
+
console.log(chalk.dim(` Compiled policy loaded: ${compiledPolicyRead.path} (${compiledPolicyRead.artifact.compilation.deterministicRuleCount} deterministic rules)`));
|
|
944
|
+
}
|
|
945
|
+
if (changeContractRead.error) {
|
|
946
|
+
console.log(chalk.yellow(` Change contract unavailable (${changeContractRead.error})`));
|
|
947
|
+
}
|
|
948
|
+
else if (changeContractRead.contract) {
|
|
949
|
+
console.log(chalk.dim(` Change contract loaded: ${changeContractRead.path}`));
|
|
950
|
+
}
|
|
951
|
+
}
|
|
843
952
|
// Load configuration
|
|
844
953
|
const config = (0, config_1.loadConfig)();
|
|
845
954
|
// 🛑 FORCE PRIORITY: Env Var > Config
|
|
@@ -941,7 +1050,7 @@ async function verifyCommand(options) {
|
|
|
941
1050
|
const message = 'Signed AI change-logs are required but no signing key is configured. Set NEURCODE_GOVERNANCE_SIGNING_KEY or NEURCODE_GOVERNANCE_SIGNING_KEYS.';
|
|
942
1051
|
recordVerifyEvent('FAIL', 'missing_signing_key_material');
|
|
943
1052
|
if (options.json) {
|
|
944
|
-
|
|
1053
|
+
emitVerifyJson({
|
|
945
1054
|
grade: 'F',
|
|
946
1055
|
score: 0,
|
|
947
1056
|
verdict: 'FAIL',
|
|
@@ -962,7 +1071,7 @@ async function verifyCommand(options) {
|
|
|
962
1071
|
scopeGuardPassed: false,
|
|
963
1072
|
mode: 'plan_enforced',
|
|
964
1073
|
policyOnly: false,
|
|
965
|
-
}
|
|
1074
|
+
});
|
|
966
1075
|
}
|
|
967
1076
|
else {
|
|
968
1077
|
console.log(chalk.red('\n⛔ Governance Signing Key Missing'));
|
|
@@ -1021,7 +1130,7 @@ async function verifyCommand(options) {
|
|
|
1021
1130
|
console.log(chalk.dim(' Make sure you have staged or unstaged changes to verify'));
|
|
1022
1131
|
}
|
|
1023
1132
|
else {
|
|
1024
|
-
|
|
1133
|
+
emitVerifyJson({
|
|
1025
1134
|
grade: 'F',
|
|
1026
1135
|
score: 0,
|
|
1027
1136
|
verdict: 'FAIL',
|
|
@@ -1033,7 +1142,7 @@ async function verifyCommand(options) {
|
|
|
1033
1142
|
totalPlannedFiles: 0,
|
|
1034
1143
|
message: 'No changes detected',
|
|
1035
1144
|
scopeGuardPassed: false,
|
|
1036
|
-
}
|
|
1145
|
+
});
|
|
1037
1146
|
}
|
|
1038
1147
|
recordVerifyEvent('NO_CHANGES', 'diff=empty');
|
|
1039
1148
|
process.exit(0);
|
|
@@ -1064,7 +1173,7 @@ async function verifyCommand(options) {
|
|
|
1064
1173
|
console.log(chalk.yellow('⚠️ No file changes detected in diff'));
|
|
1065
1174
|
}
|
|
1066
1175
|
else {
|
|
1067
|
-
|
|
1176
|
+
emitVerifyJson({
|
|
1068
1177
|
grade: 'F',
|
|
1069
1178
|
score: 0,
|
|
1070
1179
|
verdict: 'FAIL',
|
|
@@ -1076,7 +1185,7 @@ async function verifyCommand(options) {
|
|
|
1076
1185
|
totalPlannedFiles: 0,
|
|
1077
1186
|
message: 'No file changes detected in diff',
|
|
1078
1187
|
scopeGuardPassed: false,
|
|
1079
|
-
}
|
|
1188
|
+
});
|
|
1080
1189
|
}
|
|
1081
1190
|
recordVerifyEvent('NO_CHANGES', 'diff_files=0');
|
|
1082
1191
|
process.exit(0);
|
|
@@ -1106,7 +1215,10 @@ async function verifyCommand(options) {
|
|
|
1106
1215
|
signingKeys: aiLogSigningKeys,
|
|
1107
1216
|
signer: aiLogSigner,
|
|
1108
1217
|
});
|
|
1109
|
-
const baselineGovernancePayload = buildGovernancePayload(baselineGovernance, orgGovernanceSettings
|
|
1218
|
+
const baselineGovernancePayload = buildGovernancePayload(baselineGovernance, orgGovernanceSettings, {
|
|
1219
|
+
changeContract: changeContractSummary,
|
|
1220
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1221
|
+
});
|
|
1110
1222
|
const message = `Context access policy violation: ${baselineContextViolations.map((item) => item.file).join(', ')}`;
|
|
1111
1223
|
const baselineContextViolationItems = baselineContextViolations.map((item) => ({
|
|
1112
1224
|
file: item.file,
|
|
@@ -1116,7 +1228,7 @@ async function verifyCommand(options) {
|
|
|
1116
1228
|
}));
|
|
1117
1229
|
recordVerifyEvent('FAIL', `context_policy_violations=${baselineContextViolations.length}`, diffFiles.map((f) => f.path));
|
|
1118
1230
|
if (options.json) {
|
|
1119
|
-
|
|
1231
|
+
emitVerifyJson({
|
|
1120
1232
|
grade: 'F',
|
|
1121
1233
|
score: 0,
|
|
1122
1234
|
verdict: 'FAIL',
|
|
@@ -1131,7 +1243,7 @@ async function verifyCommand(options) {
|
|
|
1131
1243
|
...baselineGovernancePayload,
|
|
1132
1244
|
mode: 'policy_violation',
|
|
1133
1245
|
policyOnly: false,
|
|
1134
|
-
}
|
|
1246
|
+
});
|
|
1135
1247
|
}
|
|
1136
1248
|
else {
|
|
1137
1249
|
console.log(chalk.red('\n⛔ Context Policy Violation'));
|
|
@@ -1163,7 +1275,7 @@ async function verifyCommand(options) {
|
|
|
1163
1275
|
console.log(chalk.dim(` ${summary.totalAdded} lines added, ${summary.totalRemoved} lines removed\n`));
|
|
1164
1276
|
}
|
|
1165
1277
|
const runPolicyOnlyModeAndExit = async (source) => {
|
|
1166
|
-
const exitCode = await executePolicyOnlyMode(options, diffFiles, shouldIgnore, projectRoot, config, client, source, projectId || undefined, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner);
|
|
1278
|
+
const exitCode = await executePolicyOnlyMode(options, diffFiles, shouldIgnore, projectRoot, config, client, source, scopeTelemetry, projectId || undefined, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, compiledPolicyMetadata, changeContractSummary);
|
|
1167
1279
|
const changedFiles = diffFiles.map((f) => f.path);
|
|
1168
1280
|
const verdict = exitCode === 2 ? 'FAIL' : exitCode === 1 ? 'WARN' : 'PASS';
|
|
1169
1281
|
recordVerifyEvent(verdict, `policy_only_source=${source};exit=${exitCode}`, changedFiles);
|
|
@@ -1215,7 +1327,7 @@ async function verifyCommand(options) {
|
|
|
1215
1327
|
const message = 'Plan ID is required in strict mode. Run "neurcode plan" first or pass --plan-id.';
|
|
1216
1328
|
recordVerifyEvent('FAIL', 'missing_plan_id;require_plan=true', changedFiles);
|
|
1217
1329
|
if (options.json) {
|
|
1218
|
-
|
|
1330
|
+
emitVerifyJson({
|
|
1219
1331
|
grade: 'F',
|
|
1220
1332
|
score: 0,
|
|
1221
1333
|
verdict: 'FAIL',
|
|
@@ -1229,7 +1341,7 @@ async function verifyCommand(options) {
|
|
|
1229
1341
|
scopeGuardPassed: false,
|
|
1230
1342
|
mode: 'plan_required',
|
|
1231
1343
|
policyOnly: false,
|
|
1232
|
-
}
|
|
1344
|
+
});
|
|
1233
1345
|
}
|
|
1234
1346
|
else {
|
|
1235
1347
|
console.log(chalk.red('❌ Plan ID is required in strict mode.'));
|
|
@@ -1269,6 +1381,8 @@ async function verifyCommand(options) {
|
|
|
1269
1381
|
// Track if scope guard passed - this takes priority over AI grading
|
|
1270
1382
|
let scopeGuardPassed = false;
|
|
1271
1383
|
let governanceResult = null;
|
|
1384
|
+
let planFilesForVerification = [];
|
|
1385
|
+
let intentConstraintsForVerification;
|
|
1272
1386
|
try {
|
|
1273
1387
|
// Step A: Get Modified Files (already have from diffFiles)
|
|
1274
1388
|
const modifiedFiles = diffFiles.map(f => f.path);
|
|
@@ -1285,6 +1399,8 @@ async function verifyCommand(options) {
|
|
|
1285
1399
|
const planFiles = planData.content.files
|
|
1286
1400
|
.filter(f => f.action === 'CREATE' || f.action === 'MODIFY')
|
|
1287
1401
|
.map(f => f.path);
|
|
1402
|
+
planFilesForVerification = [...planFiles];
|
|
1403
|
+
intentConstraintsForVerification = originalIntent || undefined;
|
|
1288
1404
|
const planDependencies = Array.isArray(planData.content.dependencies)
|
|
1289
1405
|
? planData.content.dependencies.filter((item) => typeof item === 'string')
|
|
1290
1406
|
: [];
|
|
@@ -1371,11 +1487,14 @@ async function verifyCommand(options) {
|
|
|
1371
1487
|
mode: 'plan_enforced',
|
|
1372
1488
|
policyOnly: false,
|
|
1373
1489
|
...(governanceResult
|
|
1374
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
1490
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1491
|
+
changeContract: changeContractSummary,
|
|
1492
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1493
|
+
})
|
|
1375
1494
|
: {}),
|
|
1376
1495
|
};
|
|
1377
1496
|
// CRITICAL: Print JSON first, then exit
|
|
1378
|
-
|
|
1497
|
+
emitVerifyJson(jsonOutput);
|
|
1379
1498
|
await recordVerificationIfRequested(options, config, {
|
|
1380
1499
|
grade: 'F',
|
|
1381
1500
|
violations: scopeViolationItems,
|
|
@@ -1389,7 +1508,10 @@ async function verifyCommand(options) {
|
|
|
1389
1508
|
projectId: projectId || undefined,
|
|
1390
1509
|
jsonMode: true,
|
|
1391
1510
|
governance: governanceResult
|
|
1392
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
1511
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1512
|
+
changeContract: changeContractSummary,
|
|
1513
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1514
|
+
})
|
|
1393
1515
|
: undefined,
|
|
1394
1516
|
});
|
|
1395
1517
|
process.exit(1);
|
|
@@ -1425,7 +1547,10 @@ async function verifyCommand(options) {
|
|
|
1425
1547
|
projectId: projectId || undefined,
|
|
1426
1548
|
jsonMode: false,
|
|
1427
1549
|
governance: governanceResult
|
|
1428
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
1550
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1551
|
+
changeContract: changeContractSummary,
|
|
1552
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1553
|
+
})
|
|
1429
1554
|
: undefined,
|
|
1430
1555
|
});
|
|
1431
1556
|
process.exit(1);
|
|
@@ -1532,8 +1657,13 @@ async function verifyCommand(options) {
|
|
|
1532
1657
|
scopeGuardPassed,
|
|
1533
1658
|
mode: 'plan_enforced',
|
|
1534
1659
|
policyOnly: false,
|
|
1660
|
+
changeContract: changeContractSummary,
|
|
1661
|
+
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
1535
1662
|
...(governanceResult
|
|
1536
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
1663
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1664
|
+
changeContract: changeContractSummary,
|
|
1665
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1666
|
+
})
|
|
1537
1667
|
: {}),
|
|
1538
1668
|
policyLock: {
|
|
1539
1669
|
enforced: true,
|
|
@@ -1564,12 +1694,35 @@ async function verifyCommand(options) {
|
|
|
1564
1694
|
projectId: projectId || undefined,
|
|
1565
1695
|
jsonMode: Boolean(options.json),
|
|
1566
1696
|
governance: governanceResult
|
|
1567
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
1697
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1698
|
+
changeContract: changeContractSummary,
|
|
1699
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1700
|
+
})
|
|
1568
1701
|
: undefined,
|
|
1569
1702
|
});
|
|
1570
1703
|
process.exit(2);
|
|
1571
1704
|
}
|
|
1572
1705
|
}
|
|
1706
|
+
let effectiveCompiledPolicy = compiledPolicyRead.artifact;
|
|
1707
|
+
if (!effectiveCompiledPolicy) {
|
|
1708
|
+
effectiveCompiledPolicy = buildCompiledPolicyFromEffectiveRules({
|
|
1709
|
+
projectRoot,
|
|
1710
|
+
policyLockEvaluation,
|
|
1711
|
+
effectiveRules,
|
|
1712
|
+
intentConstraints: intentConstraintsForVerification,
|
|
1713
|
+
});
|
|
1714
|
+
compiledPolicyMetadata = resolveCompiledPolicyMetadata(effectiveCompiledPolicy, (0, policy_compiler_1.resolveCompiledPolicyPath)(projectRoot, options.compiledPolicy));
|
|
1715
|
+
}
|
|
1716
|
+
const hydratedCompiledPolicyRules = effectiveCompiledPolicy
|
|
1717
|
+
? (0, policy_compiler_1.hydrateCompiledPolicyRules)(effectiveCompiledPolicy)
|
|
1718
|
+
: [];
|
|
1719
|
+
if (effectiveCompiledPolicy?.source.policyLockFingerprint &&
|
|
1720
|
+
policyLockEvaluation.lockPresent &&
|
|
1721
|
+
effectiveCompiledPolicy.source.policyLockFingerprint !== (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint) {
|
|
1722
|
+
if (!options.json) {
|
|
1723
|
+
console.log(chalk.yellow(' Compiled policy lock fingerprint differs from current lock; runtime checks will continue with latest lock state'));
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1573
1726
|
// Check user tier - Policy Compliance and A-F Grading are PRO features
|
|
1574
1727
|
const governance = (0, policy_governance_1.readPolicyGovernanceConfig)(projectRoot);
|
|
1575
1728
|
const auditIntegrity = (0, policy_audit_1.verifyPolicyAuditIntegrity)(projectRoot);
|
|
@@ -1589,7 +1742,7 @@ async function verifyCommand(options) {
|
|
|
1589
1742
|
console.log(chalk.dim(' Upgrade at: https://www.neurcode.com/dashboard/purchase-plan\n'));
|
|
1590
1743
|
}
|
|
1591
1744
|
else {
|
|
1592
|
-
|
|
1745
|
+
emitVerifyJson({
|
|
1593
1746
|
grade: 'N/A',
|
|
1594
1747
|
score: 0,
|
|
1595
1748
|
verdict: 'INFO',
|
|
@@ -1604,8 +1757,13 @@ async function verifyCommand(options) {
|
|
|
1604
1757
|
mode: 'plan_enforced',
|
|
1605
1758
|
policyOnly: false,
|
|
1606
1759
|
tier: 'FREE',
|
|
1760
|
+
changeContract: changeContractSummary,
|
|
1761
|
+
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
1607
1762
|
...(governanceResult
|
|
1608
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
1763
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1764
|
+
changeContract: changeContractSummary,
|
|
1765
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1766
|
+
})
|
|
1609
1767
|
: {}),
|
|
1610
1768
|
policyLock: {
|
|
1611
1769
|
enforced: policyLockEvaluation.enforced,
|
|
@@ -1623,7 +1781,7 @@ async function verifyCommand(options) {
|
|
|
1623
1781
|
eventCount: auditIntegrity.count,
|
|
1624
1782
|
},
|
|
1625
1783
|
},
|
|
1626
|
-
}
|
|
1784
|
+
});
|
|
1627
1785
|
}
|
|
1628
1786
|
process.exit(0);
|
|
1629
1787
|
}
|
|
@@ -1729,22 +1887,146 @@ async function verifyCommand(options) {
|
|
|
1729
1887
|
})),
|
|
1730
1888
|
})),
|
|
1731
1889
|
}));
|
|
1890
|
+
const changeContractEvaluation = changeContractRead.contract
|
|
1891
|
+
? (0, change_contract_1.evaluateChangeContract)(changeContractRead.contract, {
|
|
1892
|
+
planId: finalPlanId,
|
|
1893
|
+
changedFiles: changedFiles.map((file) => file.path),
|
|
1894
|
+
policyLockFingerprint: (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint || null,
|
|
1895
|
+
compiledPolicyFingerprint: effectiveCompiledPolicy?.fingerprint || null,
|
|
1896
|
+
})
|
|
1897
|
+
: null;
|
|
1898
|
+
if (changeContractEvaluation) {
|
|
1899
|
+
changeContractSummary = {
|
|
1900
|
+
path: changeContractRead.path,
|
|
1901
|
+
exists: true,
|
|
1902
|
+
enforced: enforceChangeContract,
|
|
1903
|
+
valid: changeContractEvaluation.valid,
|
|
1904
|
+
planId: changeContractRead.contract?.planId || null,
|
|
1905
|
+
contractId: changeContractRead.contract?.contractId || null,
|
|
1906
|
+
coverage: changeContractEvaluation.coverage,
|
|
1907
|
+
violations: changeContractEvaluation.violations.map((item) => ({
|
|
1908
|
+
code: item.code,
|
|
1909
|
+
message: item.message,
|
|
1910
|
+
file: item.file,
|
|
1911
|
+
expected: item.expected,
|
|
1912
|
+
actual: item.actual,
|
|
1913
|
+
})),
|
|
1914
|
+
};
|
|
1915
|
+
if (!changeContractEvaluation.valid && enforceChangeContract) {
|
|
1916
|
+
const violations = changeContractEvaluation.violations.map((item) => ({
|
|
1917
|
+
file: item.file || '.neurcode/change-contract.json',
|
|
1918
|
+
rule: `change_contract:${item.code.toLowerCase()}`,
|
|
1919
|
+
severity: 'block',
|
|
1920
|
+
message: item.message,
|
|
1921
|
+
}));
|
|
1922
|
+
const message = `Change contract enforcement failed: ${changeContractEvaluation.violations
|
|
1923
|
+
.map((item) => item.message)
|
|
1924
|
+
.join('; ')}`;
|
|
1925
|
+
if (options.json) {
|
|
1926
|
+
emitVerifyJson({
|
|
1927
|
+
grade: 'F',
|
|
1928
|
+
score: 0,
|
|
1929
|
+
verdict: 'FAIL',
|
|
1930
|
+
violations,
|
|
1931
|
+
adherenceScore: 0,
|
|
1932
|
+
bloatCount: 0,
|
|
1933
|
+
bloatFiles: [],
|
|
1934
|
+
plannedFilesModified: 0,
|
|
1935
|
+
totalPlannedFiles: 0,
|
|
1936
|
+
message,
|
|
1937
|
+
scopeGuardPassed: false,
|
|
1938
|
+
mode: 'plan_enforced',
|
|
1939
|
+
policyOnly: false,
|
|
1940
|
+
changeContract: changeContractSummary,
|
|
1941
|
+
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
else {
|
|
1945
|
+
console.log(chalk.red('\n⛔ Change contract enforcement failed'));
|
|
1946
|
+
changeContractEvaluation.violations.forEach((item) => {
|
|
1947
|
+
console.log(chalk.red(` • ${item.message}`));
|
|
1948
|
+
});
|
|
1949
|
+
console.log(chalk.dim(` Contract path: ${changeContractRead.path}`));
|
|
1950
|
+
}
|
|
1951
|
+
await recordVerificationIfRequested(options, config, {
|
|
1952
|
+
grade: 'F',
|
|
1953
|
+
violations,
|
|
1954
|
+
verifyResult: {
|
|
1955
|
+
adherenceScore: 0,
|
|
1956
|
+
verdict: 'FAIL',
|
|
1957
|
+
bloatCount: 0,
|
|
1958
|
+
bloatFiles: [],
|
|
1959
|
+
message,
|
|
1960
|
+
},
|
|
1961
|
+
projectId: projectId || undefined,
|
|
1962
|
+
jsonMode: Boolean(options.json),
|
|
1963
|
+
governance: governanceResult
|
|
1964
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
1965
|
+
changeContract: changeContractSummary,
|
|
1966
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
1967
|
+
})
|
|
1968
|
+
: undefined,
|
|
1969
|
+
});
|
|
1970
|
+
process.exit(2);
|
|
1971
|
+
}
|
|
1972
|
+
else if (!changeContractEvaluation.valid && !options.json) {
|
|
1973
|
+
console.log(chalk.yellow('\n⚠️ Change contract drift detected (advisory mode)'));
|
|
1974
|
+
changeContractEvaluation.violations.slice(0, 5).forEach((item) => {
|
|
1975
|
+
console.log(chalk.yellow(` • ${item.message}`));
|
|
1976
|
+
});
|
|
1977
|
+
if (changeContractEvaluation.violations.length > 5) {
|
|
1978
|
+
console.log(chalk.dim(` ... ${changeContractEvaluation.violations.length - 5} more violation(s)`));
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1732
1982
|
// Call verify API
|
|
1733
1983
|
if (!options.json) {
|
|
1734
1984
|
console.log(chalk.dim(' Sending to Neurcode API...\n'));
|
|
1735
1985
|
}
|
|
1736
1986
|
try {
|
|
1737
|
-
|
|
1738
|
-
let
|
|
1987
|
+
let verifySource = 'api';
|
|
1988
|
+
let verifyResult;
|
|
1989
|
+
const deterministicPolicyRules = effectiveCompiledPolicy
|
|
1990
|
+
? [...effectiveCompiledPolicy.statements.policyRules]
|
|
1991
|
+
: effectiveRules.customPolicies
|
|
1992
|
+
.map((policy) => policy.rule_text)
|
|
1993
|
+
.filter((ruleText) => typeof ruleText === 'string' && ruleText.trim().length > 0);
|
|
1739
1994
|
try {
|
|
1740
|
-
|
|
1741
|
-
intentConstraints = planData.intent || undefined;
|
|
1995
|
+
verifyResult = await client.verifyPlan(finalPlanId, diffStats, changedFiles, projectId, intentConstraintsForVerification, deterministicPolicyRules, 'api', compiledPolicyMetadata);
|
|
1742
1996
|
}
|
|
1743
|
-
catch {
|
|
1744
|
-
|
|
1997
|
+
catch (verifyApiError) {
|
|
1998
|
+
if (planFilesForVerification.length === 0) {
|
|
1999
|
+
throw verifyApiError;
|
|
2000
|
+
}
|
|
2001
|
+
verifySource = 'local_fallback';
|
|
2002
|
+
if (!options.json) {
|
|
2003
|
+
const fallbackReason = verifyApiError instanceof Error ? verifyApiError.message : String(verifyApiError);
|
|
2004
|
+
console.log(chalk.yellow('⚠️ Verify API unavailable, using local deterministic fallback.'));
|
|
2005
|
+
console.log(chalk.dim(` Reason: ${fallbackReason}`));
|
|
2006
|
+
}
|
|
2007
|
+
const localEvaluation = (0, governance_runtime_1.evaluatePlanVerification)({
|
|
2008
|
+
planFiles: planFilesForVerification.map((path) => ({
|
|
2009
|
+
path,
|
|
2010
|
+
action: 'MODIFY',
|
|
2011
|
+
})),
|
|
2012
|
+
changedFiles,
|
|
2013
|
+
diffStats,
|
|
2014
|
+
intentConstraints: intentConstraintsForVerification,
|
|
2015
|
+
policyRules: deterministicPolicyRules,
|
|
2016
|
+
extraConstraintRules: hydratedCompiledPolicyRules.length > 0 ? hydratedCompiledPolicyRules : undefined,
|
|
2017
|
+
});
|
|
2018
|
+
verifyResult = {
|
|
2019
|
+
verificationId: `local-fallback-${Date.now()}`,
|
|
2020
|
+
adherenceScore: localEvaluation.adherenceScore,
|
|
2021
|
+
bloatCount: localEvaluation.bloatCount,
|
|
2022
|
+
bloatFiles: localEvaluation.bloatFiles,
|
|
2023
|
+
plannedFilesModified: localEvaluation.plannedFilesModified,
|
|
2024
|
+
totalPlannedFiles: localEvaluation.totalPlannedFiles,
|
|
2025
|
+
verdict: localEvaluation.verdict,
|
|
2026
|
+
diffSummary: localEvaluation.diffSummary,
|
|
2027
|
+
message: localEvaluation.message,
|
|
2028
|
+
};
|
|
1745
2029
|
}
|
|
1746
|
-
// Call verifyPlan with intentConstraints
|
|
1747
|
-
const verifyResult = await client.verifyPlan(finalPlanId, diffStats, changedFiles, projectId, intentConstraints);
|
|
1748
2030
|
// Apply custom policy verdict: block from dashboard overrides API verdict
|
|
1749
2031
|
const policyBlock = policyDecision === 'block' && policyViolations.length > 0;
|
|
1750
2032
|
const governanceDecisionBlock = governanceResult?.governanceDecision?.decision === 'block';
|
|
@@ -1846,10 +2128,16 @@ async function verifyCommand(options) {
|
|
|
1846
2128
|
bloatFiles: filteredBloatFiles,
|
|
1847
2129
|
plannedFilesModified: verifyResult.plannedFilesModified,
|
|
1848
2130
|
totalPlannedFiles: verifyResult.totalPlannedFiles,
|
|
2131
|
+
verificationSource: verifySource,
|
|
1849
2132
|
mode: 'plan_enforced',
|
|
1850
2133
|
policyOnly: false,
|
|
2134
|
+
changeContract: changeContractSummary,
|
|
2135
|
+
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
1851
2136
|
...(governanceResult
|
|
1852
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
2137
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2138
|
+
changeContract: changeContractSummary,
|
|
2139
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
2140
|
+
})
|
|
1853
2141
|
: {}),
|
|
1854
2142
|
policyLock: {
|
|
1855
2143
|
enforced: policyLockEvaluation.enforced,
|
|
@@ -1871,7 +2159,7 @@ async function verifyCommand(options) {
|
|
|
1871
2159
|
}
|
|
1872
2160
|
: {}),
|
|
1873
2161
|
};
|
|
1874
|
-
|
|
2162
|
+
emitVerifyJson(jsonOutput);
|
|
1875
2163
|
await recordVerificationIfRequested(options, config, {
|
|
1876
2164
|
grade,
|
|
1877
2165
|
violations: violations,
|
|
@@ -1881,11 +2169,16 @@ async function verifyCommand(options) {
|
|
|
1881
2169
|
bloatCount: filteredBloatFiles.length,
|
|
1882
2170
|
bloatFiles: filteredBloatFiles,
|
|
1883
2171
|
message: effectiveMessage,
|
|
2172
|
+
verificationSource: verifySource,
|
|
1884
2173
|
},
|
|
1885
2174
|
projectId: projectId || undefined,
|
|
1886
2175
|
jsonMode: true,
|
|
2176
|
+
verificationSource: verifySource,
|
|
1887
2177
|
governance: governanceResult
|
|
1888
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
2178
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2179
|
+
changeContract: changeContractSummary,
|
|
2180
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
2181
|
+
})
|
|
1889
2182
|
: undefined,
|
|
1890
2183
|
});
|
|
1891
2184
|
// Exit based on effective verdict (same logic as below)
|
|
@@ -1960,11 +2253,16 @@ async function verifyCommand(options) {
|
|
|
1960
2253
|
bloatCount: filteredBloatForReport.length,
|
|
1961
2254
|
bloatFiles: filteredBloatForReport,
|
|
1962
2255
|
message: effectiveMessage,
|
|
2256
|
+
verificationSource: verifySource,
|
|
1963
2257
|
},
|
|
1964
2258
|
projectId: projectId || undefined,
|
|
1965
2259
|
jsonMode: false,
|
|
2260
|
+
verificationSource: verifySource,
|
|
1966
2261
|
governance: governanceResult
|
|
1967
|
-
? buildGovernancePayload(governanceResult, orgGovernanceSettings
|
|
2262
|
+
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2263
|
+
changeContract: changeContractSummary,
|
|
2264
|
+
compiledPolicy: compiledPolicyMetadata,
|
|
2265
|
+
})
|
|
1968
2266
|
: undefined,
|
|
1969
2267
|
});
|
|
1970
2268
|
// Governance override: keep PASS only when scope guard passes and failure is due
|
|
@@ -2001,7 +2299,7 @@ async function verifyCommand(options) {
|
|
|
2001
2299
|
recordVerifyEvent('FAIL', `verify_api_error=${error instanceof Error ? error.message : 'unknown'}`, changedFiles, finalPlanId);
|
|
2002
2300
|
if (options.json) {
|
|
2003
2301
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
2004
|
-
|
|
2302
|
+
emitVerifyJson({
|
|
2005
2303
|
grade: 'F',
|
|
2006
2304
|
score: 0,
|
|
2007
2305
|
verdict: 'FAIL',
|
|
@@ -2013,7 +2311,7 @@ async function verifyCommand(options) {
|
|
|
2013
2311
|
totalPlannedFiles: 0,
|
|
2014
2312
|
message: `Error: ${errorMessage}`,
|
|
2015
2313
|
scopeGuardPassed: false,
|
|
2016
|
-
}
|
|
2314
|
+
});
|
|
2017
2315
|
}
|
|
2018
2316
|
else {
|
|
2019
2317
|
if (error instanceof Error) {
|
|
@@ -2048,6 +2346,14 @@ async function verifyCommand(options) {
|
|
|
2048
2346
|
totalPlannedFiles: 0,
|
|
2049
2347
|
message: `Unexpected error: ${errorMessage}`,
|
|
2050
2348
|
scopeGuardPassed: false,
|
|
2349
|
+
scope: {
|
|
2350
|
+
scanRoot: process.cwd(),
|
|
2351
|
+
startDir: process.cwd(),
|
|
2352
|
+
gitRoot: null,
|
|
2353
|
+
linkedRepoOverrideUsed: false,
|
|
2354
|
+
linkedRepos: [],
|
|
2355
|
+
blockedOverride: null,
|
|
2356
|
+
},
|
|
2051
2357
|
}, null, 2));
|
|
2052
2358
|
}
|
|
2053
2359
|
else {
|
|
@@ -2212,7 +2518,7 @@ function buildCompactVerificationPayload(payload) {
|
|
|
2212
2518
|
/**
|
|
2213
2519
|
* Report verification results to Neurcode Cloud
|
|
2214
2520
|
*/
|
|
2215
|
-
async function reportVerification(grade, violations, verifyResult, apiKey, apiUrl, projectId, jsonMode, governance) {
|
|
2521
|
+
async function reportVerification(grade, violations, verifyResult, apiKey, apiUrl, projectId, jsonMode, governance, verificationSource) {
|
|
2216
2522
|
try {
|
|
2217
2523
|
const ciContext = collectCIContext();
|
|
2218
2524
|
const payload = {
|
|
@@ -2229,6 +2535,7 @@ async function reportVerification(grade, violations, verifyResult, apiKey, apiUr
|
|
|
2229
2535
|
workflowRunId: ciContext.workflowRunId,
|
|
2230
2536
|
projectId,
|
|
2231
2537
|
governance,
|
|
2538
|
+
verificationSource: verificationSource || verifyResult.verificationSource || 'api',
|
|
2232
2539
|
};
|
|
2233
2540
|
const postPayload = async (requestPayload) => fetch(`${apiUrl}/api/v1/action/verifications`, {
|
|
2234
2541
|
method: 'POST',
|
|
@@ -2271,7 +2578,7 @@ async function reportVerification(grade, violations, verifyResult, apiKey, apiUr
|
|
|
2271
2578
|
}
|
|
2272
2579
|
}
|
|
2273
2580
|
}
|
|
2274
|
-
function buildGovernancePayload(governance, orgGovernanceSettings) {
|
|
2581
|
+
function buildGovernancePayload(governance, orgGovernanceSettings, options) {
|
|
2275
2582
|
return {
|
|
2276
2583
|
contextPolicy: governance.contextPolicy,
|
|
2277
2584
|
blastRadius: governance.blastRadius,
|
|
@@ -2292,6 +2599,8 @@ function buildGovernancePayload(governance, orgGovernanceSettings) {
|
|
|
2292
2599
|
updatedAt: orgGovernanceSettings.updatedAt || null,
|
|
2293
2600
|
}
|
|
2294
2601
|
: null,
|
|
2602
|
+
...(options?.compiledPolicy ? { policyCompilation: options.compiledPolicy } : {}),
|
|
2603
|
+
...(options?.changeContract ? { changeContract: options.changeContract } : {}),
|
|
2295
2604
|
};
|
|
2296
2605
|
}
|
|
2297
2606
|
function displayGovernanceInsights(governance, options = {}) {
|