Skip to content

[Wave 3] Medium — Security-contract fixes: Exit-code and error/output contract (part 2/2) #643

Open

Description

@cemililik

Wave 3 · 26 finding(s) in 18 fix package(s). Medium-severity findings that touch the security contract (exit codes, secrets, paths, provenance): do these before the rest of wave 3.

Findings

  • CR-A08-012 (code, Medium) — --generate-data catches deterministic config, policy and auth errors per prompt, retries every seed and exits 2 instead of failing fast with a config error
    severity Medium · reachability default · intent intended-but-harmful · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. CI runs generate-data with a typo'd or http:// api_base, or an unset OPENAI_API_KEY. A 5,000-prompt seed file is walked end to end (api_delay sleeps, or repeated model-load attempts for the local backend), and the job reports a training error (2) instead of a config error (1).
    Where. forgelm/synthetic.py:66-72, forgelm/synthetic.py:221-222, forgelm/synthetic.py:252-253, forgelm/cli/_no_train_modes.py:180-262
    Recommended fix. Let HttpSafetyError and the missing api_base/teacher-load errors abort the run immediately and map them to EXIT_CONFIG_ERROR; add config-time validation (api backend requires api_base; warn or fail when the api_key_env variable is unset); cache a load failure so it is not retried per prompt.

  • CR-A09-011 (code, Medium) — FORGELM_AUDIT_SECRET set to an empty string makes verify-audit report HMAC mismatch with exit 6 on a legitimate log
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. In CI, FORGELM_AUDIT_SECRET: ${{ secrets.AUDIT }} expands to an empty string because the secret is missing. verify-audit pages security with exit 6 for an intact log.
    Where. forgelm/cli/subcommands/_verify_audit.py:230-236, forgelm/cli/subcommands/_verify_audit.py:180, forgelm/compliance.py:2418-2427, forgelm/compliance.py:2418-2429
    Recommended fix. Normalise an empty secret to None at the CLI (os.getenv(var) or None) and in _verify_audit_log_classified, matching the writer and the pipeline corroborator (compliance.py:3437).

  • CR-A09-012 (code, Medium) — verify-annex-iv --pipeline cannot exit 0 without FORGELM_AUDIT_SECRET and a root audit log tagged on every line; the reference doc and --help omit this
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. An operator follows the reference page and gates CI on exit 0 for verify-annex-iv --pipeline. Without a secret, every valid run exits 1, and the operator is pushed to weaken the gate. With a secret, a single historical unkeyed run in the shared output dir blocks every later verification.
    Where. forgelm/verify/_pipeline_evidence.py:531-535, forgelm/compliance.py:3396-3404, forgelm/compliance.py:3437-3443, forgelm/cli/subcommands/_verify_annex_iv.py:125-133
    Recommended fix. Update the reference page (EN and TR) and --help to describe the corroboration tier, its secret prerequisite and audit_corroboration. Consider requiring tags only on the lines for this run's pipeline_run_id. Decide deliberately whether to accept a --hmac-secret-env flag.

  • CR-A09-020 (code, Medium) — verify-annex-iv --pipeline on a live or interrupted run: the manifest is read before the log, so an untampered in-progress run can exit 6 'status was altered after the run'; a crashed in_progress run…
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. A monitoring job runs verify-annex-iv --pipeline periodically during a long pipeline. On the poll that straddles a stage completion it reports exit 6 'status altered after the run' on an untouched run. Conversely, a pipeline killed mid-stage verifies 'OK' with exit 0.
    Where. forgelm/verify/_pipeline_evidence.py:580, forgelm/compliance.py:3445, forgelm/compliance.py:3502-3507, forgelm/cli/_pipeline.py:696-703
    Recommended fix. Read the log before the manifest, or re-read the manifest and retry when the log is ahead. Report final_status 'in_progress' as UNVERIFIED (exit 1) or at least print it in the text verdict. Document that --pipeline is meant for finished runs.

  • CR-A10-026 (code, Medium) — Non-ConfigError AuditLogger construction failures in purge/approve/reject escape as raw tracebacks with exit 1 and no JSON envelope
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN C33-CLI-ERROR-ENVELOPE — docs/roadmap/phase-16-trust-surface-hardening.md:497-508 (S7 open)
    Failure scenario. A JSON-consuming CI wrapper gets empty stdout and exit 1 ('fix your command') for a corrupted or unreadable audit log that needs operator repair.
    Where. forgelm/cli/subcommands/_purge.py:716, forgelm/cli/subcommands/_purge.py:736-739, forgelm/cli/subcommands/_purge.py:852-854, forgelm/cli/subcommands/_purge.py:1010-1014
    Recommended fix. Catch (OSError, ValueError) around AuditLogger construction in purge/approve/reject and map them to _output_error_and_exit(..., EXIT_TRAINING_ERROR). After a promotion, widen approve's log_event catch from OSError to (OSError, ValueError).

  • CR-A11-029 (code, Medium) — One hostile JSONL row (deep nesting or huge integer) aborts the whole audit: _read_jsonl_split catches only JSONDecodeError
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. A Hub-sourced or scraped corpus contains one row with a 5000-digit number, or one deeply nested structure (deliberate or accidental). forgelm audit crashes with exit 1 and no report, so the secrets, PII and leakage results for the other rows are lost. An operator who reads exit 1 as 'bad path' per the docs looks in the wrong place.
    Where. forgelm/data_audit/_streaming.py:211-216 (confirmed), forgelm/data_audit/_splits.py:163-175 (confirmed), forgelm/cli/subcommands/_audit.py:200-231, forgelm/safety/_inputs.py:41-44
    Recommended fix. Catch (json.JSONDecodeError, ValueError, RecursionError) per line in _read_jsonl_split and count them as parse errors, as the docstring promises. Add a test with a >4300-digit integer row and a deeply nested row.

  • CR-A11-030 (code, Medium) — The JSON envelope breaks its documented contract: success:false from a gate carries no 'error' key, and report_path / source_input are not absolute
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN C33-CLI-ERROR-ENVELOPE — docs/roadmap/phase-16-trust-surface-hardening.md:497-508 (S7 open)
    Failure scenario. A generic CI consumer written to the global envelope contract reads .error when success is false, gets null, and reports an empty failure reason. Tooling that opens report_path from another working directory fails.
    Where. forgelm/cli/subcommands/_audit.py:247-257, docs/usermanuals/en/reference/json-output.md:16, docs/usermanuals/en/reference/json-output.md:311-312, forgelm/data_audit/_orchestrator.py:466-478
    Recommended fix. On a gate failure, add an 'error' string naming the failed gate(s). Emit report_path as an absolute path (or fix the docs), and fix the docs for source_input.

  • CR-A12-006 (code, Medium) — Operator-fixable OSErrors (typo'd --tokenizer, --output parent is a file) fall into the bare-OSError bucket and exit 2 (retry) instead of 1
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. Pipeline retry-on-2 policy repeats a failing job that can never succeed.
    Where. forgelm/cli/subcommands/_ingest.py:191-227 (confirmed), docs/reference/data_ingestion_architecture.md:541-544, forgelm/cli/subcommands/_ingest.py:191, forgelm/cli/subcommands/_ingest.py:213
    Recommended fix. Wrap tokenizer resolution in ingest_path so load failures raise ValueError (config) with the tokenizer name, keeping bare OSError for real I/O; or correct the doc table.

  • ↳ same root cause: DOC-D1B02-031 (docs, Low) — docs/reference/data_ingestion_architecture.md: wrong detail — OSError -> EXIT_CONFIG_ERROR (1)
    severity Low
    Where. docs/reference/data_ingestion_architecture.md:541-544 · type: wrong detail
    The document says. OSError -> EXIT_CONFIG_ERROR (1)
    What the code does. Only FileNotFoundError, PermissionError, IsADirectoryError and ValueError map to 1; other OSError maps to EXIT_TRAINING_ERROR (2); a bad --tokenizer lands in that bucket.
    Code references. forgelm/cli/subcommands/_ingest.py:191-227
    Proposed correction. Correct the table and consider reclassifying tokenizer-load errors (F-INGEST-03).
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • CR-A12-036 (code, Medium) — Token-mode paragraph/markdown chunking emits over-budget units whole and reports nothing, while --help and the guide present --chunk-tokens as a max_length bound
    severity Medium · reachability default · intent intended-but-harmful · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. An operator runs forgelm ingest docs/ --chunk-tokens 1024 --tokenizer Qwen/... to fit model.max_length=1024, as --help and the guide suggest. About 20% of rows (76% at small N) are longer than 1024 tokens, up to 2-6x. The run reports success with no hint. SFT then truncates each such row to its first 1024 tokens, so 9-80% of the corpus tokens never reach training, and the ingest summary never said so.
    Where. forgelm/ingestion.py:1907-1914, forgelm/ingestion.py:1945-1947 (paragraph token packer: '<= max_tokens or not current'), forgelm/ingestion.py:1823-1854, forgelm/ingestion.py:1845-1847 (markdown token packer, same condition)
    Recommended fix. Either split over-cap units, with a sentence-level or sliding fallback inside the unit, or keep them whole but count them: add notes_structured['chunks_over_token_cap'] plus the largest length, log one WARNING per run, and state in --help and in both token sections of the docs that the cap is soft for paragraph and markdown. Fix the self-contradictory docstring at ingestion.py:1911-1912.

  • CR-A13-029 (code, Medium) — Forgelm deploy: an operator-input encoding failure escapes as a traceback with exit 1 and no JSON envelope. The JSON-output manual promises exit 2 for this case.
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN C33-CLI-ERROR-ENVELOPE — docs/roadmap/phase-16-trust-surface-hardening.md:497-508 (S7 open)
    Failure scenario. A CI job runs forgelm deploy ... --system "$PROMPT" --output-format json | jq with a prompt that holds Latin-1 bytes. It gets an empty stdout, so jq fails, and exit 1, which its contract maps to 'config error'. The docs promised exit 2 and an envelope. Per json-output.md:18 the consumer misreads empty stdout as an argparse usage error.
    Where. forgelm/deploy.py:114-116, forgelm/deploy.py:288-311, forgelm/deploy.py:316-330, forgelm/cli/subcommands/_deploy.py:16-53
    Recommended fix. Either catch (OSError, UnicodeError/ValueError) for the write and return error_kind='input' for an encoding failure, or validate/encode --system up front. Add a dispatcher-level guard that emits {success:false,error} and exit 2 for any residual exception in JSON mode. Then make json-output.md:913 (EN and TR) state exactly which failures map to 1 and which to 2, and pin both with a subprocess test.

  • CR-A14-004 (code, Medium) — Quickstart exits 2 when the operator leaves the auto-launched chat with Ctrl-C after training succeeded
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. A new user finishes quickstart, chats, and presses Ctrl-C as the banner implies; the shell reports failure (exit 2) after a successful training run.
    Where. forgelm/cli/subcommands/_quickstart.py:181-185, forgelm/cli/_dispatch.py:120-121, forgelm/cli/subcommands/_quickstart.py:177, forgelm/cli/subcommands/_quickstart.py:181
    Recommended fix. Inside _run_quickstart_chat_subprocess, catch KeyboardInterrupt around subprocess.run (wait for the child, then return), or ignore SIGINT in the parent while the chat child runs.

  • CR-A14-014 (code, Medium) — Wizard exits 0 and prints 'To start training later' for a saved YAML that failed its own validate-on-exit
    severity Medium · reachability default · intent intended-but-harmful · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN CLI-12 — docs/roadmap/phase-16-trust-surface-hardening.md:503,510-512 (S7 open; unit id inferred from commit-3 ordering)
    Failure scenario. With start.yaml, answer synthetic=y and keep the defaults. Output: 'Config saved to: syn.yaml', then 'Wizard output failed schema validation: ... synthetic.enabled is true but no seeds are provided', then 'To start training later, run: forgelm --config syn.yaml'. EXIT=0.
    Where. forgelm/wizard/_orchestrator.py:1090-1099, forgelm/wizard/_orchestrator.py:1155-1195, forgelm/wizard/_collectors.py:1033-1035, forgelm/cli/_wizard.py:96
    Recommended fix. Make seed_file required (re-prompt) when synthetic is enabled, or offer inline seed_prompts. Then either return a non-zero code (1) when validate-on-exit fails, or at least suppress the misleading 'run later' hint and say the file must be fixed first.

  • CR-A15-005 (code, Medium) — cache-models: advertised local paths and malformed repo ids reach snapshot_download and exit 2 (runtime) instead of the documented 1 (config)
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN CLI-17/CLI-18 (S7 commit 2; inferred) — docs/roadmap/phase-16-trust-surface-hardening.md:499-501
    Failure scenario. CI retries a permanent operator typo as a transient failure; operators following the docs pass a directory and cannot stage it.
    Where. forgelm/cli/subcommands/_cache.py:222-235 (validator body :232-235), forgelm/cli/subcommands/_cache.py:338 is not the snapshot_download call. The call to snapshot_download_callable(repo_id=name, cache_dir=cache_dir) is in _download_one_model, whi…, forgelm/cli/subcommands/_cache.py:300-315, docs/reference/cache_subcommands.md:25,64
    Recommended fix. Validate with huggingface_hub.utils.validate_repo_id (exit 1) and drop 'or a local path' from message/docs, or implement a real local-path branch.

  • ↳ same root cause: DOC-D1B02-017 (docs, Low) — docs/reference/cache_subcommands.md: false claim — --model accepts a Hub id or local path; malformed model name is exit 1
    severity Low · already tracked: KNOWN-OPEN CLI-17/CLI-18 (inferred) — docs/roadmap/phase-16-trust-surface-hardening.md:500-501
    Where. docs/reference/cache_subcommands.md:25,64 · type: false claim
    The document says. --model accepts a Hub id or local path; malformed model name is exit 1
    What the code does. Local path and malformed ids are exit 2 from snapshot_download.
    Code references. forgelm/cli/subcommands/_cache.py:338, forgelm/cli/subcommands/_cache.py:222-236
    Proposed correction. Remove 'or local path'; validate ids (F6).
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • CR-A16-025 (code, Medium) — Docker image (including forgelm:full) never installs the [distributed] extra, so strategy: deepspeed fails inside the documented multi-GPU container
    severity Medium · reachability documented-opt-in · intent contract-violation · confidence confirmed · independently confirmed · not reproduced (code trace)
    Failure scenario. The document states: The forgelm:full image can run DeepSpeed/FSDP multi-GPU training.. Observed in the code: The Dockerfile installs only the qlora, eval and unsloth extras (Dockerfile:52-66). There is no distributed extra or INSTALL_DISTRIBUTED build arg, so strategy: deepspeed in that image raises the ImportError from _check_deepspeed_available (trainer.py:564-589). The image also has no configs/deepspeed for preset names (see the presets finding), because the WORKDIR is /workspace and the p…
    Where. Dockerfile:9, forgelm/trainer.py:564-589 (confirmed), Dockerfile:50-67 (confirmed), pyproject.toml:115-117
    Recommended fix. State that the stock image supports FSDP only. For DeepSpeed, build a derived image that runs 'pip install forgelm[distributed]' and copies the preset JSON, or add --build-arg INSTALL_DISTRIBUTED once the Dockerfile supports it.

  • ↳ same root cause: DOC-D1B03-011 (docs, Medium) — docs/reference/distributed_training.md: misleading — The forgelm:full image can run DeepSpeed/FSDP multi-GPU training.
    severity Medium · already tracked: KNOWN-OPEN C31-DOCKER-RUNBOOK — docs/roadmap/phase-16-trust-surface-hardening.md:694-696,708
    Where. docs/reference/distributed_training.md:195-200, 219-222 (Turkish mirror: docs/reference/distributed_training-tr.md (Docker section)) · type: misleading
    The document says. The forgelm:full image can run DeepSpeed/FSDP multi-GPU training.
    What the code does. The Dockerfile installs only the qlora, eval and unsloth extras (Dockerfile:52-66). There is no distributed extra or INSTALL_DISTRIBUTED build arg, so strategy: deepspeed in that image raises the ImportError from _check_deepspeed_available (trainer.py:564-589). The image also has no configs/deepspeed for preset names (see the presets finding), because the WORKDIR is /workspace and the package is…
    Code references. Dockerfile:9-12,52-66,97,107, forgelm/trainer.py:564-589
    Proposed correction. State that the stock image supports FSDP only. For DeepSpeed, build a derived image that runs 'pip install forgelm[distributed]' and copies the preset JSON, or add --build-arg INSTALL_DISTRIBUTED once the Dockerfile supports it.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • CR-MISC-011 (code, Medium) — Code does not match docs/usermanuals/en/evaluation/auto-revert.md: After a revert you have no fine-tuned model on disk
    severity Medium · reachability documented-opt-in · intent contract-violation · confidence confirmed · independently confirmed
    Failure scenario. The document states: After a revert you have no fine-tuned model on disk. Observed in the code: checkpoint-N/ (with adapter_model.safetensors) survives the revert.
    Where. forgelm/trainer.py:960-1000 (_revert_model; rmtree only at :997), forgelm/trainer.py:1590-1593 (final_path = checkpoint_dir/final_model), docs/usermanuals/en/evaluation/auto-revert.md:11,133-137,170, forgelm/trainer.py:496,509-510 (HF TrainingArguments output_dir=checkpoint_dir, save_strategy=steps, save_total_limit)
    Recommended fix. Document surviving checkpoints or delete them on revert.

  • ↳ same root cause: DOC-D3B08-072 (docs, Low) — docs/usermanuals/en/evaluation/auto-revert.md: false claim — After a revert you have no fine-tuned model on disk
    severity Low
    Where. docs/usermanuals/en/evaluation/auto-revert.md:11,170 · type: false claim
    The document says. After a revert you have no fine-tuned model on disk
    What the code does. checkpoint-N/ (with adapter_model.safetensors) survives the revert.
    Code references. scripts/s7_gate.py [below]
    Proposed correction. Document surviving checkpoints or delete them on revert.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • CR-A16-058 (code, Low) — Ruff selection omits E722/BLE/S110, so bare except and except-Exception-pass pass CI despite the standard's enforcement claim
    severity Low · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. The document states: Bare except: is 'also caught by ruff B/E9' and 'Ruff E722 enforces this in CI.'. Observed in the code: The ruff selection is ['F','E9','W','B','I']. E722 is not selected (E9 covers syntax/IO errors only), and ruff's bugbear set flags no bare except. A file containing a bare except: pass passed ruff check (ruff 0.15.21) under the project config in a private copy. No other CI guard (bandit gates only HIGH) catches it.
    Where. pyproject.toml:307 (confirmed), tools/check_bandit.py:10 (confirmed), .github/workflows/ci.yml:44 (confirmed), docs/standards/error-handling.md:172
    Recommended fix. Fix the code: add "E722" (and optionally "BLE") to [tool.ruff.lint] select. Until then, replace 'Ruff E722 enforces this in CI' with 'Not yet enforced by CI (E722 is not in the ruff selection); reviewers must catch it.'

  • ↳ same root cause: DOC-D1B07-010 (docs, Medium) — docs/standards/error-handling.md: false claim — Bare except: is 'also caught by ruff B/E9' and 'Ruff E722 enforces this in CI.'
    severity Medium
    Where. docs/standards/error-handling.md:129,172 · type: false claim
    The document says. Bare except: is 'also caught by ruff B/E9' and 'Ruff E722 enforces this in CI.'
    What the code does. The ruff selection is ['F','E9','W','B','I']. E722 is not selected (E9 covers syntax/IO errors only), and ruff's bugbear set flags no bare except. A file containing a bare except: pass passed ruff check (ruff 0.15.21) under the project config in a private copy. No other CI guard (bandit gates only HIGH) catches it.
    Code references. pyproject.toml:[tool.ruff.lint] select = ["F", "E9", "W", "B", "I"], tools/check_bandit.py:5-10, .github/workflows/ci.yml:44
    Proposed correction. Fix the code: add "E722" (and optionally "BLE") to [tool.ruff.lint] select. Until then, replace 'Ruff E722 enforces this in CI' with 'Not yet enforced by CI (E722 is not in the ruff selection); reviewers must catch it.'

  • CR-A17a-032 (code, Low) — Standards promise two-way CLI --help <-> reference/cli.md parity that check_cli_help_consistency never enforces
    severity Low · reachability default · intent contract-violation · confidence confirmed · independently confirmed
    Failure scenario. The document states: check_cli_help_consistency.py: 'every flag in CLI --help output appears in docs/usermanuals/{en,tr}/reference/cli.md and vice-versa'.. Observed in the code: The tool only checks one direction: every forgelm <sub> --flag cited in docs/ fenced shell blocks (and README) must exist in the live parser (tools/check_cli_help_consistency.py:1-60, :729). It never reads reference/cli.md and has no parser-to-docs check. Evidence: 19 parser flags (e.g. purge --row-id/--run-id/--kind/…
    Where. tools/check_cli_help_consistency.py:1-36, tools/check_cli_help_consistency.py:729-778, docs/usermanuals/en/reference/cli.md, docs/standards/documentation.md:7 (confirmed)
    Recommended fix. Doc: 'every forgelm <sub> --flag cited in docs shell fences exists in the live argparse surface (docs-to-parser only)'. Alternatively extend the guard with the parser-to-cli.md reverse check the standard promises. The same wording is repeated in testing.md.

  • ↳ same root cause: DOC-D1B08-002 (docs, Medium) — docs/standards/documentation.md: false claim — check_cli_help_consistency.py: 'every flag in CLI --help output appears in docs/usermanuals/{en,tr}/reference/cli.md and vice-versa'.
    severity Medium
    Where. docs/standards/documentation.md:7 (Enforced-by list, check_cli_help_consistency) · type: false claim
    The document says. check_cli_help_consistency.py: 'every flag in CLI --help output appears in docs/usermanuals/{en,tr}/reference/cli.md and vice-versa'.
    What the code does. The tool only checks one direction: every forgelm <sub> --flag cited in docs/ fenced shell blocks (and README) must exist in the live parser (tools/check_cli_help_consistency.py:1-60, :729). It never reads reference/cli.md and has no parser-to-docs check. Evidence: 19 parser flags (e.g. purge --row-id/--run-id/--kind/--corpus, quickstart --dataset/--no-chat, export --format, reverse-pii --salt-…
    Code references. tools/check_cli_help_consistency.py:1-60, tools/check_cli_help_consistency.py:729-780, docs/usermanuals/en/reference/cli.md
    Proposed correction. Doc: 'every forgelm <sub> --flag cited in docs shell fences exists in the live argparse surface (docs-to-parser only)'. Alternatively extend the guard with the parser-to-cli.md reverse check the standard promises. The same wording is repeated in testing.md.

  • ↳ same root cause: DOC-D1B08-005 (docs, Medium) — docs/standards/testing.md: false claim — 'check_cli_help_consistency --strict — CLI --help output <-> docs/usermanuals/{en,tr}/reference/cli.md parity'.
    severity Medium
    Where. docs/standards/testing.md:195 and :271 (cli help guard) (Turkish mirror: docs/standards/documentation.md:7) · type: false claim
    The document says. 'check_cli_help_consistency --strict — CLI --help output <-> docs/usermanuals/{en,tr}/reference/cli.md parity'.
    What the code does. One-directional check of docs-cited forgelm <sub> --flag invocations against the parser; does not compare against reference/cli.md (see documentation.md finding; 19 parser flags absent from cli.md).
    Code references. tools/check_cli_help_consistency.py:1-60
    Proposed correction. 'every forgelm <sub> --flag cited in docs shell fences exists in the live parser'.

  • CR-MISC-038 (code, Low) — Code does not match docs/qms/sop_change_management.md: The PR review template (.github/PULL_REQUEST_TEMPLATE.md) MUST require an explicit checkbox confirming each gate green.
    severity Low · reachability default · intent contract-violation · confidence confirmed · independently confirmed
    Failure scenario. The document states: The PR review template (.github/PULL_REQUEST_TEMPLATE.md) MUST require an explicit checkbox confirming each gate green.. Observed in the code: The template is .github/pull_request_template.md (lower case). It has checkboxes only for pytest, ruff check, ruff format, new tests, a dry-run with the python -m form and a few generic items. It has none for field-description guard, HTTP discipline, site claims, bilingual parity, bandit, or pip-audit/SBOM, so it does not meet the SO…
    Where. .github/pull_request_template.md:21-39, docs/qms/sop_change_management.md:72-89, docs/qms/sop_change_management-tr.md:88-91
    Recommended fix. Correct the file name to pull_request_template.md and either extend the template with one checkbox per gate in the table, or reword to 'SHOULD reference the CI checks'.

  • ↳ same root cause: DOC-D1B03-007 (docs, Medium) — docs/qms/sop_change_management.md: false claim — The PR review template (.github/PULL_REQUEST_TEMPLATE.md) MUST require an explicit checkbox confirming each gate green.
    severity Medium
    Where. docs/qms/sop_change_management.md:86-89 (Turkish mirror: docs/qms/sop_change_management-tr.md (section 4 intro)) · type: false claim
    The document says. The PR review template (.github/PULL_REQUEST_TEMPLATE.md) MUST require an explicit checkbox confirming each gate green.
    What the code does. The template is .github/pull_request_template.md (lower case). It has checkboxes only for pytest, ruff check, ruff format, new tests, a dry-run with the python -m form and a few generic items. It has none for field-description guard, HTTP discipline, site claims, bilingual parity, bandit, or pip-audit/SBOM, so it does not meet the SOP's own MUST.
    Code references. .github/pull_request_template.md:1-36
    Proposed correction. Correct the file name to pull_request_template.md and either extend the template with one checkbox per gate in the table, or reword to 'SHOULD reference the CI checks'.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

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
    bugSomething isn't working
    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
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

    area: data-auditCode area: data-auditarea: dev-toolingCode area: dev-toolingarea: gdpr-approvalCode area: gdpr-approvalbacklog: knownAlready tracked in docs/roadmapbugSomething isn't workingdocumentationImprovements 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