Repository navigation
[Wave 4] Low/info — Module sweep: CLI framework: parser, dispatch, exit-code contract, logging (part 2/3) #654
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-A05-046 (code, Low) — Ingest --help drift: --normalise-profile claims 'turkish' is the default and --keep-frontmatter quotes alpha < 0.45 / underscore thresholds the code does not use
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end
Failure scenario. An operator reading --help believes Turkish normalisation is always on, or misjudges which ToC pages will be dropped.
Where.forgelm/cli/_parser.py:360(confirmed),forgelm/cli/_parser.py:441,forgelm/_pypdf_normalise.py:91(confirmed),forgelm/ingestion.py:708(confirmed)
Recommended fix. Update the help strings and the architecture reference, and correct the F-034 promise wording. -
CR-A05-047 (code, Low) — Quickstart --output help and run_quickstart docstring describe a non-existent default path
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end
Failure scenario. CI script globs configs/customer-support-*.yaml after quickstart and finds nothing.
Where.forgelm/cli/_parser.py:179(confirmed),forgelm/quickstart.py:337,forgelm/quickstart.py:431(confirmed),forgelm/quickstart.py:329
Recommended fix. Change the help string and docstring to ./configs/-/config.yaml. -
CR-A05-048 (code, Low) — cache-models accepts 'local path' per help, but local paths fail with a runtime-class exit 2
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end · already tracked: KNOWN-OPEN CLI-18 — docs/roadmap/phase-16-trust-surface-hardening.md:480,501 (S7 open; unit id inferred from commit-2 ordering)
Failure scenario. CI retries on exit 2 forever for what is a usage error.
Where.forgelm/cli/_parser.py:998,forgelm/cli/subcommands/_cache.py:243-253,forgelm/cli/subcommands/_cache.py:336-346,docs/reference/cache_subcommands.md:25
Recommended fix. Drop 'or local path' from help and docs, or reject path-like names with exit 1. -
CR-A05-049 (code, Low) — Library error section describes exit codes 0/1/2/3/4; the public contract is 0-6
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · not reproduced (code trace) · already tracked: KNOWN-OPEN C31-EXIT-CODE-PROSE — docs/roadmap/phase-16-trust-surface-hardening.md:196-197,782-785,790-792 (S15 open)
Failure scenario. A reader mapping library results onto CLI behaviour omits exit 6 for integrity failures.
Where.docs/reference/library_api_reference.md:260,docs/reference/library_api_reference-tr.md:260,forgelm/cli/_exit_codes.py:10-14,forgelm/cli/_exit_codes.py:21
Recommended fix. Say 0 through 6 and link the table. -
CR-A05-050 (code, Low) — No test that a non-public SystemExit raised inside a dispatcher is clamped (G-A05-03 confirmed; the quickstart rc map is the sole barrier)
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · independently confirmed · reproduced end to end
Where.forgelm/cli/_dispatch.py:113-122,forgelm/cli/_dispatch.py:210,forgelm/cli/subcommands/_quickstart.py:148,tests/test_verification_toolbelt.py:1825-1830
Recommended fix. Add to tests/test_cli_subcommands.py: parametrize code over [7,137,143,-9] and over a few subcommands, patch the matchingforgelm.cli._run_<name>_cmdto sys.exit(code), call main() with the subcommand argv, and assert the exit code is 2 (or xfail(strict=True) until the seam clamps SystemExit). Add one case where the dispatcher exits with each public code (0-6) and assert pass-through, so a clam… -
CR-A05-051 (code, Low) — On Python 3.14.0-3.14.6
python -m forgelm[.cli]printspython3.14 -m forgelm...as prog: the sys.argv[0] normalisation is bypassed
severity Low · reachabilityundocumented· intentintended-but-harmful· confidence likely (static only) · single reviewer · reproduced end to end
Failure scenario. An operator on a distro or container shipping CPython 3.14.0-3.14.6 runspython -m forgelm.cli audit --bogusand getspython3.14 -m forgelm.cli audit: error: ...instead offorgelm audit: error:. The prog-normalisation tests fail on that interpreter and the 'identical help' contract between the two module entry points breaks.
Where.forgelm/__main__.py:36,forgelm/cli/__main__.py:21,tests/test_main_module.py:138,tests/test_main_module.py:154
Recommended fix. Either add 3.14 to the CI and publish matrices (setup-python gives >=3.14.7, so the fix is present) and decide the supported patch floor, or make the normalisation robust by passingprog="forgelm"explicitly to ArgumentParser instead of relying on sys.argv[0]. -
CR-A05-052 (code, Low) — Remaining docstring and comment drift around the ingest flags (help-text verdict leftovers)
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · not reproduced (code trace)
Failure scenario. An operator who reads the help or docstrings forms the wrong model. For example, a PDF with blank cover pages followed by a 12-page ToC gets its probe window shifted past physical page 12. A maintainer who reads DEFAULT_TIMEOUT_S expects an abort where the code skips the pattern. None of these changes the output on its own.
Where.forgelm/cli/_parser.py:440,forgelm/ingestion.py:661,forgelm/ingestion.py:795,forgelm/cli/_parser.py:231
Recommended fix. Reword: '--keep-frontmatter: the first/last 12 text-bearing pages (within --page-range) are probed; a contiguous edge run of pages where all three signals fire is dropped'; '--overlap default: min(200, chunk_size // 2)'; ingest_path docstring 'an INFO note'; DEFAULT_TIMEOUT_S 'skips the pattern with a WARNING'; fix the 640-642 comment. -
CR-A05-053 (code, Low) — Doctor --offline help omits HF_DATASETS_OFFLINE; the docs agree with the code, so only the help text and the module docstring are stale
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · independently confirmed · reproduced end to end
Failure scenario. A training host exports HF_DATASETS_OFFLINE=1 only, so datasets stays offline while the Hub is reachable. Following --help, the operator expects doctor to probe Hub reachability. Doctor silently runs the offline-cache probe instead, and hf_hub.reachable is never checked.
Where.forgelm/cli/_parser.py:487-492,forgelm/cli/subcommands/_doctor.py:24-28,forgelm/cli/subcommands/_doctor.py:1179-1194,docs/reference/doctor_subcommand.md:19
Recommended fix. Add 'or HF_DATASETS_OFFLINE=1' to the --offline help in _parser.py:491-492 and to the env-var list in the _doctor.py module docstring. -
CR-A05-054 (code, Low) — Roadmap records per-turn chat safety via the YAML safety block as completed, but chat reads no config and silently ignores a top-level --config
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end
Failure scenario. An operator who believes the roadmap writes evaluation.safety.enabled: true and launches 'forgelm --config cfg.yaml chat ./model'. They get an unscreened REPL with exit 0 and nothing indicating that safety screening is off.
Where.forgelm/cli/_dispatch.py:219-225,forgelm/cli/_parser.py:35-67,forgelm/cli/subcommands/_chat.py,docs/roadmap/completed-phases.md:673
Recommended fix. Correct the roadmap entry to 'planned'. Warn, or exit with an error, when a top-level --config is given together with a subcommand that ignores it. -
CR-A05-055 (code, Low) — Conflicting deferral wording for ML quality classifiers, and stale 'opt-in' labels for the default-on quality filter
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · independently confirmed · reproduced end to end
Failure scenario. An operator reading --help expects ML classifiers in a future release, while the guide says they will never come. A library caller of audit_dataset() gets no quality_summary even though the docs say the filter is default-on.
Where.forgelm/cli/_parser.py:584-594,docs/guides/data_audit.md:357,docs/guides/data_audit.md:395-397,forgelm/data_audit/_quality.py:1
Recommended fix. Align the parser help with the guide ('out of scope') or the reverse, update the stale opt-in labels, and document the library-vs-CLI default difference or align the defaults. -
CR-A05-056 (code, Low) — performance.md says audit_dataset workers parallelise 'over multiple corpora'; they parallelise splits of one source
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · not reproduced (code trace)
Failure scenario. A user expects workers to fan out across several corpora and passes them one at a time, getting no speed-up on a single-split corpus.
Where.docs/guides/performance.md:183,forgelm/cli/_parser.py:632-640
Recommended fix. Say 'parallelises the per-split pipeline (train/validation/test) of one source'. -
CR-A05-057 (code, Low) — The forgelm.cli facade exposes ~138 private names as patch targets, but only 28 are read through it; the project's own test rule and the 'surface' tests hide the difference
severity Low · reachabilityunreachable· intentintended-but-harmful· confidence likely · single reviewer · reproduced end to end
Where.forgelm/cli/__init__.py:8-14,forgelm/cli/__init__.py:258-281,forgelm/cli/_dispatch.py:69-101,forgelm/cli/subcommands/_approve.py:545
Recommended fix. Add a guard or test that, for every facade name tests patch, proves production reads it through the facade, for example a tools/ check cross-referencing patch('forgelm.cli.X') targets with _cli_facade.X call sites. Alternatively, have tests patch the owning submodule name and drop the unconsulted re-exports. Make _approve.py:545/687 consistent with _approvals.py:186. Replace the hasattr 'surface'… -
CR-A05-058 (code, Low) — Logger format and text summaries pass attacker-chosen file names and split names unescaped: ESC sequences and forged log lines
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · single reviewer · reproduced end to end
Failure scenario. A corpus handed over as an archive with crafted member names produces forged INFO/ERROR lines or screen rewrites in CI logs and operator terminals; log-scraping based on line shape is misled.
Where.forgelm/cli/_logging.py:29-33,forgelm/ingestion.py:2074,forgelm/ingestion.py:2185,forgelm/data_audit/_streaming.py:214
Recommended fix. Install a logging.Formatter (or Filter) in _setup_logging that escapes control characters in the final message (newline and CR to \n/\r literals, other C0/C1 and DEL to \xNN), and pass names through !r / ascii() in the summary renderers. -
CR-A05-059 (code, Low) — forgelm.cli is declared 'public' in code and in standards, but 'internal, zero guarantee' in the library API contract; none of main, parse_args or EXIT_* is in any library tier
severity Low · reachabilitydefault· intentcontract-violation· confidence confirmed · independently confirmed · reproduced end to end
Where.forgelm/cli/__init__.py:1-19,forgelm/cli/__init__.py:258-281,forgelm/__init__.py:142-177,docs/reference/library_api_reference.md:29
Recommended fix. Add one explicit tier row to library_api_reference.md (EN and TR) and docs/design/library_api.md. Suggested wording: 'forgelm.cli: main, parse_args and the seven EXIT_* constants are Stable-by-convention (console-script and exit-code contract); everything underscore-prefixed in forgelm.cli is Internal, even where re-exported for tests'. Reword the cli/init.py docstring and the '10 underscore… -
CR-A05-060 (code, Low) — main() and parse_args() take no argv and main() never returns: the in-process contract is undocumented, and the only documented call shape (design doc) does not exist
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · single reviewer · reproduced end to end
Where.forgelm/cli/_dispatch.py:156,forgelm/cli/_parser.py:1275,forgelm/__main__.py:33,docs/design/data_audit_cli_split.md:386
Recommended fix. Document in library_api_reference.md (EN and TR) a short 'Driving the CLI in-process' note. Say main() and parse_args() read sys.argv, main() always raises SystemExit whose .code is the 0-6 contract, and library callers wanting a return value should call the subprocess form or the library functions. Alternatively, plan an additivemain(argv: Sequence[str] | None = None)in a future change, whic… -
CR-A05-061 (code, Low) — data_audit_cli_split.md describes a stale forgelm.cli surface: 5 exit codes, nonexistent import-stability test, and a lazy facade design that was not implemented
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · reproduced end to end
Where.docs/design/data_audit_cli_split.md:11,docs/design/data_audit_cli_split.md:380-386,docs/design/data_audit_cli_split.md:464-481,docs/design/data_audit_cli_split.md:511
Recommended fix. Edit the design docs: correct the §2.e all and the exit-code acceptance list to 0-6, and mark the cli lazy-facade section 'not implemented / superseded'. Either ship tests/test_cli_import_stability.py (assert all names resolve, the _PUBLIC_EXIT_CODES set equals {0..6}, and main/parse_args are callable) or delete the claim. This is a docs-only edit; no code change. -
CR-A05-062 (code, Low) — Architecture reference drift cluster: stale TXT/MD read, 'info-level' override log, stale line refs, incomplete field table, 'pre-flight' markdown overlap
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · not reproduced (code trace)
Failure scenario. Contributors and integrators rely on line refs, field lists and log levels that no longer exist.
Where.docs/reference/data_ingestion_architecture.md:38,docs/reference/data_ingestion_architecture.md:193,docs/reference/data_ingestion_architecture.md:370,docs/reference/data_ingestion_architecture.md:443
Recommended fix. Drop line numbers in favour of symbol names; regenerate the field table from dataclasses.fields(IngestionResult); fix the three stale statements; document the atomic-write semantics in the invariants list. -
CR-A05-063 (code, Low) — configuration.md (EN+TR) implies 'forgelm doctor' would catch an unfetchable revision pin; doctor reads no config and checks no pin
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · not reproduced (code trace)
Failure scenario. A CI author adds 'forgelm doctor' after '--dry-run', as the known-gaps note suggests, expecting unfetchable pins to fail fast. Doctor passes, and the run still fails at model-load time.
Where.docs/reference/configuration.md:646-648,docs/reference/configuration-tr.md:644-646,forgelm/cli/_parser.py:462-495,forgelm/cli/subcommands/_doctor.py:464-562
Recommended fix. Rewrite the bullet: nothing before load time checks pin fetchability, doctor included. Or add a config-aware pin probe. -
CR-A05-064 (code, Low) — Purge --row-id / reverse-pii --query are argv-only, and the docs promise the cleartext identifier never leaves the terminal and show no safe invocation
severity Low · reachabilitydefault· intentintended-but-harmful· confidence likely · single reviewer · reproduced end to end
Where.forgelm/cli/_parser.py:1097-1102,forgelm/cli/_parser.py:1196-1202,forgelm/cli/_parser.py:1204-1209,docs/guides/gdpr_erasure.md:31-36
Recommended fix. Add a hardening option (e.g. --query-file / --row-id-file or '-' for stdin, or an *_env selector), and until then document the exposure: warn about ps, shell history and CI logs, and recommend a leading space or HISTCONTROL, a secret-masked CI variable, or the stdin option. Reword 'never leaves the operator's terminal' to say it is never written by ForgeLM to the audit log or envelopes. Add the j… -
CR-A05-065 (code, Low) — GDPR guide, user manual and design spec (EN+TR) state the project-wide exit contract as 0/1/2/3/4 (new cluster; not covered by M-A05-017 or M5r1-A05-002)
severity Low · reachabilitydefault· intentcontract-violation· confidence likely · single reviewer · not reproduced (code trace) · already tracked: KNOWN-OPEN C31-EXIT-CODE-PROSE — docs/roadmap/phase-16-trust-surface-hardening.md:782-785 (S15 open)
Failure scenario. A reader of the GDPR guide concludes that no ForgeLM subcommand can exit 5 or 6. Their CI case statement then has no branch for the integrity code, which cicd.md:22 warns 'should never fall through to a generic default'.
Where.docs/guides/gdpr_erasure.md:71,docs/guides/gdpr_erasure-tr.md:71,docs/usermanuals/en/compliance/gdpr-erasure.md:96,docs/usermanuals/tr/compliance/gdpr-erasure.md:96
Recommended fix. Replace the range with 'the public 0-6 contract (purge/reverse-pii emit only 0/1/2)'. Extend the :104 note to name 3-6. Update design §4.3 to cite _PUBLIC_EXIT_CODES instead of an enumerated range. - 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.
Done when
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