Install any skill in seconds. Free to start, no credit card required.
Get Started Free →KVKK and GDPR compliance patterns - consent management, right to erasure, breach notification, audit logging, cookie consent, and data classification.
.claude/skills/kvkk-compliance/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-04 | ✗→✓ | ▲ Improved | — | — |
| case-08 | ✗→✓ | ▲ Improved | — | — |
Practical patterns for Turkish KVKK (Law No. 6698) and EU GDPR data protection compliance.
| Aspect | KVKK (Turkey) | GDPR (EU) | |--------|---------------|-----------| | Authority | KVKK Board (Kisisel Verileri Koruma Kurumu) | National DPAs + EDPB | | Consent | Explicit, no pre-ticked boxes | Freely given, specific, informed | | Breach notification | "As soon as possible" to Board | 72 hours to DPA | | DPO requirement | VERBiS registration | Mandatory for public bodies + large-scale | | Right to erasure | Article 7 - withdrawal + deletion | Article 17 - "Right to be forgotten" | | Data transfer abroad | Board approval or adequate country | Adequacy decision, SCCs, or BCRs | | Fines | Up to ~2M TL per violation | Up to 20M EUR or 4% global turnover | | Legal bases | 5 in Article 5 + explicit consent | 6 in Article 6 + explicit consent |
typescriptenum DataCategory { PERSONAL = 'personal', // name, email, phone SPECIAL = 'special_category', // health, biometrics, religion ANONYMOUS = 'anonymous', // cannot identify anyone PSEUDONYMOUS = 'pseudonymous', // identifiable only with additional data } interface DataField { name: string category: DataCategory retentionDays: number requiresExplicitConsent: boolean encryptAtRest: boolean } const USER_DATA_FIELDS: DataField[] = [ { name: 'email', category: DataCategory.PERSONAL, retentionDays: 730, requiresExplicitConsent: false, encryptAtRest: false }, { name: 'healthRecords', category: DataCategory.SPECIAL, retentionDays: 365, requiresExplicitConsent: true, encryptAtRest: true }, { name: 'analyticsId', category: DataCategory.PSEUDONYMOUS, retentionDays: 365, requiresExplicitConsent: false, encryptAtRest: false }, ]
typescript// BAD: single "accept all" checkbox - violates both KVKK and GDPR // <input type="checkbox" /> I accept everything // GOOD: granular, purpose-specific consent interface ConsentRecord { userId: string; purpose: string; granted: boolean timestamp: Date; ipAddress: string; version: string } const CONSENT_PURPOSES = [ { id: 'essential', label: 'Service operation', required: true }, { id: 'analytics', label: 'Usage analytics', required: false }, { id: 'marketing', label: 'Marketing emails', required: false }, { id: 'third_party', label: 'Third-party sharing', required: false }, ] as const async function recordConsent(userId: string, purposeId: string, granted: boolean, meta: { ip: string; userAgent: string }): Promise<ConsentRecord> { // Always INSERT, never UPDATE - full audit trail return db.consentRecords.create({ data: { userId, purpose: purposeId, granted, timestamp: new Date(), ipAddress: meta.ip, version: CURRENT_CONSENT_VERSION }, }) } async function hasActiveConsent(userId: string, purposeId: string): Promise<boolean> { const latest = await db.consentRecords.findFirst({ where: { userId, purpose: purposeId }, orderBy: { timestamp: 'desc' }, }) return latest?.granted === true }
typescriptconst GRACE_PERIOD_DAYS = 30 async function requestAccountDeletion(userId: string): Promise<void> { await db.users.update({ where: { id: userId }, data: { status: 'deletion_pending', deactivatedAt: new Date() } }) await db.deletionRequests.create({ data: { userId, requestedAt: new Date(), gracePeriodEndsAt: new Date(Date.now() + GRACE_PERIOD_DAYS * 86400000), status: 'pending' }, }) } // Scheduled job: purge after grace period async function purgeExpiredAccounts(): Promise<void> { const expired = await db.deletionRequests.findMany({ where: { status: 'pending', gracePeriodEndsAt: { lte: new Date() } }, }) for (const req of expired) { await db.$transaction(async (tx) => { await tx.users.update({ where: { id: req.userId }, data: { email: `deleted-${req.userId}@anon.local`, fullName: 'Deleted User', phone: null, address: null, status: 'deleted' }, }) // Anonymize consent records (retain for legal proof, not delete) await tx.consentRecords.updateMany({ where: { userId: req.userId }, data: { userId: `deleted-${req.userId}`, ipAddress: 'REDACTED' } }) await tx.orders.updateMany({ where: { userId: req.userId }, data: { userEmail: null, userName: 'Deleted User' } }) await tx.deletionRequests.update({ where: { id: req.id }, data: { status: 'completed', completedAt: new Date() } }) }) } }
typescriptconst COOKIE_CATEGORIES = { necessary: { name: 'Strictly Necessary', required: true }, functional: { name: 'Functional', required: false }, analytics: { name: 'Analytics', required: false }, marketing: { name: 'Marketing', required: false }, } as const type CookiePrefs = Record<keyof typeof COOKIE_CATEGORIES, boolean> // BAD: set tracking cookies before consent // document.cookie = '_ga=GA1.2.123; max-age=63072000' // GOOD: only after explicit consent per category function applyCookiePreferences(prefs: CookiePrefs): void { initSessionCookies() // always allowed prefs.analytics ? initGoogleAnalytics() : removeAnalyticsCookies() prefs.marketing ? initMarketingPixels() : removeMarketingCookies() document.cookie = `cookie_consent=${JSON.stringify(prefs)}; path=/; max-age=${365 * 86400}; SameSite=Lax; Secure` }
typescriptconst NOTIFICATION_DEADLINE_HOURS = 72 async function reportBreach(breach: { detectedAt: Date; severity: string; affectedCount: number; dataTypes: string[]; description: string }): Promise<void> { const record = await db.dataBreaches.create({ data: { ...breach, notifiedAuthorityAt: null, notifiedUsersAt: null } }) const deadline = new Date(breach.detectedAt.getTime() + NOTIFICATION_DEADLINE_HOURS * 3600000) // GDPR Art 33: notify authority within 72h | KVKK: "as soon as possible" await notifyDataProtectionAuthority({ breachId: record.id, deadline, ...breach }) // GDPR Art 34: high-risk breaches require user notification if (breach.severity === 'high' || breach.severity === 'critical') { await notifyAffectedUsers(record.id) } }
typescriptinterface AuditLogEntry { actorId: string; actorRole: string action: 'read' | 'create' | 'update' | 'delete' | 'export' resourceType: string; resourceId: string fieldsAccessed: string[]; ipAddress: string justification?: string } async function logDataAccess(entry: AuditLogEntry): Promise<void> { // Append-only table - no UPDATE or DELETE allowed on audit_logs await db.$executeRaw` INSERT INTO audit_logs (actor_id, actor_role, action, resource_type, resource_id, fields_accessed, ip_address, justification, timestamp) VALUES (${entry.actorId}, ${entry.actorRole}, ${entry.action}, ${entry.resourceType}, ${entry.resourceId}, ${JSON.stringify(entry.fieldsAccessed)}, ${entry.ipAddress}, ${entry.justification || null}, NOW()) ` } // Express middleware: auto-log every personal data access function auditMiddleware(resourceType: string) { return (req: Request, res: Response, next: NextFunction) => { const origJson = res.json.bind(res) res.json = (body: unknown) => { const actionMap: Record<string, string> = { GET: 'read', POST: 'create', PUT: 'update', PATCH: 'update', DELETE: 'delete' } logDataAccess({ actorId: req.user?.id || 'anon', actorRole: req.user?.role || 'unknown', action: actionMap[req.method] || 'read', resourceType, resourceId: req.params.id || 'list', fieldsAccessed: Object.keys((body as Record<string, unknown>) || {}), ipAddress: req.ip || '', }).catch(console.error) return origJson(body) } next() } }
Controller identity [ ] Company name, address, contact, VERBiS number
Data collected [ ] Full list of categories with examples
Legal basis [ ] Purpose + legal basis for each activity
Retention periods [ ] How long each type is kept
Data subject rights [ ] Access, rectification, erasure, portability, objection
Consent withdrawal [ ] Clear opt-out instructions
Cookie policy [ ] Categories, purposes, opt-out
International transfer [ ] Countries, safeguards (SCCs, adequacy)
Third parties [ ] Processors and sub-processors
Automated decisions [ ] Profiling details, right to object
Breach procedure [ ] Notification timeline and method
DPO contact [ ] Data Protection Officer detailsEvery third-party processor needs a Data Processing Agreement with these clauses:
Subject & duration What data, how long, why
Processor obligations Process only on documented instructions
Sub-processors Prior written auth, flow-down obligations
Security measures Encryption, access controls, incident response
Audit rights Controller can inspect compliance
Data return/deletion Return or destroy data at contract end
Breach notification Notify controller without undue delay
International transfer SCCs if data leaves TR/EUtypescript// BAD: blanket consent - violates KVKK Art 3, GDPR Art 7 function BadForm() { return ( <form> <label><input type="checkbox" /> I agree to privacy policy, marketing, tracking, and sharing.</label> <button>Sign Up</button> </form> ) } // GOOD: granular opt-in per purpose function GoodForm() { return ( <form> <fieldset> <legend>Data Processing Consent</legend> <label><input type="checkbox" checked disabled /> Account operation (required)</label> <label><input type="checkbox" name="analytics" /> Usage analytics</label> <label><input type="checkbox" name="marketing" /> Marketing emails</label> <label><input type="checkbox" name="third_party" /> Partner sharing (<a href="/privacy">details</a>)</label> </fieldset> <p>Change preferences anytime in account settings.</p> <button>Sign Up</button> </form> ) }
Core rule: Compliance is not a one-time feature. Build data protection into every flow, log every access, and assume regulators will ask "show me the evidence."
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.