From 76366d849fbbd0ecdd6e6611b168f05b1dd314d9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 7 Sep 2026 21:29:02 +0000 Subject: [PATCH 1/2] Fail closed when wizard.default_guidance is a list wizard_config merged a YAML list over the empty-string default. Require a YAML string at config load (empty allowed, same as wizard.system_prompt). Co-authored-by: jmjava --- milestones/README.md | 7 +++--- milestones/visual-map-sources.md | 4 ++-- milestones/wizard-default-guidance.md | 32 +++++++++++++++++++++++++++ src/docgen/config.py | 4 ++++ tests/test_config.py | 17 ++++++++++++++ 5 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 milestones/wizard-default-guidance.md diff --git a/milestones/README.md b/milestones/README.md index 821ffc9..d445fe1 100644 --- a/milestones/README.md +++ b/milestones/README.md @@ -5,11 +5,12 @@ 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:** **[visual-map-sources.md](visual-map-sources.md)** — -`visual_map` mixed `sources` must be a YAML list of strings at config -load. +**Active:** **[wizard-default-guidance.md](wizard-default-guidance.md)** — +`wizard.default_guidance` must be a YAML string at config load. **Shipped:** +- **[visual-map-sources.md](visual-map-sources.md)** — + `visual_map` mixed `sources` must be a YAML list of strings (#112). - **[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)** — diff --git a/milestones/visual-map-sources.md b/milestones/visual-map-sources.md index 94c1321..eff5a3f 100644 --- a/milestones/visual-map-sources.md +++ b/milestones/visual-map-sources.md @@ -1,7 +1,7 @@ # Milestone: visual_map mixed sources must be a list of strings -**Status:** Active -**PR:** [#112](https://github.com/jmjava/documentation-generator/pull/112) +**Status:** Shipped +**PR:** #112 **Depends on:** `milestones/visual-map-field-strings.md` (PR #108), `milestones/pages-config-strings.md` (PR #111) diff --git a/milestones/wizard-default-guidance.md b/milestones/wizard-default-guidance.md new file mode 100644 index 0000000..1c33976 --- /dev/null +++ b/milestones/wizard-default-guidance.md @@ -0,0 +1,32 @@ +# Milestone: wizard.default_guidance must be a string + +**Status:** Active +**PR:** pending +**Depends on:** `milestones/wizard-prompt-strings.md` (PR #100), +`milestones/visual-map-sources.md` (PR #112) + +## Problem + +`wizard.system_prompt` / `llm_model` are already typed at config load. +**`wizard.default_guidance`** was not. `wizard_config` merges the block +over a `""` default; a YAML list became a list in that dict instead of +a string (empty allowed, same as `system_prompt`). + +## Goal + +Fail closed at `Config.from_yaml`. Missing key still uses `""`. + +## Done when + +- [x] Present `wizard.default_guidance` must be a YAML string (empty + allowed). +- [x] Tests for a list value and an empty string. +- [ ] `ruff check src/ tests/` +- [ ] `pytest tests/` +- [ ] `docgen benchmark` (no clock change) + +## Out of scope + +- The wizard UI does not currently read `default_guidance`; this is a + config-load type gate so a later consumer of `wizard_config` cannot + inherit a list. diff --git a/src/docgen/config.py b/src/docgen/config.py index 8b32b60..d13e509 100644 --- a/src/docgen/config.py +++ b/src/docgen/config.py @@ -375,6 +375,10 @@ def __post_init__(self) -> None: require_optional_yaml_string( wiz["system_prompt"], label="wizard.system_prompt", source=src ) + if wiz.get("default_guidance") is not None: + require_optional_yaml_string( + wiz["default_guidance"], label="wizard.default_guidance", source=src + ) if wiz.get("llm_model") is not None: require_yaml_string(wiz["llm_model"], label="wizard.llm_model", source=src) ig = self._block("image_generation") diff --git a/tests/test_config.py b/tests/test_config.py index 6426176..75113b0 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -763,3 +763,20 @@ def test_from_yaml_list_visual_map_sources_item_raises(tmp_path: Path) -> None: ConfigError, match=r"visual_map.01.sources\[0\] must be a YAML string" ): Config.from_yaml(p) + + +def test_from_yaml_list_wizard_default_guidance_raises(tmp_path: Path) -> None: + p = tmp_path / "docgen.yaml" + p.write_text( + "wizard:\n default_guidance:\n - Keep it spoken\n - No markdown\n", + encoding="utf-8", + ) + with pytest.raises(ConfigError, match="wizard.default_guidance must be a YAML string"): + Config.from_yaml(p) + + +def test_from_yaml_empty_wizard_default_guidance_allowed(tmp_path: Path) -> None: + p = tmp_path / "docgen.yaml" + p.write_text('wizard:\n default_guidance: ""\n', encoding="utf-8") + c = Config.from_yaml(p) + assert c.wizard_config["default_guidance"] == "" From 4e782144a3ca5d1f9370b29990e02dfb0c489263 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 7 Sep 2026 21:29:30 +0000 Subject: [PATCH 2/2] Mark wizard-default-guidance milestone gates as run ruff, pytest (656 passed, 1 skipped), and docgen benchmark are green. Co-authored-by: jmjava --- milestones/wizard-default-guidance.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/milestones/wizard-default-guidance.md b/milestones/wizard-default-guidance.md index 1c33976..89ebbdc 100644 --- a/milestones/wizard-default-guidance.md +++ b/milestones/wizard-default-guidance.md @@ -1,7 +1,7 @@ # Milestone: wizard.default_guidance must be a string **Status:** Active -**PR:** pending +**PR:** [#113](https://github.com/jmjava/documentation-generator/pull/113) **Depends on:** `milestones/wizard-prompt-strings.md` (PR #100), `milestones/visual-map-sources.md` (PR #112) @@ -21,9 +21,9 @@ Fail closed at `Config.from_yaml`. Missing key still uses `""`. - [x] Present `wizard.default_guidance` must be a YAML string (empty allowed). - [x] Tests for a list value and an empty string. -- [ ] `ruff check src/ tests/` -- [ ] `pytest tests/` -- [ ] `docgen benchmark` (no clock change) +- [x] `ruff check src/ tests/` +- [x] `pytest tests/` +- [x] `docgen benchmark` (no clock change) ## Out of scope