diff --git a/src/docgen/narrate_from_source.py b/src/docgen/narrate_from_source.py index 0643aab..8fd2772 100644 --- a/src/docgen/narrate_from_source.py +++ b/src/docgen/narrate_from_source.py @@ -238,19 +238,28 @@ def build_owner_hints_guidance( return "\n".join(f"- {h}" for h in lines if h.strip()) +def _stem_belongs_to_segment(seg_id: str, path: Path) -> bool: + """``01*.md`` also matches segment ``010``; only the id or an ``id-`` stem belongs.""" + stem = path.stem + return stem == seg_id or stem.startswith(f"{seg_id}-") + + +def _fallback_narration_text(directory: Path, seg_id: str) -> str: + if not directory.is_dir(): + return "" + for cand in directory.glob(f"{seg_id}*.md"): + if _stem_belongs_to_segment(seg_id, cand): + return cand.read_text(encoding="utf-8") + return "" + + def _existing_narration_text(cfg: "Config", seg_id: str) -> str: """Return current ``narration/.md`` text, or empty if missing.""" seg_name = cfg.resolve_segment_name(seg_id) path = cfg.narration_dir / f"{seg_name}.md" - if not path.is_file(): - # Match wizard asset discovery for NN-*.md fallbacks. - if cfg.narration_dir.is_dir(): - for cand in cfg.narration_dir.glob(f"{seg_id}-*.md"): - return cand.read_text(encoding="utf-8") - for cand in cfg.narration_dir.glob(f"{seg_id}*.md"): - return cand.read_text(encoding="utf-8") - return "" - return path.read_text(encoding="utf-8") + if path.is_file(): + return path.read_text(encoding="utf-8") + return _fallback_narration_text(cfg.narration_dir, seg_id) def generate_narration_markdown( diff --git a/tests/test_narrate_from_source.py b/tests/test_narrate_from_source.py index 1f9dfe2..c434298 100644 --- a/tests/test_narrate_from_source.py +++ b/tests/test_narrate_from_source.py @@ -10,6 +10,7 @@ from docgen.config import Config, ConfigError from docgen.narrate_from_source import ( + _existing_narration_text, build_owner_hints_guidance, collect_source_snippets, generate_narration_markdown, @@ -405,3 +406,32 @@ def test_narration_generate_all_empty_all_raises_even_with_default(tmp_path: Pat ) assert r.exit_code != 0 assert "segments.all is empty" in (r.output + r.stderr) + + +def _narration_cfg(tmp_path: Path) -> Config: + raw = { + "segments": {"all": ["01"], "default": ["01"]}, + "segment_names": {"01": "01-missing"}, + "dirs": {"narration": "narration"}, + } + (tmp_path / "docgen.yaml").write_text(yaml.dump(raw), encoding="utf-8") + (tmp_path / "narration").mkdir() + return Config.from_yaml(tmp_path / "docgen.yaml") + + +def test_narration_fallback_does_not_read_another_segment(tmp_path: Path) -> None: + cfg = _narration_cfg(tmp_path) + (tmp_path / "narration" / "010-other.md").write_text("other segment\n", encoding="utf-8") + assert _existing_narration_text(cfg, "01") == "" + + +def test_narration_fallback_reads_hyphen_stem(tmp_path: Path) -> None: + cfg = _narration_cfg(tmp_path) + (tmp_path / "narration" / "01-demo.md").write_text("this segment\n", encoding="utf-8") + assert _existing_narration_text(cfg, "01") == "this segment\n" + + +def test_narration_fallback_reads_bare_segment_id(tmp_path: Path) -> None: + cfg = _narration_cfg(tmp_path) + (tmp_path / "narration" / "01.md").write_text("id file\n", encoding="utf-8") + assert _existing_narration_text(cfg, "01") == "id file\n"