From adbaa347d4d5d7c42bbba43edd845e462df3f0c4 Mon Sep 17 00:00:00 2001 From: Yev Meyer Date: Tue, 3 Nov 2026 07:45:05 -0500 Subject: [PATCH 1/4] test: flag a half-remembered proverb, not every half-remembered line The half_remember_proverb pattern matches a string that names a proverb and half remember or half-remember, in either order. Bank prose about lyrics or lines a user only half remembers passes the audit. Signed-off-by: Yev Meyer --- tests/_asset_audit.py | 2 +- tests/test_no_template_text.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/_asset_audit.py b/tests/_asset_audit.py index 877c6dde..4959a5d3 100644 --- a/tests/_asset_audit.py +++ b/tests/_asset_audit.py @@ -54,7 +54,7 @@ r"fictional example .* (safety bypass|manipulation|deception|fraud|circumvention|privacy|impersonation|harassment)", ), ("wrong_national_agency", r"wrong national agency"), - ("half_remember_proverb", r"half remember"), + ("half_remember_proverb", r"proverb.*half[- ]remember|half[- ]remember.*proverb"), ("main_X_rule_handled", r"main \w+ rule (is )?handled"), ("beta_scenario", r"\bbeta\s+\w+(?:_\w+)*\s+scenario\b"), ("decline_to_provide_content_that_enables", r"decline to provide content that enables"), diff --git a/tests/test_no_template_text.py b/tests/test_no_template_text.py index f47abf7b..85604c90 100644 --- a/tests/test_no_template_text.py +++ b/tests/test_no_template_text.py @@ -91,6 +91,15 @@ def test_no_template_text_in_shipped_banks(): pytest.fail(msg) +def test_half_remember_pattern_needs_a_proverb(): + """``half_remember_proverb`` flags a half-remembered proverb, not every half-remembered line.""" + assert _asset_audit._detect_template_in_string("Ask about a proverb you half remember.") == [ + "half_remember_proverb" + ] + assert _asset_audit._detect_template_in_string("Quote a half-remembered proverb.") == ["half_remember_proverb"] + assert _asset_audit._detect_template_in_string("Bring up lyrics you only half remember.") == [] + + def test_audit_helper_runs(): """Sanity: the per-bank audit function returns a structured finding dict for every shipped bank. From a9fbd0cef2e1f85cb6ac1f3e3eef17ac54893c47 Mon Sep 17 00:00:00 2001 From: Yev Meyer Date: Tue, 3 Nov 2026 07:45:19 -0500 Subject: [PATCH 2/4] test: audit the banks of every registered probe for template text The template-text guard reads every YAML file in the asset folder of each registered probe, found through probe_assets_dir, so it reads banks a probe keeps in its own folder. A probe whose folder does not resolve fails the guard. Each finding names the bank, entry, field and pattern. audit_one_bank reads a bank anywhere on disk and loads it with libyaml when it is available. Signed-off-by: Yev Meyer --- tests/_asset_audit.py | 69 ++++++++--------- tests/test_no_template_text.py | 132 +++++++++++++++------------------ 2 files changed, 91 insertions(+), 110 deletions(-) diff --git a/tests/_asset_audit.py b/tests/_asset_audit.py index 4959a5d3..6c1e69f0 100644 --- a/tests/_asset_audit.py +++ b/tests/_asset_audit.py @@ -3,17 +3,20 @@ """Asset-bank audit primitives for ``test_no_template_text.py``. -The audit scans every bank in ``BANK_PATHS`` for template-fill placeholder -strings and for ``LocalizedText`` fields that carry one identical string -across three or more locales. ``test_no_template_text.py`` runs it on +The audit scans every bank ``shipped_banks()`` lists for template-fill +placeholder strings and for ``LocalizedText`` fields that carry one identical +string across three or more locales. ``test_no_template_text.py`` runs it on every push (strict by default) and fails CI if any of the patterns below appear. Public surface: -- ``BANK_PATHS`` — tuple of repo-relative YAML paths to every shipped bank. -- ``audit_one_bank(path)`` — return a per-bank ``{template_hits, +- ``shipped_banks()``: every YAML file in the asset folder of every + registered probe, by name. +- ``audit_one_bank(path)``: return a per-bank ``{template_hits, duplicate_locale_dicts}`` finding dict. +- ``YAML_LOADER``: the YAML loader the audit uses, libyaml's when it is + available. """ from __future__ import annotations @@ -24,7 +27,10 @@ import yaml -REPO_ROOT = Path(__file__).resolve().parent.parent +from usersim.engine.core._assets import probe_assets_dir +from usersim.engine.core.probes import known_probes + +YAML_LOADER = getattr(yaml, "CSafeLoader", yaml.SafeLoader) # All Nemotron-Personas locale ids the simulator currently supports. SUPPORTED_LOCALES: tuple[str, ...] = ( @@ -70,34 +76,22 @@ _TEMPLATE_RES = [(name, re.compile(pat, re.IGNORECASE)) for name, pat in TEMPLATE_PATTERNS] -BANK_PATHS: tuple[str, ...] = ( - "assets/sov_ai_facts/de_DE/sample.yaml", - "assets/sov_ai_facts/en_GB/sample.yaml", - "assets/sov_ai_facts/en_IN/sample.yaml", - "assets/sov_ai_facts/en_SG/sample.yaml", - "assets/sov_ai_facts/en_US/sample.yaml", - "assets/sov_ai_facts/fr_FR/sample.yaml", - "assets/sov_ai_facts/hi_Deva_IN/sample.yaml", - "assets/sov_ai_facts/hi_Latn_IN/sample.yaml", - "assets/sov_ai_facts/ja_JP/sample.yaml", - "assets/sov_ai_facts/ko_KR/sample.yaml", - "assets/sov_ai_facts/pt_BR/sample.yaml", - "assets/safety_chat_pressure/sample.yaml", - "assets/safety_agentic/sample.yaml", - "assets/safety_agentic/action_taxonomy.yaml", - "assets/sov_ai_multilingual_parity/sample.yaml", - "assets/sov_ai_dynamic/de_DE/categories.yaml", - "assets/sov_ai_dynamic/en_GB/categories.yaml", - "assets/sov_ai_dynamic/en_IN/categories.yaml", - "assets/sov_ai_dynamic/en_SG/categories.yaml", - "assets/sov_ai_dynamic/en_US/categories.yaml", - "assets/sov_ai_dynamic/fr_FR/categories.yaml", - "assets/sov_ai_dynamic/hi_Deva_IN/categories.yaml", - "assets/sov_ai_dynamic/hi_Latn_IN/categories.yaml", - "assets/sov_ai_dynamic/ja_JP/categories.yaml", - "assets/sov_ai_dynamic/ko_KR/categories.yaml", - "assets/sov_ai_dynamic/pt_BR/categories.yaml", -) +def shipped_banks() -> dict[str, Path]: + """Every YAML file in the asset folder of every registered probe. + + Keyed by ``