@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 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
@@ -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;AAEH,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,CAiGxB"}
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
- // Filter ensures fixSuccessScore !== null, but TS narrows it to
79
- // number | null because Prisma's type widens. Coerce defensively.
80
- score: p.fixSuccessScore ?? 0,
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":"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"}
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.12.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.12.0",
37
- "@derwinjs/sdk": "0.12.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;
@@ -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 QATicket @relation(fields: [qaTicketId], references: [id], onDelete: Cascade)
489
- project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
490
- artifacts AuditArtifact[]
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
+ }