@derwinjs/db 0.7.0 → 0.9.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.
Files changed (49) hide show
  1. package/dist/auto-promotion-evaluator.d.ts +63 -0
  2. package/dist/auto-promotion-evaluator.d.ts.map +1 -0
  3. package/dist/auto-promotion-evaluator.js +195 -0
  4. package/dist/auto-promotion-evaluator.js.map +1 -0
  5. package/dist/budget-gate.d.ts +43 -0
  6. package/dist/budget-gate.d.ts.map +1 -0
  7. package/dist/budget-gate.js +110 -0
  8. package/dist/budget-gate.js.map +1 -0
  9. package/dist/classification-override-store.d.ts +24 -0
  10. package/dist/classification-override-store.d.ts.map +1 -0
  11. package/dist/classification-override-store.js +131 -0
  12. package/dist/classification-override-store.js.map +1 -0
  13. package/dist/freeze-window-evaluator.d.ts +62 -0
  14. package/dist/freeze-window-evaluator.d.ts.map +1 -0
  15. package/dist/freeze-window-evaluator.js +236 -0
  16. package/dist/freeze-window-evaluator.js.map +1 -0
  17. package/dist/index.d.ts +8 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +8 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/kill-switch-evaluator.d.ts +68 -0
  22. package/dist/kill-switch-evaluator.d.ts.map +1 -0
  23. package/dist/kill-switch-evaluator.js +389 -0
  24. package/dist/kill-switch-evaluator.js.map +1 -0
  25. package/dist/path-tier-resolver.d.ts +47 -0
  26. package/dist/path-tier-resolver.d.ts.map +1 -0
  27. package/dist/path-tier-resolver.js +177 -0
  28. package/dist/path-tier-resolver.js.map +1 -0
  29. package/dist/project-mode-store.d.ts +28 -0
  30. package/dist/project-mode-store.d.ts.map +1 -0
  31. package/dist/project-mode-store.js +126 -0
  32. package/dist/project-mode-store.js.map +1 -0
  33. package/dist/trust-threshold-config-store.d.ts +20 -0
  34. package/dist/trust-threshold-config-store.d.ts.map +1 -0
  35. package/dist/trust-threshold-config-store.js +88 -0
  36. package/dist/trust-threshold-config-store.js.map +1 -0
  37. package/package.json +3 -3
  38. package/prisma/migrations/20260507120000_sprint8_policy_governance/migration.sql +58 -0
  39. package/prisma/migrations/20260507120100_sprint8_phase2_thresholds_and_freeze/migration.sql +33 -0
  40. package/prisma/migrations/20260507120200_sprint8_phase3_budget_cap/migration.sql +21 -0
  41. package/prisma/migrations/20260507120300_sprint8_phase4_kill_switches/migration.sql +74 -0
  42. package/prisma/schema.prisma +183 -11
  43. package/prisma-client/edge.js +96 -19
  44. package/prisma-client/index-browser.js +93 -16
  45. package/prisma-client/index.d.ts +10997 -3426
  46. package/prisma-client/index.js +96 -19
  47. package/prisma-client/package.json +1 -1
  48. package/prisma-client/schema.prisma +183 -11
  49. package/prisma-client/wasm.js +93 -16
@@ -0,0 +1,28 @@
1
+ /**
2
+ * createPrismaProjectModeStore — Prisma-backed implementation of the SDK
3
+ * ProjectModeStore contract introduced by QAP-090.
4
+ *
5
+ * Sprint 9 (Policy + UI, Phase 1). Single choke point for reading and
6
+ * writing `Project.mode` (OBSERVE / TICKET_ONLY / AUTHOR / AUTO — see
7
+ * product brief §11.2). Every mode change goes through `setMode` so the
8
+ * audit log (ProjectModeLog) stays in lockstep with the Project row.
9
+ *
10
+ * Tenant isolation: every read scopes by projectId. `getMode` returns
11
+ * null for unknown projectId; `listLog` returns an empty array. `setMode`
12
+ * throws `project_not_found` (rather than returning null) because a
13
+ * mode-change call against a missing project is a caller bug, not a
14
+ * cross-tenant probe — this matches the operator UX of the policy editor.
15
+ *
16
+ * Atomicity: `setMode` wraps (a) read fromMode → (b) update Project.mode +
17
+ * modeChangedAt + modeChangedBy → (c) insert ProjectModeLog row in a
18
+ * single Prisma interactive transaction so a partial write never leaves
19
+ * the audit log out of sync with the Project row.
20
+ */
21
+ import type { PrismaClient } from './prisma.js';
22
+ import { type ProjectModeStore } from '@derwinjs/sdk';
23
+ export interface PrismaProjectModeStoreConfig {
24
+ /** Generated Prisma client. Pass an instance per process. */
25
+ prisma: PrismaClient;
26
+ }
27
+ export declare function createPrismaProjectModeStore(config: PrismaProjectModeStoreConfig): ProjectModeStore;
28
+ //# sourceMappingURL=project-mode-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-mode-store.d.ts","sourceRoot":"","sources":["../src/project-mode-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAML,KAAK,gBAAgB,EAEtB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AAgED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,4BAA4B,GACnC,gBAAgB,CA+ElB"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * createPrismaProjectModeStore — Prisma-backed implementation of the SDK
3
+ * ProjectModeStore contract introduced by QAP-090.
4
+ *
5
+ * Sprint 9 (Policy + UI, Phase 1). Single choke point for reading and
6
+ * writing `Project.mode` (OBSERVE / TICKET_ONLY / AUTHOR / AUTO — see
7
+ * product brief §11.2). Every mode change goes through `setMode` so the
8
+ * audit log (ProjectModeLog) stays in lockstep with the Project row.
9
+ *
10
+ * Tenant isolation: every read scopes by projectId. `getMode` returns
11
+ * null for unknown projectId; `listLog` returns an empty array. `setMode`
12
+ * throws `project_not_found` (rather than returning null) because a
13
+ * mode-change call against a missing project is a caller bug, not a
14
+ * cross-tenant probe — this matches the operator UX of the policy editor.
15
+ *
16
+ * Atomicity: `setMode` wraps (a) read fromMode → (b) update Project.mode +
17
+ * modeChangedAt + modeChangedBy → (c) insert ProjectModeLog row in a
18
+ * single Prisma interactive transaction so a partial write never leaves
19
+ * the audit log out of sync with the Project row.
20
+ */
21
+ import { ProjectModeStoreError, ProjectModeValues, } from '@derwinjs/sdk';
22
+ // ─── Validation ──────────────────────────────────────────────────────────
23
+ function validateSetInput(input) {
24
+ if (typeof input.projectId !== 'string' || input.projectId.length === 0) {
25
+ throw new ProjectModeStoreError('invalid_input', 'ProjectModeStore: projectId is required');
26
+ }
27
+ if (!ProjectModeValues.includes(input.toMode)) {
28
+ throw new ProjectModeStoreError('invalid_mode', `ProjectModeStore: toMode must be one of ${ProjectModeValues.join(', ')} (got ${input.toMode})`);
29
+ }
30
+ if (typeof input.reason !== 'string' || input.reason.trim().length === 0) {
31
+ throw new ProjectModeStoreError('invalid_input', 'ProjectModeStore: reason is required');
32
+ }
33
+ if (typeof input.changedBy !== 'string' || input.changedBy.trim().length === 0) {
34
+ throw new ProjectModeStoreError('invalid_input', 'ProjectModeStore: changedBy is required');
35
+ }
36
+ }
37
+ function mapProjectRow(row) {
38
+ return {
39
+ projectId: row.id,
40
+ mode: row.mode,
41
+ modeChangedAt: row.modeChangedAt,
42
+ modeChangedBy: row.modeChangedBy,
43
+ };
44
+ }
45
+ function mapLogRow(row) {
46
+ return {
47
+ id: row.id,
48
+ projectId: row.projectId,
49
+ fromMode: row.fromMode,
50
+ toMode: row.toMode,
51
+ reason: row.reason,
52
+ changedBy: row.changedBy,
53
+ changedAt: row.changedAt,
54
+ };
55
+ }
56
+ // ─── Factory ─────────────────────────────────────────────────────────────
57
+ export function createPrismaProjectModeStore(config) {
58
+ const { prisma } = config;
59
+ return {
60
+ async getMode(input) {
61
+ const row = await prisma.project.findUnique({
62
+ where: { id: input.projectId },
63
+ select: {
64
+ id: true,
65
+ mode: true,
66
+ modeChangedAt: true,
67
+ modeChangedBy: true,
68
+ },
69
+ });
70
+ if (row === null)
71
+ return null;
72
+ return mapProjectRow(row);
73
+ },
74
+ async setMode(input) {
75
+ validateSetInput(input);
76
+ // Atomic: read fromMode → update Project → insert log row.
77
+ // Interactive transaction so all three steps share a snapshot and
78
+ // commit together.
79
+ const updated = await prisma.$transaction(async (tx) => {
80
+ const existing = await tx.project.findUnique({
81
+ where: { id: input.projectId },
82
+ select: { id: true, mode: true },
83
+ });
84
+ if (existing === null) {
85
+ throw new ProjectModeStoreError('project_not_found', `ProjectModeStore: project ${input.projectId} not found`);
86
+ }
87
+ const fromMode = existing.mode;
88
+ const now = new Date();
89
+ const next = await tx.project.update({
90
+ where: { id: input.projectId },
91
+ data: {
92
+ mode: input.toMode,
93
+ modeChangedAt: now,
94
+ modeChangedBy: input.changedBy,
95
+ },
96
+ select: {
97
+ id: true,
98
+ mode: true,
99
+ modeChangedAt: true,
100
+ modeChangedBy: true,
101
+ },
102
+ });
103
+ await tx.projectModeLog.create({
104
+ data: {
105
+ projectId: input.projectId,
106
+ fromMode,
107
+ toMode: input.toMode,
108
+ reason: input.reason,
109
+ changedBy: input.changedBy,
110
+ },
111
+ });
112
+ return next;
113
+ });
114
+ return mapProjectRow(updated);
115
+ },
116
+ async listLog(input) {
117
+ const rows = await prisma.projectModeLog.findMany({
118
+ where: { projectId: input.projectId },
119
+ orderBy: { changedAt: 'desc' },
120
+ take: input.limit ?? 50,
121
+ });
122
+ return rows.map(mapLogRow);
123
+ },
124
+ };
125
+ }
126
+ //# sourceMappingURL=project-mode-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-mode-store.js","sourceRoot":"","sources":["../src/project-mode-store.ts"],"names":[],"mappings":"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"}
@@ -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.7.0",
3
+ "version": "0.9.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.7.0",
37
- "@derwinjs/sdk": "0.7.0"
36
+ "@derwinjs/core": "0.9.0",
37
+ "@derwinjs/sdk": "0.9.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");