@neurcode-ai/cli 0.10.1 → 0.12.0

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 (171) hide show
  1. package/.telemetry-bundle/dist/contracts.d.ts +1 -1
  2. package/README.md +74 -25
  3. package/dist/commands/remediate-export.js +1 -1
  4. package/dist/commands/replay.d.ts.map +1 -1
  5. package/dist/commands/replay.js +36 -0
  6. package/dist/commands/replay.js.map +1 -1
  7. package/dist/commands/verify-output.d.ts.map +1 -1
  8. package/dist/commands/verify-output.js +66 -4
  9. package/dist/commands/verify-output.js.map +1 -1
  10. package/dist/commands/verify.d.ts +22 -1
  11. package/dist/commands/verify.d.ts.map +1 -1
  12. package/dist/commands/verify.js +446 -34
  13. package/dist/commands/verify.js.map +1 -1
  14. package/dist/daemon/server.d.ts.map +1 -1
  15. package/dist/daemon/server.js +4 -0
  16. package/dist/daemon/server.js.map +1 -1
  17. package/dist/governance/intent/drift-detector.d.ts +100 -0
  18. package/dist/governance/intent/drift-detector.d.ts.map +1 -0
  19. package/dist/governance/intent/drift-detector.js +275 -0
  20. package/dist/governance/intent/drift-detector.js.map +1 -0
  21. package/dist/governance/intent/glob-match.d.ts +43 -0
  22. package/dist/governance/intent/glob-match.d.ts.map +1 -0
  23. package/dist/governance/intent/glob-match.js +108 -0
  24. package/dist/governance/intent/glob-match.js.map +1 -0
  25. package/dist/governance/intent/import-graph.d.ts +56 -0
  26. package/dist/governance/intent/import-graph.d.ts.map +1 -0
  27. package/dist/governance/intent/import-graph.js +133 -0
  28. package/dist/governance/intent/import-graph.js.map +1 -0
  29. package/dist/governance/intent/index.d.ts +23 -0
  30. package/dist/governance/intent/index.d.ts.map +1 -0
  31. package/dist/governance/intent/index.js +48 -0
  32. package/dist/governance/intent/index.js.map +1 -0
  33. package/dist/governance/intent/intelligence-boundaries.d.ts +69 -0
  34. package/dist/governance/intent/intelligence-boundaries.d.ts.map +1 -0
  35. package/dist/governance/intent/intelligence-boundaries.js +163 -0
  36. package/dist/governance/intent/intelligence-boundaries.js.map +1 -0
  37. package/dist/governance/intent/intent-contract.d.ts +76 -0
  38. package/dist/governance/intent/intent-contract.d.ts.map +1 -0
  39. package/dist/governance/intent/intent-contract.js +397 -0
  40. package/dist/governance/intent/intent-contract.js.map +1 -0
  41. package/dist/governance/intent/intent-graph.d.ts +135 -0
  42. package/dist/governance/intent/intent-graph.d.ts.map +1 -0
  43. package/dist/governance/intent/intent-graph.js +67 -0
  44. package/dist/governance/intent/intent-graph.js.map +1 -0
  45. package/dist/governance/pipeline/computation-trace.d.ts +52 -0
  46. package/dist/governance/pipeline/computation-trace.d.ts.map +1 -0
  47. package/dist/governance/pipeline/computation-trace.js +79 -0
  48. package/dist/governance/pipeline/computation-trace.js.map +1 -0
  49. package/dist/governance/pipeline/envelope-assembly.d.ts +132 -0
  50. package/dist/governance/pipeline/envelope-assembly.d.ts.map +1 -0
  51. package/dist/governance/pipeline/envelope-assembly.js +140 -0
  52. package/dist/governance/pipeline/envelope-assembly.js.map +1 -0
  53. package/dist/governance/pipeline/fingerprint.d.ts +34 -0
  54. package/dist/governance/pipeline/fingerprint.d.ts.map +1 -0
  55. package/dist/governance/pipeline/fingerprint.js +78 -0
  56. package/dist/governance/pipeline/fingerprint.js.map +1 -0
  57. package/dist/governance/pipeline/helpers.d.ts +74 -0
  58. package/dist/governance/pipeline/helpers.d.ts.map +1 -0
  59. package/dist/governance/pipeline/helpers.js +112 -0
  60. package/dist/governance/pipeline/helpers.js.map +1 -0
  61. package/dist/governance/pipeline/index.d.ts +27 -0
  62. package/dist/governance/pipeline/index.d.ts.map +1 -0
  63. package/dist/governance/pipeline/index.js +63 -0
  64. package/dist/governance/pipeline/index.js.map +1 -0
  65. package/dist/governance/pipeline/lineage.d.ts +26 -0
  66. package/dist/governance/pipeline/lineage.d.ts.map +1 -0
  67. package/dist/governance/pipeline/lineage.js +51 -0
  68. package/dist/governance/pipeline/lineage.js.map +1 -0
  69. package/dist/governance/pipeline/orchestration/advisory-mode-contract.d.ts +15 -0
  70. package/dist/governance/pipeline/orchestration/advisory-mode-contract.d.ts.map +1 -0
  71. package/dist/governance/pipeline/orchestration/advisory-mode-contract.js +44 -0
  72. package/dist/governance/pipeline/orchestration/advisory-mode-contract.js.map +1 -0
  73. package/dist/governance/pipeline/orchestration/advisory-mode.d.ts +102 -0
  74. package/dist/governance/pipeline/orchestration/advisory-mode.d.ts.map +1 -0
  75. package/dist/governance/pipeline/orchestration/advisory-mode.js +170 -0
  76. package/dist/governance/pipeline/orchestration/advisory-mode.js.map +1 -0
  77. package/dist/governance/pipeline/orchestration/evidence-lifecycle.d.ts +133 -0
  78. package/dist/governance/pipeline/orchestration/evidence-lifecycle.d.ts.map +1 -0
  79. package/dist/governance/pipeline/orchestration/evidence-lifecycle.js +125 -0
  80. package/dist/governance/pipeline/orchestration/evidence-lifecycle.js.map +1 -0
  81. package/dist/governance/pipeline/orchestration/index.d.ts +16 -0
  82. package/dist/governance/pipeline/orchestration/index.d.ts.map +1 -0
  83. package/dist/governance/pipeline/orchestration/index.js +30 -0
  84. package/dist/governance/pipeline/orchestration/index.js.map +1 -0
  85. package/dist/governance/pipeline/orchestration/intent-drift-orchestration.d.ts +65 -0
  86. package/dist/governance/pipeline/orchestration/intent-drift-orchestration.d.ts.map +1 -0
  87. package/dist/governance/pipeline/orchestration/intent-drift-orchestration.js +102 -0
  88. package/dist/governance/pipeline/orchestration/intent-drift-orchestration.js.map +1 -0
  89. package/dist/governance/pipeline/orchestration/plan-structural-analysis.d.ts +41 -0
  90. package/dist/governance/pipeline/orchestration/plan-structural-analysis.d.ts.map +1 -0
  91. package/dist/governance/pipeline/orchestration/plan-structural-analysis.js +74 -0
  92. package/dist/governance/pipeline/orchestration/plan-structural-analysis.js.map +1 -0
  93. package/dist/governance/pipeline/orchestration/policy-evaluation-summaries.d.ts +165 -0
  94. package/dist/governance/pipeline/orchestration/policy-evaluation-summaries.d.ts.map +1 -0
  95. package/dist/governance/pipeline/orchestration/policy-evaluation-summaries.js +160 -0
  96. package/dist/governance/pipeline/orchestration/policy-evaluation-summaries.js.map +1 -0
  97. package/dist/governance/pipeline/orchestration/scope-guard-orchestration.d.ts +152 -0
  98. package/dist/governance/pipeline/orchestration/scope-guard-orchestration.d.ts.map +1 -0
  99. package/dist/governance/pipeline/orchestration/scope-guard-orchestration.js +188 -0
  100. package/dist/governance/pipeline/orchestration/scope-guard-orchestration.js.map +1 -0
  101. package/dist/governance/pipeline/runtime.d.ts +70 -0
  102. package/dist/governance/pipeline/runtime.d.ts.map +1 -0
  103. package/dist/governance/pipeline/runtime.js +223 -0
  104. package/dist/governance/pipeline/runtime.js.map +1 -0
  105. package/dist/governance/pipeline/shared-types.d.ts +7 -0
  106. package/dist/governance/pipeline/shared-types.d.ts.map +1 -0
  107. package/dist/governance/pipeline/shared-types.js +7 -0
  108. package/dist/governance/pipeline/shared-types.js.map +1 -0
  109. package/dist/governance/pipeline/stages/compiled-policy-stage.d.ts +28 -0
  110. package/dist/governance/pipeline/stages/compiled-policy-stage.d.ts.map +1 -0
  111. package/dist/governance/pipeline/stages/compiled-policy-stage.js +53 -0
  112. package/dist/governance/pipeline/stages/compiled-policy-stage.js.map +1 -0
  113. package/dist/governance/pipeline/stages/diff-normalization-stage.d.ts +63 -0
  114. package/dist/governance/pipeline/stages/diff-normalization-stage.d.ts.map +1 -0
  115. package/dist/governance/pipeline/stages/diff-normalization-stage.js +140 -0
  116. package/dist/governance/pipeline/stages/diff-normalization-stage.js.map +1 -0
  117. package/dist/governance/pipeline/stages/governance-synthesis-stage.d.ts +53 -0
  118. package/dist/governance/pipeline/stages/governance-synthesis-stage.d.ts.map +1 -0
  119. package/dist/governance/pipeline/stages/governance-synthesis-stage.js +129 -0
  120. package/dist/governance/pipeline/stages/governance-synthesis-stage.js.map +1 -0
  121. package/dist/governance/pipeline/stages/index.d.ts +29 -0
  122. package/dist/governance/pipeline/stages/index.d.ts.map +1 -0
  123. package/dist/governance/pipeline/stages/index.js +40 -0
  124. package/dist/governance/pipeline/stages/index.js.map +1 -0
  125. package/dist/governance/pipeline/stages/policy-lock-stage.d.ts +31 -0
  126. package/dist/governance/pipeline/stages/policy-lock-stage.d.ts.map +1 -0
  127. package/dist/governance/pipeline/stages/policy-lock-stage.js +71 -0
  128. package/dist/governance/pipeline/stages/policy-lock-stage.js.map +1 -0
  129. package/dist/governance/pipeline/stages/runtime-guard-stage.d.ts +29 -0
  130. package/dist/governance/pipeline/stages/runtime-guard-stage.d.ts.map +1 -0
  131. package/dist/governance/pipeline/stages/runtime-guard-stage.js +65 -0
  132. package/dist/governance/pipeline/stages/runtime-guard-stage.js.map +1 -0
  133. package/dist/governance/pipeline/stages/structural-analysis-stage.d.ts +24 -0
  134. package/dist/governance/pipeline/stages/structural-analysis-stage.d.ts.map +1 -0
  135. package/dist/governance/pipeline/stages/structural-analysis-stage.js +58 -0
  136. package/dist/governance/pipeline/stages/structural-analysis-stage.js.map +1 -0
  137. package/dist/governance/pipeline/summary.d.ts +14 -0
  138. package/dist/governance/pipeline/summary.d.ts.map +1 -0
  139. package/dist/governance/pipeline/summary.js +50 -0
  140. package/dist/governance/pipeline/summary.js.map +1 -0
  141. package/dist/governance/pipeline/types.d.ts +69 -0
  142. package/dist/governance/pipeline/types.d.ts.map +1 -0
  143. package/dist/governance/pipeline/types.js +30 -0
  144. package/dist/governance/pipeline/types.js.map +1 -0
  145. package/dist/index.js +29 -0
  146. package/dist/index.js.map +1 -1
  147. package/dist/utils/active-engineering-context.d.ts +16 -0
  148. package/dist/utils/active-engineering-context.d.ts.map +1 -1
  149. package/dist/utils/active-engineering-context.js +302 -0
  150. package/dist/utils/active-engineering-context.js.map +1 -1
  151. package/dist/utils/import-edge-classifier.d.ts +76 -0
  152. package/dist/utils/import-edge-classifier.d.ts.map +1 -0
  153. package/dist/utils/import-edge-classifier.js +308 -0
  154. package/dist/utils/import-edge-classifier.js.map +1 -0
  155. package/dist/utils/import-edge-extractor.d.ts +52 -0
  156. package/dist/utils/import-edge-extractor.d.ts.map +1 -0
  157. package/dist/utils/import-edge-extractor.js +223 -0
  158. package/dist/utils/import-edge-extractor.js.map +1 -0
  159. package/dist/utils/import-edge-governance.d.ts +37 -0
  160. package/dist/utils/import-edge-governance.d.ts.map +1 -0
  161. package/dist/utils/import-edge-governance.js +56 -0
  162. package/dist/utils/import-edge-governance.js.map +1 -0
  163. package/dist/utils/path-boundary-classifier.d.ts +42 -0
  164. package/dist/utils/path-boundary-classifier.d.ts.map +1 -0
  165. package/dist/utils/path-boundary-classifier.js +143 -0
  166. package/dist/utils/path-boundary-classifier.js.map +1 -0
  167. package/dist/utils/replay-html-report.d.ts +29 -0
  168. package/dist/utils/replay-html-report.d.ts.map +1 -0
  169. package/dist/utils/replay-html-report.js +309 -0
  170. package/dist/utils/replay-html-report.js.map +1 -0
  171. package/package.json +2 -2
@@ -2,7 +2,20 @@
2
2
  /**
3
3
  * Verify Command
4
4
  *
5
- * Compares current work (git diff) against an Architect Plan to measure adherence and detect bloat.
5
+ * Runs deterministic operational governance against the current diff:
6
+ * - Intent contract enforcement (approved scope + forbidden boundaries)
7
+ * - Structural rules (PY/SR/DS catalogues)
8
+ * - Drift narrative synthesis + governance posture rollup
9
+ * - Generated-code spillover + boundary classification
10
+ * - Replay continuity (canonical replay checksum, byte-stable per inputs)
11
+ *
12
+ * Emits a single canonical envelope plus a `runtimeCapabilities` declaration so
13
+ * enterprise CI gates can assert what actually executed instead of inferring
14
+ * from absent fields. The command is the verification step in the canonical
15
+ * governance lifecycle; remediation is performed by an external AI assistant,
16
+ * never by this command.
17
+ *
18
+ * See `docs/governance-vocabulary.md` for canonical terminology.
6
19
  */
7
20
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
21
  if (k2 === undefined) k2 = k;
@@ -85,6 +98,9 @@ const runtime_guard_1 = require("../utils/runtime-guard");
85
98
  const artifact_signature_1 = require("../utils/artifact-signature");
86
99
  const policy_1 = require("@neurcode-ai/policy");
87
100
  const active_engineering_context_1 = require("../utils/active-engineering-context");
101
+ const core_1 = require("@neurcode-ai/core");
102
+ const path_boundary_classifier_1 = require("../utils/path-boundary-classifier");
103
+ const import_edge_governance_1 = require("../utils/import-edge-governance");
88
104
  const ai_debt_budget_1 = require("../utils/ai-debt-budget");
89
105
  const verification_evidence_1 = require("../utils/verification-evidence");
90
106
  const verify_runtime_stability_1 = require("../utils/verify-runtime-stability");
@@ -1715,6 +1731,20 @@ async function verifyCommand(options) {
1715
1731
  console.log(chalk.yellow(`\n⚠️ Failed to write verification evidence artifact: ${message}`));
1716
1732
  }
1717
1733
  }
1734
+ // Mirror the canonical envelope into `.neurcode/last-verify-output.json`
1735
+ // so downstream commands (`remediate-export`, `replay --html`) can pick
1736
+ // up the latest run without the user threading paths through stdout
1737
+ // redirection. Closes deep-OSS validation §5.8.
1738
+ try {
1739
+ if (lastCanonicalOutput) {
1740
+ const verifyOutputPath = require('path').resolve(projectRoot, '.neurcode/last-verify-output.json');
1741
+ require('fs').mkdirSync(require('path').dirname(verifyOutputPath), { recursive: true });
1742
+ require('fs').writeFileSync(verifyOutputPath, `${JSON.stringify(lastCanonicalOutput, null, 2)}\n`, 'utf-8');
1743
+ }
1744
+ }
1745
+ catch {
1746
+ // Persisting the canonical output is best-effort; never block exit.
1747
+ }
1718
1748
  };
1719
1749
  const exitWithEvidence = (exitCode) => {
1720
1750
  finalizeEvidence(exitCode);
@@ -2323,6 +2353,11 @@ async function verifyCommand(options) {
2323
2353
  console.log(chalk.dim(' Tip: Ensure changes are staged or run against a base branch.'));
2324
2354
  }
2325
2355
  else {
2356
+ // Surface runtime capabilities even on the empty-diff path so
2357
+ // CI gates that assert `runtimeCapabilities.intentRuntime` etc.
2358
+ // never receive a payload that omits the envelope. The intent
2359
+ // runtime is reported as `inactive` here regardless of whether
2360
+ // an intent-pack exists — there are no findings to govern.
2326
2361
  emitVerifyJson({
2327
2362
  grade: 'F',
2328
2363
  score: 0,
@@ -2335,6 +2370,24 @@ async function verifyCommand(options) {
2335
2370
  totalPlannedFiles: 0,
2336
2371
  message: 'No changes detected in current diff context.',
2337
2372
  scopeGuardPassed: false,
2373
+ runtimeCapabilities: {
2374
+ schemaVersion: 'neurcode.runtime-capabilities.v1',
2375
+ executionPath: localOnlyMode ? 'local-only' : 'unresolved',
2376
+ intentRuntime: 'inactive',
2377
+ intentContractSource: 'none',
2378
+ intentRuntimeRequired: options.requireIntentRuntime === true || isEnabledFlag(process.env.NEURCODE_REQUIRE_INTENT_RUNTIME),
2379
+ intentRuntimeRequirementSatisfied: true,
2380
+ driftIntelligence: 'inactive',
2381
+ scopeGuard: 'unenforced',
2382
+ forbiddenBoundaryEnforcement: 'unenforced',
2383
+ generatedCodeGovernance: 'pattern-deterministic',
2384
+ structuralRules: 'inactive',
2385
+ replayDeterminism: 'enforced',
2386
+ apiContractStatus: localOnlyMode ? 'offline' : 'unresolved',
2387
+ observedScopeCategories: [],
2388
+ observedBoundaryTypes: [],
2389
+ noChangesDetected: true,
2390
+ },
2338
2391
  });
2339
2392
  }
2340
2393
  recordVerifyEvent('NO_CHANGES', 'diff=empty');
@@ -2365,54 +2418,381 @@ async function verifyCommand(options) {
2365
2418
  // Deterministic structural governance MUST work offline, with zero API dependency.
2366
2419
  if (localOnlyMode) {
2367
2420
  if (!options.json) {
2368
- console.log(chalk.cyan('\n🔍 Local-only mode: deterministic structural verification (no API required)...'));
2421
+ console.log(chalk.cyan('\n🔍 Local-only mode: deterministic intent-runtime verification (no API required)...'));
2369
2422
  }
2370
2423
  const localStructural = (0, structural_on_diff_1.runStructuralOnDiffFiles)(projectRoot, diffFiles);
2371
2424
  const localStructuralFindings = localStructural.violations.map(canonical_pipeline_1.findingFromStructural);
2372
- const localReplayChecksum = (0, canonical_invariants_1.computeCanonicalFindingChecksum)(localStructuralFindings);
2373
2425
  const blockingViolations = localStructural.violations.filter((v) => v.severity === 'BLOCKING');
2374
2426
  const advisoryViolations = localStructural.violations.filter((v) => v.severity !== 'BLOCKING');
2375
- const localVerdict = blockingViolations.length > 0 ? 'FAIL' : 'PASS';
2376
- const localGrade = blockingViolations.length > 0 ? 'F' : 'B';
2377
- const localScore = blockingViolations.length > 0 ? 0 : 70;
2427
+ // ─── Intent-runtime activation (deterministic, offline) ──────────────────
2428
+ // When an intent-pack is present in `.neurcode/intent-pack.json` the local
2429
+ // verify path now activates the full governance runtime: scope checks,
2430
+ // forbidden-boundary enforcement, drift narratives and posture synthesis.
2431
+ // Companion artefacts (context-pack, repository-graph, session-runtime)
2432
+ // are deterministically synthesised when they are absent so the runtime
2433
+ // does NOT silently collapse into structural-only mode.
2434
+ let localActiveContext = null;
2435
+ let localGovernanceResult = null;
2436
+ const localScopeIssues = [];
2437
+ let localImportEdgeResult = null;
2438
+ try {
2439
+ localActiveContext = (0, active_engineering_context_1.loadOrSynthesizeEngineeringContext)(projectRoot);
2440
+ }
2441
+ catch (err) {
2442
+ if (!options.json && (process.env.DEBUG || process.env.VERBOSE)) {
2443
+ console.log(chalk.dim(` [intent-runtime] context load skipped: ${err.message}`));
2444
+ }
2445
+ }
2446
+ if (localActiveContext) {
2447
+ try {
2448
+ localGovernanceResult = (0, governance_1.evaluateGovernance)({
2449
+ projectRoot,
2450
+ task: localActiveContext.intentPack.intent.normalized,
2451
+ expectedFiles: localActiveContext.intentPack.approvedScope.files,
2452
+ expectedDependencies: localActiveContext.intentPack.expectedDependencies,
2453
+ diffFiles,
2454
+ contextCandidates: localActiveContext.contextPack.selectedFiles.map((f) => f.path),
2455
+ activeEngineeringContext: localActiveContext,
2456
+ });
2457
+ // Deterministic scope-guard intersection against intent-pack approvedScope
2458
+ // PLUS explicit forbiddenBoundaries. Drift intelligence already flags
2459
+ // narrative-level drift; this surfaces direct path violations as
2460
+ // first-class scope issues regardless of drift heuristics.
2461
+ const intent = localActiveContext.intentPack;
2462
+ const approvedFileSet = new Set(intent.approvedScope.files.map(core_1.normalizeRepoPath));
2463
+ const approvedModulePaths = intent.approvedScope.modules.map(core_1.normalizeRepoPath);
2464
+ const approvedServicePaths = intent.approvedScope.services.map(core_1.normalizeRepoPath);
2465
+ const matchesPrefix = (file, prefixes) => prefixes.some((p) => p && (file === p || file.startsWith(`${p}/`)));
2466
+ const changedNormalized = diffFiles
2467
+ .map((f) => (0, core_1.normalizeRepoPath)(f.path))
2468
+ .filter((p) => Boolean(p));
2469
+ const isAllowedBoundaryType = (value) => value === 'sensitive' || value === 'infra' || value === 'ci' ||
2470
+ value === 'dependency-manifest' || value === 'service' || value === 'module' ||
2471
+ value === 'generated-code' || value === 'unspecified';
2472
+ // First pass: explicit forbidden boundaries always surface (even if
2473
+ // they also happen to be inside an approved module).
2474
+ for (const boundary of intent.forbiddenBoundaries) {
2475
+ const boundaryPath = (0, core_1.normalizeRepoPath)(boundary.path);
2476
+ if (!boundaryPath)
2477
+ continue;
2478
+ for (const file of changedNormalized) {
2479
+ if (file === boundaryPath || file.startsWith(`${boundaryPath}/`)) {
2480
+ if (boundary.policy === 'allowed')
2481
+ continue;
2482
+ const alreadyFlagged = localScopeIssues.some((s) => s.file === file && s.boundaryType === boundary.type);
2483
+ if (alreadyFlagged)
2484
+ continue;
2485
+ const boundaryType = isAllowedBoundaryType(boundary.type) ? boundary.type : 'unspecified';
2486
+ localScopeIssues.push({
2487
+ file,
2488
+ message: `Forbidden boundary touched (${boundary.type}, policy=${boundary.policy}): ${boundary.reason}`,
2489
+ policy: boundary.policy === 'forbidden' ? 'forbidden' : 'review-required',
2490
+ boundaryType,
2491
+ });
2492
+ }
2493
+ }
2494
+ }
2495
+ // Second pass: out-of-scope files (not in approved file/module/service set).
2496
+ // Only run if any approvedScope dimension is non-empty — empty scope means
2497
+ // intent-pack is in observation mode and we should not synthesise FPs.
2498
+ // When a file falls outside scope, we additionally classify it against
2499
+ // well-known path patterns (generated-code, infra, CI, dependency-manifest)
2500
+ // so reviewers see WHY the boundary matters, not just THAT it was breached.
2501
+ const hasApprovedScope = approvedFileSet.size > 0 || approvedModulePaths.length > 0 || approvedServicePaths.length > 0;
2502
+ if (hasApprovedScope) {
2503
+ for (const file of changedNormalized) {
2504
+ if (approvedFileSet.has(file))
2505
+ continue;
2506
+ if (matchesPrefix(file, approvedModulePaths))
2507
+ continue;
2508
+ if (matchesPrefix(file, approvedServicePaths))
2509
+ continue;
2510
+ if (localScopeIssues.some((s) => s.file === file))
2511
+ continue;
2512
+ const classification = (0, path_boundary_classifier_1.classifyPathBoundary)(file);
2513
+ if (classification) {
2514
+ // generated-code is a stronger signal than plain out-of-scope:
2515
+ // generated files should not be hand-edited regardless of
2516
+ // approval status.
2517
+ if (classification.category === 'generated-code') {
2518
+ localScopeIssues.push({
2519
+ file,
2520
+ message: `Generated-code edit outside approved scope (${classification.reason}). Regenerate from source or update the intent-pack to declare this surface.`,
2521
+ policy: 'generated-code',
2522
+ boundaryType: 'generated-code',
2523
+ });
2524
+ continue;
2525
+ }
2526
+ const boundaryType = isAllowedBoundaryType(classification.category) ? classification.category : 'unspecified';
2527
+ localScopeIssues.push({
2528
+ file,
2529
+ message: `File modified outside the declared intent scope (${classification.category}: ${classification.reason}).`,
2530
+ policy: 'out-of-scope',
2531
+ boundaryType,
2532
+ });
2533
+ continue;
2534
+ }
2535
+ localScopeIssues.push({
2536
+ file,
2537
+ message: 'File modified outside the declared intent scope.',
2538
+ policy: 'out-of-scope',
2539
+ });
2540
+ }
2541
+ }
2542
+ else {
2543
+ // Observation mode (no approved scope): only surface generated-code
2544
+ // edits, because those are usually wrong regardless of intent.
2545
+ for (const file of changedNormalized) {
2546
+ if (localScopeIssues.some((s) => s.file === file))
2547
+ continue;
2548
+ const classification = (0, path_boundary_classifier_1.classifyPathBoundary)(file);
2549
+ if (classification?.category === 'generated-code') {
2550
+ localScopeIssues.push({
2551
+ file,
2552
+ message: `Generated-code edit detected (${classification.reason}). Regenerate from source.`,
2553
+ policy: 'generated-code',
2554
+ boundaryType: 'generated-code',
2555
+ });
2556
+ }
2557
+ }
2558
+ }
2559
+ // ─── Import-edge governance ──────────────────────────────────────
2560
+ // Deterministic import-edge classifier: even when every touched
2561
+ // path is in-scope, an `import` statement that crosses a
2562
+ // forbidden boundary surfaces as a scope issue. Closes the
2563
+ // semantic blind spot documented in
2564
+ // docs/validation/2026-05-17-deep-oss/.
2565
+ try {
2566
+ localImportEdgeResult = (0, import_edge_governance_1.evaluateImportEdgeGovernance)({
2567
+ diffFiles,
2568
+ projectRoot,
2569
+ intent: {
2570
+ approvedScope: {
2571
+ files: intent.approvedScope.files,
2572
+ modules: intent.approvedScope.modules,
2573
+ services: intent.approvedScope.services,
2574
+ },
2575
+ forbiddenBoundaries: intent.forbiddenBoundaries.map((b) => ({
2576
+ type: b.type,
2577
+ path: b.path,
2578
+ policy: b.policy,
2579
+ reason: b.reason,
2580
+ })),
2581
+ },
2582
+ });
2583
+ for (const finding of localImportEdgeResult.findings) {
2584
+ // Avoid double-flagging when the same source file already has
2585
+ // a path-touch issue for the same boundary; import-edge is
2586
+ // additive metadata in that case.
2587
+ const dupe = localScopeIssues.some((s) => s.file === finding.sourceFile
2588
+ && s.importEdge
2589
+ && s.importEdge.importTarget === finding.importTarget
2590
+ && s.importEdge.resolvedBoundary === finding.resolvedBoundary);
2591
+ if (dupe)
2592
+ continue;
2593
+ localScopeIssues.push({
2594
+ file: finding.sourceFile,
2595
+ message: `Import-edge crosses ${finding.boundaryType} boundary (policy=${finding.policy}): \`${finding.importTarget}\` resolves to \`${finding.resolvedTargetPath}\` inside \`${finding.resolvedBoundary}\`. ${finding.reason}`,
2596
+ policy: finding.policy,
2597
+ boundaryType: finding.boundaryType,
2598
+ importEdge: {
2599
+ sourceFile: finding.sourceFile,
2600
+ sourceLine: finding.sourceLine,
2601
+ importTarget: finding.importTarget,
2602
+ resolvedTargetPath: finding.resolvedTargetPath,
2603
+ resolvedBoundary: finding.resolvedBoundary,
2604
+ edgeKind: finding.edgeKind,
2605
+ language: finding.language,
2606
+ deterministic: true,
2607
+ replayStable: true,
2608
+ },
2609
+ });
2610
+ }
2611
+ }
2612
+ catch (importErr) {
2613
+ if (!options.json && (process.env.DEBUG || process.env.VERBOSE)) {
2614
+ console.log(chalk.dim(` [intent-runtime] import-edge governance skipped: ${importErr.message}`));
2615
+ }
2616
+ }
2617
+ }
2618
+ catch (err) {
2619
+ if (!options.json && (process.env.DEBUG || process.env.VERBOSE)) {
2620
+ console.log(chalk.dim(` [intent-runtime] governance evaluation skipped: ${err.message}`));
2621
+ }
2622
+ }
2623
+ }
2624
+ const blockingScopeCount = localScopeIssues.filter((s) => s.policy === 'forbidden' || s.policy === 'out-of-scope' || s.policy === 'generated-code').length;
2625
+ const advisoryScopeCount = localScopeIssues.filter((s) => s.policy === 'review-required').length;
2626
+ const intentRuntimeActive = Boolean(localGovernanceResult);
2627
+ const localScopeGuardPassed = blockingScopeCount === 0;
2628
+ // `--require-intent-runtime` (or NEURCODE_REQUIRE_INTENT_RUNTIME=1) makes
2629
+ // silent downgrade into a hard failure. Without this flag the runtime
2630
+ // gracefully degrades to structural-only when no intent contract exists;
2631
+ // with it, enterprise CI gates can assert intent governance was applied.
2632
+ const requireIntentRuntimeFlag = options.requireIntentRuntime === true || isEnabledFlag(process.env.NEURCODE_REQUIRE_INTENT_RUNTIME);
2633
+ const intentRuntimeRequirementFailed = requireIntentRuntimeFlag && !intentRuntimeActive;
2634
+ const localVerdict = blockingViolations.length > 0 || blockingScopeCount > 0 || intentRuntimeRequirementFailed ? 'FAIL' : 'PASS';
2635
+ const localGrade = localVerdict === 'FAIL' ? 'F' : 'B';
2636
+ const localScore = localVerdict === 'FAIL' ? 0 : 70;
2637
+ const scopeViolationRows = localScopeIssues
2638
+ .filter((s) => s.policy === 'forbidden' || s.policy === 'out-of-scope' || s.policy === 'generated-code')
2639
+ .map((s) => ({
2640
+ file: s.file,
2641
+ rule: 'scope_guard',
2642
+ severity: 'block',
2643
+ message: s.message,
2644
+ }));
2645
+ const scopeWarningRows = localScopeIssues
2646
+ .filter((s) => s.policy === 'review-required')
2647
+ .map((s) => ({
2648
+ file: s.file,
2649
+ rule: 'scope_guard',
2650
+ severity: 'warn',
2651
+ message: s.message,
2652
+ }));
2653
+ // Surface the intent-runtime requirement failure as a first-class
2654
+ // governance row so CI logs, dashboards, and PR comments all see the
2655
+ // same explanation. The row sits next to scope_guard rows so it shares
2656
+ // their treatment (block, blocking-count, exit code).
2657
+ const intentRuntimeRequirementRows = intentRuntimeRequirementFailed
2658
+ ? [{
2659
+ file: '.neurcode/intent-pack.json',
2660
+ rule: 'intent_runtime_required',
2661
+ severity: 'block',
2662
+ message: 'Intent-governed runtime required (--require-intent-runtime / NEURCODE_REQUIRE_INTENT_RUNTIME=1) but no `.neurcode/intent-pack.json` was found or it could not be synthesised. Either author an intent pack (`neurcode start`) or drop the requirement to allow structural-only verification.',
2663
+ }]
2664
+ : [];
2378
2665
  const localPayload = {
2379
2666
  grade: localGrade,
2380
2667
  score: localScore,
2381
2668
  verdict: localVerdict,
2382
- violations: localStructural.violations.map((v) => ({
2383
- file: v.filePath,
2384
- rule: v.ruleId,
2385
- severity: v.severity === 'BLOCKING' ? 'block' : 'warn',
2386
- message: `${v.ruleName}: ${v.evidence.slice(0, 120)}`,
2387
- })),
2669
+ violations: [
2670
+ ...localStructural.violations.map((v) => ({
2671
+ file: v.filePath,
2672
+ rule: v.ruleId,
2673
+ severity: v.severity === 'BLOCKING' ? 'block' : 'warn',
2674
+ message: `${v.ruleName}: ${v.evidence.slice(0, 120)}`,
2675
+ })),
2676
+ ...scopeViolationRows,
2677
+ ...scopeWarningRows,
2678
+ ...intentRuntimeRequirementRows,
2679
+ ],
2388
2680
  adherenceScore: localScore,
2389
- bloatCount: 0,
2390
- bloatFiles: [],
2681
+ bloatCount: blockingScopeCount,
2682
+ bloatFiles: scopeViolationRows.map((r) => r.file),
2391
2683
  plannedFilesModified: 0,
2392
2684
  totalPlannedFiles: 0,
2393
- message: `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`,
2394
- scopeGuardPassed: true,
2395
- mode: 'local_only_structural',
2685
+ message: intentRuntimeRequirementFailed
2686
+ ? 'Intent-runtime requirement failed: no `.neurcode/intent-pack.json` available to activate scope + forbidden-boundary enforcement.'
2687
+ : (intentRuntimeActive
2688
+ ? `Local intent-runtime verification: ${localStructural.violations.length} structural finding(s), ${blockingViolations.length} blocking; ${blockingScopeCount} scope violation(s), ${advisoryScopeCount} advisory boundary issue(s).`
2689
+ : `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`),
2690
+ scopeGuardPassed: localScopeGuardPassed,
2691
+ scopeIssues: localScopeIssues.map((s) => ({
2692
+ file: s.file,
2693
+ message: s.message,
2694
+ policy: s.policy,
2695
+ boundaryType: s.boundaryType,
2696
+ ...(s.importEdge ? { importEdge: s.importEdge } : {}),
2697
+ })),
2698
+ mode: intentRuntimeActive ? 'local_intent_runtime' : 'local_only_structural',
2396
2699
  policyOnly: true,
2397
- replayChecksum: localReplayChecksum,
2398
- replayMode: 'local-structural',
2700
+ // NOTE: this is the structural-only checksum. When the canonical
2701
+ // envelope is attached below, `governanceVerification.replayChecksum`
2702
+ // becomes the authoritative hash over the merged finding set
2703
+ // (structural + drift + scope). The top-level `replayChecksum` is
2704
+ // rewritten post-attach so enterprise CI sees a single canonical
2705
+ // hash for the activated runtime.
2706
+ replayChecksum: (0, canonical_invariants_1.computeCanonicalFindingChecksum)(localStructuralFindings),
2707
+ replayMode: intentRuntimeActive ? 'local-intent-runtime' : 'local-structural',
2399
2708
  structuralViolations: localStructural.violations,
2400
2709
  structuralBlockingCount: blockingViolations.length,
2401
2710
  structuralRulesApplied: localStructural.rulesApplied,
2402
2711
  changeContract: changeContractSummary,
2712
+ // driftIntelligence drives intentGovernance summary + drift findings
2713
+ // inside attachCanonicalGovernance.
2714
+ driftIntelligence: localGovernanceResult?.driftIntelligence ?? null,
2715
+ engineeringContext: localGovernanceResult?.engineeringContext ?? null,
2716
+ intentRuntime: localActiveContext
2717
+ ? {
2718
+ active: true,
2719
+ synthesized: Boolean(localActiveContext.synthesized),
2720
+ intentPackId: localActiveContext.intentPack.intentPackId,
2721
+ contextPackId: localActiveContext.contextPack.contextPackId,
2722
+ repositoryGraphId: localActiveContext.repositoryGraph.graphId,
2723
+ warnings: localActiveContext.warnings,
2724
+ }
2725
+ : { active: false, synthesized: false, intentPackId: null, contextPackId: null, repositoryGraphId: null, warnings: [] },
2726
+ // Machine-readable capability declaration for enterprise CI. Every
2727
+ // field is "what actually executed", not "what we wished happened".
2728
+ // CI gates that need a specific guarantee (e.g. intent-runtime active,
2729
+ // generated-code governance active) can assert against this envelope
2730
+ // rather than inferring from absence of fields. See
2731
+ // docs/validation/2026-05-16-activated/activation-report-2026-05-16.md
2732
+ // §6 for the rationale.
2733
+ runtimeCapabilities: {
2734
+ schemaVersion: 'neurcode.runtime-capabilities.v1',
2735
+ executionPath: 'local-only',
2736
+ intentRuntime: intentRuntimeActive
2737
+ ? (localActiveContext?.synthesized ? 'active-synthesized' : 'active-authored')
2738
+ : 'inactive',
2739
+ intentContractSource: localActiveContext
2740
+ ? (localActiveContext.synthesized ? 'intent-pack-only' : 'full-bundle')
2741
+ : 'none',
2742
+ // Reflect whether the caller required intent runtime to be active.
2743
+ // Mirrors how `requireRuntimeGuard` is surfaced; lets dashboards
2744
+ // distinguish "intent runtime inactive by choice" from
2745
+ // "intent runtime inactive against caller policy".
2746
+ intentRuntimeRequired: requireIntentRuntimeFlag,
2747
+ intentRuntimeRequirementSatisfied: !intentRuntimeRequirementFailed,
2748
+ driftIntelligence: localGovernanceResult?.driftIntelligence ? 'active' : 'inactive',
2749
+ scopeGuard: intentRuntimeActive ? 'enforced' : 'unenforced',
2750
+ forbiddenBoundaryEnforcement: intentRuntimeActive ? 'enforced' : 'unenforced',
2751
+ generatedCodeGovernance: 'pattern-deterministic',
2752
+ // Import-edge governance is enforced whenever the intent runtime
2753
+ // is active. Pattern-deterministic (regex over diff additions +
2754
+ // path classifier), no AST, no probability.
2755
+ importEdgeGovernance: intentRuntimeActive ? 'pattern-deterministic' : 'inactive',
2756
+ structuralRules: 'active',
2757
+ replayDeterminism: 'enforced',
2758
+ apiContractStatus: 'offline',
2759
+ // Counts of categories observed in THIS run (not capacity).
2760
+ observedScopeCategories: Array.from(new Set(localScopeIssues.map((s) => s.policy))).sort(),
2761
+ observedBoundaryTypes: Array.from(new Set(localScopeIssues
2762
+ .map((s) => s.boundaryType)
2763
+ .filter((b) => Boolean(b)))).sort(),
2764
+ // Import-edge observability — counts only, plus a sorted list of
2765
+ // observed boundary types from the edge pass. Helpful for CI gates
2766
+ // that want to assert "this run had ≥0 edges analysed".
2767
+ importEdgesAnalyzed: localImportEdgeResult?.edgeCount ?? 0,
2768
+ importEdgeBlockingFindings: localImportEdgeResult?.blockingFindingCount ?? 0,
2769
+ importEdgeAdvisoryFindings: localImportEdgeResult?.advisoryFindingCount ?? 0,
2770
+ observedImportEdgeBoundaryTypes: localImportEdgeResult?.observedBoundaryTypes ?? [],
2771
+ },
2403
2772
  };
2404
- captureEvidencePayload((0, canonical_pipeline_1.attachCanonicalGovernance)(localPayload));
2773
+ // Run the canonical pipeline once so we can extract the merged-finding
2774
+ // replayChecksum (structural + drift + scope) and back-write it to the
2775
+ // top-level payload + custody envelope. This keeps a single authoritative
2776
+ // hash visible at every replay surface when the intent runtime is active.
2777
+ const enrichedLocalPayload = (0, canonical_pipeline_1.attachCanonicalGovernance)(localPayload);
2778
+ const envelopeReplayChecksum = (() => {
2779
+ const env = enrichedLocalPayload.governanceVerification;
2780
+ return env && typeof env.replayChecksum === 'string' ? env.replayChecksum : localPayload.replayChecksum;
2781
+ })();
2782
+ enrichedLocalPayload.replayChecksum = envelopeReplayChecksum;
2783
+ localPayload.replayChecksum = envelopeReplayChecksum;
2784
+ captureEvidencePayload(enrichedLocalPayload);
2405
2785
  const localReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
2406
2786
  projectRoot,
2407
2787
  diffContext: `${options.base || 'HEAD'} vs working tree`,
2408
2788
  filesAnalyzed: diffFiles.length,
2409
2789
  planId: null,
2410
- verificationSource: 'local_only_structural',
2790
+ verificationSource: intentRuntimeActive ? 'local_intent_runtime' : 'local_only_structural',
2411
2791
  policyLockFingerprint: null,
2412
2792
  compiledPolicyFingerprint: null,
2413
2793
  ruleIds: localStructural.rulesApplied,
2414
- blockingCount: blockingViolations.length,
2415
- advisoryCount: advisoryViolations.length,
2794
+ blockingCount: blockingViolations.length + blockingScopeCount,
2795
+ advisoryCount: advisoryViolations.length + advisoryScopeCount,
2416
2796
  suppressedCount: localStructural.suppressedCount,
2417
2797
  structuralBlockingCount: blockingViolations.length,
2418
2798
  structuralAdvisoryCount: advisoryViolations.length,
@@ -2422,10 +2802,12 @@ async function verifyCommand(options) {
2422
2802
  ? Math.round((localStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length / localStructural.violations.length) * 100)
2423
2803
  : 100,
2424
2804
  verdict: localVerdict,
2425
- governanceDecision: 'local deterministic structural verification',
2805
+ governanceDecision: intentRuntimeActive
2806
+ ? `local intent-runtime verification${localActiveContext?.synthesized ? ' (synthesised context)' : ''}`
2807
+ : 'local deterministic structural verification',
2426
2808
  actor: custodyActor,
2427
2809
  source: custodySource,
2428
- replayChecksum: localReplayChecksum,
2810
+ replayChecksum: envelopeReplayChecksum,
2429
2811
  });
2430
2812
  applyCapturedReplayCustody(lastCanonicalOutput, localReplayCustody);
2431
2813
  if (options.json) {
@@ -2435,8 +2817,10 @@ async function verifyCommand(options) {
2435
2817
  });
2436
2818
  }
2437
2819
  else {
2438
- if (localStructural.violations.length === 0) {
2439
- console.log(chalk.green('\n✅ No structural violations found (local-only mode).'));
2820
+ if (localStructural.violations.length === 0 && localScopeIssues.length === 0) {
2821
+ console.log(chalk.green(intentRuntimeActive
2822
+ ? '\n✅ No structural or scope violations found (intent-runtime active, local-only).'
2823
+ : '\n✅ No structural violations found (local-only mode).'));
2440
2824
  }
2441
2825
  else {
2442
2826
  localStructural.violations.forEach((v) => {
@@ -2445,14 +2829,42 @@ async function verifyCommand(options) {
2445
2829
  console.log(chalk.dim(` ${v.ruleName}`));
2446
2830
  console.log(chalk.dim(` ${v.evidence.slice(0, 100)}`));
2447
2831
  });
2832
+ for (const issue of localScopeIssues) {
2833
+ const prefix = issue.policy === 'forbidden' || issue.policy === 'out-of-scope'
2834
+ ? chalk.red(' ⛔ SCOPE ')
2835
+ : chalk.yellow(' ⚠ SCOPE ');
2836
+ console.log(`${prefix} ${issue.file}`);
2837
+ console.log(chalk.dim(` ${issue.message}`));
2838
+ }
2448
2839
  }
2449
- console.log(chalk.dim(`\n[Local-only] ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking. ` +
2450
- `Structural analysis ran on this checkout only (no verify API).\n`));
2451
- console.log(chalk.dim(' Replay: same commit + same flags → same structural findings.\n' +
2452
- ' Full plan/adherence + remote verify: drop --local-only when policy allows network/API.\n'));
2453
- }
2454
- recordVerifyEvent(localVerdict, `local_only;structural=${localStructural.violations.length}`, diffFiles.map((f) => f.path));
2455
- exitWithEvidence(blockingViolations.length > 0 ? 2 : 0);
2840
+ const modeLabel = intentRuntimeActive
2841
+ ? (localActiveContext?.synthesized ? 'Local intent-runtime (synthesised context)' : 'Local intent-runtime')
2842
+ : 'Local-only structural';
2843
+ console.log(chalk.dim(`\n[${modeLabel}] ${localStructural.violations.length} structural finding(s), ` +
2844
+ `${blockingViolations.length} blocking; ${localScopeIssues.length} scope issue(s), ${blockingScopeCount} blocking.\n`));
2845
+ // Governance posture banner: surface gate + rollout trust prominently
2846
+ // when the intent runtime is active so the most-glanced terminal line
2847
+ // reflects the canonical governance lifecycle, not just verify counts.
2848
+ if (intentRuntimeActive && localGovernanceResult?.driftIntelligence) {
2849
+ const drift = localGovernanceResult.driftIntelligence;
2850
+ const gate = drift.riskSynthesis?.governanceGate || drift.governancePosture?.governanceGate;
2851
+ const rolloutTrust = drift.riskSynthesis?.rolloutTrust || drift.governancePosture?.rolloutTrust;
2852
+ if (gate || rolloutTrust) {
2853
+ const gateLabel = gate ? `gate=${chalk.bold(gate)}` : 'gate=advisory';
2854
+ const trustLabel = rolloutTrust ? `rollout-trust=${chalk.bold(rolloutTrust)}` : 'rollout-trust=rollout-safe';
2855
+ console.log(chalk.dim(` Governance posture: ${gateLabel} · ${trustLabel}\n`));
2856
+ }
2857
+ }
2858
+ console.log(chalk.dim(` Replay: same commit + same flags + same intent-pack → same canonical checksum.\n` +
2859
+ ` Replay checksum: ${envelopeReplayChecksum.slice(0, 16)}…\n` +
2860
+ (intentRuntimeActive
2861
+ ? ' Intent governance: ACTIVE (offline). Add full intent-runtime artefacts for richer semantic narratives.\n'
2862
+ : ' Intent governance: INACTIVE. Add `.neurcode/intent-pack.json` to activate scope + forbidden-boundary enforcement locally.\n')));
2863
+ }
2864
+ recordVerifyEvent(localVerdict, intentRuntimeActive
2865
+ ? `local_intent_runtime;structural=${localStructural.violations.length};scope=${localScopeIssues.length}`
2866
+ : `local_only;structural=${localStructural.violations.length}`, diffFiles.map((f) => f.path));
2867
+ exitWithEvidence(localVerdict === 'FAIL' ? 2 : 0);
2456
2868
  }
2457
2869
  const summary = (0, diff_parser_1.getDiffSummary)(diffFiles);
2458
2870
  if (diffFiles.length === 0) {