@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.
Files changed (44) hide show
  1. package/dist/api-client.d.ts +54 -0
  2. package/dist/api-client.d.ts.map +1 -1
  3. package/dist/api-client.js +36 -0
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/commands/bootstrap.d.ts +20 -0
  6. package/dist/commands/bootstrap.d.ts.map +1 -0
  7. package/dist/commands/bootstrap.js +378 -0
  8. package/dist/commands/bootstrap.js.map +1 -0
  9. package/dist/commands/contract.d.ts.map +1 -1
  10. package/dist/commands/contract.js +125 -2
  11. package/dist/commands/contract.js.map +1 -1
  12. package/dist/commands/feedback.d.ts.map +1 -1
  13. package/dist/commands/feedback.js +145 -0
  14. package/dist/commands/feedback.js.map +1 -1
  15. package/dist/commands/guard.d.ts +3 -0
  16. package/dist/commands/guard.d.ts.map +1 -0
  17. package/dist/commands/guard.js +390 -0
  18. package/dist/commands/guard.js.map +1 -0
  19. package/dist/commands/policy.d.ts.map +1 -1
  20. package/dist/commands/policy.js +106 -1
  21. package/dist/commands/policy.js.map +1 -1
  22. package/dist/commands/remediate.d.ts +4 -0
  23. package/dist/commands/remediate.d.ts.map +1 -1
  24. package/dist/commands/remediate.js +420 -65
  25. package/dist/commands/remediate.js.map +1 -1
  26. package/dist/commands/verify.d.ts +4 -0
  27. package/dist/commands/verify.d.ts.map +1 -1
  28. package/dist/commands/verify.js +329 -7
  29. package/dist/commands/verify.js.map +1 -1
  30. package/dist/index.js +59 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/utils/ai-debt-budget.d.ts +49 -0
  33. package/dist/utils/ai-debt-budget.d.ts.map +1 -0
  34. package/dist/utils/ai-debt-budget.js +241 -0
  35. package/dist/utils/ai-debt-budget.js.map +1 -0
  36. package/dist/utils/policy-compiler.d.ts +4 -0
  37. package/dist/utils/policy-compiler.d.ts.map +1 -1
  38. package/dist/utils/policy-compiler.js +43 -0
  39. package/dist/utils/policy-compiler.js.map +1 -1
  40. package/dist/utils/runtime-guard.d.ts +93 -0
  41. package/dist/utils/runtime-guard.d.ts.map +1 -0
  42. package/dist/utils/runtime-guard.js +327 -0
  43. package/dist/utils/runtime-guard.js.map +1 -0
  44. package/package.json +2 -2
@@ -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: scopeViolationItems,
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: scopeViolationItems,
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: scopeViolationItems,
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 violations = [...scopeViolations, ...policyViolationItems];
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 = {}) {