@derwinjs/db 0.6.0 → 0.8.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/budget-gate.d.ts +43 -0
- package/dist/budget-gate.d.ts.map +1 -0
- package/dist/budget-gate.js +110 -0
- package/dist/budget-gate.js.map +1 -0
- package/dist/classification-override-store.d.ts +24 -0
- package/dist/classification-override-store.d.ts.map +1 -0
- package/dist/classification-override-store.js +131 -0
- package/dist/classification-override-store.js.map +1 -0
- package/dist/drift-detector.d.ts +63 -0
- package/dist/drift-detector.d.ts.map +1 -0
- package/dist/drift-detector.js +0 -0
- package/dist/drift-detector.js.map +1 -0
- package/dist/freeze-window-evaluator.d.ts +62 -0
- package/dist/freeze-window-evaluator.d.ts.map +1 -0
- package/dist/freeze-window-evaluator.js +236 -0
- package/dist/freeze-window-evaluator.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/kill-switch-evaluator.d.ts +68 -0
- package/dist/kill-switch-evaluator.d.ts.map +1 -0
- package/dist/kill-switch-evaluator.js +389 -0
- package/dist/kill-switch-evaluator.js.map +1 -0
- package/dist/path-tier-resolver.d.ts +47 -0
- package/dist/path-tier-resolver.d.ts.map +1 -0
- package/dist/path-tier-resolver.js +177 -0
- package/dist/path-tier-resolver.js.map +1 -0
- package/dist/rag-retriever.d.ts.map +1 -1
- package/dist/rag-retriever.js +38 -0
- package/dist/rag-retriever.js.map +1 -1
- package/dist/regression-detector.d.ts +45 -0
- package/dist/regression-detector.d.ts.map +1 -0
- package/dist/regression-detector.js +78 -0
- package/dist/regression-detector.js.map +1 -0
- package/dist/trust-threshold-config-store.d.ts +20 -0
- package/dist/trust-threshold-config-store.d.ts.map +1 -0
- package/dist/trust-threshold-config-store.js +88 -0
- package/dist/trust-threshold-config-store.js.map +1 -0
- package/package.json +3 -3
- package/prisma/migrations/20260507120000_sprint8_policy_governance/migration.sql +58 -0
- package/prisma/migrations/20260507120100_sprint8_phase2_thresholds_and_freeze/migration.sql +33 -0
- package/prisma/migrations/20260507120200_sprint8_phase3_budget_cap/migration.sql +21 -0
- package/prisma/migrations/20260507120300_sprint8_phase4_kill_switches/migration.sql +74 -0
- package/prisma/schema.prisma +183 -11
- package/prisma-client/edge.js +96 -19
- package/prisma-client/index-browser.js +93 -16
- package/prisma-client/index.d.ts +10997 -3426
- package/prisma-client/index.js +96 -19
- package/prisma-client/package.json +1 -1
- package/prisma-client/schema.prisma +183 -11
- package/prisma-client/wasm.js +93 -16
package/dist/rag-retriever.js
CHANGED
|
@@ -122,6 +122,30 @@ export function createPrismaRAGRetriever(config) {
|
|
|
122
122
|
}
|
|
123
123
|
return { id };
|
|
124
124
|
},
|
|
125
|
+
async updateOutcomeQuality(input) {
|
|
126
|
+
validateUpdateOutcomeQualityInput(input);
|
|
127
|
+
// Pattern D — tenant isolation. The WHERE clause scopes by both
|
|
128
|
+
// projectId AND fixAttemptId so a Sprint 7 Phase 5 update cannot
|
|
129
|
+
// bleed across tenants even if the caller mis-routes a fixAttemptId.
|
|
130
|
+
// Cross-tenant or missing-row → updateMany returns count=0; surface
|
|
131
|
+
// that as `{ updated: 0 }` per the contract (no throw).
|
|
132
|
+
//
|
|
133
|
+
// We deliberately use the typed `updateMany` (not raw SQL) because:
|
|
134
|
+
// (a) the `embedding` column is Unsupported but we're not touching
|
|
135
|
+
// it — we only set `outcomeQuality` (Float, fully typed); and
|
|
136
|
+
// (b) `updateMany` returns the affected-row count, which we need to
|
|
137
|
+
// hand back to the orchestrator for the audit trail.
|
|
138
|
+
const result = await prisma.rAGCorpus.updateMany({
|
|
139
|
+
where: {
|
|
140
|
+
projectId: input.projectId,
|
|
141
|
+
fixAttemptId: input.fixAttemptId,
|
|
142
|
+
},
|
|
143
|
+
data: {
|
|
144
|
+
outcomeQuality: input.outcomeQuality,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
return { updated: result.count };
|
|
148
|
+
},
|
|
125
149
|
};
|
|
126
150
|
}
|
|
127
151
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
@@ -147,6 +171,20 @@ function validateFindSimilarInput(input) {
|
|
|
147
171
|
throw new RAGRetrieverError('invalid_embedding_length', `queryEmbedding must be ${String(EMBEDDING_DIM)}-dim, got ${actual}`);
|
|
148
172
|
}
|
|
149
173
|
}
|
|
174
|
+
function validateUpdateOutcomeQualityInput(input) {
|
|
175
|
+
if (typeof input.projectId !== 'string' || !input.projectId.trim()) {
|
|
176
|
+
throw new RAGRetrieverError('invalid_input', 'projectId is required');
|
|
177
|
+
}
|
|
178
|
+
if (typeof input.fixAttemptId !== 'string' || !input.fixAttemptId.trim()) {
|
|
179
|
+
throw new RAGRetrieverError('invalid_input', 'fixAttemptId is required');
|
|
180
|
+
}
|
|
181
|
+
if (typeof input.outcomeQuality !== 'number' ||
|
|
182
|
+
Number.isNaN(input.outcomeQuality) ||
|
|
183
|
+
input.outcomeQuality < 0 ||
|
|
184
|
+
input.outcomeQuality > 1) {
|
|
185
|
+
throw new RAGRetrieverError('invalid_input', 'outcomeQuality must be in [0, 1]');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
150
188
|
function validatePersistInput(input) {
|
|
151
189
|
if (typeof input.projectId !== 'string' || !input.projectId.trim()) {
|
|
152
190
|
throw new RAGRetrieverError('invalid_input', 'projectId is required');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rag-retriever.js","sourceRoot":"","sources":["../src/rag-retriever.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,iBAAiB,
|
|
1
|
+
{"version":3,"file":"rag-retriever.js","sourceRoot":"","sources":["../src/rag-retriever.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default RegressionDetector — QAP-Sprint7 Phase 3 / QAP-071.
|
|
3
|
+
*
|
|
4
|
+
* Stock implementation of the SDK RegressionDetector callback type
|
|
5
|
+
* (packages/sdk/src/types/regression-detector.ts). Wires `QATicketStore.listQATickets`
|
|
6
|
+
* with (classification, surface, ageHours, status) filters to find newly-opened
|
|
7
|
+
* tickets matching the original ticket's classification + surface within
|
|
8
|
+
* `withinDays` of `sinceMergedAt`. Returns true iff at least one such ticket
|
|
9
|
+
* exists (excluding the original ticket itself).
|
|
10
|
+
*
|
|
11
|
+
* Why a callback factory in @derwinjs/db rather than a stateful adapter:
|
|
12
|
+
* The RegressionDetector contract is intentionally a function type — not
|
|
13
|
+
* every consumer reads QA tickets to decide regression. A telemetry-driven
|
|
14
|
+
* Datadog/OpenTelemetry impl would query metrics instead. This file provides
|
|
15
|
+
* the *default* QATicketStore-backed callback that runPostVerify wires when
|
|
16
|
+
* the consumer opts into Derwin's standard regression-detection rule.
|
|
17
|
+
*
|
|
18
|
+
* Tenant isolation: the projectId argument from the callback flows directly
|
|
19
|
+
* into ticketStore.getQATicket and ticketStore.listQATickets, both of which
|
|
20
|
+
* are tenant-scoped per QAP-018B. No cross-project leakage is possible at
|
|
21
|
+
* this layer.
|
|
22
|
+
*
|
|
23
|
+
* Failure semantics: the callback never throws. Defense-in-depth — if the
|
|
24
|
+
* original ticket cannot be resolved (missing or cross-tenant), return false
|
|
25
|
+
* (no original means no regression context). This mirrors the orchestrator's
|
|
26
|
+
* "regression detection MUST NOT abort the post-verify path" rule. The
|
|
27
|
+
* upstream caller (runPostVerify) additionally wraps the call in try/catch
|
|
28
|
+
* for the same defensive posture.
|
|
29
|
+
*/
|
|
30
|
+
import type { QATicketStore, RegressionDetector } from '@derwinjs/sdk';
|
|
31
|
+
export interface PrismaRegressionDetectorConfig {
|
|
32
|
+
/**
|
|
33
|
+
* QATicketStore instance — the same instance the rest of the platform uses,
|
|
34
|
+
* so the lookups respect the same tenant scope and DB connection pool.
|
|
35
|
+
*/
|
|
36
|
+
ticketStore: QATicketStore;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a RegressionDetector backed by QATicketStore.listQATickets.
|
|
40
|
+
*
|
|
41
|
+
* The returned callback is invoked by runPostVerify when post-verify reports
|
|
42
|
+
* failure signals after a merge — see packages/core/src/orchestrator/run-post-verify.ts.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createPrismaRegressionDetector(config: PrismaRegressionDetectorConfig): RegressionDetector;
|
|
45
|
+
//# sourceMappingURL=regression-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regression-detector.d.ts","sourceRoot":"","sources":["../src/regression-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAIvE,MAAM,WAAW,8BAA8B;IAC7C;;;OAGG;IACH,WAAW,EAAE,aAAa,CAAC;CAC5B;AAQD;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,8BAA8B,GACrC,kBAAkB,CAyCpB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default RegressionDetector — QAP-Sprint7 Phase 3 / QAP-071.
|
|
3
|
+
*
|
|
4
|
+
* Stock implementation of the SDK RegressionDetector callback type
|
|
5
|
+
* (packages/sdk/src/types/regression-detector.ts). Wires `QATicketStore.listQATickets`
|
|
6
|
+
* with (classification, surface, ageHours, status) filters to find newly-opened
|
|
7
|
+
* tickets matching the original ticket's classification + surface within
|
|
8
|
+
* `withinDays` of `sinceMergedAt`. Returns true iff at least one such ticket
|
|
9
|
+
* exists (excluding the original ticket itself).
|
|
10
|
+
*
|
|
11
|
+
* Why a callback factory in @derwinjs/db rather than a stateful adapter:
|
|
12
|
+
* The RegressionDetector contract is intentionally a function type — not
|
|
13
|
+
* every consumer reads QA tickets to decide regression. A telemetry-driven
|
|
14
|
+
* Datadog/OpenTelemetry impl would query metrics instead. This file provides
|
|
15
|
+
* the *default* QATicketStore-backed callback that runPostVerify wires when
|
|
16
|
+
* the consumer opts into Derwin's standard regression-detection rule.
|
|
17
|
+
*
|
|
18
|
+
* Tenant isolation: the projectId argument from the callback flows directly
|
|
19
|
+
* into ticketStore.getQATicket and ticketStore.listQATickets, both of which
|
|
20
|
+
* are tenant-scoped per QAP-018B. No cross-project leakage is possible at
|
|
21
|
+
* this layer.
|
|
22
|
+
*
|
|
23
|
+
* Failure semantics: the callback never throws. Defense-in-depth — if the
|
|
24
|
+
* original ticket cannot be resolved (missing or cross-tenant), return false
|
|
25
|
+
* (no original means no regression context). This mirrors the orchestrator's
|
|
26
|
+
* "regression detection MUST NOT abort the post-verify path" rule. The
|
|
27
|
+
* upstream caller (runPostVerify) additionally wraps the call in try/catch
|
|
28
|
+
* for the same defensive posture.
|
|
29
|
+
*/
|
|
30
|
+
// ─── Constants ────────────────────────────────────────────────────────────
|
|
31
|
+
const HOURS_PER_DAY = 24;
|
|
32
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
33
|
+
/**
|
|
34
|
+
* Build a RegressionDetector backed by QATicketStore.listQATickets.
|
|
35
|
+
*
|
|
36
|
+
* The returned callback is invoked by runPostVerify when post-verify reports
|
|
37
|
+
* failure signals after a merge — see packages/core/src/orchestrator/run-post-verify.ts.
|
|
38
|
+
*/
|
|
39
|
+
export function createPrismaRegressionDetector(config) {
|
|
40
|
+
const { ticketStore } = config;
|
|
41
|
+
return async ({ projectId, qaTicketId, sinceMergedAt, withinDays }) => {
|
|
42
|
+
// (1) Fetch the original ticket. Both not-found and cross-tenant
|
|
43
|
+
// collisions return null per the QATicketStore contract — either way,
|
|
44
|
+
// returning false here is the correct defensive answer (no original →
|
|
45
|
+
// no regression context to evaluate).
|
|
46
|
+
const original = await ticketStore.getQATicket({ id: qaTicketId, projectId });
|
|
47
|
+
if (original === null) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// (2) List candidate regression tickets via the store's existing filter
|
|
51
|
+
// shape. We narrow on (classification, surface) to scope to the same
|
|
52
|
+
// failure family, on ageHoursMax = withinDays * 24 to bound the search
|
|
53
|
+
// window, and on status: 'OPEN' so already-resolved historical tickets
|
|
54
|
+
// don't generate false positives. ageHoursMin: 0 is the floor.
|
|
55
|
+
const candidates = await ticketStore.listQATickets({
|
|
56
|
+
projectId,
|
|
57
|
+
classification: original.classification,
|
|
58
|
+
surface: original.surface,
|
|
59
|
+
ageHoursMin: 0,
|
|
60
|
+
ageHoursMax: withinDays * HOURS_PER_DAY,
|
|
61
|
+
status: 'OPEN',
|
|
62
|
+
});
|
|
63
|
+
// (3) Paranoia filter — listQATickets uses age (relative to "now"), not
|
|
64
|
+
// an absolute timestamp, and the original's age might place IT inside
|
|
65
|
+
// the window too. We exclude (a) tickets created at or before the merge
|
|
66
|
+
// moment and (b) the original ticket itself. The combination means:
|
|
67
|
+
// only tickets opened strictly AFTER the merge that are NOT the original
|
|
68
|
+
// count as regressions.
|
|
69
|
+
const mergedAtMs = sinceMergedAt.getTime();
|
|
70
|
+
const matches = candidates.tickets.filter((t) => {
|
|
71
|
+
if (t.id === qaTicketId)
|
|
72
|
+
return false;
|
|
73
|
+
return t.createdAt.getTime() > mergedAtMs;
|
|
74
|
+
});
|
|
75
|
+
return matches.length > 0;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=regression-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regression-detector.js","sourceRoot":"","sources":["../src/regression-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAcH,6EAA6E;AAE7E,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,4EAA4E;AAE5E;;;;;GAKG;AACH,MAAM,UAAU,8BAA8B,CAC5C,MAAsC;IAEtC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE/B,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,EAAE,EAAE;QACpE,iEAAiE;QACjE,sEAAsE;QACtE,sEAAsE;QACtE,sCAAsC;QACtC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9E,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,wEAAwE;QACxE,qEAAqE;QACrE,uEAAuE;QACvE,uEAAuE;QACvE,+DAA+D;QAC/D,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC;YACjD,SAAS;YACT,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,WAAW,EAAE,CAAC;YACd,WAAW,EAAE,UAAU,GAAG,aAAa;YACvC,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QAEH,wEAAwE;QACxE,sEAAsE;QACtE,wEAAwE;QACxE,oEAAoE;QACpE,yEAAyE;QACzE,wBAAwB;QACxB,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAC9C,IAAI,CAAC,CAAC,EAAE,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC;YACtC,OAAO,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaTrustThresholdConfigStore — Prisma-backed implementation of
|
|
3
|
+
* the SDK TrustThresholdConfigStore contract introduced by QAP-082.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 8 Phase 2 (Policy Governance). The config is stored as a JSON
|
|
6
|
+
* column on Project.trustThresholds. Null in storage means "use defaults".
|
|
7
|
+
*
|
|
8
|
+
* Tenant isolation: every method scopes by projectId. Pattern D applies —
|
|
9
|
+
* unknown / wrong-project lookups return DEFAULT_TRUST_THRESHOLDS rather
|
|
10
|
+
* than throwing or leaking existence (defense-in-depth against tenant
|
|
11
|
+
* enumeration).
|
|
12
|
+
*/
|
|
13
|
+
import type { PrismaClient } from './prisma.js';
|
|
14
|
+
import { type TrustThresholdConfigStore } from '@derwinjs/sdk';
|
|
15
|
+
export interface PrismaTrustThresholdConfigStoreConfig {
|
|
16
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
17
|
+
prisma: PrismaClient;
|
|
18
|
+
}
|
|
19
|
+
export declare function createPrismaTrustThresholdConfigStore(config: PrismaTrustThresholdConfigStoreConfig): TrustThresholdConfigStore;
|
|
20
|
+
//# sourceMappingURL=trust-threshold-config-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust-threshold-config-store.d.ts","sourceRoot":"","sources":["../src/trust-threshold-config-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAIL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,qCAAqC;IACpD,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AA8DD,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,qCAAqC,GAC5C,yBAAyB,CA4B3B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaTrustThresholdConfigStore — Prisma-backed implementation of
|
|
3
|
+
* the SDK TrustThresholdConfigStore contract introduced by QAP-082.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 8 Phase 2 (Policy Governance). The config is stored as a JSON
|
|
6
|
+
* column on Project.trustThresholds. Null in storage means "use defaults".
|
|
7
|
+
*
|
|
8
|
+
* Tenant isolation: every method scopes by projectId. Pattern D applies —
|
|
9
|
+
* unknown / wrong-project lookups return DEFAULT_TRUST_THRESHOLDS rather
|
|
10
|
+
* than throwing or leaking existence (defense-in-depth against tenant
|
|
11
|
+
* enumeration).
|
|
12
|
+
*/
|
|
13
|
+
import { DEFAULT_TRUST_THRESHOLDS, TrustThresholdConfigError, } from '@derwinjs/sdk';
|
|
14
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Each threshold must be a finite integer in [0, 100]. We accept floats
|
|
17
|
+
* because the JSON column can store them, but the dispatcher's comparison
|
|
18
|
+
* is integer-percent semantically; reject NaN / Infinity / out-of-range.
|
|
19
|
+
*/
|
|
20
|
+
function validateConfig(config) {
|
|
21
|
+
for (const [key, value] of [
|
|
22
|
+
['low', config.low],
|
|
23
|
+
['medium', config.medium],
|
|
24
|
+
['high', config.high],
|
|
25
|
+
]) {
|
|
26
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
27
|
+
throw new TrustThresholdConfigError('invalid_input', `TrustThresholdConfig: ${key} must be a finite number (got ${String(value)})`);
|
|
28
|
+
}
|
|
29
|
+
if (value < 0 || value > 100) {
|
|
30
|
+
throw new TrustThresholdConfigError('invalid_input', `TrustThresholdConfig: ${key} must be in [0, 100] (got ${String(value)})`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Best-effort coercion of an unknown JSON value into a TrustThresholdConfig.
|
|
36
|
+
* Returns null if the shape is wrong — caller falls back to defaults. We
|
|
37
|
+
* intentionally do not throw here: defense-in-depth so a corrupted row
|
|
38
|
+
* doesn't cascade into a runtime error.
|
|
39
|
+
*/
|
|
40
|
+
function tryParseConfig(value) {
|
|
41
|
+
if (value !== null &&
|
|
42
|
+
typeof value === 'object' &&
|
|
43
|
+
!Array.isArray(value) &&
|
|
44
|
+
'low' in value &&
|
|
45
|
+
'medium' in value &&
|
|
46
|
+
'high' in value) {
|
|
47
|
+
const v = value;
|
|
48
|
+
if (typeof v.low === 'number' &&
|
|
49
|
+
typeof v.medium === 'number' &&
|
|
50
|
+
typeof v.high === 'number' &&
|
|
51
|
+
Number.isFinite(v.low) &&
|
|
52
|
+
Number.isFinite(v.medium) &&
|
|
53
|
+
Number.isFinite(v.high)) {
|
|
54
|
+
return { low: v.low, medium: v.medium, high: v.high };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
60
|
+
export function createPrismaTrustThresholdConfigStore(config) {
|
|
61
|
+
const { prisma } = config;
|
|
62
|
+
return {
|
|
63
|
+
async getConfig(input) {
|
|
64
|
+
const project = await prisma.project.findUnique({
|
|
65
|
+
where: { id: input.projectId },
|
|
66
|
+
select: { trustThresholds: true },
|
|
67
|
+
});
|
|
68
|
+
if (project === null)
|
|
69
|
+
return { ...DEFAULT_TRUST_THRESHOLDS };
|
|
70
|
+
const parsed = tryParseConfig(project.trustThresholds);
|
|
71
|
+
return parsed ?? { ...DEFAULT_TRUST_THRESHOLDS };
|
|
72
|
+
},
|
|
73
|
+
async setConfig(input) {
|
|
74
|
+
validateConfig(input.config);
|
|
75
|
+
await prisma.project.update({
|
|
76
|
+
where: { id: input.projectId },
|
|
77
|
+
data: {
|
|
78
|
+
trustThresholds: {
|
|
79
|
+
low: input.config.low,
|
|
80
|
+
medium: input.config.medium,
|
|
81
|
+
high: input.config.high,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=trust-threshold-config-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust-threshold-config-store.js","sourceRoot":"","sources":["../src/trust-threshold-config-store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@derwinjs/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Prisma schema + migrations for Derwin's own Postgres. 14 models, project-namespaced. Per ADR-0005. Ships its own generated Prisma client (multi-platform binaries) for cross-consumer compatibility.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@prisma/client": "^5.22.0",
|
|
36
|
-
"@derwinjs/core": "0.
|
|
37
|
-
"@derwinjs/sdk": "0.
|
|
36
|
+
"@derwinjs/core": "0.8.0",
|
|
37
|
+
"@derwinjs/sdk": "0.8.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@vitest/coverage-v8": "^2.1.9",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
-- Sprint 8 (QAP-080..081) — Policy governance: path-tier rules + classification override table.
|
|
2
|
+
--
|
|
3
|
+
-- Promotes Policy.pathRules / Policy.classificationOverrides JSON columns to
|
|
4
|
+
-- first-class tables so the policy-editor UI (Sprint 9 / QAP-088) can render,
|
|
5
|
+
-- reorder, and audit them as discrete rows. The legacy JSON fields on Policy
|
|
6
|
+
-- stay for back-compat with QAP-013's createPrismaFixPolicy until Sprint 9
|
|
7
|
+
-- migrates the read path.
|
|
8
|
+
--
|
|
9
|
+
-- Idempotent (IF NOT EXISTS) — safe to re-run on environments where the
|
|
10
|
+
-- enum or tables already exist.
|
|
11
|
+
|
|
12
|
+
-- 1. Project.defaultTier — fallback tier when no PathTierRule matches.
|
|
13
|
+
ALTER TABLE "derwin"."projects"
|
|
14
|
+
ADD COLUMN IF NOT EXISTS "defaultTier" "derwin"."RiskTier" NOT NULL DEFAULT 'LOW';
|
|
15
|
+
|
|
16
|
+
-- 2. OverrideMode enum.
|
|
17
|
+
DO $$ BEGIN
|
|
18
|
+
CREATE TYPE "derwin"."OverrideMode" AS ENUM ('BLOCK_AUTO_FIX', 'FORCE_ESCALATION', 'DOWNGRADE_TIER');
|
|
19
|
+
EXCEPTION
|
|
20
|
+
WHEN duplicate_object THEN NULL;
|
|
21
|
+
END $$;
|
|
22
|
+
|
|
23
|
+
-- 3. PathTierRule.
|
|
24
|
+
CREATE TABLE IF NOT EXISTS "derwin"."path_tier_rules" (
|
|
25
|
+
"id" TEXT PRIMARY KEY,
|
|
26
|
+
"projectId" TEXT NOT NULL,
|
|
27
|
+
"pattern" TEXT NOT NULL,
|
|
28
|
+
"tier" "derwin"."RiskTier" NOT NULL,
|
|
29
|
+
"priority" INTEGER NOT NULL,
|
|
30
|
+
"reason" TEXT,
|
|
31
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
32
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
33
|
+
CONSTRAINT "path_tier_rules_projectId_fkey"
|
|
34
|
+
FOREIGN KEY ("projectId") REFERENCES "derwin"."projects"("id")
|
|
35
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE INDEX IF NOT EXISTS "path_tier_rules_projectId_priority_idx"
|
|
39
|
+
ON "derwin"."path_tier_rules" ("projectId", "priority");
|
|
40
|
+
|
|
41
|
+
-- 4. ClassificationOverride.
|
|
42
|
+
CREATE TABLE IF NOT EXISTS "derwin"."classification_overrides" (
|
|
43
|
+
"id" TEXT PRIMARY KEY,
|
|
44
|
+
"projectId" TEXT NOT NULL,
|
|
45
|
+
"classification" TEXT NOT NULL,
|
|
46
|
+
"surface" TEXT,
|
|
47
|
+
"overrideMode" "derwin"."OverrideMode" NOT NULL,
|
|
48
|
+
"downgradeTier" "derwin"."RiskTier",
|
|
49
|
+
"reason" TEXT NOT NULL,
|
|
50
|
+
"createdBy" TEXT NOT NULL,
|
|
51
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
52
|
+
CONSTRAINT "classification_overrides_projectId_fkey"
|
|
53
|
+
FOREIGN KEY ("projectId") REFERENCES "derwin"."projects"("id")
|
|
54
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
CREATE INDEX IF NOT EXISTS "classification_overrides_projectId_classification_surface_idx"
|
|
58
|
+
ON "derwin"."classification_overrides" ("projectId", "classification", "surface");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- Sprint 8 Phase 2 (QAP-082..083) — Trust threshold config + freeze windows.
|
|
2
|
+
--
|
|
3
|
+
-- Phase 1 (20260507120000_sprint8_policy_governance) added PathTierRule,
|
|
4
|
+
-- ClassificationOverride, and Project.defaultTier. Phase 2 layers on:
|
|
5
|
+
-- 1. Project.trustThresholds JSONB — per-tier minimum trustPercent for
|
|
6
|
+
-- auto-fix dispatch. Null → use SDK defaults ({low:60, medium:80, high:95}).
|
|
7
|
+
-- 2. freeze_windows table — operator-defined recurring dispatch freeze
|
|
8
|
+
-- windows. Cron-driven, 5-field, no seconds.
|
|
9
|
+
--
|
|
10
|
+
-- Idempotent (IF NOT EXISTS). Safe to re-run on environments where Phase 1
|
|
11
|
+
-- already landed but Phase 2 didn't.
|
|
12
|
+
|
|
13
|
+
-- 1. Project.trustThresholds — JSONB, nullable.
|
|
14
|
+
ALTER TABLE "derwin"."projects"
|
|
15
|
+
ADD COLUMN IF NOT EXISTS "trustThresholds" JSONB;
|
|
16
|
+
|
|
17
|
+
-- 2. FreezeWindow.
|
|
18
|
+
CREATE TABLE IF NOT EXISTS "derwin"."freeze_windows" (
|
|
19
|
+
"id" TEXT PRIMARY KEY,
|
|
20
|
+
"projectId" TEXT NOT NULL,
|
|
21
|
+
"cronExpression" TEXT NOT NULL,
|
|
22
|
+
"durationMinutes" INTEGER NOT NULL,
|
|
23
|
+
"label" TEXT NOT NULL,
|
|
24
|
+
"blocksDispatch" BOOLEAN NOT NULL DEFAULT TRUE,
|
|
25
|
+
"enabled" BOOLEAN NOT NULL DEFAULT TRUE,
|
|
26
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
27
|
+
CONSTRAINT "freeze_windows_projectId_fkey"
|
|
28
|
+
FOREIGN KEY ("projectId") REFERENCES "derwin"."projects"("id")
|
|
29
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX IF NOT EXISTS "freeze_windows_projectId_enabled_idx"
|
|
33
|
+
ON "derwin"."freeze_windows" ("projectId", "enabled");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Sprint 8 Phase 3 (QAP-084) — Per-project budget cap + soft-warn pct.
|
|
2
|
+
--
|
|
3
|
+
-- Adds the budget-gate columns the BudgetGate contract reads:
|
|
4
|
+
-- 1. Project.monthlyBudgetUsd — DOUBLE PRECISION, nullable. Null = NO_CAP.
|
|
5
|
+
-- 2. Project.budgetSoftWarnPct — DOUBLE PRECISION, default 0.8 (80%).
|
|
6
|
+
--
|
|
7
|
+
-- The dispatcher consults a BudgetGate before runFixCycle and refuses new
|
|
8
|
+
-- auto-fix dispatches when month-to-date spend / monthlyBudgetUsd >= 1.0.
|
|
9
|
+
-- Soft-warn (>= budgetSoftWarnPct, < 1.0) is observable but non-blocking;
|
|
10
|
+
-- hard-stop (>= 1.0) blocks. NO_CAP (monthlyBudgetUsd IS NULL) is the
|
|
11
|
+
-- pre-Phase-3 default and preserves legacy behavior for projects that have
|
|
12
|
+
-- not opted into explicit USD caps.
|
|
13
|
+
--
|
|
14
|
+
-- Idempotent (IF NOT EXISTS). Safe to re-run on environments where Phase 1
|
|
15
|
+
-- and Phase 2 have already landed but Phase 3 has not.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE "derwin"."projects"
|
|
18
|
+
ADD COLUMN IF NOT EXISTS "monthlyBudgetUsd" DOUBLE PRECISION;
|
|
19
|
+
|
|
20
|
+
ALTER TABLE "derwin"."projects"
|
|
21
|
+
ADD COLUMN IF NOT EXISTS "budgetSoftWarnPct" DOUBLE PRECISION NOT NULL DEFAULT 0.8;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
-- Sprint 8 Phase 4 (QAP-085 / QAP-086 / QAP-087) — Three kill switches.
|
|
2
|
+
--
|
|
3
|
+
-- Adds the schema the KillSwitchEvaluator contract reads/writes:
|
|
4
|
+
-- 1. Project.killSwitchConfig — JSONB, nullable. Per-project threshold
|
|
5
|
+
-- overrides; null → use SDK defaults.
|
|
6
|
+
-- 2. KillSwitchType enum — three trigger discriminators.
|
|
7
|
+
-- 3. KillSwitchScope enum — three granularity discriminators.
|
|
8
|
+
-- 4. kill_switch_states table — history-preserving log of every trip.
|
|
9
|
+
--
|
|
10
|
+
-- The dispatcher consults a KillSwitchEvaluator before runFixCycle and
|
|
11
|
+
-- refuses to dispatch when any matching engaged switch is present:
|
|
12
|
+
-- - PROJECT or PLATFORM-scope switches block before ticket fetch.
|
|
13
|
+
-- - CLASSIFICATION-scope switches block after ticket fetch when the
|
|
14
|
+
-- ticket's classification + surface match the engaged switch's tuple.
|
|
15
|
+
--
|
|
16
|
+
-- History semantics: a new trip inserts a new row; an unblock flips
|
|
17
|
+
-- engaged → false on the existing row; subsequent re-trips on the same
|
|
18
|
+
-- tuple insert another row. This preserves the audit trail.
|
|
19
|
+
--
|
|
20
|
+
-- Idempotent (IF NOT EXISTS / DO ... EXCEPTION). Safe to re-run on
|
|
21
|
+
-- environments where Phase 1 / Phase 2 / Phase 3 already landed but
|
|
22
|
+
-- Phase 4 has not.
|
|
23
|
+
|
|
24
|
+
-- 1. Project.killSwitchConfig — JSONB, nullable.
|
|
25
|
+
ALTER TABLE "derwin"."projects"
|
|
26
|
+
ADD COLUMN IF NOT EXISTS "killSwitchConfig" JSONB;
|
|
27
|
+
|
|
28
|
+
-- 2. KillSwitchType enum.
|
|
29
|
+
DO $$ BEGIN
|
|
30
|
+
CREATE TYPE "derwin"."KillSwitchType" AS ENUM (
|
|
31
|
+
'SUCCESS_RATE_DROP',
|
|
32
|
+
'CONSECUTIVE_FAILURE',
|
|
33
|
+
'REGRESSION_CASCADE'
|
|
34
|
+
);
|
|
35
|
+
EXCEPTION
|
|
36
|
+
WHEN duplicate_object THEN NULL;
|
|
37
|
+
END $$;
|
|
38
|
+
|
|
39
|
+
-- 3. KillSwitchScope enum.
|
|
40
|
+
DO $$ BEGIN
|
|
41
|
+
CREATE TYPE "derwin"."KillSwitchScope" AS ENUM (
|
|
42
|
+
'CLASSIFICATION',
|
|
43
|
+
'PROJECT',
|
|
44
|
+
'PLATFORM'
|
|
45
|
+
);
|
|
46
|
+
EXCEPTION
|
|
47
|
+
WHEN duplicate_object THEN NULL;
|
|
48
|
+
END $$;
|
|
49
|
+
|
|
50
|
+
-- 4. KillSwitchState table.
|
|
51
|
+
CREATE TABLE IF NOT EXISTS "derwin"."kill_switch_states" (
|
|
52
|
+
"id" TEXT PRIMARY KEY,
|
|
53
|
+
"projectId" TEXT,
|
|
54
|
+
"classification" TEXT,
|
|
55
|
+
"surface" TEXT,
|
|
56
|
+
"type" "derwin"."KillSwitchType" NOT NULL,
|
|
57
|
+
"scope" "derwin"."KillSwitchScope" NOT NULL,
|
|
58
|
+
"engaged" BOOLEAN NOT NULL DEFAULT TRUE,
|
|
59
|
+
"engagedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
60
|
+
"unblockedAt" TIMESTAMP(3),
|
|
61
|
+
"unblockedBy" TEXT,
|
|
62
|
+
"reason" TEXT NOT NULL,
|
|
63
|
+
"triggerMetrics" JSONB NOT NULL,
|
|
64
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
65
|
+
CONSTRAINT "kill_switch_states_projectId_fkey"
|
|
66
|
+
FOREIGN KEY ("projectId") REFERENCES "derwin"."projects"("id")
|
|
67
|
+
ON DELETE CASCADE ON UPDATE CASCADE
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
CREATE INDEX IF NOT EXISTS "kill_switch_states_projectId_engaged_idx"
|
|
71
|
+
ON "derwin"."kill_switch_states" ("projectId", "engaged");
|
|
72
|
+
|
|
73
|
+
CREATE INDEX IF NOT EXISTS "kill_switch_states_type_scope_engaged_idx"
|
|
74
|
+
ON "derwin"."kill_switch_states" ("type", "scope", "engaged");
|