Skip to content

[Wave 3] Medium — Docs: repository root files #622

Open

Description

@cemililik

Wave 3 · 20 finding(s) in 20 fix package(s). Documents with fewer than five findings each in this wave, grouped by directory.

Findings

  • DOC-BLEGAL-053 (docs, Medium) — CODE_OF_CONDUCT.md: wrong detail — "This Code of Conduct is adapted from the Contributor Covenant, version 2.1."
    severity Medium
    Where. CODE_OF_CONDUCT.md:63 · type: wrong detail
    The document says. "This Code of Conduct is adapted from the Contributor Covenant, version 2.1."
    What the code does. Contributor Covenant 2.1 is licensed under CC BY 4.0. Its template designates the versioned URI (contributor-covenant.org/version/2/1/code_of_conduct.html) and credits Mozilla's code of conduct enforcement ladder for the Community Impact Guidelines. CC BY 4.0 s.3(a)(1) requires the licence notice/URI, which is missing here. The 'adapted' wording does indicate modification. Contributor Covenant 3.…
    Proposed correction. 'This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html, licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Community Impact Guidelines were inspired by Mozilla's code of conduct enfo…

  • DOC-BLEGAL-062 (docs, Medium) — config_template.yaml: wrong detail — # Post-Market Monitoring (Art. 12+17)
    severity Medium
    Where. config_template.yaml:258 · type: wrong detail
    The document says. # Post-Market Monitoring (Art. 12+17)
    What the code does. Post-market monitoring is Art. 72. Art. 12(2)(b) only requires logging that facilitates it, and Art. 17(1)(h) requires the QMS to include a PMM system 'in accordance with Article 72'. Reg. 2026/1744 amended Art. 72(3): the plan is part of the Annex IV documentation, and Commission guidance and a template are due by 2 Sep 2027.
    Proposed correction. '# Post-Market Monitoring (Art. 72; QMS Art. 17(1)(h))'. Fix forgelm/config.py:1280 and :1284 too.

  • DOC-D4B01-013 (docs, Medium) — CHANGELOG.md: misleading — "An enabled LLM-judge gate with an empty eval dataset now fails ... the run exited 0. Symmetric with the missing-file branch beside it, and with the safety orchestrator's…
    severity Medium · already tracked: PARTIALLY-FIXED CORE-04 docs/roadmap/phase-16-trust-surface-hardening.md:356-364; CHANGELOG.md:49-54
    Where. CHANGELOG.md:49-54 · type: misleading
    The document says. "An enabled LLM-judge gate with an empty eval dataset now fails ... the run exited 0. Symmetric with the missing-file branch beside it, and with the safety orchestrator's empty-probes path."
    What the code does. The judge now returns passed=False, but under the default auto_revert:false the training run still exits 0 because the failure is only recorded. Under auto_revert:true the judge's no-evidence failure deletes the model (_revert_model), whereas the safety empty-probes path sets evaluation_completed=False and withholds the revert. The two are not symmetric.
    Code references. forgelm/judge.py:609-621, forgelm/trainer.py:1488-1493, forgelm/trainer.py:1458-1460, forgelm/safety/_orchestrator.py:221-232
    Proposed correction. "...now returns passed: false. With auto_revert: true this fails the run (exit 3) and deletes the model — unlike the safety empty-probes path, which keeps it; with the default auto_revert: false the failure is recorded and the run still exits 0."

  • DOC-D4B01-014 (docs, Medium) — CHANGELOG.md: wrong detail — SyntheticDataGenerator.__init__ and WebhookNotifier.__init__: "Both are stable-tier callables"
    severity Medium
    Where. CHANGELOG.md:78-86 · type: wrong detail
    The document says. SyntheticDataGenerator.__init__ and WebhookNotifier.__init__: "Both are stable-tier callables"
    What the code does. Both are "experimental" in the stability-tier registry, confirmed at runtime: forgelm._STABILITY_TIERS[...] == 'experimental'.
    Code references. forgelm/__init__.py:170, forgelm/__init__.py:175
    Proposed correction. "Both are public (experimental-tier) callables, so this is visible to a downstream mypy --strict consumer..." (and fix the same sentence in forgelm/_version.py:53-54).

  • DOC-D4B02-008 (docs, Medium) — config_template.yaml: misleading — quantize_experts: true # quantize inactive experts for VRAM savings
    severity Medium
    Where. config_template.yaml:139 · type: misleading
    The document says. quantize_experts: true # quantize inactive experts for VRAM savings
    What the code does. _apply_moe_expert_quantization does not quantize. It casts the frozen base expert weights (all of them, not only 'inactive' ones) to float16/bfloat16, and it is skipped entirely when the model is loaded in 4-bit. load_in_4bit: true is this template's own setting (line 6).
    Code references. forgelm/model.py:501-513, forgelm/model.py:644-669, config_template.yaml:6
    Proposed correction. quantize_experts: true # cast frozen expert weights to fp16/bf16 for VRAM savings (no-op when load_in_4bit: true)

  • DOC-D4B02-010 (docs, Medium) — config_template.yaml: misleading — max_acceptable_loss: 0.8 # If final eval_loss is higher, reverts adapters
    severity Medium · already tracked: KNOWN-OPEN DOCS-04 docs/roadmap/phase-16-trust-surface-hardening.md:760-765
    Where. config_template.yaml:180 · type: misleading
    The document says. max_acceptable_loss: 0.8 # If final eval_loss is higher, reverts adapters
    What the code does. Auto-revert does not restore anything. With auto_revert: true, _revert_model rmtree's the saved final model directory, whether adapters or a merged full model, and emits an audit event and a webhook. The field description says "Nothing is restored". Without auto_revert, the breach is only logged.
    Code references. forgelm/trainer.py:886-903, forgelm/trainer.py:960-1007, forgelm/config.py:1191-1194
    Proposed correction. max_acceptable_loss: 0.8 # If final eval_loss is higher (and auto_revert: true), the saved model directory is deleted — nothing is restored

  • DOC-D4B03-002 (docs, Medium) — AGENTS.md: wrong detail — Inherits every CLAUDE.md discrepancy at identical line numbers: compliance export described as opt-in (L121), compliance.py scope (L71), mergekit/ImportError (L116), env-var…
    severity Medium
    Where. AGENTS.md:117,114,121,119,144,83,71,62,96,328,317,270 (Turkish mirror: CLAUDE.md (same lines)) · type: wrong detail
    The document says. Inherits every CLAUDE.md discrepancy at identical line numbers: compliance export described as opt-in (L121), compliance.py scope (L71), mergekit/ImportError (L116), env-var principle (L114), 127 test modules (L83), 23 models (L62), 4-section manual (L96), || true outlawed (L119,144), `--root doc…
    What the code does. See the CLAUDE.md entries; the text is identical apart from the allowed substitutions.
    Proposed correction. Apply the same edits to AGENTS.md as to CLAUDE.md. The mirror-parity guard requires identical content after substitution, and both are listed in tests/test_guard_wiring.py::_GAUNTLET_DOCS.

  • DOC-D4B03-003 (docs, Medium) — CLAUDE.md: wrong detail — Tree comment: compliance.py is 'EU AI Act Articles 9-17 + Annex IV + GDPR purge / reverse-pii primitives'.
    severity Medium
    Where. CLAUDE.md:71 · type: wrong detail
    The document says. Tree comment: compliance.py is 'EU AI Act Articles 9-17 + Annex IV + GDPR purge / reverse-pii primitives'.
    What the code does. compliance.py's own docstring covers Articles 9-15 only (Risk, Data governance, Annex IV, Record-keeping, Transparency, Human oversight, Integrity). It contains no purge or reverse-pii primitives. They live in forgelm/cli/subcommands/_purge.py (salt and hashing at :153-227, run_purge at :1510) and _reverse_pii.py (:472, :709). Article 17 (QMS) is covered only by the docs/qms templates.
    Code references. forgelm/compliance.py:1-7, forgelm/cli/subcommands/_purge.py:153, forgelm/cli/subcommands/_reverse_pii.py:472
    Proposed correction. Change to: 'EU AI Act Articles 9-15 + Annex IV, audit log, approval/integrity artefacts (GDPR purge and reverse-pii live in cli/subcommands/_purge.py and _reverse_pii.py)'.

  • DOC-D4B03-005 (docs, Medium) — CLAUDE.md: wrong detail — Principle 3: heavy deps 'bitsandbytes, unsloth, deepspeed, lm-eval, wandb, mergekit' live under [project.optional-dependencies] and raise ImportError with an install hint wh…
    severity Medium
    Where. CLAUDE.md:116 · type: wrong detail
    The document says. Principle 3: heavy deps 'bitsandbytes, unsloth, deepspeed, lm-eval, wandb, mergekit' live under [project.optional-dependencies] and raise ImportError with an install hint when missing.
    What the code does. mergekit is not an extra. pyproject.toml:121-126 says it was declared but never imported; merging = [] is a no-op extra, and merging is native on state dicts. ForgeLM itself raises the install-hint ImportError only for unsloth (model.py:291-295), deepspeed (trainer.py:581-586), lm-eval (benchmark.py:35, trainer.py:1772) and a few others. For bitsandbytes (model.py:441-447) and wandb it relies o…
    Code references. pyproject.toml:118-126, forgelm/model.py:291-295, forgelm/trainer.py:581-586, forgelm/benchmark.py:35
    Proposed correction. Remove mergekit from the list (merging is native; merging is a no-op extra) and soften to 'raise ImportError (with an install hint where ForgeLM guards the import)'. Alternatively add the missing hints for bitsandbytes and wandb in code.

  • DOC-D4B03-006 (docs, Medium) — CLAUDE.md: misleading — Principle 8: 'Enterprise features (compliance export, human approval, safety eval) are opt-in; new users aren't burdened.'
    severity Medium
    Where. CLAUDE.md:121 · type: misleading
    The document says. Principle 8: 'Enterprise features (compliance export, human approval, safety eval) are opt-in; new users aren't burdened.'
    What the code does. Safety eval, human approval and auto-revert are opt-in, but the compliance bundle is not. ForgeTrainer._finalize_artifacts (trainer.py:1496-1505) unconditionally calls _generate_model_card, _generate_model_integrity, _generate_deployer_instructions and _export_compliance_if_needed after every run. The compliance export has no config guard (trainer.py:2006 onwards), and the promise registry F-085…
    Code references. forgelm/trainer.py:1496-1505, forgelm/trainer.py:2006-2075
    Proposed correction. Reword: 'Safety eval, human approval and auto-revert are opt-in. The Annex IV / compliance bundle, audit log and model-integrity manifest are produced on every run; --compliance-export re-exports them on demand.'

  • DOC-D4B03-007 (docs, Medium) — CONTRIBUTING.md: wrong detail — Adding a trainer type, step 4: 'Add the trainer initialization in trainer.py:train()'.
    severity Medium
    Where. CONTRIBUTING.md:222 · type: wrong detail
    The document says. Adding a trainer type, step 4: 'Add the trainer initialization in trainer.py:train()'.
    What the code does. train() (forgelm/trainer.py:1661) only orchestrates. TRL trainer objects are built in ForgeTrainer._build_trainer (trainer.py:1009), _build_simple_trl_trainer (:1028, SFT/ORPO/DPO/SimPO via CPOTrainer/KTO) and _build_grpo_trainer (:1057). Step 3's _get_training_args_for_type() is correct (trainer.py:685).
    Code references. forgelm/trainer.py:1009, forgelm/trainer.py:1028, forgelm/trainer.py:1057, forgelm/trainer.py:1661
    Proposed correction. Change step 4 to: 'Add the trainer initialization in trainer.py:_build_simple_trl_trainer() (or a dedicated _build_<type>_trainer() like _build_grpo_trainer()), dispatched from _build_trainer().' Also mention the preference/column validation in data.py (_validate_trainer_columns, _passthrough_for_trainer).

  • DOC-D4B03-008 (docs, Medium) — CONTRIBUTING.md: misleading — Trainer-type step 6: update forgelm/wizard/_collectors.py 'and forgelm/wizard/_defaults.json if the new type needs its own defaults'.
    severity Medium
    Where. CONTRIBUTING.md:224 · type: misleading
    The document says. Trainer-type step 6: update forgelm/wizard/_collectors.py 'and forgelm/wizard/_defaults.json if the new type needs its own defaults'.
    What the code does. _defaults.json is generated and marked 'DO NOT EDIT BY HAND' (line 2). It is regenerated by tools/generate_wizard_defaults.py from json_schema_extra={'wizard': True} fields in config.py. tools/check_wizard_defaults_sync.py (a gauntlet guard) byte-compares it. It only holds model.max_length, lora r/alpha/dropout and the four common training hyper-parameters. It has no per-trainer defaults. Per-t…
    Code references. forgelm/wizard/_defaults.json:2-4, tools/generate_wizard_defaults.py, tools/check_wizard_defaults_sync.py, forgelm/wizard/_collectors.py:355-430
    Proposed correction. Replace the parenthesis with: '(shared wizard defaults are schema-derived: mark the field with json_schema_extra={"wizard": True} in config.py and run python tools/generate_wizard_defaults.py; never edit _defaults.json by hand)'.

  • DOC-D4B04-002 (docs, Medium) — .claude/skills/add-test/SKILL.md: broken example — notifier.send(...) with safe_post raising requests.RequestException must not raise; caplog contains 'webhook delivery failed'
    severity Medium
    Where. .claude/skills/add-test/SKILL.md:143-151 · type: broken example
    The document says. notifier.send(...) with safe_post raising requests.RequestException must not raise; caplog contains 'webhook delivery failed'
    What the code does. WebhookNotifier has no public send(); the public methods are notify_start/success/awaiting_approval/failure/reverted/pipeline_* (forgelm/webhook.py:429-644) over a keyword-only _send. A RequestException is logged as "Webhook transport error for event '%s' ..." (webhook.py:233-239); no log line contains 'webhook delivery failed', so the assertion would fail. The patch target forgelm.webhook.safe_p…
    Code references. forgelm/webhook.py:8, forgelm/webhook.py:233-239, forgelm/webhook.py:429-440
    Proposed correction. Use notifier.notify_failure("run", "reason") (or notify_start) with import requests, and assert "webhook transport error" in caplog.text.lower().

  • DOC-D4B04-003 (docs, Medium) — .claude/skills/add-test/SKILL.md: broken example — cli.main(["--config", "bad.yaml", "--dry-run"]) raises SystemExit with code 1
    severity Medium
    Where. .claude/skills/add-test/SKILL.md:213-218 · type: broken example
    The document says. cli.main(["--config", "bad.yaml", "--dry-run"]) raises SystemExit with code 1
    What the code does. forgelm/cli/_dispatch.py:156 defines main() with no parameters (calling it with a list raises TypeError, reproduced). It reads sys.argv, and tests/test_cli.py:179-186 patch sys.argv and call main(). The behavioural outcome (exit 1, 'config' in stderr logs) is right when run as a subprocess (reproduced: exit 1, '[ERROR] forgelm.cli: Configuration error: Config file not found').
    Code references. forgelm/cli/_dispatch.py:156-168, tests/test_cli.py:179-186
    Proposed correction. Show with patch("sys.argv", ["forgelm", "--config", "bad.yaml", "--dry-run"]): with pytest.raises(SystemExit) as exc: main() as tests/test_cli.py does.

  • DOC-D4B04-004 (docs, Medium) — .claude/skills/add-trainer-feature/SKILL.md: misleading — Worked example: TRAINER_REGISTRY dict mapping trainer types to run functions, from trl import IPOTrainer # optional import at the top…
    severity Medium
    Where. .claude/skills/add-trainer-feature/SKILL.md:83-103 · type: misleading
    The document says. Worked example: TRAINER_REGISTRY dict mapping trainer types to run functions, from trl import IPOTrainer # optional import at the top of trainer.py
    What the code does. forgelm/trainer.py has no TRAINER_REGISTRY or run* functions. Dispatch is if/elif chains in ForgeTrainer._build_training_args (trainer.py:686-783), _build_trainer (:1009), _build_simple_trl_trainer (:1028-1055) and _build_grpo_trainer (:1057), with TRL classes imported lazily inside the methods (trainer.py:10 note). TRL 1.7.1 (installed in the review env) has no IPOTrainer (IPO is a DPO loss ty…
    Code references. forgelm/trainer.py:686-783, forgelm/trainer.py:1009-1072
    Proposed correction. Rewrite step 2 to name the real wiring points (_build_training_args branch, _build_simple_trl_trainer branch, lazy TRL import inside the method), and pick an example whose TRL class exists, or state that the example is hypothetical.

  • DOC-D4B04-005 (docs, Medium) — .claude/skills/add-trainer-feature/SKILL.md: wrong detail — Extend _detect_dataset_format with if 'chosen' in first_row and 'rejected' in first_row: return 'preference_pairs'
    severity Medium
    Where. .claude/skills/add-trainer-feature/SKILL.md:105-111 · type: wrong detail
    The document says. Extend _detect_dataset_format with if 'chosen' in first_row and 'rejected' in first_row: return 'preference_pairs'
    What the code does. forgelm/data.py:23-48: _detect_dataset_format(columns: list) takes a column list, returns a dict {description, suggested_trainer}, and is explicitly advisory. The authoritative gate is _validate_trainer_columns (data.py:584, called at :722); a new trainer needs that function updated.
    Code references. forgelm/data.py:23-48, forgelm/data.py:584, forgelm/data.py:722
    Proposed correction. Describe extending _validate_trainer_columns (and, for the advisory hint, the dict returned by _detect_dataset_format).

  • DOC-D4B04-006 (docs, Medium) — .claude/skills/cut-release/SKILL.md: false claim — GPG-signed tag (-s) is required for trusted PyPI publishing
    severity Medium
    Where. .claude/skills/cut-release/SKILL.md:172 · type: false claim
    The document says. GPG-signed tag (-s) is required for trusted PyPI publishing
    What the code does. .github/workflows/publish.yml triggers on any pushed v* tag (tags: ['v*']) and the publish job uses OIDC (id-token: write, environment pypi, pypa/gh-action-pypi-publish) with no tag-signature check. PyPI trusted publishing does not verify tag signatures. docs/standards/release.md:152 only says the tag is GPG-signed (a project convention), not that PyPI requires it.
    Code references. .github/workflows/publish.yml:9-12, .github/workflows/publish.yml:118-146, docs/standards/release.md:152
    Proposed correction. Replace with: 'Sign the tag (-s) by project convention (release.md); publishing is authorised by the PyPI OIDC trusted-publisher setup and the pypi environment, not by the signature.'

  • DOC-D4B04-007 (docs, Medium) — .claude/skills/review-pr/SKILL.md: misleading — No [A-Za-z0-9_] (Sonar S6353); use \w
    severity Medium
    Where. .claude/skills/review-pr/SKILL.md:97 · type: misleading
    The document says. No [A-Za-z0-9_] (Sonar S6353); use \w
    What the code does. docs/standards/regex.md:11-45 (rule 1): \w is Unicode by default; for ASCII-only grammars (tokens, credentials) use \w WITH re.ASCII, or keep the explicit [A-Za-z0-9_] ('ALSO RIGHT'). Blindly replacing changes matching semantics (e.g. forgelm/data_audit/_secrets.py credential patterns).
    Code references. docs/standards/regex.md:11-45
    Proposed correction. Write: 'Prefer \w, but state Unicode intent: ASCII-only grammars use \w with re.ASCII (or the explicit class); natural-language text uses bare \w (regex.md rule 1).'

  • DOC-D4B04-008 (docs, Medium) — .claude/skills/review-pr/SKILL.md: wrong detail — Operator-controlled input: 10K-char pathological-input wall-clock benchmark stays <= 10ms
    severity Medium
    Where. .claude/skills/review-pr/SKILL.md:103 · type: wrong detail
    The document says. Operator-controlled input: 10K-char pathological-input wall-clock benchmark stays <= 10ms
    What the code does. docs/standards/regex.md:157 and :205 explicitly reject a hard ms cutoff: measure scaling at 1K/5K/10K characters, median of 5 runs, roughly linear growth; existing tests only assert generous floors (<=100 ms / <=1 s).
    Code references. docs/standards/regex.md:157, docs/standards/regex.md:205
    Proposed correction. Replace with 'pathological benchmark at 1K/5K/10K characters, median of N runs, approximately linear growth (regex.md ReDoS exposure budget)'; adjust L107 accordingly.

  • DOC-D4B04-009 (docs, Medium) — .claude/skills/sync-bilingual-docs/SKILL.md: false denial — Do not use for docs/qms/* (English only) and docs/guides/* (English only for now, Turkish is future work)
    severity Medium
    Where. .claude/skills/sync-bilingual-docs/SKILL.md:30-33 · type: false denial
    The document says. Do not use for docs/qms/* (English only) and docs/guides/* (English only for now, Turkish is future work)
    What the code does. docs/qms has 10 EN/TR pairs and docs/guides has 15, all registered in tools/check_bilingual_parity.py _PAIRS (lines ~66-90) and CI-enforced; localization.md:36,40 lists both as bilingual (only enterprise_deployment in guides is EN-only).
    Code references. tools/check_bilingual_parity.py:66-90, docs/standards/localization.md:36, docs/standards/localization.md:40
    Proposed correction. Move docs/guides/* (except enterprise_deployment.md) and docs/qms/* into the 'When to use' list; keep standards, design and marketing as English-only.

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.

Activity

  1. added
    documentationImprovements or additions to documentation
    backlog: knownAlready tracked in docs/roadmap
    severity: mediumReal defect with limited reach or a workaround
    source: review-2026-10Found by the October 2026 code and documentation review
    wave: 3Roadmap wave 3: medium severity, module sweeps
    on Oct 9, 2026
  2. cemililik commented on Oct 9, 2026

    @cemililik
    CollaboratorAuthor

    Progress on development — verified 2026-10-09 at 08f2426.

    Fixed (ticked in the list above):

    • DOC-D4B01-014 — the CHANGELOG and _version.py class both annotated __init__s as experimental-tier (08f2426)

    Everything else in this issue is unchanged.

  3. cemililik commented on Oct 9, 2026

    @cemililik
    CollaboratorAuthor

    Progress on development — verified 2026-10-09 at e469e24.

    Fixed (ticked in the list above):

    • DOC-D4B03-007 — step 4 of Adding a New Trainer Type now names trainer.py:_build_trainer(), where the TRL trainer is constructed, and _build_simple_trl_trainer() for the plain TRL trainers, instead of train() (e469e24)

    Everything else in this issue is unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backlog: knownAlready tracked in docs/roadmapdocumentationImprovements or additions to documentationseverity: mediumReal defect with limited reach or a workaroundsource: review-2026-10Found by the October 2026 code and documentation reviewwave: 3Roadmap wave 3: medium severity, module sweeps

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions