Repository navigation
Memorisation warning looks only at <output_dir>/final_model, and output_dir defaults to the corpus folder, so a default invocation never warns (CR-A10-022) #102
Description
Wave 0 · theme D0-6: Personal data: audit and masking coverage · highest severity High · 10 finding(s) · effort medium
Summary
An operator erases a subject's row with the plain documented command. The audit chain records a completed erasure with no memorisation warning, and the DPO concludes that no trained model holds the data. The run's own audit log, under checkpoints/, holds no record of the erasure.
Primary finding
CR-A10-022 — Memorisation warning looks only at <output_dir>/final_model, and output_dir defaults to the corpus folder, so a default invocation never warns
severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
What happens. _detect_warning_conditions fires data.erasure_warning_memorisation only when the literal <output_dir>/final_model exists (_purge.py:401-404). In row mode output_dir defaults to the corpus's parent directory (_purge.py:715), not to training.output_dir, which defaults to ./checkpoints. A loaded --config is used only for webhook targets, never to find training.output_dir or training.final_model_dir. A non-default training.final_model_dir (config.py:453-455, used by trainer.py:1590-1593) is missed even with the correct --output-dir. The same hard-coded name affects staging purge (:970) and the retention scan (:1282). So the documented 'warning when a final_model/ exists for any run that consumed the corpus' (docs/guides/gdpr_erasure.md:22; catalog) does not fire on default use. The implicit fallback also writes the salt, audit_log.jsonl and the genesis manifest next to the corpus, in a separate chain from the run's audit log. docs/reference/purge_subcommand.md:38 and docs/usermanuals/en/compliance/gdpr-erasure.md:117 say this fallback 'emits a WARNING naming the resolved dir', but purge emits none. HF datasets Arrow-cache copies are outside the documented scope (design §6 scope limitation), so they are not counted here.
Where.
forgelm/cli/subcommands/_purge.py:715forgelm/cli/subcommands/_purge.py:401-404forgelm/cli/subcommands/_purge.py:406forgelm/trainer.py:1590-1593forgelm/config.py:451-456docs/reference/purge_subcommand.md:38docs/usermanuals/en/compliance/gdpr-erasure.md:117(the implicit-fallback WARNING claim)docs/guides/gdpr_erasure.md:22(the 'Does not' list claim about the memorisation event)forgelm/cli/subcommands/_purge.py:660docs/reference/audit_event_catalog.md:80
Evidence.
- Repro r7. (1) Corpus at r7/data/train.jsonl, trained model at r7/checkpoints/final_model, no --output-dir: warnings [], and .forgelm_audit_salt, audit_log.jsonl and its manifest appear in r7/data. (2) --output-dir r7/ck2 holding my_model/ (custom final_model_dir): warnings []. Repro r12: text mode prints only 'Erased 1 row(s); 22 bytes freed.' with no WARNING. rg finds no logger.warning in _purge.py other than the config-load failure path (:1450).
Recommended fix. When --config is given, use training.output_dir and training.final_model_dir to find final models and staging directories. Make the fallback emit the documented WARNING, or require --output-dir. Glob <final_model_dir> and <final_model_dir>.staging.* instead of hard-coding 'final_model'.
Other findings with the same root cause
These close with the same fix; update them in the same pull request.
-
CR-A10-042 (code, Medium) — The memorisation warning and retention horizons ignore checkpoint-* dirs, which every completed run leaves behind, and the memorisation warning also ignores staging dirs and exported models: purge re…
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Failure scenario. Case 1: a run with evaluation.require_human_approval exits 4 and is pending or rejected. Case 2: a run trips auto_revert, which deletes final_model/. In both cases out/ still holds checkpoint-1/ and checkpoint-2/ with LoRA weights trained on the subject's row, plus final_model.staging.<run_id>/ in case 1.forgelm purge --row-id alice --corpus data/train.jsonl --output-dir outexits 0 with warnings [], and the Art. 17 audit chain records no memorisation gap. --check-policy with every horizon a…
Where.forgelm/cli/subcommands/_purge.py:388-390,forgelm/cli/subcommands/_purge.py:1199-1221,forgelm/trainer.py:509-510,forgelm/trainer.py:509
Recommended fix. In row mode, raise the memorisation warning, or a sibling event, when any checkpoint-, <final_model_dir>.staging. or configured merge or export output exists under the configured training.output_dir. Read these paths from --config. List the paths in the payload. Add a checkpoint horizon, or say in the docs that checkpoints are outside retention, and correct the docs that promise the operator se… -
TECH-T11-001 (technology, Now) — Regulation and standards: GDPR, ISO/IEC 27001, SOC 2, ISO/IEC 42001, EDPB, EU Cyber Resilience Act: EDPB Opinion 28/2024 and later EDPB guidance: personal data persisting in trained weights, and the…
severity Now
Item. EDPB Opinion 28/2024 and later EDPB guidance: personal data persisting in trained weights, and the limits of ForgeLM's 'GDPR Article 17 erasure' and model-anonymity statements · verdict:incorrect-claim· urgency: Now
State in ForgeLM. Accurate surfaces: docs/guides/gdpr_erasure.md:22 and docs/usermanuals/en/compliance/gdpr-erasure.md:51/:113 say purge does not unmemorise weights and that retraining is the proper mitigation. purge emits data.erasure_warning_memorisation (forgelm/cli/subcommands/_purge.py:72, :403). Overstated or wrong surfaces: docs/usermanuals/en/compliance/gdpr.md:78 (TR :78), inside the Art. 15 access-reques…
State of the ecosystem on 2026-10-01. EDPB Opinion 28/2024 was adopted on 17 December 2024 under Art. 64(2) GDPR. Its executive summary says AI models trained with personal data 'cannot, in all cases, be considered anonymous'. Anonymity requires the likelihood of extraction and of obtaining personal data from queries to be 'insignificant', assessed case by case. Para. 31: personal data 'may still remain ‘absorbed’ in the parameters o…
Recommendation. Fix the docs now. Delete or rewrite gdpr.md:78 and :94 (EN+TR): masking reduces risk but does not make the model anonymous; anonymity needs a case-by-case demonstration (Opinion 28/2024 paras 31 and 38-43 framework); ForgeLM produces no extraction or regurgitation evidence. Change gdpr-erasure.md:17 to 'delete this corpus row / run artefacts'. Next release: add one sentence to the purge help/desc…
Sources. -
DOC-BLEGAL-024 (docs, High) — docs/usermanuals/en/compliance/gdpr.md: false claim — No matches found in masked data. / Because PII was masked at ingest, no specific person's data is recoverable from the model. The audit report co…
severity High
Where.docs/usermanuals/en/compliance/gdpr.md:75-78,85 (Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:78,85) · type: false claim
The document says. No matches found in masked data. / Because PII was masked at ingest, no specific person's data is recoverable from the model. The audit report confirms this. / The masked JSONL doesn't contain their identifying info — already minimised.
What the code does. Under GDPR Recital 26, pseudonymised data that can be attributed with additional information stays personal data, and EDPB Guidelines 01/2025 agree. Art. 15(1) and Art. 17(1) apply to all personal data the controller processes, not only to the masked training copy. EDPB Opinion 28/2024 says models trained with personal data 'cannot, in all cases, be considered anonymous' and that data may remain…
Proposed correction. Search every store you control (raw documents, masked corpora, logs), not only the masked JSONL. Masking reduces but does not eliminate personal data, and it does not make a trained model anonymous (EDPB Opinion 28/2024); assess memorisation risk case by case.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-BLEGAL-044 (docs, High) — site/features.html: misleading — GDPR Article 17 erasure —
forgelm purgedrops a data-subject's rows from training corpora and stamps the deletion in the append-only audit log.
severity High
Where.site/features.html:417-418(Turkish mirror:site/js/translations.js:1140-1141) · type: misleading
The document says. GDPR Article 17 erasure —forgelm purgedrops a data-subject's rows from training corpora and stamps the deletion in the append-only audit log.
What the code does. Art. 17(1) obliges erasure of the personal data concerning the subject. EDPB Opinion 28/2024 (adopted 2024-12-17) states that models trained on personal data 'cannot, in all cases, be considered anonymous' and that data may remain 'absorbed' in the parameters (para. 31). Supervisory authorities may order erasure of the dataset and/or the model (paras 114-115). purge removes corpus rows only. Labe…
Proposed correction. Title 'GDPR Art. 17 corpus erasure'. Append to the body: 'Trained weights are not modified — assess whether the model must be retrained or withdrawn.' Fix translations.js:1035-1036 (EN), 1140-1141 (TR), 4480-4481 (DE), 4526-4527 (FR), 4572-4573 (ES) and 4618-4619 (ZH) too. -
DOC-D3B02-005 (docs, High) — docs/usermanuals/en/compliance/gdpr-erasure.md: misleading —
forgelm purgeanswers "Delete every record of me." The example isforgelm purge --row-id "ali@example.com" --corpus train.jsonl.
severity High
Where.docs/usermanuals/en/compliance/gdpr-erasure.md:17,27-28 (Turkish mirror:docs/usermanuals/tr/compliance/gdpr-erasure.md:17,27-28) · type: misleading
The document says.forgelm purgeanswers "Delete every record of me." The example isforgelm purge --row-id "ali@example.com" --corpus train.jsonl.
What the code does. Row mode deletes only rows whoseidorrow_idfield equals the value. It does not search row content for the identifier. Reproduced: in a corpus with{"id":"ali@example.com"}and{"id":"r2","text":"mail ali@example.com"}, purge removed row 1, reported success, and left the email in row 2. Rows without an id key cannot be purged at all.
Code references.forgelm/cli/subcommands/_purge.py:87,forgelm/cli/subcommands/_purge.py:272-295,forgelm/cli/subcommands/_purge.py:527-582
Proposed correction. Change the table cell to: "Delete the row(s) with this id." Add: "--row-idmatches the row'sid/row_idfield only, not text content. Runforgelm reverse-pii --query <identifier>first to find every row that mentions the subject, and purge each one by its id."
Turkish mirror. the Turkish page has no such statement -
DOC-D1B04-022 (docs, Medium) — docs/reference/audit_event_catalog.md: misleading — data.erasure_warning_memorisation fires when final_model/ exists 'for any run that consumed this corpus'.
severity Medium
Where.docs/reference/audit_event_catalog.md:80(Turkish mirror:docs/reference/audit_event_catalog-tr.md:80) · type: misleading
The document says. data.erasure_warning_memorisation fires when final_model/ exists 'for any run that consumed this corpus'.
What the code does. The warning fires only when the literal directory <output_dir>/final_model exists. There is no corpus-consumption check, and training.final_model_dir is ignored. --output-dir defaults to the corpus' parent directory, so a model under checkpoints/ is not seen.
Code references.forgelm/cli/subcommands/_purge.py:401-404,forgelm/cli/subcommands/_purge.py:713
Proposed correction. 'Fires when <--output-dir>/final_model/ exists (literal name; --output-dir defaults to the corpus directory). It does not determine which runs consumed the corpus; point --output-dir at the training output directory.'
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B02-021 (docs, Medium) — docs/usermanuals/en/compliance/gdpr-erasure.md: wrong detail —
query_hashondata.access_request_queryandtarget_idondata.erasure_*are byte-identical for the same subject.
severity Medium
Where.docs/usermanuals/en/compliance/gdpr-erasure.md:19(Turkish mirror:docs/usermanuals/tr/compliance/gdpr-erasure.md:19) · type: wrong detail
The document says.query_hashondata.access_request_queryandtarget_idondata.erasure_*are byte-identical for the same subject.
What the code does. They match only when both runs resolve the same salt. Two cases break it: FORGELM_AUDIT_SECRET is set for one run and not the other, or reverse-pii runs with--salt-source per_dir(which ignores the env secret) while purge, which has no salt-source flag, mixes it in.
Code references.forgelm/cli/subcommands/_reverse_pii.py:251-268,forgelm/cli/subcommands/_purge.py:196-224
Proposed correction. Add: "... byte-identical as long as both invocations use the same--output-dirand the sameFORGELM_AUDIT_SECRETstate (and reverse-pii is not run with--salt-source per_dirwhile the secret is set)."
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B02-044 (docs, Low) — docs/usermanuals/en/compliance/gdpr-erasure.md: wrong detail — purge "atomically removes a row, a staging directory, or a compliance bundle".
severity Low · already tracked: KNOWN-OPEN TRUST-02 docs/roadmap/phase-16-trust-surface-hardening.md:553-562
Where.docs/usermanuals/en/compliance/gdpr-erasure.md:17(Turkish mirror:docs/usermanuals/tr/compliance/gdpr-erasure.md:17) · type: wrong detail
The document says. purge "atomically removes a row, a staging directory, or a compliance bundle".
What the code does. Only row erasure is atomic (temp file plus os.replace). Run-scoped erasure deletes targets one by one with shutil.rmtree/unlink and can stop part-way ("Deletion failed mid-batch: N of M target(s) removed").
Code references.forgelm/cli/subcommands/_purge.py:298-340,forgelm/cli/subcommands/_purge.py:908-930,forgelm/cli/subcommands/_purge.py:1092-1100
Proposed correction. "atomically rewrites the corpus without the row, or deletes a staging directory or compliance files (run-scoped deletion is not atomic; a failure mid-batch is recorded indata.erasure_failed.files_modified)."
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
DOC-D3B02-048 (docs, Low) — docs/usermanuals/en/compliance/gdpr.md: wrong detail — "Detect memorisation: the audit step flags rows that overlap with known PII patterns."
severity Low
Where.docs/usermanuals/en/compliance/gdpr.md:94(Turkish mirror:docs/usermanuals/tr/compliance/gdpr.md:94) · type: wrong detail
The document says. "Detect memorisation: the audit step flags rows that overlap with known PII patterns."
What the code does.forgelm auditreports PII counts per split and per type (pii_summary,pii_severity). It does not list or flag individual rows, and it does not detect memorisation.
Code references.forgelm/data_audit/_aggregator.py:274-300
Proposed correction. "The audit step counts PII matches per split and type (pii_summary,pii_severity), so you can see where personal data remains before training."
Turkish mirror. the Turkish page has no such statement
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
fix: rope_scaling on transformers 5, hub telemetry, reserved retentio…
fix(purge): warn about the weights a run really leaves behind (#102)
Merge pull request #1068 from HodeTech/issue-102-purge-config-memoris…
checkpoint-*directories #1069checkpoint-*directories #1069