Skip to content

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

Closed

Description

@cemililik

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.

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 · reachability default · intent contract-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-mask runs 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-ml adds 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 artefacts erases <output_dir>/compliance/*.json files 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 artefacts erases <output_dir>/compliance/*.json files 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 artefacts finds 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 artefacts resolves 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 artefacts only 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 block ingestion: pii_mask: {enabled: true, locale: "tr", categories: [email, phone, iban, id_tr]}. **What the code does.** ForgeConfig has no ingestionfield 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 of forgelm 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.json documents 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.json documents 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 real forgelm audit run 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.json records 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 ships forgelm purge --row-id ID --corpus FILE. It atomically removes the matching rows from a JSONL corpus and records data.erasure_requested, data.erasure_completed and data.erasure_failed with a salted hash of the id. It also offers --dry-run, --justification and run-scoped --run-id ... --kind staging.
    Code references. forgelm/cli/subcommands/_purge.py:702-818, forgelm/cli/_parser.py:1068-1160
    Proposed correction. Add: "Use forgelm purge --row-id <id> --corpus data/train.jsonl --output-dir ./outputs --justification \"<ticket>\" to remove the subject's rows (matched on the row's id/row_id field) 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: true and 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. "Set international_transfers.enabled: true and document the safeguards."
    What the code does. No international_transfers key 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 a forgelm reject decision before scheduled cleanup.
    severity Medium
    Where. forgelm/config.py:1559-1567 (RetentionConfig.staging_ttl_days, F65) · type: wrong detail
    The document says. Days to retain final_model.staging.<run_id>/ after a forgelm reject decision before scheduled cleanup.
    What the code does. No scheduled cleanup exists. purge --check-policy only 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 by forgelm 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 — staging targets final_model.staging.<run_id>/ (and the legacy final_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. staging targets final_model.staging.<run_id>/ (and the legacy final_model.staging/ if present)
    What the code does. The legacy directory is deleted only if it contains a staging_run_id marker 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 legacy final_model.staging/ only when it contains a staging_run_id file 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_memorisation when a final_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 emits data.erasure_warning_memorisation when a final_model/ exists for any run that consumed the corpus (also L99).
    What the code does. The check is only os.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: "emits data.erasure_warning_memorisation when <output_dir>/final_model/ exists (point --output-dir at the training output directory; models elsewhere, or under a custom training.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 staging removes final_model.staging.<run_id>/ and the legacy final_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 staging removes final_model.staging.<run_id>/ and the legacy final_model.staging/ if present.
    What the code does. The legacy unscoped directory is removed only when it contains a staging_run_id marker 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 legacy final_model.staging/ only if it carries a staging_run_id marker 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 days and "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 days and "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. The retention: block is read only by forgelm purge --check-policy, a read-only violation report that always exits 0. Deletion happens only through an explicit forgelm purge or the operator's own storage layer. No trainer or compliance code reads retention.
    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. Run forgelm purge --check-policy --config <cfg> --output-dir <dir> to list artefacts past their horizon, then remove them with forgelm purge or 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/*.jsonl prints "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/*.jsonl prints "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_salt and appends a data.access_request_query event 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. Use forgelm reverse-pii --query "ali@example.com" --output-dir ./outputs data/*.jsonl and 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.

Activity

  1. added
    bugSomething isn't working
    documentationImprovements or additions to documentation
    backlog: knownAlready tracked in docs/roadmap
    dependenciesDependency or ecosystem currency
    severity: criticalBreaks a stated security or safety contract
    source: review-2026-10Found by the October 2026 code and documentation review
    urgency: next-releaseTechnology currency: fix in the next release
    wave: 0Roadmap wave 0: immediate (patch release)
    on Oct 9, 2026
  2. cemililik commented on Oct 9, 2026

    @cemililik
    CollaboratorAuthor

    Additional locations from the September 2026 independent review (F-W1-PROCESS-04), re-verified against development at f94595f on 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 at docs/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:242 and docs/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.

  3. moved this from Todo to In progress in ForgeLM Project Planon Oct 21, 2026
  4. self-assigned this
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: data-auditCode area: data-auditarea: dev-toolingCode area: dev-toolingbacklog: knownAlready tracked in docs/roadmapbugSomething isn't workingdependenciesDependency or ecosystem currencydocumentationImprovements or additions to documentationseverity: criticalBreaks a stated security or safety contractsource: review-2026-10Found by the October 2026 code and documentation reviewurgency: next-releaseTechnology currency: fix in the next releasewave: 0Roadmap wave 0: immediate (patch release)

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions