From 081dc594b6a16e1876ce9c0ef5295672a08a2c69 Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Mon, 20 Jul 2026 02:07:39 -0400 Subject: [PATCH 1/2] fix(genbi): guard non-dict models/columns in inventory format _format_model_inventory assumed dict rows; junk MDL entries raised AttributeError while composing build instructions. --- core/wren/src/wren/genbi/composer.py | 13 ++++++-- .../unit/test_genbi_format_model_inventory.py | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 core/wren/tests/unit/test_genbi_format_model_inventory.py diff --git a/core/wren/src/wren/genbi/composer.py b/core/wren/src/wren/genbi/composer.py index 726b8d5860..6bcff8061f 100644 --- a/core/wren/src/wren/genbi/composer.py +++ b/core/wren/src/wren/genbi/composer.py @@ -51,14 +51,23 @@ def _data_mode_guidance(data_mode: str) -> str: raise ValueError(f"unknown data-mode {data_mode!r}; expected one of {DATA_MODES}") -def _format_model_inventory(models: list[dict]) -> str: +def _format_model_inventory(models: list[dict] | None) -> str: """One markdown bullet per model with its column names.""" if not models: return "- (no models found — run `wren context build` first)" lines = [] for model in models: - cols = ", ".join(c.get("name", "?") for c in model.get("columns", [])) + if not isinstance(model, dict): + continue + raw_cols = model.get("columns") or [] + if not isinstance(raw_cols, list): + raw_cols = [] + cols = ", ".join( + c.get("name", "?") for c in raw_cols if isinstance(c, dict) + ) lines.append(f"- **{model.get('name', '?')}**: {cols}") + if not lines: + return "- (no models found — run `wren context build` first)" return "\n".join(lines) diff --git a/core/wren/tests/unit/test_genbi_format_model_inventory.py b/core/wren/tests/unit/test_genbi_format_model_inventory.py new file mode 100644 index 0000000000..b8415d81ed --- /dev/null +++ b/core/wren/tests/unit/test_genbi_format_model_inventory.py @@ -0,0 +1,30 @@ +from wren.genbi.composer import _format_model_inventory + + +def test_formats_clean_models(): + text = _format_model_inventory( + [{"name": "orders", "columns": [{"name": "id"}, {"name": "total"}]}] + ) + assert "orders" in text + assert "id" in text + + +def test_skips_non_dict_models_and_columns(): + text = _format_model_inventory( + [ + None, + "x", + { + "name": "t", + "columns": [None, {"name": "a"}, "bad"], + }, + ] + ) + assert "t" in text + assert "a" in text + assert "None" not in text + + +def test_all_junk_falls_back_to_empty_message(): + text = _format_model_inventory([None, "x", 1]) + assert "no models found" in text From 87bcf4db6229647fa997c7944a02577b0de2a34e Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Mon, 20 Jul 2026 12:06:18 -0400 Subject: [PATCH 2/2] style(genbi): ruff format composer model inventory guards --- core/wren/src/wren/genbi/composer.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/wren/src/wren/genbi/composer.py b/core/wren/src/wren/genbi/composer.py index 6bcff8061f..f4afc11c11 100644 --- a/core/wren/src/wren/genbi/composer.py +++ b/core/wren/src/wren/genbi/composer.py @@ -62,9 +62,7 @@ def _format_model_inventory(models: list[dict] | None) -> str: raw_cols = model.get("columns") or [] if not isinstance(raw_cols, list): raw_cols = [] - cols = ", ".join( - c.get("name", "?") for c in raw_cols if isinstance(c, dict) - ) + cols = ", ".join(c.get("name", "?") for c in raw_cols if isinstance(c, dict)) lines.append(f"- **{model.get('name', '?')}**: {cols}") if not lines: return "- (no models found — run `wren context build` first)"