Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .claude/schemas/company-details.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"enum": ["not_designated", "designated", "likely", "unknown"],
"default": "unknown"
},
"processes_children_data": { "type": "boolean", "default": false },
"processes_sensitive_data": { "type": "boolean", "default": false },
"processes_children_data": { "type": "boolean", "description": "Leave out if unknown" },
"processes_sensitive_data": { "type": "boolean", "description": "Leave out if unknown" },
"dpo": {
"type": ["object", "null"],
"properties": {
Expand All @@ -36,6 +36,7 @@
"appointed_at": { "type": "string", "format": "date" }
}
},
"unknowns": { "type": "array", "items": { "type": "string" }, "description": "Fields nobody could answer at onboarding" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
},
Expand Down
7 changes: 4 additions & 3 deletions .claude/schemas/data-map.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"purpose": { "type": "string" },
"lawful_basis": {
"type": "string",
"enum": ["consent", "contract", "legal_obligation", "vital_interests", "public_task", "legitimate_interests", "dpdp_section_7"]
"enum": ["consent", "contract", "legal_obligation", "vital_interests", "public_task", "legitimate_interests", "dpdp_section_7", "none_identified"]
},
"source": { "type": "string", "description": "Where data is collected from" },
"destination": { "type": "string", "description": "Where data flows to" },
Expand All @@ -29,14 +29,15 @@
"items": { "type": "string" },
"description": "Third-party processors involved"
},
"cross_border": { "type": "boolean", "default": false },
"cross_border": { "type": "boolean", "description": "Leave out if unknown" },
"transfer_mechanism": {
"type": ["string", "null"],
"enum": ["adequacy", "scc", "bcr", "dpf", "derogation", null]
},
"retention_period": { "type": "string" },
"encryption": { "type": "boolean" },
"dpa_in_place": { "type": "boolean", "description": "Data Processing Agreement with processors" }
"dpa_in_place": { "type": "boolean", "description": "Data Processing Agreement with processors" },
"notes": { "type": "string", "description": "What the fields above can't hold, such as the DPDP ground or why a value is uncertain" }
},
"additionalProperties": false
}
Expand Down
23 changes: 19 additions & 4 deletions .claude/scripts/tests/guardrails.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, rmSync, cpSync, existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, dirname } from 'node:path';
import { join, dirname, relative, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadLayout, matchRule, checkFile, validate, loadFindings, workspaceRoot, frontmatterErrors } from '../lib.mjs';

Expand All @@ -28,13 +28,20 @@ function workspace() {
return dir;
}

// A copy of the repo's data and package.json, for scripts that read the working directory.
// The tracked demo only. A real audit in this checkout is gitignored and must not change what these tests count.
const DEMO_ENTRIES = ['templates', 'sample-co', '2026-10-19-sample-co-baseline'];
const isDemoData = src => {
const [, entry] = relative(repo, src).split(sep);
return !entry || DEMO_ENTRIES.includes(entry);
};

// A copy of the repo's demo data and package.json, for scripts that read the working directory.
function dataCopy() {
const dir = mkdtempSync(join(tmpdir(), 'vigil-'));
copyFileSync(join(repo, 'package.json'), join(dir, 'package.json'));
mkdirSync(join(dir, 'kpis'));
cpSync(join(repo, 'assessments'), join(dir, 'assessments'), { recursive: true });
cpSync(join(repo, 'company-profile'), join(dir, 'company-profile'), { recursive: true });
cpSync(join(repo, 'assessments'), join(dir, 'assessments'), { recursive: true, filter: isDemoData });
cpSync(join(repo, 'company-profile'), join(dir, 'company-profile'), { recursive: true, filter: isDemoData });
return dir;
}

Expand Down Expand Up @@ -193,6 +200,14 @@ test('exports filter findings by company', () => {
assert.equal(loadFindings('nobody', repo).length, 0);
});

test('test data leaves out local audits', () => {
assert.equal(isDemoData(join(repo, 'assessments')), true);
assert.equal(isDemoData(join(repo, 'assessments/2026-10-19-sample-co-baseline/report.json')), true);
assert.equal(isDemoData(join(repo, 'company-profile/sample-co/details.json')), true);
assert.equal(isDemoData(join(repo, 'assessments/2026-11-05-acme-demo-full/report.json')), false);
assert.equal(isDemoData(join(repo, 'company-profile/acme-demo/details.json')), false);
});

test('a second audit replaces the first in exports and KPIs', () => {
const dir = dataCopy();
secondAudit(dir);
Expand Down
33 changes: 19 additions & 14 deletions .claude/skills/audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,41 @@ Run a complete privacy compliance audit covering both DPDP and GDPR. Orchestrate
### Phase 2: DPDP assessment

3. **Consent audit** (`/dpdp-consent`): review consent collection, notices, and dark patterns.
4. **Rights mechanisms** (`/dpdp-rights`): verify grievance and rights request mechanisms.
5. **DPIA check** (`/dpdp-dpia`): determine if DPIA is needed; conduct if required.
6. **Breach readiness** (`/dpdp-breach`): verify breach detection and notification capability.
4. **Rights mechanisms** (`/dpdp-rights audit`): verify grievance and rights request mechanisms.
5. **DPIA check** (`/dpdp-dpia screen`): determine if a DPIA is needed. If it is, write a `draft` with `/dpdp-dpia full`.
6. **Breach readiness** (`/dpdp-breach readiness`): compare the runbook with the required steps.
7. **Sector check**: if `details.json` lists `RBI`, `SEBI` or `IRDAI`, or the company is financial and its regulated status is unknown, check the matching rows of `/dpdp-catalog` under "Related Indian instruments". File a finding under the closest DPDP clause, name the instrument in `cross_reference`, and say whether it applies or only might.

### Phase 3: GDPR assessment

7. **Code audit** (`/gdpr-audit`): audit code and systems against GDPR requirements.
8. **Document review** (`/gdpr-draft`): check privacy policy, DPAs, consent notices for completeness.
9. **Transfer review** (`/gdpr-audit transfers`): verify cross-border transfer mechanisms.
8. **Code audit** (`/gdpr-audit`): audit code and systems against GDPR requirements.
9. **Document review** (`/gdpr-draft review`): check privacy policy, DPAs, consent notices for completeness.
10. **Transfer review** (`/gdpr-audit transfers`): verify cross-border transfer mechanisms.

### Phase 4: Synthesis

10. **Posture dashboard** (`/posture`): generate unified compliance posture.
11. **Report**: compile all findings into a single audit report sorted by severity.
11. **Posture dashboard** (`/posture`): generate unified compliance posture.
12. **Report**: compile all findings into a single audit report sorted by severity.

If a step can't run, follow "When something is missing or blocked" in `AGENTS.md`, finish the rest, and name the skipped step in `summary.md`. If the evidence answers an unknown from onboarding, such as under-18 users in the code, update `details.json`.

## Rules for findings

- Before 13 May 2027 most DPDP obligations are not yet in force. Report DPDP gaps as readiness findings and say so in the issue text; check `/dpdp-catalog` for each clause.
- Before 13 May 2027 most DPDP obligations are not yet in force. Report DPDP gaps as readiness findings and say so in the issue text of any finding that cites DPDP, whether as `framework` or in `cross_reference`; check `/dpdp-catalog` for each clause.
- Severity measures the risk, not the date. A readiness gap can still be critical, and the issue text carries the timing.
- If the only evidence is an absence, write "not found in the reviewed material" and rate it medium at most. A DPIA or RoPA rarely lives in a code repository.
- Number findings `F-0001` upwards within the report.
- Every finding cites a clause, and a `cross_reference` when the same gap breaches the other framework.
- Set `found_at`. Leave `owner` and `due_date` for `/remediate` unless the user gave them.
- Reference code by `file` and `line`. `file` is relative to the root of the audited codebase, never absolute, so SARIF maps onto that repository. Leave it out for a gap with no file, such as a missing contract. Never copy personal data into a finding.
- Count applicable and examined controls (sections, rules, articles), record them in `coverage`, and say in `summary.md` how you counted. The denominator is your own count, so do not present it as comparable across runs.
- Count applicable and examined controls and record them in `coverage`. One control is one section, rule or article at its top-level number, so s.8(5) and s.8(7) count once as s.8. List them in `summary.md`, with the ones you did not examine. The denominator is your own count, so do not present it as comparable across runs.

## Output

Assessment report saved to `assessments/{assessment_id}/` (use `YYYY-MM-DD-{company_id}-{scope}` as the id):
- `report.json`: `assessment_id`, `company_id`, `scope`, `started_at`, `completed_at`, `coverage` and `findings`, per `.claude/schemas/report.schema.json`. Also record `target` (repo and commit audited), `model`, `vigil_version` and `law_as_at` (the catalogs' "Last verified" date) so someone can check the run later
- `posture.json`: compliance posture snapshot
- `data-map.json`: personal data inventory
Assessment report saved to `assessments/{assessment_id}/`. Use `YYYY-MM-DD-{company_id}-{scope}` as the id, where `{scope}` is `full`, `dpdp` or `gdpr` to match the command. That is only a label: the `scope` field in `report.json` is the list of frameworks, such as `["DPDP", "GDPR"]`.
- `report.json`: `assessment_id`, `company_id`, `scope`, `started_at`, `completed_at`, `coverage` and `findings`, per `.claude/schemas/report.schema.json`. Also record `target` (repo and commit audited), `model`, `vigil_version` and `law_as_at` (the older "Last verified" date of the catalogs used) so someone can check the run later
- `posture.json`: compliance posture snapshot. Also write it to `company-profile/{id}/posture.json`, the company's current view
- `data-map.json`: personal data inventory. Also write it to `company-profile/{id}/data-map.json`
- `summary.md`: human-readable executive summary

## Commands
Expand Down
16 changes: 16 additions & 0 deletions .claude/skills/dpdp-breach/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ DPDP has no public-notice substitute for individual intimation (that is a GDPR A

Output: post-incident report with findings and corrective actions, and a `reviewed` line in the breach log.

### 7. Readiness check

For `/audit` or a drill, with no live incident. Compare the runbook with the steps below and raise a finding for each one missing. Write nothing to the breach log.

| Step the runbook needs | Clause | Applies |
|---|---|---|
| Personal data assessment, recording `aware_at` | s.8(6), Rule 7 | 13 May 2027 |
| CERT-In within 6 hours | CERT-In Directions 2022 | Today |
| RBI within 6 hours, if a regulated entity | RBI Cybersecurity Directions 2026 | Today |
| Board without delay, with an initial description | Rule 7(2)(a) | 13 May 2027 |
| Board within 72 hours of becoming aware, with the detailed report | Rule 7(2)(b) | 13 May 2027 |
| Each affected Data Principal without delay | Rule 7(1) | 13 May 2027 |
| EU or UK supervisory authority within 72 hours, and affected people if the risk is high | GDPR Art. 33(1), 34(1) | Today |
| Logs kept for one year, and ICT logs for 180 days in India | Rule 6(1)(e); CERT-In Directions 2022 | 13 May 2027; today |

## Guardrails

- Never apply a materiality threshold to DPDP intimation. Every breach is intimated.
Expand All @@ -114,3 +129,4 @@ Invoke the skill directly, or describe the task in plain English and Claude pick
| `/dpdp-breach notify-board` | Prepare the Board intimation and 72-hour report |
| `/dpdp-breach notify-principals` | Prepare the Data Principal intimation |
| `/dpdp-breach review` | Conduct post-incident review |
| `/dpdp-breach readiness` | Check a runbook against the required steps, with no live incident |
20 changes: 20 additions & 0 deletions .claude/skills/dpdp-catalog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,25 @@ Pending: in January 2026 MeitY consulted on compressing the 18-month phase to 12
| 22 | Appeal to the Appellate Tribunal | 13 May 2027 |
| 23 | Calling for information (Seventh Schedule) | 13 May 2027 |

### Rule 3 and Rule 6 sub-clauses

| Clause | Requires |
|---|---|
| Rule 3(a) | Notice presented and understandable independently of any other information |
| Rule 3(b) | Clear and plain language: itemised description of the data (i); specified purpose and the goods, services or uses it enables (ii) |
| Rule 3(c) | Link and other means to withdraw consent with comparable ease (i), exercise rights (ii), complain to the Board (iii) |
| Rule 6(1)(a) | Data security measures: encryption, obfuscation, masking, virtual tokens |
| Rule 6(1)(b) | Control of access to computer resources |
| Rule 6(1)(c) | Visibility on access to personal data: logs, monitoring and review, to detect unauthorised access |
| Rule 6(1)(d) | Continued processing after a compromise, such as backups |
| Rule 6(1)(e) | Keep those logs and personal data for one year, unless another law requires otherwise |
| Rule 6(1)(f) | Security safeguards provision in the Data Processor contract |
| Rule 6(1)(g) | Technical and organisational measures to make the safeguards effective |

Personal data sitting in an application log is a masking gap under 6(1)(a). A missing access log is a gap under 6(1)(c). Bundled consent can breach 3(a) (not independent of the terms) and 3(b) (no specific purpose).

Sub-clause letters were checked on 5 November 2026 against a reproduction of the notified text at dpdpa.com, because MeitY's PDF refused the fetch. Confirm them against the Gazette before relying on them in a filing.

### Related Indian instruments

| Instrument | Issuer | Relevance |
Expand All @@ -94,6 +113,7 @@ Pending: in January 2026 MeitY consulted on compressing the 18-month phase to 12
| CERT-In Directions, 28 Apr 2022 (s.70B(6)) | CERT-In | Report cyber incidents within 6 hours; keep ICT logs for 180 days in India |
| RBI Cybersecurity, Technology: Risk, Resilience and Assurance Framework Directions, 2026 (31 Jul 2026) | RBI | Entity-specific directions for banks and NBFCs; replace the 2016 framework and the 2023 IT Governance Master Direction; 6-hour incident reporting to RBI |
| RBI Storage of Payment System Data circular (6 Apr 2018) | RBI | Payment data stored only in India |
| Reserve Bank of India (Digital Lending) Directions, 2025 (RBI/2025-26/36, 8 May 2025) | RBI | Banks, co-operative banks, NBFCs and all-India financial institutions, and through them their lending service providers. Need-based data collection with prior explicit consent and an audit trail (para 12). Service providers keep only basic data; all data on servers in India, with data processed abroad back within 24 hours (para 13). Public privacy policy naming third parties (para 14). Paragraph numbers are from the May 2025 text, so check RBI's current Master Directions before citing one |
| SEBI Cybersecurity and Cyber Resilience Framework (CSCRF), Aug 2024 | SEBI | Market intermediaries |
| IRDAI Information and Cyber Security Guidelines, 2023 | IRDAI | Insurers and intermediaries |
| Guidelines for Prevention and Regulation of Dark Patterns, 2023 | Central Consumer Protection Authority | 13 listed dark patterns in consumer-facing interfaces (Consumer Protection Act 2019) |
Expand Down
13 changes: 13 additions & 0 deletions .claude/skills/dpdp-rights/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ Track every request against its `due_at`:

Flag overdue requests. Report SLA compliance to the DPO and the Board of Directors quarterly.

### 7. Mechanism audit

For `/audit` or a readiness check, with no live request. Look for each route in the code, docs and published site, credit what exists, and raise a finding for what doesn't. A working access endpoint is not a gap because erasure is missing. Write nothing to the rights log.

| Check | DPDP | GDPR |
|---|---|---|
| Access route, including who the data was shared with | s.11(1), Rule 14(1) | Art. 15(1)(c) |
| Correction and erasure routes | s.12, Rule 14(1) | Art. 16, 17 |
| Erasure reaches processors | s.8(7) | Art. 19 |
| Grievance route and a published response period | s.13(1), Rule 14(3) | Art. 12(2), 13(2)(d) |
| Nomination | s.14, Rule 14(4) | None |

## Guardrails

- Never refuse a valid request without recording the legal basis in `refusal_basis`.
Expand All @@ -104,3 +116,4 @@ Invoke the skill directly, or describe the task in plain English and Claude pick
| `/dpdp-rights erase` | Process an erasure request |
| `/dpdp-rights grievance` | Handle a grievance |
| `/dpdp-rights sla` | Check SLA compliance for open requests |
| `/dpdp-rights audit` | Check which rights mechanisms exist, with no live request |
6 changes: 3 additions & 3 deletions .claude/skills/gdpr-audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ For special category data, verify an Art. 9(2) condition is met in addition.

### Step 4: Security and technical measures

Evaluate against Art. 25 (Privacy by Design) and Art. 32 (Security):
- Encryption at rest and in transit (Art. 32(1)(a))
Evaluate against Art. 25 (Privacy by Design) and Art. 32 (Security), citing the Art. 32(1) limb that applies:
- Encryption at rest and in transit, and masking of personal data in logs (Art. 32(1)(a))
- Pseudonymisation where feasible (Art. 25(1))
- Access controls with least privilege
- Access controls with least privilege, and credentials kept out of source (Art. 32(1)(b))
- Audit logging for accountability (Art. 5(2))
- Breach detection capability (Art. 33-34)
- Input validation and injection prevention
Expand Down
2 changes: 2 additions & 0 deletions .claude/skills/gdpr-catalog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ description: "Reference registry for EU GDPR, UK GDPR and the Data (Use and Acce

Registry of EU GDPR and UK GDPR instruments, key articles, penalty framework, and recent regulatory developments. Reference for all GDPR skills.

Last verified: 2 November 2026. Before relying on a status, check EUR-Lex, the Commission's adequacy page and legislation.gov.uk.

## Instrument Registry

### Primary legislation
Expand Down
7 changes: 6 additions & 1 deletion .claude/skills/gdpr-data-flows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ For each system or service:

## Output

Data flow map: a structured representation showing each flow with its lawful basis, safeguards, and gaps. Findings for gaps follow `.claude/schemas/finding.schema.json`.
Data flow map: a structured representation showing each flow with its lawful basis, safeguards, and gaps. Save it to `company-profile/{id}/data-map.json`, and during an `/audit` also to `assessments/{assessment_id}/data-map.json`. Findings for gaps follow `.claude/schemas/finding.schema.json`.

Recording a flow:
- `lawful_basis` takes one value. For a flow under both frameworks, record the GDPR basis and put the DPDP ground (consent under s.6 or a s.7 use) in `notes`, because DPDP has no contract or legitimate-interests ground.
- If no valid basis is evidenced, use `none_identified` and raise a finding.
- If `cross_border`, `encryption` or `dpa_in_place` is unknown, leave it out. Write `retention_period` as `"Unknown"` or `"None defined"`; they mean different things.

## Commands

Expand Down
5 changes: 5 additions & 0 deletions .claude/skills/gdpr-draft/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Draft GDPR-compliant documents: privacy policies, Data Processing Agreements, co
- Countries data is transferred to
- Retention periods

## Reviewing existing documents

`/gdpr-draft review` checks what exists against the required contents below and drafts nothing unless asked. For each document (privacy notice, DPAs, consent notices, retention policy, rights procedure), record present, incomplete or not found. "Not found" means not found in the reviewed material, because a code repository rarely holds contracts. Findings follow `.claude/schemas/finding.schema.json`.

## Document types

### Privacy Policy / Notice
Expand Down Expand Up @@ -97,6 +101,7 @@ Invoke the skill directly, or describe the task in plain English and Claude pick

| Run | Action |
|---|---|
| `/gdpr-draft review` | Check existing documents for completeness |
| `/gdpr-draft privacy-policy` | Draft a GDPR-compliant privacy policy |
| `/gdpr-draft dpa` | Draft a Data Processing Agreement |
| `/gdpr-draft consent-banner` | Draft a consent notice / cookie banner |
Expand Down
Loading
Loading