Repository navigation
GDPR page claims masked JSONL holds no identifiable subjects and that no person's data is recoverable from the model; its YAML example is rejected (CR-A11-001) #101
Description
Wave 0 · theme D0-6: Personal data: audit and masking coverage · highest severity Critical · 30 finding(s) · effort small
Summary
A deployer trusts the page, treats masked JSONL as anonymised, and records 'no personal data recoverable' in a DPIA or DSAR reply. In fact names, addresses, birth dates and diagnoses remain in the training data. Copying the documented YAML makes the run fail at config load.
Primary finding
CR-A11-001 — GDPR page claims masked JSONL holds no identifiable subjects and that no person's data is recoverable from the model; its YAML example is rejected
severity Critical · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
What happens. gdpr.md:38,78,85 (EN; TR mirror :38) state that masking removes identifiable subjects and that no specific person's data is recoverable from the model, 'confirmed' by the audit report. Masking is a fixed regex set (email/phone/IBAN/card/national IDs; _pii_regex.py:37-80). Names, addresses, DOB and health data pass through, and the phone pattern can leave digits. No ForgeLM component measures memorisation, so the audit report cannot confirm non-recoverability. The ingestion.pii_mask YAML at gdpr.md:30-36 fails validation (ForgeConfig extra='forbid', config.py:1604-1605). The page's other examples also do not match the code: the sample reverse-pii output at :75, and international_transfers.enabled at :125, which matches nothing in forgelm/.
Where.
docs/usermanuals/en/compliance/gdpr.md:30-38docs/usermanuals/en/compliance/gdpr.md:94docs/usermanuals/en/compliance/gdpr.md:85docs/usermanuals/en/compliance/gdpr.md:125docs/usermanuals/tr/compliance/gdpr.md:38forgelm/data_audit/_pii_regex.py:37-80forgelm/config.py:1604-1605docs/usermanuals/tr/compliance/gdpr.md:71-76docs/usermanuals/tr/compliance/gdpr.md:125forgelm/cli/subcommands/_reverse_pii.py:813-818
Evidence.
- d4/ dynamic run; _pii_regex.py catalogue.
Recommended fix. Replace the absolute claims with what the regex set covers, as pii-masking.md:23,52 already does. Point to --pii-ml for names. Replace the YAML with the forgelm ingest --pii-mask CLI form and fix the sample output. Remove the nonexistent international_transfers key. Mirror the changes in TR.
Other findings with the same root cause
These close with the same fix; update them in the same pull request.
-
CR-A17a-007 (code, Medium) — Schema-drift guard ignores unknown top-level blocks, so three fabricated config blocks pass
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN C31-DOC-SCHEMA-PROSE — docs/roadmap/phase-16-trust-surface-hardening.md:773,785-786
Failure scenario. A reader copies the documented 'chat:' or 'output:' block into a config and gets a Pydantic extra_forbidden error. The guard that promises to prevent this stays green.
Where.tools/check_usermanual_schema_drift.py:304(skip branch; check_snippet spans :286-307, not :300-320),docs/usermanuals/en/compliance/gdpr.md:31,docs/usermanuals/en/deployment/chat.md:41,docs/usermanuals/en/operations/gpu-cost.md:39
Recommended fix. Flag unknown top-level keys in fragments that are not allowlisted non-ForgeLM YAML, and correct or remove the three fabricated blocks in both languages. -
TECH-T11-002 (technology, Next release) — Regulation and standards: GDPR, ISO/IEC 27001, SOC 2, ISO/IEC 42001, EDPB, EU Cyber Resilience Act: GDPR (Regulation (EU) 2016/679) as ForgeLM's legal base: Art. 5(1)(e), 9, 10, 15, 17(3)(b), 87 cita…
severity Next release
Item. GDPR (Regulation (EU) 2016/679) as ForgeLM's legal base: Art. 5(1)(e), 9, 10, 15, 17(3)(b), 87 citations, the retention block and the user-manual GDPR page's config claims · verdict:incorrect-claim· urgency: Next release
State in ForgeLM. Cited across code and docs. purge is labelled Art. 17 and reverse-pii Art. 15 (forgelm/cli/_parser.py:1069-1188). RetentionConfig (forgelm/config.py:1526-1532) says its defaults are 'chosen to be compliant out of the box', and config.py:1556 reads 'Set to 0 to retain indefinitely (Article 17(3)(b) defence)'. The audit log is called an Art. 17(3)(b) record without qualification in forgelm/cli/_par…
State of the ecosystem on 2026-10-01. Regulation (EU) 2016/679 (OJ L 119, 4.5.2016, p. 1; CELEX 32016R0679; ELI http://data.europa.eu/eli/reg/2016/679/oj) is in force and has applied since 25 May 2018. A CELLAR SPARQL query for acts amending 32016R0679 returned nothing on 2026-10-01. The same query pattern returns 32025R0327 for the CRA, so the empty result means no amendment, not a broken query. The Digital Omnibus proposal COM(2025…
Recommendation. Change _types.py:26 and data_audit.md:154 to cite GDPR Art. 87 (national identification number) or ENISA only. Change pii-ml.md:8 (EN+TR) to Art. 5(1)(c)/Art. 4(1). Reword config.py:1556 to something like 'retain until reviewed; name the Union/Member State retention obligation you rely on', and drop 'compliant out of the box' at config.py:1532. In _parser.py:1128, gdpr_erasure.md:23/:61, gdpr-era…
Sources. -
DOC-D3B02-001 (docs, Critical) — docs/usermanuals/en/compliance/gdpr.md: false claim — "By the time data lands in JSONL, identifiable subjects are removed." / "Because PII was masked at ingest, no specific person's data is recoverab…
severity Critical
Where.docs/usermanuals/en/compliance/gdpr.md:38,78 (Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:38,78) · type: false claim
The document says. "By the time data lands in JSONL, identifiable subjects are removed." / "Because PII was masked at ingest, no specific person's data is recoverable from the model. The audit report confirms this."
What the code does.--pii-maskruns regex detectors for 8 identifier types only: credit_card, iban, tr_id, de_id, fr_ssn, us_ssn, email and phone. Names, postal addresses, dates of birth and any free-text identifier stay in the JSONL. Neither reverse-pii nor data_audit_report.json inspects the trained model. A reverse-pii result with no matches only shows that the literal query string does not appear in the scanne…
Code references.forgelm/data_audit/_types.py:29-46,forgelm/data_audit/_pii_regex.py:37,forgelm/data_audit/_pii_regex.py:265-300,forgelm/cli/subcommands/_reverse_pii.py:709-791
Proposed correction. "Masking at ingest removes the identifier types ForgeLM detects (emails, phone numbers, card numbers, IBANs, TR/DE/FR/US national IDs). Names, addresses and other free-text identifiers are not masked;forgelm audit --pii-mladds NER-based detection of names, organisations and locations. A reverse-PII scan with no matches only shows that the quer…
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-004 (docs, High) — docs/design/gdpr_erasure.md: misleading — including (3)(b) compliance with a legal obligation of EU law, which is the entire reason the audit log itself is preserved ... the audit log is the legal-ob…
severity High
Where.docs/design/gdpr_erasure.md:48,50 · type: misleading
The document says. including (3)(b) compliance with a legal obligation of EU law, which is the entire reason the audit log itself is preserved ... the audit log is the legal-obligation record under Art. 17(3)(b)
What the code does. Art. 17(3)(b) covers processing necessary for a legal obligation under 'Union or Member State law to which the controller is subject', and only to that extent. No EU-law obligation attaches to a training audit log merely because ForgeLM produces it. The controller must identify the specific obligation, such as AI Act Art. 19/26(6) for high-risk systems, which is not yet applicable.
Proposed correction. 'Article 17(3)(b) (legal obligation under Union or Member State law) and 17(3)(e) (legal claims) can justify keeping audit entries only to the extent a specific obligation binding the controller requires it; the controller must document that obligation. The tool therefore records erasures as new events and leaves the retention decision to the cont… -
DOC-BLEGAL-005 (docs, High) — docs/guides/gdpr_erasure.md: misleading — Article 17(3)(b) preserves audit / accounting records as a legal-obligation defence; ... audit logs are append-only Article 17(3)(b) records
severity High
Where.docs/guides/gdpr_erasure.md:23(also :61) (Turkish mirror:docs/guides/gdpr_erasure-tr.md:23,61) · type: misleading
The document says. Article 17(3)(b) preserves audit / accounting records as a legal-obligation defence; ... audit logs are append-only Article 17(3)(b) records
What the code does. GDPR Art. 17(3)(b) disapplies erasure only 'to the extent that processing is necessary ... for compliance with a legal obligation which requires processing by Union or Member State law to which the controller is subject'. It does not preserve audit records as a class. The controller must name a concrete obligation, and the guide names none. Candidate AI Act obligations are Art. 19(1) and Art. 26(…
Proposed correction. Replace with: 'Delete entries from the audit log itself. The tool records the erasure as a new event. Whether audit entries that reference the subject may be kept is the controller's decision: Article 17(3)(b) applies only to the extent a specific Union or Member State legal obligation requires the processing (for example AI Act Art. 19/26(6) log…
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-041 (docs, High) — site/compliance.html: false claim — "forgelm reverse-pii ... reports every row of every split where that subject appears, satisfying the access-request side of the GDPR's data-subject rights."
severity High
Where.site/compliance.html:205(compliance.gdpr15.body, static fallback text) · type: false claim
The document says. "forgelm reverse-pii ... reports every row of every split where that subject appears, satisfying the access-request side of the GDPR's data-subject rights."
What the code does. GDPR Art. 15(1) gives the data subject confirmation, access and the information in (a)-(h): purposes, categories, recipients, storage period, rights, complaint, source and automated decision-making. Art. 15(2) adds transfer safeguards and Art. 15(3) a copy of the data. Art. 12(3) sets a one-month response deadline. EDPB Guidelines 01/2022 v2.1 name three components. Locating rows is only an input…
Proposed correction. '... reports every row where that subject appears — an input to your GDPR Art. 15 access response, which must also give a copy of the data and the Art. 15(1)(a)-(h) information.' -
DOC-BLEGAL-047 (docs, High) — site/index.html: misleading — "Article 17 erasure with forgelm purge and Article 15 access with forgelm reverse-pii, both wired into the audit log."
severity High
Where.site/index.html:519(home.cap.gdpr.body, EN site/js/translations.js:393) (Turkish mirror:site/js/translations.js:499(TR home.cap.gdpr.body)) · type: misleading
The document says. "Article 17 erasure with forgelm purge and Article 15 access with forgelm reverse-pii, both wired into the audit log."
What the code does. GDPR (Reg. (EU) 2016/679, CELEX 32016R0679) Art. 15(1)-(3) requires confirmation, access, the information in (a)-(h) and a copy of the data. EDPB Guidelines 01/2022 name three components: confirmation, access and information. reverse-pii only locates corpus rows. For Art. 17, EDPB Opinion 28/2024 says models trained on personal data 'cannot, in all cases, be considered anonymous' and that data ma…
Proposed correction. 'Tooling for GDPR data-subject requests: forgelm reverse-pii locates a subject's rows (input to an Art. 15 response); forgelm purge removes them from training corpora (one step of Art. 17 erasure — trained weights are not changed). Both are recorded in the audit log.' -
DOC-BLEGAL-050 (docs, High) — site/js/translations.js: misleading — "--pii-mask redacts emails, phones, credit cards (Luhn), IBAN, national IDs ... before chunks land in JSONL — Article 5(1)(c) data minimisation enforced upstream…
severity High
Where.site/js/translations.js:3869(compliance.gdpr.body, EN) (Turkish mirror:site/js/translations.js:3928(TR 'upstream'de uygulanır')) · type: misleading
The document says. "--pii-mask redacts emails, phones, credit cards (Luhn), IBAN, national IDs ... before chunks land in JSONL — Article 5(1)(c) data minimisation enforced upstream."
What the code does. The citation is correct: GDPR Art. 5(1)(c) is data minimisation. But Art. 5(2) makes the controller responsible for, and able to demonstrate, compliance. Masking is a technical measure under Art. 25(1), and Art. 25(2) requires data protection by default, whereas --pii-mask is opt-in and covers only the listed structured identifiers. An opt-in regex mask does not 'enforce' minimisation.
Proposed correction. '... — an opt-in technical measure that supports Article 5(1)(c) data minimisation (Art. 25); unstructured identifiers such as names need additional handling.' -
DOC-D1B05-005 (docs, High) — docs/reference/purge_subcommand.md: false claim —
--kind artefactserases<output_dir>/compliance/*.jsonfiles whose name embeds<run_id>(L6: deletes a run's compliance bundle)
severity High · already tracked: KNOWN-OPEN TRUST-02 (inferred) — docs/roadmap/phase-16-trust-surface-hardening.md:554-566
Where.docs/reference/purge_subcommand.md:51(Turkish mirror:docs/reference/purge_subcommand-tr.md:6,51) · type: false claim
The document says.--kind artefactserases<output_dir>/compliance/*.jsonfiles whose name embeds<run_id>(L6: deletes a run's compliance bundle)
What the code does. The resolver only matches compliance/ files whose name contains the run_id as a token, but the trainer/exporter writes fixed names (compliance_report.json, training_manifest.yaml, data_provenance.json, risk_assessment.json, annex_iv_metadata.json, data_governance_report.json). On a real ForgeLM output dir--kind artefactsfinds nothing, emits data.erasure_failed (NoMatchingArtefacts) and exits…
Code references.forgelm/cli/subcommands/_purge.py:998-1014,forgelm/cli/subcommands/_purge.py:854-866,forgelm/compliance.py:2173-2236,forgelm/trainer.py:2087-2096
Proposed correction. Fix the code so--kind artefactsresolves the bundle ForgeLM actually writes (e.g. by run_id recorded inside compliance_report.json / training_manifest.yaml), then document the exact file set. Until then add a warning: "--kind artefactsonly matches compliance files whose filename contains the run id; the standard bundle filenames do not, so t…
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B02-007 (docs, High) — docs/usermanuals/en/compliance/gdpr.md: broken example — PII masking at ingest is configured with a YAML block
ingestion: pii_mask: {enabled: true, locale: "tr", categories: [email, phone, iban, id_… severity **High** · already tracked: KNOWN-OPEN C31-DOC-SCHEMA-PROSE docs/roadmap/phase-16-trust-surface-hardening.md:773,785-786 **Where.** [docs/usermanuals/en/compliance/gdpr.md:30-36](https://github.com/HodeTech/ForgeLM/blob/cab2947/docs/usermanuals/en/compliance/gdpr.md#L30-L36) (Turkish mirror: [docs/usermanuals/tr/compliance/gdpr.md:30-36](https://github.com/HodeTech/ForgeLM/blob/cab2947/docs/usermanuals/tr/compliance/gdpr.md#L30-L36)) · type: broken example **The document says.** PII masking at ingest is configured with a YAML blockingestion: pii_mask: {enabled: true, locale: "tr", categories: [email, phone, iban, id_tr]}. **What the code does.** ForgeConfig has noingestionfield and is extra="forbid". A config that contains the block fails--dry-runwith "Extra inputs are not permitted" (exit 1, reproduced). PII masking is the CLI flagforgelm ingest INPUT --output FILE --pii-mask(or--all-mask). It has no locale or category selection and replaces every detected span with[REDACTED]. The schema-drift guard skips unknown top-l… **Code references.** [forgelm/config.py](https://github.com/HodeTech/ForgeLM/blob/cab2947/forgelm/config.py) (ForgeConfig model_config extra=forbid; no ingestion field), [forgelm/ingestion.py:2226](https://github.com/HodeTech/ForgeLM/blob/cab2947/forgelm/ingestion.py#L2226), [forgelm/ingestion.py:2375-2378](https://github.com/HodeTech/ForgeLM/blob/cab2947/forgelm/ingestion.py#L2375-L2378), [forgelm/cli/_parser.py](https://github.com/HodeTech/ForgeLM/blob/cab2947/forgelm/cli/_parser.py) (ingest --pii-mask help) **Proposed correction.** Replace the YAML with: "PII masking is a flag offorgelm ingest, not a config block:forgelm ingest ./raw_docs --output data/train.jsonl --pii-mask(add--secrets-mask, or use--all-maskfor both). Detected spans are replaced with[REDACTED]`. The detector set is fixed (email, phone, credit card, IBAN, TR/DE/FR/US national IDs) and cannot…
Turkish mirror. the Turkish page has no such statement -
DOC-D3B02-008 (docs, High) — docs/usermanuals/en/compliance/gdpr.md: false claim —
data_audit_report.jsondocuments PII counts "(before masking)", "Source attribution (which document each row came from)", quality/language dist…
severity High
Where.docs/usermanuals/en/compliance/gdpr.md:42-47(Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:42-47) · type: false claim
The document says.data_audit_report.jsondocuments PII counts "(before masking)", "Source attribution (which document each row came from)", quality/language distribution, and a "SHA-256 manifest for tamper-evidence".
What the code does. A realforgelm auditrun produced a report with these keys: generated_at, source_path, source_input, total_samples, splits, cross_split_overlap, pii_summary, pii_severity, near_duplicate_summary, secrets_summary, quality_summary, croissant and notes. The report has no per-row source attribution and no SHA-256 manifest or other tamper-evidence. PII counts are per split and type, and they describ…
Code references.forgelm/data_audit/_orchestrator.py:463-477,forgelm/data_audit/_aggregator.py:274-300
Proposed correction. "data_audit_report.jsonrecords per-split sample counts, length statistics, the top 3 languages, PII counts by type and severity tier, secrets counts, near-duplicate and cross-split leakage summaries, and quality flags. It has no per-row source attribution and no hash manifest, so protect it with your own integrity controls. When it sits in `tra…
Turkish mirror. the Turkish page has no such statement -
DOC-D3B02-009 (docs, High) — docs/usermanuals/en/compliance/gdpr.md: false denial — Right to erasure is handled by manual steps: rely on masking, drop the source documents and re-ingest. No ForgeLM tool is named.
severity High
Where.docs/usermanuals/en/compliance/gdpr.md:80-87(Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:80-87) · type: false denial
The document says. Right to erasure is handled by manual steps: rely on masking, drop the source documents and re-ingest. No ForgeLM tool is named.
What the code does. ForgeLM shipsforgelm purge --row-id ID --corpus FILE. It atomically removes the matching rows from a JSONL corpus and recordsdata.erasure_requested,data.erasure_completedanddata.erasure_failedwith a salted hash of the id. It also offers--dry-run,--justificationand run-scoped--run-id ... --kind staging.
Code references.forgelm/cli/subcommands/_purge.py:702-818,forgelm/cli/_parser.py:1068-1160
Proposed correction. Add: "Useforgelm purge --row-id <id> --corpus data/train.jsonl --output-dir ./outputs --justification \"<ticket>\"to remove the subject's rows (matched on the row'sid/row_idfield) with a hashed audit record. See GDPR Erasure & Access. Source documents outside the JSONL remain your responsibility."
Turkish mirror. the Turkish page has no such statement -
DOC-D3B02-010 (docs, High) — docs/usermanuals/en/compliance/gdpr.md: broken example — "Set
international_transfers.enabled: trueand document the safeguards."
severity High · already tracked: KNOWN-OPEN C31-DOC-SCHEMA-PROSE docs/roadmap/phase-16-trust-surface-hardening.md:785-786
Where.docs/usermanuals/en/compliance/gdpr.md:124-126(Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:124-126) · type: broken example
The document says. "Setinternational_transfers.enabled: trueand document the safeguards."
What the code does. Nointernational_transferskey exists. A config that contains it fails validation with extra_forbidden (dry-run exit 1, reproduced).
Code references.forgelm/config.py(ForgeConfig extra=forbid)
Proposed correction. "ForgeLM has no configuration for international transfers. Record the transfer mechanism and safeguards in your DPIA and processing records."
Turkish mirror. the Turkish page has no such statement -
DOC-BFIELD-019 (docs, Medium) — forgelm/config.py: wrong detail — Days to retain
final_model.staging.<run_id>/after aforgelm rejectdecision before scheduled cleanup.
severity Medium
Where.forgelm/config.py:1559-1567(RetentionConfig.staging_ttl_days, F65) · type: wrong detail
The document says. Days to retainfinal_model.staging.<run_id>/after aforgelm rejectdecision before scheduled cleanup.
What the code does. No scheduled cleanup exists.purge --check-policyonly reports, and always exits 0. It ages every final_model.staging.<run_id> directory, rejected or still awaiting approval, from that run's first audit event, falling back to mtime (_purge.py:1200-1221, 1260-1288). The reject decision time is not used.
Code references.forgelm/cli/subcommands/_purge.py:1200-1221,forgelm/cli/subcommands/_purge.py:1260-1288,forgelm/cli/subcommands/_purge.py:1108-1169
Proposed correction. 'Age (from the run's first audit event) after which a staging directory is reported as overdue byforgelm purge --check-policy; nothing is deleted automatically.' -
DOC-BLEGAL-064 (docs, Medium) — docs/design/gdpr_erasure.md: misleading — Article 5(1)(e) requires that any personal data not actively required for the declared purpose is removed by default after a defined retention horizon.
severity Medium
Where.docs/design/gdpr_erasure.md:21· type: misleading
The document says. Article 5(1)(e) requires that any personal data not actively required for the declared purpose is removed by default after a defined retention horizon.
What the code does. Art. 5(1)(e) requires data to be 'kept in a form which permits identification of data subjects for no longer than is necessary for the purposes', with an Art. 89(1) exception. It says nothing about removal 'by default' or a 'defined retention horizon'. Those ideas come from recital 39 (controller-set time limits), Art. 25(2) (by default, the storage period), Arts 13(2)(a)/14(2)(a) and Art. 30(1)(…
Proposed correction. 'Article 5(1)(e) requires that personal data be kept in identifiable form no longer than necessary for the purpose; together with Art. 25(2) and recital 39, controllers set time limits for erasure or periodic review.' -
DOC-BLEGAL-068 (docs, Medium) — docs/guides/data_audit.md: wrong detail — [IS-T11-16] 'The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 9 + ENISA for government IDs).' (same citation in…
severity Medium
Where.docs/guides/data_audit.md:153-154(Turkish mirror:docs/guides/data_audit-tr.md:163-164) · type: wrong detail
The document says. [IS-T11-16] 'The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 9 + ENISA for government IDs).' (same citation in forgelm/data_audit/_types.py:26)
What the code does. GDPR Art. 9(1) lists special categories (racial or ethnic origin, political opinions, religious beliefs, trade-union membership, genetic, biometric, health, sex life or orientation data). National identification numbers are not among them; Art. 87 governs their processing. IBAN and card numbers are not Art. 9 data. Regulation (EU) 2016/679, CELEX 32016R0679, is unamended as of 2026-10-01.
Proposed correction. The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 87 (national identification numbers) and Art. 32 risk-based security, plus ENISA's severity methodology, for government IDs). Fix forgelm/data_audit/_types.py:26 the same way.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-097 (docs, Medium) — docs/reference/configuration.md: wrong detail — Horizons honour GDPR Article 5(1)(e) "storage limitation" and Article 17 "right to erasure" deadlines.
severity Medium
Where.docs/reference/configuration.md:255-256(Turkish mirror:docs/reference/configuration-tr.md:255-256) · type: wrong detail
The document says. Horizons honour GDPR Article 5(1)(e) "storage limitation" and Article 17 "right to erasure" deadlines.
What the code does. GDPR Art. 5(1)(e) keeps data identifiable 'for no longer than is necessary' and sets no period. Art. 17(1) requires erasure 'without undue delay'. The only fixed response deadline is Art. 12(3): one month, extendable by two more. Art. 17 therefore sets no retention 'deadlines'. A fixed default horizon does not by itself 'honour' storage limitation. The 1825-day default does exceed the AI Act Art.…
Proposed correction. Horizons are operator-set retention limits that help implement GDPR Article 5(1)(e) storage limitation (no fixed period: data may be kept only as long as necessary) and support erasure handling under Article 17 (requests answered without undue delay, within the Art. 12(3) one-month period). Choose values from your own necessity assessment.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-126 (docs, Medium) — docs/usermanuals/en/data/pii-ml.md: wrong detail — covers the structured identifiers GDPR Article 10 cares about (email, phone, IBAN, credit card, national IDs)
severity Medium
Where.docs/usermanuals/en/data/pii-ml.md:8(Turkish mirror:docs/usermanuals/tr/data/pii-ml.md:8) · type: wrong detail
The document says. covers the structured identifiers GDPR Article 10 cares about (email, phone, IBAN, credit card, national IDs)
What the code does. GDPR Art. 10 covers personal data relating to criminal convictions and offences. Identifiers such as these are personal data under Art. 4(1) ('an identifier such as a name, an identification number, location data, an online identifier'), and national identification numbers are also addressed by Art. 87.
Proposed correction. 'covers structured identifiers that make data personal under GDPR Article 4(1) (email, phone, IBAN, credit card, national IDs — see also Art. 87)'. TR: 'GDPR Madde 4(1) kapsamında kişisel veri yapan yapılandırılmış tanımlayıcıları (… ulusal kimlikler — ayrıca Madde 87)'.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-146 (docs, Medium) — docs/usermanuals/en/reference/configuration.md: wrong detail — audit_log_retention_days: 1825 # 5 years default (Article 5(1)(e))
severity Medium
Where.docs/usermanuals/en/reference/configuration.md:293(Turkish mirror:docs/usermanuals/tr/reference/configuration.md:294) · type: wrong detail
The document says. audit_log_retention_days: 1825 # 5 years default (Article 5(1)(e))
What the code does. GDPR Art. 5(1)(e) sets no period. It only says data may be kept no longer than necessary, with an Art. 89(1) exception. The AI Act's retention periods are Art. 19(1) and Art. 26(6), at least six months for logs, and Art. 18(1), 10 years for technical documentation. Art. 12 sets no period. No EU provision yields 5 years, so the default is a project choice, not a legal baseline.
Proposed correction. '# 5 years: ForgeLM default (project choice; GDPR Art. 5(1)(e) sets no fixed period. Align with AI Act Art. 19(1) (logs >= 6 months) and Art. 18(1) (documentation 10 years) as applicable)'. TR: '# 5 yıl: ForgeLM varsayılanı (proje tercihi; GDPR Madde 5(1)(e) sabit süre koymaz)'.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BYAML-004 (docs, Medium) — docs/usermanuals/en/compliance/gdpr.md: false claim — YAML 'ingestion: pii_mask: {enabled, locale, categories: [email, phone, iban, id_tr]}' configures PII masking at ingest
severity Medium · already tracked: KNOWN-OPEN C31-DOC-SCHEMA-PROSE — docs/roadmap/phase-16-trust-surface-hardening.md:773,785-786
Where.docs/usermanuals/en/compliance/gdpr.md:30-36(TR :30) · type: false claim
The document says. YAML 'ingestion: pii_mask: {enabled, locale, categories: [email, phone, iban, id_tr]}' configures PII masking at ingest
What the code does. Ingestion is configured by CLI flags (--pii-mask, --secrets-mask); ForgeConfig has no ingestion section and the PII kind is tr_id, not id_tr; there is no locale/categories selection.
Code references.forgelm/cli/_parser.py:257-279,forgelm/data_audit/_pii_regex.py:72
Proposed correction. Replace with 'forgelm ingest ... --pii-mask'.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D1B05-017 (docs, Medium) — docs/reference/purge_subcommand.md: wrong detail —
stagingtargetsfinal_model.staging.<run_id>/(and the legacyfinal_model.staging/if present)
severity Medium
Where.docs/reference/purge_subcommand.md:50(Turkish mirror:docs/reference/purge_subcommand-tr.md:50) · type: wrong detail
The document says.stagingtargetsfinal_model.staging.<run_id>/(and the legacyfinal_model.staging/if present)
What the code does. The legacy directory is deleted only if it contains astaging_run_idmarker file whose content equals the run_id; no ForgeLM code writes that marker, so in practice the legacy directory is never purged. Runtime: legacy dir left in place after a successful staging purge.
Code references.forgelm/cli/subcommands/_purge.py:952-995
Proposed correction. "... and the legacyfinal_model.staging/only when it contains astaging_run_idfile whose content equals<run_id>(ForgeLM does not write this marker; remove unmarked legacy staging directories manually)."
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D2B02-024 (docs, Medium) — docs/guides/gdpr_erasure.md: misleading — The tool emits
data.erasure_warning_memorisationwhen afinal_model/exists for any run that consumed the corpus (also L99).
severity Medium
Where.docs/guides/gdpr_erasure.md:22(Turkish mirror:docs/guides/gdpr_erasure-tr.md:22) · type: misleading
The document says. The tool emitsdata.erasure_warning_memorisationwhen afinal_model/exists for any run that consumed the corpus (also L99).
What the code does. The check is onlyos.path.isdir(<output_dir>/final_model). Corpus consumption is never checked. It misses a renamed training.final_model_dir, pipeline stage dirs, and any model outside --output-dir. --output-dir defaults to the corpus's parent directory, so a default invocation usually finds no final_model. affected_run_ids come from staging dirs or human_approval.granted events, not from corpu…
Code references.forgelm/cli/subcommands/_purge.py:401-404,forgelm/cli/subcommands/_purge.py:419-453,forgelm/cli/subcommands/_purge.py:715,forgelm/config.py:453-455
Proposed correction. Reword: "emitsdata.erasure_warning_memorisationwhen<output_dir>/final_model/exists (point--output-dirat the training output directory; models elsewhere, or under a customtraining.final_model_dir, are not detected)."
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D2B02-025 (docs, Medium) — docs/guides/gdpr_erasure.md: wrong detail —
--kind stagingremoves final_model.staging.<run_id>/ and the legacyfinal_model.staging/if present.
severity Medium
Where.docs/guides/gdpr_erasure.md:59(Turkish mirror:docs/guides/gdpr_erasure-tr.md:59) · type: wrong detail
The document says.--kind stagingremoves final_model.staging.<run_id>/ and the legacyfinal_model.staging/if present.
What the code does. The legacy unscoped directory is removed only when it contains astaging_run_idmarker file whose content equals the run id. Otherwise it is skipped, deliberately, to avoid deleting another run's staging dir.
Code references.forgelm/cli/subcommands/_purge.py:952-995
Proposed correction. "…and the legacyfinal_model.staging/only if it carries astaging_run_idmarker matching<run_id>; otherwise remove it manually."
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B02-024 (docs, Medium) — docs/usermanuals/en/compliance/gdpr.md: false claim —
raw_documents_retention_days: 90 # auto-delete originals after N daysand "ForgeLM just records the intended TTL in the audit log."
severity Medium
Where.docs/usermanuals/en/compliance/gdpr.md:55-61(Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:55-61) · type: false claim
The document says.raw_documents_retention_days: 90 # auto-delete originals after N daysand "ForgeLM just records the intended TTL in the audit log."
What the code does. Nothing deletes on a schedule and nothing writes the retention horizons to the audit log. Theretention:block is read only byforgelm purge --check-policy, a read-only violation report that always exits 0. Deletion happens only through an explicitforgelm purgeor the operator's own storage layer. No trainer or compliance code readsretention.
Code references.forgelm/cli/subcommands/_purge.py:1108-1235,forgelm/config.py:1525-1592
Proposed correction. Change the comment to# horizon reported by forgelm purge --check-policy. Replace the parenthetical with: "ForgeLM neither deletes on a schedule nor logs these horizons. Runforgelm purge --check-policy --config <cfg> --output-dir <dir>to list artefacts past their horizon, then remove them withforgelm purgeor your storage layer."
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B07-044 (docs, Medium) — docs/usermanuals/en/data/pii-ml.md: wrong detail — the structured identifiers GDPR Article 10 cares about
severity Medium
Where.docs/usermanuals/en/data/pii-ml.md:8(Turkish mirror:docs/usermanuals/tr/data/pii-ml.md:8) · type: wrong detail
The document says. the structured identifiers GDPR Article 10 cares about
What the code does. GDPR Art. 10 concerns personal data on criminal convictions and offences. The code comments name no GDPR article here (pyproject says 'GDPR-mandated structured identifiers').
Code references.pyproject.toml:159-166
Proposed correction. covers the structured identifiers (email, phone, IBAN, credit card, national IDs)
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-179 (docs, Low) — docs/guides/data_audit.md: wrong detail — "The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 9 + ENISA for government IDs)."
severity Low
Where.docs/guides/data_audit.md:153-154(Turkish mirror:docs/guides/data_audit-tr.md:164) · type: wrong detail
The document says. "The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 9 + ENISA for government IDs)."
What the code does. GDPR Art. 9(1) lists special categories (racial/ethnic origin, political opinions, religion, union membership, genetic, biometric, health, sex life/orientation), and national identification numbers are not among them. Art. 87 ('Processing of the national identification number') covers them, with Art. 32 risk-based security.
Proposed correction. '... GDPR Art. 87 (national identification numbers) and Art. 32 + ENISA for government IDs'.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-180 (docs, Low) — docs/guides/data_audit.md: wrong detail — The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 9 + ENISA for government IDs).
severity Low
Where.docs/guides/data_audit.md:153-154(Turkish mirror:docs/guides/data_audit-tr.md:164) · type: wrong detail
The document says. The tier table is consensus regulatory weighting (PCI-DSS for financial identifiers; GDPR Art. 9 + ENISA for government IDs).
What the code does. GDPR Art. 9(1) lists the special categories: racial or ethnic origin, political opinions, religious or philosophical beliefs, trade-union membership, genetic data, biometric data for unique identification, health, and sex life or sexual orientation. National identification numbers are not among them. They are addressed in Art. 87 ('Processing of the national identification number'), which lets Me…
Proposed correction. Replace 'GDPR Art. 9 + ENISA for government IDs' with 'GDPR Art. 87 (national identification numbers; Member State conditions) and Art. 32 risk-based security + ENISA for government IDs'. Mirror in the TR file.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D2B02-051 (docs, Low) — docs/guides/gdpr_erasure.md: misleading — Memorisation warning fires when final_model/ exists 'for any run that consumed the corpus'
severity Low
Where.docs/guides/gdpr_erasure.md:22; docs/reference/purge_subcommand.md:83 · type: misleading
The document says. Memorisation warning fires when final_model/ exists 'for any run that consumed the corpus'
What the code does. Only the literal <output_dir>/final_model is checked. Consumption is never checked, and affected_run_ids is 'unknown' for non-gated runs. See F7.
Code references.forgelm/cli/subcommands/_purge.py:401-453
Proposed correction. See F7.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B02-047 (docs, Low) — docs/usermanuals/en/compliance/gdpr.md: wrong detail —
forgelm reverse-pii --query "ali@example.com" data/*.jsonlprints "No matches found in masked data."
severity Low
Where.docs/usermanuals/en/compliance/gdpr.md:73-76(Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:73-76) · type: wrong detail
The document says.forgelm reverse-pii --query "ali@example.com" data/*.jsonlprints "No matches found in masked data."
What the code does. The real text output is "No matches for identifier hash <16 hex>… (literal, plaintext mode) in N file(s)." Without--output-dir, the command also creates.forgelm_audit_saltand appends adata.access_request_queryevent in the first corpus file's parent directory, and warns about doing so.
Code references.forgelm/cli/subcommands/_reverse_pii.py:811-818,forgelm/cli/subcommands/_reverse_pii.py:736-752
Proposed correction. Useforgelm reverse-pii --query "ali@example.com" --output-dir ./outputs data/*.jsonland show the real line:No matches for identifier hash 3f2a… (literal, plaintext mode) in 3 file(s).
Turkish mirror. the Turkish page has the same error; fix both in the same PR
Acceptance criteria
- A regression test reproduces the failure scenario and asserts the corrected behaviour.
- Documentation listed above is corrected in English and in its Turkish mirror in the same PR.
- Every finding in this issue is resolved or explicitly closed with a reason.
- The release note names the contract this fix restores.
Found by the October 2026 ForgeLM code and documentation review against commit cab2947. Code links point at that commit; line numbers may have moved since. Finding IDs (CR-…, DOC-…, TECH-…) are stable references for this backlog.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
cemililik commentedon Oct 9, 2026
Additional locations from the September 2026 independent review (
F-W1-PROCESS-04), re-verified againstdevelopmentatf94595fon 2026-10-09.DOC-BLEGAL-004 cites
docs/design/gdpr_erasure.md:48. The same blanket Article 17(3)(b) claim recurs in that design atdocs/design/gdpr_erasure.md:59("Never delete original"),docs/design/gdpr_erasure.md:167(the exemption "applies" even after the retention horizon is overstayed),docs/design/gdpr_erasure.md:242anddocs/design/gdpr_erasure.md:283; correct them with the same wording in the same PR. A separate error in the same design — Article 5(1)(b) cited as a lawful basis at :300 — is tracked in #886.Code links point at
cab2947; line numbers may have moved since.docs: compliance and privacy wording, SBOM and Article 12/14, and the…
docs: CodeRabbit follow-ups — a CI gate that fails, digest/salt condi…
fix(docs): digest-correlation rule, CI gate, exit codes, JSON envelop…
docs: changelog for the corrected GDPR documentation, sync-bilingual-…