Skip to content

map_step_runner.py parse_requirements_index() imports PyYAML → validate_blueprint_contract silently reports 'malformed' Requirements Index without it installed #319

Description

@azalio

Summary

parse_requirements_index() in .map/scripts/map_step_runner.py (added for the Requirements Index / forward-completeness gate, present as of mapify_version 3.20.0) does a lazy import yaml (yaml.safe_load) to parse the mapify:requirements-index:v1 sentinel block from the spec. When PyYAML is not installed in the environment running python3 .map/scripts/map_step_runner.py, the function catches the ImportError and returns status='malformed' — even when the spec's Requirements Index is perfectly well-formed.

This regresses the explicit scope note in #245 ("The .map/scripts/map_step_runner.py runner is NOT affected — it reads config via a manual scalar parser ... no yaml import"). That was true for _read_map_config_scalars at the time, but parse_requirements_index is a newer function in the same file that DOES import yaml, reintroducing the same root cause (PyYAML not a declared runtime dependency) in a code path #245 said was safe.

Observed

Running /map-plan on a fresh env without PyYAML (python3 -c "import yaml"ModuleNotFoundError: No module named 'yaml'):

$ python3 .map/scripts/map_step_runner.py validate_blueprint_contract
{
  "valid": false,
  "errors": [
    "Forward-coverage: Requirements Index is malformed (see the mapify:requirements-index:v1 template in plan-reference.md.jinja)"
  ],
  ...
  "forward_coverage": {"status": "malformed", "basis": "Requirements Index malformed; could not diff"}
}

The spec's Requirements Index block was hand-verified to exactly match the documented sentinel-pair + fenced-yaml contract (open <!-- mapify:requirements-index:v1 -->, fenced ```yaml block, close <!-- /mapify:requirements-index:v1 -->, valid `{requirements: [{id, kind}, ...]}` shape). After `pip3 install pyyaml` the same command reports `forward_coverage.status: "present_nonempty"` correctly — confirming the failure is purely the missing import, not a spec formatting issue. This makes the error message ("Requirements Index is malformed") actively misleading: it points the operator at the wrong file (the spec) when the actual defect is the local Python environment's missing dependency.

Expected

Either:

  1. parse_requirements_index() distinguishes ImportError from a genuine YAML parse error / malformed structure, and surfaces a distinct, honest status/message (e.g. status='pyyaml_missing' with a message like "PyYAML not installed; cannot validate Requirements Index — run pip install pyyaml") instead of conflating it with malformed, OR
  2. PyYAML is made a true runtime dependency of everything .map/scripts/*.py needs (not just the mapify CLI package's pyproject.toml, which doesn't even apply here since these are vendored per-repo scripts, not an installed package) — e.g. documented as a required local Python dependency in .map/scripts/README or checked with a clear preflight error at the top of map_step_runner.py.

Scope

  • .map/scripts/map_step_runner.py: parse_requirements_index() (~line 2761, import yaml at ~line 2828) and its caller path through validate_blueprint_contract's forward-coverage check.
  • Affects any repo running /map-plan's Step 5.6 (validate_blueprint_contract) in a Python environment without PyYAML — likely common, since PyYAML is not obviously a prerequisite for the .map/scripts/ runner scripts (per PyYAML missing from runtime deps → mapify CLI silently ignores .map/config.yaml (uses defaults) #245's own scope note, which is now stale for this function).

Environment

  • mapify_version: 3.20.0 (.claude/skills/map-plan/SKILL.md MAP-MANAGED header, installed_at 2026-07-02T11:40:03Z)
  • python3 --version: 3.12.8 (/usr/local/bin/python3)
  • pip3 show pyyaml: not found (before workaround)
  • Related: PyYAML missing from runtime deps → mapify CLI silently ignores .map/config.yaml (uses defaults) #245 (same root cause — PyYAML not a hard dependency — different code path; that issue's fix/scope note explicitly excluded map_step_runner.py, which is now inaccurate for parse_requirements_index)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions