Audit category selector identities

This commit is contained in:
2026-06-27 20:59:37 +02:00
parent de6615c024
commit 1f9544233e
3 changed files with 140 additions and 6 deletions
+131
View File
@@ -118,6 +118,10 @@ AUDIT_DOC_SNIPPETS: tuple[tuple[str, str], ...] = (
"docs/prompt-pool-routing-map.md",
"multi-seed route sweeps",
),
(
"docs/prompt-pool-routing-map.md",
"category identity validation",
),
(
"docs/prompt-pool-routing-map.md",
"node documentation validation",
@@ -141,6 +145,15 @@ ALLOWED_PROMPT_ROW_READS: set[tuple[str, str]] = {
ROUTE_POLICY_ACTION_EXCLUSIONS = {"default"}
ROUTE_POLICY_POSITION_EXCLUSIONS = {"any"}
RESERVED_CATEGORY_CHOICES = {
"auto_weighted",
"auto_full",
"woman",
"man",
"couple",
"group_or_layout",
"custom_random",
}
NODE_DOC_PATHS = (
"docs/prompt-pool-routing-map.md",
"README.md",
@@ -418,6 +431,117 @@ def _json_reference_errors(paths: list[Path]) -> list[tuple[str, str, str]]:
return errors
def _identity_key(value: Any) -> str:
return re.sub(r"\s+", " ", str(value or "").strip()).lower()
def _identity_location(category: dict[str, Any], subcategory: dict[str, Any] | None = None) -> str:
category_slug = str(category.get("slug") or category.get("name") or "category").strip()
location = f"categories.{category_slug}"
if subcategory is not None:
sub_slug = str(subcategory.get("slug") or subcategory.get("name") or "subcategory").strip()
location = f"{location}.subcategories.{sub_slug}"
return location
def _add_duplicate_identity_error(
seen: dict[str, str],
*,
key: str,
location: str,
label: str,
errors: list[tuple[str, str, str]],
) -> None:
if not key:
return
previous = seen.get(key)
if previous is not None:
errors.append(("(category library)", location, f"duplicate {label}: {key!r} also used at {previous}"))
return
seen[key] = location
def _category_identity_errors(paths: list[Path]) -> list[tuple[str, str, str]]:
# Use the normalized library because that is the selector surface exposed by
# category/preset nodes and exact subcategory routing.
_ = paths
try:
categories = category_policy.load_category_library()
except Exception as exc:
return [("(category library)", "categories", f"cannot load categories: {exc}")]
errors: list[tuple[str, str, str]] = []
category_names: dict[str, str] = {}
category_slugs: dict[str, str] = {}
exact_selectors: dict[str, str] = {}
exact_slug_selectors: dict[str, str] = {}
for category in categories:
location = _identity_location(category)
name = _identity_key(category.get("name"))
slug = _identity_key(category.get("slug"))
if not name:
errors.append(("(category library)", f"{location}.name", "missing normalized category name"))
if not slug:
errors.append(("(category library)", f"{location}.slug", "missing normalized category slug"))
if name in RESERVED_CATEGORY_CHOICES or slug in RESERVED_CATEGORY_CHOICES:
errors.append(
(
"(category library)",
location,
"category identity collides with reserved built-in selector",
)
)
_add_duplicate_identity_error(category_names, key=name, location=location, label="category name", errors=errors)
_add_duplicate_identity_error(category_slugs, key=slug, location=location, label="category slug", errors=errors)
subcategory_names: dict[str, str] = {}
subcategory_slugs: dict[str, str] = {}
for subcategory in category.get("subcategories") or []:
if not isinstance(subcategory, dict):
continue
sub_location = _identity_location(category, subcategory)
sub_name = _identity_key(subcategory.get("name"))
sub_slug = _identity_key(subcategory.get("slug"))
if not sub_name:
errors.append(("(category library)", f"{sub_location}.name", "missing normalized subcategory name"))
if not sub_slug:
errors.append(("(category library)", f"{sub_location}.slug", "missing normalized subcategory slug"))
_add_duplicate_identity_error(
subcategory_names,
key=sub_name,
location=sub_location,
label=f"subcategory name in {category.get('name')}",
errors=errors,
)
_add_duplicate_identity_error(
subcategory_slugs,
key=sub_slug,
location=sub_location,
label=f"subcategory slug in {category.get('name')}",
errors=errors,
)
exact_selector = _identity_key(category_policy.exact_subcategory_selector(category, subcategory))
slug_selector = f"{slug} / {sub_slug}" if slug and sub_slug else ""
_add_duplicate_identity_error(
exact_selectors,
key=exact_selector,
location=sub_location,
label="exact subcategory selector",
errors=errors,
)
_add_duplicate_identity_error(
exact_slug_selectors,
key=slug_selector,
location=sub_location,
label="category/subcategory slug selector",
errors=errors,
)
return errors
def _hardcore_template_metadata_errors(paths: list[Path]) -> list[tuple[str, str, str]]:
errors: list[tuple[str, str, str]] = []
for path in paths:
@@ -1064,6 +1188,13 @@ def main() -> int:
return 1
print("OK: all JSON pool references and item template axes resolve.")
print("\n# Category Identity Validation")
category_identity_errors = _category_identity_errors(category_paths)
if category_identity_errors:
print_table(("Source", "Path", "Issue"), category_identity_errors)
return 1
print("OK: category and subcategory identities are unique and selector-safe.")
print("\n# Hardcore Template Metadata Validation")
hardcore_metadata_errors = _hardcore_template_metadata_errors(category_paths)
if hardcore_metadata_errors: