@kernlang/review 3.3.8 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/cache.js +1 -1
  2. package/dist/call-graph.d.ts +10 -0
  3. package/dist/call-graph.js +138 -9
  4. package/dist/call-graph.js.map +1 -1
  5. package/dist/concept-rules/auth-drift.js +2 -0
  6. package/dist/concept-rules/auth-drift.js.map +1 -1
  7. package/dist/concept-rules/auth-propagation-drift.d.ts +10 -0
  8. package/dist/concept-rules/auth-propagation-drift.js +85 -0
  9. package/dist/concept-rules/auth-propagation-drift.js.map +1 -0
  10. package/dist/concept-rules/body-shape-drift.d.ts +32 -0
  11. package/dist/concept-rules/body-shape-drift.js +98 -0
  12. package/dist/concept-rules/body-shape-drift.js.map +1 -0
  13. package/dist/concept-rules/contract-drift.js +3 -1
  14. package/dist/concept-rules/contract-drift.js.map +1 -1
  15. package/dist/concept-rules/contract-method-drift.js +2 -0
  16. package/dist/concept-rules/contract-method-drift.js.map +1 -1
  17. package/dist/concept-rules/cross-stack-utils.d.ts +24 -0
  18. package/dist/concept-rules/cross-stack-utils.js +123 -29
  19. package/dist/concept-rules/cross-stack-utils.js.map +1 -1
  20. package/dist/concept-rules/index.d.ts +4 -2
  21. package/dist/concept-rules/index.js +22 -3
  22. package/dist/concept-rules/index.js.map +1 -1
  23. package/dist/concept-rules/mutation-without-idempotency.d.ts +10 -0
  24. package/dist/concept-rules/mutation-without-idempotency.js +47 -0
  25. package/dist/concept-rules/mutation-without-idempotency.js.map +1 -0
  26. package/dist/concept-rules/request-validation-drift.d.ts +11 -0
  27. package/dist/concept-rules/request-validation-drift.js +99 -0
  28. package/dist/concept-rules/request-validation-drift.js.map +1 -0
  29. package/dist/concept-rules/root-cause.d.ts +4 -0
  30. package/dist/concept-rules/root-cause.js +31 -0
  31. package/dist/concept-rules/root-cause.js.map +1 -0
  32. package/dist/concept-rules/unbounded-collection-query.d.ts +10 -0
  33. package/dist/concept-rules/unbounded-collection-query.js +58 -0
  34. package/dist/concept-rules/unbounded-collection-query.js.map +1 -0
  35. package/dist/concept-rules/unhandled-api-error-shape.d.ts +10 -0
  36. package/dist/concept-rules/unhandled-api-error-shape.js +59 -0
  37. package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -0
  38. package/dist/default-export.d.ts +41 -0
  39. package/dist/default-export.js +76 -0
  40. package/dist/default-export.js.map +1 -0
  41. package/dist/eval.d.ts +67 -0
  42. package/dist/eval.js +177 -0
  43. package/dist/eval.js.map +1 -0
  44. package/dist/external-tools.js +52 -3
  45. package/dist/external-tools.js.map +1 -1
  46. package/dist/file-context.js +32 -13
  47. package/dist/file-context.js.map +1 -1
  48. package/dist/file-role.d.ts +6 -0
  49. package/dist/file-role.js +27 -0
  50. package/dist/file-role.js.map +1 -1
  51. package/dist/framework-seeds.d.ts +46 -0
  52. package/dist/framework-seeds.js +245 -0
  53. package/dist/framework-seeds.js.map +1 -0
  54. package/dist/git-env.d.ts +1 -0
  55. package/dist/git-env.js +25 -0
  56. package/dist/git-env.js.map +1 -0
  57. package/dist/graph.js +246 -21
  58. package/dist/graph.js.map +1 -1
  59. package/dist/index.d.ts +12 -3
  60. package/dist/index.js +314 -96
  61. package/dist/index.js.map +1 -1
  62. package/dist/mappers/ts-concepts.js +730 -1
  63. package/dist/mappers/ts-concepts.js.map +1 -1
  64. package/dist/path-canonical.d.ts +34 -0
  65. package/dist/path-canonical.js +85 -0
  66. package/dist/path-canonical.js.map +1 -0
  67. package/dist/policy.d.ts +22 -0
  68. package/dist/policy.js +47 -0
  69. package/dist/policy.js.map +1 -0
  70. package/dist/project-context.d.ts +135 -0
  71. package/dist/project-context.js +563 -0
  72. package/dist/project-context.js.map +1 -0
  73. package/dist/public-api.d.ts +21 -0
  74. package/dist/public-api.js +17 -2
  75. package/dist/public-api.js.map +1 -1
  76. package/dist/python-fallback.d.ts +2 -0
  77. package/dist/python-fallback.js +506 -0
  78. package/dist/python-fallback.js.map +1 -0
  79. package/dist/reporter.js +106 -1
  80. package/dist/reporter.js.map +1 -1
  81. package/dist/rule-quality.d.ts +58 -0
  82. package/dist/rule-quality.js +357 -0
  83. package/dist/rule-quality.js.map +1 -0
  84. package/dist/rules/base.js +21 -3
  85. package/dist/rules/base.js.map +1 -1
  86. package/dist/rules/dead-code.d.ts +2 -2
  87. package/dist/rules/dead-code.js +88 -4
  88. package/dist/rules/dead-code.js.map +1 -1
  89. package/dist/rules/index.d.ts +22 -0
  90. package/dist/rules/index.js +72 -0
  91. package/dist/rules/index.js.map +1 -1
  92. package/dist/rules/kern-source.d.ts +4 -0
  93. package/dist/rules/kern-source.js +184 -0
  94. package/dist/rules/kern-source.js.map +1 -1
  95. package/dist/rules/react.js +52 -3
  96. package/dist/rules/react.js.map +1 -1
  97. package/dist/rules/suggest-kern-primitive.js +0 -1
  98. package/dist/rules/suggest-kern-primitive.js.map +1 -1
  99. package/dist/semantic-diff.js +2 -0
  100. package/dist/semantic-diff.js.map +1 -1
  101. package/dist/suppression/apply-suppression.js +2 -0
  102. package/dist/suppression/apply-suppression.js.map +1 -1
  103. package/dist/suppression/parse-directives.d.ts +13 -5
  104. package/dist/suppression/parse-directives.js +62 -8
  105. package/dist/suppression/parse-directives.js.map +1 -1
  106. package/dist/suppression/types.d.ts +9 -0
  107. package/dist/suppression/types.js +6 -1
  108. package/dist/suppression/types.js.map +1 -1
  109. package/dist/taint-crossfile.js +15 -8
  110. package/dist/taint-crossfile.js.map +1 -1
  111. package/dist/telemetry.d.ts +126 -0
  112. package/dist/telemetry.js +303 -0
  113. package/dist/telemetry.js.map +1 -0
  114. package/dist/types.d.ts +172 -2
  115. package/dist/types.js.map +1 -1
  116. package/package.json +4 -3
@@ -0,0 +1,303 @@
1
+ import { createHash } from 'crypto';
2
+ import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import { dirname, resolve } from 'path';
4
+ import { inferReviewPolicy } from './policy.js';
5
+ import { getRuleQualityProfile } from './rule-quality.js';
6
+ export function buildReviewTelemetry(reports, options = {}) {
7
+ const findings = reports.flatMap((report) => report.findings);
8
+ const suppressed = reports.flatMap((report) => report.suppressedFindings ?? []);
9
+ const allForRuleCounts = [...findings, ...suppressed];
10
+ const rootCauseKeys = new Set(findings.map((finding) => finding.rootCause?.key).filter(Boolean));
11
+ const healthEntries = reports.flatMap((report) => report.health?.entries ?? []);
12
+ const rules = buildRuleTelemetry(allForRuleCounts, suppressed);
13
+ return {
14
+ schemaVersion: 1,
15
+ generatedAt: options.generatedAt ?? new Date().toISOString(),
16
+ policy: options.policy ?? inferReviewPolicy(),
17
+ files: reports.length,
18
+ findings: {
19
+ total: findings.length,
20
+ errors: findings.filter((finding) => finding.severity === 'error').length,
21
+ warnings: findings.filter((finding) => finding.severity === 'warning').length,
22
+ notes: findings.filter((finding) => finding.severity === 'info').length,
23
+ },
24
+ suppressed: {
25
+ total: suppressed.length,
26
+ },
27
+ rootCauses: rootCauseKeys.size,
28
+ health: {
29
+ status: healthEntries.some((entry) => entry.kind === 'error')
30
+ ? 'partial'
31
+ : healthEntries.length > 0
32
+ ? 'degraded'
33
+ : 'ok',
34
+ errors: healthEntries.filter((entry) => entry.kind === 'error').length,
35
+ fallbacks: healthEntries.filter((entry) => entry.kind === 'fallback').length,
36
+ skipped: healthEntries.filter((entry) => entry.kind === 'skipped').length,
37
+ },
38
+ rules,
39
+ ...(options.durationMs !== undefined ? { performance: { durationMs: options.durationMs } } : {}),
40
+ ...(options.includeFindings
41
+ ? {
42
+ findingRows: findings.map((f) => toFindingRow(f, options.policy ?? inferReviewPolicy(), options.redactPaths ?? false)),
43
+ }
44
+ : {}),
45
+ };
46
+ }
47
+ export function writeReviewTelemetrySnapshot(reports, options = {}) {
48
+ const outputPath = resolve(options.outputPath ?? '.kern/cache/review-telemetry.jsonl');
49
+ const snapshot = buildReviewTelemetry(reports, options);
50
+ mkdirSync(dirname(outputPath), { recursive: true });
51
+ const line = `${JSON.stringify(snapshot)}\n`;
52
+ if (options.append === false) {
53
+ writeFileSync(outputPath, line, 'utf-8');
54
+ }
55
+ else {
56
+ appendFileSync(outputPath, line, 'utf-8');
57
+ }
58
+ return { outputPath, snapshot };
59
+ }
60
+ export function parseReviewTelemetryJsonl(source) {
61
+ const snapshots = [];
62
+ const lines = source.split(/\r?\n/);
63
+ for (let i = 0; i < lines.length; i++) {
64
+ const line = lines[i].trim();
65
+ if (!line)
66
+ continue;
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(line);
70
+ }
71
+ catch (err) {
72
+ throw new Error(`telemetry line ${i + 1} is not valid JSON: ${err.message}`);
73
+ }
74
+ if (!isTelemetrySnapshot(parsed)) {
75
+ throw new Error(`telemetry line ${i + 1} is not a KERN review telemetry snapshot`);
76
+ }
77
+ snapshots.push(parsed);
78
+ }
79
+ return snapshots;
80
+ }
81
+ export function readReviewTelemetrySnapshots(path) {
82
+ return parseReviewTelemetryJsonl(readFileSync(path, 'utf-8'));
83
+ }
84
+ export function summarizeReviewTelemetry(snapshots) {
85
+ const ruleMap = new Map();
86
+ const durations = snapshots.map((snapshot) => snapshot.performance?.durationMs).filter(isNumber);
87
+ const sortedDates = snapshots.map((snapshot) => snapshot.generatedAt).sort();
88
+ for (const snapshot of snapshots) {
89
+ for (const rule of snapshot.rules) {
90
+ const existing = ruleMap.get(rule.ruleId) ??
91
+ {
92
+ ruleId: rule.ruleId,
93
+ findings: 0,
94
+ suppressed: 0,
95
+ errors: 0,
96
+ warnings: 0,
97
+ notes: 0,
98
+ rootCauses: 0,
99
+ runs: 0,
100
+ suppressionRate: 0,
101
+ averageFindingsPerRun: 0,
102
+ ...(rule.precision ? { precision: rule.precision } : {}),
103
+ ...(rule.lifecycle ? { lifecycle: rule.lifecycle } : {}),
104
+ ...(rule.ciDefault ? { ciDefault: rule.ciDefault } : {}),
105
+ };
106
+ existing.findings += rule.findings;
107
+ existing.suppressed += rule.suppressed;
108
+ existing.errors += rule.errors;
109
+ existing.warnings += rule.warnings;
110
+ existing.notes += rule.notes;
111
+ existing.rootCauses += rule.rootCauses;
112
+ existing.runs++;
113
+ ruleMap.set(rule.ruleId, existing);
114
+ }
115
+ }
116
+ const rules = Array.from(ruleMap.values())
117
+ .map((rule) => {
118
+ const totalObserved = rule.findings + rule.suppressed;
119
+ return {
120
+ ...rule,
121
+ suppressionRate: totalObserved > 0 ? rule.suppressed / totalObserved : 0,
122
+ averageFindingsPerRun: snapshots.length > 0 ? rule.findings / snapshots.length : 0,
123
+ };
124
+ })
125
+ .sort((a, b) => b.findings + b.suppressed - (a.findings + a.suppressed) || a.ruleId.localeCompare(b.ruleId));
126
+ return {
127
+ runs: snapshots.length,
128
+ ...(sortedDates[0] ? { firstRun: sortedDates[0] } : {}),
129
+ ...(sortedDates[sortedDates.length - 1] ? { lastRun: sortedDates[sortedDates.length - 1] } : {}),
130
+ files: snapshots.reduce((sum, snapshot) => sum + snapshot.files, 0),
131
+ findings: {
132
+ total: snapshots.reduce((sum, snapshot) => sum + snapshot.findings.total, 0),
133
+ errors: snapshots.reduce((sum, snapshot) => sum + snapshot.findings.errors, 0),
134
+ warnings: snapshots.reduce((sum, snapshot) => sum + snapshot.findings.warnings, 0),
135
+ notes: snapshots.reduce((sum, snapshot) => sum + snapshot.findings.notes, 0),
136
+ },
137
+ suppressed: {
138
+ total: snapshots.reduce((sum, snapshot) => sum + snapshot.suppressed.total, 0),
139
+ },
140
+ rootCauses: snapshots.reduce((sum, snapshot) => sum + snapshot.rootCauses, 0),
141
+ health: {
142
+ partial: snapshots.filter((snapshot) => snapshot.health.status === 'partial').length,
143
+ degraded: snapshots.filter((snapshot) => snapshot.health.status === 'degraded').length,
144
+ ok: snapshots.filter((snapshot) => snapshot.health.status === 'ok').length,
145
+ errors: snapshots.reduce((sum, snapshot) => sum + snapshot.health.errors, 0),
146
+ fallbacks: snapshots.reduce((sum, snapshot) => sum + snapshot.health.fallbacks, 0),
147
+ skipped: snapshots.reduce((sum, snapshot) => sum + snapshot.health.skipped, 0),
148
+ },
149
+ performance: {
150
+ ...(durations.length > 0
151
+ ? {
152
+ averageDurationMs: Math.round(durations.reduce((sum, duration) => sum + duration, 0) / durations.length),
153
+ maxDurationMs: Math.max(...durations),
154
+ }
155
+ : {}),
156
+ },
157
+ rules,
158
+ noisyRules: rules.filter((rule) => rule.suppressed >= 3 && rule.suppressionRate >= 0.5),
159
+ promotionCandidates: rules.filter((rule) => rule.precision === 'high' &&
160
+ rule.lifecycle === 'stable' &&
161
+ rule.ciDefault !== 'on' &&
162
+ rule.findings > 0 &&
163
+ rule.suppressionRate <= 0.1),
164
+ };
165
+ }
166
+ export function formatReviewTelemetrySummary(summary) {
167
+ const lines = [
168
+ 'KERN Review Telemetry',
169
+ '',
170
+ `Runs: ${summary.runs}`,
171
+ `Files: ${summary.files}`,
172
+ `Findings: ${summary.findings.total} (${summary.findings.errors} errors, ${summary.findings.warnings} warnings, ${summary.findings.notes} notes)`,
173
+ `Suppressed: ${summary.suppressed.total}`,
174
+ `Root causes: ${summary.rootCauses}`,
175
+ `Health: ${summary.health.ok} ok, ${summary.health.degraded} degraded, ${summary.health.partial} partial`,
176
+ ];
177
+ if (summary.performance.averageDurationMs !== undefined) {
178
+ lines.push(`Duration: avg ${summary.performance.averageDurationMs}ms, max ${summary.performance.maxDurationMs ?? 0}ms`);
179
+ }
180
+ lines.push('', 'Top Rules:');
181
+ for (const rule of summary.rules.slice(0, 10)) {
182
+ const suppressionPct = `${Math.round(rule.suppressionRate * 100)}%`;
183
+ lines.push(` ${rule.ruleId}: ${rule.findings} findings, ${rule.suppressed} suppressed (${suppressionPct} suppressed)`);
184
+ }
185
+ if (summary.noisyRules.length > 0) {
186
+ lines.push('', 'Noisy Rules:');
187
+ for (const rule of summary.noisyRules.slice(0, 10)) {
188
+ lines.push(` ${rule.ruleId}: ${Math.round(rule.suppressionRate * 100)}% suppression rate`);
189
+ }
190
+ }
191
+ if (summary.promotionCandidates.length > 0) {
192
+ lines.push('', 'Promotion Candidates:');
193
+ for (const rule of summary.promotionCandidates.slice(0, 10)) {
194
+ lines.push(` ${rule.ruleId}: high/stable with low suppression`);
195
+ }
196
+ }
197
+ return lines.join('\n');
198
+ }
199
+ function buildRuleTelemetry(findings, suppressedFindings) {
200
+ const suppressedKeys = new Set(suppressedFindings.map((finding) => `${finding.ruleId}:${finding.fingerprint}`));
201
+ const byRule = new Map();
202
+ for (const finding of findings) {
203
+ const existing = byRule.get(finding.ruleId) ?? makeRuleTelemetry(finding.ruleId);
204
+ const isSuppressed = suppressedKeys.has(`${finding.ruleId}:${finding.fingerprint}`);
205
+ if (isSuppressed) {
206
+ existing.suppressed++;
207
+ switch (finding.suppressionReason) {
208
+ case 'false-positive':
209
+ existing.suppressedAsFalsePositive = (existing.suppressedAsFalsePositive ?? 0) + 1;
210
+ break;
211
+ case 'wont-fix':
212
+ existing.suppressedAsWontFix = (existing.suppressedAsWontFix ?? 0) + 1;
213
+ break;
214
+ case 'intentional':
215
+ existing.suppressedAsIntentional = (existing.suppressedAsIntentional ?? 0) + 1;
216
+ break;
217
+ case 'not-applicable':
218
+ existing.suppressedAsNotApplicable = (existing.suppressedAsNotApplicable ?? 0) + 1;
219
+ break;
220
+ }
221
+ }
222
+ else {
223
+ existing.findings++;
224
+ if (finding.severity === 'error')
225
+ existing.errors++;
226
+ else if (finding.severity === 'warning')
227
+ existing.warnings++;
228
+ else
229
+ existing.notes++;
230
+ if (finding.rootCause?.key)
231
+ existing.rootCauseKeys.add(finding.rootCause.key);
232
+ }
233
+ byRule.set(finding.ruleId, existing);
234
+ }
235
+ return Array.from(byRule.values())
236
+ .map(({ rootCauseKeys, ...rule }) => {
237
+ const fp = rule.suppressedAsFalsePositive ?? 0;
238
+ const denom = rule.findings + fp;
239
+ const fpRateEstimate = denom > 0 && fp > 0 ? fp / denom : undefined;
240
+ return {
241
+ ...rule,
242
+ rootCauses: rootCauseKeys.size,
243
+ ...(fpRateEstimate !== undefined ? { fpRateEstimate } : {}),
244
+ };
245
+ })
246
+ .sort((a, b) => b.findings - a.findings || a.ruleId.localeCompare(b.ruleId));
247
+ }
248
+ function makeRuleTelemetry(ruleId) {
249
+ const profile = getRuleQualityProfile(ruleId);
250
+ return {
251
+ ruleId,
252
+ findings: 0,
253
+ suppressed: 0,
254
+ errors: 0,
255
+ warnings: 0,
256
+ notes: 0,
257
+ rootCauses: 0,
258
+ rootCauseKeys: new Set(),
259
+ ...(profile
260
+ ? {
261
+ precision: profile.precision,
262
+ lifecycle: profile.lifecycle,
263
+ ciDefault: profile.ciDefault,
264
+ }
265
+ : {}),
266
+ };
267
+ }
268
+ function toFindingRow(finding, policy, redactPaths) {
269
+ return {
270
+ file: redactPaths ? hashPath(finding.primarySpan.file) : finding.primarySpan.file,
271
+ ruleId: finding.ruleId,
272
+ severity: finding.severity,
273
+ ...(finding.confidence !== undefined ? { confidence: finding.confidence } : {}),
274
+ ...(finding.rootCause?.key ? { rootCauseKey: finding.rootCause.key } : {}),
275
+ ...(policy === 'audit' && finding.calibrationTrail && finding.calibrationTrail.length > 0
276
+ ? { calibrationTrail: finding.calibrationTrail }
277
+ : {}),
278
+ };
279
+ }
280
+ /**
281
+ * Stable, prefix-truncated hash of a file path. 16 hex chars = 64 bits of
282
+ * entropy — collision-resistant within any single project, while opaque to
283
+ * a multi-tenant telemetry consumer.
284
+ */
285
+ function hashPath(p) {
286
+ return `path:${createHash('sha256').update(p).digest('hex').slice(0, 16)}`;
287
+ }
288
+ function isTelemetrySnapshot(value) {
289
+ if (!value || typeof value !== 'object')
290
+ return false;
291
+ const candidate = value;
292
+ return (candidate.schemaVersion === 1 &&
293
+ typeof candidate.generatedAt === 'string' &&
294
+ typeof candidate.policy === 'string' &&
295
+ typeof candidate.files === 'number' &&
296
+ Boolean(candidate.findings) &&
297
+ Boolean(candidate.suppressed) &&
298
+ Array.isArray(candidate.rules));
299
+ }
300
+ function isNumber(value) {
301
+ return typeof value === 'number' && Number.isFinite(value);
302
+ }
303
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -50,6 +50,15 @@ export interface ProvenanceChain {
50
50
  /** Optional one-line summary shown before expanding the chain */
51
51
  summary?: string;
52
52
  }
53
+ /** Semantic root cause used to group findings that describe the same underlying issue. */
54
+ export interface RootCause {
55
+ /** Stable grouping key. Prefer graph/concept IDs over raw line numbers. */
56
+ key: string;
57
+ /** Coarse class of the underlying issue source. */
58
+ kind: 'api-call' | 'route' | 'data-flow' | 'symbol' | 'file' | 'unknown';
59
+ /** Optional structured facets for dashboards and future explainers. */
60
+ facets?: Record<string, string>;
61
+ }
53
62
  /** Unified finding from any review layer */
54
63
  export interface ReviewFinding {
55
64
  /** Which layer produced this finding */
@@ -82,6 +91,44 @@ export interface ReviewFinding {
82
91
  distance?: number;
83
92
  /** Evidence chain explaining WHY the finding fired (taint path, boundary walk, etc.) */
84
93
  provenance?: ProvenanceChain;
94
+ /** Semantic grouping key for cross-rule/root-cause ownership. */
95
+ rootCause?: RootCause;
96
+ /**
97
+ * Per-stage calibration record. Populated by applyRuleQualityCalibration when a
98
+ * factor actually changes severity or confidence. Lets audit policy show why a
99
+ * finding was demoted/dropped without recomputing the chain.
100
+ */
101
+ calibrationTrail?: CalibrationStage[];
102
+ /**
103
+ * Set true after applyRuleQualityCalibration runs over this finding so that
104
+ * subsequent calls (e.g. graph-mode rerun after cross-file injection) skip it.
105
+ * Prevents compounding multipliers when Phase 1 role/overlap factors land.
106
+ */
107
+ calibrated?: boolean;
108
+ /**
109
+ * If this finding was suppressed by a `// kern-ignore [reason: …]` directive,
110
+ * the closed-enum reason is propagated here so telemetry can compute per-rule
111
+ * FP/intent rates without scanning source again. Closed-enum: any free text
112
+ * is rejected at parse time.
113
+ */
114
+ suppressionReason?: import('./suppression/types.js').SuppressionReason;
115
+ }
116
+ /** One step of calibration applied to a finding. */
117
+ export interface CalibrationStage {
118
+ /** Identifier for the stage that ran (e.g. 'rule-quality:demote-advisory'). */
119
+ stage: string;
120
+ /** Multiplicative factor applied to confidence (1.0 = no change). */
121
+ factor: number;
122
+ /** Why this stage acted on this finding. */
123
+ reason: string;
124
+ /** Confidence before this stage (undefined if confidence was unset). */
125
+ beforeConfidence?: number;
126
+ /** Confidence after this stage. */
127
+ afterConfidence?: number;
128
+ /** Severity before this stage, if changed. */
129
+ beforeSeverity?: ReviewFinding['severity'];
130
+ /** Severity after this stage, if changed. */
131
+ afterSeverity?: ReviewFinding['severity'];
85
132
  }
86
133
  /** Confidence level for an inference match */
87
134
  export type Confidence = 'high' | 'medium' | 'low';
@@ -216,6 +263,18 @@ export interface ReviewStats {
216
263
  /** Number of KERN-expressible constructs */
217
264
  constructCount: number;
218
265
  }
266
+ /** Named review posture. Guard is low-noise default, CI is strict, audit is broad. */
267
+ export type ReviewPolicy = 'guard' | 'ci' | 'audit';
268
+ export interface ReviewTelemetryConfig {
269
+ /** When true, persist a machine-readable telemetry snapshot for this review run. */
270
+ enabled?: boolean;
271
+ /** Output file. Defaults to .kern/cache/review-telemetry.jsonl when telemetry is enabled. */
272
+ outputPath?: string;
273
+ /** Append JSONL snapshots instead of replacing the file. Defaults to true. */
274
+ append?: boolean;
275
+ /** Include per-finding rows in addition to aggregate counts. Defaults to false. */
276
+ includeFindings?: boolean;
277
+ }
219
278
  /** Enforcement result for CI */
220
279
  export interface EnforceResult {
221
280
  /** Whether enforcement passed */
@@ -262,6 +321,16 @@ export interface ReviewConfig {
262
321
  format?: 'text' | 'json' | 'sarif';
263
322
  /** Build target — activates framework-specific rules */
264
323
  target?: string;
324
+ /**
325
+ * Cross-stack review precision mode.
326
+ * guard — default, high precision / low noise for KERN Guard and CI.
327
+ * audit — broader exploratory findings for local investigations.
328
+ */
329
+ crossStackMode?: 'guard' | 'audit';
330
+ /** Explicit review policy. Used by CLI/defaulting/telemetry to distinguish CI, guard, and audit runs. */
331
+ policy?: ReviewPolicy;
332
+ /** Optional persistent telemetry for rule/noise calibration. */
333
+ telemetry?: ReviewTelemetryConfig;
265
334
  /** Minimum confidence for findings to count in enforcement (default: 0) */
266
335
  minConfidence?: number;
267
336
  /** Show confidence scores in output */
@@ -311,7 +380,7 @@ export interface ReviewConfig {
311
380
  * names (`src/routes.ts`) never collide. Rules that consult
312
381
  * `ctx.allConcepts` treat the keys opaquely.
313
382
  */
314
- externalConcepts?: Map<string, import('@kernlang/core').ConceptMap>;
383
+ externalConcepts?: ReadonlyMap<string, unknown>;
315
384
  }
316
385
  /** Runtime boundary determined by position in the import tree */
317
386
  export type RuntimeBoundary = 'server' | 'client' | 'api' | 'middleware' | 'shared' | 'unknown';
@@ -350,7 +419,24 @@ export interface RuleContext {
350
419
  }
351
420
  /** A review rule function */
352
421
  export type ReviewRule = (ctx: RuleContext) => ReviewFinding[];
353
- export type GraphEdgeKind = 'side-effect-import' | 'default-import' | 'named-import' | 'namespace-import' | 'named-reexport' | 'export-all';
422
+ export type GraphEdgeKind = 'side-effect-import' | 'default-import' | 'named-import' | 'namespace-import' | 'named-reexport' | 'export-all'
423
+ /**
424
+ * `export * as ns from './m'` — distinct from bare `export *` because the
425
+ * namespace alias gives us a concrete local name (`ns`) that Producer 1
426
+ * can attach a symbol-scoped blocker to when the target fails to resolve.
427
+ * Bare `export *` stays under `'export-all'` and produces no blocker
428
+ * (no symbol to pin).
429
+ */
430
+ | 'namespace-reexport'
431
+ /**
432
+ * Literal `import('./mod')` — emitted by the graph walker when the argument
433
+ * is a StringLiteral or NoSubstitutionTemplateLiteral. Distinct from the
434
+ * static-import variants so a strongest-path traversal can prefer a static
435
+ * (full-confidence) edge over a dynamic (capped) edge to the same target.
436
+ * Non-literal `import(expr)` does NOT produce an edge — it produces a
437
+ * `ReachabilityBlocker` instead (see step 9b).
438
+ */
439
+ | 'dynamic-import';
354
440
  export interface GraphEdge {
355
441
  from: string;
356
442
  to: string;
@@ -365,7 +451,26 @@ export interface GraphEdge {
365
451
  }
366
452
  /** A file node in the import graph */
367
453
  export interface GraphFile {
454
+ /**
455
+ * Display path — caller-facing (whatever the user passed in for entry
456
+ * files; whatever ts-morph reports for BFS-reached files). Use this
457
+ * for `report.filePath`, finding `primarySpan.file`, and any UI
458
+ * surface. NEVER use this as a Map key for cross-file resolution —
459
+ * symlinks (pnpm, macOS /var → /private/var) cause the same physical
460
+ * file to have multiple display paths and would silently produce
461
+ * twin entries (red-team #9).
462
+ */
368
463
  path: string;
464
+ /**
465
+ * Internal identity key — `realpathSync(path)` with a graceful fallback
466
+ * for paths that don't exist on disk. ALL Map keys, ts-morph
467
+ * `addSourceFileAtPath` calls, and cross-file binding lookups MUST use
468
+ * this form so two display paths pointing at the same physical file
469
+ * collapse to a single entry. The two forms are equal for normal
470
+ * projects without symlinks, so this is a no-op there. See
471
+ * `path-canonical.ts` for the canonicaliser invariant.
472
+ */
473
+ canonicalPath: string;
369
474
  distance: number;
370
475
  imports: string[];
371
476
  importedBy: string[];
@@ -381,12 +486,77 @@ export interface GraphResult {
381
486
  /** ts-morph Project used to resolve the graph. Exposed so downstream
382
487
  * analyses (call graph, cross-file taint) can reuse it without re-parsing. */
383
488
  project?: import('ts-morph').Project;
489
+ /**
490
+ * Symbol-scoped reachability blockers discovered during graph resolution.
491
+ * Currently fed by Producer 1 (named re-export with unresolved relative
492
+ * target). Consumed by deadExportRule to cap finding confidence at 0.4
493
+ * with severity=info instead of emitting at full strength. Empty when
494
+ * every re-export resolved cleanly. NEVER file-scope — see jsdoc on
495
+ * ReachabilityBlocker.
496
+ */
497
+ blockers?: ReachabilityBlocker[];
498
+ /**
499
+ * Telemetry-only counter: number of `import(expr)` call sites where the
500
+ * specifier is non-literal. Cannot derive a target file or export name
501
+ * from these, so they NEVER produce a blocker (would re-introduce the
502
+ * red-team CRITICAL #1 file-scope silencer). Reported via review-health
503
+ * so consumers know dead-export findings on files containing such
504
+ * dynamic dispatch may include FPs that the cap mechanism cannot reach.
505
+ */
506
+ unmappedDynamicImports?: number;
507
+ /**
508
+ * Telemetry-only counter: number of static import declarations that
509
+ * threw when ts-morph tried to read them (malformed AST, transient FS
510
+ * error, etc.). Surfaced via review-health so a silent catch never
511
+ * hides a category of failures from operators. Set KERN_DEBUG to also
512
+ * log the underlying error per occurrence.
513
+ */
514
+ malformedImports?: number;
515
+ }
516
+ /**
517
+ * A reachability blocker — recorded when the call-graph cannot resolve an
518
+ * edge to a single concrete target (filePath, exportName) and so cannot
519
+ * prove a candidate dead export is unreachable.
520
+ *
521
+ * Blockers are SYMBOL-SCOPED. A non-literal dynamic import inside one
522
+ * function must NEVER suppress findings on unrelated exports in the same
523
+ * file — that was the killing red-team finding against Plan v3 ("one
524
+ * dynamic import silenced 50 unrelated symbols"). Each blocker carries the
525
+ * exact `(filePath, exportName)` it applies to. When the resolver cannot
526
+ * derive an exportName (e.g. fully non-literal `import(expr)`), it must
527
+ * NOT fall back to file scope; emit health/telemetry instead.
528
+ *
529
+ * Blockers do NOT hard-suppress. They cap finding confidence at 0.4 (see
530
+ * step 9b) and append a `CalibrationStage` to the finding's audit trail.
531
+ * Telemetry still sees the finding so `fpRateEstimate` stays honest.
532
+ */
533
+ export type ReachabilityBlockerReason =
534
+ /** `import(expr)` where `expr` is not a string literal — target unknown. */
535
+ 'non-literal-dynamic-import'
536
+ /** A re-export (`export * from`, `export { x } from`) whose target file
537
+ * resolved but the symbol couldn't be tied to a concrete declaration. */
538
+ | 'unresolved-re-export'
539
+ /** A public-API seed (package.json `exports`, framework convention) that
540
+ * pointed at a symbol the call graph never observed declaring. */
541
+ | 'unmapped-public-surface';
542
+ export interface ReachabilityBlocker {
543
+ reason: ReachabilityBlockerReason;
544
+ /** The candidate export this blocker applies to. SYMBOL scope, not file. */
545
+ filePath: string;
546
+ exportName: string;
547
+ /** Where the blocker decision was made — for the audit trail. */
548
+ site: {
549
+ file: string;
550
+ line: number;
551
+ };
384
552
  }
385
553
  /** Options for resolveImportGraph */
386
554
  export interface GraphOptions {
387
555
  maxDepth?: number;
388
556
  tsConfigFilePath?: string;
389
557
  project?: import('ts-morph').Project;
558
+ /** Optional graph already resolved by the caller, used to avoid duplicate resolution in CLI/watch flows. */
559
+ precomputedGraph?: GraphResult;
390
560
  }
391
561
  /** Create a stable fingerprint for dedup across sources and runs.
392
562
  * Returns the raw composite key — collision-free by construction. */
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2bH,4EAA4E;AAE5E;sEACsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,SAAiB,EAAE,QAAgB;IACnF,OAAO,GAAG,MAAM,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwmBH,4EAA4E;AAE5E;sEACsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,SAAiB,EAAE,QAAgB;IACnF,OAAO,GAAG,MAAM,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;AAC9C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernlang/review",
3
- "version": "3.3.8",
3
+ "version": "3.4.0",
4
4
  "description": "Kern Review — scan TS, infer .kern IR, roundtrip diff, report",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,10 +26,11 @@
26
26
  "license": "AGPL-3.0",
27
27
  "dependencies": {
28
28
  "ts-morph": "^28.0.0",
29
- "@kernlang/core": "3.3.8"
29
+ "@kernlang/core": "3.4.0"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsc -b",
33
- "test": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --config jest.config.js"
33
+ "test": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --config jest.config.js",
34
+ "test:handles": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --config jest.config.js --detectOpenHandles --runInBand"
34
35
  }
35
36
  }