@lanefoundry/source-prod-slice 0.0.0-stage → 0.1.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/audit-export.d.ts +58 -0
- package/dist/audit-export.d.ts.map +1 -0
- package/dist/audit-export.js +341 -0
- package/dist/audit-export.js.map +1 -0
- package/dist/canary-orchestrator.d.ts +150 -0
- package/dist/canary-orchestrator.d.ts.map +1 -0
- package/dist/canary-orchestrator.js +274 -0
- package/dist/canary-orchestrator.js.map +1 -0
- package/dist/compare-scores.d.ts +55 -0
- package/dist/compare-scores.d.ts.map +1 -0
- package/dist/compare-scores.js +131 -0
- package/dist/compare-scores.js.map +1 -0
- package/dist/freeze-slice.d.ts +63 -0
- package/dist/freeze-slice.d.ts.map +1 -0
- package/dist/freeze-slice.js +85 -0
- package/dist/freeze-slice.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/promotion-decision.d.ts +64 -0
- package/dist/promotion-decision.d.ts.map +1 -0
- package/dist/promotion-decision.js +105 -0
- package/dist/promotion-decision.js.map +1 -0
- package/dist/replay-batch.d.ts +71 -0
- package/dist/replay-batch.d.ts.map +1 -0
- package/dist/replay-batch.js +82 -0
- package/dist/replay-batch.js.map +1 -0
- package/dist/signed-report.d.ts +82 -0
- package/dist/signed-report.d.ts.map +1 -0
- package/dist/signed-report.js +104 -0
- package/dist/signed-report.js.map +1 -0
- package/package.json +65 -4
- package/README.md +0 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-export.ts — Export PromotionReport for compliance (JSON/CSV).
|
|
3
|
+
*
|
|
4
|
+
* Produces audit-ready exports of promotion decisions with full traceability.
|
|
5
|
+
* Supports JSON (full fidelity) and CSV (spreadsheet-friendly) formats.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/prd.md §5.1 — The gate (audit trail)
|
|
8
|
+
*/
|
|
9
|
+
import type { PromotionReport, PromotionDecision } from '@lanefoundry/gatelane-sdk';
|
|
10
|
+
import type { FrozenDataset } from '@lanefoundry/gatelane-sdk';
|
|
11
|
+
/** Export format. */
|
|
12
|
+
export type ExportFormat = 'json' | 'csv';
|
|
13
|
+
/** Arguments for audit export. */
|
|
14
|
+
export type AuditExportArgs = {
|
|
15
|
+
/** The signed PromotionReport to export. */
|
|
16
|
+
report: PromotionReport;
|
|
17
|
+
/** The PromotionDecision. */
|
|
18
|
+
decision: PromotionDecision;
|
|
19
|
+
/** The FrozenDataset used (optional, for context). */
|
|
20
|
+
dataset?: FrozenDataset;
|
|
21
|
+
/** Export format. Default: 'json'. */
|
|
22
|
+
format?: ExportFormat;
|
|
23
|
+
/** Include full judge matrix in export. Default: true. */
|
|
24
|
+
includeJudgeMatrix?: boolean;
|
|
25
|
+
/** Include per-candidate metric details. Default: true. */
|
|
26
|
+
includeMetrics?: boolean;
|
|
27
|
+
/** Include dataset metadata. Default: true. */
|
|
28
|
+
includeDataset?: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** Result of audit export. */
|
|
31
|
+
export type AuditExportResult = {
|
|
32
|
+
/** Exported content as string. */
|
|
33
|
+
content: string;
|
|
34
|
+
/** MIME type of the export. */
|
|
35
|
+
mimeType: string;
|
|
36
|
+
/** Suggested filename. */
|
|
37
|
+
filename: string;
|
|
38
|
+
/** Export format used. */
|
|
39
|
+
format: ExportFormat;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Export a PromotionReport for compliance/audit.
|
|
43
|
+
*/
|
|
44
|
+
export declare function exportAudit(args: AuditExportArgs): AuditExportResult;
|
|
45
|
+
/**
|
|
46
|
+
* Export multiple PromotionReports as a combined CSV.
|
|
47
|
+
* Useful for batch audit reviews.
|
|
48
|
+
*/
|
|
49
|
+
export declare function exportAuditBatch(reports: ReadonlyArray<{
|
|
50
|
+
report: PromotionReport;
|
|
51
|
+
decision: PromotionDecision;
|
|
52
|
+
dataset?: FrozenDataset;
|
|
53
|
+
}>, format?: ExportFormat): AuditExportResult;
|
|
54
|
+
/**
|
|
55
|
+
* Export a human-readable summary for quick review.
|
|
56
|
+
*/
|
|
57
|
+
export declare function exportSummary(report: PromotionReport, decision: PromotionDecision): string;
|
|
58
|
+
//# sourceMappingURL=audit-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-export.d.ts","sourceRoot":"","sources":["../src/audit-export.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,qBAAqB;AACrB,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1C,kCAAkC;AAClC,MAAM,MAAM,eAAe,GAAG;IAC5B,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,6BAA6B;IAC7B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,sDAAsD;IACtD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,sCAAsC;IACtC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,8BAA8B;AAC9B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAepE;AA8MD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC,EACzG,MAAM,GAAE,YAAoB,GAC3B,iBAAiB,CA4GnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAuC1F"}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-export.ts — Export PromotionReport for compliance (JSON/CSV).
|
|
3
|
+
*
|
|
4
|
+
* Produces audit-ready exports of promotion decisions with full traceability.
|
|
5
|
+
* Supports JSON (full fidelity) and CSV (spreadsheet-friendly) formats.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/prd.md §5.1 — The gate (audit trail)
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Export a PromotionReport for compliance/audit.
|
|
11
|
+
*/
|
|
12
|
+
export function exportAudit(args) {
|
|
13
|
+
const { report, decision, dataset, format = 'json', includeJudgeMatrix = true, includeMetrics = true, includeDataset = true, } = args;
|
|
14
|
+
if (format === 'json') {
|
|
15
|
+
return exportJson(report, decision, dataset, includeJudgeMatrix, includeMetrics, includeDataset);
|
|
16
|
+
}
|
|
17
|
+
return exportCsv(report, decision, dataset, includeJudgeMatrix, includeMetrics, includeDataset);
|
|
18
|
+
}
|
|
19
|
+
/** Export as full-fidelity JSON. */
|
|
20
|
+
function exportJson(report, decision, dataset, includeJudgeMatrix, includeMetrics, includeDataset) {
|
|
21
|
+
const output = {
|
|
22
|
+
report: includeJudgeMatrix && includeMetrics
|
|
23
|
+
? report
|
|
24
|
+
: stripReport(report, includeJudgeMatrix, includeMetrics),
|
|
25
|
+
decision,
|
|
26
|
+
exported_at: new Date().toISOString(),
|
|
27
|
+
export_version: '1.0',
|
|
28
|
+
};
|
|
29
|
+
if (includeDataset && dataset) {
|
|
30
|
+
output.dataset = {
|
|
31
|
+
content_hash: dataset.content_hash,
|
|
32
|
+
version: dataset.version,
|
|
33
|
+
source_kind: dataset.source_kind,
|
|
34
|
+
source_ref: dataset.source_ref,
|
|
35
|
+
slice_filter: dataset.slice_filter,
|
|
36
|
+
frozen_at: dataset.frozen_at,
|
|
37
|
+
item_count: dataset.item_count,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const content = JSON.stringify(output, null, 2);
|
|
41
|
+
return {
|
|
42
|
+
content,
|
|
43
|
+
mimeType: 'application/json',
|
|
44
|
+
filename: `promotion-report-${report.id}-${Date.now()}.json`,
|
|
45
|
+
format: 'json',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Export as CSV (one row per candidate). */
|
|
49
|
+
function exportCsv(report, decision, dataset, _includeJudgeMatrix, _includeMetrics, _includeDataset) {
|
|
50
|
+
const rows = [];
|
|
51
|
+
const headers = [
|
|
52
|
+
'report_id',
|
|
53
|
+
'gate_run_id',
|
|
54
|
+
'dataset_content_hash',
|
|
55
|
+
'dataset_version',
|
|
56
|
+
'dataset_source_kind',
|
|
57
|
+
'dataset_source_ref',
|
|
58
|
+
'dataset_frozen_at',
|
|
59
|
+
'dataset_item_count',
|
|
60
|
+
'candidate_ref',
|
|
61
|
+
'candidate_sha',
|
|
62
|
+
'judge_sha',
|
|
63
|
+
'aggregate_delta',
|
|
64
|
+
'cost_delta',
|
|
65
|
+
'latency_delta',
|
|
66
|
+
'mean_score',
|
|
67
|
+
'pass_rate',
|
|
68
|
+
'n_items',
|
|
69
|
+
'total_cost_usd',
|
|
70
|
+
'mean_latency_ms',
|
|
71
|
+
'judge_wins',
|
|
72
|
+
'is_consensus_winner',
|
|
73
|
+
'policy_min_delta',
|
|
74
|
+
'policy_judge_stability_threshold',
|
|
75
|
+
'policy_cost_ceiling',
|
|
76
|
+
'policy_latency_ceiling',
|
|
77
|
+
'policy_approval_required',
|
|
78
|
+
'decision_action',
|
|
79
|
+
'decision_winner',
|
|
80
|
+
'decision_reason',
|
|
81
|
+
'approver',
|
|
82
|
+
'report_timestamp',
|
|
83
|
+
'signature',
|
|
84
|
+
];
|
|
85
|
+
rows.push(headers);
|
|
86
|
+
const { candidate_metrics, candidate_shas, judge_shas, judge_matrix, policy } = report;
|
|
87
|
+
const firstJudgeKey = Object.keys(judge_shas)[0];
|
|
88
|
+
for (const candidateRef of Object.keys(candidate_metrics)) {
|
|
89
|
+
const metric = candidate_metrics[candidateRef];
|
|
90
|
+
if (!metric)
|
|
91
|
+
continue;
|
|
92
|
+
const candidateSha = candidate_shas[candidateRef] ?? '';
|
|
93
|
+
const judgeSha = firstJudgeKey ? judge_shas[firstJudgeKey] ?? '' : '';
|
|
94
|
+
const judgeWins = judge_matrix.winners_by_judge[candidateRef] ?? 0;
|
|
95
|
+
const isConsensus = judge_matrix.consensus_winners.includes(candidateRef);
|
|
96
|
+
const row = [
|
|
97
|
+
report.id,
|
|
98
|
+
report.gate_run_id,
|
|
99
|
+
report.dataset_content_hash,
|
|
100
|
+
report.dataset_version,
|
|
101
|
+
dataset?.source_kind ?? '',
|
|
102
|
+
dataset?.source_ref ?? '',
|
|
103
|
+
dataset?.frozen_at ?? '',
|
|
104
|
+
String(dataset?.item_count ?? ''),
|
|
105
|
+
candidateRef,
|
|
106
|
+
candidateSha,
|
|
107
|
+
judgeSha,
|
|
108
|
+
String(metric.aggregate_delta),
|
|
109
|
+
String(metric.cost_delta),
|
|
110
|
+
String(metric.latency_delta),
|
|
111
|
+
// Note: CandidateMetric doesn't have mean_score/pass_rate directly
|
|
112
|
+
// These would need to come from the original compare output
|
|
113
|
+
'', // mean_score
|
|
114
|
+
'', // pass_rate
|
|
115
|
+
'', // n_items
|
|
116
|
+
'', // total_cost_usd
|
|
117
|
+
'', // mean_latency_ms
|
|
118
|
+
String(judgeWins),
|
|
119
|
+
String(isConsensus),
|
|
120
|
+
String(policy.min_delta),
|
|
121
|
+
String(policy.judge_stability_threshold),
|
|
122
|
+
String(policy.cost_ceiling),
|
|
123
|
+
String(policy.latency_ceiling),
|
|
124
|
+
String(policy.approval_required),
|
|
125
|
+
decision.action,
|
|
126
|
+
'winner' in decision ? decision.winner : '',
|
|
127
|
+
decision.reason,
|
|
128
|
+
report.approver ?? '',
|
|
129
|
+
report.timestamp,
|
|
130
|
+
report.signature,
|
|
131
|
+
];
|
|
132
|
+
rows.push(row);
|
|
133
|
+
}
|
|
134
|
+
// Add a summary row for the overall decision
|
|
135
|
+
if (decision.action === 'pass' && 'winner' in decision) {
|
|
136
|
+
const winnerMetric = candidate_metrics[decision.winner];
|
|
137
|
+
if (winnerMetric) {
|
|
138
|
+
const summaryRow = [
|
|
139
|
+
report.id,
|
|
140
|
+
report.gate_run_id,
|
|
141
|
+
report.dataset_content_hash,
|
|
142
|
+
report.dataset_version,
|
|
143
|
+
dataset?.source_kind ?? '',
|
|
144
|
+
dataset?.source_ref ?? '',
|
|
145
|
+
dataset?.frozen_at ?? '',
|
|
146
|
+
String(dataset?.item_count ?? ''),
|
|
147
|
+
`SUMMARY (${decision.winner})`,
|
|
148
|
+
candidate_shas[decision.winner] ?? '',
|
|
149
|
+
(() => { const jk = Object.keys(judge_shas)[0]; return jk ? judge_shas[jk] ?? '' : ''; })(),
|
|
150
|
+
String(winnerMetric.aggregate_delta),
|
|
151
|
+
String(winnerMetric.cost_delta),
|
|
152
|
+
String(winnerMetric.latency_delta),
|
|
153
|
+
'', '', '', '', '',
|
|
154
|
+
String(judge_matrix.winners_by_judge[decision.winner] ?? 0),
|
|
155
|
+
String(judge_matrix.consensus_winners.includes(decision.winner)),
|
|
156
|
+
String(policy.min_delta),
|
|
157
|
+
String(policy.judge_stability_threshold),
|
|
158
|
+
String(policy.cost_ceiling),
|
|
159
|
+
String(policy.latency_ceiling),
|
|
160
|
+
String(policy.approval_required),
|
|
161
|
+
decision.action,
|
|
162
|
+
decision.winner,
|
|
163
|
+
decision.reason,
|
|
164
|
+
report.approver ?? '',
|
|
165
|
+
report.timestamp,
|
|
166
|
+
report.signature,
|
|
167
|
+
];
|
|
168
|
+
rows.push(summaryRow);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const content = rows.map((row) => row.map(escapeCsv).join(',')).join('\n');
|
|
172
|
+
return {
|
|
173
|
+
content,
|
|
174
|
+
mimeType: 'text/csv',
|
|
175
|
+
filename: `promotion-report-${report.id}-${Date.now()}.csv`,
|
|
176
|
+
format: 'csv',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/** Escape a value for CSV. */
|
|
180
|
+
function escapeCsv(value) {
|
|
181
|
+
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
|
|
182
|
+
return '"' + value.replace(/"/g, '""') + '"';
|
|
183
|
+
}
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
/** Strip optional fields from report for smaller exports. */
|
|
187
|
+
function stripReport(report, includeJudgeMatrix, includeMetrics) {
|
|
188
|
+
const { judge_matrix, candidate_metrics, ...rest } = report;
|
|
189
|
+
return {
|
|
190
|
+
...rest,
|
|
191
|
+
...(includeJudgeMatrix ? { judge_matrix } : { judge_matrix: { candidates: [], judges: [], winners_by_judge: {}, consensus_winners: [] } }),
|
|
192
|
+
...(includeMetrics ? { candidate_metrics } : { candidate_metrics: {} }),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Export multiple PromotionReports as a combined CSV.
|
|
197
|
+
* Useful for batch audit reviews.
|
|
198
|
+
*/
|
|
199
|
+
export function exportAuditBatch(reports, format = 'csv') {
|
|
200
|
+
if (format === 'json') {
|
|
201
|
+
const content = JSON.stringify({
|
|
202
|
+
exports: reports.map(({ report, decision, dataset }) => ({
|
|
203
|
+
report: stripReport(report, true, true),
|
|
204
|
+
decision,
|
|
205
|
+
dataset: dataset ? {
|
|
206
|
+
content_hash: dataset.content_hash,
|
|
207
|
+
version: dataset.version,
|
|
208
|
+
source_kind: dataset.source_kind,
|
|
209
|
+
source_ref: dataset.source_ref,
|
|
210
|
+
frozen_at: dataset.frozen_at,
|
|
211
|
+
item_count: dataset.item_count,
|
|
212
|
+
} : undefined,
|
|
213
|
+
})),
|
|
214
|
+
exported_at: new Date().toISOString(),
|
|
215
|
+
export_version: '1.0',
|
|
216
|
+
count: reports.length,
|
|
217
|
+
}, null, 2);
|
|
218
|
+
return {
|
|
219
|
+
content,
|
|
220
|
+
mimeType: 'application/json',
|
|
221
|
+
filename: `promotion-reports-batch-${Date.now()}.json`,
|
|
222
|
+
format: 'json',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// CSV batch export
|
|
226
|
+
const allRows = [];
|
|
227
|
+
const headers = [
|
|
228
|
+
'report_id',
|
|
229
|
+
'gate_run_id',
|
|
230
|
+
'dataset_content_hash',
|
|
231
|
+
'dataset_version',
|
|
232
|
+
'dataset_source_kind',
|
|
233
|
+
'dataset_source_ref',
|
|
234
|
+
'dataset_frozen_at',
|
|
235
|
+
'dataset_item_count',
|
|
236
|
+
'candidate_ref',
|
|
237
|
+
'candidate_sha',
|
|
238
|
+
'aggregate_delta',
|
|
239
|
+
'cost_delta',
|
|
240
|
+
'latency_delta',
|
|
241
|
+
'judge_wins',
|
|
242
|
+
'is_consensus_winner',
|
|
243
|
+
'policy_min_delta',
|
|
244
|
+
'policy_judge_stability_threshold',
|
|
245
|
+
'policy_cost_ceiling',
|
|
246
|
+
'policy_latency_ceiling',
|
|
247
|
+
'policy_approval_required',
|
|
248
|
+
'decision_action',
|
|
249
|
+
'decision_winner',
|
|
250
|
+
'decision_reason',
|
|
251
|
+
'approver',
|
|
252
|
+
'report_timestamp',
|
|
253
|
+
'signature',
|
|
254
|
+
];
|
|
255
|
+
allRows.push(headers);
|
|
256
|
+
for (const { report, decision, dataset } of reports) {
|
|
257
|
+
const { candidate_metrics, candidate_shas, judge_matrix, policy } = report;
|
|
258
|
+
for (const candidateRef of Object.keys(candidate_metrics)) {
|
|
259
|
+
const metric = candidate_metrics[candidateRef];
|
|
260
|
+
if (!metric)
|
|
261
|
+
continue;
|
|
262
|
+
const judgeWins = judge_matrix.winners_by_judge[candidateRef] ?? 0;
|
|
263
|
+
const isConsensus = judge_matrix.consensus_winners.includes(candidateRef);
|
|
264
|
+
const row = [
|
|
265
|
+
report.id,
|
|
266
|
+
report.gate_run_id,
|
|
267
|
+
report.dataset_content_hash,
|
|
268
|
+
report.dataset_version,
|
|
269
|
+
dataset?.source_kind ?? '',
|
|
270
|
+
dataset?.source_ref ?? '',
|
|
271
|
+
dataset?.frozen_at ?? '',
|
|
272
|
+
String(dataset?.item_count ?? ''),
|
|
273
|
+
candidateRef,
|
|
274
|
+
candidate_shas[candidateRef] ?? '',
|
|
275
|
+
String(metric.aggregate_delta),
|
|
276
|
+
String(metric.cost_delta),
|
|
277
|
+
String(metric.latency_delta),
|
|
278
|
+
String(judgeWins),
|
|
279
|
+
String(isConsensus),
|
|
280
|
+
String(policy.min_delta),
|
|
281
|
+
String(policy.judge_stability_threshold),
|
|
282
|
+
String(policy.cost_ceiling),
|
|
283
|
+
String(policy.latency_ceiling),
|
|
284
|
+
String(policy.approval_required),
|
|
285
|
+
decision.action,
|
|
286
|
+
'winner' in decision ? decision.winner : '',
|
|
287
|
+
decision.reason,
|
|
288
|
+
report.approver ?? '',
|
|
289
|
+
report.timestamp,
|
|
290
|
+
report.signature,
|
|
291
|
+
];
|
|
292
|
+
allRows.push(row);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const content = allRows.map((row) => row.map(escapeCsv).join(',')).join('\n');
|
|
296
|
+
return {
|
|
297
|
+
content,
|
|
298
|
+
mimeType: 'text/csv',
|
|
299
|
+
filename: `promotion-reports-batch-${Date.now()}.csv`,
|
|
300
|
+
format: 'csv',
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Export a human-readable summary for quick review.
|
|
305
|
+
*/
|
|
306
|
+
export function exportSummary(report, decision) {
|
|
307
|
+
const lines = [
|
|
308
|
+
`=== Promotion Gate Report ===`,
|
|
309
|
+
`Report ID: ${report.id}`,
|
|
310
|
+
`Gate Run: ${report.gate_run_id}`,
|
|
311
|
+
`Dataset: ${report.dataset_content_hash} (v${report.dataset_version})`,
|
|
312
|
+
`Timestamp: ${report.timestamp}`,
|
|
313
|
+
`Decision: ${decision.action.toUpperCase()}`,
|
|
314
|
+
...(decision.action === 'pass' && 'winner' in decision ? [`Winner: ${decision.winner}`] : []),
|
|
315
|
+
`Reason: ${decision.reason}`,
|
|
316
|
+
`Approver: ${report.approver ?? 'N/A'}`,
|
|
317
|
+
`Signature: ${report.signature.slice(0, 16)}...`,
|
|
318
|
+
``,
|
|
319
|
+
`=== Policy ===`,
|
|
320
|
+
`Min Delta: ${report.policy.min_delta}`,
|
|
321
|
+
`Judge Stability: ${report.policy.judge_stability_threshold}`,
|
|
322
|
+
`Cost Ceiling: ${report.policy.cost_ceiling}`,
|
|
323
|
+
`Latency Ceiling: ${report.policy.latency_ceiling}`,
|
|
324
|
+
`Approval Required: ${report.policy.approval_required}`,
|
|
325
|
+
``,
|
|
326
|
+
`=== Judge Matrix ===`,
|
|
327
|
+
`Judges: ${report.judge_matrix.judges.join(', ')}`,
|
|
328
|
+
`Consensus Winners: ${report.judge_matrix.consensus_winners.join(', ') || 'none'}`,
|
|
329
|
+
``,
|
|
330
|
+
`=== Candidate Metrics ===`,
|
|
331
|
+
];
|
|
332
|
+
for (const [ref, metric] of Object.entries(report.candidate_metrics)) {
|
|
333
|
+
lines.push(` ${ref}: delta=${metric.aggregate_delta.toFixed(4)}, cost_delta=${metric.cost_delta.toFixed(4)}, latency_delta=${metric.latency_delta.toFixed(4)}`);
|
|
334
|
+
}
|
|
335
|
+
lines.push(``, `=== Judge Wins ===`);
|
|
336
|
+
for (const [ref, wins] of Object.entries(report.judge_matrix.winners_by_judge)) {
|
|
337
|
+
lines.push(` ${ref}: ${wins} win(s)`);
|
|
338
|
+
}
|
|
339
|
+
return lines.join('\n');
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=audit-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-export.js","sourceRoot":"","sources":["../src/audit-export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* canary-orchestrator.ts — Canary state machine: 10% → 24h observe → promote 100% or rollback.
|
|
3
|
+
*
|
|
4
|
+
* Manages the canary lifecycle with D1 persistence for durability.
|
|
5
|
+
* States: pending → canary → observing → promoting → promoted | rolled_back | failed
|
|
6
|
+
*
|
|
7
|
+
* @see docs/prd.md §5.1 — The gate (auto_block_rule)
|
|
8
|
+
*/
|
|
9
|
+
import type { PromotionReport, PromotionDecision, PromotionPolicy } from '@lanefoundry/gatelane-sdk/promotion';
|
|
10
|
+
import type { D1DatabaseLike } from '@lanefoundry/gatelane-sdk';
|
|
11
|
+
/** Canary deployment state. */
|
|
12
|
+
export type CanaryState = 'pending' | 'canary' | 'observing' | 'promoting' | 'promoted' | 'rolled_back' | 'failed';
|
|
13
|
+
/** Canary deployment record (persisted in D1). */
|
|
14
|
+
export type CanaryRecord = {
|
|
15
|
+
/** Unique canary deployment ID. */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Gate run ID that produced the promotion decision. */
|
|
18
|
+
gateRunId: string;
|
|
19
|
+
/** Winner candidate reference. */
|
|
20
|
+
candidateRef: string;
|
|
21
|
+
/** Current state. */
|
|
22
|
+
state: CanaryState;
|
|
23
|
+
/** Canary traffic percentage (0-100). */
|
|
24
|
+
trafficPercent: number;
|
|
25
|
+
/** When the canary was started. */
|
|
26
|
+
startedAt: string;
|
|
27
|
+
/** When the observation window ends (ISO 8601). */
|
|
28
|
+
observationEndsAt?: string;
|
|
29
|
+
/** When the canary was completed (promoted/rolled_back). */
|
|
30
|
+
completedAt?: string;
|
|
31
|
+
/** Auto-rollback rule from policy. */
|
|
32
|
+
autoRollbackRule?: PromotionPolicy['auto_block_rule'];
|
|
33
|
+
/** Metric observations during canary. */
|
|
34
|
+
observations: CanaryObservation[];
|
|
35
|
+
/** Error message if failed. */
|
|
36
|
+
error?: string;
|
|
37
|
+
/** The original PromotionReport. */
|
|
38
|
+
report: PromotionReport;
|
|
39
|
+
/** The original PromotionDecision. */
|
|
40
|
+
decision: PromotionDecision;
|
|
41
|
+
};
|
|
42
|
+
/** Single metric observation during canary. */
|
|
43
|
+
export type CanaryObservation = {
|
|
44
|
+
timestamp: string;
|
|
45
|
+
/** Metric name (e.g., 'error_rate', 'latency_p99', 'cost_per_1k'). */
|
|
46
|
+
metric: string;
|
|
47
|
+
/** Current value. */
|
|
48
|
+
value: number;
|
|
49
|
+
/** Baseline value for comparison. */
|
|
50
|
+
baseline: number;
|
|
51
|
+
/** Delta from baseline (value - baseline) / baseline. */
|
|
52
|
+
delta: number;
|
|
53
|
+
};
|
|
54
|
+
/** Arguments for starting a canary. */
|
|
55
|
+
export type StartCanaryArgs = {
|
|
56
|
+
/** Gate run ID from the promotion report. */
|
|
57
|
+
gateRunId: string;
|
|
58
|
+
/** The winning candidate reference. */
|
|
59
|
+
candidateRef: string;
|
|
60
|
+
/** The signed PromotionReport. */
|
|
61
|
+
report: PromotionReport;
|
|
62
|
+
/** The PromotionDecision (must be 'pass'). */
|
|
63
|
+
decision: PromotionDecision;
|
|
64
|
+
/** Initial canary traffic percentage. Default: 10. */
|
|
65
|
+
initialTrafficPercent?: number;
|
|
66
|
+
/** Custom observation window. Default: from policy.auto_block_rule.window or "24h". */
|
|
67
|
+
observationWindow?: string;
|
|
68
|
+
};
|
|
69
|
+
/** Result of canary operations. */
|
|
70
|
+
export type CanaryResult = {
|
|
71
|
+
/** The canary record. */
|
|
72
|
+
record: CanaryRecord;
|
|
73
|
+
/** Whether the operation advanced the state. */
|
|
74
|
+
advanced: boolean;
|
|
75
|
+
};
|
|
76
|
+
/** D1 storage interface for canary persistence. */
|
|
77
|
+
export interface CanaryStorage {
|
|
78
|
+
/** Create a new canary record. */
|
|
79
|
+
create(record: CanaryRecord): Promise<void>;
|
|
80
|
+
/** Read a canary record by ID. */
|
|
81
|
+
read(id: string): Promise<CanaryRecord | null>;
|
|
82
|
+
/** Update an existing canary record. */
|
|
83
|
+
update(record: CanaryRecord): Promise<void>;
|
|
84
|
+
/** List canary records (optionally filtered by state). */
|
|
85
|
+
list(filter?: {
|
|
86
|
+
state?: CanaryState;
|
|
87
|
+
limit?: number;
|
|
88
|
+
}): Promise<ReadonlyArray<CanaryRecord>>;
|
|
89
|
+
}
|
|
90
|
+
/** In-memory storage for tests / local dev. */
|
|
91
|
+
export declare class InMemoryCanaryStorage implements CanaryStorage {
|
|
92
|
+
#private;
|
|
93
|
+
create(record: CanaryRecord): Promise<void>;
|
|
94
|
+
read(id: string): Promise<CanaryRecord | null>;
|
|
95
|
+
update(record: CanaryRecord): Promise<void>;
|
|
96
|
+
list(filter?: {
|
|
97
|
+
state?: CanaryState;
|
|
98
|
+
limit?: number;
|
|
99
|
+
}): Promise<ReadonlyArray<CanaryRecord>>;
|
|
100
|
+
}
|
|
101
|
+
/** Replace the active canary storage. Returns previous for test restoration. */
|
|
102
|
+
export declare function setCanaryStorage(storage: CanaryStorage): CanaryStorage;
|
|
103
|
+
/** Get the active canary storage. */
|
|
104
|
+
export declare function getCanaryStorage(): CanaryStorage;
|
|
105
|
+
/**
|
|
106
|
+
* Start a new canary deployment.
|
|
107
|
+
* Creates the record in 'canary' state with 10% traffic.
|
|
108
|
+
*/
|
|
109
|
+
export declare function startCanary(args: StartCanaryArgs): Promise<CanaryResult>;
|
|
110
|
+
/**
|
|
111
|
+
* Record a metric observation for a canary.
|
|
112
|
+
* Checks auto-rollback rule and transitions to rolled_back if triggered.
|
|
113
|
+
*/
|
|
114
|
+
export declare function recordObservation(canaryId: string, metric: string, value: number, baseline: number): Promise<CanaryResult>;
|
|
115
|
+
/**
|
|
116
|
+
* Advance the canary state machine.
|
|
117
|
+
* canary -> observing -> promoting -> promoted
|
|
118
|
+
*/
|
|
119
|
+
export declare function advanceCanary(canaryId: string): Promise<CanaryResult>;
|
|
120
|
+
/**
|
|
121
|
+
* Manually rollback a canary.
|
|
122
|
+
*/
|
|
123
|
+
export declare function rollbackCanary(canaryId: string, reason: string): Promise<CanaryResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Mark a canary as failed (e.g., deployment error).
|
|
126
|
+
*/
|
|
127
|
+
export declare function failCanary(canaryId: string, error: string): Promise<CanaryResult>;
|
|
128
|
+
/**
|
|
129
|
+
* Get all canaries that need observation window checks.
|
|
130
|
+
* Useful for a cron job that periodically calls advanceCanary.
|
|
131
|
+
*/
|
|
132
|
+
export declare function getActiveCanaries(): Promise<ReadonlyArray<CanaryRecord>>;
|
|
133
|
+
/**
|
|
134
|
+
* Check and advance all active canaries.
|
|
135
|
+
* Returns records that changed state.
|
|
136
|
+
*/
|
|
137
|
+
export declare function tickCanaries(): Promise<ReadonlyArray<CanaryRecord>>;
|
|
138
|
+
export declare class D1CanaryStorage implements CanaryStorage {
|
|
139
|
+
private readonly db;
|
|
140
|
+
constructor(db: D1DatabaseLike);
|
|
141
|
+
create(record: CanaryRecord): Promise<void>;
|
|
142
|
+
read(id: string): Promise<CanaryRecord | null>;
|
|
143
|
+
update(record: CanaryRecord): Promise<void>;
|
|
144
|
+
list(filter?: {
|
|
145
|
+
state?: CanaryState;
|
|
146
|
+
limit?: number;
|
|
147
|
+
}): Promise<ReadonlyArray<CanaryRecord>>;
|
|
148
|
+
private rowToRecord;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=canary-orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canary-orchestrator.d.ts","sourceRoot":"","sources":["../src/canary-orchestrator.ts"],"names":[],"mappings":"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"}
|