@derwinjs/db 0.12.0 → 0.13.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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/learning-health-reporter.d.ts.map +1 -1
- package/dist/learning-health-reporter.js +14 -3
- package/dist/learning-health-reporter.js.map +1 -1
- package/dist/qa-audit-artifact-store.d.ts +25 -0
- package/dist/qa-audit-artifact-store.d.ts.map +1 -0
- package/dist/qa-audit-artifact-store.js +60 -0
- package/dist/qa-audit-artifact-store.js.map +1 -0
- package/dist/self-review-verdict-store.d.ts +29 -0
- package/dist/self-review-verdict-store.d.ts.map +1 -0
- package/dist/self-review-verdict-store.js +167 -0
- package/dist/self-review-verdict-store.js.map +1 -0
- package/package.json +3 -3
- package/prisma/migrations/20260510000000_sprint13_phase9_self_review_verdict/migration.sql +73 -0
- package/prisma/schema.prisma +54 -3
- package/prisma-client/edge.js +21 -5
- package/prisma-client/index-browser.js +18 -2
- package/prisma-client/index.d.ts +2423 -177
- package/prisma-client/index.js +21 -5
- package/prisma-client/package.json +1 -1
- package/prisma-client/schema.prisma +54 -3
- package/prisma-client/wasm.js +18 -2
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
export declare const PACKAGE_NAME: "@derwinjs/db";
|
|
17
17
|
export { createPrismaQATicketStore, type PrismaQATicketStoreConfig } from './qa-ticket-store.js';
|
|
18
18
|
export { createPrismaQAFixAttemptStore, type PrismaQAFixAttemptStoreConfig, } from './qa-fix-attempt-store.js';
|
|
19
|
+
export { createPrismaQAAuditArtifactStore, type PrismaQAAuditArtifactStoreConfig, } from './qa-audit-artifact-store.js';
|
|
19
20
|
export { createPrismaQARunStore, type PrismaQARunStoreConfig } from './qa-run-store.js';
|
|
20
21
|
export { createPrismaQAPatternStore, type PrismaQAPatternStoreConfig } from './qa-pattern-store.js';
|
|
21
22
|
export { createPrismaQARevertStore, type PrismaQARevertStoreConfig } from './qa-revert-store.js';
|
|
@@ -39,5 +40,6 @@ export { createPrismaTenantFuzzConfigStore, type PrismaTenantFuzzConfigStoreConf
|
|
|
39
40
|
export { createPrismaRumSampleStore, RumSampleStoreError, type PrismaRumSampleStoreConfig, } from './rum-sample-store.js';
|
|
40
41
|
export { createPrismaMilestoneEventStore, type PrismaMilestoneEventStoreConfig, } from './milestone-event-store.js';
|
|
41
42
|
export { createPrismaCostRegressionEvaluator, type PrismaCostRegressionEvaluatorConfig, } from './cost-regression-evaluator.js';
|
|
43
|
+
export { createPrismaSelfReviewVerdictStore, type PrismaSelfReviewVerdictStoreConfig, } from './self-review-verdict-store.js';
|
|
42
44
|
export * from './prisma.js';
|
|
43
45
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,YAAY,EAAG,cAAuB,CAAC;AAIpD,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,KAAK,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kCAAkC,EAClC,KAAK,kCAAkC,GACxC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,KAAK,8BAA8B,GACpC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,SAAS,EACT,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uCAAuC,EACvC,KAAK,uCAAuC,GAC7C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qCAAqC,EACrC,KAAK,qCAAqC,GAC3C,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,SAAS,EACT,cAAc,EACd,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,EAC9B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,4BAA4B,EAC5B,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,kCAAkC,EAClC,4BAA4B,EAC5B,KAAK,kCAAkC,GACxC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,KAAK,0BAA0B,GAChC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,mCAAmC,EACnC,KAAK,mCAAmC,GACzC,MAAM,gCAAgC,CAAC;AA4BxC,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,YAAY,EAAG,cAAuB,CAAC;AAIpD,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,gCAAgC,EAChC,KAAK,gCAAgC,GACtC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,KAAK,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kCAAkC,EAClC,KAAK,kCAAkC,GACxC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,KAAK,8BAA8B,GACpC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,SAAS,EACT,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uCAAuC,EACvC,KAAK,uCAAuC,GAC7C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qCAAqC,EACrC,KAAK,qCAAqC,GAC3C,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,SAAS,EACT,cAAc,EACd,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,EAC9B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,4BAA4B,EAC5B,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,kCAAkC,EAClC,4BAA4B,EAC5B,KAAK,kCAAkC,GACxC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,KAAK,0BAA0B,GAChC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,mCAAmC,EACnC,KAAK,mCAAmC,GACzC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,kCAAkC,EAClC,KAAK,kCAAkC,GACxC,MAAM,gCAAgC,CAAC;AA4BxC,cAAc,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export const PACKAGE_NAME = '@derwinjs/db';
|
|
|
17
17
|
// ─── Storage implementations ─────────────────────────────────────────────
|
|
18
18
|
export { createPrismaQATicketStore } from './qa-ticket-store.js';
|
|
19
19
|
export { createPrismaQAFixAttemptStore, } from './qa-fix-attempt-store.js';
|
|
20
|
+
export { createPrismaQAAuditArtifactStore, } from './qa-audit-artifact-store.js';
|
|
20
21
|
export { createPrismaQARunStore } from './qa-run-store.js';
|
|
21
22
|
export { createPrismaQAPatternStore } from './qa-pattern-store.js';
|
|
22
23
|
export { createPrismaQARevertStore } from './qa-revert-store.js';
|
|
@@ -40,6 +41,7 @@ export { createPrismaTenantFuzzConfigStore, } from './tenant-fuzz-config-store.j
|
|
|
40
41
|
export { createPrismaRumSampleStore, RumSampleStoreError, } from './rum-sample-store.js';
|
|
41
42
|
export { createPrismaMilestoneEventStore, } from './milestone-event-store.js';
|
|
42
43
|
export { createPrismaCostRegressionEvaluator, } from './cost-regression-evaluator.js';
|
|
44
|
+
export { createPrismaSelfReviewVerdictStore, } from './self-review-verdict-store.js';
|
|
43
45
|
// ─── Prisma client re-export ─────────────────────────────────────────────
|
|
44
46
|
//
|
|
45
47
|
// @derwinjs/db ships its OWN generated Prisma client (output: prisma-client/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAuB,CAAC;AAEpD,4EAA4E;AAE5E,OAAO,EAAE,yBAAyB,EAAkC,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAA+B,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAmC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAkC,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iCAAiC,GAElC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kCAAkC,GAEnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,GAE/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAkC,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,SAAS,GAEV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uCAAuC,GAExC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qCAAqC,GAEtC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,SAAS,EACT,cAAc,GAEf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAA+B,MAAM,kBAAkB,CAAC;AACvF,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAE/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,4BAA4B,GAE7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,kCAAkC,EAClC,4BAA4B,GAE7B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,iCAAiC,GAElC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,iCAAiC,GAElC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,GAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,mCAAmC,GAEpC,MAAM,gCAAgC,CAAC;AAExC,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,gDAAgD;AAChD,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,yCAAyC;AACzC,+EAA+E;AAC/E,yCAAyC;AACzC,uEAAuE;AACvE,sEAAsE;AACtE,kEAAkE;AAClE,+BAA+B;AAC/B,EAAE;AACF,oBAAoB;AACpB,8DAA8D;AAC9D,4CAA4C;AAC5C,qEAAqE;AACrE,QAAQ;AACR,gDAAgD;AAEhD,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAuB,CAAC;AAEpD,4EAA4E;AAE5E,OAAO,EAAE,yBAAyB,EAAkC,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,gCAAgC,GAEjC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAA+B,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAmC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAkC,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iCAAiC,GAElC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kCAAkC,GAEnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,GAE/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAkC,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,SAAS,GAEV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uCAAuC,GAExC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qCAAqC,GAEtC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,SAAS,EACT,cAAc,GAEf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAA+B,MAAM,kBAAkB,CAAC;AACvF,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAE/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,4BAA4B,GAE7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,kCAAkC,EAClC,4BAA4B,GAE7B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,iCAAiC,GAElC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,iCAAiC,GAElC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,GAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,mCAAmC,GAEpC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,kCAAkC,GAEnC,MAAM,gCAAgC,CAAC;AAExC,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,gDAAgD;AAChD,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,yCAAyC;AACzC,+EAA+E;AAC/E,yCAAyC;AACzC,uEAAuE;AACvE,sEAAsE;AACtE,kEAAkE;AAClE,+BAA+B;AAC/B,EAAE;AACF,oBAAoB;AACpB,8DAA8D;AAC9D,4CAA4C;AAC5C,qEAAqE;AACrE,QAAQ;AACR,gDAAgD;AAEhD,cAAc,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learning-health-reporter.d.ts","sourceRoot":"","sources":["../src/learning-health-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;
|
|
1
|
+
{"version":3,"file":"learning-health-reporter.d.ts","sourceRoot":"","sources":["../src/learning-health-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAKL,KAAK,sBAAsB,EAC5B,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,kCAAkC;IACjD,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AAUD,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,kCAAkC,GACzC,sBAAsB,CA4GxB"}
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* merged attempts).
|
|
28
28
|
* - Lifeline `QAPatternInsight` → Derwin `QAPattern` (D-2 rename).
|
|
29
29
|
*/
|
|
30
|
+
import { translateFixSuccessToOutcomeQuality } from '@derwinjs/core';
|
|
30
31
|
import { LearningHealthReporterError, } from '@derwinjs/sdk';
|
|
31
32
|
// ─── Constants (Lifeline parity) ─────────────────────────────────────────
|
|
32
33
|
const DEFAULT_WINDOW_DAYS = 30;
|
|
@@ -75,9 +76,19 @@ export function createPrismaLearningHealthReporter(config) {
|
|
|
75
76
|
});
|
|
76
77
|
const topImprovingClassifications = patterns.map((p) => ({
|
|
77
78
|
classification: p.classification,
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
79
|
+
// Unit translation: QAPattern.fixSuccessScore is a signed mean in
|
|
80
|
+
// [-1, +1] (Lifeline-shape preserved per D-2 Decision 1). The SDK
|
|
81
|
+
// contract for ClassificationTrendRow.score is the [0, 1]
|
|
82
|
+
// outcomeQuality scale, so consumers like LearningHealthDashboard
|
|
83
|
+
// can render Math.round(score * 100) without producing negative
|
|
84
|
+
// percentages on regression-heavy patterns. The WHERE filter above
|
|
85
|
+
// (`fixSuccessScore: { not: null }`) means the null branch is
|
|
86
|
+
// unreachable in practice — the defensive `=== null` guard is
|
|
87
|
+
// there for the Prisma type-widening case (Prisma's projection
|
|
88
|
+
// type doesn't narrow with WHERE clauses).
|
|
89
|
+
score: p.fixSuccessScore === null
|
|
90
|
+
? null
|
|
91
|
+
: translateFixSuccessToOutcomeQuality(p.fixSuccessScore),
|
|
81
92
|
sampleSize: p.fixOutcomeSampleSize,
|
|
82
93
|
}));
|
|
83
94
|
// ─── 3. Patterns with samples (transparency metric) ────────────────
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learning-health-reporter.js","sourceRoot":"","sources":["../src/learning-health-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;
|
|
1
|
+
{"version":3,"file":"learning-health-reporter.js","sourceRoot":"","sources":["../src/learning-health-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAAuditArtifactStore — Prisma implementation against Derwin's AuditArtifact
|
|
3
|
+
* table.
|
|
4
|
+
*
|
|
5
|
+
* QAP-094 follow-on (Sprint 13 Phase 2). Implements the QAAuditArtifactStore
|
|
6
|
+
* contract from @derwinjs/sdk (see packages/sdk/src/types/
|
|
7
|
+
* qa-audit-artifact-store.ts) against the @derwinjs/db Prisma client. Pattern
|
|
8
|
+
* matches createPrismaQAFixAttemptStore: a factory taking `{ prisma }` so the
|
|
9
|
+
* caller injects the client at boot.
|
|
10
|
+
*
|
|
11
|
+
* Tenant isolation: `listArtifactsForFixAttempt` filters by BOTH
|
|
12
|
+
* `qaFixAttemptId` AND `projectId` in a single Prisma `findMany`. A
|
|
13
|
+
* cross-tenant probe (right id, wrong projectId) returns []; an unknown id
|
|
14
|
+
* returns []; a known id with no artifacts returns []. The three negatives
|
|
15
|
+
* are indistinguishable — Pattern D defense-in-depth, no separate
|
|
16
|
+
* verify-attempt-exists round-trip.
|
|
17
|
+
*/
|
|
18
|
+
import type { PrismaClient } from './prisma.js';
|
|
19
|
+
import { type QAAuditArtifactStore } from '@derwinjs/sdk';
|
|
20
|
+
export interface PrismaQAAuditArtifactStoreConfig {
|
|
21
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
22
|
+
prisma: PrismaClient;
|
|
23
|
+
}
|
|
24
|
+
export declare function createPrismaQAAuditArtifactStore(config: PrismaQAAuditArtifactStoreConfig): QAAuditArtifactStore;
|
|
25
|
+
//# sourceMappingURL=qa-audit-artifact-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-audit-artifact-store.d.ts","sourceRoot":"","sources":["../src/qa-audit-artifact-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,gCAAgC;IAC/C,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AAID,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,gCAAgC,GACvC,oBAAoB,CAoBtB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAAuditArtifactStore — Prisma implementation against Derwin's AuditArtifact
|
|
3
|
+
* table.
|
|
4
|
+
*
|
|
5
|
+
* QAP-094 follow-on (Sprint 13 Phase 2). Implements the QAAuditArtifactStore
|
|
6
|
+
* contract from @derwinjs/sdk (see packages/sdk/src/types/
|
|
7
|
+
* qa-audit-artifact-store.ts) against the @derwinjs/db Prisma client. Pattern
|
|
8
|
+
* matches createPrismaQAFixAttemptStore: a factory taking `{ prisma }` so the
|
|
9
|
+
* caller injects the client at boot.
|
|
10
|
+
*
|
|
11
|
+
* Tenant isolation: `listArtifactsForFixAttempt` filters by BOTH
|
|
12
|
+
* `qaFixAttemptId` AND `projectId` in a single Prisma `findMany`. A
|
|
13
|
+
* cross-tenant probe (right id, wrong projectId) returns []; an unknown id
|
|
14
|
+
* returns []; a known id with no artifacts returns []. The three negatives
|
|
15
|
+
* are indistinguishable — Pattern D defense-in-depth, no separate
|
|
16
|
+
* verify-attempt-exists round-trip.
|
|
17
|
+
*/
|
|
18
|
+
import { ArtifactStoreError, } from '@derwinjs/sdk';
|
|
19
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
20
|
+
export function createPrismaQAAuditArtifactStore(config) {
|
|
21
|
+
const { prisma } = config;
|
|
22
|
+
return {
|
|
23
|
+
async listArtifactsForFixAttempt(filter) {
|
|
24
|
+
validateFilter(filter);
|
|
25
|
+
const rows = await prisma.auditArtifact.findMany({
|
|
26
|
+
where: {
|
|
27
|
+
qaFixAttemptId: filter.qaFixAttemptId,
|
|
28
|
+
projectId: filter.projectId,
|
|
29
|
+
},
|
|
30
|
+
orderBy: { capturedAt: 'asc' },
|
|
31
|
+
});
|
|
32
|
+
return rows.map(mapArtifact);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function mapArtifact(row) {
|
|
37
|
+
return {
|
|
38
|
+
id: row.id,
|
|
39
|
+
projectId: row.projectId,
|
|
40
|
+
qaFixAttemptId: row.qaFixAttemptId,
|
|
41
|
+
qaTicketId: row.qaTicketId,
|
|
42
|
+
artifactType: row.artifactType,
|
|
43
|
+
stage: row.stage,
|
|
44
|
+
storageBackend: row.storageBackend,
|
|
45
|
+
storageKey: row.storageKey,
|
|
46
|
+
contentType: row.contentType,
|
|
47
|
+
sizeBytes: row.sizeBytes,
|
|
48
|
+
contentHash: row.contentHash,
|
|
49
|
+
capturedAt: row.capturedAt,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function validateFilter(filter) {
|
|
53
|
+
if (filter.qaFixAttemptId.trim().length === 0) {
|
|
54
|
+
throw new ArtifactStoreError('invalid_input', 'qaFixAttemptId is required for listArtifactsForFixAttempt');
|
|
55
|
+
}
|
|
56
|
+
if (filter.projectId.trim().length === 0) {
|
|
57
|
+
throw new ArtifactStoreError('invalid_input', 'projectId is required for listArtifactsForFixAttempt — there is no cross-tenant list query');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=qa-audit-artifact-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-audit-artifact-store.js","sourceRoot":"","sources":["../src/qa-audit-artifact-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACL,kBAAkB,GAMnB,MAAM,eAAe,CAAC;AASvB,4EAA4E;AAE5E,MAAM,UAAU,gCAAgC,CAC9C,MAAwC;IAExC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE1B,OAAO;QACL,KAAK,CAAC,0BAA0B,CAC9B,MAAwC;YAExC,cAAc,CAAC,MAAM,CAAC,CAAC;YAEvB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC;gBAC/C,KAAK,EAAE;oBACL,cAAc,EAAE,MAAM,CAAC,cAAc;oBACrC,SAAS,EAAE,MAAM,CAAC,SAAS;iBAC5B;gBACD,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE;aAC/B,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC;KACF,CAAC;AACJ,CAAC;AAyBD,SAAS,WAAW,CAAC,GAA2B;IAC9C,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,YAAY,EAAE,GAAG,CAAC,YAAiC;QACnD,KAAK,EAAE,GAAG,CAAC,KAA2B;QACtC,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,MAAwC;IAC9D,IAAI,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,kBAAkB,CAC1B,eAAe,EACf,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,kBAAkB,CAC1B,eAAe,EACf,4FAA4F,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaSelfReviewVerdictStore — Prisma-backed implementation of the
|
|
3
|
+
* SDK SelfReviewVerdictStore contract introduced by QAP-133.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 13 Phase 9 (recursive self-review pre-merge gate). One row per
|
|
6
|
+
* self-review call regardless of outcome — the audit trail is
|
|
7
|
+
* non-negotiable. The orchestrator persists every call's verdict; the
|
|
8
|
+
* `?force=true` operator-override path on the orchestrate route also writes
|
|
9
|
+
* here with verdict='pass' and reasonHints capturing the operator's userId
|
|
10
|
+
* so every override is auditable.
|
|
11
|
+
*
|
|
12
|
+
* Tenant isolation: every method scopes by projectId. Pattern D applies —
|
|
13
|
+
* `getLatestForAttempt` returns null on cross-tenant or missing rows
|
|
14
|
+
* (defense-in-depth against tenant enumeration). `listVerdicts` returns []
|
|
15
|
+
* on cross-tenant projectId.
|
|
16
|
+
*
|
|
17
|
+
* Validation runs at the factory boundary BEFORE any Prisma call — empty
|
|
18
|
+
* `projectId` / `qaFixAttemptId` / `qaTicketId` / negative issue counts
|
|
19
|
+
* throw `invalid_input` so the error surfaces with a clear caller-bug code
|
|
20
|
+
* rather than as a Prisma constraint violation.
|
|
21
|
+
*/
|
|
22
|
+
import { type PrismaClient } from './prisma.js';
|
|
23
|
+
import { type SelfReviewVerdictStore } from '@derwinjs/sdk';
|
|
24
|
+
export interface PrismaSelfReviewVerdictStoreConfig {
|
|
25
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
26
|
+
prisma: PrismaClient;
|
|
27
|
+
}
|
|
28
|
+
export declare function createPrismaSelfReviewVerdictStore(config: PrismaSelfReviewVerdictStoreConfig): SelfReviewVerdictStore;
|
|
29
|
+
//# sourceMappingURL=self-review-verdict-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-review-verdict-store.d.ts","sourceRoot":"","sources":["../src/self-review-verdict-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAML,KAAK,sBAAsB,EAE5B,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,kCAAkC;IACjD,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AA8ED,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,kCAAkC,GACzC,sBAAsB,CA+FxB"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaSelfReviewVerdictStore — Prisma-backed implementation of the
|
|
3
|
+
* SDK SelfReviewVerdictStore contract introduced by QAP-133.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 13 Phase 9 (recursive self-review pre-merge gate). One row per
|
|
6
|
+
* self-review call regardless of outcome — the audit trail is
|
|
7
|
+
* non-negotiable. The orchestrator persists every call's verdict; the
|
|
8
|
+
* `?force=true` operator-override path on the orchestrate route also writes
|
|
9
|
+
* here with verdict='pass' and reasonHints capturing the operator's userId
|
|
10
|
+
* so every override is auditable.
|
|
11
|
+
*
|
|
12
|
+
* Tenant isolation: every method scopes by projectId. Pattern D applies —
|
|
13
|
+
* `getLatestForAttempt` returns null on cross-tenant or missing rows
|
|
14
|
+
* (defense-in-depth against tenant enumeration). `listVerdicts` returns []
|
|
15
|
+
* on cross-tenant projectId.
|
|
16
|
+
*
|
|
17
|
+
* Validation runs at the factory boundary BEFORE any Prisma call — empty
|
|
18
|
+
* `projectId` / `qaFixAttemptId` / `qaTicketId` / negative issue counts
|
|
19
|
+
* throw `invalid_input` so the error surfaces with a clear caller-bug code
|
|
20
|
+
* rather than as a Prisma constraint violation.
|
|
21
|
+
*/
|
|
22
|
+
import { Prisma } from './prisma.js';
|
|
23
|
+
import { SelfReviewVerdictStoreError, } from '@derwinjs/sdk';
|
|
24
|
+
// ─── Constants ───────────────────────────────────────────────────────────
|
|
25
|
+
const DEFAULT_LIST_LIMIT = 50;
|
|
26
|
+
const MAX_LIST_LIMIT = 200;
|
|
27
|
+
const VALID_VERDICTS = new Set([
|
|
28
|
+
'pass',
|
|
29
|
+
'fail',
|
|
30
|
+
'manual_review_required',
|
|
31
|
+
]);
|
|
32
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
33
|
+
function assertNonEmpty(value, fieldName) {
|
|
34
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
35
|
+
throw new SelfReviewVerdictStoreError('invalid_input', `createPrismaSelfReviewVerdictStore: ${fieldName} is required and non-empty`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function assertNonNegativeInteger(value, fieldName) {
|
|
39
|
+
if (typeof value !== 'number' ||
|
|
40
|
+
!Number.isFinite(value) ||
|
|
41
|
+
value < 0 ||
|
|
42
|
+
!Number.isInteger(value)) {
|
|
43
|
+
throw new SelfReviewVerdictStoreError('invalid_input', `createPrismaSelfReviewVerdictStore: ${fieldName} must be a non-negative integer`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function assertVerdict(value) {
|
|
47
|
+
if (typeof value !== 'string' || !VALID_VERDICTS.has(value)) {
|
|
48
|
+
throw new SelfReviewVerdictStoreError('invalid_input', `createPrismaSelfReviewVerdictStore: verdict must be one of 'pass' | 'fail' | 'manual_review_required'`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function assertCreateInput(input) {
|
|
52
|
+
assertNonEmpty(input.projectId, 'projectId');
|
|
53
|
+
assertNonEmpty(input.qaFixAttemptId, 'qaFixAttemptId');
|
|
54
|
+
assertNonEmpty(input.qaTicketId, 'qaTicketId');
|
|
55
|
+
assertNonEmpty(input.classification, 'classification');
|
|
56
|
+
assertNonEmpty(input.surface, 'surface');
|
|
57
|
+
assertVerdict(input.verdict);
|
|
58
|
+
assertNonNegativeInteger(input.criticalIssuesFound, 'criticalIssuesFound');
|
|
59
|
+
assertNonNegativeInteger(input.minorIssuesFound, 'minorIssuesFound');
|
|
60
|
+
if (!Array.isArray(input.reasonHints)) {
|
|
61
|
+
throw new SelfReviewVerdictStoreError('invalid_input', `createPrismaSelfReviewVerdictStore: reasonHints must be an array of strings`);
|
|
62
|
+
}
|
|
63
|
+
for (const hint of input.reasonHints) {
|
|
64
|
+
if (typeof hint !== 'string') {
|
|
65
|
+
throw new SelfReviewVerdictStoreError('invalid_input', `createPrismaSelfReviewVerdictStore: reasonHints must be an array of strings`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function clampLimit(value) {
|
|
70
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
71
|
+
return DEFAULT_LIST_LIMIT;
|
|
72
|
+
return Math.min(MAX_LIST_LIMIT, Math.floor(value));
|
|
73
|
+
}
|
|
74
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
75
|
+
export function createPrismaSelfReviewVerdictStore(config) {
|
|
76
|
+
const { prisma } = config;
|
|
77
|
+
return {
|
|
78
|
+
async createVerdict(input) {
|
|
79
|
+
assertCreateInput(input);
|
|
80
|
+
try {
|
|
81
|
+
const row = await prisma.selfReviewVerdict.create({
|
|
82
|
+
data: {
|
|
83
|
+
projectId: input.projectId,
|
|
84
|
+
qaFixAttemptId: input.qaFixAttemptId,
|
|
85
|
+
qaTicketId: input.qaTicketId,
|
|
86
|
+
classification: input.classification,
|
|
87
|
+
surface: input.surface,
|
|
88
|
+
verdict: input.verdict,
|
|
89
|
+
reasonHints: input.reasonHints,
|
|
90
|
+
criticalIssuesFound: input.criticalIssuesFound,
|
|
91
|
+
minorIssuesFound: input.minorIssuesFound,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return toRecord(row);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2003') {
|
|
98
|
+
// FK violation — projectId or qaFixAttemptId references a row
|
|
99
|
+
// that does not exist. Surface as fk_violation so the API layer
|
|
100
|
+
// can return 400 / 404 as appropriate.
|
|
101
|
+
throw new SelfReviewVerdictStoreError('fk_violation', `createPrismaSelfReviewVerdictStore: projectId or qaFixAttemptId does not resolve to an existing row`, err);
|
|
102
|
+
}
|
|
103
|
+
throw new SelfReviewVerdictStoreError('storage_failed', `createPrismaSelfReviewVerdictStore: createVerdict raised: ${err.message}`, err);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
async listVerdicts(input) {
|
|
107
|
+
assertNonEmpty(input.projectId, 'projectId');
|
|
108
|
+
const limit = clampLimit(input.limit);
|
|
109
|
+
const where = { projectId: input.projectId };
|
|
110
|
+
if (input.qaFixAttemptId !== undefined) {
|
|
111
|
+
assertNonEmpty(input.qaFixAttemptId, 'qaFixAttemptId');
|
|
112
|
+
where.qaFixAttemptId = input.qaFixAttemptId;
|
|
113
|
+
}
|
|
114
|
+
if (input.verdict !== undefined) {
|
|
115
|
+
assertVerdict(input.verdict);
|
|
116
|
+
where.verdict = input.verdict;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const rows = await prisma.selfReviewVerdict.findMany({
|
|
120
|
+
where,
|
|
121
|
+
orderBy: { createdAt: 'desc' },
|
|
122
|
+
take: limit,
|
|
123
|
+
});
|
|
124
|
+
return rows.map(toRecord);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
throw new SelfReviewVerdictStoreError('storage_failed', `createPrismaSelfReviewVerdictStore: listVerdicts raised: ${err.message}`, err);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
async getLatestForAttempt(input) {
|
|
131
|
+
assertNonEmpty(input.qaFixAttemptId, 'qaFixAttemptId');
|
|
132
|
+
assertNonEmpty(input.projectId, 'projectId');
|
|
133
|
+
try {
|
|
134
|
+
const row = await prisma.selfReviewVerdict.findFirst({
|
|
135
|
+
where: {
|
|
136
|
+
qaFixAttemptId: input.qaFixAttemptId,
|
|
137
|
+
projectId: input.projectId,
|
|
138
|
+
},
|
|
139
|
+
orderBy: { createdAt: 'desc' },
|
|
140
|
+
});
|
|
141
|
+
return row === null ? null : toRecord(row);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
throw new SelfReviewVerdictStoreError('storage_failed', `createPrismaSelfReviewVerdictStore: getLatestForAttempt raised: ${err.message}`, err);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function toRecord(row) {
|
|
150
|
+
return {
|
|
151
|
+
id: row.id,
|
|
152
|
+
projectId: row.projectId,
|
|
153
|
+
qaFixAttemptId: row.qaFixAttemptId,
|
|
154
|
+
qaTicketId: row.qaTicketId,
|
|
155
|
+
classification: row.classification,
|
|
156
|
+
surface: row.surface,
|
|
157
|
+
// The string column is held as a free-form String at the schema level
|
|
158
|
+
// (no Prisma enum) — the SDK contract narrows it to the union after
|
|
159
|
+
// a runtime validation guard.
|
|
160
|
+
verdict: row.verdict,
|
|
161
|
+
reasonHints: row.reasonHints,
|
|
162
|
+
criticalIssuesFound: row.criticalIssuesFound,
|
|
163
|
+
minorIssuesFound: row.minorIssuesFound,
|
|
164
|
+
createdAt: row.createdAt,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=self-review-verdict-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-review-verdict-store.js","sourceRoot":"","sources":["../src/self-review-verdict-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.13.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.13.0",
|
|
37
|
+
"@derwinjs/sdk": "0.13.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@vitest/coverage-v8": "^2.1.9",
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
-- Sprint 13 Phase 9 (QAP-133) — Recursive self-review verdict audit trail.
|
|
2
|
+
--
|
|
3
|
+
-- Adds:
|
|
4
|
+
-- 1. New AttemptStatus enum value `MANUAL_REVIEW_REQUIRED` — set by the
|
|
5
|
+
-- orchestrator when the recursive self-reviewer returns 'fail' or
|
|
6
|
+
-- 'manual_review_required'. Blocks dispatch until an operator overrides
|
|
7
|
+
-- via the orchestrate route's `?force=true` query flag.
|
|
8
|
+
-- 2. New `derwin.self_review_verdicts` table — one row per self-review call
|
|
9
|
+
-- regardless of outcome. Persisted by both the orchestrator (every fix
|
|
10
|
+
-- cycle) and the admin POST /qa/fix-attempts/:id/self-review route.
|
|
11
|
+
-- The `?force=true` operator-override path also writes here with
|
|
12
|
+
-- verdict='pass' and reasonHints capturing the operator's userId so
|
|
13
|
+
-- every override is auditable.
|
|
14
|
+
-- 3. Indexes:
|
|
15
|
+
-- - (projectId) for tenant-scoped scans
|
|
16
|
+
-- - (qaFixAttemptId) for the per-attempt latest-verdict lookup
|
|
17
|
+
-- - (projectId, classification, surface) for cohort dashboards
|
|
18
|
+
-- - (projectId, createdAt DESC) for the recent-verdicts pane
|
|
19
|
+
-- 4. Foreign keys to derwin.projects and derwin.qa_fix_attempts (cascade
|
|
20
|
+
-- on delete — deleting a project/attempt removes its verdict trail).
|
|
21
|
+
--
|
|
22
|
+
-- Idempotent (IF NOT EXISTS) — safe to re-run on environments where parts
|
|
23
|
+
-- of the migration already landed.
|
|
24
|
+
|
|
25
|
+
-- ─── 1. Extend AttemptStatus enum ───────────────────────────────────────────
|
|
26
|
+
-- ALTER TYPE ... ADD VALUE is not transactional in Postgres < 12, but Prisma
|
|
27
|
+
-- supports it on 12+ which is our target. IF NOT EXISTS guards against
|
|
28
|
+
-- re-running on environments where this already landed.
|
|
29
|
+
|
|
30
|
+
ALTER TYPE "AttemptStatus" ADD VALUE IF NOT EXISTS 'MANUAL_REVIEW_REQUIRED';
|
|
31
|
+
|
|
32
|
+
-- ─── 2. self_review_verdicts table ──────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS "derwin"."self_review_verdicts" (
|
|
35
|
+
"id" TEXT NOT NULL,
|
|
36
|
+
"projectId" TEXT NOT NULL,
|
|
37
|
+
"qaFixAttemptId" TEXT NOT NULL,
|
|
38
|
+
"qaTicketId" TEXT NOT NULL,
|
|
39
|
+
"classification" TEXT NOT NULL,
|
|
40
|
+
"surface" TEXT NOT NULL,
|
|
41
|
+
"verdict" TEXT NOT NULL,
|
|
42
|
+
"reasonHints" TEXT[] NOT NULL,
|
|
43
|
+
"criticalIssuesFound" INTEGER NOT NULL,
|
|
44
|
+
"minorIssuesFound" INTEGER NOT NULL,
|
|
45
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
46
|
+
CONSTRAINT "self_review_verdicts_pkey" PRIMARY KEY ("id")
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
-- ─── 3. Indexes ────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS "self_review_verdicts_projectId_idx"
|
|
52
|
+
ON "derwin"."self_review_verdicts"("projectId");
|
|
53
|
+
|
|
54
|
+
CREATE INDEX IF NOT EXISTS "self_review_verdicts_qaFixAttemptId_idx"
|
|
55
|
+
ON "derwin"."self_review_verdicts"("qaFixAttemptId");
|
|
56
|
+
|
|
57
|
+
CREATE INDEX IF NOT EXISTS "self_review_verdicts_projectId_classification_surface_idx"
|
|
58
|
+
ON "derwin"."self_review_verdicts"("projectId", "classification", "surface");
|
|
59
|
+
|
|
60
|
+
CREATE INDEX IF NOT EXISTS "self_review_verdicts_projectId_createdAt_idx"
|
|
61
|
+
ON "derwin"."self_review_verdicts"("projectId", "createdAt" DESC);
|
|
62
|
+
|
|
63
|
+
-- ─── 4. Foreign keys ───────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
ALTER TABLE "derwin"."self_review_verdicts"
|
|
66
|
+
ADD CONSTRAINT "self_review_verdicts_projectId_fkey"
|
|
67
|
+
FOREIGN KEY ("projectId") REFERENCES "derwin"."projects"("id")
|
|
68
|
+
ON DELETE CASCADE ON UPDATE CASCADE;
|
|
69
|
+
|
|
70
|
+
ALTER TABLE "derwin"."self_review_verdicts"
|
|
71
|
+
ADD CONSTRAINT "self_review_verdicts_qaFixAttemptId_fkey"
|
|
72
|
+
FOREIGN KEY ("qaFixAttemptId") REFERENCES "derwin"."qa_fix_attempts"("id")
|
|
73
|
+
ON DELETE CASCADE ON UPDATE CASCADE;
|
package/prisma/schema.prisma
CHANGED
|
@@ -153,6 +153,7 @@ model Project {
|
|
|
153
153
|
tenantFuzzConfig TenantFuzzConfig?
|
|
154
154
|
rumSamples RumSample[]
|
|
155
155
|
milestoneEvents MilestoneEvent[]
|
|
156
|
+
selfReviewVerdicts SelfReviewVerdict[]
|
|
156
157
|
|
|
157
158
|
@@map("projects")
|
|
158
159
|
@@schema("derwin")
|
|
@@ -485,9 +486,10 @@ model QAFixAttempt {
|
|
|
485
486
|
attemptedAt DateTime @default(now())
|
|
486
487
|
closedAt DateTime?
|
|
487
488
|
|
|
488
|
-
ticket
|
|
489
|
-
project
|
|
490
|
-
artifacts
|
|
489
|
+
ticket QATicket @relation(fields: [qaTicketId], references: [id], onDelete: Cascade)
|
|
490
|
+
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
491
|
+
artifacts AuditArtifact[]
|
|
492
|
+
selfReviewVerdicts SelfReviewVerdict[]
|
|
491
493
|
|
|
492
494
|
@@index([qaTicketId, attemptNumber])
|
|
493
495
|
@@index([projectId, attemptedAt(sort: Desc)])
|
|
@@ -505,6 +507,11 @@ enum AttemptStatus {
|
|
|
505
507
|
REJECTED
|
|
506
508
|
REGRESSED_REVERTED
|
|
507
509
|
FAILED
|
|
510
|
+
// QAP-133 (Sprint 13 Phase 9). Set by the orchestrator when the recursive
|
|
511
|
+
// self-reviewer returns 'fail' or 'manual_review_required'. Blocks dispatch
|
|
512
|
+
// until an operator overrides via the orchestrate route's ?force=true flag
|
|
513
|
+
// (which auditably writes a synthetic SelfReviewVerdict 'pass' row).
|
|
514
|
+
MANUAL_REVIEW_REQUIRED
|
|
508
515
|
|
|
509
516
|
@@schema("derwin")
|
|
510
517
|
}
|
|
@@ -1171,3 +1178,47 @@ model MilestoneEvent {
|
|
|
1171
1178
|
@@map("milestone_events")
|
|
1172
1179
|
@@schema("derwin")
|
|
1173
1180
|
}
|
|
1181
|
+
|
|
1182
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1183
|
+
// QAP-133 (Sprint 13 Phase 9) — Recursive self-review verdict audit trail.
|
|
1184
|
+
//
|
|
1185
|
+
// Pre-merge gate: the recursive self-reviewer (Anthropic-backed adapter
|
|
1186
|
+
// reviewing Claude's own diff) emits one verdict row per call. The
|
|
1187
|
+
// orchestrator persists every call's verdict regardless of outcome — the
|
|
1188
|
+
// audit trail is non-negotiable. The orchestrate route's `?force=true`
|
|
1189
|
+
// override path also writes here with verdict='pass' and reasonHints
|
|
1190
|
+
// capturing the operator's userId so every override is auditable.
|
|
1191
|
+
//
|
|
1192
|
+
// Tenant scope: every read/write filters by projectId (Pattern D). The
|
|
1193
|
+
// (projectId, classification, surface) compound index supports cohort
|
|
1194
|
+
// queries surfaced on the dashboard. The (projectId, createdAt desc)
|
|
1195
|
+
// index supports the recent-verdicts pane.
|
|
1196
|
+
//
|
|
1197
|
+
// The Prisma-backed implementation lives at
|
|
1198
|
+
// packages/db/src/self-review-verdict-store.ts; the SDK contract is in
|
|
1199
|
+
// @derwinjs/sdk (SelfReviewVerdictStore in
|
|
1200
|
+
// packages/sdk/src/types/self-review-verdict-store.ts).
|
|
1201
|
+
|
|
1202
|
+
model SelfReviewVerdict {
|
|
1203
|
+
id String @id @default(cuid())
|
|
1204
|
+
projectId String
|
|
1205
|
+
qaFixAttemptId String
|
|
1206
|
+
qaTicketId String
|
|
1207
|
+
classification String // mirrored from QATicket for cohort analysis
|
|
1208
|
+
surface String // mirrored from QATicket for cohort analysis
|
|
1209
|
+
verdict String // 'pass' | 'fail' | 'manual_review_required'
|
|
1210
|
+
reasonHints String[] // pgsql native array; reasonHints from the SDK
|
|
1211
|
+
criticalIssuesFound Int
|
|
1212
|
+
minorIssuesFound Int
|
|
1213
|
+
createdAt DateTime @default(now())
|
|
1214
|
+
|
|
1215
|
+
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
1216
|
+
qaFixAttempt QAFixAttempt @relation(fields: [qaFixAttemptId], references: [id], onDelete: Cascade)
|
|
1217
|
+
|
|
1218
|
+
@@index([projectId])
|
|
1219
|
+
@@index([qaFixAttemptId])
|
|
1220
|
+
@@index([projectId, classification, surface]) // cohort queries
|
|
1221
|
+
@@index([projectId, createdAt(sort: Desc)]) // recent verdicts
|
|
1222
|
+
@@map("self_review_verdicts")
|
|
1223
|
+
@@schema("derwin")
|
|
1224
|
+
}
|