@neurcode-ai/cli 0.9.39 → 0.9.41
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/dist/api-client.d.ts +54 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +36 -0
- package/dist/api-client.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +20 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +378 -0
- package/dist/commands/bootstrap.js.map +1 -0
- package/dist/commands/contract.d.ts.map +1 -1
- package/dist/commands/contract.js +125 -2
- package/dist/commands/contract.js.map +1 -1
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +145 -0
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/guard.d.ts +3 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +390 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +106 -1
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/remediate.d.ts +4 -0
- package/dist/commands/remediate.d.ts.map +1 -1
- package/dist/commands/remediate.js +420 -65
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +329 -7
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/ai-debt-budget.d.ts +49 -0
- package/dist/utils/ai-debt-budget.d.ts.map +1 -0
- package/dist/utils/ai-debt-budget.js +241 -0
- package/dist/utils/ai-debt-budget.js.map +1 -0
- package/dist/utils/policy-compiler.d.ts +4 -0
- package/dist/utils/policy-compiler.d.ts.map +1 -1
- package/dist/utils/policy-compiler.js +43 -0
- package/dist/utils/policy-compiler.js.map +1 -1
- package/dist/utils/runtime-guard.d.ts +93 -0
- package/dist/utils/runtime-guard.d.ts.map +1 -0
- package/dist/utils/runtime-guard.js +327 -0
- package/dist/utils/runtime-guard.js.map +1 -0
- package/package.json +2 -2
package/dist/commands/verify.js
CHANGED
|
@@ -64,8 +64,10 @@ const policy_audit_1 = require("../utils/policy-audit");
|
|
|
64
64
|
const governance_1 = require("../utils/governance");
|
|
65
65
|
const policy_compiler_1 = require("../utils/policy-compiler");
|
|
66
66
|
const change_contract_1 = require("../utils/change-contract");
|
|
67
|
+
const runtime_guard_1 = require("../utils/runtime-guard");
|
|
67
68
|
const artifact_signature_1 = require("../utils/artifact-signature");
|
|
68
69
|
const policy_1 = require("@neurcode-ai/policy");
|
|
70
|
+
const ai_debt_budget_1 = require("../utils/ai-debt-budget");
|
|
69
71
|
// Import chalk with fallback
|
|
70
72
|
let chalk;
|
|
71
73
|
try {
|
|
@@ -434,6 +436,93 @@ function isEnabledFlag(value) {
|
|
|
434
436
|
const normalized = value.trim().toLowerCase();
|
|
435
437
|
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
436
438
|
}
|
|
439
|
+
function createRuntimeGuardSummary(required, projectRoot, runtimeGuardPath) {
|
|
440
|
+
return {
|
|
441
|
+
required,
|
|
442
|
+
path: (0, runtime_guard_1.resolveRuntimeGuardPath)(projectRoot, runtimeGuardPath),
|
|
443
|
+
exists: false,
|
|
444
|
+
valid: false,
|
|
445
|
+
active: false,
|
|
446
|
+
pass: !required,
|
|
447
|
+
changedFiles: 0,
|
|
448
|
+
outOfScopeFiles: [],
|
|
449
|
+
constraintViolations: [],
|
|
450
|
+
violations: [],
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function runtimeGuardViolationsToReport(summary) {
|
|
454
|
+
if (!summary.required || summary.pass) {
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
if (!summary.exists) {
|
|
458
|
+
return [
|
|
459
|
+
{
|
|
460
|
+
file: summary.path,
|
|
461
|
+
rule: 'runtime_guard_missing',
|
|
462
|
+
severity: 'block',
|
|
463
|
+
message: 'Runtime guard artifact is missing. Run `neurcode guard start` before verify.',
|
|
464
|
+
},
|
|
465
|
+
];
|
|
466
|
+
}
|
|
467
|
+
if (!summary.valid) {
|
|
468
|
+
return [
|
|
469
|
+
{
|
|
470
|
+
file: summary.path,
|
|
471
|
+
rule: 'runtime_guard_invalid',
|
|
472
|
+
severity: 'block',
|
|
473
|
+
message: 'Runtime guard artifact is invalid. Regenerate with `neurcode guard start`.',
|
|
474
|
+
},
|
|
475
|
+
];
|
|
476
|
+
}
|
|
477
|
+
return summary.violations.map((item) => ({
|
|
478
|
+
file: item.file || summary.path,
|
|
479
|
+
rule: `runtime_guard:${item.code.toLowerCase()}`,
|
|
480
|
+
severity: 'block',
|
|
481
|
+
message: item.message,
|
|
482
|
+
}));
|
|
483
|
+
}
|
|
484
|
+
function toAiDebtSummary(evaluation) {
|
|
485
|
+
return {
|
|
486
|
+
mode: evaluation.mode,
|
|
487
|
+
pass: evaluation.pass,
|
|
488
|
+
score: evaluation.score,
|
|
489
|
+
source: evaluation.source,
|
|
490
|
+
metrics: {
|
|
491
|
+
addedTodoFixme: evaluation.metrics.addedTodoFixme,
|
|
492
|
+
addedConsoleLogs: evaluation.metrics.addedConsoleLogs,
|
|
493
|
+
addedAnyTypes: evaluation.metrics.addedAnyTypes,
|
|
494
|
+
largeFilesTouched: evaluation.metrics.largeFilesTouched,
|
|
495
|
+
bloatFiles: evaluation.metrics.bloatFiles,
|
|
496
|
+
},
|
|
497
|
+
thresholds: {
|
|
498
|
+
maxAddedTodoFixme: evaluation.thresholds.maxAddedTodoFixme,
|
|
499
|
+
maxAddedConsoleLogs: evaluation.thresholds.maxAddedConsoleLogs,
|
|
500
|
+
maxAddedAnyTypes: evaluation.thresholds.maxAddedAnyTypes,
|
|
501
|
+
maxLargeFilesTouched: evaluation.thresholds.maxLargeFilesTouched,
|
|
502
|
+
largeFileDeltaLines: evaluation.thresholds.largeFileDeltaLines,
|
|
503
|
+
maxBloatFiles: evaluation.thresholds.maxBloatFiles,
|
|
504
|
+
},
|
|
505
|
+
violations: evaluation.violations.map((item) => ({
|
|
506
|
+
code: item.code,
|
|
507
|
+
metric: item.metric,
|
|
508
|
+
observed: item.observed,
|
|
509
|
+
budget: item.budget,
|
|
510
|
+
message: item.message,
|
|
511
|
+
})),
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function toAiDebtReportViolations(summary) {
|
|
515
|
+
if (summary.mode === 'off' || summary.violations.length === 0) {
|
|
516
|
+
return [];
|
|
517
|
+
}
|
|
518
|
+
const severity = summary.mode === 'enforce' ? 'block' : 'warn';
|
|
519
|
+
return summary.violations.map((item) => ({
|
|
520
|
+
rule: `ai_debt:${item.code}`,
|
|
521
|
+
file: '.neurcode/ai-debt-budget.json',
|
|
522
|
+
severity,
|
|
523
|
+
message: item.message,
|
|
524
|
+
}));
|
|
525
|
+
}
|
|
437
526
|
function parseSigningKeyRing(raw) {
|
|
438
527
|
if (!raw || !raw.trim()) {
|
|
439
528
|
return {};
|
|
@@ -1188,6 +1277,16 @@ async function verifyCommand(options) {
|
|
|
1188
1277
|
&& !isEnabledFlag(process.env.NEURCODE_VERIFY_ALLOW_NON_STRICT_CI)
|
|
1189
1278
|
&& Boolean(options.apiKey || process.env.NEURCODE_API_KEY);
|
|
1190
1279
|
const strictArtifactMode = explicitStrictArtifactMode || ciEnterpriseDefaultStrict;
|
|
1280
|
+
const runtimeGuardArtifactPath = (0, runtime_guard_1.resolveRuntimeGuardPath)(projectRoot, options.runtimeGuard);
|
|
1281
|
+
const autoRuntimeGuardInStrict = strictArtifactMode
|
|
1282
|
+
&& (0, fs_1.existsSync)(runtimeGuardArtifactPath)
|
|
1283
|
+
&& !isEnabledFlag(process.env.NEURCODE_VERIFY_DISABLE_AUTO_RUNTIME_GUARD);
|
|
1284
|
+
const requireRuntimeGuard = options.requireRuntimeGuard === true
|
|
1285
|
+
|| isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD)
|
|
1286
|
+
|| autoRuntimeGuardInStrict;
|
|
1287
|
+
const aiDebtConfig = (0, ai_debt_budget_1.resolveAiDebtBudgetConfig)(projectRoot, {
|
|
1288
|
+
strictDefault: strictArtifactMode || requireRuntimeGuard,
|
|
1289
|
+
});
|
|
1191
1290
|
const signingConfig = resolveGovernanceSigningConfig();
|
|
1192
1291
|
const aiLogSigningKey = signingConfig.signingKey;
|
|
1193
1292
|
const aiLogSigningKeyId = signingConfig.signingKeyId;
|
|
@@ -1201,6 +1300,7 @@ async function verifyCommand(options) {
|
|
|
1201
1300
|
|| (!allowUnsignedArtifacts && strictArtifactMode && hasSigningMaterial);
|
|
1202
1301
|
const changeContractRead = (0, change_contract_1.readChangeContract)(projectRoot, options.changeContract);
|
|
1203
1302
|
const compiledPolicyRead = (0, policy_compiler_1.readCompiledPolicyArtifact)(projectRoot, options.compiledPolicy);
|
|
1303
|
+
let runtimeGuardSummary = createRuntimeGuardSummary(requireRuntimeGuard, projectRoot, options.runtimeGuard);
|
|
1204
1304
|
let compiledPolicyMetadata = resolveCompiledPolicyMetadata(compiledPolicyRead.artifact, compiledPolicyRead.exists ? compiledPolicyRead.path : null);
|
|
1205
1305
|
const compiledPolicySignatureStatus = compiledPolicyRead.artifact
|
|
1206
1306
|
? (0, artifact_signature_1.verifyGovernanceArtifactSignature)({
|
|
@@ -1391,6 +1491,9 @@ async function verifyCommand(options) {
|
|
|
1391
1491
|
if (ciEnterpriseDefaultStrict && !explicitStrictArtifactMode) {
|
|
1392
1492
|
console.log(chalk.dim(' CI enterprise mode detected: strict deterministic artifact enforcement is auto-enabled (set NEURCODE_VERIFY_ALLOW_NON_STRICT_CI=1 to opt out).'));
|
|
1393
1493
|
}
|
|
1494
|
+
if (autoRuntimeGuardInStrict && !options.requireRuntimeGuard && !isEnabledFlag(process.env.NEURCODE_VERIFY_REQUIRE_RUNTIME_GUARD)) {
|
|
1495
|
+
console.log(chalk.dim(` Strict mode detected runtime guard artifact: auto-enforcing runtime guard (${runtimeGuardArtifactPath}).`));
|
|
1496
|
+
}
|
|
1394
1497
|
if (requireSignedArtifacts) {
|
|
1395
1498
|
console.log(chalk.dim(' Artifact signature enforcement: enabled (set NEURCODE_VERIFY_ALLOW_UNSIGNED_ARTIFACTS=1 to relax)'));
|
|
1396
1499
|
}
|
|
@@ -1705,6 +1808,140 @@ async function verifyCommand(options) {
|
|
|
1705
1808
|
const normalized = toUnixPath(filePath || '');
|
|
1706
1809
|
return ignoreFilter(normalized) || runtimeIgnoreSet.has(normalized);
|
|
1707
1810
|
};
|
|
1811
|
+
if (requireRuntimeGuard) {
|
|
1812
|
+
const guardRead = (0, runtime_guard_1.readRuntimeGuardArtifact)(projectRoot, options.runtimeGuard);
|
|
1813
|
+
runtimeGuardSummary = {
|
|
1814
|
+
...runtimeGuardSummary,
|
|
1815
|
+
path: guardRead.path,
|
|
1816
|
+
exists: guardRead.exists,
|
|
1817
|
+
valid: Boolean(guardRead.artifact),
|
|
1818
|
+
};
|
|
1819
|
+
if (!guardRead.artifact) {
|
|
1820
|
+
const message = guardRead.error
|
|
1821
|
+
? `Runtime guard artifact is invalid: ${guardRead.error}`
|
|
1822
|
+
: 'Runtime guard artifact missing. Run `neurcode guard start` before verify.';
|
|
1823
|
+
runtimeGuardSummary = {
|
|
1824
|
+
...runtimeGuardSummary,
|
|
1825
|
+
active: false,
|
|
1826
|
+
pass: false,
|
|
1827
|
+
violations: [
|
|
1828
|
+
{
|
|
1829
|
+
code: guardRead.error ? 'RUNTIME_GUARD_INACTIVE' : 'RUNTIME_GUARD_INACTIVE',
|
|
1830
|
+
message,
|
|
1831
|
+
},
|
|
1832
|
+
],
|
|
1833
|
+
};
|
|
1834
|
+
const runtimeGuardViolationItems = runtimeGuardViolationsToReport(runtimeGuardSummary);
|
|
1835
|
+
recordVerifyEvent('FAIL', 'runtime_guard_missing_or_invalid', diffFiles.map((f) => f.path));
|
|
1836
|
+
if (options.json) {
|
|
1837
|
+
emitVerifyJson({
|
|
1838
|
+
grade: 'F',
|
|
1839
|
+
score: 0,
|
|
1840
|
+
verdict: 'FAIL',
|
|
1841
|
+
violations: runtimeGuardViolationItems,
|
|
1842
|
+
adherenceScore: 0,
|
|
1843
|
+
bloatCount: 0,
|
|
1844
|
+
bloatFiles: [],
|
|
1845
|
+
plannedFilesModified: 0,
|
|
1846
|
+
totalPlannedFiles: 0,
|
|
1847
|
+
message,
|
|
1848
|
+
scopeGuardPassed: false,
|
|
1849
|
+
mode: 'runtime_guard_required',
|
|
1850
|
+
policyOnly: Boolean(options.policyOnly),
|
|
1851
|
+
runtimeGuard: runtimeGuardSummary,
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
else {
|
|
1855
|
+
console.log(chalk.red('\n⛔ Runtime Guard Required'));
|
|
1856
|
+
console.log(chalk.red(` ${message}`));
|
|
1857
|
+
console.log(chalk.dim(` Path: ${runtimeGuardSummary.path}`));
|
|
1858
|
+
console.log(chalk.dim(' Start guard: neurcode guard start --strict\n'));
|
|
1859
|
+
}
|
|
1860
|
+
await recordVerificationIfRequested(options, config, {
|
|
1861
|
+
grade: 'F',
|
|
1862
|
+
violations: runtimeGuardViolationItems,
|
|
1863
|
+
verifyResult: {
|
|
1864
|
+
adherenceScore: 0,
|
|
1865
|
+
verdict: 'FAIL',
|
|
1866
|
+
bloatCount: 0,
|
|
1867
|
+
bloatFiles: [],
|
|
1868
|
+
message,
|
|
1869
|
+
},
|
|
1870
|
+
projectId: projectId || undefined,
|
|
1871
|
+
jsonMode: Boolean(options.json),
|
|
1872
|
+
});
|
|
1873
|
+
process.exit(2);
|
|
1874
|
+
}
|
|
1875
|
+
const runtimeGuardEvaluation = (0, runtime_guard_1.evaluateRuntimeGuardArtifact)(guardRead.artifact, diffFiles.filter((file) => !shouldIgnore(file.path)));
|
|
1876
|
+
runtimeGuardSummary = {
|
|
1877
|
+
...runtimeGuardSummary,
|
|
1878
|
+
active: guardRead.artifact.active,
|
|
1879
|
+
pass: runtimeGuardEvaluation.pass,
|
|
1880
|
+
changedFiles: runtimeGuardEvaluation.changedFiles.length,
|
|
1881
|
+
outOfScopeFiles: runtimeGuardEvaluation.outOfScopeFiles,
|
|
1882
|
+
constraintViolations: runtimeGuardEvaluation.constraintViolations,
|
|
1883
|
+
violations: runtimeGuardEvaluation.violations.map((item) => ({
|
|
1884
|
+
code: item.code,
|
|
1885
|
+
message: item.message,
|
|
1886
|
+
...(item.file ? { file: item.file } : {}),
|
|
1887
|
+
})),
|
|
1888
|
+
};
|
|
1889
|
+
const runtimeGuardUpdated = (0, runtime_guard_1.withRuntimeGuardCheckStats)(guardRead.artifact, {
|
|
1890
|
+
blocked: !runtimeGuardEvaluation.pass,
|
|
1891
|
+
});
|
|
1892
|
+
(0, runtime_guard_1.writeRuntimeGuardArtifact)(projectRoot, runtimeGuardUpdated, options.runtimeGuard);
|
|
1893
|
+
if (!runtimeGuardEvaluation.pass) {
|
|
1894
|
+
const message = runtimeGuardEvaluation.violations.length > 0
|
|
1895
|
+
? `Runtime guard blocked ${runtimeGuardEvaluation.violations.length} violation(s).`
|
|
1896
|
+
: 'Runtime guard blocked verification.';
|
|
1897
|
+
const runtimeGuardViolationItems = runtimeGuardViolationsToReport(runtimeGuardSummary);
|
|
1898
|
+
recordVerifyEvent('FAIL', `runtime_guard_violations=${runtimeGuardEvaluation.violations.length}`, diffFiles.map((f) => f.path));
|
|
1899
|
+
if (options.json) {
|
|
1900
|
+
emitVerifyJson({
|
|
1901
|
+
grade: 'F',
|
|
1902
|
+
score: 0,
|
|
1903
|
+
verdict: 'FAIL',
|
|
1904
|
+
violations: runtimeGuardViolationItems,
|
|
1905
|
+
adherenceScore: 0,
|
|
1906
|
+
bloatCount: runtimeGuardEvaluation.outOfScopeFiles.length,
|
|
1907
|
+
bloatFiles: runtimeGuardEvaluation.outOfScopeFiles,
|
|
1908
|
+
plannedFilesModified: runtimeGuardEvaluation.plannedFilesModified,
|
|
1909
|
+
totalPlannedFiles: runtimeGuardEvaluation.totalPlannedFiles,
|
|
1910
|
+
message,
|
|
1911
|
+
scopeGuardPassed: false,
|
|
1912
|
+
mode: 'runtime_guard_blocked',
|
|
1913
|
+
policyOnly: Boolean(options.policyOnly),
|
|
1914
|
+
runtimeGuard: runtimeGuardSummary,
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
else {
|
|
1918
|
+
console.log(chalk.red('\n⛔ Runtime Guard Blocked Verification'));
|
|
1919
|
+
runtimeGuardEvaluation.violations.forEach((item) => {
|
|
1920
|
+
const file = item.file ? `${item.file}: ` : '';
|
|
1921
|
+
console.log(chalk.red(` • ${file}${item.message}`));
|
|
1922
|
+
});
|
|
1923
|
+
console.log(chalk.dim(`\n Guard artifact: ${runtimeGuardSummary.path}\n`));
|
|
1924
|
+
}
|
|
1925
|
+
await recordVerificationIfRequested(options, config, {
|
|
1926
|
+
grade: 'F',
|
|
1927
|
+
violations: runtimeGuardViolationItems,
|
|
1928
|
+
verifyResult: {
|
|
1929
|
+
adherenceScore: runtimeGuardEvaluation.adherenceScore,
|
|
1930
|
+
verdict: 'FAIL',
|
|
1931
|
+
bloatCount: runtimeGuardEvaluation.outOfScopeFiles.length,
|
|
1932
|
+
bloatFiles: runtimeGuardEvaluation.outOfScopeFiles,
|
|
1933
|
+
message,
|
|
1934
|
+
},
|
|
1935
|
+
projectId: projectId || undefined,
|
|
1936
|
+
jsonMode: Boolean(options.json),
|
|
1937
|
+
});
|
|
1938
|
+
process.exit(2);
|
|
1939
|
+
}
|
|
1940
|
+
if (!options.json) {
|
|
1941
|
+
console.log(chalk.dim(` Runtime guard passed (${runtimeGuardSummary.changedFiles} changed file(s), ` +
|
|
1942
|
+
`${runtimeGuardSummary.violations.length} violation(s))`));
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1708
1945
|
const baselineContextPolicyLocal = (0, policy_1.loadContextPolicy)(projectRoot);
|
|
1709
1946
|
const baselineContextPolicy = orgGovernanceSettings?.contextPolicy
|
|
1710
1947
|
? (0, policy_1.mergeContextPolicies)(baselineContextPolicyLocal, orgGovernanceSettings.contextPolicy)
|
|
@@ -1973,6 +2210,11 @@ async function verifyCommand(options) {
|
|
|
1973
2210
|
const filteredViolations = violations.filter((p) => !shouldIgnore(p));
|
|
1974
2211
|
// Step D: The Block (only report scope violations for non-ignored files)
|
|
1975
2212
|
if (filteredViolations.length > 0) {
|
|
2213
|
+
const aiDebtSummaryForScope = toAiDebtSummary((0, ai_debt_budget_1.evaluateAiDebtBudget)({
|
|
2214
|
+
diffFiles,
|
|
2215
|
+
bloatCount: filteredViolations.length,
|
|
2216
|
+
config: aiDebtConfig,
|
|
2217
|
+
}));
|
|
1976
2218
|
recordVerifyEvent('FAIL', `scope_violation=${filteredViolations.length}`, modifiedFiles, finalPlanId);
|
|
1977
2219
|
const scopeViolationItems = filteredViolations.map((file) => ({
|
|
1978
2220
|
file,
|
|
@@ -1980,6 +2222,11 @@ async function verifyCommand(options) {
|
|
|
1980
2222
|
severity: 'block',
|
|
1981
2223
|
message: 'File modified outside the plan',
|
|
1982
2224
|
}));
|
|
2225
|
+
const aiDebtViolationItems = toAiDebtReportViolations(aiDebtSummaryForScope);
|
|
2226
|
+
const scopeViolationReportItems = [
|
|
2227
|
+
...scopeViolationItems,
|
|
2228
|
+
...aiDebtViolationItems,
|
|
2229
|
+
];
|
|
1983
2230
|
const scopeViolationMessage = `Scope violation: ${filteredViolations.length} file(s) modified outside the plan`;
|
|
1984
2231
|
if (options.json) {
|
|
1985
2232
|
// Output JSON for scope violation BEFORE exit. Must include violations for GitHub Action annotations.
|
|
@@ -1987,7 +2234,7 @@ async function verifyCommand(options) {
|
|
|
1987
2234
|
grade: 'F',
|
|
1988
2235
|
score: 0,
|
|
1989
2236
|
verdict: 'FAIL',
|
|
1990
|
-
violations:
|
|
2237
|
+
violations: scopeViolationReportItems,
|
|
1991
2238
|
adherenceScore: 0,
|
|
1992
2239
|
bloatCount: filteredViolations.length,
|
|
1993
2240
|
bloatFiles: filteredViolations,
|
|
@@ -1997,10 +2244,12 @@ async function verifyCommand(options) {
|
|
|
1997
2244
|
scopeGuardPassed: false,
|
|
1998
2245
|
mode: 'plan_enforced',
|
|
1999
2246
|
policyOnly: false,
|
|
2247
|
+
aiDebt: aiDebtSummaryForScope,
|
|
2000
2248
|
...(governanceResult
|
|
2001
2249
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2002
2250
|
changeContract: changeContractSummary,
|
|
2003
2251
|
compiledPolicy: compiledPolicyMetadata,
|
|
2252
|
+
aiDebt: aiDebtSummaryForScope,
|
|
2004
2253
|
})
|
|
2005
2254
|
: {}),
|
|
2006
2255
|
};
|
|
@@ -2008,7 +2257,7 @@ async function verifyCommand(options) {
|
|
|
2008
2257
|
emitVerifyJson(jsonOutput);
|
|
2009
2258
|
await recordVerificationIfRequested(options, config, {
|
|
2010
2259
|
grade: 'F',
|
|
2011
|
-
violations:
|
|
2260
|
+
violations: scopeViolationReportItems,
|
|
2012
2261
|
verifyResult: {
|
|
2013
2262
|
adherenceScore: 0,
|
|
2014
2263
|
verdict: 'FAIL',
|
|
@@ -2022,6 +2271,7 @@ async function verifyCommand(options) {
|
|
|
2022
2271
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2023
2272
|
changeContract: changeContractSummary,
|
|
2024
2273
|
compiledPolicy: compiledPolicyMetadata,
|
|
2274
|
+
aiDebt: aiDebtSummaryForScope,
|
|
2025
2275
|
})
|
|
2026
2276
|
: undefined,
|
|
2027
2277
|
});
|
|
@@ -2041,13 +2291,31 @@ async function verifyCommand(options) {
|
|
|
2041
2291
|
filteredViolations.forEach(file => {
|
|
2042
2292
|
console.log(chalk.dim(` neurcode allow ${file}`));
|
|
2043
2293
|
});
|
|
2294
|
+
if (aiDebtSummaryForScope.mode !== 'off') {
|
|
2295
|
+
console.log('');
|
|
2296
|
+
const header = aiDebtSummaryForScope.mode === 'enforce'
|
|
2297
|
+
? aiDebtSummaryForScope.pass
|
|
2298
|
+
? chalk.green('AI Debt Budget: PASS')
|
|
2299
|
+
: chalk.red('AI Debt Budget: BLOCK')
|
|
2300
|
+
: chalk.yellow('AI Debt Budget: ADVISORY');
|
|
2301
|
+
console.log(header);
|
|
2302
|
+
console.log(chalk.dim(` Score: ${aiDebtSummaryForScope.score} | TODO/FIXME +${aiDebtSummaryForScope.metrics.addedTodoFixme} | ` +
|
|
2303
|
+
`console.log +${aiDebtSummaryForScope.metrics.addedConsoleLogs} | any +${aiDebtSummaryForScope.metrics.addedAnyTypes} | ` +
|
|
2304
|
+
`large files ${aiDebtSummaryForScope.metrics.largeFilesTouched} | bloat ${aiDebtSummaryForScope.metrics.bloatFiles}`));
|
|
2305
|
+
if (aiDebtSummaryForScope.violations.length > 0) {
|
|
2306
|
+
aiDebtSummaryForScope.violations.forEach((item) => {
|
|
2307
|
+
const color = aiDebtSummaryForScope.mode === 'enforce' ? chalk.red : chalk.yellow;
|
|
2308
|
+
console.log(color(` • ${item.message}`));
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2044
2312
|
if (governanceResult) {
|
|
2045
2313
|
displayGovernanceInsights(governanceResult, { explain: options.explain });
|
|
2046
2314
|
}
|
|
2047
2315
|
console.log('');
|
|
2048
2316
|
await recordVerificationIfRequested(options, config, {
|
|
2049
2317
|
grade: 'F',
|
|
2050
|
-
violations:
|
|
2318
|
+
violations: scopeViolationReportItems,
|
|
2051
2319
|
verifyResult: {
|
|
2052
2320
|
adherenceScore: 0,
|
|
2053
2321
|
verdict: 'FAIL',
|
|
@@ -2061,6 +2329,7 @@ async function verifyCommand(options) {
|
|
|
2061
2329
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2062
2330
|
changeContract: changeContractSummary,
|
|
2063
2331
|
compiledPolicy: compiledPolicyMetadata,
|
|
2332
|
+
aiDebt: aiDebtSummaryForScope,
|
|
2064
2333
|
})
|
|
2065
2334
|
: undefined,
|
|
2066
2335
|
});
|
|
@@ -2550,6 +2819,19 @@ async function verifyCommand(options) {
|
|
|
2550
2819
|
console.log(chalk.yellow('⚠️ Verify API unavailable, using local deterministic fallback.'));
|
|
2551
2820
|
console.log(chalk.dim(` Reason: ${fallbackReason}`));
|
|
2552
2821
|
}
|
|
2822
|
+
const localFileContents = {};
|
|
2823
|
+
for (const file of changedFiles) {
|
|
2824
|
+
const absolutePath = (0, path_1.join)(projectRoot, file.path);
|
|
2825
|
+
if (!(0, fs_1.existsSync)(absolutePath)) {
|
|
2826
|
+
continue;
|
|
2827
|
+
}
|
|
2828
|
+
try {
|
|
2829
|
+
localFileContents[file.path] = (0, fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
2830
|
+
}
|
|
2831
|
+
catch {
|
|
2832
|
+
// Best effort only; fallback can still run on diff-only context.
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2553
2835
|
const localEvaluation = (0, governance_runtime_1.evaluatePlanVerification)({
|
|
2554
2836
|
planFiles: planFilesForVerification.map((path) => ({
|
|
2555
2837
|
path,
|
|
@@ -2560,6 +2842,7 @@ async function verifyCommand(options) {
|
|
|
2560
2842
|
intentConstraints: intentConstraintsForVerification,
|
|
2561
2843
|
policyRules: deterministicPolicyRules,
|
|
2562
2844
|
extraConstraintRules: hydratedCompiledPolicyRules.length > 0 ? hydratedCompiledPolicyRules : undefined,
|
|
2845
|
+
fileContents: localFileContents,
|
|
2563
2846
|
});
|
|
2564
2847
|
verifyResult = {
|
|
2565
2848
|
verificationId: `local-fallback-${Date.now()}`,
|
|
@@ -2573,12 +2856,19 @@ async function verifyCommand(options) {
|
|
|
2573
2856
|
message: localEvaluation.message,
|
|
2574
2857
|
};
|
|
2575
2858
|
}
|
|
2859
|
+
const aiDebtEvaluation = (0, ai_debt_budget_1.evaluateAiDebtBudget)({
|
|
2860
|
+
diffFiles,
|
|
2861
|
+
bloatCount: verifyResult.bloatCount,
|
|
2862
|
+
config: aiDebtConfig,
|
|
2863
|
+
});
|
|
2864
|
+
const aiDebtSummary = toAiDebtSummary(aiDebtEvaluation);
|
|
2865
|
+
const aiDebtHardBlock = aiDebtSummary.mode === 'enforce' && aiDebtSummary.violations.length > 0;
|
|
2576
2866
|
// Apply custom policy verdict: block from dashboard overrides API verdict
|
|
2577
2867
|
const policyBlock = policyDecision === 'block' && policyViolations.length > 0;
|
|
2578
2868
|
const governanceDecisionBlock = governanceResult?.governanceDecision?.decision === 'block';
|
|
2579
2869
|
const governanceIntegrityBlock = signedLogsRequired && governanceResult ? governanceResult.aiChangeLogIntegrity.valid !== true : false;
|
|
2580
2870
|
const governanceHardBlock = governanceDecisionBlock || governanceIntegrityBlock;
|
|
2581
|
-
const effectiveVerdict = policyBlock || governanceHardBlock
|
|
2871
|
+
const effectiveVerdict = policyBlock || governanceHardBlock || aiDebtHardBlock
|
|
2582
2872
|
? 'FAIL'
|
|
2583
2873
|
: verifyResult.verdict;
|
|
2584
2874
|
const policyMessageBase = policyBlock
|
|
@@ -2589,9 +2879,12 @@ async function verifyCommand(options) {
|
|
|
2589
2879
|
: governanceDecisionBlock
|
|
2590
2880
|
? governanceResult?.governanceDecision?.summary || 'Governance decision matrix returned BLOCK.'
|
|
2591
2881
|
: null;
|
|
2882
|
+
const aiDebtBlockReason = aiDebtHardBlock
|
|
2883
|
+
? `AI debt budget exceeded: ${aiDebtSummary.violations.map((item) => item.message).join(' ')}`
|
|
2884
|
+
: null;
|
|
2592
2885
|
const effectiveMessage = (policyExceptionsSummary.suppressed > 0
|
|
2593
2886
|
? `${policyMessageBase} Policy exceptions suppressed ${policyExceptionsSummary.suppressed} violation(s).`
|
|
2594
|
-
: policyMessageBase) + (governanceBlockReason ? ` ${governanceBlockReason}` : '');
|
|
2887
|
+
: policyMessageBase) + (governanceBlockReason ? ` ${governanceBlockReason}` : '') + (aiDebtBlockReason ? ` ${aiDebtBlockReason}` : '');
|
|
2595
2888
|
// Calculate grade from effective verdict and score
|
|
2596
2889
|
// CRITICAL: 0/0 planned files = 'F' (Incomplete), not 'B'
|
|
2597
2890
|
// Bloat automatically drops grade by at least one letter
|
|
@@ -2637,10 +2930,11 @@ async function verifyCommand(options) {
|
|
|
2637
2930
|
const changedPathsForBrain = diffFiles
|
|
2638
2931
|
.flatMap((file) => [file.path, file.oldPath])
|
|
2639
2932
|
.filter((value) => Boolean(value));
|
|
2640
|
-
recordVerifyEvent(effectiveVerdict, `adherence=${verifyResult.adherenceScore};bloat=${verifyResult.bloatCount};scopeGuard=${scopeGuardPassed ? 1 : 0};policy=${policyDecision};policyExceptions=${policyExceptionsSummary.suppressed}`, changedPathsForBrain, finalPlanId);
|
|
2933
|
+
recordVerifyEvent(effectiveVerdict, `adherence=${verifyResult.adherenceScore};bloat=${verifyResult.bloatCount};scopeGuard=${scopeGuardPassed ? 1 : 0};policy=${policyDecision};policyExceptions=${policyExceptionsSummary.suppressed};aiDebt=${aiDebtSummary.score}`, changedPathsForBrain, finalPlanId);
|
|
2641
2934
|
const shouldForceGovernancePass = scopeGuardPassed &&
|
|
2642
2935
|
!policyBlock &&
|
|
2643
2936
|
!governanceHardBlock &&
|
|
2937
|
+
!aiDebtHardBlock &&
|
|
2644
2938
|
(effectiveVerdict === 'PASS' ||
|
|
2645
2939
|
((verifyResult.verdict === 'FAIL' || verifyResult.verdict === 'WARN') &&
|
|
2646
2940
|
policyViolations.length === 0 &&
|
|
@@ -2661,7 +2955,8 @@ async function verifyCommand(options) {
|
|
|
2661
2955
|
message: v.message,
|
|
2662
2956
|
...(v.line != null ? { startLine: v.line } : {}),
|
|
2663
2957
|
}));
|
|
2664
|
-
const
|
|
2958
|
+
const aiDebtViolationItems = toAiDebtReportViolations(aiDebtSummary);
|
|
2959
|
+
const violations = [...scopeViolations, ...policyViolationItems, ...aiDebtViolationItems];
|
|
2665
2960
|
const jsonOutput = {
|
|
2666
2961
|
grade,
|
|
2667
2962
|
score: verifyResult.adherenceScore,
|
|
@@ -2677,12 +2972,14 @@ async function verifyCommand(options) {
|
|
|
2677
2972
|
verificationSource: verifySource,
|
|
2678
2973
|
mode: 'plan_enforced',
|
|
2679
2974
|
policyOnly: false,
|
|
2975
|
+
aiDebt: aiDebtSummary,
|
|
2680
2976
|
changeContract: changeContractSummary,
|
|
2681
2977
|
...(compiledPolicyMetadata ? { policyCompilation: compiledPolicyMetadata } : {}),
|
|
2682
2978
|
...(governanceResult
|
|
2683
2979
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2684
2980
|
changeContract: changeContractSummary,
|
|
2685
2981
|
compiledPolicy: compiledPolicyMetadata,
|
|
2982
|
+
aiDebt: aiDebtSummary,
|
|
2686
2983
|
})
|
|
2687
2984
|
: {}),
|
|
2688
2985
|
policyLock: {
|
|
@@ -2693,6 +2990,7 @@ async function verifyCommand(options) {
|
|
|
2693
2990
|
},
|
|
2694
2991
|
policyExceptions: policyExceptionsSummary,
|
|
2695
2992
|
policyGovernance: policyGovernanceSummary,
|
|
2993
|
+
...(runtimeGuardSummary.required ? { runtimeGuard: runtimeGuardSummary } : {}),
|
|
2696
2994
|
...(policyViolations.length > 0 && { policyDecision }),
|
|
2697
2995
|
...(effectiveRules.policyPack
|
|
2698
2996
|
? {
|
|
@@ -2724,6 +3022,7 @@ async function verifyCommand(options) {
|
|
|
2724
3022
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2725
3023
|
changeContract: changeContractSummary,
|
|
2726
3024
|
compiledPolicy: compiledPolicyMetadata,
|
|
3025
|
+
aiDebt: aiDebtSummary,
|
|
2727
3026
|
})
|
|
2728
3027
|
: undefined,
|
|
2729
3028
|
});
|
|
@@ -2754,6 +3053,23 @@ async function verifyCommand(options) {
|
|
|
2754
3053
|
if (governanceResult) {
|
|
2755
3054
|
displayGovernanceInsights(governanceResult, { explain: options.explain });
|
|
2756
3055
|
}
|
|
3056
|
+
if (aiDebtSummary.mode !== 'off') {
|
|
3057
|
+
const header = aiDebtSummary.mode === 'enforce'
|
|
3058
|
+
? aiDebtSummary.pass
|
|
3059
|
+
? chalk.green('\nAI Debt Budget: PASS')
|
|
3060
|
+
: chalk.red('\nAI Debt Budget: BLOCK')
|
|
3061
|
+
: chalk.yellow('\nAI Debt Budget: ADVISORY');
|
|
3062
|
+
console.log(header);
|
|
3063
|
+
console.log(chalk.dim(` Score: ${aiDebtSummary.score} | TODO/FIXME +${aiDebtSummary.metrics.addedTodoFixme} | ` +
|
|
3064
|
+
`console.log +${aiDebtSummary.metrics.addedConsoleLogs} | any +${aiDebtSummary.metrics.addedAnyTypes} | ` +
|
|
3065
|
+
`large files ${aiDebtSummary.metrics.largeFilesTouched} | bloat ${aiDebtSummary.metrics.bloatFiles}`));
|
|
3066
|
+
if (aiDebtSummary.violations.length > 0) {
|
|
3067
|
+
aiDebtSummary.violations.forEach((item) => {
|
|
3068
|
+
const color = aiDebtSummary.mode === 'enforce' ? chalk.red : chalk.yellow;
|
|
3069
|
+
console.log(color(` • ${item.message}`));
|
|
3070
|
+
});
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
2757
3073
|
console.log(chalk.dim(`\n Policy exceptions source: ${describePolicyExceptionSource(policyExceptionsSummary.sourceMode)}`));
|
|
2758
3074
|
if (policyExceptionsSummary.suppressed > 0) {
|
|
2759
3075
|
console.log(chalk.yellow(`\n⚠️ Policy exceptions applied: ${policyExceptionsSummary.suppressed}`));
|
|
@@ -2774,6 +3090,9 @@ async function verifyCommand(options) {
|
|
|
2774
3090
|
console.log(chalk.red(` • ${issue}`));
|
|
2775
3091
|
});
|
|
2776
3092
|
}
|
|
3093
|
+
if (runtimeGuardSummary.required) {
|
|
3094
|
+
console.log(chalk.dim(`\n Runtime guard: ${runtimeGuardSummary.pass ? 'pass' : 'block'} (${runtimeGuardSummary.path})`));
|
|
3095
|
+
}
|
|
2777
3096
|
}
|
|
2778
3097
|
// Report to Neurcode Cloud if --record flag is set
|
|
2779
3098
|
const filteredBloatForReport = (verifyResult.bloatFiles || []).filter((f) => !shouldIgnore(f));
|
|
@@ -2790,6 +3109,7 @@ async function verifyCommand(options) {
|
|
|
2790
3109
|
severity: v.severity,
|
|
2791
3110
|
message: v.message,
|
|
2792
3111
|
})),
|
|
3112
|
+
...toAiDebtReportViolations(aiDebtSummary),
|
|
2793
3113
|
];
|
|
2794
3114
|
await recordVerificationIfRequested(options, config, {
|
|
2795
3115
|
grade,
|
|
@@ -2809,6 +3129,7 @@ async function verifyCommand(options) {
|
|
|
2809
3129
|
? buildGovernancePayload(governanceResult, orgGovernanceSettings, {
|
|
2810
3130
|
changeContract: changeContractSummary,
|
|
2811
3131
|
compiledPolicy: compiledPolicyMetadata,
|
|
3132
|
+
aiDebt: aiDebtSummary,
|
|
2812
3133
|
})
|
|
2813
3134
|
: undefined,
|
|
2814
3135
|
});
|
|
@@ -3151,6 +3472,7 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
|
|
|
3151
3472
|
: null,
|
|
3152
3473
|
...(options?.compiledPolicy ? { policyCompilation: options.compiledPolicy } : {}),
|
|
3153
3474
|
...(options?.changeContract ? { changeContract: options.changeContract } : {}),
|
|
3475
|
+
...(options?.aiDebt ? { aiDebt: options.aiDebt } : {}),
|
|
3154
3476
|
};
|
|
3155
3477
|
}
|
|
3156
3478
|
function displayGovernanceInsights(governance, options = {}) {
|