fix(admin): resolve all 266 TypeScript errors, enable strict build

Eliminate the pre-existing TS errors that were masked by
next.config.js `typescript.ignoreBuildErrors: true`, then turn the flag
OFF so the compiler is a real safety net for future changes. `next build`
and `tsc --noEmit` now pass with 0 errors.

The errors were not cosmetic — several exposed real latent bugs hidden by
the flag, e.g. the drafting-engine ConstraintEnforcer read non-existent
fields (`t.rule.dsfaRequired`, `d.required`, `r.title`), so its DSFA hard
gate and risk-flag checks were silently no-ops; scopeDefaults read
snake_case CompanyProfile fields that never matched the camelCase type
(generator defaults never populated). Both fixed by aligning code to the
current types.

Highlights:
- Vitest globals: add vitest-globals.d.ts (config already had globals:true)
  so the test files type-check; exclude Playwright specs from vitest.
- Add a minimal ambient `pg` module declaration (no @types/pg installed).
- Fix Next 15 route handlers to await Promise params.
- Reconcile drifted types across loeschfristen, compliance-scope, document-
  generator, drafting-engine, vendor-compliance, agent and more.

Pre-existing (NOT caused here, proven by stashing the diff): 3 vitest
logic tests still fail — getNextStep (2) and buildDocumentScope priority (1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-11 00:42:44 +02:00
parent bb9aacc3d3
commit a28db8f8f0
76 changed files with 280 additions and 190 deletions
@@ -12,9 +12,9 @@ describe('ConstraintEnforcer', () => {
scores: { risk_score: 50, complexity_score: 50, assurance_need: 50, composite_score: 50 },
triggeredHardTriggers: [],
requiredDocuments: [
{ documentType: 'vvt', label: 'VVT', required: true, depth: 'Standard', detailItems: [], estimatedEffort: '2h', triggeredBy: [] },
{ documentType: 'tom', label: 'TOM', required: true, depth: 'Standard', detailItems: [], estimatedEffort: '3h', triggeredBy: [] },
{ documentType: 'lf', label: 'LF', required: true, depth: 'Basis', detailItems: [], estimatedEffort: '1h', triggeredBy: [] },
{ documentType: 'vvt', label: 'VVT', requirement: 'mandatory' as const, priority: 'medium' as const, estimatedEffort: 2, triggeredBy: [] },
{ documentType: 'tom', label: 'TOM', requirement: 'mandatory' as const, priority: 'medium' as const, estimatedEffort: 3, triggeredBy: [] },
{ documentType: 'lf', label: 'LF', requirement: 'mandatory' as const, priority: 'medium' as const, estimatedEffort: 1, triggeredBy: [] },
],
riskFlags: [],
gaps: [],
@@ -66,7 +66,7 @@ describe('ConstraintEnforcer', () => {
it('should warn but allow optional documents', () => {
const decision = makeDecision({
requiredDocuments: [
{ documentType: 'vvt', label: 'VVT', required: true, depth: 'Standard', detailItems: [], estimatedEffort: '2h', triggeredBy: [] },
{ documentType: 'vvt', label: 'VVT', requirement: 'mandatory' as const, priority: 'medium' as const, estimatedEffort: 2, triggeredBy: [] },
],
})
const result = enforcer.check('dsfa', decision)
@@ -115,18 +115,13 @@ describe('ConstraintEnforcer', () => {
const decision = makeDecision({
determinedLevel: 'L3',
triggeredHardTriggers: [{
rule: {
id: 'HT-ART9',
label: 'Art. 9 Daten',
description: '',
conditionField: '',
conditionOperator: 'EQUALS' as const,
conditionValue: null,
minimumLevel: 'L3',
mandatoryDocuments: ['dsfa'],
dsfaRequired: true,
legalReference: 'Art. 35 DSGVO',
},
ruleId: 'HT-ART9',
category: '',
description: 'Art. 9 Daten',
minimumLevel: 'L3',
requiresDSFA: true,
mandatoryDocuments: ['dsfa'],
legalReference: 'Art. 35 DSGVO',
matchedValue: null,
explanation: 'Art. 9 Daten verarbeitet',
}],
@@ -139,24 +134,19 @@ describe('ConstraintEnforcer', () => {
const decision = makeDecision({
determinedLevel: 'L3',
triggeredHardTriggers: [{
rule: {
id: 'HT-ART9',
label: 'Art. 9 Daten',
description: '',
conditionField: '',
conditionOperator: 'EQUALS' as const,
conditionValue: null,
minimumLevel: 'L3',
mandatoryDocuments: ['dsfa'],
dsfaRequired: true,
legalReference: 'Art. 35 DSGVO',
},
ruleId: 'HT-ART9',
category: '',
description: 'Art. 9 Daten',
minimumLevel: 'L3',
requiresDSFA: true,
mandatoryDocuments: ['dsfa'],
legalReference: 'Art. 35 DSGVO',
matchedValue: null,
explanation: '',
}],
requiredDocuments: [
{ documentType: 'dsfa', label: 'DSFA', required: true, depth: 'Vollstaendig', detailItems: [], estimatedEffort: '8h', triggeredBy: [] },
{ documentType: 'vvt', label: 'VVT', required: true, depth: 'Standard', detailItems: [], estimatedEffort: '2h', triggeredBy: [] },
{ documentType: 'dsfa', label: 'DSFA', requirement: 'mandatory' as const, priority: 'medium' as const, estimatedEffort: 8, triggeredBy: [] },
{ documentType: 'vvt', label: 'VVT', requirement: 'mandatory' as const, priority: 'medium' as const, estimatedEffort: 2, triggeredBy: [] },
],
})
const result = enforcer.check('vvt', decision)
@@ -169,9 +159,9 @@ describe('ConstraintEnforcer', () => {
it('should note critical risk flags', () => {
const decision = makeDecision({
riskFlags: [
{ id: 'rf-1', severity: 'CRITICAL', title: 'Offene Art. 9 Verarbeitung', description: '', recommendation: 'DSFA durchfuehren' },
{ id: 'rf-2', severity: 'HIGH', title: 'Fehlende Verschluesselung', description: '', recommendation: 'TOM erstellen' },
{ id: 'rf-3', severity: 'LOW', title: 'Dokumentation unvollstaendig', description: '', recommendation: '' },
{ severity: 'CRITICAL', category: '', message: 'Offene Art. 9 Verarbeitung', recommendation: 'DSFA durchfuehren' },
{ severity: 'HIGH', category: '', message: 'Fehlende Verschluesselung', recommendation: 'TOM erstellen' },
{ severity: 'LOW', category: '', message: 'Dokumentation unvollstaendig', recommendation: '' },
],
})
const result = enforcer.check('vvt', decision)
@@ -266,9 +266,9 @@ function deriveTriggeredRegulations(
const regs = new Set<string>(['DSGVO'])
const triggers = scope.decision.triggeredHardTriggers || []
for (const t of triggers) {
if (t.rule.id.includes('ai_act') || t.rule.id.includes('ai-act')) regs.add('AI Act')
if (t.rule.id.includes('nis2') || t.rule.id.includes('NIS2')) regs.add('NIS2')
if (t.rule.id.includes('ttdsg') || t.rule.id.includes('TTDSG')) regs.add('TTDSG')
if (t.ruleId.includes('ai_act') || t.ruleId.includes('ai-act')) regs.add('AI Act')
if (t.ruleId.includes('nis2') || t.ruleId.includes('NIS2')) regs.add('NIS2')
if (t.ruleId.includes('ttdsg') || t.ruleId.includes('TTDSG')) regs.add('TTDSG')
}
return Array.from(regs)
}
@@ -11,7 +11,7 @@
*/
import type { ScopeDecision, ScopeDocumentType, ComplianceDepthLevel } from '../compliance-scope-types'
import { DOCUMENT_SCOPE_MATRIX, getDepthLevelNumeric } from '../compliance-scope-types'
import { DOCUMENT_SCOPE_MATRIX_CORE, getDepthLevelNumeric } from '../compliance-scope-types'
import type { ConstraintCheckResult, DraftContext } from './types'
export class ConstraintEnforcer {
@@ -57,9 +57,9 @@ export class ConstraintEnforcer {
// -----------------------------------------------------------------------
checkedRules.push('RULE-DOC-REQUIRED')
const isRequired = decision.requiredDocuments.some(
d => d.documentType === documentType && d.required
d => d.documentType === documentType && d.requirement === 'mandatory'
)
const scopeReq = DOCUMENT_SCOPE_MATRIX[documentType]?.[level]
const scopeReq = DOCUMENT_SCOPE_MATRIX_CORE[documentType]?.[level]
if (!isRequired && scopeReq && !scopeReq.required) {
// Nicht blockieren, aber warnen
@@ -96,7 +96,7 @@ export class ConstraintEnforcer {
checkedRules.push('RULE-DSFA-ENFORCEMENT')
if (documentType === 'dsfa') {
const dsfaRequired = decision.triggeredHardTriggers.some(
t => t.rule.dsfaRequired
t => t.requiresDSFA
)
if (!dsfaRequired && level !== 'L4') {
@@ -110,10 +110,10 @@ export class ConstraintEnforcer {
// Umgekehrt: Wenn DSFA verpflichtend und Typ != dsfa, ggf. hinweisen
if (documentType !== 'dsfa') {
const dsfaRequired = decision.triggeredHardTriggers.some(
t => t.rule.dsfaRequired
t => t.requiresDSFA
)
const dsfaInRequired = decision.requiredDocuments.some(
d => d.documentType === 'dsfa' && d.required
d => d.documentType === 'dsfa' && d.requirement === 'mandatory'
)
if (dsfaRequired && dsfaInRequired) {
@@ -136,7 +136,7 @@ export class ConstraintEnforcer {
if (criticalRisks.length > 0) {
adjustments.push(
`${criticalRisks.length} kritische/hohe Risiko-Flags erkannt. ` +
`Draft muss diese adressieren: ${criticalRisks.map(r => r.title).join(', ')}`
`Draft muss diese adressieren: ${criticalRisks.map(r => r.message).join(', ')}`
)
}
@@ -145,7 +145,7 @@ export class ConstraintEnforcer {
// -----------------------------------------------------------------------
checkedRules.push('RULE-HARD-TRIGGER-CONSISTENCY')
for (const trigger of decision.triggeredHardTriggers) {
const mandatoryDocs = trigger.rule.mandatoryDocuments
const mandatoryDocs = trigger.mandatoryDocuments
if (mandatoryDocs.includes(documentType)) {
// Gut - wir erstellen ein mandatory document
} else {
@@ -175,35 +175,28 @@ export class ConstraintEnforcer {
determinedLevel: context.decisions.level,
scores: context.decisions.scores,
triggeredHardTriggers: context.decisions.hardTriggers.map(t => ({
rule: {
id: t.id,
label: t.label,
description: '',
conditionField: '',
conditionOperator: 'EQUALS' as const,
conditionValue: null,
minimumLevel: context.decisions.level,
mandatoryDocuments: [],
dsfaRequired: false,
legalReference: t.legalReference,
},
ruleId: t.id,
category: '',
description: t.label,
legalReference: t.legalReference,
minimumLevel: context.decisions.level,
requiresDSFA: false,
mandatoryDocuments: [],
matchedValue: null,
explanation: '',
})),
requiredDocuments: context.decisions.requiredDocuments.map(d => ({
documentType: d.documentType,
label: d.documentType,
required: true,
depth: d.depth,
detailItems: d.detailItems,
estimatedEffort: '',
requirement: 'mandatory' as const,
priority: 'medium' as const,
estimatedEffort: 0,
triggeredBy: [],
})),
riskFlags: context.constraints.riskFlags.map(f => ({
id: `rf-${f.title}`,
severity: f.severity as 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL',
title: f.title,
description: '',
severity: f.severity,
category: '',
message: f.title,
recommendation: f.recommendation,
})),
gaps: [],
@@ -14,7 +14,7 @@ export interface DocumentRAGConfig {
query: string
}
export const DOCUMENT_RAG_CONFIG: Record<ScopeDocumentType, DocumentRAGConfig> = {
export const DOCUMENT_RAG_CONFIG: Partial<Record<ScopeDocumentType, DocumentRAGConfig>> = {
dsfa: {
collection: 'bp_dsfa_corpus',
query: 'Art. 35 DSGVO Datenschutz-Folgenabschaetzung DSFA Risikobewertung WP248 EDPB',