@eduardbar/drift 1.2.0 → 1.3.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 (61) hide show
  1. package/.github/workflows/publish-vscode.yml +3 -3
  2. package/.github/workflows/publish.yml +3 -3
  3. package/.github/workflows/review-pr.yml +98 -6
  4. package/AGENTS.md +6 -0
  5. package/README.md +160 -10
  6. package/ROADMAP.md +6 -5
  7. package/dist/analyzer.d.ts +2 -2
  8. package/dist/analyzer.js +420 -159
  9. package/dist/benchmark.d.ts +2 -0
  10. package/dist/benchmark.js +185 -0
  11. package/dist/cli.js +453 -62
  12. package/dist/diff.js +74 -10
  13. package/dist/git.js +12 -0
  14. package/dist/index.d.ts +5 -3
  15. package/dist/index.js +3 -1
  16. package/dist/plugins.d.ts +2 -1
  17. package/dist/plugins.js +177 -28
  18. package/dist/printer.js +4 -0
  19. package/dist/review.js +2 -2
  20. package/dist/rules/comments.js +2 -2
  21. package/dist/rules/complexity.js +2 -7
  22. package/dist/rules/nesting.js +3 -13
  23. package/dist/rules/phase0-basic.js +10 -10
  24. package/dist/rules/shared.d.ts +2 -0
  25. package/dist/rules/shared.js +27 -3
  26. package/dist/saas.d.ts +143 -7
  27. package/dist/saas.js +478 -37
  28. package/dist/trust-kpi.d.ts +9 -0
  29. package/dist/trust-kpi.js +445 -0
  30. package/dist/trust.d.ts +65 -0
  31. package/dist/trust.js +571 -0
  32. package/dist/types.d.ts +154 -0
  33. package/docs/PRD.md +187 -109
  34. package/docs/plugin-contract.md +61 -0
  35. package/docs/trust-core-release-checklist.md +55 -0
  36. package/package.json +5 -3
  37. package/src/analyzer.ts +484 -155
  38. package/src/benchmark.ts +244 -0
  39. package/src/cli.ts +562 -79
  40. package/src/diff.ts +75 -10
  41. package/src/git.ts +16 -0
  42. package/src/index.ts +48 -0
  43. package/src/plugins.ts +354 -26
  44. package/src/printer.ts +4 -0
  45. package/src/review.ts +2 -2
  46. package/src/rules/comments.ts +2 -2
  47. package/src/rules/complexity.ts +2 -7
  48. package/src/rules/nesting.ts +3 -13
  49. package/src/rules/phase0-basic.ts +11 -12
  50. package/src/rules/shared.ts +31 -3
  51. package/src/saas.ts +641 -43
  52. package/src/trust-kpi.ts +518 -0
  53. package/src/trust.ts +774 -0
  54. package/src/types.ts +171 -0
  55. package/tests/diff.test.ts +124 -0
  56. package/tests/new-features.test.ts +71 -0
  57. package/tests/plugins.test.ts +219 -0
  58. package/tests/rules.test.ts +23 -1
  59. package/tests/saas-foundation.test.ts +358 -1
  60. package/tests/trust-kpi.test.ts +120 -0
  61. package/tests/trust.test.ts +584 -0
@@ -0,0 +1,445 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import { dirname, isAbsolute, resolve } from 'node:path';
3
+ import { MERGE_RISK_ORDER, normalizeMergeRiskLevel } from './trust.js';
4
+ const IGNORED_DIRECTORIES = new Set(['node_modules', '.git', 'dist', '.next', 'build']);
5
+ function toPosixPath(path) {
6
+ return path.replace(/\\/g, '/');
7
+ }
8
+ function round(value, decimals = 2) {
9
+ return Number(value.toFixed(decimals));
10
+ }
11
+ function median(values) {
12
+ if (values.length === 0)
13
+ return null;
14
+ const sorted = [...values].sort((a, b) => a - b);
15
+ const mid = Math.floor(sorted.length / 2);
16
+ if (sorted.length % 2 === 0) {
17
+ return round((sorted[mid - 1] + sorted[mid]) / 2);
18
+ }
19
+ return round(sorted[mid]);
20
+ }
21
+ function average(values) {
22
+ if (values.length === 0)
23
+ return null;
24
+ return round(values.reduce((sum, value) => sum + value, 0) / values.length);
25
+ }
26
+ function listFilesRecursively(root) {
27
+ if (!existsSync(root))
28
+ return [];
29
+ const out = [];
30
+ const stack = [root];
31
+ while (stack.length > 0) {
32
+ const current = stack.pop();
33
+ for (const entry of readdirSync(current)) {
34
+ const fullPath = resolve(current, entry);
35
+ const info = statSync(fullPath);
36
+ if (info.isDirectory()) {
37
+ if (IGNORED_DIRECTORIES.has(entry))
38
+ continue;
39
+ stack.push(fullPath);
40
+ }
41
+ else {
42
+ out.push(fullPath);
43
+ }
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+ function isGlobPattern(input) {
49
+ return /[*?[\]{}]/.test(input);
50
+ }
51
+ function escapeRegex(char) {
52
+ return /[\\^$+?.()|{}\[\]]/.test(char) ? `\\${char}` : char;
53
+ }
54
+ function globToRegex(pattern) {
55
+ const normalized = toPosixPath(pattern);
56
+ let expression = '^';
57
+ for (let index = 0; index < normalized.length; index += 1) {
58
+ const char = normalized[index];
59
+ const nextChar = normalized[index + 1];
60
+ const nextNextChar = normalized[index + 2];
61
+ if (char === '*' && nextChar === '*') {
62
+ if (nextNextChar === '/') {
63
+ expression += '(?:.*/)?';
64
+ index += 2;
65
+ continue;
66
+ }
67
+ expression += '.*';
68
+ index += 1;
69
+ continue;
70
+ }
71
+ if (char === '*') {
72
+ expression += '[^/]*';
73
+ continue;
74
+ }
75
+ if (char === '?') {
76
+ expression += '[^/]';
77
+ continue;
78
+ }
79
+ expression += escapeRegex(char);
80
+ }
81
+ expression += '$';
82
+ return new RegExp(expression);
83
+ }
84
+ function globBaseDir(pattern) {
85
+ const normalized = toPosixPath(pattern);
86
+ const wildcardIndex = normalized.search(/[*?[\]{}]/);
87
+ if (wildcardIndex < 0)
88
+ return dirname(pattern);
89
+ const prefix = normalized.slice(0, wildcardIndex);
90
+ const slashIndex = prefix.lastIndexOf('/');
91
+ if (slashIndex < 0)
92
+ return '.';
93
+ if (slashIndex === 0)
94
+ return '/';
95
+ return prefix.slice(0, slashIndex);
96
+ }
97
+ function discoverTrustJsonFiles(input, cwd) {
98
+ const diagnostics = [];
99
+ const source = input.trim() || '.';
100
+ if (isGlobPattern(source)) {
101
+ const absolutePattern = isAbsolute(source) ? source : resolve(cwd, source);
102
+ const regex = globToRegex(toPosixPath(absolutePattern));
103
+ const base = resolve(cwd, globBaseDir(source));
104
+ if (!existsSync(base)) {
105
+ diagnostics.push({
106
+ level: 'error',
107
+ code: 'path-not-found',
108
+ message: `Glob base path does not exist: ${base}`,
109
+ });
110
+ return { files: [], diagnostics };
111
+ }
112
+ const matched = listFilesRecursively(base)
113
+ .filter((filePath) => regex.test(toPosixPath(filePath)))
114
+ .filter((filePath) => filePath.toLowerCase().endsWith('.json'))
115
+ .sort((a, b) => a.localeCompare(b));
116
+ return { files: matched, diagnostics };
117
+ }
118
+ const absolute = isAbsolute(source) ? source : resolve(cwd, source);
119
+ if (!existsSync(absolute)) {
120
+ diagnostics.push({
121
+ level: 'error',
122
+ code: 'path-not-found',
123
+ message: `Path does not exist: ${absolute}`,
124
+ });
125
+ return { files: [], diagnostics };
126
+ }
127
+ const info = statSync(absolute);
128
+ if (info.isDirectory()) {
129
+ const files = listFilesRecursively(absolute)
130
+ .filter((filePath) => filePath.toLowerCase().endsWith('.json'))
131
+ .sort((a, b) => a.localeCompare(b));
132
+ return { files, diagnostics };
133
+ }
134
+ if (info.isFile()) {
135
+ if (!absolute.toLowerCase().endsWith('.json')) {
136
+ diagnostics.push({
137
+ level: 'warning',
138
+ code: 'path-not-supported',
139
+ file: absolute,
140
+ message: 'Input file is not JSON; attempting to parse anyway',
141
+ });
142
+ }
143
+ return { files: [absolute], diagnostics };
144
+ }
145
+ diagnostics.push({
146
+ level: 'error',
147
+ code: 'path-not-supported',
148
+ message: `Path is neither a file nor directory: ${absolute}`,
149
+ });
150
+ return { files: [], diagnostics };
151
+ }
152
+ function isObjectLike(value) {
153
+ return typeof value === 'object' && value !== null;
154
+ }
155
+ function normalizeDiffContext(raw) {
156
+ if (!isObjectLike(raw)) {
157
+ return {
158
+ diagnostic: {
159
+ level: 'warning',
160
+ code: 'invalid-diff-context',
161
+ message: 'diff_context is present but malformed; skipping diff trend fields for this artifact',
162
+ },
163
+ };
164
+ }
165
+ const baseRef = typeof raw.baseRef === 'string' ? raw.baseRef : 'unknown';
166
+ const status = raw.status;
167
+ const scoreDelta = typeof raw.scoreDelta === 'number' && Number.isFinite(raw.scoreDelta) ? raw.scoreDelta : null;
168
+ const newIssues = typeof raw.newIssues === 'number' && Number.isFinite(raw.newIssues) ? raw.newIssues : null;
169
+ const resolvedIssues = typeof raw.resolvedIssues === 'number' && Number.isFinite(raw.resolvedIssues) ? raw.resolvedIssues : null;
170
+ const filesChanged = typeof raw.filesChanged === 'number' && Number.isFinite(raw.filesChanged) ? raw.filesChanged : 0;
171
+ const penalty = typeof raw.penalty === 'number' && Number.isFinite(raw.penalty) ? raw.penalty : 0;
172
+ const bonus = typeof raw.bonus === 'number' && Number.isFinite(raw.bonus) ? raw.bonus : 0;
173
+ const netImpact = typeof raw.netImpact === 'number' && Number.isFinite(raw.netImpact) ? raw.netImpact : 0;
174
+ if (scoreDelta == null || newIssues == null || resolvedIssues == null) {
175
+ return {
176
+ diagnostic: {
177
+ level: 'warning',
178
+ code: 'invalid-diff-context',
179
+ message: 'diff_context is missing numeric scoreDelta/newIssues/resolvedIssues; skipping diff trend fields for this artifact',
180
+ },
181
+ };
182
+ }
183
+ const normalizedStatus = status === 'improved' || status === 'regressed' || status === 'neutral'
184
+ ? status
185
+ : scoreDelta < 0
186
+ ? 'improved'
187
+ : scoreDelta > 0
188
+ ? 'regressed'
189
+ : 'neutral';
190
+ return {
191
+ diffContext: {
192
+ baseRef,
193
+ status: normalizedStatus,
194
+ scoreDelta,
195
+ newIssues,
196
+ resolvedIssues,
197
+ filesChanged,
198
+ penalty,
199
+ bonus,
200
+ netImpact,
201
+ },
202
+ };
203
+ }
204
+ function parseTrustArtifact(filePath) {
205
+ const diagnostics = [];
206
+ let rawContent = '';
207
+ try {
208
+ rawContent = readFileSync(filePath, 'utf8');
209
+ }
210
+ catch (error) {
211
+ diagnostics.push({
212
+ level: 'error',
213
+ code: 'read-failed',
214
+ file: filePath,
215
+ message: error instanceof Error ? error.message : String(error),
216
+ });
217
+ return { diagnostics };
218
+ }
219
+ let parsed;
220
+ try {
221
+ parsed = JSON.parse(rawContent);
222
+ }
223
+ catch (error) {
224
+ diagnostics.push({
225
+ level: 'error',
226
+ code: 'parse-failed',
227
+ file: filePath,
228
+ message: error instanceof Error ? error.message : String(error),
229
+ });
230
+ return { diagnostics };
231
+ }
232
+ if (!isObjectLike(parsed)) {
233
+ diagnostics.push({
234
+ level: 'error',
235
+ code: 'invalid-shape',
236
+ file: filePath,
237
+ message: 'Trust artifact must be a JSON object',
238
+ });
239
+ return { diagnostics };
240
+ }
241
+ const trustScore = parsed.trust_score;
242
+ if (typeof trustScore !== 'number' || !Number.isFinite(trustScore)) {
243
+ diagnostics.push({
244
+ level: 'error',
245
+ code: 'invalid-shape',
246
+ file: filePath,
247
+ message: 'Missing numeric trust_score',
248
+ });
249
+ return { diagnostics };
250
+ }
251
+ const mergeRisk = typeof parsed.merge_risk === 'string'
252
+ ? normalizeMergeRiskLevel(parsed.merge_risk)
253
+ : undefined;
254
+ if (!mergeRisk) {
255
+ diagnostics.push({
256
+ level: 'error',
257
+ code: 'invalid-shape',
258
+ file: filePath,
259
+ message: `Missing/invalid merge_risk (expected one of ${MERGE_RISK_ORDER.join(', ')})`,
260
+ });
261
+ return { diagnostics };
262
+ }
263
+ let diffContext;
264
+ if (parsed.diff_context !== undefined) {
265
+ const normalized = normalizeDiffContext(parsed.diff_context);
266
+ if (normalized.diagnostic) {
267
+ diagnostics.push({ ...normalized.diagnostic, file: filePath });
268
+ }
269
+ else {
270
+ diffContext = normalized.diffContext;
271
+ }
272
+ }
273
+ return {
274
+ record: {
275
+ filePath,
276
+ trustScore,
277
+ mergeRisk,
278
+ diffContext,
279
+ },
280
+ diagnostics,
281
+ };
282
+ }
283
+ function buildDiffTrend(records) {
284
+ const withDiff = records.filter((record) => record.diffContext);
285
+ if (withDiff.length === 0) {
286
+ return {
287
+ available: false,
288
+ samples: 0,
289
+ statusDistribution: {
290
+ improved: 0,
291
+ regressed: 0,
292
+ neutral: 0,
293
+ },
294
+ scoreDelta: {
295
+ average: null,
296
+ median: null,
297
+ },
298
+ issues: {
299
+ newTotal: 0,
300
+ resolvedTotal: 0,
301
+ netNew: 0,
302
+ },
303
+ };
304
+ }
305
+ const scoreDeltas = withDiff.map((record) => record.diffContext.scoreDelta);
306
+ const newIssues = withDiff.reduce((sum, record) => sum + record.diffContext.newIssues, 0);
307
+ const resolvedIssues = withDiff.reduce((sum, record) => sum + record.diffContext.resolvedIssues, 0);
308
+ const statusDistribution = {
309
+ improved: withDiff.filter((record) => record.diffContext.status === 'improved').length,
310
+ regressed: withDiff.filter((record) => record.diffContext.status === 'regressed').length,
311
+ neutral: withDiff.filter((record) => record.diffContext.status === 'neutral').length,
312
+ };
313
+ return {
314
+ available: true,
315
+ samples: withDiff.length,
316
+ statusDistribution,
317
+ scoreDelta: {
318
+ average: average(scoreDeltas),
319
+ median: median(scoreDeltas),
320
+ },
321
+ issues: {
322
+ newTotal: newIssues,
323
+ resolvedTotal: resolvedIssues,
324
+ netNew: newIssues - resolvedIssues,
325
+ },
326
+ };
327
+ }
328
+ export function computeTrustKpis(input, options) {
329
+ const cwd = options?.cwd ?? process.cwd();
330
+ const discovered = discoverTrustJsonFiles(input, cwd);
331
+ const records = [];
332
+ const diagnostics = [...discovered.diagnostics];
333
+ for (const filePath of discovered.files) {
334
+ const parsed = parseTrustArtifact(filePath);
335
+ diagnostics.push(...parsed.diagnostics);
336
+ if (parsed.record)
337
+ records.push(parsed.record);
338
+ }
339
+ const trustScores = records.map((record) => record.trustScore);
340
+ const mergeRiskDistribution = {
341
+ LOW: 0,
342
+ MEDIUM: 0,
343
+ HIGH: 0,
344
+ CRITICAL: 0,
345
+ };
346
+ for (const record of records) {
347
+ mergeRiskDistribution[record.mergeRisk] += 1;
348
+ }
349
+ const highRiskCount = mergeRiskDistribution.HIGH + mergeRiskDistribution.CRITICAL;
350
+ return {
351
+ generatedAt: new Date().toISOString(),
352
+ input,
353
+ files: {
354
+ matched: discovered.files.length,
355
+ parsed: records.length,
356
+ malformed: discovered.files.length - records.length,
357
+ },
358
+ prsEvaluated: records.length,
359
+ mergeRiskDistribution,
360
+ trustScore: {
361
+ average: average(trustScores),
362
+ median: median(trustScores),
363
+ min: trustScores.length > 0 ? Math.min(...trustScores) : null,
364
+ max: trustScores.length > 0 ? Math.max(...trustScores) : null,
365
+ },
366
+ highRiskRatio: records.length > 0 ? round(highRiskCount / records.length, 4) : null,
367
+ diffTrend: buildDiffTrend(records),
368
+ diagnostics,
369
+ };
370
+ }
371
+ export function formatTrustKpiConsole(kpi) {
372
+ const parts = [
373
+ 'drift kpi',
374
+ '',
375
+ `Input: ${kpi.input}`,
376
+ `Files matched: ${kpi.files.matched} | parsed: ${kpi.files.parsed} | malformed: ${kpi.files.malformed}`,
377
+ `PRs evaluated: ${kpi.prsEvaluated}`,
378
+ `Trust score (avg/median): ${kpi.trustScore.average ?? 'n/a'} / ${kpi.trustScore.median ?? 'n/a'}`,
379
+ `High-risk ratio (HIGH+CRITICAL): ${kpi.highRiskRatio == null ? 'n/a' : `${round(kpi.highRiskRatio * 100, 2)}%`}`,
380
+ `Merge risk distribution: LOW=${kpi.mergeRiskDistribution.LOW} MEDIUM=${kpi.mergeRiskDistribution.MEDIUM} HIGH=${kpi.mergeRiskDistribution.HIGH} CRITICAL=${kpi.mergeRiskDistribution.CRITICAL}`,
381
+ ];
382
+ if (kpi.diffTrend.available) {
383
+ const avgDelta = kpi.diffTrend.scoreDelta.average;
384
+ const signedDelta = avgDelta == null ? 'n/a' : `${avgDelta >= 0 ? '+' : ''}${avgDelta}`;
385
+ parts.push(`Diff trend samples: ${kpi.diffTrend.samples} | avg score delta: ${signedDelta} | new/resolved: +${kpi.diffTrend.issues.newTotal}/-${kpi.diffTrend.issues.resolvedTotal}`);
386
+ }
387
+ else {
388
+ parts.push('Diff trend samples: 0 (no diff_context found)');
389
+ }
390
+ if (kpi.diagnostics.length > 0) {
391
+ const errorCount = kpi.diagnostics.filter((diagnostic) => diagnostic.level === 'error').length;
392
+ const warningCount = kpi.diagnostics.filter((diagnostic) => diagnostic.level === 'warning').length;
393
+ parts.push(`Diagnostics: ${errorCount} error(s), ${warningCount} warning(s)`);
394
+ }
395
+ return parts.join('\n');
396
+ }
397
+ export function formatTrustKpiJson(kpi) {
398
+ return JSON.stringify(kpi, null, 2);
399
+ }
400
+ export function computeTrustKpisFromReports(reports) {
401
+ const tempRecords = reports.reduce((acc, report, index) => {
402
+ const mergeRisk = normalizeMergeRiskLevel(report.merge_risk);
403
+ if (!mergeRisk || typeof report.trust_score !== 'number')
404
+ return acc;
405
+ acc.push({
406
+ filePath: `report-${index + 1}`,
407
+ trustScore: report.trust_score,
408
+ mergeRisk,
409
+ diffContext: report.diff_context,
410
+ });
411
+ return acc;
412
+ }, []);
413
+ const trustScores = tempRecords.map((record) => record.trustScore);
414
+ const mergeRiskDistribution = {
415
+ LOW: 0,
416
+ MEDIUM: 0,
417
+ HIGH: 0,
418
+ CRITICAL: 0,
419
+ };
420
+ for (const record of tempRecords) {
421
+ mergeRiskDistribution[record.mergeRisk] += 1;
422
+ }
423
+ const highRiskCount = mergeRiskDistribution.HIGH + mergeRiskDistribution.CRITICAL;
424
+ return {
425
+ generatedAt: new Date().toISOString(),
426
+ input: 'in-memory',
427
+ files: {
428
+ matched: reports.length,
429
+ parsed: tempRecords.length,
430
+ malformed: reports.length - tempRecords.length,
431
+ },
432
+ prsEvaluated: tempRecords.length,
433
+ mergeRiskDistribution,
434
+ trustScore: {
435
+ average: average(trustScores),
436
+ median: median(trustScores),
437
+ min: trustScores.length > 0 ? Math.min(...trustScores) : null,
438
+ max: trustScores.length > 0 ? Math.max(...trustScores) : null,
439
+ },
440
+ highRiskRatio: tempRecords.length > 0 ? round(highRiskCount / tempRecords.length, 4) : null,
441
+ diffTrend: buildDiffTrend(tempRecords),
442
+ diagnostics: [],
443
+ };
444
+ }
445
+ //# sourceMappingURL=trust-kpi.js.map
@@ -0,0 +1,65 @@
1
+ import type { DriftConfig, DriftDiff, DriftReport, DriftTrustReport, MergeRiskLevel } from './types.js';
2
+ import type { SnapshotEntry } from './snapshot.js';
3
+ interface BuildTrustOptions {
4
+ diff?: DriftDiff;
5
+ advanced?: {
6
+ enabled?: boolean;
7
+ previousTrust?: Partial<DriftTrustReport>;
8
+ snapshots?: SnapshotEntry[];
9
+ };
10
+ }
11
+ interface TrustRenderOptions {
12
+ json?: boolean;
13
+ markdown?: boolean;
14
+ }
15
+ export interface TrustGateOptions {
16
+ enabled?: boolean;
17
+ minTrust?: number;
18
+ maxRisk?: MergeRiskLevel;
19
+ }
20
+ export interface TrustGatePolicyResolutionOptions {
21
+ branchName?: string;
22
+ policyPack?: string;
23
+ overrides?: TrustGateOptions;
24
+ }
25
+ export interface TrustGatePolicyResolutionStep {
26
+ source: 'base' | 'policy-pack' | 'branch-preset' | 'overrides';
27
+ name: string;
28
+ values: TrustGateOptions;
29
+ }
30
+ export interface TrustGatePolicyExplanation {
31
+ effectivePolicy: TrustGateOptions;
32
+ branchName?: string;
33
+ selectedPolicyPack?: string;
34
+ invalidPolicyPack?: string;
35
+ steps: TrustGatePolicyResolutionStep[];
36
+ }
37
+ export interface TrustGateEvaluation {
38
+ shouldFail: boolean;
39
+ reasons: string[];
40
+ checks: {
41
+ gateDisabled: boolean;
42
+ belowMinTrust: boolean;
43
+ aboveMaxRisk: boolean;
44
+ minTrust?: number;
45
+ maxRisk?: MergeRiskLevel;
46
+ };
47
+ }
48
+ export declare const MERGE_RISK_ORDER: MergeRiskLevel[];
49
+ export declare function normalizeMergeRiskLevel(value: string): MergeRiskLevel | undefined;
50
+ export declare function detectBranchName(env?: NodeJS.ProcessEnv): string | undefined;
51
+ export declare function explainTrustGatePolicy(config: DriftConfig | undefined, branchName?: string, overrides?: TrustGateOptions): TrustGatePolicyExplanation;
52
+ export declare function explainTrustGatePolicy(config: DriftConfig | undefined, options?: TrustGatePolicyResolutionOptions): TrustGatePolicyExplanation;
53
+ export declare function resolveTrustGatePolicy(config: DriftConfig | undefined, branchName?: string, overrides?: TrustGateOptions): TrustGateOptions;
54
+ export declare function resolveTrustGatePolicy(config: DriftConfig | undefined, options?: TrustGatePolicyResolutionOptions): TrustGateOptions;
55
+ export declare function formatTrustGatePolicyExplanation(explanation: TrustGatePolicyExplanation): string;
56
+ export declare function buildTrustReport(report: DriftReport, options?: BuildTrustOptions): DriftTrustReport;
57
+ export declare function formatTrustConsole(trust: DriftTrustReport): string;
58
+ export declare function formatTrustMarkdown(trust: DriftTrustReport): string;
59
+ export declare function formatTrustJson(trust: DriftTrustReport): string;
60
+ export declare function renderTrustOutput(trust: DriftTrustReport, options?: TrustRenderOptions): string;
61
+ export declare function shouldFailByMaxRisk(actual: MergeRiskLevel, allowedMaxRisk: MergeRiskLevel): boolean;
62
+ export declare function evaluateTrustGate(trust: DriftTrustReport, options: TrustGateOptions): TrustGateEvaluation;
63
+ export declare function shouldFailTrustGate(trust: DriftTrustReport, options: TrustGateOptions): boolean;
64
+ export {};
65
+ //# sourceMappingURL=trust.d.ts.map