Repository navigation
[Wave 3] Medium — Module sweep: Packaging, container, CI/CD workflows & supply chain #566
Description
Wave 3 · 13 finding(s) in 11 fix package(s). Fix these whenever a pull request touches this module ("clean the module you touch").
Findings
-
CR-A16-010 (code, Medium) — Documented 140-prompt safety library configs/safety_prompts/ is not shipped in the wheel or Docker image
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Failure scenario. A pip user copies the documentedtest_prompts: configs/safety_prompts/general_safety.jsonl; safety evaluation then cannot find its prompt file and fails at the safety gate, after training.
Where.pyproject.toml:257-278,docs/guides/safety_compliance.md:107,118,docs/reference/configuration.md:211,docs/reference/architecture.md:94
Recommended fix. Either package the sets (forgelm/safety_prompts/) and expose them by name, or change the docs to say they exist only in a source checkout and recommendforgelm safety-eval --default-probes/ the packaged default_probes.jsonl for installed use. -
↳ same root cause: DOC-D1B01-039 (docs, Low) — docs/reference/configuration.md: misleading —
test_prompts... Built-in sets inconfigs/safety_prompts/
severity Low
Where.docs/reference/configuration.md:211(Turkish mirror:docs/reference/configuration-tr.md) · type: misleading
The document says.test_prompts... Built-in sets inconfigs/safety_prompts/
What the code does. The built-in sets are not available in an installed package; the default 'safety_prompts.jsonl' is a CWD-relative file that nothing ships.
Code references.pyproject.toml:257-278
Proposed correction. Same as above; also mention that the packaged default_probes.jsonl is addressed by --default-probes.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
↳ same root cause: DOC-D2B02-068 (docs, Low) — docs/guides/safety_compliance.md: false claim — ForgeLM ships with curated adversarial prompt sets in
configs/safety_prompts/
severity Low
Where.docs/guides/safety_compliance.md:107(Turkish mirror:docs/guides/safety_compliance-tr.md) · type: false claim
The document says. ForgeLM ships with curated adversarial prompt sets inconfigs/safety_prompts/
What the code does. The six files are not in the wheel. Afterpip install forgelmonly forgelm/safety_prompts/default_probes.jsonl (51 prompts) exists.
Code references.pyproject.toml:268-270,forgelm/safety_prompts/default_probes.jsonl
Proposed correction. Package the sets, or state that they exist only in a source checkout and point installed users to --default-probes / default_probes.jsonl.
Turkish mirror. the Turkish page has the same error; fix both in the same PR -
CR-A16-011 (code, Medium) — Gguf is used by verify-gguf but declared in no extra; the docs call it an 'optional extra'; a skipped metadata layer is not stated in the verdict
severity Medium · reachabilitydefault· intentintended-but-harmful· confidence confirmed · independently confirmed · reproduced (plumbing only) · already tracked: KNOWN-OPEN TRUST-04 — docs/roadmap/phase-16-trust-surface-hardening.md:419-424 (S5 open)
Failure scenario. A pipeline installs forgelm[export] and assumes metadata validation runs. verify-gguf prints OK and exits 0 with only the magic check and the sidecar, so a structurally corrupt GGUF with a matching or absent sidecar passes more weakly than the operator believes. Without a sidecar, a file with only the magic bytes 'GGUF' plus junk also exits 0 when gguf is absent.
Where.pyproject.toml:126-131,forgelm/verify/_gguf.py:201-206,forgelm/verify/_gguf.py:240-248,docs/reference/verify_gguf_subcommand.md:158
Recommended fix. Add an explicit extra (for example verify = ["gguf>=0.10,<1"]) or name it in the docs by its real install command, and append 'metadata check skipped (gguf package not installed)' to the success reason. Gain: the verdict is honest and reproducible; no runtime cost. -
CR-A16-015 (code, Medium) — DeepSpeed presets hard-code optimizer (AdamW) and WarmupLR scheduler blocks, silently overriding ForgeLM's optim/GaLore and LR schedule
severity Medium · reachabilitydefault· intentintended-but-harmful· confidence confirmed · independently confirmed · not reproduced (code trace)
Failure scenario. A multi-GPU run reproduces a single-GPU config but trains at constant peak LR after warmup, or with plain AdamW instead of the configured GaLore. Nothing warns, and the run records claim the configured setup.
Where.configs/deepspeed/zero2.json:13-29(confirmed),configs/deepspeed/zero3.json:13-29(confirmed),configs/deepspeed/zero3_offload.json:13-29(confirmed),forgelm/trainer.py:544-562
Recommended fix. Drop the optimizer and scheduler blocks from the presets so HF builds both from TrainingArguments (HF supports DS ZeRO with HF optimizer and scheduler). Otherwise, reject galore_enabled together with strategy: deepspeed and document the scheduler difference. -
CR-A16-016 (code, Medium) — .dockerignore misses secrets, data, weights, audit logs and salts, so
COPY . .bakes them into the image
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · not reproduced (code trace)
Failure scenario. An operator follows the README/Dockerfile flow from the repo root, which holds.env(HF_TOKEN, FORGELM_AUDIT_SECRET, webhook URL),.hf_token,my_config.yamlwith inline auth.hf_token, and./data/*.jsonlwith personal data.docker compose buildordocker build .copies all of them into /app in the image. Pushing the image to a shared registry then leaks the HF token, the audit HMAC secret (which defeats audit-log tamper evidence) and the training data.
Where..dockerignore:1-68,Dockerfile:87-89,.gitignore,Dockerfile:12-15
Recommended fix. Switch to an allowlist (*followed by!forgelm/ !pyproject.toml !README.md !LICENSE), or at least add the .gitignore secret and data entries. -
CR-A16-018 (code, Medium) — Release matrix tests the git checkout, not the wheel it claims to validate
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN OPS-01 — docs/roadmap/phase-16-trust-surface-hardening.md:642-652,871-875 (remedy narrowed, C-12)
Failure scenario. A future change drops a sub-package or package-data glob from the wheel, or a stale build/lib adds a shadow module. All 12 matrix cells pass because the tests import the source tree, and publish uploads a broken wheel. Users then hit ModuleNotFoundError or FileNotFoundError (e.g.forgelm safety-eval --default-probes).
Where..github/workflows/publish.yml:65-73,.github/workflows/publish.yml:87-102,tests/__init__.py,.github/workflows/nightly.yml:127-213
Recommended fix. Run the matrix tests against the installed wheel: use--import-mode=importlibwith the forgelm/ tree moved or removed, run from a copy of tests/ outside the checkout, or assertforgelm.__file__is under site-packages at session start. Extend wheel-install-smoke to check every package-data group. -
CR-A16-019 (code, Medium) — publish.yml publishes any pushed v* tag from any ref with only a pytest gate; no version/branch/lint/guard/bandit/pip-audit check, while docs and a test rationale claim audit gating
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · not reproduced (code trace) · already tracked: KNOWN-OPEN OPS-02..19 (S12) — docs/roadmap/phase-16-trust-surface-hardening.md:653-659,662-664
Failure scenario. A maintainer tags a commit before bumping pyproject, or a collaborator pushes a v* tag from the development branch. The matrix passes (it tests the checkout, see the related finding), and PyPI permanently receives a mis-versioned or unreviewed release that never passed ci.yml's guards or a fresh pip-audit. PyPI versions cannot be re-uploaded.
Where..github/workflows/publish.yml:9-11,.github/workflows/publish.yml:113-136,tests/test_guard_wiring.py:80-84(false claim at :83),docs/standards/release.md:309(states pip-audit is NOT run by publish.yml; the false part is the 24h-audited assurance)
Recommended fix. Add a first job asserting${GITHUB_REF_NAME#v} == pyproject versionand that the SHA is an ancestor of origin/main. Run (or require) the ci.yml validate/lint jobs and check_pip_audit against the tagged SHA. Correct the test_guard_wiring rationale, release.md:309 and the GPG sentence. -
CR-A16-024 (code, Medium) — usermanuals-validate 'missing page' smoke gate can never fire: the workflow regex expects a quoted key, the builder emits an unquoted one
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Failure scenario. A section is renamed in _meta.yaml without moving its markdown. CI stays green and the live site shows placeholder pages.
Where..github/workflows/usermanuals-validate.yml:57,tools/build_usermanuals.py:316-328,tools/build_usermanuals.py:378-380(confirmed),tools/build_usermanuals.py:317-327
Recommended fix. Matchmissing:\s*true(unquoted), or have build_usermanuals.py exit non-zero, or emit a JSON manifest to check, plus a test that feeds a missing page through the workflow check. -
CR-A16-029 (code, Medium) — Terms page says dependency licence compatibility 'is documented in pyproject.toml'; it is not, and the [ingestion] extra pulls in AGPL-3.0+ EbookLib, which is never disclosed
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Where.site/js/translations.js:4445,pyproject.toml:144,forgelm/ingestion.py:1313,pyproject.toml:23
Recommended fix. Replace the pyproject claim with a real third-party licence table (or SBOM licence fields), and flag the AGPL EPUB dependency next to the [ingestion] extra and on the terms page. -
CR-A16-030 (code, Medium) — The image does not include lm-eval by default, so the recipe's
RUN forgelm cache-tasksexits 1; the docs say the image carries 'all extras' and never mention eval
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Failure scenario. docker build of airgap.Dockerfile on an image built with Dockerfile defaults stops at the cache-tasks RUN with exit 1, 'lm-eval is required for cache-tasks'.
Where.Dockerfile:57-61(ARG INSTALL_EVAL=false; [eval] installed only when it is set),Dockerfile:50-55,forgelm/cli/subcommands/_cache.py:405-415(ImportError -> EXIT_CONFIG_ERROR=1),docs/usermanuals/en/operations/docker.md:30('latest' = 'Base + all extras (ingestion, scale, export, deepspeed)')
Recommended fix. Make the recipe build its base with--build-arg INSTALL_EVAL=true(or addRUN pip install 'forgelm[eval]'). Correct the extras claims in docker.md:30 and installation.md:99 to match what the Dockerfile actually installs. -
CR-A16-033 (code, Medium) — Docs promise SPDX conversion through a one-line
cyclonedx-pycall, but no cyclonedx-py version reads a CycloneDX BOM or writes SPDX
severity Medium · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Failure scenario. A deployer's procurement or auditor asks for an SPDX SBOM. The compliance team follows supply_chain_security.md and installs cyclonedx-bom.cyclonedx-pyoffers no way to feed it sbom-*.json or to emit SPDX; at best it regenerates a new CycloneDX BOM from whatever local venv the team has, which describes a different environment. The documented fallback behind decision D-22-02 is unavailable, and the team has to find cyclonedx-cli (.NET) on its own.
Where.docs/reference/supply_chain_security.md:21-22,docs/reference/supply_chain_security-tr.md:21-22,docs/design/iso27001_soc2_alignment.md:431-434,docs/design/iso27001_soc2_alignment.md:787
Recommended fix. Replace the recipe with the cyclonedx-cli convert command, or with spdx-tools/sbom-utility where verified. Keep the EN/TR mirrors aligned. Reword D-22-02's 'Reversible?' column so it does not rest on cyclonedx-py. -
CR-A16-034 (code, Medium) — Documented Dependency-Track ingest command likely fails: raw octet-stream POST with no project identifier
severity Medium · reachabilitydefault· intentcontract-violation· confidence likely (static only) · independently confirmed · not reproduced (code trace)
Failure scenario. An auditor follows the documented command against a Dependency-Track instance; the upload fails, and the SBOM is never ingested.
Where.docs/reference/supply_chain_security.md:77-80,docs/reference/supply_chain_security.md:80,docs/reference/supply_chain_security-tr.md:77-80,docs/usermanuals/en/operations/supply-chain.md:31-34
Recommended fix. Use the documented DT form:curl -X POST -H "X-Api-Key: $DT_KEY" -F autoCreate=true -F projectName=forgelm -F projectVersion=<X.Y.Z> -F bom=@sbom.json .../api/v1/bom.
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
cemililik commentedon Oct 9, 2026
A change on
developmentthat affects this issue — verified 2026-10-09 ate469e24.Repository settings changed on 2026-10-09 bear on
CR-A16-019: only repository admins can createv*tags (the Release tags (v*) ruleset), andpublish.yml's publish job waits for a maintainer to approve thepypienvironment; release.md documents both (e469e24). A collaborator can no longer publish by pushing a tag, but the workflow still has no tag-vs-version, branch, guard or audit gate, so the finding stays open.