iceboundrock / iceboundrock/AutoForge

The `yaml` extra's PyYAML branch is exercised by no environment

Open
#40 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
0
Forks
0
Avg merge
8h 56m
Merged PRs (30d)
40

Description

Follow-up from PR #38 (issue #31), listed there as out of scope.

pyproject.toml declares an optional extra:

[project.optional-dependencies]
# Full YAML config support (without it, the built-in subset parser is used).
yaml = ["pyyaml>=6"]

and config.py branches on whether it is installed:

def _load_yaml(path: Path) -> dict:
    text = path.read_text(encoding="utf-8")
    try:
        import yaml  # type: ignore

        data = yaml.safe_load(text)
        return data if isinstance(data, dict) else {}
    except ImportError:
        return _minimal_yaml_parse(text)

No environment exercises the PyYAML branch. The dev dependency group is
["pytest>=8", "ruff>=0.4", "mypy>=1.10"], the local .venv does not have
PyYAML, and the CI matrix added in #38 installs uv sync --locked --group dev
— so import yaml raises in all three, and _minimal_yaml_parse is what
actually runs everywhere. The one test that touches this,
tests/test_config.py::test_example_yaml_loads_without_pyyaml, pins the
absent case deliberately (monkeypatch.setitem(sys.modules, "yaml", None)).

Why it matters

The extra is a documented, supported configuration path, and it is the branch
an operator gets the moment they pip install autoforge[yaml]. Today that
operator runs a code path with zero test coverage, on a config file that
decides routing, loop bounds and safety gates — including safety.allow_merge.

The two parsers are also not guaranteed to agree. _minimal_yaml_parse handles
a documented subset (2-space nested maps, - lists, inline scalars) and
raises ConfigurationError beyond it, while PyYAML accepts far more. A config
that parses one way locally and another way under the extra is a silent
divergence that nothing currently detects. The interesting risk is not the
input the subset parser rejects — it is input both accept and interpret
differently (yes/no/on/off as booleans, 1.0 vs "1.0", sexagesimal
and octal-looking scalars, empty values, duplicate keys).

Proposed

  1. Make the branch reachable in tests regardless of the ambient environment —
    e.g. a fixture that skips when PyYAML is genuinely unavailable, plus one
    that installs/imports it, so _load_yaml is covered in both directions
    rather than only in the ImportError direction.
  2. Add a differential test over the repository's own example config and a
    handful of the ambiguous scalars above: both parsers must produce the same
    resulting Config, or the subset parser must refuse outright. Silent
    disagreement is the failure mode worth catching.
  3. Decide where PyYAML gets installed. Either add an extras entry to the dev
    group / a CI job that installs .[yaml], or — if the extra is not actually
    wanted — remove it and the branch, and document that the subset parser is
    the only YAML path. Keeping an untested optional branch is the one outcome
    worth avoiding.

Not a bug report

Nothing is known to be broken. yaml.YAMLError on malformed input is already
caught by the caller's except Exception and re-raised as ConfigurationError
with the file path, which was verified while writing this. This is a coverage
and consistency gap, not a defect.

https://claude.ai/code/session_01Gjom7eoTaAMMhkHCb5TEgM

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with _load_yaml in config.py, the YAML dependency declaration in pyproject.toml, and tests/test_config.py::test_example_yaml_loads_without_pyyaml. Review the existing CI setup and test the PyYAML and fallback paths against the repository example and ambiguous scalars. Done means both branches are exercised, parser divergence is detected or rejected, and the dependency or removal decision is reflected in configuration and documentation.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ci-cd, testing
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.