Every table or collection holding personal data has an entry in a personal-data map (a parsed document or in-schema annotations) that lists its personal-data fields and declares a purpose or lawful basis, and the entry matches the current schema. No AI engine is involved: the verdict is reproducible from the source alone.
Inputs
Non-test code files of the checkout (JavaScript, TypeScript, Python, PHP) analysed statically plus Prisma, SQL, Markdown, text, YAML, JSON, TOML and CSV files. Schema files (models, entities, migrations, Prisma, SQL) yield the stores as store blocks with their personal-data fields (strong: e-mail, phone, names, birth date, identifiers, address, financial, health, location; contextual: name, username, city, country, ip…). Map documents are found by name (pii, personal data, data map, inventory, catalog, ROPA, record of processing, privacy register, DPIA, GDPR) or by content (lawful basis, purpose of processing, data categories, art. 30) and parsed into entries: Markdown table rows (keyed by header), sections and list items with their key/value lines, YAML and JSON mapping nodes, CSV rows. In-schema annotations are read per field line and per block (/// @pii, @pii, pii:, @purpose, purpose:, lawful basis, @PersonalData, personal_data:, help_text with a purpose, @DataCategory, @Sensitive). A store's entry is the entry whose name (or a key/value under a heading, or a loose mention) names the store by its model, table or collection name, singular or plural. Paths listed in excludes are never analysed. Nothing of the checkout is executed.
Decision rule
Not applicable when no store with personal-data fields is declared. FAIL (HIGH, on the first store) when personal-data stores exist and no map document nor annotated schema block exists anywhere. A store that no entry names and whose block carries no annotation is MEDIUM (store-unmapped). For a mapped store: an entry listing no personal-data field, or omitting a strong field the schema declares (by name, by a normalised variant, or by a category word such as contact, identity, address, payment, health, location), is MEDIUM (map-outdated); an entry without a purpose or lawful basis (a purpose/basis key, or purpose, lawful basis, consent, contract, legitimate interest wording) is MEDIUM (purpose-undeclared). An annotated block with a strong field lacking a pii/purpose annotation and no block-level purpose is MEDIUM (map-outdated); an annotated block without a purpose or basis is MEDIUM (purpose-undeclared). MEDIUM blocks in Extended suites. PASS when every store is mapped with fields and purpose; the summary names the entry per store (file, line). Not judged by this check (declared): whether the purpose is adequate or the basis correct, semantic equivalence of field names beyond normalisation and the category words listed, a map kept outside the checkout, stores without personal-data vocabulary in their field names, and reference tables.
Type
deterministic
1.1.1: a callee whose root is a name that `Object.prototype` also carries (`build().toString()` tokenizes to the bare callee `toString`; also `constructor`, `valueOf`, `hasOwnProperty`) was looked up in the plain object that holds the imported names, so it resolved to the INHERITED FUNCTION instead of to nothing and the whole check died with "name.includes is not a function". Only a real binding counts now, and the name of a unit is always read as a string. Found on a 1 319-file checkout (2fa-available and session-expiry-rotation, 2026-09-24); the same line was in the 15 scripts that walk the call graph, so all fifteen ship the fix. (1.1.0: deterministic: the rule runs over the static analysis kit (tokens, function units, calls, imports, routes) with no AI engine; exact decisionRule and languages published; not-applicable with the reason when the checkout gives nothing to evaluate. Stores are schema STORE BLOCKS; the map is parsed into entries (markdown, YAML, JSON, CSV) or read from per-field and per-block annotations; the entry must list the schema's strong personal-data fields (name, normalised variant or category word) and a purpose or lawful basis. Not judged: adequacy of the purpose or basis, semantic equivalence beyond names and the listed categories, maps outside the checkout. (1.0.2: never analyses temporary files (*.tmp.*; spec.excludes follows AUDITOR_ANALYSIS_EXCLUDES). (1.0.1: never analyses test, spec, fixture and mock paths nor the auditor's own scripts (spec.excludes = AUDITOR_ANALYSIS_EXCLUDES); a fixture-looking secret (sk_test_, example, dummy) in real code is LOW, informative; every model call stays under the engine prompt cap (spec.promptMaxChars = AUDITOR_AI_PROMPT_MAX_CHARS): assess mode sends the candidate set in parts and aggregates the verdicts (a HIGH in any part is a FAIL, an unsatisfied part is a FAIL).)))
// stores = STORE BLOCKS of the schema files with personal-data fields; the map is PARSED: markdown table rows / sections / list items, YAML-JSON mapping nodes, CSV rows -> entries { name, line, values, text }
const FIELD_ANNOTATION = /\/\/\/\s*@?pii\b|@pii\b|@purpose\b|purpose\s*[:=]|lawful[_ ]?basis|@PersonalData/i; // per field line (or the comment above)
// no map nor annotation -> HIGH; store named by no entry and not annotated -> MEDIUM; entry missing a strong field of the schema (name, normalised variant or category word) -> MEDIUM; entry without purpose/lawful basis -> MEDIUM
The full script is disclosed on request in a read-only viewer (never published on GitHub); the attestation binds to this exact hash.