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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ All notable changes to ForgeLM are documented here.

## [Unreleased]

### Fixed

- **`forgelm purge --row-id` warns about the weights a run actually leaves behind.** This restores the documented
contract that `data.erasure_warning_memorisation` fires when a trained model exists for the erased row. The check
used to look only for a literal `<--output-dir>/final_model/`, so a default invocation (`--output-dir` omitted,
which uses the corpus directory), a non-default `training.final_model_dir`, a run awaiting or refused human
approval (its model exists only as `<final_model_dir>.staging.<run_id>/`) and a run whose auto-revert deleted the
model but left `checkpoint-*` all exited `0` with no warning. It now reports the model directory, its
`<final_model_dir>.staging[.<run_id>]` siblings and `checkpoint-*` directories under `--output-dir` and, with
`--config`, under `training.output_dir` / `training.final_model_dir`, every `pipeline` stage's `training` block
and `merge.output_dir`. The event gains `memorisation_artefact_paths` (the absolute path of every artefact
found); `affected_run_ids` also reads the run's own audit log under the configured training directory; a
directory that cannot be listed is logged at WARNING instead of reading as "nothing found". Models you export or
copy outside those directories stay invisible to the check (#102, CR-A10-022, CR-A10-042).
- `forgelm purge --run-id <id> --kind staging` reads `--config` for `training.final_model_dir`, and
`--check-policy` takes it from a full config, so a run with a non-default model directory has its
`<final_model_dir>.staging.<run_id>/` found, erased and aged like the default `final_model.staging.<run_id>/`.
A `retention:`-only file still means the default `final_model`. No retention horizon covers `checkpoint-*`
directories; the purge, retention and configuration docs now say so (#102, CR-A10-022, CR-A10-042).
- The user manual states that `forgelm purge --row-id` matches the row's `id` / `row_id` field only, never the row's
text or a line number, and points to `forgelm reverse-pii` for finding every row that mentions the subject
(#102, DOC-D3B02-005).

## [0.11.1] — 2026-10-23

_(**Numbered as a patch by a maintainer decision, as a one-time exception.**
Expand Down
4 changes: 2 additions & 2 deletions docs/design/gdpr_erasure.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,10 @@ The design above shipped in v0.5.5, with the deviations listed after this list:
- Retention horizons are reported by `forgelm purge --check-policy` and never enforced; `retention.enforce` is reserved and no command acts on it (the training CLI logs a warning when it is set). There is no trainer pre-flight gate: nothing exits 3 or refuses a run on age.
- The legacy `final_model.staging/` is purged only when it holds a `staging_run_id` marker equal to the run id. Nothing in ForgeLM writes that marker.
- `--check-policy` ages the raw-documents directories (`raw_documents/`, `ingestion_output/`) from the audit log's first event, not per run; it reads `--config` only when given (no `./forgelm.yaml` discovery) and, without a `retention:` block, reports that nothing was checked. `--dry-run` combined with `--check-policy` is not rejected.
- The `data.erasure_warning_external_copies` event is emitted in row mode only, and only when `--config` is given; run mode never loads the config.
- The `data.erasure_warning_external_copies` event is emitted in row mode only, and only when `--config` is given; run mode reads the config only for `training.final_model_dir`, to name the staging directory.
- Mode flags that conflict (for example `--row-id` with `--run-id`, or `--kind logs`) are rejected by the argument parser, which exits 2 without a JSON envelope; other configuration errors exit 1. A run id is not looked up in the audit log: it is "unknown" only when no matching artefact exists.
- With `FORGELM_AUDIT_SECRET` set, the per-directory salt is XOR-combined with the first 16 bytes of the secret (`salt_source=env_var`), so a row's `target_id` changes when the secret rotates. `forgelm reverse-pii` derives its `query_hash` the same way in its default plaintext mode; `--salt-source` switches it to a hash-mask scan that looks for digests inside the corpus.
- `data.erasure_warning_memorisation` fires whenever `<output_dir>/final_model/` exists; the check does not tie that model to the erased row.
- `data.erasure_warning_memorisation` fires whenever a model directory, a `<final_model_dir>.staging[.<run_id>]` directory or a `checkpoint-*` directory exists under `--output-dir` or, with `--config`, under the training directories the config names (and `merge.output_dir`); the check does not tie any of them to the erased row.
- The recovery paths of §4.4 assume the temp file survives a failed rename; the shipped code removes it (best effort) instead.
- Neither the trainer pre-flight gate nor the `forgelm audit` retention notes of §3.4 exist.
- The `recommendation` field listed for the new events in §5.1 is not emitted, and the test table in §7 predates the shipped tests (for example, a dry-run appends audit events, and erasing the same row twice exits 1 with `NoMatchingRow`); `tests/test_gdpr_erasure.py` is the source of truth.
12 changes: 6 additions & 6 deletions docs/guides/gdpr_erasure-tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

**Yapmaz:**

- Modelleri yeniden eğitmez. Bir satırı corpus'tan kaldırmak onu zaten eğitilmiş ağırlıklardan unutturmaz — silinmiş satır olmadan tam yeniden eğitim tek doğru çözüm. Tool, `--output-dir` altında doğrudan bir `final_model/` dizini mevcut olduğunda `data.erasure_warning_memorisation` yayar (`--output-dir` varsayılan olarak corpus'un dizinidir; bu yüzden eğitim çıktı dizininizi verin); böylece operatör boşluğu görür. Corpus'u hangi koşumların tükettiğini ayırt edemez.
- Modelleri yeniden eğitmez. Bir satırı corpus'tan kaldırmak onu zaten eğitilmiş ağırlıklardan unutturmaz — silinmiş satır olmadan tam yeniden eğitim tek doğru çözüm. Tool, bir model dizini, bir `<final_model_dir>.staging.<run_id>` staging dizini (insan onayını bekleyen veya reddedilen bir koşum) ya da bir `checkpoint-*` dizini bulduğunda `data.erasure_warning_memorisation` yayar — `--output-dir` altında (varsayılan olarak corpus'un dizinidir; bu yüzden eğitim çıktı dizininizi verin) ve `--config` ile config'in adlandırdığı eğitim dizinlerinde (pipeline stage'leri ve `merge.output_dir` dahil) — böylece operatör boşluğu görür. Corpus'u hangi koşumların tükettiğini ayırt edemez ve başka yere kopyaladığınız ya da export ettiğiniz modelleri görmez.
- Audit log'un kendisinden entry silmez. Tool, silmeyi geçmişi yeniden yazmak yerine yeni bir event olarak kaydeder ve audit log'dan hiçbir girdiyi silmez. Veri sahibini adlandıran girdilerin tutulup tutulamayacağı veri sorumlusunun kararıdır ve belgelenmelidir. Madde 17(1)'deki silme nedenlerinden biri geçerliyse girdiler yalnızca bir Madde 17(3) istisnasının kapsadığı yerde kalır: 17(3)(b), yalnızca işlemenin veri sorumlusunun tabi olduğu AB veya üye devlet hukukundan doğan bir yasal yükümlülüğe uyum için (veya kamu yararına ya da kamu otoritesinin kullanılmasına ilişkin bir görev için) gerekli olduğu ölçüde uygulanır; 17(3)(e) ise hukuki taleplerin ileri sürülmesini, kullanılmasını veya savunulmasını kapsar. Madde 17(1)'deki nedenlerin hiçbiri geçerli değilse (girdilerin toplandıkları amaç için hâlâ gerekli olması yalnızca 17(1)(a)'yı dışlar; rızanın geri çekilmesini, Madde 21 itirazını ve hukuka aykırı işlemeyi de kontrol edin) silme gerekmez; ancak talebe yine de gecikmeksizin ve en geç talebin alınmasından itibaren bir ay içinde yanıt verilir; yanıtta gerekçe, bir denetim otoritesine şikâyet etme ve yargı yoluna başvurma imkânı bildirilir (Madde 12(4)).
- Downstream tüketicilere silme bildirimi göndermez. Webhook receiver'ları, dataset mirror'ları, deploy edilmiş model endpoint'leri ve yayınlanmış checkpoint'ten türetilmiş üçüncü-taraf fine-tune'lar operatörün runtime-katmanı sorumluluğudur (GDPR Madde 19, verinin ifşa edildiği her alıcıya silmeyi bildirmeyi ister; bu, imkânsız olmadıkça veya orantısız çaba gerektirmedikçe geçerlidir ve veri sahibi isterse bu alıcıları ona da bildirmek gerekir; Madde 17(2) ise veri sorumlusu veriyi kamuya açtıysa makul adımlar ekler). Tool, row modunda `--config` ile verdiğiniz config webhook block'u içerdiğinde `data.erasure_warning_external_copies` yayar; böylece audit log'u sorgulayan downstream tüketici açık hatırlatmayı görür.
- Yedekleri silmez. Operatörün `<output_dir>` sınırı dışındaki replikalar, snapshot'lar ve storage backup'lar altyapı tarafı endişelerdir.
Expand Down Expand Up @@ -60,7 +60,7 @@ $ forgelm purge --run-id fg-abc123def456 --kind staging --output-dir ./outputs
$ forgelm purge --run-id fg-abc123def456 --kind artefacts --output-dir ./outputs
```

- `--kind staging` — `<output_dir>/final_model.staging.<run_id>/`'i siler; legacy `final_model.staging/`'i yalnızca içeriği `<run_id>`'ye eşit bir `staging_run_id` işaretçisi taşıyorsa siler (ForgeLM bu işaretçiyi yazmaz; aksi halde legacy dizini kendiniz silin).
- `--kind staging` — `<output_dir>/<final_model_dir>.staging.<run_id>/`'i siler (`--config` başka bir `training.final_model_dir` adlandırmadıkça `final_model`); legacy `<final_model_dir>.staging/`'i yalnızca içeriği `<run_id>`'ye eşit bir `staging_run_id` işaretçisi taşıyorsa siler (ForgeLM bu işaretçiyi yazmaz; aksi halde legacy dizini kendiniz silin).
- `--kind artefacts` — `<output_dir>/compliance/` altında doğrudan bulunan, adı `<run_id>`'yi `_`, `-`, `.` ya da adın başı/sonu ile sınırlı olarak içeren her dosyayı siler (yalnızca `.json` değil). **Standart paketi bulamaz:** trainer run id içermeyen sabit dosya adları yazar (`compliance_report.json`, `training_manifest.yaml`, ...), bu yüzden normal bir çıktı dizininde komut hiçbir şey bulmaz, `data.erasure_failed` (`NoMatchingArtefacts`) yayar, 1 ile çıkar ve hiçbir şey silmez. Bu, gerçek bir run id için (`fg-…`) geçerlidir: sabit bir adda geçen genel bir token (örneğin `json` veya `report`) o dosyalarla eşleşir ve onları siler; bu yüzden her zaman tam run id'yi verin. Resolver düzeltilene kadar paketi depolama katmanınız üzerinden kaldırın.
- `--kind logs` **bilinçli olarak yok**: audit log append-only'dir ve tool onu asla silmez (girdilerin tutulup tutulamayacağına kimin karar verdiği için yukarıdaki "Yapmaz" listesine bakın).

Expand All @@ -70,7 +70,7 @@ $ forgelm purge --run-id fg-abc123def456 --kind artefacts --output-dir ./outputs
$ forgelm purge --check-policy --config configs/run.yaml --output-dir ./outputs
```

Salt-okunur tarama. Output dizinini gezer, her artefact'ın yaşını (canonical: `final_model.staging.<run_id>/` dizini için koşumun ilk audit event'i, yoksa audit-log genesis `timestamp`'i; fallback: filesystem `mtime` + `age_source=mtime` flag'i ile) yüklü config'in `retention:` horizon'larına karşı kıyaslar ve structured violation list yayar. `--config`, eğitim config'i ya da yalnızca `retention:` bloğunu içeren bir dosya olabilir; başka bir üst düzey anahtar içeren dosya tam config olarak doğrulanır.
Salt-okunur tarama. Output dizinini gezer, her artefact'ın yaşını (canonical: `<final_model_dir>.staging.<run_id>/` dizini için koşumun ilk audit event'i, yoksa audit-log genesis `timestamp`'i; fallback: filesystem `mtime` + `age_source=mtime` flag'i ile) yüklü config'in `retention:` horizon'larına karşı kıyaslar ve structured violation list yayar. `--config`, eğitim config'i ya da yalnızca `retention:` bloğunu içeren bir dosya olabilir; başka bir üst düzey anahtar içeren dosya tam config olarak doğrulanır. `final_model_dir`, tam bir config'in `training` bloğundan gelir; yalnızca `retention:` içeren dosya varsayılan `final_model`'i bırakır.

**Başarılı policy raporları 0 ile çıkar** (rapor-değil-gate semantiği — design §10 Q5). Config-load başarısızlıkları `EXIT_CONFIG_ERROR` (sıfır olmayan) ile çıkar: eksik / okunamayan / Pydantic validation'ı geçemeyen explicit `--config`, "loader failed" durumunu "ihlal yok" sanmamak için `EXIT_CONFIG_ERROR` olarak yüzeye çıkar. CI gate isteyen operatörler `--output-format json` kullanır ve raporu kendileri sınar, örneğin `set -o pipefail` altında `jq -e '.success == true and has("count") and .count == 0'`: `count`, hiçbir `retention:` bloğu yüklenmediğinde yoktur; böylece gate hiç kontrol edilmemiş bir dizinde geçmek yerine başarısız olur, ve `--output-dir` gerçek eğitim çıktı dizini olmalıdır (olmayan bir dizin `count: 0` raporlar). Bu, public exit-code kontratını her ForgeLM subcommand'ında tutarlı tutar.

Expand All @@ -85,7 +85,7 @@ retention:
enforce: log_only # ayrılmış: henüz hiçbir komut onu dikkate almaz (log_only / warn_on_excess / block_on_excess)
```

Herhangi bir horizon'u `0` olarak ayarlamak o artefact kind'ı için raporu kapatır (her iki durumda da hiçbir şey silinmez). Horizon'lar `forgelm purge --check-policy` ile raporlanır (salt-okunur; kaç tane bulursa bulsun 0 ile çıkar); hiçbir şey otomatik silinmez ya da engellenmez. ForgeLM `raw_documents/` veya `ingestion_output/` dizinlerini oluşturmaz; `raw_documents_retention_days` yalnızca kaynak dokümanları orada tutuyorsanız etkili olur. `enforce` ayrılmıştır: henüz hiçbir komut onu dikkate almaz, bu yüzden üç değer de `log_only` gibi davranır.
Herhangi bir horizon'u `0` olarak ayarlamak o artefact kind'ı için raporu kapatır (her iki durumda da hiçbir şey silinmez). Horizon'lar `forgelm purge --check-policy` ile raporlanır (salt-okunur; kaç tane bulursa bulsun 0 ile çıkar); hiçbir şey otomatik silinmez ya da engellenmez. ForgeLM `raw_documents/` veya `ingestion_output/` dizinlerini oluşturmaz; `raw_documents_retention_days` yalnızca kaynak dokümanları orada tutuyorsanız etkili olur. Hiçbir horizon `checkpoint-*` dizinlerini kapsamaz: bir koşum bunları `training.output_dir` altına kaydeder (`save_total_limit`'in tuttuğu kadarını) ve rapor bunları hiç listelemez; bu yüzden depolama katmanınız üzerinden kendiniz silin. `enforce` ayrılmıştır: henüz hiçbir komut onu dikkate almaz, bu yüzden üç değer de `log_only` gibi davranır.

## Kaldırıldı: `evaluation.staging_ttl_days`

Expand All @@ -100,9 +100,9 @@ Herhangi bir horizon'u `0` olarak ayarlamak o artefact kind'ı için raporu kapa
| `data.erasure_requested` | Herhangi bir `forgelm purge --row-id` / `--run-id` çağrısının ilk adımı, herhangi bir silmeden önce (`--check-policy` salt-okunur; audit event yaymaz) | `target_kind` ∈ `{row, staging, artefacts}`, `target_id` (row mode'da hash'lenmiş), `salt_source` (row mode), `corpus_path` (row), `output_dir` (run), `justification`, `dry_run` |
| `data.erasure_completed` | Başarılı silme bittiğinde | Tüm `requested` field'ları + `bytes_freed`, `files_modified`, `pre_erasure_line_number` (row mode), `match_count` (row mode) |
| `data.erasure_failed` | Disk operasyonu raise etti VEYA eşleşen satır/koşum bulunamadı VEYA çoklu-satır policy belirsizliği reddetti | Tüm `requested` field'ları + `error_class`, `error_message` |
| `data.erasure_warning_memorisation` | **Yalnız row modunda.** Row erasure × `--output-dir` altında doğrudan bir `final_model/` dizini mevcut. (Run modu memorisation uyarısı emit etmez — run-scoped `staging` / `artefacts` türleri tanım gereği promote-öncesi.) | Tüm `completed` field'ları + `affected_run_ids` |
| `data.erasure_warning_memorisation` | **Yalnız row modunda.** Row erasure × `--output-dir` altında ya da `--config` ile config'in adlandırdığı eğitim dizinlerinde bir model dizini, bir staging dizini veya bir `checkpoint-*` dizini mevcut. (Run modu memorisation uyarısı emit etmez — run-scoped `staging` / `artefacts` türleri tanım gereği promote-öncesi.) | Tüm `completed` field'ları + `affected_run_ids`, `memorisation_artefact_paths` |
| `data.erasure_warning_synthetic_data_present` | **Yalnız row modunda.** Row erasure × `output_dir`'de `synthetic_data*.jsonl` mevcut. (Run modu bu uyarıyı yüzeylemez — synthetic veri corpus-scoped'dur, run-scoped değil.) | Tüm `completed` field'ları + `synthetic_files` |
| `data.erasure_warning_external_copies` | **Yalnız row modunda, `--config` verildiğinde.** Yüklü config webhook block'u içeriyor. (Run modu `--config` yüklemez.) | Tüm `completed` field'ları + `webhook_targets` |
| `data.erasure_warning_external_copies` | **Yalnız row modunda, `--config` verildiğinde.** Yüklü config webhook block'u içeriyor. (Run modu `--config`'i yalnızca staging dizinini adlandırmak için `training.final_model_dir` değerini okumak üzere yükler.) | Tüm `completed` field'ları + `webhook_targets` |

## Silme sonrası zincir doğrulama

Expand Down
Loading
Loading