Repository navigation
[Wave 4] Low/info — Module sweep: Trainer core, OOM recovery, auto-revert & GRPO rewards (part 3/4) #710
Description
Wave 4 · 20 finding(s) in 20 fix package(s). Fix these whenever a pull request touches this module ("clean the module you touch").
Findings
-
CR-A02-121 (code, Low) — With merge_adapters: true, save_final_model merges the live model in place, so any later training of that model object trains zero parameters and still reports success
severity Low · reachabilityundocumented· intentintended-but-harmful· confidence confirmed · independently confirmed · reproduced end to end
Failure scenario. A user runs a learning-rate sweep or a retry with merge_adapters: true. Every cell after the first trains nothing yet reports success with identical metrics, so the sweep's conclusions, and any model shipped from a later cell, are wrong.
Where.forgelm/trainer.py:1733-1738,forgelm/trainer.py:1735,forgelm/trainer.py:1737,forgelm/trainer.py:1009-1020
Recommended fix. Do not mutate the live model when saving a merged copy: merge a copy, or merge, save and then unmerge. Alternatively mark the instance or model as consumed and refuse further training. In either case, fail fast in _build_trainer/train() when the model has no trainable parameters, instead of running a no-op that reports success. -
CR-A02-122 (code, Low) — execute_evaluation_checks deletes any directory the caller passes as final_path, including paths outside output_dir and output_dir itself (with the audit log), and runs before train()
severity Low · reachabilitydocumented-opt-in· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Failure scenario. An orchestrator re-runs the gate through the public method with a path built from a variable (empty string, '.', the output dir, or a parent directory). The method recursively deletes that tree, which can include the run's tamper-evident audit log or unrelated data outside the run directory.
Where.forgelm/trainer.py:785-805,forgelm/trainer.py:863,forgelm/trainer.py:883,forgelm/trainer.py:902
Recommended fix. In _revert_model, resolve realpath(final_path) and refuse to delete it unless it lies strictly inside realpath(self.checkpoint_dir), is not that directory, and does not contain audit_log.jsonl. Preferably also require that it is a path this instance passed to save_final_model. Either make execute_evaluation_checks private, or document it with its preconditions and destructive effect. -
CR-A02-123 (code, Low) — GRPO's built-in reward chain is chosen by a runtime dataset probe and recorded nowhere, so no artefact shows whether the run optimised format/length only or also gold_answer correctness
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · not reproduced (code trace)
Failure scenario. Two GRPO runs with byte-identical YAML, one on a dataset with gold_answer and one without, optimise different objectives (structure-only vs correctness). Their manifests and Annex IV artefacts are identical apart from the dataset fingerprint. For a local directory corpus there is no content hash either (already reported), so the objective cannot be recovered at all.
Where.forgelm/trainer.py:1087-1115,forgelm/trainer.py:276-331,forgelm/grpo_rewards.py:160-161,docs/reference/configuration.md:520-523
Recommended fix. Record the resolved reward function names and weights in training_parameters (e.g. grpo_reward_source: builtin_format_length[+gold_answer_correctness] | model:) and emit them in an audit event. -
CR-A02-124 (code, Low) — SOC2 CC7.3 says pipeline.failed carries error_class + error_message, but it emits only
error; CC4.2 names events that do not exist (pipeline.reverted / pipeline.erasure_failed)
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · independently confirmed · reproduced with stubs
Failure scenario. A deployer wires the SOC2 CC7.3 incident-evaluation rule asevent == pipeline.failed and error_class in {...}. Every real training failure has no error_class, so the rule never fires.
Where.docs/reference/soc2_trust_criteria_mapping.md:41,docs/reference/soc2_trust_criteria_mapping.md:55,docs/reference/soc2_trust_criteria_mapping-tr.md:41,docs/reference/soc2_trust_criteria_mapping-tr.md:55
Recommended fix. Either change CC7.3/CC4.2 (EN and TR) to the real names and payloads (pipeline.failed:error; model.reverted / pipeline.stage_reverted; data.erasure_failed), or add error_class plus a sanitised, bounded error_message to pipeline.failed (reusing _purge._sanitise_audit_error_message) and update the catalog in the same change. -
CR-A02-125 (code, Low) — SOC2 A1.2 claims an 'auto_revert swap-back' recovery control; auto-revert only deletes the final model directory and restores nothing
severity Low · reachabilitydocumented-opt-in· intentcontract-violation· confidence likely · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN DOCS-04 — docs/roadmap/phase-16-trust-surface-hardening.md:760-765,778-780 (S15 open; unit id inferred)
Failure scenario. An operator retrains into the same output_dir and relies on A1.2's 'swap-back' to keep the last good model if the gate fails. The gate fails, final_model/ is deleted, and the earlier model is gone with it.
Where.docs/reference/soc2_trust_criteria_mapping.md:70,docs/reference/soc2_trust_criteria_mapping-tr.md:69,forgelm/trainer.py:960-1007,forgelm/trainer.py:1606-1621
Recommended fix. Reword A1.2 (EN and TR, and the design doc) to 'auto_revert discards the regressed artefacts (deletes final_model/); recovery means redeploying a previously archived model', or implement a real swap-back (stage to a sibling directory and promote only on gate pass, as the approval path already does). -
CR-A02-126 (code, Low) — Built-in GRPO length shaping saturates at 200 characters, so it is constant within a group and adds no gradient, contrary to its stated purpose
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · reproduced (plumbing only)
Failure scenario. In early training, before a model reliably varies its Answer: format, all-long completions give zero advantage and the step is a no-op while the run reports success.
Where.forgelm/grpo_rewards.py:156,forgelm/grpo_rewards.py:193-220,forgelm/trainer.py:1098-1102
Recommended fix. Make the docstring honest. Alternatively, scale saturation to max_completion_length or use a shaping term that varies among long completions, and warn when frac_reward_zero_std stays at 1 for N steps. -
CR-A02-127 (code, Low) — Extension of M-A17a-031: the TR mirror carries the same stale Stable signatures and the same crashing samples on identical lines, and the guard passes it as well
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end
Failure scenario. A Turkish-reading integrator copies TR:175trainer = ForgeTrainer(config)and gets TypeError (missing tokenizer/config/dataset). A CI author copies TR:146-150 and gets a gate that fails on every log, including correctly HMAC-signed ones, so they are pushed to drop require_hmac. Both stale states ship with a green guard on both mirrors.
Where.docs/reference/library_api_reference-tr.md:46,docs/reference/library_api_reference-tr.md:54,docs/reference/library_api_reference-tr.md:55,docs/reference/library_api_reference-tr.md:56
Recommended fix. Cite the TR lines above in M-A17a-031. Fix EN and TR together: the 4-argument ForgeTrainer constructor with get_model_and_tokenizer/prepare_dataset,config_path, the missing kwargs, the TrainResult fields, andreason: str(default ''). Pass the secret in the verify_audit_log sample (e.g. from os.environ['FORGELM_AUDIT_SECRET']). Extend check_library_api_doc.py to compare inspect.signature par… -
CR-A02-128 (code, Low) — compliance_report.json records the safety verdict but not the benchmark or judge verdict; training_manifest.yaml (read by approve) drops benchmark scores and every verdict
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Failure scenario. An auditor reads compliance_report.json for a run whose benchmark gate failed and was kept. Average 0.5 is recorded, but whether it passed or failed has to be reconstructed by hand from config.evaluation.benchmark.min_score. The approver's summary file shows no benchmark result at all.
Where.forgelm/trainer.py:2034-2048,forgelm/results.py:18,forgelm/compliance.py:1620-1625,forgelm/compliance.py:2178-2190
Recommended fix. Record passed (and threshold) for every gate in evaluation_results, using result.benchmark_passed and a judge passed flag. Include benchmark scores and per-gate verdicts in training_manifest.yaml. -
CR-A02-129 (code, Low) — The one deterministic preflight that exists (lm-eval missing) exits 2, the CI-retry class, runs only after the model load, and is skipped by --dry-run
severity Low · reachabilitydocumented-opt-in· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Failure scenario. A CI job treats exit 2 as transient and retries. Each retry reloads the model and fails again on the same missing extra. A --dry-run gate placed before the job does not catch it.
Where.forgelm/trainer.py:396-411,forgelm/benchmark.py:28-36,forgelm/cli/_training.py:141-148,forgelm/cli/_training.py:163-179
Recommended fix. Wrap the preflight ImportError in ConfigError (exit 1) and keep the install hint. Run the gate preflights from --dry-run, and before get_model_and_tokenizer where possible. -
CR-A02-130 (code, Low) — Documented-allowed pipeline/stage equality: the stage's Art. 12 export record lists pipeline_manifest.json as its own artefact, and the docs and docstring understate the sharing
severity Low · reachabilitydocumented-opt-in· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Failure scenario. An auditor reading the stage's compliance.artifacts_exported record attributes pipeline_manifest.json to the stage export. A SIEM rule following the guide (pipeline.* = orchestrator) counts stage-level pipeline.completed events as pipeline completions. An operator who follows the docstring and rm -rf's the stage dir deletes the pipeline state, manifest and audit log.
Where.forgelm/trainer.py:2139-2147,docs/guides/pipeline.md:277-285,forgelm/cli/_pipeline.py:213-222,forgelm/config.py:2110-2117
Recommended fix. Record the files export_compliance_artifacts actually wrote rather than a directory listing. Reword the guide paragraph to say the stage's pipeline.initialized and pipeline.completed also land in the shared file, and that the state, manifest and log share the stage dir's lifetime. Align the field description with the guide, or refuse the layout. -
CR-A02-131 (code, Low) — neftune_noise_alpha: 0.0 is accepted, installs the hook and logs 'NEFTune enabled'
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Failure scenario. An operator who 'disables' NEFTune by setting 0.0, rather than null, still has the hook active and gets a log line claiming NEFTune is enabled.
Where.forgelm/trainer.py:538-542,forgelm/wizard/_collectors.py:605-609,forgelm/config.py:582-585
Recommended fix. Treat 0.0 as disabled (skip forwarding and the log line), or require gt=0 in the schema and wizard. -
CR-A02-132 (code, Low) — Quickstart never audits its own bundled dataset, so every quickstart training run logs the Article 10 'No data_audit_report.json' WARNING and appends compliance.governance_section_missing to the audi…
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · reproduced (plumbing only)
Failure scenario. A first-time user runsforgelm quickstart customer-support. Training succeeds, but stderr shows a compliance WARNING and the append-only audit log permanently records a governance gap for data ForgeLM itself ships and could have audited.
Where.forgelm/trainer.py:1496-1506,forgelm/trainer.py:2093-2116,forgelm/compliance.py:668-704,forgelm/wizard/_byod.py:345-376
Recommended fix. Have run_quickstart (bundled path) write data_audit_report.json into training.output_dir with audit_dataset, or ship a precomputed report. Alternatively, make the WARNING and gap event say that quickstart seed data was not audited. -
CR-A02-133 (code, Low) — Docs show a single 'truthfulqa' score (and a single 'mmlu' score) that forgelm never produces
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end
Where.docs/usermanuals/en/evaluation/benchmarks.md:84,docs/usermanuals/en/evaluation/benchmarks.md:111-114,docs/usermanuals/en/getting-started/first-run.md:145,docs/usermanuals/en/operations/experiment-tracking.md:116-128
Recommended fix. Rewrite the sample output with the real keys (truthfulqa_mc1/mc2/gen, mmlu_* rows), or change the docs to recommend leaf task names. -
CR-A02-134 (code, Info) — IterableDataset support anticipated by the gold-answer probe is unreachable (
len(train)raises first), and the single-row probe can silently drop the correctness reward
severity Info · reachabilityundocumented· intentintended-but-harmful· confidence likely · single reviewer · reproduced end to end
Failure scenario. A library caller passing a streaming dataset gets a TypeError instead of the intended fallback. A GRPO dataset whose first (shuffled) row lacks gold_answer trains on format only.
Where.forgelm/trainer.py:289,forgelm/trainer.py:293-334,forgelm/trainer.py:431,forgelm/trainer.py:448
Recommended fix. Guard len() with a try/except TypeError (or hasattr len), or delete the dead iterator path. Probe for any non-empty gold_answer instead of row 0 only. -
CR-A02-135 (code, Info) — Non-finite loss ceiling without auto_revert: exit 3 with nothing reverted, final_model_path null while the model is on disk, and no terminal event or webhook
severity Info · reachabilityundocumented· intentintended-but-harmful· confidence likely · independently confirmed · reproduced with stubs · already tracked: PARTIALLY-FIXED C33-TRAIN-JSON-CONTRACT — docs/roadmap/phase-16-trust-surface-hardening.md:291,308 (S2 done); CHANGELOG.md:106-109
Failure scenario. A library integrator who skips schema validation gets exit 3 together with a null model path, deletes nothing, and receives no webhook outcome.
Where.forgelm/trainer.py:837-864,forgelm/trainer.py:1622-1639,docs/standards/error-handling.md:24,docs/usermanuals/en/reference/json-output.md:193
Recommended fix. Keep final_model_path on the non-revert failure result, emit pipeline.failed (or a terminal event) and notify_failure, and document that exit 3 can occur without a revert. -
CR-A02-136 (code, Info) — Training failures are logged with a full traceback twice (library and CLI)
severity Info · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · not reproduced (code trace)
Failure scenario. Every crash prints two identical tracebacks to stderr, which makes log triage noisier.
Where.forgelm/trainer.py:1709,forgelm/cli/_training.py:170-177,forgelm/cli/_training.py:21-36,docs/standards/logging-observability.md:55
Recommended fix. Drop the traceback in one of the two layers. -
CR-A02-137 (code, Info) — Post-train evaluate() recomputes an eval loss that load_best_model_at_end already measured
severity Info · reachabilitydefault· intentintended-and-acceptable· confidence likely · independently confirmed · not reproduced (code trace)
Failure scenario. None; only extra time.
Where.forgelm/trainer.py:1587-1588,forgelm/trainer.py:1272-1304
Recommended fix. Optional: reuse state.best_metric when the best checkpoint is loaded, or accept the cost. No change needed unless validation sets are very large. -
CR-A02-138 (code, Info) — Behavioural changes on stable symbols between v0.11.0 and cab2947 are not reflected in any API-version note, in a PATCH-labelled release
severity Info · reachabilitydefault· intentintended-but-harmful· confidence likely · single reviewer · reproduced end to end
Where.forgelm/trainer.py:_loss_gate_reverted,forgelm/judge.py:611,forgelm/judge.py:817,forgelm/compliance.py:567
Recommended fix. Record these in the release notes under Changed/Breaking and decide whether the release should be MINOR rather than PATCH given the stricter validation and the gate default change. -
CR-A02-139 (code, Info) — No completion-only or assistant-only loss is possible; system and user tokens dominate SFT loss and the eval-loss gate
severity Info · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Failure scenario. On long-system-prompt corpora, most of the gradient fits the static system prompt. The eval-loss gate barely reflects answer quality.
Where.forgelm/trainer.py:690-718,forgelm/data.py:479-487
Recommended fix. Keep conversational and prompt-completion structure through to TRL and expose completion_only_loss / assistant_only_loss in TrainingConfig. -
CR-A02-140 (code, Info) — Model/run names derived with split('/') are wrong for Windows paths and trailing-slash paths
severity Info · reachabilitydefault· intentintended-but-harmful· confidence likely · single reviewer · not reproduced (code trace)
Failure scenario. Webhook titles and compliance documents show 'C:\models\qwen_finetune' or '_finetune'.
Where.forgelm/trainer.py:347,forgelm/model_card.py:314,forgelm/compliance.py:1693
Recommended fix. Use os.path.basename(os.path.normpath(...)) after normalising backslashes.
Done when
- Every finding above is fixed or closed with a reason (tick it here).
- Code fixes carry a test; doc fixes update the Turkish mirror in the same PR.
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 fieldsTodo
Activity