Skip to content

[Triage] Help parsing is sensitive to ANSI colour (latent on Python 3.14): coloured {choices} silently disable choice-value drift checking (CR-A17a-053) #829

Open

Description

@cemililik

Needs triage. Reviewers disagreed on this finding, so it is not counted in the backlog. Decide whether it is a real defect: if yes, set severity and wave; if not, close it with the reason.

CR-A17a-053 — Help parsing is sensitive to ANSI colour (latent on Python 3.14): coloured {choices} silently disable choice-value drift checking

severity Info · confidence disputed · single reviewer · reproduced with stubs

What happens. The tool does not scrub FORCE_COLOR, PYTHON_COLORS or NO_COLOR for the child and does not strip ANSI. On the real default lane (Python 3.13.14), FORCE_COLOR=1 and PYTHON_COLORS=1, alone and together, change nothing: 0 ESC bytes, 130 identical help lines, and the tool prints OK 515, exit 0. Python 3.14's coloured argparse is not in the classifiers (3.10-3.13) or the ci.yml matrix and no 3.14 interpreter was available here, so the following is a stub-level test only. With synthetic ANSI wrapped around the COMMAND metavar, _parse_subcommand_list returns [] (a loud RuntimeError, exit 1). With ANSI wrapped around {a,b,c}, _FLAG_CHOICES_RE no longer matches: the choices map becomes {} while the flag set stays equal, which silently disables the choice-value check.

Failure scenario. On a future 3.14 lane or any harness exporting FORCE_COLOR=1, if argparse colours the choice braces (exact 3.14 layout not verified), 'forgelm export --quant q9' in docs would pass although the parser rejects it. The failure is not visible in the run output.

Where.

Recommended fix. Run the child with env NO_COLOR=1 and without FORCE_COLOR/PYTHON_COLORS (or strip ANSI before parsing). Add a test with injected ANSI text. Fail loudly if any subcommand ends with an empty choices map where the previous release had one.


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
    needs-triageNot triaged yet: every new issue starts with it; only a maintainer removes it
    source: review-2026-10Found by the October 2026 code and documentation review
    on Oct 9, 2026
  2. cemililik commented on Oct 20, 2026

    @cemililik
    CollaboratorAuthor

    Checked on development: on Python 3.13, FORCE_COLOR=1 and PYTHON_COLORS=1 change nothing (no escape bytes, identical help, same parsed surface), so nothing is broken on a supported interpreter today.

    With synthetic ANSI around the flags and {choices}, the parser keeps the flags but returns an empty choice map, and a coloured COMMAND line makes the subcommand list parse fail loudly. We have no 3.14 interpreter to confirm the real layout. It is less silent than described: test_deploy_target_choices_recovered asserts the choices are recovered and would fail in the same environment.

    Kept as low-priority hardening: run the child with colour disabled (and/or strip ANSI) and add a test with injected ANSI, ideally together with adding 3.14 to CI (#526).

    Triage result: enhancement, area: dev-tooling, severity: info, wave: 4.

    Triage round 1, checked against development on 2026-10-20.

  3. added
    enhancementNew feature or request
    severity: infoHardening or improvement suggestion
    wave: 4Roadmap wave 4: low / info / watch, when touching the module
    on Oct 20, 2026
  4. removed
    needs-triageNot triaged yet: every new issue starts with it; only a maintainer removes it
    on Oct 20, 2026
  5. added and removed
    bugSomething isn't working
    on Oct 20, 2026
  6. cemililik commented on Oct 22, 2026

    @cemililik
    CollaboratorAuthor

    Where to start

    Thanks for taking a look. Claim it by starting a comment with /assign so nobody duplicates the work.

    Start with these files on development:

    _run_help() (about line 123) runs python3 -m forgelm.cli ... --help with the parent's environment and parses the text. Python 3.14's argparse can colour its help; whether it does depends on FORCE_COLOR, PYTHON_COLORS and NO_COLOR. If it does, the escape bytes break two parsers:

    • _parse_subcommand_list stops finding the COMMAND line, which raises a loud RuntimeError;
    • _FLAG_CHOICES_RE stops matching --flag {a,b,c}, so the choice map comes back empty while the flag set looks fine, and drift in choice values goes unchecked.

    On Python 3.13 colour changes nothing, so nothing is broken today; this is hardening.

    What to change (decided, so you do not need to choose):

    1. In _run_help, pass env= to subprocess.run: a copy of os.environ without FORCE_COLOR, with NO_COLOR=1 and PYTHON_COLORS=0.
    2. Add a small _strip_ansi(text) helper and apply it to the text _run_help returns, as a second line of defence. Use a bounded pattern such as \x1b\[[0-9;]{0,32}m (regex standard).
    3. Leave out two parts of the finding. Do not add a check that fails when a subcommand's choice map is empty compared with the previous release: there is no previous-release data to compare. Adding Python 3.14 to CI is tracked in Python support window lags: 3.14 (GA Oct 2025) is installable but never tested; 3.10 reaches EOL this month (CR-A16-056) #526.

    Add the tests to tests/test_check_cli_help_consistency.py. Copy test_deploy_target_choices_recovered in TestParserSurfaceDiscovery and use the tool fixture:

    • Run _strip_ansi on a --target {a,b} line with escape codes around the flag and around the braces. Then _parse_usage_flags(...) returns the choice map. Do the same for a coloured COMMAND line and _parse_subcommand_list.
    • monkeypatch.setenv("FORCE_COLOR", "1"), replace tool.subprocess.run with a fake that records its keyword arguments, and call _run_help(["--help"]). Assert that the recorded env has no FORCE_COLOR, that NO_COLOR == "1" and that PYTHON_COLORS == "0".
    pytest tests/test_check_cli_help_consistency.py --no-cov
    FORCE_COLOR=1 PYTHON_COLORS=1 python3 tools/check_cli_help_consistency.py --strict --quiet
    ruff format --check tools/check_cli_help_consistency.py tests/test_check_cli_help_consistency.py
    ruff check tools/check_cli_help_consistency.py tests/test_check_cli_help_consistency.py

    No GPU, network access or model download is needed. Add an entry under ## [Unreleased] in CHANGELOG.md, where earlier guard changes are listed. The change is tooling only, so there is no Turkish mirror to update.

    Open the PR against development with Fixes #829 (finding CR-A17a-053).

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: dev-toolingCode area: dev-toolingenhancementNew feature or requestgood first issueGood for newcomersseverity: infoHardening or improvement suggestionsource: review-2026-10Found by the October 2026 code and documentation reviewwave: 4Roadmap wave 4: low / info / watch, when touching the module

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions