@neurcode-ai/cli 0.9.65 → 0.10.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.
- package/dist/commands/bootstrap-policy.d.ts +29 -0
- package/dist/commands/bootstrap-policy.d.ts.map +1 -0
- package/dist/commands/bootstrap-policy.js +334 -0
- package/dist/commands/bootstrap-policy.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +82 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/governance.d.ts +3 -0
- package/dist/commands/governance.d.ts.map +1 -0
- package/dist/commands/governance.js +390 -0
- package/dist/commands/governance.js.map +1 -0
- package/dist/commands/quickstart.d.ts +21 -0
- package/dist/commands/quickstart.d.ts.map +1 -0
- package/dist/commands/quickstart.js +178 -0
- package/dist/commands/quickstart.js.map +1 -0
- package/dist/commands/remediate-export.d.ts +36 -0
- package/dist/commands/remediate-export.d.ts.map +1 -0
- package/dist/commands/remediate-export.js +1072 -0
- package/dist/commands/remediate-export.js.map +1 -0
- package/dist/commands/replay.d.ts.map +1 -1
- package/dist/commands/replay.js +14 -0
- package/dist/commands/replay.js.map +1 -1
- package/dist/commands/session.d.ts +7 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +156 -0
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/start-intent.d.ts.map +1 -1
- package/dist/commands/start-intent.js +61 -11
- package/dist/commands/start-intent.js.map +1 -1
- package/dist/commands/verify-guidance.d.ts +5 -0
- package/dist/commands/verify-guidance.d.ts.map +1 -0
- package/dist/commands/verify-guidance.js +49 -0
- package/dist/commands/verify-guidance.js.map +1 -0
- package/dist/commands/verify-output.d.ts +37 -0
- package/dist/commands/verify-output.d.ts.map +1 -0
- package/dist/commands/verify-output.js +572 -0
- package/dist/commands/verify-output.js.map +1 -0
- package/dist/commands/verify-render.d.ts +41 -0
- package/dist/commands/verify-render.d.ts.map +1 -0
- package/dist/commands/verify-render.js +457 -0
- package/dist/commands/verify-render.js.map +1 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +384 -1091
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/workspace.d.ts.map +1 -1
- package/dist/commands/workspace.js +3 -14
- package/dist/commands/workspace.js.map +1 -1
- package/dist/context-engine/graph.d.ts.map +1 -1
- package/dist/context-engine/graph.js +69 -7
- package/dist/context-engine/graph.js.map +1 -1
- package/dist/context-engine/scanner.d.ts.map +1 -1
- package/dist/context-engine/scanner.js +9 -2
- package/dist/context-engine/scanner.js.map +1 -1
- package/dist/daemon/compatibility/execution.d.ts +42 -0
- package/dist/daemon/compatibility/execution.d.ts.map +1 -0
- package/dist/daemon/compatibility/execution.js +183 -0
- package/dist/daemon/compatibility/execution.js.map +1 -0
- package/dist/daemon/compatibility/mutation.d.ts +24 -0
- package/dist/daemon/compatibility/mutation.d.ts.map +1 -0
- package/dist/daemon/compatibility/mutation.js +724 -0
- package/dist/daemon/compatibility/mutation.js.map +1 -0
- package/dist/daemon/routes.d.ts +19 -0
- package/dist/daemon/routes.d.ts.map +1 -0
- package/dist/daemon/routes.js +123 -0
- package/dist/daemon/routes.js.map +1 -0
- package/dist/daemon/runtime/execution-bus.d.ts +217 -0
- package/dist/daemon/runtime/execution-bus.d.ts.map +1 -0
- package/dist/daemon/runtime/execution-bus.js +1420 -0
- package/dist/daemon/runtime/execution-bus.js.map +1 -0
- package/dist/daemon/runtime/workspace-runtime.d.ts +280 -0
- package/dist/daemon/runtime/workspace-runtime.d.ts.map +1 -0
- package/dist/daemon/runtime/workspace-runtime.js +1473 -0
- package/dist/daemon/runtime/workspace-runtime.js.map +1 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +171 -874
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shaping.d.ts +11 -0
- package/dist/daemon/shaping.d.ts.map +1 -0
- package/dist/daemon/shaping.js +240 -0
- package/dist/daemon/shaping.js.map +1 -0
- package/dist/governance/canonical-invariants.d.ts +88 -0
- package/dist/governance/canonical-invariants.d.ts.map +1 -0
- package/dist/governance/canonical-invariants.js +197 -0
- package/dist/governance/canonical-invariants.js.map +1 -0
- package/dist/governance/canonical-ordering.d.ts +76 -0
- package/dist/governance/canonical-ordering.d.ts.map +1 -0
- package/dist/governance/canonical-ordering.js +189 -0
- package/dist/governance/canonical-ordering.js.map +1 -0
- package/dist/governance/canonical-pipeline.d.ts +9 -1
- package/dist/governance/canonical-pipeline.d.ts.map +1 -1
- package/dist/governance/canonical-pipeline.js +367 -24
- package/dist/governance/canonical-pipeline.js.map +1 -1
- package/dist/governance/diff-line-provenance.d.ts +59 -0
- package/dist/governance/diff-line-provenance.d.ts.map +1 -0
- package/dist/governance/diff-line-provenance.js +118 -0
- package/dist/governance/diff-line-provenance.js.map +1 -0
- package/dist/governance/pilot-readiness.d.ts +34 -0
- package/dist/governance/pilot-readiness.d.ts.map +1 -0
- package/dist/governance/pilot-readiness.js +226 -0
- package/dist/governance/pilot-readiness.js.map +1 -0
- package/dist/governance/policy-parity-validator.d.ts +62 -0
- package/dist/governance/policy-parity-validator.d.ts.map +1 -0
- package/dist/governance/policy-parity-validator.js +137 -0
- package/dist/governance/policy-parity-validator.js.map +1 -0
- package/dist/governance/remediation-boundary.d.ts +55 -0
- package/dist/governance/remediation-boundary.d.ts.map +1 -0
- package/dist/governance/remediation-boundary.js +120 -0
- package/dist/governance/remediation-boundary.js.map +1 -0
- package/dist/governance/structural-cache.d.ts +103 -0
- package/dist/governance/structural-cache.d.ts.map +1 -0
- package/dist/governance/structural-cache.js +235 -0
- package/dist/governance/structural-cache.js.map +1 -0
- package/dist/governance/structural-on-diff.d.ts +22 -2
- package/dist/governance/structural-on-diff.d.ts.map +1 -1
- package/dist/governance/structural-on-diff.js +36 -4
- package/dist/governance/structural-on-diff.js.map +1 -1
- package/dist/governance/structural-policy-merge.d.ts +8 -0
- package/dist/governance/structural-policy-merge.d.ts.map +1 -1
- package/dist/governance/structural-policy-merge.js +7 -0
- package/dist/governance/structural-policy-merge.js.map +1 -1
- package/dist/governance/verify-runtime-guard.d.ts +99 -0
- package/dist/governance/verify-runtime-guard.d.ts.map +1 -0
- package/dist/governance/verify-runtime-guard.js +129 -0
- package/dist/governance/verify-runtime-guard.js.map +1 -0
- package/dist/index.js +277 -77
- package/dist/index.js.map +1 -1
- package/dist/intent-engine/repo-classifier.d.ts +64 -0
- package/dist/intent-engine/repo-classifier.d.ts.map +1 -0
- package/dist/intent-engine/repo-classifier.js +178 -0
- package/dist/intent-engine/repo-classifier.js.map +1 -0
- package/dist/structural-rules/index.d.ts +4 -0
- package/dist/structural-rules/index.d.ts.map +1 -1
- package/dist/structural-rules/index.js +18 -1
- package/dist/structural-rules/index.js.map +1 -1
- package/dist/structural-rules/python/PY003-broad-except-clause.d.ts +21 -0
- package/dist/structural-rules/python/PY003-broad-except-clause.d.ts.map +1 -1
- package/dist/structural-rules/python/PY003-broad-except-clause.js +212 -21
- package/dist/structural-rules/python/PY003-broad-except-clause.js.map +1 -1
- package/dist/structural-rules/python/PY011-thread-lifecycle.d.ts +11 -0
- package/dist/structural-rules/python/PY011-thread-lifecycle.d.ts.map +1 -0
- package/dist/structural-rules/python/PY011-thread-lifecycle.js +97 -0
- package/dist/structural-rules/python/PY011-thread-lifecycle.js.map +1 -0
- package/dist/structural-rules/python/PY012-asyncio-run-misuse.d.ts +11 -0
- package/dist/structural-rules/python/PY012-asyncio-run-misuse.d.ts.map +1 -0
- package/dist/structural-rules/python/PY012-asyncio-run-misuse.js +83 -0
- package/dist/structural-rules/python/PY012-asyncio-run-misuse.js.map +1 -0
- package/dist/structural-rules/python/PY013-mutable-default-arg.d.ts +11 -0
- package/dist/structural-rules/python/PY013-mutable-default-arg.d.ts.map +1 -0
- package/dist/structural-rules/python/PY013-mutable-default-arg.js +73 -0
- package/dist/structural-rules/python/PY013-mutable-default-arg.js.map +1 -0
- package/dist/structural-rules/python/PY014-fixed-sleep-retry.d.ts +11 -0
- package/dist/structural-rules/python/PY014-fixed-sleep-retry.d.ts.map +1 -0
- package/dist/structural-rules/python/PY014-fixed-sleep-retry.js +115 -0
- package/dist/structural-rules/python/PY014-fixed-sleep-retry.js.map +1 -0
- package/dist/structural-rules/types.d.ts +12 -0
- package/dist/structural-rules/types.d.ts.map +1 -1
- package/dist/utils/active-engineering-context.d.ts +12 -0
- package/dist/utils/active-engineering-context.d.ts.map +1 -0
- package/dist/utils/active-engineering-context.js +67 -0
- package/dist/utils/active-engineering-context.js.map +1 -0
- package/dist/utils/artifact-io.d.ts +33 -0
- package/dist/utils/artifact-io.d.ts.map +1 -0
- package/dist/utils/artifact-io.js +183 -0
- package/dist/utils/artifact-io.js.map +1 -0
- package/dist/utils/change-contract.d.ts +6 -2
- package/dist/utils/change-contract.d.ts.map +1 -1
- package/dist/utils/change-contract.js +175 -0
- package/dist/utils/change-contract.js.map +1 -1
- package/dist/utils/context-pack.d.ts +12 -0
- package/dist/utils/context-pack.d.ts.map +1 -0
- package/dist/utils/context-pack.js +147 -0
- package/dist/utils/context-pack.js.map +1 -0
- package/dist/utils/control-plane.d.ts +18 -0
- package/dist/utils/control-plane.d.ts.map +1 -1
- package/dist/utils/control-plane.js +31 -4
- package/dist/utils/control-plane.js.map +1 -1
- package/dist/utils/drift-intelligence.d.ts +47 -0
- package/dist/utils/drift-intelligence.d.ts.map +1 -0
- package/dist/utils/drift-intelligence.js +2099 -0
- package/dist/utils/drift-intelligence.js.map +1 -0
- package/dist/utils/execution-actions.d.ts +22 -0
- package/dist/utils/execution-actions.d.ts.map +1 -0
- package/dist/utils/execution-actions.js +103 -0
- package/dist/utils/execution-actions.js.map +1 -0
- package/dist/utils/execution-bus.d.ts +1 -214
- package/dist/utils/execution-bus.d.ts.map +1 -1
- package/dist/utils/execution-bus.js +15 -1359
- package/dist/utils/execution-bus.js.map +1 -1
- package/dist/utils/git.d.ts +1 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +13 -3
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/governance-decisions.d.ts +75 -0
- package/dist/utils/governance-decisions.d.ts.map +1 -0
- package/dist/utils/governance-decisions.js +412 -0
- package/dist/utils/governance-decisions.js.map +1 -0
- package/dist/utils/governance-provenance.d.ts +1 -1
- package/dist/utils/governance-provenance.d.ts.map +1 -1
- package/dist/utils/governance-provenance.js +5 -7
- package/dist/utils/governance-provenance.js.map +1 -1
- package/dist/utils/governance.d.ts +108 -0
- package/dist/utils/governance.d.ts.map +1 -1
- package/dist/utils/governance.js +209 -7
- package/dist/utils/governance.js.map +1 -1
- package/dist/utils/intelligence-runtime-common.d.ts +30 -0
- package/dist/utils/intelligence-runtime-common.d.ts.map +1 -0
- package/dist/utils/intelligence-runtime-common.js +156 -0
- package/dist/utils/intelligence-runtime-common.js.map +1 -0
- package/dist/utils/intent-contract-diagnostics.d.ts +9 -0
- package/dist/utils/intent-contract-diagnostics.d.ts.map +1 -0
- package/dist/utils/intent-contract-diagnostics.js +322 -0
- package/dist/utils/intent-contract-diagnostics.js.map +1 -0
- package/dist/utils/intent-pack.d.ts +15 -0
- package/dist/utils/intent-pack.d.ts.map +1 -0
- package/dist/utils/intent-pack.js +196 -0
- package/dist/utils/intent-pack.js.map +1 -0
- package/dist/utils/plan-sync.d.ts +1 -0
- package/dist/utils/plan-sync.d.ts.map +1 -1
- package/dist/utils/plan-sync.js +23 -0
- package/dist/utils/plan-sync.js.map +1 -1
- package/dist/utils/policy-decision.d.ts +5 -0
- package/dist/utils/policy-decision.d.ts.map +1 -0
- package/dist/utils/policy-decision.js +17 -0
- package/dist/utils/policy-decision.js.map +1 -0
- package/dist/utils/replay-custody.d.ts +43 -0
- package/dist/utils/replay-custody.d.ts.map +1 -0
- package/dist/utils/replay-custody.js +168 -0
- package/dist/utils/replay-custody.js.map +1 -0
- package/dist/utils/replay-runtime.d.ts +13 -0
- package/dist/utils/replay-runtime.d.ts.map +1 -1
- package/dist/utils/replay-runtime.js +96 -9
- package/dist/utils/replay-runtime.js.map +1 -1
- package/dist/utils/repository-intelligence.d.ts +9 -0
- package/dist/utils/repository-intelligence.d.ts.map +1 -0
- package/dist/utils/repository-intelligence.js +372 -0
- package/dist/utils/repository-intelligence.js.map +1 -0
- package/dist/utils/runtime-events.d.ts.map +1 -1
- package/dist/utils/runtime-events.js +25 -6
- package/dist/utils/runtime-events.js.map +1 -1
- package/dist/utils/semantic-contract-intelligence.d.ts +20 -0
- package/dist/utils/semantic-contract-intelligence.d.ts.map +1 -0
- package/dist/utils/semantic-contract-intelligence.js +825 -0
- package/dist/utils/semantic-contract-intelligence.js.map +1 -0
- package/dist/utils/session-continuity.d.ts +56 -0
- package/dist/utils/session-continuity.d.ts.map +1 -0
- package/dist/utils/session-continuity.js +318 -0
- package/dist/utils/session-continuity.js.map +1 -0
- package/dist/utils/verification-evidence.d.ts.map +1 -1
- package/dist/utils/verification-evidence.js +4 -1
- package/dist/utils/verification-evidence.js.map +1 -1
- package/dist/utils/verify-runtime-stability.d.ts +142 -0
- package/dist/utils/verify-runtime-stability.d.ts.map +1 -0
- package/dist/utils/verify-runtime-stability.js +230 -0
- package/dist/utils/verify-runtime-stability.js.map +1 -0
- package/dist/utils/workspace-runtime.d.ts +1 -266
- package/dist/utils/workspace-runtime.d.ts.map +1 -1
- package/dist/utils/workspace-runtime.js +15 -1412
- package/dist/utils/workspace-runtime.js.map +1 -1
- package/package.json +11 -10
- package/LICENSE +0 -201
package/dist/commands/verify.js
CHANGED
|
@@ -67,19 +67,28 @@ const policy_compiler_1 = require("../utils/policy-compiler");
|
|
|
67
67
|
const change_contract_1 = require("../utils/change-contract");
|
|
68
68
|
const diff_symbols_1 = require("../utils/diff-symbols");
|
|
69
69
|
const advisory_signals_1 = require("../utils/advisory-signals");
|
|
70
|
+
const verify_guidance_1 = require("./verify-guidance");
|
|
71
|
+
const verify_output_1 = require("./verify-output");
|
|
72
|
+
const verify_render_1 = require("./verify-render");
|
|
70
73
|
const structural_rules_1 = require("../structural-rules");
|
|
71
74
|
const canonical_pipeline_1 = require("../governance/canonical-pipeline");
|
|
75
|
+
const canonical_invariants_1 = require("../governance/canonical-invariants");
|
|
72
76
|
const structural_on_diff_1 = require("../governance/structural-on-diff");
|
|
73
|
-
|
|
77
|
+
// NOTE: mergeStructuralIntoPolicyViolations is intentionally NOT imported.
|
|
78
|
+
// Structural violations flow exclusively through payload.structuralViolations
|
|
79
|
+
// into the canonical pipeline. Merging them into policyViolations caused
|
|
80
|
+
// duplicate GovernanceFinding objects (fixed in Phase 1 canonical graph hardening).
|
|
74
81
|
const telemetry_1 = require("@neurcode-ai/telemetry");
|
|
75
|
-
const governance_provenance_1 = require("../utils/governance-provenance");
|
|
76
82
|
const pilot_metrics_1 = require("../utils/pilot-metrics");
|
|
77
|
-
const
|
|
83
|
+
const replay_custody_1 = require("../utils/replay-custody");
|
|
78
84
|
const runtime_guard_1 = require("../utils/runtime-guard");
|
|
79
85
|
const artifact_signature_1 = require("../utils/artifact-signature");
|
|
80
86
|
const policy_1 = require("@neurcode-ai/policy");
|
|
87
|
+
const active_engineering_context_1 = require("../utils/active-engineering-context");
|
|
81
88
|
const ai_debt_budget_1 = require("../utils/ai-debt-budget");
|
|
82
89
|
const verification_evidence_1 = require("../utils/verification-evidence");
|
|
90
|
+
const verify_runtime_stability_1 = require("../utils/verify-runtime-stability");
|
|
91
|
+
const policy_decision_1 = require("../utils/policy-decision");
|
|
83
92
|
// Import chalk with fallback
|
|
84
93
|
let chalk;
|
|
85
94
|
try {
|
|
@@ -100,6 +109,74 @@ catch {
|
|
|
100
109
|
white: (str) => str,
|
|
101
110
|
};
|
|
102
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Structured CI explainability for `neurcode verify --ci` / `--policy-only` human output.
|
|
114
|
+
* Keeps logs short — no JSON dumps — and separates merge-blocking vs advisory signals.
|
|
115
|
+
*/
|
|
116
|
+
function logCiPolicyOnlyOutcomeExplainability(params) {
|
|
117
|
+
if (!params.ciModeEnabled || params.json) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const modeLine = params.source === 'ci'
|
|
121
|
+
? '`verify --ci` uses deterministic local governance (compiled/custom policy + structural rules). Remote plan-verify API is not used.'
|
|
122
|
+
: '`--policy-only` — local policy + structural governance without plan adherence.';
|
|
123
|
+
const sev = (s) => String(s || '').toLowerCase();
|
|
124
|
+
const sBlock = params.structuralViolations.filter((v) => v.severity === 'BLOCKING').length;
|
|
125
|
+
const sAdv = params.structuralViolations.length - sBlock;
|
|
126
|
+
const pBlock = params.policyViolations.filter((v) => sev(v.severity) === 'block').length;
|
|
127
|
+
const pWarn = params.policyViolations.filter((v) => sev(v.severity) === 'warn').length;
|
|
128
|
+
if (params.verdict === 'PASS') {
|
|
129
|
+
console.log(chalk.dim('\n── CI verify contract ──'));
|
|
130
|
+
console.log(chalk.dim(` ${modeLine}`));
|
|
131
|
+
console.log(chalk.dim(' Exit 0: no blocking severities. Replay checksum (JSON) anchors structural findings for audit parity.'));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.log(chalk.bold.red('\n── CI failure explainability ──'));
|
|
135
|
+
console.log(chalk.dim(` ${modeLine}`));
|
|
136
|
+
console.log(chalk.red(` Merge-blocking rows: structural BLOCKING ${sBlock}; policy/custom severity=block ${pBlock}`));
|
|
137
|
+
console.log(chalk.yellow(` Non-blocking (warn/advisory-class): structural advisory ${sAdv}; policy warn ${pWarn} — does not fail CI unless your gate maps warns to failure`));
|
|
138
|
+
console.log(chalk.dim(' Offline / structural-only: set NEURCODE_VERIFY_LOCAL_ONLY=1 or `--local-only` to skip API compatibility probes (AST gates still run).'));
|
|
139
|
+
if (params.replayChecksum) {
|
|
140
|
+
console.log(chalk.dim(` Structural replay checksum: ${params.replayChecksum.slice(0, 16)}… · mode ${params.replayMode ?? 'local-structural'}`));
|
|
141
|
+
}
|
|
142
|
+
console.log(chalk.dim(' Next: resolve BLOCKING first → `neurcode remediate-export` (optional) → re-run `neurcode verify --ci`.\n'));
|
|
143
|
+
}
|
|
144
|
+
function driftSeverityToPolicySeverity(severity) {
|
|
145
|
+
return severity === 'critical' || severity === 'high' ? 'block' : 'warn';
|
|
146
|
+
}
|
|
147
|
+
function driftGateToPolicySeverity(gate, fallbackSeverity) {
|
|
148
|
+
if (gate === 'policy-blocker' || gate === 'rollout-blocker' || gate === 'architecture-blocker') {
|
|
149
|
+
return 'block';
|
|
150
|
+
}
|
|
151
|
+
if (gate === 'review-blocker' || gate === 'advisory') {
|
|
152
|
+
return 'warn';
|
|
153
|
+
}
|
|
154
|
+
return driftSeverityToPolicySeverity(fallbackSeverity);
|
|
155
|
+
}
|
|
156
|
+
function driftFindingsToVerificationViolations(drift) {
|
|
157
|
+
if (!drift || !Array.isArray(drift.findings)) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(drift.narratives) && drift.narratives.length > 0) {
|
|
161
|
+
return drift.narratives.map((narrative) => ({
|
|
162
|
+
file: narrative.affectedFiles[0]
|
|
163
|
+
|| narrative.affectedModules[0]
|
|
164
|
+
|| narrative.affectedServices[0]
|
|
165
|
+
|| '.neurcode/intent-pack.json',
|
|
166
|
+
rule: `drift_narrative:${narrative.category}`,
|
|
167
|
+
severity: driftGateToPolicySeverity(drift.riskSynthesis?.governanceGate, narrative.severity),
|
|
168
|
+
message: narrative.summary,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
return drift.findings.map((finding) => ({
|
|
172
|
+
file: finding.file || finding.module || finding.service || '.neurcode/intent-pack.json',
|
|
173
|
+
rule: `drift_intelligence:${finding.category}`,
|
|
174
|
+
severity: driftGateToPolicySeverity(finding.governanceGate, finding.severity),
|
|
175
|
+
message: finding.priority
|
|
176
|
+
? `${finding.message} (${finding.priority}; ${finding.governanceGate || 'review-blocker'})`
|
|
177
|
+
: finding.message,
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
103
180
|
;
|
|
104
181
|
function toArtifactSignatureSummary(status) {
|
|
105
182
|
return {
|
|
@@ -660,569 +737,6 @@ function runtimeGuardViolationsToReport(summary) {
|
|
|
660
737
|
message: item.message,
|
|
661
738
|
}));
|
|
662
739
|
}
|
|
663
|
-
function asObjectRecord(value) {
|
|
664
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
665
|
-
return null;
|
|
666
|
-
}
|
|
667
|
-
return value;
|
|
668
|
-
}
|
|
669
|
-
function asObjectArray(value) {
|
|
670
|
-
if (!Array.isArray(value)) {
|
|
671
|
-
return [];
|
|
672
|
-
}
|
|
673
|
-
return value
|
|
674
|
-
.map((item) => asObjectRecord(item))
|
|
675
|
-
.filter((item) => item !== null);
|
|
676
|
-
}
|
|
677
|
-
function asBooleanFlag(value) {
|
|
678
|
-
return typeof value === 'boolean' ? value : null;
|
|
679
|
-
}
|
|
680
|
-
function asNumberValue(value) {
|
|
681
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
682
|
-
}
|
|
683
|
-
function asStringValue(value) {
|
|
684
|
-
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
685
|
-
}
|
|
686
|
-
const EXPEDITE_FOLLOW_UP_CHECKLIST = [
|
|
687
|
-
'Add validation back',
|
|
688
|
-
'Move logic to proper layer',
|
|
689
|
-
'Remove temporary code',
|
|
690
|
-
];
|
|
691
|
-
function containsAnyToken(value, tokens) {
|
|
692
|
-
const normalized = value.toLowerCase();
|
|
693
|
-
return tokens.some((token) => normalized.includes(token));
|
|
694
|
-
}
|
|
695
|
-
function isSecurityOrAuthViolation(fileRaw, policyRaw, messageRaw) {
|
|
696
|
-
const combined = `${fileRaw} ${policyRaw} ${messageRaw}`.toLowerCase();
|
|
697
|
-
return containsAnyToken(combined, [
|
|
698
|
-
'auth',
|
|
699
|
-
'authentication',
|
|
700
|
-
'authorization',
|
|
701
|
-
'security',
|
|
702
|
-
'permission',
|
|
703
|
-
'access control',
|
|
704
|
-
'access_control',
|
|
705
|
-
'token',
|
|
706
|
-
'secret',
|
|
707
|
-
'credential',
|
|
708
|
-
'encryption',
|
|
709
|
-
'encrypt',
|
|
710
|
-
'decrypt',
|
|
711
|
-
'csrf',
|
|
712
|
-
'xss',
|
|
713
|
-
'sql injection',
|
|
714
|
-
'sqli',
|
|
715
|
-
'insecure',
|
|
716
|
-
'vulnerability',
|
|
717
|
-
]);
|
|
718
|
-
}
|
|
719
|
-
function isCriticalScopeBreach(fileRaw, messageRaw) {
|
|
720
|
-
const combined = `${fileRaw} ${messageRaw}`.toLowerCase();
|
|
721
|
-
return containsAnyToken(combined, [
|
|
722
|
-
'auth',
|
|
723
|
-
'security',
|
|
724
|
-
'secret',
|
|
725
|
-
'token',
|
|
726
|
-
'credential',
|
|
727
|
-
'permission',
|
|
728
|
-
'infra/terraform',
|
|
729
|
-
'terraform',
|
|
730
|
-
'k8s',
|
|
731
|
-
'helm',
|
|
732
|
-
'migration',
|
|
733
|
-
'database/migration',
|
|
734
|
-
'policy',
|
|
735
|
-
'contract',
|
|
736
|
-
]);
|
|
737
|
-
}
|
|
738
|
-
function resolveExpediteModeFromPayload(payload) {
|
|
739
|
-
const explicit = asBooleanFlag(payload.expediteMode);
|
|
740
|
-
if (explicit !== null) {
|
|
741
|
-
return explicit;
|
|
742
|
-
}
|
|
743
|
-
const message = asStringValue(payload.message) || '';
|
|
744
|
-
return containsAnyToken(message, ['hotfix', 'urgent', 'prod down', 'incident', 'expedite']);
|
|
745
|
-
}
|
|
746
|
-
function toVerifySeverity(value) {
|
|
747
|
-
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
748
|
-
if (normalized === 'critical' || normalized === 'block')
|
|
749
|
-
return 'critical';
|
|
750
|
-
if (normalized === 'high')
|
|
751
|
-
return 'high';
|
|
752
|
-
if (normalized === 'warn'
|
|
753
|
-
|| normalized === 'warning'
|
|
754
|
-
|| normalized === 'medium'
|
|
755
|
-
|| normalized === 'low') {
|
|
756
|
-
return 'warning';
|
|
757
|
-
}
|
|
758
|
-
return 'info';
|
|
759
|
-
}
|
|
760
|
-
function toVerifyVerdict(value) {
|
|
761
|
-
const normalized = typeof value === 'string' ? value.trim().toUpperCase() : '';
|
|
762
|
-
if (normalized === 'PASS' || normalized === 'WARN' || normalized === 'FAIL') {
|
|
763
|
-
return normalized;
|
|
764
|
-
}
|
|
765
|
-
return 'FAIL';
|
|
766
|
-
}
|
|
767
|
-
function normalizeScopeIssueMessage(rawMessage) {
|
|
768
|
-
const message = asStringValue(rawMessage);
|
|
769
|
-
return message || 'File modified outside intended scope';
|
|
770
|
-
}
|
|
771
|
-
function pushVerifyIssue(target, seen, key, value) {
|
|
772
|
-
if (seen.has(key))
|
|
773
|
-
return;
|
|
774
|
-
seen.add(key);
|
|
775
|
-
target.push(value);
|
|
776
|
-
}
|
|
777
|
-
function dedupeTriageItems(items) {
|
|
778
|
-
const seen = new Set();
|
|
779
|
-
const output = [];
|
|
780
|
-
for (const item of items) {
|
|
781
|
-
const key = `${item.source}|${item.file.toLowerCase()}|${item.policy.toLowerCase()}|${item.message.toLowerCase()}`;
|
|
782
|
-
if (seen.has(key))
|
|
783
|
-
continue;
|
|
784
|
-
seen.add(key);
|
|
785
|
-
output.push(item);
|
|
786
|
-
}
|
|
787
|
-
return output;
|
|
788
|
-
}
|
|
789
|
-
function toCanonicalVerifyOutput(payload) {
|
|
790
|
-
const verdict = toVerifyVerdict(payload.verdict);
|
|
791
|
-
const violations = [];
|
|
792
|
-
const warnings = [];
|
|
793
|
-
const scopeIssues = [];
|
|
794
|
-
const seenViolations = new Set();
|
|
795
|
-
const seenWarnings = new Set();
|
|
796
|
-
const seenScopeIssues = new Set();
|
|
797
|
-
const addScopeIssue = (fileRaw, messageRaw) => {
|
|
798
|
-
const file = asStringValue(fileRaw) || 'unknown';
|
|
799
|
-
const message = normalizeScopeIssueMessage(messageRaw);
|
|
800
|
-
const key = file.toLowerCase();
|
|
801
|
-
pushVerifyIssue(scopeIssues, seenScopeIssues, key, { file, message });
|
|
802
|
-
};
|
|
803
|
-
const addWarning = (fileRaw, messageRaw, policyRaw) => {
|
|
804
|
-
const file = asStringValue(fileRaw) || 'unknown';
|
|
805
|
-
const message = asStringValue(messageRaw) || 'Warning detected';
|
|
806
|
-
const policy = asStringValue(policyRaw) || 'warning';
|
|
807
|
-
const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}`;
|
|
808
|
-
pushVerifyIssue(warnings, seenWarnings, key, { file, message, policy });
|
|
809
|
-
};
|
|
810
|
-
const addViolation = (fileRaw, messageRaw, policyRaw, severityRaw) => {
|
|
811
|
-
const file = asStringValue(fileRaw) || 'unknown';
|
|
812
|
-
const message = asStringValue(messageRaw) || 'Policy violation detected';
|
|
813
|
-
const policy = asStringValue(policyRaw) || 'unknown_policy';
|
|
814
|
-
const severity = toVerifySeverity(severityRaw);
|
|
815
|
-
const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}|${severity}`;
|
|
816
|
-
pushVerifyIssue(violations, seenViolations, key, { file, message, policy, severity });
|
|
817
|
-
};
|
|
818
|
-
const rawScopeIssues = Array.isArray(payload.scopeIssues) ? payload.scopeIssues : [];
|
|
819
|
-
for (const item of rawScopeIssues) {
|
|
820
|
-
const record = asObjectRecord(item);
|
|
821
|
-
if (record) {
|
|
822
|
-
addScopeIssue(record.file, record.message);
|
|
823
|
-
}
|
|
824
|
-
else {
|
|
825
|
-
addScopeIssue(item, null);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
const rawBloatFiles = Array.isArray(payload.bloatFiles) ? payload.bloatFiles : [];
|
|
829
|
-
for (const item of rawBloatFiles) {
|
|
830
|
-
addScopeIssue(item, null);
|
|
831
|
-
}
|
|
832
|
-
const rawWarnings = Array.isArray(payload.warnings) ? payload.warnings : [];
|
|
833
|
-
for (const item of rawWarnings) {
|
|
834
|
-
const record = asObjectRecord(item);
|
|
835
|
-
if (record) {
|
|
836
|
-
addWarning(record.file, record.message, record.policy ?? record.rule);
|
|
837
|
-
}
|
|
838
|
-
else if (typeof item === 'string') {
|
|
839
|
-
addWarning('unknown', item, 'warning');
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
const rawViolations = Array.isArray(payload.violations) ? payload.violations : [];
|
|
843
|
-
for (const item of rawViolations) {
|
|
844
|
-
const record = asObjectRecord(item);
|
|
845
|
-
if (!record)
|
|
846
|
-
continue;
|
|
847
|
-
const file = record.file;
|
|
848
|
-
const message = record.message;
|
|
849
|
-
const policy = record.policy ?? record.rule;
|
|
850
|
-
const severity = toVerifySeverity(record.severity);
|
|
851
|
-
const combined = `${String(policy || '').toLowerCase()} ${String(message || '').toLowerCase()}`;
|
|
852
|
-
const isScopeIssue = combined.includes('scope_guard')
|
|
853
|
-
|| combined.includes('scope')
|
|
854
|
-
|| combined.includes('outside the plan')
|
|
855
|
-
|| combined.includes('out of scope');
|
|
856
|
-
if (isScopeIssue) {
|
|
857
|
-
addScopeIssue(file, message);
|
|
858
|
-
continue;
|
|
859
|
-
}
|
|
860
|
-
// Artifact presence/signature checks are advisory — they must never block a PR.
|
|
861
|
-
// Real governance signal (policy violations, scope drift) should not be obscured
|
|
862
|
-
// by infrastructure setup state.
|
|
863
|
-
const policyStr = String(policy || '').toLowerCase();
|
|
864
|
-
const isArtifactCheck = policyStr === 'deterministic_artifacts_required'
|
|
865
|
-
|| policyStr === 'signed_artifacts_required';
|
|
866
|
-
if (isArtifactCheck) {
|
|
867
|
-
addWarning(file, message, policy);
|
|
868
|
-
continue;
|
|
869
|
-
}
|
|
870
|
-
if (severity === 'warning' || severity === 'info') {
|
|
871
|
-
addWarning(file, message, policy);
|
|
872
|
-
continue;
|
|
873
|
-
}
|
|
874
|
-
addViolation(file, message, policy, severity);
|
|
875
|
-
}
|
|
876
|
-
const payloadMessage = asStringValue(payload.message);
|
|
877
|
-
if (payloadMessage
|
|
878
|
-
&& violations.length === 0
|
|
879
|
-
&& warnings.length === 0
|
|
880
|
-
&& scopeIssues.length === 0) {
|
|
881
|
-
addWarning('unknown', payloadMessage, 'verify_result');
|
|
882
|
-
}
|
|
883
|
-
const summaryRecord = asObjectRecord(payload.summary);
|
|
884
|
-
const fileSet = new Set();
|
|
885
|
-
for (const violation of violations)
|
|
886
|
-
fileSet.add(violation.file);
|
|
887
|
-
for (const warning of warnings)
|
|
888
|
-
fileSet.add(warning.file);
|
|
889
|
-
for (const scopeIssue of scopeIssues)
|
|
890
|
-
fileSet.add(scopeIssue.file);
|
|
891
|
-
const totalFilesChanged = (() => {
|
|
892
|
-
const fromSummary = summaryRecord ? asNumberValue(summaryRecord.totalFilesChanged) : null;
|
|
893
|
-
if (fromSummary !== null)
|
|
894
|
-
return Math.max(0, Math.floor(fromSummary));
|
|
895
|
-
const blastRadius = asObjectRecord(payload.blastRadius);
|
|
896
|
-
const fromBlastRadius = blastRadius ? asNumberValue(blastRadius.filesChanged) : null;
|
|
897
|
-
if (fromBlastRadius !== null)
|
|
898
|
-
return Math.max(0, Math.floor(fromBlastRadius));
|
|
899
|
-
return fileSet.size;
|
|
900
|
-
})();
|
|
901
|
-
const driftScoreRaw = asNumberValue(payload.driftScore);
|
|
902
|
-
const driftScore = driftScoreRaw === null
|
|
903
|
-
? undefined
|
|
904
|
-
: Math.max(0, Math.min(100, Math.round(driftScoreRaw)));
|
|
905
|
-
const expediteModeUsed = resolveExpediteModeFromPayload(payload);
|
|
906
|
-
const scopeTriageItems = scopeIssues.map((item) => ({
|
|
907
|
-
file: item.file,
|
|
908
|
-
message: item.message,
|
|
909
|
-
policy: 'scope_guard',
|
|
910
|
-
severity: 'block',
|
|
911
|
-
source: 'scope',
|
|
912
|
-
}));
|
|
913
|
-
const violationTriageItems = violations.map((item) => ({
|
|
914
|
-
file: item.file,
|
|
915
|
-
message: item.message,
|
|
916
|
-
policy: item.policy,
|
|
917
|
-
severity: item.severity,
|
|
918
|
-
source: 'violation',
|
|
919
|
-
}));
|
|
920
|
-
const warningTriageItems = warnings.map((item) => ({
|
|
921
|
-
file: item.file,
|
|
922
|
-
message: item.message,
|
|
923
|
-
policy: item.policy,
|
|
924
|
-
severity: 'warning',
|
|
925
|
-
source: 'warning',
|
|
926
|
-
}));
|
|
927
|
-
const defaultBlockingItems = dedupeTriageItems([
|
|
928
|
-
...scopeTriageItems,
|
|
929
|
-
...violationTriageItems.filter((item) => item.severity === 'critical' || item.severity === 'high'),
|
|
930
|
-
]);
|
|
931
|
-
const defaultAdvisoryItems = dedupeTriageItems([
|
|
932
|
-
...warningTriageItems,
|
|
933
|
-
...violationTriageItems.filter((item) => item.severity === 'warning' || item.severity === 'info'),
|
|
934
|
-
]);
|
|
935
|
-
const expediteBlockingItems = dedupeTriageItems([
|
|
936
|
-
...scopeTriageItems.filter((item) => isCriticalScopeBreach(item.file, item.message)),
|
|
937
|
-
...violationTriageItems.filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message)),
|
|
938
|
-
...warningTriageItems
|
|
939
|
-
.filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
|
|
940
|
-
.map((item) => ({
|
|
941
|
-
...item,
|
|
942
|
-
source: 'violation',
|
|
943
|
-
})),
|
|
944
|
-
]);
|
|
945
|
-
const expediteItems = dedupeTriageItems([
|
|
946
|
-
...scopeTriageItems
|
|
947
|
-
.filter((item) => !isCriticalScopeBreach(item.file, item.message))
|
|
948
|
-
.map((item) => ({
|
|
949
|
-
...item,
|
|
950
|
-
source: 'expedite',
|
|
951
|
-
})),
|
|
952
|
-
...violationTriageItems
|
|
953
|
-
.filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
|
|
954
|
-
.map((item) => ({
|
|
955
|
-
...item,
|
|
956
|
-
source: 'expedite',
|
|
957
|
-
})),
|
|
958
|
-
...warningTriageItems
|
|
959
|
-
.filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
|
|
960
|
-
.map((item) => ({
|
|
961
|
-
...item,
|
|
962
|
-
source: 'expedite',
|
|
963
|
-
})),
|
|
964
|
-
]);
|
|
965
|
-
// ── Intent issues and summary from engine ───────────────────────────────
|
|
966
|
-
const rawIntentIssues = Array.isArray(payload.intentIssues) ? payload.intentIssues : [];
|
|
967
|
-
const intentDomains = Array.isArray(payload.intentDomains) ? payload.intentDomains : [];
|
|
968
|
-
const intentSummary = (payload.intentSummary ?? null);
|
|
969
|
-
const rawFlowIssues = Array.isArray(payload.flowIssues) ? payload.flowIssues : [];
|
|
970
|
-
const rawRegressions = Array.isArray(payload.regressions) ? payload.regressions : [];
|
|
971
|
-
// High-severity intent issues become blocking; medium become advisory.
|
|
972
|
-
const intentBlockingTriageItems = rawIntentIssues
|
|
973
|
-
.filter((i) => i.severity === 'high')
|
|
974
|
-
.map((i) => ({
|
|
975
|
-
file: (i.files?.[0]) ?? 'intent-analysis',
|
|
976
|
-
message: i.message,
|
|
977
|
-
policy: i.rule,
|
|
978
|
-
severity: 'high',
|
|
979
|
-
source: 'violation',
|
|
980
|
-
}));
|
|
981
|
-
const intentAdvisoryTriageItems = rawIntentIssues
|
|
982
|
-
.filter((i) => i.severity === 'medium')
|
|
983
|
-
.map((i) => ({
|
|
984
|
-
file: (i.files?.[0]) ?? 'intent-analysis',
|
|
985
|
-
message: i.message,
|
|
986
|
-
policy: i.rule,
|
|
987
|
-
severity: 'warning',
|
|
988
|
-
source: 'warning',
|
|
989
|
-
}));
|
|
990
|
-
// V5: flow issues — high → blocking, medium → advisory
|
|
991
|
-
const flowBlockingTriageItems = rawFlowIssues
|
|
992
|
-
.filter((i) => i.severity === 'high')
|
|
993
|
-
.map((i) => ({
|
|
994
|
-
file: (i.files?.[0]) ?? 'flow-analysis',
|
|
995
|
-
message: i.message,
|
|
996
|
-
policy: i.rule,
|
|
997
|
-
severity: 'high',
|
|
998
|
-
source: 'violation',
|
|
999
|
-
}));
|
|
1000
|
-
const flowAdvisoryTriageItems = rawFlowIssues
|
|
1001
|
-
.filter((i) => i.severity === 'medium')
|
|
1002
|
-
.map((i) => ({
|
|
1003
|
-
file: (i.files?.[0]) ?? 'flow-analysis',
|
|
1004
|
-
message: i.message,
|
|
1005
|
-
policy: i.rule,
|
|
1006
|
-
severity: 'warning',
|
|
1007
|
-
source: 'warning',
|
|
1008
|
-
}));
|
|
1009
|
-
let blockingItems = expediteModeUsed ? expediteBlockingItems : defaultBlockingItems;
|
|
1010
|
-
let advisoryItems = expediteModeUsed ? expediteItems : defaultAdvisoryItems;
|
|
1011
|
-
if (intentBlockingTriageItems.length > 0) {
|
|
1012
|
-
blockingItems = dedupeTriageItems([...blockingItems, ...intentBlockingTriageItems]);
|
|
1013
|
-
}
|
|
1014
|
-
if (intentAdvisoryTriageItems.length > 0) {
|
|
1015
|
-
advisoryItems = dedupeTriageItems([...advisoryItems, ...intentAdvisoryTriageItems]);
|
|
1016
|
-
}
|
|
1017
|
-
if (flowBlockingTriageItems.length > 0) {
|
|
1018
|
-
blockingItems = dedupeTriageItems([...blockingItems, ...flowBlockingTriageItems]);
|
|
1019
|
-
}
|
|
1020
|
-
if (flowAdvisoryTriageItems.length > 0) {
|
|
1021
|
-
advisoryItems = dedupeTriageItems([...advisoryItems, ...flowAdvisoryTriageItems]);
|
|
1022
|
-
}
|
|
1023
|
-
// V6: regressions — always blocking
|
|
1024
|
-
const regressionBlockingTriageItems = rawRegressions.map((r) => ({
|
|
1025
|
-
file: 'regression-analysis',
|
|
1026
|
-
message: r.message,
|
|
1027
|
-
policy: r.rule,
|
|
1028
|
-
severity: 'high',
|
|
1029
|
-
source: 'violation',
|
|
1030
|
-
}));
|
|
1031
|
-
if (regressionBlockingTriageItems.length > 0) {
|
|
1032
|
-
blockingItems = dedupeTriageItems([...regressionBlockingTriageItems, ...blockingItems]);
|
|
1033
|
-
}
|
|
1034
|
-
const grade = verdict === 'PASS' ? 'A' : verdict === 'WARN' ? 'C' : 'F';
|
|
1035
|
-
const canonical = {
|
|
1036
|
-
grade,
|
|
1037
|
-
score: violations.length === 0 && warnings.length === 0 && scopeIssues.length === 0 ? 100 : 0,
|
|
1038
|
-
verdict,
|
|
1039
|
-
summary: {
|
|
1040
|
-
totalFilesChanged,
|
|
1041
|
-
totalViolations: violations.length,
|
|
1042
|
-
totalWarnings: warnings.length,
|
|
1043
|
-
totalScopeIssues: scopeIssues.length,
|
|
1044
|
-
},
|
|
1045
|
-
violations,
|
|
1046
|
-
warnings,
|
|
1047
|
-
scopeIssues,
|
|
1048
|
-
blockingCount: blockingItems.length,
|
|
1049
|
-
advisoryCount: advisoryItems.length,
|
|
1050
|
-
blockingItems,
|
|
1051
|
-
advisoryItems,
|
|
1052
|
-
intentIssues: rawIntentIssues,
|
|
1053
|
-
intentDomains,
|
|
1054
|
-
intentSummary,
|
|
1055
|
-
flowIssues: rawFlowIssues,
|
|
1056
|
-
regressions: rawRegressions,
|
|
1057
|
-
expediteModeUsed,
|
|
1058
|
-
expediteCount: expediteModeUsed ? expediteItems.length : 0,
|
|
1059
|
-
expediteItems: expediteModeUsed ? expediteItems : [],
|
|
1060
|
-
expediteFollowUpChecklist: expediteModeUsed ? [...EXPEDITE_FOLLOW_UP_CHECKLIST] : [],
|
|
1061
|
-
...(expediteModeUsed ? { expediteNote: 'Expedite Mode used' } : {}),
|
|
1062
|
-
...(typeof driftScore === 'number' ? { driftScore } : {}),
|
|
1063
|
-
};
|
|
1064
|
-
// Preserve actionable metadata from rich verify payloads so downstream
|
|
1065
|
-
// consumers (Action, CI contracts, dashboards) keep structured context.
|
|
1066
|
-
const passthroughKeys = [
|
|
1067
|
-
'message',
|
|
1068
|
-
'mode',
|
|
1069
|
-
'ciMode',
|
|
1070
|
-
'policyOnly',
|
|
1071
|
-
'policyOnlySource',
|
|
1072
|
-
'policySources',
|
|
1073
|
-
'scopeGuardPassed',
|
|
1074
|
-
'policyLock',
|
|
1075
|
-
'policyCompilation',
|
|
1076
|
-
'policyExceptions',
|
|
1077
|
-
'policyGovernance',
|
|
1078
|
-
'changeContract',
|
|
1079
|
-
'runtimeGuard',
|
|
1080
|
-
'governanceDecision',
|
|
1081
|
-
'orgGovernance',
|
|
1082
|
-
'aiChangeLog',
|
|
1083
|
-
'verificationSource',
|
|
1084
|
-
'tier',
|
|
1085
|
-
'aiDebt',
|
|
1086
|
-
'blastRadius',
|
|
1087
|
-
'suspiciousChange',
|
|
1088
|
-
'policyDecision',
|
|
1089
|
-
'policyPack',
|
|
1090
|
-
'changeContractViolations',
|
|
1091
|
-
'governanceVerification',
|
|
1092
|
-
'governanceFindings',
|
|
1093
|
-
'structuralViolations',
|
|
1094
|
-
'structuralRulesApplied',
|
|
1095
|
-
'structuralSuppressedCount',
|
|
1096
|
-
];
|
|
1097
|
-
const canonicalMutable = canonical;
|
|
1098
|
-
for (const key of passthroughKeys) {
|
|
1099
|
-
const v = payload[key];
|
|
1100
|
-
if (Object.prototype.hasOwnProperty.call(payload, key) && v !== undefined && v !== null) {
|
|
1101
|
-
canonicalMutable[key] = v;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
// Backward-compatibility alias: older integrations and tests expect `rule`
|
|
1105
|
-
// while canonical contract uses `policy`.
|
|
1106
|
-
canonical.violations = canonical.violations.map((item) => ({
|
|
1107
|
-
...item,
|
|
1108
|
-
rule: item.policy,
|
|
1109
|
-
}));
|
|
1110
|
-
canonical.warnings = canonical.warnings.map((item) => ({
|
|
1111
|
-
...item,
|
|
1112
|
-
rule: item.policy,
|
|
1113
|
-
}));
|
|
1114
|
-
return canonical;
|
|
1115
|
-
}
|
|
1116
|
-
function emitCanonicalVerifyJson(payload, onEmit) {
|
|
1117
|
-
const canonical = toCanonicalVerifyOutput((0, canonical_pipeline_1.attachCanonicalGovernance)(payload));
|
|
1118
|
-
onEmit?.(canonical);
|
|
1119
|
-
// Use sync stdout write so immediate process.exit paths do not truncate JSON.
|
|
1120
|
-
const serialized = Buffer.from(`${JSON.stringify(canonical, null, 2)}\n`, 'utf-8');
|
|
1121
|
-
try {
|
|
1122
|
-
let offset = 0;
|
|
1123
|
-
while (offset < serialized.length) {
|
|
1124
|
-
try {
|
|
1125
|
-
const written = (0, fs_1.writeSync)(1, serialized, offset, serialized.length - offset);
|
|
1126
|
-
if (written <= 0)
|
|
1127
|
-
break;
|
|
1128
|
-
offset += written;
|
|
1129
|
-
}
|
|
1130
|
-
catch (error) {
|
|
1131
|
-
const code = error.code;
|
|
1132
|
-
if (code === 'EAGAIN' || code === 'EWOULDBLOCK') {
|
|
1133
|
-
continue;
|
|
1134
|
-
}
|
|
1135
|
-
throw error;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
catch {
|
|
1140
|
-
process.stdout.write(serialized.toString('utf-8'));
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
function buildDeterministicLayerSummary(payload) {
|
|
1144
|
-
const verdict = asStringValue(payload.verdict) || 'UNKNOWN';
|
|
1145
|
-
const mode = asStringValue(payload.mode) || 'unknown';
|
|
1146
|
-
const policyOnly = payload.policyOnly === true;
|
|
1147
|
-
const scopeGuardPassed = asBooleanFlag(payload.scopeGuardPassed);
|
|
1148
|
-
const violations = asObjectArray(payload.violations);
|
|
1149
|
-
const policyViolations = violations.filter((entry) => {
|
|
1150
|
-
const rule = String(entry.rule || '').toLowerCase();
|
|
1151
|
-
return (!rule.includes('scope_guard')
|
|
1152
|
-
&& !rule.includes('change_contract')
|
|
1153
|
-
&& !rule.includes('runtime_guard')
|
|
1154
|
-
&& !rule.includes('deterministic_artifacts_required')
|
|
1155
|
-
&& !rule.includes('signed_artifacts_required'));
|
|
1156
|
-
});
|
|
1157
|
-
const policyBlocking = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'block');
|
|
1158
|
-
const policyWarnings = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'warn');
|
|
1159
|
-
const changeContract = asObjectRecord(payload.changeContract);
|
|
1160
|
-
const changeContractValid = asBooleanFlag(changeContract?.valid);
|
|
1161
|
-
const changeContractEnforced = changeContract?.enforced === true;
|
|
1162
|
-
const changeContractViolations = Array.isArray(changeContract?.violations)
|
|
1163
|
-
? (changeContract?.violations).length
|
|
1164
|
-
: 0;
|
|
1165
|
-
const explicitContractViolations = violations.filter((entry) => {
|
|
1166
|
-
const rule = String(entry.rule || '').toLowerCase();
|
|
1167
|
-
return rule.includes('scope_guard') || rule.includes('change_contract');
|
|
1168
|
-
}).length;
|
|
1169
|
-
const runtimeGuard = asObjectRecord(payload.runtimeGuard);
|
|
1170
|
-
const runtimeGuardRequired = runtimeGuard?.required === true;
|
|
1171
|
-
const runtimeGuardPass = asBooleanFlag(runtimeGuard?.pass);
|
|
1172
|
-
const runtimeGuardViolations = Array.isArray(runtimeGuard?.violations)
|
|
1173
|
-
? (runtimeGuard?.violations).length
|
|
1174
|
-
: violations.filter((entry) => String(entry.rule || '').toLowerCase().includes('runtime_guard')).length;
|
|
1175
|
-
const policyCompilation = asObjectRecord(payload.policyCompilation);
|
|
1176
|
-
const deterministicRuleCount = asNumberValue(policyCompilation?.deterministicRuleCount);
|
|
1177
|
-
const unmatchedStatements = asNumberValue(policyCompilation?.unmatchedStatements);
|
|
1178
|
-
let policyGateStatus = 'pass';
|
|
1179
|
-
if (policyBlocking.length > 0) {
|
|
1180
|
-
policyGateStatus = 'fail';
|
|
1181
|
-
}
|
|
1182
|
-
else if (policyWarnings.length > 0 || verdict === 'WARN') {
|
|
1183
|
-
policyGateStatus = 'warn';
|
|
1184
|
-
}
|
|
1185
|
-
let contractGateStatus = 'not_applicable';
|
|
1186
|
-
if (!policyOnly) {
|
|
1187
|
-
contractGateStatus = 'pass';
|
|
1188
|
-
if (changeContractEnforced
|
|
1189
|
-
&& (changeContractValid === false || changeContractViolations > 0 || explicitContractViolations > 0 || scopeGuardPassed === false)) {
|
|
1190
|
-
contractGateStatus = 'fail';
|
|
1191
|
-
}
|
|
1192
|
-
else if (!changeContractEnforced && (changeContractViolations > 0 || explicitContractViolations > 0)) {
|
|
1193
|
-
contractGateStatus = 'warn';
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
let runtimeGuardStatus = 'not_applicable';
|
|
1197
|
-
if (runtimeGuardRequired) {
|
|
1198
|
-
runtimeGuardStatus = runtimeGuardPass === false || runtimeGuardViolations > 0 ? 'fail' : 'pass';
|
|
1199
|
-
}
|
|
1200
|
-
else if (runtimeGuardViolations > 0) {
|
|
1201
|
-
runtimeGuardStatus = 'fail';
|
|
1202
|
-
}
|
|
1203
|
-
return {
|
|
1204
|
-
policyGate: {
|
|
1205
|
-
status: policyGateStatus,
|
|
1206
|
-
blockingViolations: policyBlocking.length,
|
|
1207
|
-
warningViolations: policyWarnings.length,
|
|
1208
|
-
deterministicRuleCount: deterministicRuleCount ?? null,
|
|
1209
|
-
unmatchedStatements: unmatchedStatements ?? null,
|
|
1210
|
-
},
|
|
1211
|
-
contractGate: {
|
|
1212
|
-
status: contractGateStatus,
|
|
1213
|
-
enforced: changeContractEnforced,
|
|
1214
|
-
valid: changeContractValid,
|
|
1215
|
-
violationCount: changeContractViolations + explicitContractViolations,
|
|
1216
|
-
mode,
|
|
1217
|
-
},
|
|
1218
|
-
runtimeGuardGate: {
|
|
1219
|
-
status: runtimeGuardStatus,
|
|
1220
|
-
required: runtimeGuardRequired,
|
|
1221
|
-
pass: runtimeGuardPass,
|
|
1222
|
-
violationCount: runtimeGuardViolations,
|
|
1223
|
-
},
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
740
|
function toAiDebtSummary(evaluation) {
|
|
1227
741
|
return {
|
|
1228
742
|
mode: evaluation.mode,
|
|
@@ -1326,7 +840,7 @@ function resolveVerifyExpediteMode(projectRoot) {
|
|
|
1326
840
|
return true;
|
|
1327
841
|
}
|
|
1328
842
|
const branchName = (0, git_1.detectCurrentGitBranch)(projectRoot) || process.env.GITHUB_REF_NAME || '';
|
|
1329
|
-
return containsAnyToken(branchName, ['hotfix', 'urgent', 'prod-down', 'prod_down', 'prod down', 'incident', 'expedite']);
|
|
843
|
+
return (0, verify_output_1.containsAnyToken)(branchName, ['hotfix', 'urgent', 'prod-down', 'prod_down', 'prod down', 'incident', 'expedite']);
|
|
1330
844
|
}
|
|
1331
845
|
function isSignedAiLogsRequired(orgGovernanceSettings) {
|
|
1332
846
|
const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
|
|
@@ -1351,19 +865,6 @@ function toPolicyLockViolations(mismatches) {
|
|
|
1351
865
|
message: item.message,
|
|
1352
866
|
}));
|
|
1353
867
|
}
|
|
1354
|
-
function resolvePolicyDecisionFromViolations(violations) {
|
|
1355
|
-
let hasWarn = false;
|
|
1356
|
-
for (const violation of violations) {
|
|
1357
|
-
const severity = String(violation.severity || '').toLowerCase();
|
|
1358
|
-
if (severity === 'block') {
|
|
1359
|
-
return 'block';
|
|
1360
|
-
}
|
|
1361
|
-
if (severity === 'warn') {
|
|
1362
|
-
hasWarn = true;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return hasWarn ? 'warn' : 'allow';
|
|
1366
|
-
}
|
|
1367
868
|
function explainExceptionEligibilityReason(reason) {
|
|
1368
869
|
switch (reason) {
|
|
1369
870
|
case 'reason_required':
|
|
@@ -1500,12 +1001,15 @@ async function recordVerificationIfRequested(options, config, payload) {
|
|
|
1500
1001
|
* Returns the exit code to use
|
|
1501
1002
|
*/
|
|
1502
1003
|
async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, ciModeEnabled, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, expediteModeEnabled, compiledPolicyArtifact, compiledPolicyMetadata, changeContractSummary, onCanonicalEmit) {
|
|
1503
|
-
const emitPolicyOnlyJson = (payload) => {
|
|
1504
|
-
emitCanonicalVerifyJson({
|
|
1004
|
+
const emitPolicyOnlyJson = (payload, onEmit) => {
|
|
1005
|
+
(0, verify_output_1.emitCanonicalVerifyJson)({
|
|
1505
1006
|
...payload,
|
|
1506
1007
|
ciMode: payload.ciMode ?? ciModeEnabled,
|
|
1507
1008
|
expediteMode: expediteModeEnabled,
|
|
1508
|
-
},
|
|
1009
|
+
}, (canonical) => {
|
|
1010
|
+
onEmit?.(canonical);
|
|
1011
|
+
onCanonicalEmit?.(canonical);
|
|
1012
|
+
});
|
|
1509
1013
|
};
|
|
1510
1014
|
const policyOnlyVerificationSource = 'policy_only';
|
|
1511
1015
|
const recordPolicyOnlyVerification = async (payload) => recordVerificationIfRequested(options, config, {
|
|
@@ -1522,18 +1026,22 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1522
1026
|
const diffFilesForPolicy = diffFiles.filter((f) => !ignoreFilter(f.path));
|
|
1523
1027
|
const expectedPolicyOnlyFiles = diffFilesForPolicy.map((file) => file.path);
|
|
1524
1028
|
const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
|
|
1029
|
+
const activeEngineeringContext = (0, active_engineering_context_1.loadActiveEngineeringContext)(projectRoot);
|
|
1525
1030
|
const governanceAnalysis = (0, governance_1.evaluateGovernance)({
|
|
1526
1031
|
projectRoot,
|
|
1527
1032
|
task: 'Policy-only verification',
|
|
1528
1033
|
expectedFiles: expectedPolicyOnlyFiles,
|
|
1529
1034
|
diffFiles: diffFilesForPolicy,
|
|
1530
|
-
contextCandidates:
|
|
1035
|
+
contextCandidates: activeEngineeringContext
|
|
1036
|
+
? activeEngineeringContext.contextPack.selectedFiles.map((item) => item.path)
|
|
1037
|
+
: expectedPolicyOnlyFiles,
|
|
1531
1038
|
orgGovernance: orgGovernanceSettings,
|
|
1532
1039
|
requireSignedAiLogs: signedLogsRequired,
|
|
1533
1040
|
signingKey: aiLogSigningKey,
|
|
1534
1041
|
signingKeyId: aiLogSigningKeyId,
|
|
1535
1042
|
signingKeys: aiLogSigningKeys,
|
|
1536
1043
|
signer: aiLogSigner,
|
|
1044
|
+
activeEngineeringContext,
|
|
1537
1045
|
});
|
|
1538
1046
|
const governancePayload = buildGovernancePayload(governanceAnalysis, orgGovernanceSettings, {
|
|
1539
1047
|
compiledPolicy: compiledPolicyMetadata,
|
|
@@ -1599,19 +1107,23 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1599
1107
|
const message = governanceAnalysis.governanceDecision.summary
|
|
1600
1108
|
|| 'Governance decision matrix returned BLOCK.';
|
|
1601
1109
|
const reasonCodes = governanceAnalysis.governanceDecision.reasonCodes || [];
|
|
1110
|
+
const driftViolations = driftFindingsToVerificationViolations(governanceAnalysis.driftIntelligence)
|
|
1111
|
+
.filter((item) => !ignoreFilter(item.file));
|
|
1112
|
+
const governanceBlockViolations = [
|
|
1113
|
+
{
|
|
1114
|
+
file: '.neurcode/ai-change-log.json',
|
|
1115
|
+
rule: 'governance_decision_block',
|
|
1116
|
+
severity: 'block',
|
|
1117
|
+
message,
|
|
1118
|
+
},
|
|
1119
|
+
...driftViolations,
|
|
1120
|
+
];
|
|
1602
1121
|
if (options.json) {
|
|
1603
1122
|
emitPolicyOnlyJson({
|
|
1604
1123
|
grade: 'F',
|
|
1605
1124
|
score: 0,
|
|
1606
1125
|
verdict: 'FAIL',
|
|
1607
|
-
violations:
|
|
1608
|
-
{
|
|
1609
|
-
file: '.neurcode/ai-change-log.json',
|
|
1610
|
-
rule: 'governance_decision_block',
|
|
1611
|
-
severity: 'block',
|
|
1612
|
-
message,
|
|
1613
|
-
},
|
|
1614
|
-
],
|
|
1126
|
+
violations: governanceBlockViolations,
|
|
1615
1127
|
message,
|
|
1616
1128
|
scopeGuardPassed: false,
|
|
1617
1129
|
bloatCount: 0,
|
|
@@ -1634,14 +1146,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1634
1146
|
}
|
|
1635
1147
|
await recordPolicyOnlyVerification({
|
|
1636
1148
|
grade: 'F',
|
|
1637
|
-
violations:
|
|
1638
|
-
{
|
|
1639
|
-
file: '.neurcode/ai-change-log.json',
|
|
1640
|
-
rule: 'governance_decision_block',
|
|
1641
|
-
severity: 'block',
|
|
1642
|
-
message,
|
|
1643
|
-
},
|
|
1644
|
-
],
|
|
1149
|
+
violations: governanceBlockViolations,
|
|
1645
1150
|
verifyResult: {
|
|
1646
1151
|
adherenceScore: 0,
|
|
1647
1152
|
verdict: 'FAIL',
|
|
@@ -1931,9 +1436,15 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1931
1436
|
message: `Policy audit chain is invalid: ${auditIntegrityStatus.issues.join('; ') || 'unknown issue'}`,
|
|
1932
1437
|
});
|
|
1933
1438
|
}
|
|
1439
|
+
if (governanceAnalysis.driftIntelligence) {
|
|
1440
|
+
const driftViolations = driftFindingsToVerificationViolations(governanceAnalysis.driftIntelligence);
|
|
1441
|
+
policyViolations.push(...driftViolations.filter((item) => !ignoreFilter(item.file)));
|
|
1442
|
+
}
|
|
1934
1443
|
const policyOnlyStructural = (0, structural_on_diff_1.runStructuralOnDiffFiles)(projectRoot, diffFilesForPolicy);
|
|
1935
|
-
|
|
1936
|
-
|
|
1444
|
+
// Structural violations are passed to the canonical pipeline via payload.structuralViolations
|
|
1445
|
+
// (see line ~2584). Do NOT merge them into policyViolations — that would create structural:*
|
|
1446
|
+
// duplicates that contaminate the canonical finding graph.
|
|
1447
|
+
policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
|
|
1937
1448
|
const effectiveVerdict = policyDecision === 'block' ? 'FAIL' : policyDecision === 'warn' ? 'WARN' : 'PASS';
|
|
1938
1449
|
const grade = effectiveVerdict === 'PASS' ? 'A' : effectiveVerdict === 'WARN' ? 'C' : 'F';
|
|
1939
1450
|
const score = effectiveVerdict === 'PASS' ? 100 : effectiveVerdict === 'WARN' ? 50 : 0;
|
|
@@ -1989,6 +1500,11 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
1989
1500
|
eventCount: auditIntegrity.count,
|
|
1990
1501
|
},
|
|
1991
1502
|
};
|
|
1503
|
+
// Phase 4: Compute replay checksum from structural findings so replayChecksum
|
|
1504
|
+
// is populated in --policy-only and --local-only CI/daemonless modes.
|
|
1505
|
+
// This closes the N/A gap identified in the Apache Airflow benchmark.
|
|
1506
|
+
const policyOnlyStructuralFindings = policyOnlyStructural.violations.map(canonical_pipeline_1.findingFromStructural);
|
|
1507
|
+
const policyOnlyReplayChecksum = (0, canonical_invariants_1.computeCanonicalFindingChecksum)(policyOnlyStructuralFindings);
|
|
1992
1508
|
const policyOnlyPayload = {
|
|
1993
1509
|
grade,
|
|
1994
1510
|
score,
|
|
@@ -2007,6 +1523,8 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
2007
1523
|
mode: 'policy_only',
|
|
2008
1524
|
policyOnly: true,
|
|
2009
1525
|
policyOnlySource: source,
|
|
1526
|
+
replayChecksum: policyOnlyReplayChecksum,
|
|
1527
|
+
replayMode: 'local-structural',
|
|
2010
1528
|
...governancePayload,
|
|
2011
1529
|
policyLock: {
|
|
2012
1530
|
enforced: policyLockEvaluation.enforced,
|
|
@@ -2027,15 +1545,46 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
2027
1545
|
}
|
|
2028
1546
|
: {}),
|
|
2029
1547
|
};
|
|
1548
|
+
const policyOnlyReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
|
|
1549
|
+
projectRoot,
|
|
1550
|
+
diffContext: `${options.base || 'HEAD'} vs working tree`,
|
|
1551
|
+
filesAnalyzed: diffFiles.length,
|
|
1552
|
+
planId: null,
|
|
1553
|
+
verificationSource: policyOnlyVerificationSource,
|
|
1554
|
+
policyLockFingerprint: (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint || null,
|
|
1555
|
+
compiledPolicyFingerprint: compiledPolicyArtifact?.fingerprint || null,
|
|
1556
|
+
ruleIds: policyOnlyStructural.rulesApplied,
|
|
1557
|
+
blockingCount: policyViolations.filter((v) => v.severity === 'block').length
|
|
1558
|
+
+ policyOnlyStructural.violations.filter((v) => v.severity === 'BLOCKING').length,
|
|
1559
|
+
advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length
|
|
1560
|
+
+ policyOnlyStructural.violations.filter((v) => v.severity !== 'BLOCKING').length,
|
|
1561
|
+
suppressedCount: policyOnlyStructural.suppressedCount,
|
|
1562
|
+
structuralBlockingCount: policyOnlyStructural.violations.filter((v) => v.severity === 'BLOCKING').length,
|
|
1563
|
+
structuralAdvisoryCount: policyOnlyStructural.violations.filter((v) => v.severity !== 'BLOCKING').length,
|
|
1564
|
+
deterministicSignals: policyOnlyStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length,
|
|
1565
|
+
heuristicSignals: policyOnlyStructural.violations.filter((v) => v.determinism === 'heuristic-advisory').length,
|
|
1566
|
+
overallTrustScore: policyOnlyStructural.violations.length > 0
|
|
1567
|
+
? Math.round((policyOnlyStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length / policyOnlyStructural.violations.length) * 100)
|
|
1568
|
+
: 100,
|
|
1569
|
+
verdict: effectiveVerdict,
|
|
1570
|
+
governanceDecision: governanceAnalysis.governanceDecision.summary || 'policy-only',
|
|
1571
|
+
actor: ciModeEnabled ? 'ci-runner' : 'local-user',
|
|
1572
|
+
source: ciModeEnabled ? 'ci' : 'cli',
|
|
1573
|
+
replayChecksum: policyOnlyReplayChecksum,
|
|
1574
|
+
});
|
|
2030
1575
|
if (options.json) {
|
|
2031
|
-
emitPolicyOnlyJson(policyOnlyPayload)
|
|
1576
|
+
emitPolicyOnlyJson(policyOnlyPayload, (canonical) => {
|
|
1577
|
+
(0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(canonical, policyOnlyReplayCustody);
|
|
1578
|
+
});
|
|
2032
1579
|
}
|
|
2033
1580
|
else {
|
|
2034
|
-
|
|
1581
|
+
const policyOnlyCanonical = (0, verify_output_1.toCanonicalVerifyOutput)((0, canonical_pipeline_1.attachCanonicalGovernance)({
|
|
2035
1582
|
...policyOnlyPayload,
|
|
2036
1583
|
ciMode: ciModeEnabled,
|
|
2037
1584
|
expediteMode: expediteModeEnabled,
|
|
2038
1585
|
}));
|
|
1586
|
+
(0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(policyOnlyCanonical, policyOnlyReplayCustody);
|
|
1587
|
+
onCanonicalEmit?.(policyOnlyCanonical);
|
|
2039
1588
|
if (effectiveVerdict === 'PASS') {
|
|
2040
1589
|
console.log(chalk.green('✅ Policy check passed'));
|
|
2041
1590
|
}
|
|
@@ -2055,8 +1604,18 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
|
|
|
2055
1604
|
if (governance.audit.requireIntegrity && !auditIntegrityStatus.valid) {
|
|
2056
1605
|
console.log(chalk.red(' Policy audit integrity check failed'));
|
|
2057
1606
|
}
|
|
2058
|
-
displayGovernanceInsights(governanceAnalysis, { explain: options.explain });
|
|
1607
|
+
(0, verify_render_1.displayGovernanceInsights)(chalk, governanceAnalysis, { explain: options.explain });
|
|
2059
1608
|
console.log(chalk.dim(`\n${message}`));
|
|
1609
|
+
logCiPolicyOnlyOutcomeExplainability({
|
|
1610
|
+
ciModeEnabled,
|
|
1611
|
+
json: Boolean(options.json),
|
|
1612
|
+
verdict: effectiveVerdict,
|
|
1613
|
+
source,
|
|
1614
|
+
structuralViolations: policyOnlyStructural.violations,
|
|
1615
|
+
policyViolations,
|
|
1616
|
+
replayChecksum: policyOnlyReplayChecksum,
|
|
1617
|
+
replayMode: 'local-structural',
|
|
1618
|
+
});
|
|
2060
1619
|
}
|
|
2061
1620
|
await recordPolicyOnlyVerification({
|
|
2062
1621
|
grade,
|
|
@@ -2092,6 +1651,15 @@ async function verifyCommand(options) {
|
|
|
2092
1651
|
let lastCanonicalOutput = null;
|
|
2093
1652
|
let lastEvidenceFallbackOutput = null;
|
|
2094
1653
|
let evidenceFinalizeAttempted = false;
|
|
1654
|
+
const custodySource = ciModeEnabled ? 'ci' : 'cli';
|
|
1655
|
+
const custodyActor = ciModeEnabled ? 'ci-runner' : 'local-user';
|
|
1656
|
+
// ── Phase 1 Runtime Stability: create context early so all subsystems share it ──
|
|
1657
|
+
// Structural governance is NEVER gated by this context — it always runs.
|
|
1658
|
+
const runtimeCtx = (0, verify_runtime_stability_1.createVerifyRuntimeContext)(ciModeEnabled);
|
|
1659
|
+
// Large repo detection: sets largeRepoMode and emits cache-build recommendation.
|
|
1660
|
+
(0, verify_runtime_stability_1.applyLargeRepoProtection)(runtimeCtx, projectRoot);
|
|
1661
|
+
// Initial memory pressure check at entry.
|
|
1662
|
+
(0, verify_runtime_stability_1.applyMemoryPressureDegradation)(runtimeCtx);
|
|
2095
1663
|
if (ciModeEnabled) {
|
|
2096
1664
|
options.policyOnly = true;
|
|
2097
1665
|
options.requirePlan = false;
|
|
@@ -2099,8 +1667,16 @@ async function verifyCommand(options) {
|
|
|
2099
1667
|
options.asyncMode = false;
|
|
2100
1668
|
}
|
|
2101
1669
|
const captureEvidencePayload = (payload) => {
|
|
2102
|
-
|
|
2103
|
-
|
|
1670
|
+
const enrichedPayload = (0, canonical_pipeline_1.attachCanonicalGovernance)(payload);
|
|
1671
|
+
lastEvidenceFallbackOutput = enrichedPayload;
|
|
1672
|
+
lastCanonicalOutput = (0, verify_output_1.toCanonicalVerifyOutput)(enrichedPayload);
|
|
1673
|
+
};
|
|
1674
|
+
const applyCapturedReplayCustody = (canonical, custody) => {
|
|
1675
|
+
if (!canonical || !custody) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
(0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(canonical, custody);
|
|
1679
|
+
lastProvenanceRunId = custody.provenanceRecord?.runId ?? lastProvenanceRunId;
|
|
2104
1680
|
};
|
|
2105
1681
|
const finalizeEvidence = (exitCode) => {
|
|
2106
1682
|
if (!evidenceEnabled || evidenceFinalizeAttempted) {
|
|
@@ -2162,7 +1738,10 @@ async function verifyCommand(options) {
|
|
|
2162
1738
|
let structuralViolations = [];
|
|
2163
1739
|
let structuralRulesApplied = [];
|
|
2164
1740
|
let structuralSuppressedCount = 0;
|
|
2165
|
-
const emitVerifyJson = (payload) => {
|
|
1741
|
+
const emitVerifyJson = (payload, onEmit) => {
|
|
1742
|
+
// Check memory pressure immediately before emission (may have changed during long verify)
|
|
1743
|
+
(0, verify_runtime_stability_1.applyMemoryPressureDegradation)(runtimeCtx);
|
|
1744
|
+
const runtimeStabilityReport = (0, verify_runtime_stability_1.buildVerifyRuntimeReport)(runtimeCtx);
|
|
2166
1745
|
const enrichedPayload = {
|
|
2167
1746
|
...payload,
|
|
2168
1747
|
ciMode: payload.ciMode ?? ciModeEnabled,
|
|
@@ -2178,10 +1757,13 @@ async function verifyCommand(options) {
|
|
|
2178
1757
|
structuralViolations: payload.structuralViolations ?? structuralViolations,
|
|
2179
1758
|
structuralRulesApplied: payload.structuralRulesApplied ?? structuralRulesApplied,
|
|
2180
1759
|
structuralSuppressedCount: payload.structuralSuppressedCount ?? structuralSuppressedCount,
|
|
1760
|
+
// Runtime stability transparency — always present
|
|
1761
|
+
runtimeStabilityReport,
|
|
2181
1762
|
};
|
|
2182
1763
|
lastEvidenceFallbackOutput = enrichedPayload;
|
|
2183
|
-
emitCanonicalVerifyJson(enrichedPayload, (canonical) => {
|
|
1764
|
+
(0, verify_output_1.emitCanonicalVerifyJson)(enrichedPayload, (canonical) => {
|
|
2184
1765
|
lastCanonicalOutput = canonical;
|
|
1766
|
+
onEmit?.(lastCanonicalOutput);
|
|
2185
1767
|
});
|
|
2186
1768
|
};
|
|
2187
1769
|
if (!isGitRepository(projectRoot)) {
|
|
@@ -2622,8 +2204,19 @@ async function verifyCommand(options) {
|
|
|
2622
2204
|
try {
|
|
2623
2205
|
let contextNote = note;
|
|
2624
2206
|
if (Array.isArray(changedFiles) && changedFiles.length > 0) {
|
|
2625
|
-
|
|
2626
|
-
|
|
2207
|
+
// Runtime stability: skip brain context refresh if semantic layer is degraded
|
|
2208
|
+
// (memory pressure or time pressure). Structural verification is unaffected.
|
|
2209
|
+
if ((0, verify_runtime_stability_1.shouldSkipSemanticLayer)(runtimeCtx)) {
|
|
2210
|
+
contextNote = `${contextNote};semantic_skipped=runtime_pressure`;
|
|
2211
|
+
if (!ciModeEnabled && runtimeCtx.degradationReasons.length > 0) {
|
|
2212
|
+
console.log(chalk.yellow(`\n⚠️ Brain context refresh skipped: ${runtimeCtx.degradationReasons[runtimeCtx.degradationReasons.length - 1]}`));
|
|
2213
|
+
console.log(chalk.dim(' Deterministic structural governance continues unaffected.\n'));
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
else {
|
|
2217
|
+
const refreshed = (0, brain_context_1.refreshBrainContextForFiles)(projectRoot, brainScope, changedFiles);
|
|
2218
|
+
contextNote = `${contextNote};indexed=${refreshed.indexed};removed=${refreshed.removed};skipped=${refreshed.skipped}`;
|
|
2219
|
+
}
|
|
2627
2220
|
}
|
|
2628
2221
|
(0, brain_context_1.recordBrainProgressEvent)(projectRoot, brainScope, {
|
|
2629
2222
|
type: 'verify',
|
|
@@ -2775,34 +2368,70 @@ async function verifyCommand(options) {
|
|
|
2775
2368
|
console.log(chalk.cyan('\n🔍 Local-only mode: deterministic structural verification (no API required)...'));
|
|
2776
2369
|
}
|
|
2777
2370
|
const localStructural = (0, structural_on_diff_1.runStructuralOnDiffFiles)(projectRoot, diffFiles);
|
|
2371
|
+
const localStructuralFindings = localStructural.violations.map(canonical_pipeline_1.findingFromStructural);
|
|
2372
|
+
const localReplayChecksum = (0, canonical_invariants_1.computeCanonicalFindingChecksum)(localStructuralFindings);
|
|
2778
2373
|
const blockingViolations = localStructural.violations.filter((v) => v.severity === 'BLOCKING');
|
|
2374
|
+
const advisoryViolations = localStructural.violations.filter((v) => v.severity !== 'BLOCKING');
|
|
2779
2375
|
const localVerdict = blockingViolations.length > 0 ? 'FAIL' : 'PASS';
|
|
2780
2376
|
const localGrade = blockingViolations.length > 0 ? 'F' : 'B';
|
|
2781
2377
|
const localScore = blockingViolations.length > 0 ? 0 : 70;
|
|
2378
|
+
const localPayload = {
|
|
2379
|
+
grade: localGrade,
|
|
2380
|
+
score: localScore,
|
|
2381
|
+
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
|
+
})),
|
|
2388
|
+
adherenceScore: localScore,
|
|
2389
|
+
bloatCount: 0,
|
|
2390
|
+
bloatFiles: [],
|
|
2391
|
+
plannedFilesModified: 0,
|
|
2392
|
+
totalPlannedFiles: 0,
|
|
2393
|
+
message: `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`,
|
|
2394
|
+
scopeGuardPassed: true,
|
|
2395
|
+
mode: 'local_only_structural',
|
|
2396
|
+
policyOnly: true,
|
|
2397
|
+
replayChecksum: localReplayChecksum,
|
|
2398
|
+
replayMode: 'local-structural',
|
|
2399
|
+
structuralViolations: localStructural.violations,
|
|
2400
|
+
structuralBlockingCount: blockingViolations.length,
|
|
2401
|
+
structuralRulesApplied: localStructural.rulesApplied,
|
|
2402
|
+
changeContract: changeContractSummary,
|
|
2403
|
+
};
|
|
2404
|
+
captureEvidencePayload((0, canonical_pipeline_1.attachCanonicalGovernance)(localPayload));
|
|
2405
|
+
const localReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
|
|
2406
|
+
projectRoot,
|
|
2407
|
+
diffContext: `${options.base || 'HEAD'} vs working tree`,
|
|
2408
|
+
filesAnalyzed: diffFiles.length,
|
|
2409
|
+
planId: null,
|
|
2410
|
+
verificationSource: 'local_only_structural',
|
|
2411
|
+
policyLockFingerprint: null,
|
|
2412
|
+
compiledPolicyFingerprint: null,
|
|
2413
|
+
ruleIds: localStructural.rulesApplied,
|
|
2414
|
+
blockingCount: blockingViolations.length,
|
|
2415
|
+
advisoryCount: advisoryViolations.length,
|
|
2416
|
+
suppressedCount: localStructural.suppressedCount,
|
|
2417
|
+
structuralBlockingCount: blockingViolations.length,
|
|
2418
|
+
structuralAdvisoryCount: advisoryViolations.length,
|
|
2419
|
+
deterministicSignals: localStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length,
|
|
2420
|
+
heuristicSignals: localStructural.violations.filter((v) => v.determinism === 'heuristic-advisory').length,
|
|
2421
|
+
overallTrustScore: localStructural.violations.length > 0
|
|
2422
|
+
? Math.round((localStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length / localStructural.violations.length) * 100)
|
|
2423
|
+
: 100,
|
|
2424
|
+
verdict: localVerdict,
|
|
2425
|
+
governanceDecision: 'local deterministic structural verification',
|
|
2426
|
+
actor: custodyActor,
|
|
2427
|
+
source: custodySource,
|
|
2428
|
+
replayChecksum: localReplayChecksum,
|
|
2429
|
+
});
|
|
2430
|
+
applyCapturedReplayCustody(lastCanonicalOutput, localReplayCustody);
|
|
2782
2431
|
if (options.json) {
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
verdict: localVerdict,
|
|
2787
|
-
violations: localStructural.violations.map((v) => ({
|
|
2788
|
-
file: v.filePath,
|
|
2789
|
-
rule: v.ruleId,
|
|
2790
|
-
severity: v.severity === 'BLOCKING' ? 'block' : 'warn',
|
|
2791
|
-
message: `${v.ruleName}: ${v.evidence.slice(0, 120)}`,
|
|
2792
|
-
})),
|
|
2793
|
-
adherenceScore: localScore,
|
|
2794
|
-
bloatCount: 0,
|
|
2795
|
-
bloatFiles: [],
|
|
2796
|
-
plannedFilesModified: 0,
|
|
2797
|
-
totalPlannedFiles: 0,
|
|
2798
|
-
message: `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`,
|
|
2799
|
-
scopeGuardPassed: true,
|
|
2800
|
-
mode: 'local_only_structural',
|
|
2801
|
-
policyOnly: true,
|
|
2802
|
-
structuralViolations: localStructural.violations,
|
|
2803
|
-
structuralBlockingCount: blockingViolations.length,
|
|
2804
|
-
structuralRulesApplied: localStructural.rulesApplied,
|
|
2805
|
-
changeContract: changeContractSummary,
|
|
2432
|
+
(0, verify_output_1.emitCanonicalVerifyJson)(localPayload, (canonical) => {
|
|
2433
|
+
applyCapturedReplayCustody(canonical, localReplayCustody);
|
|
2434
|
+
lastCanonicalOutput = canonical;
|
|
2806
2435
|
});
|
|
2807
2436
|
}
|
|
2808
2437
|
else {
|
|
@@ -2818,7 +2447,9 @@ async function verifyCommand(options) {
|
|
|
2818
2447
|
});
|
|
2819
2448
|
}
|
|
2820
2449
|
console.log(chalk.dim(`\n[Local-only] ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking. ` +
|
|
2821
|
-
`
|
|
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'));
|
|
2822
2453
|
}
|
|
2823
2454
|
recordVerifyEvent(localVerdict, `local_only;structural=${localStructural.violations.length}`, diffFiles.map((f) => f.path));
|
|
2824
2455
|
exitWithEvidence(blockingViolations.length > 0 ? 2 : 0);
|
|
@@ -3197,6 +2828,10 @@ async function verifyCommand(options) {
|
|
|
3197
2828
|
missingExpectedFiles: 0,
|
|
3198
2829
|
blockedFilesTouched: 0,
|
|
3199
2830
|
actionMismatches: 0,
|
|
2831
|
+
serviceBoundaryBreaches: 0,
|
|
2832
|
+
infraBoundaryBreaches: 0,
|
|
2833
|
+
dependencyBoundaryBreaches: 0,
|
|
2834
|
+
sensitiveBoundaryBreaches: 0,
|
|
3200
2835
|
expectedSymbols: advisoryContract.expectedSymbols?.length || 0,
|
|
3201
2836
|
changedSymbols: 0,
|
|
3202
2837
|
missingExpectedSymbols: 0,
|
|
@@ -3216,10 +2851,14 @@ async function verifyCommand(options) {
|
|
|
3216
2851
|
}
|
|
3217
2852
|
const message = 'No plan linked yet. Ran advisory verification for quick first-run experience. ' +
|
|
3218
2853
|
'Use `neurcode plan` and `neurcode contract import --auto-detect --write-change-contract` for full enforcement.';
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
2854
|
+
// Runtime stability: skip advisory signals if advisory layer is degraded.
|
|
2855
|
+
// Structural rules always run regardless.
|
|
2856
|
+
const advisorySignals = (0, verify_runtime_stability_1.shouldSkipAdvisoryLayer)(runtimeCtx)
|
|
2857
|
+
? []
|
|
2858
|
+
: (0, advisory_signals_1.evaluateAdvisorySignals)({ diffFiles, summary });
|
|
2859
|
+
if ((0, verify_runtime_stability_1.shouldSkipAdvisoryLayer)(runtimeCtx) && !options.json) {
|
|
2860
|
+
console.log(chalk.dim(' Advisory signals skipped (runtime pressure — structural governance unaffected).'));
|
|
2861
|
+
}
|
|
3223
2862
|
const advisoryWarnCount = advisorySignals.filter((item) => item.severity === 'warn').length;
|
|
3224
2863
|
// ── Advisory-first: always run structural rules, downgrade scope issues to advisory ──
|
|
3225
2864
|
// Structural rules are deterministic and local — they MUST always run regardless of plan.
|
|
@@ -3302,8 +2941,8 @@ async function verifyCommand(options) {
|
|
|
3302
2941
|
});
|
|
3303
2942
|
}
|
|
3304
2943
|
else {
|
|
3305
|
-
printFirstRunAdvisoryMessage(options.demo === true);
|
|
3306
|
-
printAdvisorySignals(advisorySignals, options.demo === true);
|
|
2944
|
+
(0, verify_guidance_1.printFirstRunAdvisoryMessage)(chalk, options.demo === true);
|
|
2945
|
+
(0, verify_guidance_1.printAdvisorySignals)(chalk, advisorySignals, options.demo === true);
|
|
3307
2946
|
if (autoContractPath) {
|
|
3308
2947
|
console.log(chalk.green(`✅ Auto-generated minimal advisory contract: ${autoContractPath}`));
|
|
3309
2948
|
}
|
|
@@ -3341,6 +2980,7 @@ async function verifyCommand(options) {
|
|
|
3341
2980
|
let planFiles = [];
|
|
3342
2981
|
let planDependencies = [];
|
|
3343
2982
|
let remotePlanSessionId = null;
|
|
2983
|
+
const activeEngineeringContext = (0, active_engineering_context_1.loadActiveEngineeringContext)(projectRoot);
|
|
3344
2984
|
if (useLocalPlanSync) {
|
|
3345
2985
|
const localIntent = (localPlanSync.intent || '').trim();
|
|
3346
2986
|
const localConstraintText = localPlanSync.constraints.length > 0
|
|
@@ -3375,6 +3015,27 @@ async function verifyCommand(options) {
|
|
|
3375
3015
|
}
|
|
3376
3016
|
planFilesForVerification = [...new Set([...planFiles, ...localPlanExpectedFiles])];
|
|
3377
3017
|
intentConstraintsForVerification = originalIntent || undefined;
|
|
3018
|
+
if (activeEngineeringContext) {
|
|
3019
|
+
if (activeEngineeringContext.intentPack.approvedScope.files.length > 0) {
|
|
3020
|
+
planFilesForVerification = [...activeEngineeringContext.intentPack.approvedScope.files];
|
|
3021
|
+
}
|
|
3022
|
+
if (activeEngineeringContext.intentPack.intent.normalized) {
|
|
3023
|
+
intentConstraintsForVerification = activeEngineeringContext.intentPack.intent.normalized;
|
|
3024
|
+
governanceTask = activeEngineeringContext.intentPack.intent.normalized;
|
|
3025
|
+
}
|
|
3026
|
+
planDependencies = [...new Set([
|
|
3027
|
+
...planDependencies,
|
|
3028
|
+
...activeEngineeringContext.intentPack.expectedDependencies,
|
|
3029
|
+
])];
|
|
3030
|
+
if (!options.json) {
|
|
3031
|
+
console.log(chalk.dim(` Intent runtime loaded: ${activeEngineeringContext.sessionRuntime.sessionId} ` +
|
|
3032
|
+
`(${planFilesForVerification.length} approved file(s), ` +
|
|
3033
|
+
`${activeEngineeringContext.contextPack.selectedFiles.length} context file(s))`));
|
|
3034
|
+
activeEngineeringContext.warnings.slice(0, 3).forEach((warning) => {
|
|
3035
|
+
console.log(chalk.yellow(` Context warning: ${warning}`));
|
|
3036
|
+
});
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3378
3039
|
// ── Intent-Aware Engine ─────────────────────────────────────────────
|
|
3379
3040
|
// Run once we have both diffFiles and the resolved intent text.
|
|
3380
3041
|
// Stored in outer scope so all emitCanonicalVerifyJson call sites can
|
|
@@ -3428,13 +3089,16 @@ async function verifyCommand(options) {
|
|
|
3428
3089
|
expectedFiles: planFilesForVerification,
|
|
3429
3090
|
expectedDependencies: planDependencies,
|
|
3430
3091
|
diffFiles,
|
|
3431
|
-
contextCandidates:
|
|
3092
|
+
contextCandidates: activeEngineeringContext
|
|
3093
|
+
? activeEngineeringContext.contextPack.selectedFiles.map((item) => item.path)
|
|
3094
|
+
: planFilesForVerification,
|
|
3432
3095
|
orgGovernance: orgGovernanceSettings,
|
|
3433
3096
|
requireSignedAiLogs: signedLogsRequired,
|
|
3434
3097
|
signingKey: aiLogSigningKey,
|
|
3435
3098
|
signingKeyId: aiLogSigningKeyId,
|
|
3436
3099
|
signingKeys: aiLogSigningKeys,
|
|
3437
3100
|
signer: aiLogSigner,
|
|
3101
|
+
activeEngineeringContext,
|
|
3438
3102
|
});
|
|
3439
3103
|
// Get sessionId from state file (.neurcode/state.json) first, then fallback to config
|
|
3440
3104
|
// Fallback to sessionId from plan if not in state/config
|
|
@@ -3476,13 +3140,17 @@ async function verifyCommand(options) {
|
|
|
3476
3140
|
}
|
|
3477
3141
|
}
|
|
3478
3142
|
// Step C: The Intersection Logic
|
|
3479
|
-
const approvedSet = new Set([
|
|
3143
|
+
const approvedSet = new Set([
|
|
3144
|
+
...planFilesForVerification,
|
|
3145
|
+
...allowedFiles,
|
|
3146
|
+
...(governanceResult.engineeringContext?.approvedScope?.files || []),
|
|
3147
|
+
]);
|
|
3480
3148
|
const violations = modifiedFiles.filter(f => !approvedSet.has(f));
|
|
3481
3149
|
const filteredViolations = violations.filter((p) => !shouldIgnore(p));
|
|
3482
3150
|
// Step D: The Block (only report scope violations for non-ignored files)
|
|
3483
3151
|
if (filteredViolations.length > 0) {
|
|
3484
3152
|
const criticalScopeViolations = expediteModeEnabled
|
|
3485
|
-
? filteredViolations.filter((file) => isCriticalScopeBreach(file, 'File modified outside the plan'))
|
|
3153
|
+
? filteredViolations.filter((file) => (0, verify_output_1.isCriticalScopeBreach)(file, 'File modified outside the plan'))
|
|
3486
3154
|
: filteredViolations;
|
|
3487
3155
|
const expediteScopeViolations = expediteModeEnabled
|
|
3488
3156
|
? filteredViolations.filter((file) => !criticalScopeViolations.includes(file))
|
|
@@ -3601,7 +3269,7 @@ async function verifyCommand(options) {
|
|
|
3601
3269
|
}
|
|
3602
3270
|
}
|
|
3603
3271
|
if (governanceResult) {
|
|
3604
|
-
displayGovernanceInsights(governanceResult, { explain: options.explain });
|
|
3272
|
+
(0, verify_render_1.displayGovernanceInsights)(chalk, governanceResult, { explain: options.explain });
|
|
3605
3273
|
}
|
|
3606
3274
|
// ── Intent Status in scope-violation path ──────────────────────
|
|
3607
3275
|
if (intentEngineSummary) {
|
|
@@ -3670,7 +3338,7 @@ async function verifyCommand(options) {
|
|
|
3670
3338
|
console.log(chalk.yellow(` • ${file}`));
|
|
3671
3339
|
});
|
|
3672
3340
|
console.log(chalk.dim(' Follow-up checklist:'));
|
|
3673
|
-
EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
|
|
3341
|
+
verify_output_1.EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
|
|
3674
3342
|
console.log(chalk.dim(` - ${item}`));
|
|
3675
3343
|
});
|
|
3676
3344
|
console.log(chalk.dim(' Note: Expedite Mode used\n'));
|
|
@@ -3971,8 +3639,14 @@ async function verifyCommand(options) {
|
|
|
3971
3639
|
message: `Policy audit chain is invalid: ${auditIntegrityStatus.issues.join('; ') || 'unknown issue'}`,
|
|
3972
3640
|
});
|
|
3973
3641
|
}
|
|
3974
|
-
(
|
|
3975
|
-
|
|
3642
|
+
if (governanceResult?.driftIntelligence) {
|
|
3643
|
+
const driftViolations = driftFindingsToVerificationViolations(governanceResult.driftIntelligence);
|
|
3644
|
+
policyViolations.push(...driftViolations.filter((item) => !shouldIgnore(item.file)));
|
|
3645
|
+
}
|
|
3646
|
+
// Structural violations are passed to the canonical pipeline via payload.structuralViolations
|
|
3647
|
+
// (see line ~5281). Do NOT merge them into policyViolations — that would create structural:*
|
|
3648
|
+
// duplicates that contaminate the canonical finding graph.
|
|
3649
|
+
policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
|
|
3976
3650
|
const policyExceptionsSummary = {
|
|
3977
3651
|
sourceMode: policyExceptionResolution.mode,
|
|
3978
3652
|
sourceWarning: policyExceptionResolution.warning,
|
|
@@ -4114,7 +3788,7 @@ async function verifyCommand(options) {
|
|
|
4114
3788
|
message: violation,
|
|
4115
3789
|
}));
|
|
4116
3790
|
policyViolations.push(...intentProofPolicyViolations);
|
|
4117
|
-
policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
|
|
3791
|
+
policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
|
|
4118
3792
|
}
|
|
4119
3793
|
if (!options.json) {
|
|
4120
3794
|
if (intentProofSummary.pass) {
|
|
@@ -4193,7 +3867,7 @@ async function verifyCommand(options) {
|
|
|
4193
3867
|
});
|
|
4194
3868
|
}
|
|
4195
3869
|
else {
|
|
4196
|
-
displayChangeContractDrift(changeContractSummary, { advisory: false });
|
|
3870
|
+
(0, verify_render_1.displayChangeContractDrift)(chalk, changeContractSummary, { advisory: false });
|
|
4197
3871
|
}
|
|
4198
3872
|
await recordVerificationIfRequested(options, config, {
|
|
4199
3873
|
grade: 'F',
|
|
@@ -4217,7 +3891,7 @@ async function verifyCommand(options) {
|
|
|
4217
3891
|
exitWithEvidence(2);
|
|
4218
3892
|
}
|
|
4219
3893
|
else if (!changeContractEvaluation.valid && !options.json) {
|
|
4220
|
-
displayChangeContractDrift(changeContractSummary, { advisory: true });
|
|
3894
|
+
(0, verify_render_1.displayChangeContractDrift)(chalk, changeContractSummary, { advisory: true });
|
|
4221
3895
|
}
|
|
4222
3896
|
}
|
|
4223
3897
|
// Call verify API (or deterministic local evaluation for Plan Sync scope mode)
|
|
@@ -4451,9 +4125,50 @@ async function verifyCommand(options) {
|
|
|
4451
4125
|
: {}),
|
|
4452
4126
|
};
|
|
4453
4127
|
captureEvidencePayload(verifyEvidencePayload);
|
|
4128
|
+
const canonicalReplayChecksum = (() => {
|
|
4129
|
+
const direct = lastCanonicalOutput?.['replayChecksum'];
|
|
4130
|
+
if (typeof direct === 'string') {
|
|
4131
|
+
return direct;
|
|
4132
|
+
}
|
|
4133
|
+
const envelope = lastCanonicalOutput?.['governanceVerification'] ?? undefined;
|
|
4134
|
+
return typeof envelope?.replayChecksum === 'string' ? envelope.replayChecksum : null;
|
|
4135
|
+
})();
|
|
4136
|
+
const structuralBlocking = structuralViolations.filter(v => v.severity === 'BLOCKING').length;
|
|
4137
|
+
const structuralAdvisory = structuralViolations.filter(v => v.severity === 'ADVISORY').length;
|
|
4138
|
+
const deterministicSigs = structuralViolations.filter(v => v.determinism === 'deterministic-structural').length;
|
|
4139
|
+
const heuristicSigs = structuralViolations.filter(v => v.determinism === 'heuristic-advisory').length;
|
|
4140
|
+
const trustScore = structuralViolations.length > 0
|
|
4141
|
+
? Math.round((deterministicSigs / structuralViolations.length) * 100)
|
|
4142
|
+
: 100;
|
|
4143
|
+
const mainReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
|
|
4144
|
+
projectRoot,
|
|
4145
|
+
diffContext: `${options.base || 'HEAD'} vs working tree`,
|
|
4146
|
+
filesAnalyzed: diffFiles.length,
|
|
4147
|
+
planId: finalPlanId || null,
|
|
4148
|
+
verificationSource: verifySource,
|
|
4149
|
+
policyLockFingerprint: (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint || null,
|
|
4150
|
+
compiledPolicyFingerprint: effectiveCompiledPolicy?.fingerprint || null,
|
|
4151
|
+
ruleIds: structuralRulesApplied,
|
|
4152
|
+
blockingCount: policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking,
|
|
4153
|
+
advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory,
|
|
4154
|
+
suppressedCount: structuralSuppressedCount,
|
|
4155
|
+
structuralBlockingCount: structuralBlocking,
|
|
4156
|
+
structuralAdvisoryCount: structuralAdvisory,
|
|
4157
|
+
deterministicSignals: deterministicSigs,
|
|
4158
|
+
heuristicSignals: heuristicSigs,
|
|
4159
|
+
overallTrustScore: trustScore,
|
|
4160
|
+
verdict: effectiveVerdict,
|
|
4161
|
+
governanceDecision: governanceResult?.governanceDecision?.summary || 'automatic',
|
|
4162
|
+
actor: custodyActor,
|
|
4163
|
+
source: custodySource,
|
|
4164
|
+
replayChecksum: canonicalReplayChecksum,
|
|
4165
|
+
});
|
|
4166
|
+
applyCapturedReplayCustody(lastCanonicalOutput, mainReplayCustody);
|
|
4454
4167
|
// If JSON output requested, output JSON and exit
|
|
4455
4168
|
if (options.json) {
|
|
4456
|
-
emitVerifyJson(verifyEvidencePayload)
|
|
4169
|
+
emitVerifyJson(verifyEvidencePayload, (canonical) => {
|
|
4170
|
+
applyCapturedReplayCustody(canonical, mainReplayCustody);
|
|
4171
|
+
});
|
|
4457
4172
|
await recordVerificationIfRequested(options, config, {
|
|
4458
4173
|
grade,
|
|
4459
4174
|
violations: violations,
|
|
@@ -4487,7 +4202,7 @@ async function verifyCommand(options) {
|
|
|
4487
4202
|
// Display results (only if not in json mode; exclude ignored paths from bloat)
|
|
4488
4203
|
if (!options.json) {
|
|
4489
4204
|
const displayBloatFiles = (verifyResult.bloatFiles || []).filter((f) => !shouldIgnore(f));
|
|
4490
|
-
displayVerifyResults({
|
|
4205
|
+
(0, verify_render_1.displayVerifyResults)(chalk, {
|
|
4491
4206
|
...verifyResult,
|
|
4492
4207
|
verdict: effectiveVerdict,
|
|
4493
4208
|
message: effectiveMessage,
|
|
@@ -4495,7 +4210,7 @@ async function verifyCommand(options) {
|
|
|
4495
4210
|
bloatCount: displayBloatFiles.length,
|
|
4496
4211
|
}, policyViolations, expediteModeEnabled, intentEngineIssues, intentEngineSummary, intentEngineFlowIssues, intentEngineRegressions, structuralViolations);
|
|
4497
4212
|
if (governanceResult) {
|
|
4498
|
-
displayGovernanceInsights(governanceResult, { explain: options.explain });
|
|
4213
|
+
(0, verify_render_1.displayGovernanceInsights)(chalk, governanceResult, { explain: options.explain });
|
|
4499
4214
|
}
|
|
4500
4215
|
if (aiDebtSummary.mode !== 'off') {
|
|
4501
4216
|
const header = aiDebtSummary.mode === 'enforce'
|
|
@@ -4556,38 +4271,20 @@ async function verifyCommand(options) {
|
|
|
4556
4271
|
console.log(chalk.dim(' Intent proof repo scan reached configured file/byte limit (truncated).'));
|
|
4557
4272
|
}
|
|
4558
4273
|
}
|
|
4274
|
+
console.log(chalk.dim('\n── Verification contract (this run) ──'));
|
|
4275
|
+
console.log(chalk.dim(` Verify source: ${verifySource === 'local_fallback'
|
|
4276
|
+
? 'local deterministic fallback (verify API unavailable)'
|
|
4277
|
+
: 'verify API'}`));
|
|
4278
|
+
console.log(chalk.dim(` Structural findings: ${structuralViolations.length} ` +
|
|
4279
|
+
`(${structuralViolations.filter((v) => v.severity === 'BLOCKING').length} blocking, ` +
|
|
4280
|
+
`${structuralViolations.filter((v) => v.severity !== 'BLOCKING').length} advisory)`));
|
|
4281
|
+
console.log(chalk.dim(' Merge gates follow rule severity + policy: blocking structural findings are reproducible on this tree.'));
|
|
4282
|
+
console.log(chalk.dim(' Evidence trail: see `.neurcode/` on success (provenance, telemetry) — use `neurcode replay` / dashboard for audit parity.'));
|
|
4559
4283
|
}
|
|
4560
4284
|
// ── Governance Provenance Chain + Pilot Metrics ───────────────────────
|
|
4561
4285
|
// Best-effort: never throws, never changes the verification outcome.
|
|
4562
4286
|
try {
|
|
4563
|
-
|
|
4564
|
-
const structuralAdvisory = structuralViolations.filter(v => v.severity === 'ADVISORY').length;
|
|
4565
|
-
const deterministicSigs = structuralViolations.filter(v => v.determinism === 'deterministic-structural').length;
|
|
4566
|
-
const heuristicSigs = structuralViolations.filter(v => v.determinism === 'heuristic-advisory').length;
|
|
4567
|
-
const trustScore = structuralViolations.length > 0
|
|
4568
|
-
? Math.round((deterministicSigs / structuralViolations.length) * 100)
|
|
4569
|
-
: 100;
|
|
4570
|
-
const prov = (0, governance_provenance_1.buildProvenanceRecord)({
|
|
4571
|
-
repoRoot: projectRoot,
|
|
4572
|
-
filesAnalyzed: diffFiles.length,
|
|
4573
|
-
diffContext: `${options.base || 'HEAD'} vs working tree`,
|
|
4574
|
-
planId: finalPlanId || null,
|
|
4575
|
-
intentHash: null,
|
|
4576
|
-
policyHash: null,
|
|
4577
|
-
ruleIds: structuralRulesApplied,
|
|
4578
|
-
blockingCount: policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking,
|
|
4579
|
-
advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory,
|
|
4580
|
-
suppressedCount: structuralSuppressedCount,
|
|
4581
|
-
structuralBlockingCount: structuralBlocking,
|
|
4582
|
-
structuralAdvisoryCount: structuralAdvisory,
|
|
4583
|
-
deterministicSignals: deterministicSigs,
|
|
4584
|
-
heuristicSignals: heuristicSigs,
|
|
4585
|
-
overallTrustScore: trustScore,
|
|
4586
|
-
verdict: effectiveVerdict,
|
|
4587
|
-
governanceDecision: governanceResult?.governanceDecision?.summary || 'automatic',
|
|
4588
|
-
});
|
|
4589
|
-
(0, governance_provenance_1.saveProvenanceRecord)(projectRoot, prov);
|
|
4590
|
-
lastProvenanceRunId = prov.runId;
|
|
4287
|
+
lastProvenanceRunId = mainReplayCustody.provenanceRecord?.runId ?? lastProvenanceRunId;
|
|
4591
4288
|
// Tally per-rule counts for pilot metrics
|
|
4592
4289
|
const ruleCounts = {};
|
|
4593
4290
|
for (const v of structuralViolations) {
|
|
@@ -4598,8 +4295,8 @@ async function verifyCommand(options) {
|
|
|
4598
4295
|
verifyCount: 1,
|
|
4599
4296
|
passCount: effectiveVerdict === 'PASS' ? 1 : 0,
|
|
4600
4297
|
failCount: effectiveVerdict === 'FAIL' ? 1 : 0,
|
|
4601
|
-
blockingCaught:
|
|
4602
|
-
advisoryCaught:
|
|
4298
|
+
blockingCaught: mainReplayCustody.provenanceRecord?.blockingCount ?? (policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking),
|
|
4299
|
+
advisoryCaught: mainReplayCustody.provenanceRecord?.advisoryCount ?? (policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory),
|
|
4603
4300
|
suppressions: structuralSuppressedCount,
|
|
4604
4301
|
structuralCaught: structuralViolations.length,
|
|
4605
4302
|
aiDebtDelta: aiDebtSummary?.score ?? 0,
|
|
@@ -4721,7 +4418,7 @@ async function verifyCommand(options) {
|
|
|
4721
4418
|
catch (error) {
|
|
4722
4419
|
if (options.json) {
|
|
4723
4420
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4724
|
-
emitCanonicalVerifyJson({
|
|
4421
|
+
(0, verify_output_1.emitCanonicalVerifyJson)({
|
|
4725
4422
|
verdict: 'FAIL',
|
|
4726
4423
|
summary: {
|
|
4727
4424
|
totalFilesChanged: 0,
|
|
@@ -4972,6 +4669,8 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
|
|
|
4972
4669
|
suspiciousChange: governance.suspiciousChange,
|
|
4973
4670
|
changeJustification: governance.changeJustification,
|
|
4974
4671
|
governanceDecision: governance.governanceDecision,
|
|
4672
|
+
engineeringContext: governance.engineeringContext,
|
|
4673
|
+
driftIntelligence: governance.driftIntelligence,
|
|
4975
4674
|
aiChangeLog: {
|
|
4976
4675
|
path: governance.aiChangeLogPath,
|
|
4977
4676
|
auditPath: governance.aiChangeLogAuditPath,
|
|
@@ -4994,412 +4693,6 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
|
|
|
4994
4693
|
...(options?.aiDebt ? { aiDebt: options.aiDebt } : {}),
|
|
4995
4694
|
};
|
|
4996
4695
|
}
|
|
4997
|
-
function displayGovernanceInsights(governance, options = {}) {
|
|
4998
|
-
const maxUnexpectedFiles = options.maxUnexpectedFiles ?? 20;
|
|
4999
|
-
const decision = governance.governanceDecision;
|
|
5000
|
-
console.log(chalk.bold.white('\nBlast Radius:'));
|
|
5001
|
-
console.log(chalk.dim(` Files touched: ${governance.blastRadius.filesChanged}`));
|
|
5002
|
-
console.log(chalk.dim(` Functions impacted: ${governance.blastRadius.functionsAffected}`));
|
|
5003
|
-
console.log(chalk.dim(` Modules impacted: ${governance.blastRadius.modulesAffected.join(', ') || 'none'}`));
|
|
5004
|
-
if (governance.blastRadius.dependenciesAdded.length > 0) {
|
|
5005
|
-
console.log(chalk.dim(` Dependencies added: ${governance.blastRadius.dependenciesAdded.join(', ')}`));
|
|
5006
|
-
}
|
|
5007
|
-
console.log(chalk.dim(` Risk level: ${governance.blastRadius.riskScore.toUpperCase()}`));
|
|
5008
|
-
console.log(chalk.dim(` Governance decision: ${decision.decision.toUpperCase().replace('_', ' ')} | Avg relevance: ${decision.averageRelevanceScore}`));
|
|
5009
|
-
console.log(chalk.dim(` Policy source: ${governance.policySources.mode}${governance.policySources.orgPolicy ? ' (org + local)' : ' (local)'}`));
|
|
5010
|
-
console.log(governance.aiChangeLogIntegrity.valid
|
|
5011
|
-
? chalk.dim(` AI change-log integrity: valid (${governance.aiChangeLogIntegrity.signed ? 'signed' : 'unsigned'})`)
|
|
5012
|
-
: chalk.red(` AI change-log integrity: invalid (${governance.aiChangeLogIntegrity.issues.join('; ') || 'unknown'})`));
|
|
5013
|
-
if (governance.aiChangeLogIntegrity.signed) {
|
|
5014
|
-
const keyId = typeof governance.aiChangeLogIntegrity.keyId === 'string'
|
|
5015
|
-
? governance.aiChangeLogIntegrity.keyId
|
|
5016
|
-
: null;
|
|
5017
|
-
const verifiedWithKeyId = typeof governance.aiChangeLogIntegrity.verifiedWithKeyId === 'string'
|
|
5018
|
-
? governance.aiChangeLogIntegrity.verifiedWithKeyId
|
|
5019
|
-
: null;
|
|
5020
|
-
if (keyId || verifiedWithKeyId) {
|
|
5021
|
-
console.log(chalk.dim(` Signing key: ${keyId || 'n/a'}${verifiedWithKeyId ? ` (verified via ${verifiedWithKeyId})` : ''}`));
|
|
5022
|
-
}
|
|
5023
|
-
}
|
|
5024
|
-
if (governance.suspiciousChange.flagged) {
|
|
5025
|
-
console.log(chalk.red('\nSuspicious Change Detected'));
|
|
5026
|
-
console.log(chalk.red(` Plan expected files: ${governance.suspiciousChange.expectedFiles} | AI modified files: ${governance.suspiciousChange.actualFiles}`));
|
|
5027
|
-
governance.suspiciousChange.unexpectedFiles.slice(0, maxUnexpectedFiles).forEach((filePath) => {
|
|
5028
|
-
console.log(chalk.red(` • ${filePath}`));
|
|
5029
|
-
});
|
|
5030
|
-
console.log(chalk.red(` Confidence: ${governance.suspiciousChange.confidence}`));
|
|
5031
|
-
}
|
|
5032
|
-
if (decision.lowRelevanceFiles.length > 0) {
|
|
5033
|
-
console.log(chalk.yellow('\nLow Relevance Files'));
|
|
5034
|
-
decision.lowRelevanceFiles.slice(0, 10).forEach((item) => {
|
|
5035
|
-
console.log(chalk.yellow(` • ${item.file} (score ${item.relevanceScore}, ${item.planLink.replace('_', ' ')})`));
|
|
5036
|
-
});
|
|
5037
|
-
}
|
|
5038
|
-
if (options.explain) {
|
|
5039
|
-
console.log(chalk.bold.white('\nAI Change Justification:'));
|
|
5040
|
-
console.log(chalk.dim(` Task: ${governance.changeJustification.task}`));
|
|
5041
|
-
governance.changeJustification.changes.forEach((item) => {
|
|
5042
|
-
const relevance = typeof item.relevanceScore === 'number' ? ` [score ${item.relevanceScore}]` : '';
|
|
5043
|
-
console.log(chalk.dim(` • ${item.file} — ${item.reason}${relevance}`));
|
|
5044
|
-
});
|
|
5045
|
-
}
|
|
5046
|
-
}
|
|
5047
|
-
function displayChangeContractDrift(summary, options = { advisory: false }) {
|
|
5048
|
-
const groups = (0, change_contract_1.groupChangeContractViolations)(summary.violations.map((item) => ({
|
|
5049
|
-
code: item.code,
|
|
5050
|
-
message: item.message,
|
|
5051
|
-
...(item.file ? { file: item.file } : {}),
|
|
5052
|
-
...(item.symbol ? { symbol: item.symbol } : {}),
|
|
5053
|
-
...(item.symbolType ? { symbolType: item.symbolType } : {}),
|
|
5054
|
-
...(item.expected ? { expected: item.expected } : {}),
|
|
5055
|
-
...(item.actual ? { actual: item.actual } : {}),
|
|
5056
|
-
})));
|
|
5057
|
-
if (groups.length === 0)
|
|
5058
|
-
return;
|
|
5059
|
-
const maxItemsPerGroup = options.maxItemsPerGroup ?? 12;
|
|
5060
|
-
const header = options.advisory
|
|
5061
|
-
? chalk.yellow('\nWARN ⚠️ Change contract drift detected')
|
|
5062
|
-
: chalk.red('\nFAIL ❌ Change contract enforcement failed');
|
|
5063
|
-
console.log(header);
|
|
5064
|
-
for (const group of groups) {
|
|
5065
|
-
console.log(chalk.white(`\n${group.title}:`));
|
|
5066
|
-
group.items.slice(0, maxItemsPerGroup).forEach((entry) => {
|
|
5067
|
-
console.log(` - ${entry}`);
|
|
5068
|
-
});
|
|
5069
|
-
if (group.items.length > maxItemsPerGroup) {
|
|
5070
|
-
console.log(chalk.dim(` - ... ${group.items.length - maxItemsPerGroup} more`));
|
|
5071
|
-
}
|
|
5072
|
-
console.log(chalk.dim(` Why it matters: ${group.impact}`));
|
|
5073
|
-
}
|
|
5074
|
-
console.log(chalk.dim('\nSummary:'));
|
|
5075
|
-
console.log(chalk.dim('Implementation deviates from intended contract.'));
|
|
5076
|
-
console.log(chalk.dim(`Contract path: ${summary.path}`));
|
|
5077
|
-
}
|
|
5078
|
-
/**
|
|
5079
|
-
* Display verification results in a formatted report card
|
|
5080
|
-
*/
|
|
5081
|
-
function displayVerifyResults(result, policyViolations, expediteModeUsed = false, intentIssuesForDisplay = [], intentSummaryForDisplay = null, flowIssuesForDisplay = [], regressionsForDisplay = [], structuralViolationsForDisplay = []) {
|
|
5082
|
-
// ── Header ────────────────────────────────────────────────────────────────
|
|
5083
|
-
const headerLabel = result.verdict === 'PASS'
|
|
5084
|
-
? chalk.bold.green('\n✅ VERIFICATION PASSED')
|
|
5085
|
-
: result.verdict === 'WARN'
|
|
5086
|
-
? chalk.bold.yellow('\n⚠️ VERIFICATION PASSED WITH WARNINGS')
|
|
5087
|
-
: chalk.bold.red('\n❌ VERIFICATION FAILED');
|
|
5088
|
-
console.log(headerLabel);
|
|
5089
|
-
// ── Intent Status block ──────────────────────────────────────────────────
|
|
5090
|
-
if (intentSummaryForDisplay) {
|
|
5091
|
-
const s = intentSummaryForDisplay;
|
|
5092
|
-
const domainLabel = s.domain.charAt(0).toUpperCase() + s.domain.slice(1);
|
|
5093
|
-
const confColor = s.confidence === 'HIGH'
|
|
5094
|
-
? chalk.green
|
|
5095
|
-
: s.confidence === 'MEDIUM'
|
|
5096
|
-
? chalk.yellow
|
|
5097
|
-
: chalk.red;
|
|
5098
|
-
// V4: weighted coverage bar
|
|
5099
|
-
const wCovPct = s.weightedCoverage != null
|
|
5100
|
-
? Math.round(s.weightedCoverage * 100)
|
|
5101
|
-
: s.coveragePct;
|
|
5102
|
-
const barWidth = 20;
|
|
5103
|
-
const filled = Math.round((wCovPct / 100) * barWidth);
|
|
5104
|
-
const bar = chalk.cyan('█'.repeat(filled)) + chalk.dim('░'.repeat(barWidth - filled));
|
|
5105
|
-
// V4: system status label
|
|
5106
|
-
const sysStatus = s.status;
|
|
5107
|
-
const statusLabel = sysStatus === 'CRITICAL'
|
|
5108
|
-
? chalk.bold.red('[CRITICAL]')
|
|
5109
|
-
: sysStatus === 'AT RISK'
|
|
5110
|
-
? chalk.bold.yellow('[AT RISK]')
|
|
5111
|
-
: chalk.bold.green('[SECURE]');
|
|
5112
|
-
console.log(chalk.bold('\n━━━ INTENT STATUS ━━━━━━━━━━━━━━━━━━━━━━'));
|
|
5113
|
-
console.log(` ${statusLabel} ${chalk.bold(`${domainLabel} Implementation:`)} ${bar} ${chalk.bold(`${wCovPct}%`)} (weighted)`);
|
|
5114
|
-
console.log(` Confidence: ${confColor(s.confidence)}`);
|
|
5115
|
-
if (s.foundList.length > 0) {
|
|
5116
|
-
const foundLabels = s.foundList
|
|
5117
|
-
.map((k) => k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' '))
|
|
5118
|
-
.slice(0, 4);
|
|
5119
|
-
console.log(` Found: ${chalk.green(foundLabels.join(', '))}${s.foundList.length > 4 ? chalk.dim(` +${s.foundList.length - 4} more`) : ''}`);
|
|
5120
|
-
}
|
|
5121
|
-
// V4: show critical missing and non-critical missing separately
|
|
5122
|
-
const critMissing = s.criticalMissing ?? [];
|
|
5123
|
-
const otherMissing = s.missing.filter((k) => !critMissing.includes(k));
|
|
5124
|
-
if (critMissing.length > 0) {
|
|
5125
|
-
console.log(` ${chalk.bold.red('Critical missing:')}`);
|
|
5126
|
-
critMissing.forEach((k) => {
|
|
5127
|
-
const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
5128
|
-
console.log(chalk.red(` ✗ ${label}`));
|
|
5129
|
-
});
|
|
5130
|
-
}
|
|
5131
|
-
if (otherMissing.length > 0) {
|
|
5132
|
-
console.log(` ${chalk.bold.yellow('Missing:')}`);
|
|
5133
|
-
otherMissing.forEach((k) => {
|
|
5134
|
-
const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
5135
|
-
console.log(chalk.yellow(` • ${label}`));
|
|
5136
|
-
});
|
|
5137
|
-
}
|
|
5138
|
-
if (critMissing.length === 0 && otherMissing.length === 0) {
|
|
5139
|
-
console.log(` Missing: ${chalk.green('none — all components detected')}`);
|
|
5140
|
-
}
|
|
5141
|
-
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
5142
|
-
}
|
|
5143
|
-
// ── Triage items ──────────────────────────────────────────────────────────
|
|
5144
|
-
const maxBlockingItems = 20;
|
|
5145
|
-
const maxAdvisoryItems = 8;
|
|
5146
|
-
const maxExpediteItems = 12;
|
|
5147
|
-
const policyItems = policyViolations || [];
|
|
5148
|
-
const isBlockingSeverity = (severityRaw) => {
|
|
5149
|
-
const normalized = String(severityRaw || '').toLowerCase();
|
|
5150
|
-
return normalized === 'block' || normalized === 'critical' || normalized === 'high';
|
|
5151
|
-
};
|
|
5152
|
-
const scopeItems = result.bloatFiles.map((file) => ({
|
|
5153
|
-
file,
|
|
5154
|
-
message: 'File modified outside intended scope',
|
|
5155
|
-
policy: 'scope_guard',
|
|
5156
|
-
}));
|
|
5157
|
-
const policyTriageItems = policyItems.map((item) => ({
|
|
5158
|
-
file: item.file,
|
|
5159
|
-
message: item.message || item.rule,
|
|
5160
|
-
policy: item.rule || 'policy_violation',
|
|
5161
|
-
severity: item.severity,
|
|
5162
|
-
}));
|
|
5163
|
-
// Structural rule violations — split by severity
|
|
5164
|
-
const structuralBlocking = structuralViolationsForDisplay
|
|
5165
|
-
.filter((v) => v.severity === 'BLOCKING')
|
|
5166
|
-
.map((v) => ({
|
|
5167
|
-
file: v.filePath,
|
|
5168
|
-
message: `${v.ruleId} · ${v.ruleName} (line ${v.line}) — ${v.operationalRisk}`,
|
|
5169
|
-
}));
|
|
5170
|
-
const structuralAdvisory = structuralViolationsForDisplay
|
|
5171
|
-
.filter((v) => v.severity === 'ADVISORY')
|
|
5172
|
-
.map((v) => ({
|
|
5173
|
-
file: v.filePath,
|
|
5174
|
-
message: `${v.ruleId} · ${v.ruleName} (line ${v.line}) — ${v.operationalRisk}`,
|
|
5175
|
-
}));
|
|
5176
|
-
let blockingItems = [
|
|
5177
|
-
...scopeItems.map((item) => ({
|
|
5178
|
-
file: item.file,
|
|
5179
|
-
message: item.message,
|
|
5180
|
-
})),
|
|
5181
|
-
...policyTriageItems
|
|
5182
|
-
.filter((item) => isBlockingSeverity(item.severity))
|
|
5183
|
-
.map((item) => ({
|
|
5184
|
-
file: item.file,
|
|
5185
|
-
message: item.message,
|
|
5186
|
-
})),
|
|
5187
|
-
...structuralBlocking,
|
|
5188
|
-
];
|
|
5189
|
-
let advisoryItems = [
|
|
5190
|
-
...policyTriageItems
|
|
5191
|
-
.filter((item) => !isBlockingSeverity(item.severity))
|
|
5192
|
-
.map((item) => ({
|
|
5193
|
-
file: item.file,
|
|
5194
|
-
message: item.message,
|
|
5195
|
-
})),
|
|
5196
|
-
...structuralAdvisory,
|
|
5197
|
-
];
|
|
5198
|
-
let expediteItems = [];
|
|
5199
|
-
if (expediteModeUsed) {
|
|
5200
|
-
blockingItems = [
|
|
5201
|
-
...scopeItems
|
|
5202
|
-
.filter((item) => isCriticalScopeBreach(item.file, item.message))
|
|
5203
|
-
.map((item) => ({ file: item.file, message: item.message })),
|
|
5204
|
-
...policyTriageItems
|
|
5205
|
-
.filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
|
|
5206
|
-
.map((item) => ({ file: item.file, message: item.message })),
|
|
5207
|
-
];
|
|
5208
|
-
expediteItems = [
|
|
5209
|
-
...scopeItems
|
|
5210
|
-
.filter((item) => !isCriticalScopeBreach(item.file, item.message))
|
|
5211
|
-
.map((item) => ({ file: item.file, message: item.message })),
|
|
5212
|
-
...policyTriageItems
|
|
5213
|
-
.filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
|
|
5214
|
-
.map((item) => ({ file: item.file, message: item.message })),
|
|
5215
|
-
];
|
|
5216
|
-
advisoryItems = [];
|
|
5217
|
-
}
|
|
5218
|
-
// ── Counts ────────────────────────────────────────────────────────────────
|
|
5219
|
-
console.log(blockingItems.length > 0
|
|
5220
|
-
? chalk.red(`Blocking Issues: ${blockingItems.length}`)
|
|
5221
|
-
: chalk.dim('Blocking Issues: 0'));
|
|
5222
|
-
if (expediteModeUsed) {
|
|
5223
|
-
console.log(chalk.yellow(`Expedite Issues: ${expediteItems.length}`));
|
|
5224
|
-
}
|
|
5225
|
-
else {
|
|
5226
|
-
console.log(advisoryItems.length > 0
|
|
5227
|
-
? chalk.yellow(`Advisory Issues: ${advisoryItems.length}`)
|
|
5228
|
-
: chalk.dim('Advisory Issues: 0'));
|
|
5229
|
-
}
|
|
5230
|
-
console.log(chalk.dim(`Plan adherence: ${result.plannedFilesModified}/${result.totalPlannedFiles} files (${result.adherenceScore}%)`));
|
|
5231
|
-
// ── Top issues ────────────────────────────────────────────────────────────
|
|
5232
|
-
const topIssues = [
|
|
5233
|
-
...blockingItems,
|
|
5234
|
-
...(expediteModeUsed ? expediteItems : advisoryItems),
|
|
5235
|
-
].slice(0, 2);
|
|
5236
|
-
if (topIssues.length > 0) {
|
|
5237
|
-
console.log(chalk.bold('\nTop Issues:'));
|
|
5238
|
-
topIssues.forEach((item, i) => {
|
|
5239
|
-
console.log(` ${i + 1}. ${item.message} → ${chalk.cyan(item.file)}`);
|
|
5240
|
-
});
|
|
5241
|
-
}
|
|
5242
|
-
// ── Detailed lists ────────────────────────────────────────────────────────
|
|
5243
|
-
if (blockingItems.length > 0) {
|
|
5244
|
-
console.log(chalk.red(`\nBLOCKING (${blockingItems.length})`));
|
|
5245
|
-
blockingItems.slice(0, maxBlockingItems).forEach((item) => {
|
|
5246
|
-
console.log(` - ${item.file}: ${item.message}`);
|
|
5247
|
-
});
|
|
5248
|
-
if (blockingItems.length > maxBlockingItems) {
|
|
5249
|
-
console.log(chalk.dim(` - ... ${blockingItems.length - maxBlockingItems} more`));
|
|
5250
|
-
}
|
|
5251
|
-
}
|
|
5252
|
-
if (advisoryItems.length > 0) {
|
|
5253
|
-
console.log(chalk.yellow(`\nADVISORY (${advisoryItems.length})`));
|
|
5254
|
-
advisoryItems.slice(0, maxAdvisoryItems).forEach((item) => {
|
|
5255
|
-
console.log(` - ${item.file}: ${item.message}`);
|
|
5256
|
-
});
|
|
5257
|
-
if (advisoryItems.length > maxAdvisoryItems) {
|
|
5258
|
-
console.log(chalk.dim(` - ... ${advisoryItems.length - maxAdvisoryItems} more (summarized)`));
|
|
5259
|
-
}
|
|
5260
|
-
}
|
|
5261
|
-
if (expediteModeUsed && expediteItems.length > 0) {
|
|
5262
|
-
console.log(chalk.yellow(`\nEXPEDITE (requires follow-up) (${expediteItems.length})`));
|
|
5263
|
-
expediteItems.slice(0, maxExpediteItems).forEach((item) => {
|
|
5264
|
-
console.log(` - ${item.file}: ${item.message}`);
|
|
5265
|
-
});
|
|
5266
|
-
if (expediteItems.length > maxExpediteItems) {
|
|
5267
|
-
console.log(chalk.dim(` - ... ${expediteItems.length - maxExpediteItems} more (summarized)`));
|
|
5268
|
-
}
|
|
5269
|
-
console.log(chalk.dim(' Follow-up checklist:'));
|
|
5270
|
-
EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((checkItem) => {
|
|
5271
|
-
console.log(chalk.dim(` - ${checkItem}`));
|
|
5272
|
-
});
|
|
5273
|
-
console.log(chalk.dim(' Note: Expedite Mode used'));
|
|
5274
|
-
}
|
|
5275
|
-
// ── Intent issues ─────────────────────────────────────────────────────────
|
|
5276
|
-
if (intentIssuesForDisplay.length > 0) {
|
|
5277
|
-
console.log(chalk.magenta(`\nINTENT ISSUES (${intentIssuesForDisplay.length})`));
|
|
5278
|
-
intentIssuesForDisplay.forEach((issue) => {
|
|
5279
|
-
const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
|
|
5280
|
-
const typeLabel = issue.type === 'missing' ? 'Missing' : issue.type === 'misplaced' ? 'Misplaced' : 'Partial';
|
|
5281
|
-
console.log(` ${label} ${typeLabel}: ${issue.message}`);
|
|
5282
|
-
});
|
|
5283
|
-
}
|
|
5284
|
-
// ── Flow Validation ───────────────────────────────────────────────────────
|
|
5285
|
-
if (flowIssuesForDisplay.length > 0) {
|
|
5286
|
-
console.log(chalk.bold('\n━━━ FLOW VALIDATION ━━━━━━━━━━━━━━━━━━━━━'));
|
|
5287
|
-
flowIssuesForDisplay.forEach((issue) => {
|
|
5288
|
-
const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
|
|
5289
|
-
const typeIcon = issue.type === 'missing-flow' ? '⛓' : issue.type === 'misplaced-flow' ? '⚠' : '⊘';
|
|
5290
|
-
console.log(` ${label} ${typeIcon} ${issue.message}`);
|
|
5291
|
-
if (issue.files && issue.files.length > 0) {
|
|
5292
|
-
const display = issue.files.slice(0, 3);
|
|
5293
|
-
console.log(chalk.dim(` → ${display.join(', ')}${issue.files.length > 3 ? ` +${issue.files.length - 3} more` : ''}`));
|
|
5294
|
-
}
|
|
5295
|
-
});
|
|
5296
|
-
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
5297
|
-
}
|
|
5298
|
-
// ── Regression Analysis ───────────────────────────────────────────────────
|
|
5299
|
-
if (regressionsForDisplay.length > 0) {
|
|
5300
|
-
console.log(chalk.bold.red('\n━━━ REGRESSION ANALYSIS ━━━━━━━━━━━━━━━━━'));
|
|
5301
|
-
regressionsForDisplay.forEach((reg) => {
|
|
5302
|
-
const icon = reg.type === 'coverage-regression' ? '📉' :
|
|
5303
|
-
reg.type === 'critical-regression' ? '🔴' :
|
|
5304
|
-
reg.type === 'flow-regression' ? '⛓' : '⚠';
|
|
5305
|
-
console.log(` ${chalk.red('[REGRESSION]')} ${icon} ${reg.message}`);
|
|
5306
|
-
});
|
|
5307
|
-
console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
5308
|
-
}
|
|
5309
|
-
// ── Structural Rule Engine ─────────────────────────────────────────────────
|
|
5310
|
-
// Display detailed structural violations with AST evidence and determinism labels.
|
|
5311
|
-
if (structuralViolationsForDisplay.length > 0) {
|
|
5312
|
-
try {
|
|
5313
|
-
const report = (0, explainability_1.buildViolationReport)(structuralViolationsForDisplay, '');
|
|
5314
|
-
const formatter = new explainability_1.ViolationFormatter();
|
|
5315
|
-
const blocking = report.blocking;
|
|
5316
|
-
const advisory = report.advisory;
|
|
5317
|
-
if (blocking.length > 0 || advisory.length > 0) {
|
|
5318
|
-
console.log(chalk.bold('\n━━━ STRUCTURAL ANALYSIS ━━━━━━━━━━━━━━━━━'));
|
|
5319
|
-
blocking.slice(0, 5).forEach((v) => {
|
|
5320
|
-
const detLabel = v.determinism === 'deterministic-structural'
|
|
5321
|
-
? chalk.cyan('⚙ AST-verified')
|
|
5322
|
-
: chalk.yellow('⚡ heuristic');
|
|
5323
|
-
console.log(chalk.red(`\n ● ${v.ruleId} [BLOCKING] ${detLabel} · confidence ${Math.round(v.confidence * 100)}%`));
|
|
5324
|
-
console.log(chalk.bold(` ${v.filePath}:${v.line}`));
|
|
5325
|
-
console.log(chalk.dim(` Pattern: ${v.evidence.matchReason}`));
|
|
5326
|
-
if (v.evidence.codeSnippet) {
|
|
5327
|
-
console.log(chalk.dim(` Code: ${v.evidence.codeSnippet.slice(0, 100)}`));
|
|
5328
|
-
}
|
|
5329
|
-
console.log(chalk.yellow(` Risk: ${v.operationalRisk}`));
|
|
5330
|
-
console.log(chalk.green(` Fix: ${v.remediation}`));
|
|
5331
|
-
});
|
|
5332
|
-
if (blocking.length > 5) {
|
|
5333
|
-
console.log(chalk.dim(`\n ... ${blocking.length - 5} more blocking structural violations`));
|
|
5334
|
-
}
|
|
5335
|
-
advisory.slice(0, 3).forEach((v) => {
|
|
5336
|
-
console.log(chalk.yellow(`\n ○ ${v.ruleId} [ADVISORY] ⚡ heuristic · confidence ${Math.round(v.confidence * 100)}%`));
|
|
5337
|
-
console.log(chalk.dim(` ${v.filePath}:${v.line} — ${v.operationalRisk}`));
|
|
5338
|
-
});
|
|
5339
|
-
if (advisory.length > 3) {
|
|
5340
|
-
console.log(chalk.dim(` ... ${advisory.length - 3} more advisory structural violations`));
|
|
5341
|
-
}
|
|
5342
|
-
const deterministicCount = report.deterministicCount;
|
|
5343
|
-
const heuristicCount = report.heuristicCount;
|
|
5344
|
-
console.log(chalk.dim(`\n Determinism: ${deterministicCount} AST-verified · ${heuristicCount} heuristic`));
|
|
5345
|
-
console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
5346
|
-
}
|
|
5347
|
-
}
|
|
5348
|
-
catch {
|
|
5349
|
-
// Non-fatal: explainability rendering must never break verification display
|
|
5350
|
-
}
|
|
5351
|
-
}
|
|
5352
|
-
const hasAnyIssue = blockingItems.length > 0 ||
|
|
5353
|
-
advisoryItems.length > 0 ||
|
|
5354
|
-
expediteItems.length > 0 ||
|
|
5355
|
-
intentIssuesForDisplay.length > 0 ||
|
|
5356
|
-
flowIssuesForDisplay.length > 0 ||
|
|
5357
|
-
regressionsForDisplay.length > 0;
|
|
5358
|
-
if (!hasAnyIssue) {
|
|
5359
|
-
console.log(chalk.green('\nNo issues detected.'));
|
|
5360
|
-
}
|
|
5361
|
-
// ── Next step ─────────────────────────────────────────────────────────────
|
|
5362
|
-
if (hasAnyIssue) {
|
|
5363
|
-
console.log(chalk.bold('\nNext step:'));
|
|
5364
|
-
console.log(` ${chalk.cyan('neurcode fix')}`);
|
|
5365
|
-
console.log(chalk.dim(' or: neurcode fix --apply-safe (auto-apply high-confidence patches)'));
|
|
5366
|
-
}
|
|
5367
|
-
console.log(chalk.dim(`\nDetails: ${result.message}\n`));
|
|
5368
|
-
}
|
|
5369
|
-
function printFirstRunAdvisoryMessage(demoMode) {
|
|
5370
|
-
console.log(chalk.cyan('\nNeurcode first-run advisory mode'));
|
|
5371
|
-
console.log(chalk.dim('Neurcode checks if your AI-generated code matches your intended plan.'));
|
|
5372
|
-
console.log(chalk.dim('To get full enforcement:'));
|
|
5373
|
-
console.log(chalk.dim('1. Define a plan'));
|
|
5374
|
-
console.log(chalk.dim('2. Generate a contract'));
|
|
5375
|
-
console.log(chalk.dim('Running in advisory mode for now.\n'));
|
|
5376
|
-
if (demoMode) {
|
|
5377
|
-
console.log(chalk.dim('Demo mode: this run is intentionally non-blocking to make evaluation easy.'));
|
|
5378
|
-
}
|
|
5379
|
-
}
|
|
5380
|
-
function printAdvisorySignals(signals, demoMode) {
|
|
5381
|
-
if (signals.length === 0) {
|
|
5382
|
-
if (demoMode) {
|
|
5383
|
-
console.log(chalk.dim('No high-signal advisory findings detected for this diff.'));
|
|
5384
|
-
}
|
|
5385
|
-
return;
|
|
5386
|
-
}
|
|
5387
|
-
console.log(chalk.yellow('\nAdvisory findings (non-blocking):'));
|
|
5388
|
-
for (const signal of signals) {
|
|
5389
|
-
const severityLabel = signal.severity === 'warn' ? chalk.yellow('[warn]') : chalk.dim('[info]');
|
|
5390
|
-
console.log(`${severityLabel} ${signal.title}`);
|
|
5391
|
-
console.log(chalk.dim(` ${signal.detail}`));
|
|
5392
|
-
console.log(chalk.dim(` Confidence: ${signal.confidence.toUpperCase()} (advisory-only)`));
|
|
5393
|
-
if (signal.evidence.length > 0) {
|
|
5394
|
-
console.log(chalk.dim(` Evidence: ${signal.evidence.join(', ')}`));
|
|
5395
|
-
}
|
|
5396
|
-
console.log(chalk.dim(` Structural gap: ${signal.structuralCoverageGap}`));
|
|
5397
|
-
console.log(chalk.dim(` Uncertainty: ${signal.uncertainty}`));
|
|
5398
|
-
signal.files.forEach((file) => {
|
|
5399
|
-
console.log(chalk.dim(` - ${file}`));
|
|
5400
|
-
});
|
|
5401
|
-
}
|
|
5402
|
-
}
|
|
5403
4696
|
function buildMinimalAdvisoryContractFromDiff(diffFiles, fallbackPlanId) {
|
|
5404
4697
|
const expectedFiles = [...new Set(diffFiles.map((file) => toUnixPath(file.path)).filter(Boolean))];
|
|
5405
4698
|
const planFiles = expectedFiles.map((path) => {
|