@derwinjs/db 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/LICENSE +25 -0
- package/README.md +79 -0
- package/dist/agent-findings-ingestor.d.ts +40 -0
- package/dist/agent-findings-ingestor.d.ts.map +1 -0
- package/dist/agent-findings-ingestor.js +154 -0
- package/dist/agent-findings-ingestor.js.map +1 -0
- package/dist/classification-trust-store.d.ts +31 -0
- package/dist/classification-trust-store.d.ts.map +1 -0
- package/dist/classification-trust-store.js +154 -0
- package/dist/classification-trust-store.js.map +1 -0
- package/dist/coverage-gap-reporter.d.ts +35 -0
- package/dist/coverage-gap-reporter.d.ts.map +1 -0
- package/dist/coverage-gap-reporter.js +84 -0
- package/dist/coverage-gap-reporter.js.map +1 -0
- package/dist/fix-policy.d.ts +46 -0
- package/dist/fix-policy.d.ts.map +1 -0
- package/dist/fix-policy.js +162 -0
- package/dist/fix-policy.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/learning-health-reporter.d.ts +37 -0
- package/dist/learning-health-reporter.d.ts.map +1 -0
- package/dist/learning-health-reporter.js +141 -0
- package/dist/learning-health-reporter.js.map +1 -0
- package/dist/prisma.d.ts +31 -0
- package/dist/prisma.d.ts.map +1 -0
- package/dist/prisma.js +31 -0
- package/dist/prisma.js.map +1 -0
- package/dist/qa-fix-attempt-store.d.ts +28 -0
- package/dist/qa-fix-attempt-store.d.ts.map +1 -0
- package/dist/qa-fix-attempt-store.js +258 -0
- package/dist/qa-fix-attempt-store.js.map +1 -0
- package/dist/qa-pattern-store.d.ts +32 -0
- package/dist/qa-pattern-store.d.ts.map +1 -0
- package/dist/qa-pattern-store.js +123 -0
- package/dist/qa-pattern-store.js.map +1 -0
- package/dist/qa-revert-store.d.ts +24 -0
- package/dist/qa-revert-store.d.ts.map +1 -0
- package/dist/qa-revert-store.js +139 -0
- package/dist/qa-revert-store.js.map +1 -0
- package/dist/qa-run-store.d.ts +46 -0
- package/dist/qa-run-store.d.ts.map +1 -0
- package/dist/qa-run-store.js +201 -0
- package/dist/qa-run-store.js.map +1 -0
- package/dist/qa-ticket-store.d.ts +35 -0
- package/dist/qa-ticket-store.d.ts.map +1 -0
- package/dist/qa-ticket-store.js +293 -0
- package/dist/qa-ticket-store.js.map +1 -0
- package/dist/qa-uniformity-store.d.ts +41 -0
- package/dist/qa-uniformity-store.d.ts.map +1 -0
- package/dist/qa-uniformity-store.js +288 -0
- package/dist/qa-uniformity-store.js.map +1 -0
- package/dist/scripts/smoke-auto-fix.d.ts +37 -0
- package/dist/scripts/smoke-auto-fix.d.ts.map +1 -0
- package/dist/scripts/smoke-auto-fix.js +270 -0
- package/dist/scripts/smoke-auto-fix.js.map +1 -0
- package/dist/scripts/smoke-learning-loop.d.ts +21 -0
- package/dist/scripts/smoke-learning-loop.d.ts.map +1 -0
- package/dist/scripts/smoke-learning-loop.js +375 -0
- package/dist/scripts/smoke-learning-loop.js.map +1 -0
- package/dist/scripts/smoke-orchestration.d.ts +35 -0
- package/dist/scripts/smoke-orchestration.d.ts.map +1 -0
- package/dist/scripts/smoke-orchestration.js +215 -0
- package/dist/scripts/smoke-orchestration.js.map +1 -0
- package/dist/scripts/smoke-qa-ticket-store.d.ts +18 -0
- package/dist/scripts/smoke-qa-ticket-store.d.ts.map +1 -0
- package/dist/scripts/smoke-qa-ticket-store.js +233 -0
- package/dist/scripts/smoke-qa-ticket-store.js.map +1 -0
- package/package.json +69 -0
- package/prisma/migrations/20260501165631_init/migration.sql +407 -0
- package/prisma/migrations/20260503051425_0002_qap018b_qaticket_crosslink_fields/migration.sql +6 -0
- package/prisma/migrations/20260504231316_add_project_repofullname_and_webhooksecret/migration.sql +12 -0
- package/prisma/migrations/20260504232851_add_qaticket_resolvedby/migration.sql +2 -0
- package/prisma/migrations/20260505042646_add_qapattern_qarevert/migration.sql +77 -0
- package/prisma/migrations/20260505055826_add_qauniformity_and_agent_trigger/migration.sql +35 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +748 -0
- package/prisma/seed.ts +181 -0
- package/prisma-client/default.d.ts +1 -0
- package/prisma-client/default.js +1 -0
- package/prisma-client/edge.d.ts +1 -0
- package/prisma-client/edge.js +631 -0
- package/prisma-client/index-browser.js +615 -0
- package/prisma-client/index.d.ts +34509 -0
- package/prisma-client/index.js +660 -0
- package/prisma-client/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/prisma-client/libquery_engine-linux-arm64-openssl-3.0.x.so.node +0 -0
- package/prisma-client/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/prisma-client/package.json +97 -0
- package/prisma-client/runtime/edge-esm.js +31 -0
- package/prisma-client/runtime/edge.js +31 -0
- package/prisma-client/runtime/index-browser.d.ts +365 -0
- package/prisma-client/runtime/index-browser.js +13 -0
- package/prisma-client/runtime/library.d.ts +3403 -0
- package/prisma-client/runtime/library.js +143 -0
- package/prisma-client/runtime/react-native.js +80 -0
- package/prisma-client/runtime/wasm.js +32 -0
- package/prisma-client/schema.prisma +748 -0
- package/prisma-client/wasm.d.ts +1 -0
- package/prisma-client/wasm.js +615 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAUniformityStore — Prisma implementation against Derwin's QAUniformity
|
|
3
|
+
* table.
|
|
4
|
+
*
|
|
5
|
+
* QAP-019F (Sprint 2 Group D-3, Commit 1 foundation). Implements the
|
|
6
|
+
* QAUniformityStore contract from @derwinjs/sdk against the @derwinjs/db Prisma
|
|
7
|
+
* client. Migrated from Lifeline's `QAUniformityAudit` model + service —
|
|
8
|
+
* dropped the `Audit` suffix per Group D-3 plan Decision 5.
|
|
9
|
+
*
|
|
10
|
+
* Two methods:
|
|
11
|
+
* - ingestFindings: bulk-insert N findings as a single createMany call.
|
|
12
|
+
* Optionally tied to a qaRunId. Returns inserted + per-status counts.
|
|
13
|
+
* P2003 → fk_violation (unknown projectId or qaRunId).
|
|
14
|
+
* - getSummary: three aggregate sections for the dashboard's "Uniformity"
|
|
15
|
+
* tab — per-rule pass rates, latest violations (most recent run only),
|
|
16
|
+
* and trend across the most recent N runs (default 10, max 50).
|
|
17
|
+
*
|
|
18
|
+
* Tenant isolation: every method scopes by projectId in WHERE clauses.
|
|
19
|
+
* App-layer guard ahead of Sprint 3 RLS migration.
|
|
20
|
+
*/
|
|
21
|
+
import { Prisma } from './prisma.js';
|
|
22
|
+
import { QAUniformityStoreError, } from '@derwinjs/sdk';
|
|
23
|
+
// ─── Constants (Lifeline parity) ─────────────────────────────────────────
|
|
24
|
+
const DEFAULT_RECENT = 10;
|
|
25
|
+
const MAX_RECENT = 50;
|
|
26
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
27
|
+
export function createPrismaQAUniformityStore(config) {
|
|
28
|
+
const { prisma } = config;
|
|
29
|
+
return {
|
|
30
|
+
async ingestFindings(input) {
|
|
31
|
+
validateIngestInput(input);
|
|
32
|
+
const data = input.findings.map((f) => ({
|
|
33
|
+
projectId: input.projectId,
|
|
34
|
+
qaRunId: input.qaRunId ?? null,
|
|
35
|
+
pagePath: f.pagePath,
|
|
36
|
+
ruleName: f.ruleName,
|
|
37
|
+
ruleCategory: f.ruleCategory,
|
|
38
|
+
status: f.status,
|
|
39
|
+
violationDetail: f.violationDetail ?? null,
|
|
40
|
+
}));
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = await prisma.qAUniformity.createMany({ data });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
// P2003 surfaces as fk_violation — the projectId or qaRunId FK is
|
|
47
|
+
// bad. Distinguish from generic DB errors so the route handler
|
|
48
|
+
// returns 400 vs 500.
|
|
49
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2003') {
|
|
50
|
+
throw new QAUniformityStoreError('fk_violation', `QAUniformity FK violation: ${err.message}`, { projectId: input.projectId, qaRunId: input.qaRunId });
|
|
51
|
+
}
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
// Per-status counts derived from the input (createMany returns count
|
|
55
|
+
// only). Cheaper than another query and 1:1 with what we just wrote
|
|
56
|
+
// because createMany either inserts everything or throws.
|
|
57
|
+
let passed = 0;
|
|
58
|
+
let failed = 0;
|
|
59
|
+
let skipped = 0;
|
|
60
|
+
for (const f of input.findings) {
|
|
61
|
+
if (f.status === 'PASSED')
|
|
62
|
+
passed++;
|
|
63
|
+
else if (f.status === 'FAILED')
|
|
64
|
+
failed++;
|
|
65
|
+
else
|
|
66
|
+
skipped++; // f.status === 'SKIPPED' — the remaining union member.
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
inserted: result.count,
|
|
70
|
+
passed,
|
|
71
|
+
failed,
|
|
72
|
+
skipped,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
async getSummary(filter) {
|
|
76
|
+
validateProjectId(filter.projectId);
|
|
77
|
+
const recent = clampRecent(filter.recent);
|
|
78
|
+
// ─── Section 1: per-rule pass rates (project-wide) ─────────────────
|
|
79
|
+
//
|
|
80
|
+
// groupBy ruleName + ruleCategory + status. Walk the result once to
|
|
81
|
+
// produce one row per (ruleName, ruleCategory) with passed/failed/
|
|
82
|
+
// skipped counts and pass rate.
|
|
83
|
+
const grouped = await prisma.qAUniformity.groupBy({
|
|
84
|
+
by: ['ruleName', 'ruleCategory', 'status'],
|
|
85
|
+
where: { projectId: filter.projectId },
|
|
86
|
+
_count: { _all: true },
|
|
87
|
+
_max: { createdAt: true },
|
|
88
|
+
});
|
|
89
|
+
const ruleAcc = new Map();
|
|
90
|
+
for (const row of grouped) {
|
|
91
|
+
const key = `${row.ruleName}::${row.ruleCategory}`;
|
|
92
|
+
const acc = ruleAcc.get(key) ?? {
|
|
93
|
+
ruleName: row.ruleName,
|
|
94
|
+
ruleCategory: row.ruleCategory,
|
|
95
|
+
passedCount: 0,
|
|
96
|
+
failedCount: 0,
|
|
97
|
+
skippedCount: 0,
|
|
98
|
+
lastSeenAt: new Date(0),
|
|
99
|
+
};
|
|
100
|
+
const count = row._count._all;
|
|
101
|
+
if (row.status === 'PASSED')
|
|
102
|
+
acc.passedCount += count;
|
|
103
|
+
else if (row.status === 'FAILED')
|
|
104
|
+
acc.failedCount += count;
|
|
105
|
+
else
|
|
106
|
+
acc.skippedCount += count; // 'SKIPPED' — remaining union member.
|
|
107
|
+
const groupLastSeen = row._max.createdAt;
|
|
108
|
+
if (groupLastSeen !== null && groupLastSeen > acc.lastSeenAt) {
|
|
109
|
+
acc.lastSeenAt = groupLastSeen;
|
|
110
|
+
}
|
|
111
|
+
ruleAcc.set(key, acc);
|
|
112
|
+
}
|
|
113
|
+
const rules = Array.from(ruleAcc.values()).map((r) => {
|
|
114
|
+
const passFailDenominator = r.passedCount + r.failedCount;
|
|
115
|
+
const passRate = passFailDenominator > 0 ? r.passedCount / passFailDenominator : 0;
|
|
116
|
+
return {
|
|
117
|
+
ruleName: r.ruleName,
|
|
118
|
+
ruleCategory: r.ruleCategory,
|
|
119
|
+
totalCount: r.passedCount + r.failedCount + r.skippedCount,
|
|
120
|
+
passedCount: r.passedCount,
|
|
121
|
+
failedCount: r.failedCount,
|
|
122
|
+
skippedCount: r.skippedCount,
|
|
123
|
+
passRate,
|
|
124
|
+
lastSeenAt: r.lastSeenAt,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
// Sort: highest violation count first (most-broken rules at top).
|
|
128
|
+
rules.sort((a, b) => b.failedCount - a.failedCount);
|
|
129
|
+
// ─── Section 2: latest violations (most recent run only) ───────────
|
|
130
|
+
//
|
|
131
|
+
// Find the most recent qaRunId that has uniformity rows in this
|
|
132
|
+
// project, then list the FAILED rows for that run. Untied rows
|
|
133
|
+
// (qaRunId IS NULL) are excluded from latestViolations because
|
|
134
|
+
// "latest run" implies a run.
|
|
135
|
+
const latestRun = await prisma.qAUniformity.findFirst({
|
|
136
|
+
where: {
|
|
137
|
+
projectId: filter.projectId,
|
|
138
|
+
qaRunId: { not: null },
|
|
139
|
+
},
|
|
140
|
+
orderBy: { createdAt: 'desc' },
|
|
141
|
+
select: { qaRunId: true },
|
|
142
|
+
});
|
|
143
|
+
let latestViolations = [];
|
|
144
|
+
if (latestRun?.qaRunId !== undefined && latestRun.qaRunId !== null) {
|
|
145
|
+
const violations = await prisma.qAUniformity.findMany({
|
|
146
|
+
where: {
|
|
147
|
+
projectId: filter.projectId,
|
|
148
|
+
qaRunId: latestRun.qaRunId,
|
|
149
|
+
status: 'FAILED',
|
|
150
|
+
},
|
|
151
|
+
orderBy: { createdAt: 'desc' },
|
|
152
|
+
});
|
|
153
|
+
latestViolations = violations.map((v) => ({
|
|
154
|
+
pagePath: v.pagePath,
|
|
155
|
+
ruleName: v.ruleName,
|
|
156
|
+
ruleCategory: v.ruleCategory,
|
|
157
|
+
violationDetail: v.violationDetail,
|
|
158
|
+
createdAt: v.createdAt,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
// ─── Section 3: trend across recent N runs ─────────────────────────
|
|
162
|
+
//
|
|
163
|
+
// Find the most recent N distinct qaRunIds, then aggregate per-run.
|
|
164
|
+
// Untied rows excluded (no run → no trend point). One row per
|
|
165
|
+
// (projectId, qaRunId), oldest-first so the chart axis is monotonic.
|
|
166
|
+
const distinctRuns = await prisma.qAUniformity.groupBy({
|
|
167
|
+
by: ['qaRunId'],
|
|
168
|
+
where: {
|
|
169
|
+
projectId: filter.projectId,
|
|
170
|
+
qaRunId: { not: null },
|
|
171
|
+
},
|
|
172
|
+
_max: { createdAt: true },
|
|
173
|
+
orderBy: { _max: { createdAt: 'desc' } },
|
|
174
|
+
take: recent,
|
|
175
|
+
});
|
|
176
|
+
const runIds = distinctRuns.map((r) => r.qaRunId).filter((id) => id !== null);
|
|
177
|
+
let trend = [];
|
|
178
|
+
if (runIds.length > 0) {
|
|
179
|
+
const trendRows = await prisma.qAUniformity.groupBy({
|
|
180
|
+
by: ['qaRunId', 'status'],
|
|
181
|
+
where: {
|
|
182
|
+
projectId: filter.projectId,
|
|
183
|
+
qaRunId: { in: runIds },
|
|
184
|
+
},
|
|
185
|
+
_count: { _all: true },
|
|
186
|
+
_max: { createdAt: true },
|
|
187
|
+
});
|
|
188
|
+
const trendAcc = new Map();
|
|
189
|
+
for (const row of trendRows) {
|
|
190
|
+
if (row.qaRunId === null)
|
|
191
|
+
continue;
|
|
192
|
+
const acc = trendAcc.get(row.qaRunId) ?? {
|
|
193
|
+
passedCount: 0,
|
|
194
|
+
failedCount: 0,
|
|
195
|
+
capturedAt: new Date(0),
|
|
196
|
+
};
|
|
197
|
+
const count = row._count._all;
|
|
198
|
+
if (row.status === 'PASSED')
|
|
199
|
+
acc.passedCount += count;
|
|
200
|
+
else if (row.status === 'FAILED')
|
|
201
|
+
acc.failedCount += count;
|
|
202
|
+
const rowCapturedAt = row._max.createdAt;
|
|
203
|
+
if (rowCapturedAt !== null && rowCapturedAt > acc.capturedAt) {
|
|
204
|
+
acc.capturedAt = rowCapturedAt;
|
|
205
|
+
}
|
|
206
|
+
trendAcc.set(row.qaRunId, acc);
|
|
207
|
+
}
|
|
208
|
+
trend = Array.from(trendAcc.entries())
|
|
209
|
+
.map(([qaRunId, acc]) => {
|
|
210
|
+
const denominator = acc.passedCount + acc.failedCount;
|
|
211
|
+
return {
|
|
212
|
+
qaRunId,
|
|
213
|
+
passRate: denominator > 0 ? acc.passedCount / denominator : 0,
|
|
214
|
+
failedCount: acc.failedCount,
|
|
215
|
+
capturedAt: acc.capturedAt,
|
|
216
|
+
};
|
|
217
|
+
})
|
|
218
|
+
// Oldest first — chart x-axis renders left→right chronologically.
|
|
219
|
+
.sort((a, b) => a.capturedAt.getTime() - b.capturedAt.getTime());
|
|
220
|
+
}
|
|
221
|
+
return { rules, latestViolations, trend };
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
226
|
+
function validateProjectId(projectId) {
|
|
227
|
+
if (typeof projectId !== 'string' || projectId.length === 0) {
|
|
228
|
+
throw new QAUniformityStoreError('invalid_input', 'QAUniformityStore: projectId is required');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function validateIngestInput(input) {
|
|
232
|
+
validateProjectId(input.projectId);
|
|
233
|
+
if (input.qaRunId !== undefined &&
|
|
234
|
+
(typeof input.qaRunId !== 'string' || input.qaRunId.length === 0)) {
|
|
235
|
+
throw new QAUniformityStoreError('invalid_input', 'QAUniformityStore.ingestFindings: qaRunId must be a non-empty string when provided');
|
|
236
|
+
}
|
|
237
|
+
const findingsValue = input.findings;
|
|
238
|
+
if (!Array.isArray(findingsValue)) {
|
|
239
|
+
throw new QAUniformityStoreError('invalid_input', 'QAUniformityStore.ingestFindings: findings must be an array');
|
|
240
|
+
}
|
|
241
|
+
if (input.findings.length === 0) {
|
|
242
|
+
throw new QAUniformityStoreError('invalid_input', 'QAUniformityStore.ingestFindings: findings must not be empty');
|
|
243
|
+
}
|
|
244
|
+
input.findings.forEach((f, i) => {
|
|
245
|
+
validateFinding(f, i);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function validateFinding(f, idx) {
|
|
249
|
+
if (typeof f.pagePath !== 'string' || f.pagePath.length === 0) {
|
|
250
|
+
throw new QAUniformityStoreError('invalid_input', `QAUniformityStore.ingestFindings: findings[${String(idx)}].pagePath is required`);
|
|
251
|
+
}
|
|
252
|
+
if (typeof f.ruleName !== 'string' || f.ruleName.length === 0) {
|
|
253
|
+
throw new QAUniformityStoreError('invalid_input', `QAUniformityStore.ingestFindings: findings[${String(idx)}].ruleName is required`);
|
|
254
|
+
}
|
|
255
|
+
if (typeof f.ruleCategory !== 'string' || f.ruleCategory.length === 0) {
|
|
256
|
+
throw new QAUniformityStoreError('invalid_input', `QAUniformityStore.ingestFindings: findings[${String(idx)}].ruleCategory is required`);
|
|
257
|
+
}
|
|
258
|
+
// Cast through unknown to bypass the literal-union narrowing — this is a
|
|
259
|
+
// runtime guard against consumers that bypass TS (route layer / direct
|
|
260
|
+
// SDK callers passing untyped JSON).
|
|
261
|
+
const statusValue = f.status;
|
|
262
|
+
if (statusValue !== 'PASSED' && statusValue !== 'FAILED' && statusValue !== 'SKIPPED') {
|
|
263
|
+
throw new QAUniformityStoreError('invalid_input', `QAUniformityStore.ingestFindings: findings[${String(idx)}].status must be PASSED|FAILED|SKIPPED`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function clampRecent(recent) {
|
|
267
|
+
if (recent === undefined)
|
|
268
|
+
return DEFAULT_RECENT;
|
|
269
|
+
if (recent < 1)
|
|
270
|
+
return 1;
|
|
271
|
+
if (recent > MAX_RECENT)
|
|
272
|
+
return MAX_RECENT;
|
|
273
|
+
return recent;
|
|
274
|
+
}
|
|
275
|
+
export function mapUniformity(row) {
|
|
276
|
+
return {
|
|
277
|
+
id: row.id,
|
|
278
|
+
projectId: row.projectId,
|
|
279
|
+
qaRunId: row.qaRunId,
|
|
280
|
+
pagePath: row.pagePath,
|
|
281
|
+
ruleName: row.ruleName,
|
|
282
|
+
ruleCategory: row.ruleCategory,
|
|
283
|
+
status: row.status,
|
|
284
|
+
violationDetail: row.violationDetail,
|
|
285
|
+
createdAt: row.createdAt,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=qa-uniformity-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-uniformity-store.js","sourceRoot":"","sources":["../src/qa-uniformity-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* QAP-020 smoke — exercises the auto-fix dispatcher (DRY_RUN) end-to-end
|
|
4
|
+
* against the real local Postgres. Parity reference: Lifeline's
|
|
5
|
+
* scripts/qa-auto-fix-smoke.ts (QLL-028).
|
|
6
|
+
*
|
|
7
|
+
* Pipeline exercised:
|
|
8
|
+
* 1. Seed Lifeline-project QARun + RawSignal + QATicket (HIGH/PRODUCT_BUG)
|
|
9
|
+
* 2. Construct Prisma-backed AttemptStore + TicketStore + FixPolicy
|
|
10
|
+
* (FixPolicy uses a `defaultConfig` override so we don't need a
|
|
11
|
+
* Policy row — { enabled: true, mode: 'DRY_RUN', minSeverity: 'LOW',
|
|
12
|
+
* maxAttemptsPerDay: 5 })
|
|
13
|
+
* 3. Stub the FixAuthor adapter via the dispatcher's
|
|
14
|
+
* QA_AUTOFIX_E2E_FIXTURE_DIFF env hook (no Anthropic call). Stub the
|
|
15
|
+
* Repo adapter to throw on any method (defensive — DRY_RUN should
|
|
16
|
+
* never reach openPR/etc).
|
|
17
|
+
* 4. Call dispatcher.dispatch({ qaTicketId, projectId })
|
|
18
|
+
* 5. Assert outcome === 'dry_run_logged'
|
|
19
|
+
* 6. Assert the QAFixAttempt row has dispatchStatus === 'PRE_VERIFY_PASSED'
|
|
20
|
+
* (Derwin's terminal-for-DRY_RUN status — see dispatcher.ts line ~218),
|
|
21
|
+
* a non-empty diff, prUrl null, and attemptedAt within the last minute
|
|
22
|
+
*
|
|
23
|
+
* Run: pnpm --filter @derwinjs/db smoke:auto-fix
|
|
24
|
+
*
|
|
25
|
+
* Prereqs:
|
|
26
|
+
* - Local Postgres running on port 5433 (per Sprint 1 docker setup)
|
|
27
|
+
* - DATABASE_URL set in packages/db/.env
|
|
28
|
+
* - Migrations applied
|
|
29
|
+
* - Seed run (Lifeline project exists at slug='lifeline')
|
|
30
|
+
*
|
|
31
|
+
* Self-cleaning: deletes its own test rows in a finally block whether the
|
|
32
|
+
* script succeeds or fails. Re-runs idempotently.
|
|
33
|
+
*
|
|
34
|
+
* Prints "QAP-020 AUTO-FIX SMOKE READY ✓" on success. Exits 1 on failure.
|
|
35
|
+
*/
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=smoke-auto-fix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke-auto-fix.d.ts","sourceRoot":"","sources":["../../src/scripts/smoke-auto-fix.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* QAP-020 smoke — exercises the auto-fix dispatcher (DRY_RUN) end-to-end
|
|
4
|
+
* against the real local Postgres. Parity reference: Lifeline's
|
|
5
|
+
* scripts/qa-auto-fix-smoke.ts (QLL-028).
|
|
6
|
+
*
|
|
7
|
+
* Pipeline exercised:
|
|
8
|
+
* 1. Seed Lifeline-project QARun + RawSignal + QATicket (HIGH/PRODUCT_BUG)
|
|
9
|
+
* 2. Construct Prisma-backed AttemptStore + TicketStore + FixPolicy
|
|
10
|
+
* (FixPolicy uses a `defaultConfig` override so we don't need a
|
|
11
|
+
* Policy row — { enabled: true, mode: 'DRY_RUN', minSeverity: 'LOW',
|
|
12
|
+
* maxAttemptsPerDay: 5 })
|
|
13
|
+
* 3. Stub the FixAuthor adapter via the dispatcher's
|
|
14
|
+
* QA_AUTOFIX_E2E_FIXTURE_DIFF env hook (no Anthropic call). Stub the
|
|
15
|
+
* Repo adapter to throw on any method (defensive — DRY_RUN should
|
|
16
|
+
* never reach openPR/etc).
|
|
17
|
+
* 4. Call dispatcher.dispatch({ qaTicketId, projectId })
|
|
18
|
+
* 5. Assert outcome === 'dry_run_logged'
|
|
19
|
+
* 6. Assert the QAFixAttempt row has dispatchStatus === 'PRE_VERIFY_PASSED'
|
|
20
|
+
* (Derwin's terminal-for-DRY_RUN status — see dispatcher.ts line ~218),
|
|
21
|
+
* a non-empty diff, prUrl null, and attemptedAt within the last minute
|
|
22
|
+
*
|
|
23
|
+
* Run: pnpm --filter @derwinjs/db smoke:auto-fix
|
|
24
|
+
*
|
|
25
|
+
* Prereqs:
|
|
26
|
+
* - Local Postgres running on port 5433 (per Sprint 1 docker setup)
|
|
27
|
+
* - DATABASE_URL set in packages/db/.env
|
|
28
|
+
* - Migrations applied
|
|
29
|
+
* - Seed run (Lifeline project exists at slug='lifeline')
|
|
30
|
+
*
|
|
31
|
+
* Self-cleaning: deletes its own test rows in a finally block whether the
|
|
32
|
+
* script succeeds or fails. Re-runs idempotently.
|
|
33
|
+
*
|
|
34
|
+
* Prints "QAP-020 AUTO-FIX SMOKE READY ✓" on success. Exits 1 on failure.
|
|
35
|
+
*/
|
|
36
|
+
import { PrismaClient } from '../prisma.js';
|
|
37
|
+
import { createFixDispatcher } from '@derwinjs/core';
|
|
38
|
+
import { createPrismaQATicketStore } from '../qa-ticket-store.js';
|
|
39
|
+
import { createPrismaQAFixAttemptStore } from '../qa-fix-attempt-store.js';
|
|
40
|
+
import { createPrismaFixPolicy } from '../fix-policy.js';
|
|
41
|
+
// ─── Constants ────────────────────────────────────────────────────────────
|
|
42
|
+
const SMOKE_MARKER = '[SMOKE-QAP-020]';
|
|
43
|
+
const SMOKE_PROJECT_SLUG = 'lifeline';
|
|
44
|
+
const DASHBOARD_BASE_URL = process.env.DERWIN_DASHBOARD_URL ?? 'http://localhost:3000';
|
|
45
|
+
const FIXTURE_DIFF = `--- a/src/example/fixture.ts
|
|
46
|
+
+++ b/src/example/fixture.ts
|
|
47
|
+
@@ -1,3 +1,3 @@
|
|
48
|
+
-const broken = true;
|
|
49
|
+
+const broken = false; // QAP-020 fixture diff
|
|
50
|
+
export default broken;
|
|
51
|
+
`;
|
|
52
|
+
// Inject the fixture diff via the dispatcher's test-only env hook. Must be
|
|
53
|
+
// set BEFORE dispatcher.dispatch() runs. The dispatcher's callAuthor()
|
|
54
|
+
// honors QA_AUTOFIX_E2E_FIXTURE_DIFF when NODE_ENV !== 'production' and
|
|
55
|
+
// returns this diff instead of calling the real FixAuthor adapter — lets
|
|
56
|
+
// the smoke exercise the full pipeline without burning tokens or needing
|
|
57
|
+
// network.
|
|
58
|
+
process.env.QA_AUTOFIX_E2E_FIXTURE_DIFF = FIXTURE_DIFF;
|
|
59
|
+
// ─── Stub adapters ────────────────────────────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* QAFixAuthorAdapter stub. The env-fixture hook short-circuits the
|
|
62
|
+
* dispatcher BEFORE it calls generateDiff(), so this should never be
|
|
63
|
+
* reached. If it is, throw loudly so the smoke fails rather than silently
|
|
64
|
+
* burning a real Anthropic call.
|
|
65
|
+
*/
|
|
66
|
+
const stubFixAuthor = {
|
|
67
|
+
name: 'qap020-smoke-stub',
|
|
68
|
+
generateDiff: (_req) => {
|
|
69
|
+
throw new Error('smoke: stubFixAuthor.generateDiff was called — env fixture hook should have short-circuited');
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* QARepoAdapter stub. DRY_RUN mode should never reach the repo adapter
|
|
74
|
+
* (the dispatcher branches before openPR/mergePR). Defensive guard — if
|
|
75
|
+
* any method is called, the smoke fails rather than attempting a real
|
|
76
|
+
* GitHub operation.
|
|
77
|
+
*/
|
|
78
|
+
const stubRepo = {
|
|
79
|
+
openPR: (_opts) => {
|
|
80
|
+
throw new Error('smoke: stubRepo.openPR called — DRY_RUN should not reach repo');
|
|
81
|
+
},
|
|
82
|
+
mergePR: (_prNumber) => {
|
|
83
|
+
throw new Error('smoke: stubRepo.mergePR called — DRY_RUN should not reach repo');
|
|
84
|
+
},
|
|
85
|
+
revertPR: (_prNumber, _reason) => {
|
|
86
|
+
throw new Error('smoke: stubRepo.revertPR called — DRY_RUN should not reach repo');
|
|
87
|
+
},
|
|
88
|
+
describeRepo: () => {
|
|
89
|
+
throw new Error('smoke: stubRepo.describeRepo called — DRY_RUN should not reach repo');
|
|
90
|
+
},
|
|
91
|
+
fetchPRDiff: (_prNumber) => Promise.resolve(null),
|
|
92
|
+
};
|
|
93
|
+
// ─── Wiring ───────────────────────────────────────────────────────────────
|
|
94
|
+
const prisma = new PrismaClient();
|
|
95
|
+
const ticketStore = createPrismaQATicketStore({
|
|
96
|
+
prisma,
|
|
97
|
+
dashboardBaseUrl: DASHBOARD_BASE_URL,
|
|
98
|
+
});
|
|
99
|
+
const attemptStore = createPrismaQAFixAttemptStore({ prisma });
|
|
100
|
+
const fixPolicy = createPrismaFixPolicy({
|
|
101
|
+
prisma,
|
|
102
|
+
attemptStore,
|
|
103
|
+
// Override platform defaults so the smoke doesn't depend on a Policy row
|
|
104
|
+
// existing for the Lifeline project. Permissive for the fixture path.
|
|
105
|
+
defaultConfig: {
|
|
106
|
+
enabled: true,
|
|
107
|
+
mode: 'DRY_RUN',
|
|
108
|
+
minSeverity: 'LOW',
|
|
109
|
+
maxAttemptsPerDay: 5,
|
|
110
|
+
pathAllowlist: [],
|
|
111
|
+
pathBlocklist: [],
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
let createdRunId;
|
|
115
|
+
let createdSignalId;
|
|
116
|
+
let createdTicketId;
|
|
117
|
+
const createdAttemptIds = [];
|
|
118
|
+
async function main() {
|
|
119
|
+
// ─── Setup: find Lifeline + create QARun + RawSignal + QATicket ─────
|
|
120
|
+
const lifeline = await prisma.project.findUnique({
|
|
121
|
+
where: { slug: SMOKE_PROJECT_SLUG },
|
|
122
|
+
});
|
|
123
|
+
if (!lifeline) {
|
|
124
|
+
throw new Error(`Project '${SMOKE_PROJECT_SLUG}' not found. Run \`pnpm db:seed\` first.`);
|
|
125
|
+
}
|
|
126
|
+
console.log(`✓ Found Lifeline (id=${lifeline.id})`);
|
|
127
|
+
const run = await prisma.qARun.create({
|
|
128
|
+
data: {
|
|
129
|
+
projectId: lifeline.id,
|
|
130
|
+
triggeredBy: SMOKE_MARKER,
|
|
131
|
+
triggerType: 'OPERATOR_ON_DEMAND',
|
|
132
|
+
scope: { specs: ['smoke/auto-fix-fixture.spec.ts'], note: SMOKE_MARKER },
|
|
133
|
+
status: 'COMPLETED',
|
|
134
|
+
completedAt: new Date(),
|
|
135
|
+
signalsRaised: 1,
|
|
136
|
+
ticketsCreated: 1,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
createdRunId = run.id;
|
|
140
|
+
console.log(`✓ Created synthetic QARun (id=${run.id})`);
|
|
141
|
+
const created = await ticketStore.createQATicket({
|
|
142
|
+
projectId: lifeline.id,
|
|
143
|
+
qaRunId: run.id,
|
|
144
|
+
title: `${SMOKE_MARKER} fixture: const broken should be false`,
|
|
145
|
+
surface: 'FUNCTIONAL_CORRECTNESS',
|
|
146
|
+
classification: 'PRODUCT_BUG',
|
|
147
|
+
severity: 'HIGH',
|
|
148
|
+
riskTier: 'HIGH',
|
|
149
|
+
reproSteps: [
|
|
150
|
+
{
|
|
151
|
+
step: 1,
|
|
152
|
+
action: 'Run the fixture',
|
|
153
|
+
expected: 'broken === false',
|
|
154
|
+
actual: 'broken === true',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
suspectedRootCause: `${SMOKE_MARKER} fixture file has \`const broken = true\` — should be false`,
|
|
158
|
+
blastRadius: {
|
|
159
|
+
affectedFiles: ['src/example/fixture.ts'],
|
|
160
|
+
affectedTests: ['smoke/auto-fix-fixture.spec.ts'],
|
|
161
|
+
affectedPages: [],
|
|
162
|
+
},
|
|
163
|
+
proposedFixApproach: 'Flip `const broken = true` to `const broken = false`',
|
|
164
|
+
});
|
|
165
|
+
createdTicketId = created.ticket.id;
|
|
166
|
+
console.log(`✓ Created QATicket (id=${created.ticket.id}, severity=HIGH)`);
|
|
167
|
+
// RawSignal pointing at the ticket. Per schema this is the discovery
|
|
168
|
+
// input that triage already processed (qaTicketId set means investigated).
|
|
169
|
+
const signal = await prisma.rawSignal.create({
|
|
170
|
+
data: {
|
|
171
|
+
qaRunId: run.id,
|
|
172
|
+
qaTicketId: created.ticket.id,
|
|
173
|
+
routeName: 'qap020-smoke',
|
|
174
|
+
signalType: 'test_failure',
|
|
175
|
+
rawData: {
|
|
176
|
+
marker: SMOKE_MARKER,
|
|
177
|
+
testTitle: '[QAP-020] fixture',
|
|
178
|
+
errorMessage: 'fixture: const broken should be false',
|
|
179
|
+
},
|
|
180
|
+
rawArtifactRefs: [],
|
|
181
|
+
investigatedAt: new Date(),
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
createdSignalId = signal.id;
|
|
185
|
+
console.log(`✓ Created RawSignal (id=${signal.id})`);
|
|
186
|
+
// ─── Construct dispatcher + dispatch ────────────────────────────────
|
|
187
|
+
const dispatcher = createFixDispatcher({
|
|
188
|
+
fixAuthor: stubFixAuthor,
|
|
189
|
+
repo: stubRepo,
|
|
190
|
+
ticketStore,
|
|
191
|
+
attemptStore,
|
|
192
|
+
policy: fixPolicy,
|
|
193
|
+
});
|
|
194
|
+
console.log('✓ Dispatcher constructed; calling dispatch() (DRY_RUN, fixture diff via env hook)...');
|
|
195
|
+
const beforeDispatch = Date.now();
|
|
196
|
+
const out = await dispatcher.dispatch({
|
|
197
|
+
qaTicketId: created.ticket.id,
|
|
198
|
+
projectId: lifeline.id,
|
|
199
|
+
});
|
|
200
|
+
console.log(` outcome=${out.outcome} attemptId=${out.attemptId ?? 'null'}`);
|
|
201
|
+
if (out.outcome !== 'dry_run_logged') {
|
|
202
|
+
throw new Error(`Expected outcome 'dry_run_logged', got '${out.outcome}' (errorMessage=${out.errorMessage ?? ''})`);
|
|
203
|
+
}
|
|
204
|
+
if (!out.attemptId) {
|
|
205
|
+
throw new Error('dispatch returned dry_run_logged but attemptId is null');
|
|
206
|
+
}
|
|
207
|
+
createdAttemptIds.push(out.attemptId);
|
|
208
|
+
// ─── Verify QAFixAttempt row landed correctly ───────────────────────
|
|
209
|
+
// NOTE: Lifeline writes 'DRY_RUN_LOGGED' as its terminal status; Derwin's
|
|
210
|
+
// AttemptStatus enum doesn't have that value — the dispatcher writes
|
|
211
|
+
// 'PRE_VERIFY_PASSED' for DRY_RUN (see packages/core/src/auto-fix/
|
|
212
|
+
// dispatcher.ts line ~218). The semantic equivalent is the OUTCOME
|
|
213
|
+
// string 'dry_run_logged' (asserted above).
|
|
214
|
+
const attempt = await attemptStore.getAttempt({
|
|
215
|
+
id: out.attemptId,
|
|
216
|
+
projectId: lifeline.id,
|
|
217
|
+
});
|
|
218
|
+
if (!attempt) {
|
|
219
|
+
throw new Error(`QAFixAttempt ${out.attemptId} not found after dispatch`);
|
|
220
|
+
}
|
|
221
|
+
if (attempt.dispatchStatus !== 'PRE_VERIFY_PASSED') {
|
|
222
|
+
throw new Error(`attempt.dispatchStatus expected 'PRE_VERIFY_PASSED' (Derwin DRY_RUN terminal), got '${attempt.dispatchStatus}'`);
|
|
223
|
+
}
|
|
224
|
+
if (!attempt.diff || attempt.diff.length === 0) {
|
|
225
|
+
throw new Error('attempt.diff was empty — fixture diff did not persist');
|
|
226
|
+
}
|
|
227
|
+
if (attempt.prUrl !== null) {
|
|
228
|
+
throw new Error(`attempt.prUrl was non-null in DRY_RUN mode: ${attempt.prUrl}`);
|
|
229
|
+
}
|
|
230
|
+
const attemptedAtMs = attempt.attemptedAt.getTime();
|
|
231
|
+
if (attemptedAtMs < beforeDispatch - 1000 || attemptedAtMs > Date.now() + 1000) {
|
|
232
|
+
throw new Error(`attempt.attemptedAt (${attempt.attemptedAt.toISOString()}) not within the dispatch window`);
|
|
233
|
+
}
|
|
234
|
+
console.log(` ✓ attempt ${out.attemptId} captured ${String(attempt.diff.length)}-byte diff, status=PRE_VERIFY_PASSED, prUrl=null`);
|
|
235
|
+
console.log('\nQAP-020 AUTO-FIX SMOKE READY ✓');
|
|
236
|
+
}
|
|
237
|
+
async function cleanup() {
|
|
238
|
+
// Delete in reverse FK order: attempt → signal → ticket → run.
|
|
239
|
+
// QAFixAttempt has Cascade on qaTicketId, but be explicit so cleanup is
|
|
240
|
+
// grep-friendly and survives if the schema relaxes that cascade later.
|
|
241
|
+
if (createdAttemptIds.length > 0) {
|
|
242
|
+
await prisma.qAFixAttempt.deleteMany({
|
|
243
|
+
where: { id: { in: createdAttemptIds } },
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (createdSignalId) {
|
|
247
|
+
await prisma.rawSignal.deleteMany({ where: { id: createdSignalId } });
|
|
248
|
+
}
|
|
249
|
+
if (createdTicketId) {
|
|
250
|
+
await prisma.qATicket.deleteMany({ where: { id: createdTicketId } });
|
|
251
|
+
}
|
|
252
|
+
if (createdRunId) {
|
|
253
|
+
await prisma.qARun.delete({ where: { id: createdRunId } });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
main()
|
|
257
|
+
.catch((err) => {
|
|
258
|
+
console.error('\n[smoke-auto-fix] ✗ Smoke failed:', err);
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
})
|
|
261
|
+
.finally(async () => {
|
|
262
|
+
try {
|
|
263
|
+
await cleanup();
|
|
264
|
+
}
|
|
265
|
+
catch (cleanupErr) {
|
|
266
|
+
console.error('[smoke-auto-fix] cleanup error (manual row pruning may be needed):', cleanupErr);
|
|
267
|
+
}
|
|
268
|
+
await prisma.$disconnect();
|
|
269
|
+
});
|
|
270
|
+
//# sourceMappingURL=smoke-auto-fix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke-auto-fix.js","sourceRoot":"","sources":["../../src/scripts/smoke-auto-fix.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* QAP-014 / QAP-015 smoke — exercises the OutcomeRecorder + TrustScoreUpdater
|
|
4
|
+
* + ranker against the real local Postgres. Parity reference: Lifeline's
|
|
5
|
+
* scripts/qa-learning-backfill.ts (QLL-035).
|
|
6
|
+
*
|
|
7
|
+
* Run: pnpm --filter @derwinjs/db smoke:learning-loop
|
|
8
|
+
*
|
|
9
|
+
* Prereqs:
|
|
10
|
+
* - Local Postgres running on port 5433 (per Sprint 1 docker setup)
|
|
11
|
+
* - DATABASE_URL set in packages/db/.env
|
|
12
|
+
* - Migrations applied
|
|
13
|
+
* - Seed run (Lifeline project exists at slug='lifeline')
|
|
14
|
+
*
|
|
15
|
+
* Self-cleaning: deletes its own test rows in a finally block. Re-runs
|
|
16
|
+
* idempotently.
|
|
17
|
+
*
|
|
18
|
+
* Prints "QAP-014/015 LEARNING LOOP READY ✓" on success. Exits 1 on failure.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=smoke-learning-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke-learning-loop.d.ts","sourceRoot":"","sources":["../../src/scripts/smoke-learning-loop.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG"}
|