diff --git a/milestones/README.md b/milestones/README.md index 38d03f3..821ffc9 100644 --- a/milestones/README.md +++ b/milestones/README.md @@ -5,10 +5,13 @@ repositories that install `docgen` and maintain their own demo bundle. The library no longer ships an in-repo dogfood; consumers are the integration test of record. -**Active:** **[pages-config-strings.md](pages-config-strings.md)** — -`pages.docs_dir` / title / extra_links must be typed at config load. +**Active:** **[visual-map-sources.md](visual-map-sources.md)** — +`visual_map` mixed `sources` must be a YAML list of strings at config +load. **Shipped:** +- **[pages-config-strings.md](pages-config-strings.md)** — + `pages.docs_dir` / title / extra_links must be typed (#111). - **[generation-segment-strings.md](generation-segment-strings.md)** — per-segment narration / scene-generation prompts must be YAML strings (#110). diff --git a/milestones/pages-config-strings.md b/milestones/pages-config-strings.md index 3cf3f6f..783fcdd 100644 --- a/milestones/pages-config-strings.md +++ b/milestones/pages-config-strings.md @@ -1,7 +1,7 @@ # Milestone: pages docs_dir / title / extra_links must be typed -**Status:** Active -**PR:** [#111](https://github.com/jmjava/documentation-generator/pull/111) +**Status:** Shipped +**PR:** #111 **Depends on:** `milestones/generation-segment-strings.md` (PR #110), `milestones/path-config-strings.md` (PR #109), `milestones/concat-segment-lists.md` (PR #84) diff --git a/milestones/visual-map-sources.md b/milestones/visual-map-sources.md new file mode 100644 index 0000000..94c1321 --- /dev/null +++ b/milestones/visual-map-sources.md @@ -0,0 +1,35 @@ +# Milestone: visual_map mixed sources must be a list of strings + +**Status:** Active +**PR:** [#112](https://github.com/jmjava/documentation-generator/pull/112) +**Depends on:** `milestones/visual-map-field-strings.md` (PR #108), +`milestones/pages-config-strings.md` (PR #111) + +## Problem + +`visual_map..source` is already a YAML string. Mixed rows use +**`sources:`** instead: + +1. A YAML **string** (`sources: clip.mp4`) was iterated as characters + in `Composer.compose_segments`, looking up one-letter paths. +2. A nested list item was passed to `_resolve_source` and Path-joined. + +Missing `sources` remains allowed (empty mixed row). + +## Goal + +Fail closed at `Config.from_yaml`. Present `visual_map..sources` +must be a YAML list of non-empty strings. + +## Done when + +- [x] Present `visual_map..sources` must be a YAML list of strings. +- [x] Tests for a string value and a nested-list item. +- [x] `ruff check src/ tests/` +- [x] `pytest tests/` +- [x] `docgen benchmark` (no clock change) + +## Out of scope + +- `wizard.default_guidance` type gating is separate. +- Empty `sources: []` remains allowed. diff --git a/src/docgen/config.py b/src/docgen/config.py index efb3c89..8b32b60 100644 --- a/src/docgen/config.py +++ b/src/docgen/config.py @@ -338,6 +338,13 @@ def __post_init__(self) -> None: f"{src}: visual_map.{sid_s}.{fname} must be a YAML string, " f"not {type(val).__name__}" ) + if spec.get("sources") is not None: + string_list_block( + spec, + "sources", + label=f"visual_map.{sid_s}.sources", + source=src, + ) wiz = self._block("wizard") if wiz.get("exclude_patterns") is not None: string_list_block( diff --git a/tests/test_config.py b/tests/test_config.py index f1459e6..6426176 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -741,3 +741,25 @@ def test_from_yaml_list_pages_segment_title_raises(tmp_path: Path) -> None: ) with pytest.raises(ConfigError, match="pages.segments.01.title must be a YAML string"): Config.from_yaml(p) + + +def test_from_yaml_string_visual_map_sources_raises(tmp_path: Path) -> None: + p = tmp_path / "docgen.yaml" + p.write_text( + 'visual_map:\n "01":\n type: mixed\n sources: clip.mp4\n', + encoding="utf-8", + ) + with pytest.raises(ConfigError, match="visual_map.01.sources must be a YAML list"): + Config.from_yaml(p) + + +def test_from_yaml_list_visual_map_sources_item_raises(tmp_path: Path) -> None: + p = tmp_path / "docgen.yaml" + p.write_text( + 'visual_map:\n "01":\n type: mixed\n sources:\n - - clip.mp4\n', + encoding="utf-8", + ) + with pytest.raises( + ConfigError, match=r"visual_map.01.sources\[0\] must be a YAML string" + ): + Config.from_yaml(p)