Audit registered node documentation
This commit is contained in:
@@ -39,6 +39,9 @@ The map audit currently sees:
|
||||
- Route simulation now has an opt-in multi-seed sweep, and the smoke suite runs
|
||||
a three-seed sweep so representative route/noise checks are not proven by one
|
||||
lucky seed only.
|
||||
- Map audit now fails when a registered ComfyUI node display name is missing
|
||||
from the route map or README, so utility nodes cannot silently drift out of
|
||||
user-facing documentation.
|
||||
|
||||
## Architectural Finding
|
||||
|
||||
|
||||
@@ -871,14 +871,14 @@ These do not own prompt pool wording, but they affect execution and review:
|
||||
|
||||
| Node family | Files | Purpose |
|
||||
| --- | --- | --- |
|
||||
| Loop nodes | `loop_nodes.py`, `web/loop_slots.js` | While/for loop execution and carry values. |
|
||||
| Index switch | `loop_nodes.py`, `index_switch_policy.py`, `web/index_switch_slots.js` | Multi-input to selected output, and selected input to multi-output routing. Pure index-base, missing-input, route-output, status, and lazy-input policy lives in `index_switch_policy.py`. |
|
||||
| Loop nodes | `loop_nodes.py`, `web/loop_slots.js` | While/for loop execution and carry values. Includes `SxCP While Loop Start`, `SxCP While Loop End`, `SxCP For Loop Start`, `SxCP For Loop End`, `SxCP Loop Int Add`, `SxCP Loop Less Than`, and `SxCP Loop Less Than Or Equal`. |
|
||||
| Index switch | `loop_nodes.py`, `index_switch_policy.py`, `web/index_switch_slots.js` | `SxCP Index Switch`: multi-input to selected output, and selected input to multi-output routing. Pure index-base, missing-input, route-output, status, and lazy-input policy lives in `index_switch_policy.py`. |
|
||||
| Accumulator | `loop_nodes.py`, `web/accumulator_preview.js` | Stores generated values/images during workflow execution and previews/reorders/deletes them. |
|
||||
| Persistent text preview | `loop_nodes.py`, `web/preview_any_text.js` | Stores any value as text and keeps it after workflow reload. |
|
||||
| Builder node wrappers | `node_builder.py`, imported by `__init__.py` | Direct prompt builder and config-driven prompt builder ComfyUI declarations. |
|
||||
| Seed and resolution utility nodes | `node_seed_resolution.py`, imported by `__init__.py` | UI wrappers for global/per-axis seed configs via `seed_config.py`, plus SDXL/Krea width/height helpers. |
|
||||
| Camera utility nodes | `node_camera.py`, imported by `__init__.py` | UI wrappers for direct camera config, orbit-to-camera config, and Qwen MultiAngle camera translation via `camera_config.py`. |
|
||||
| Character utility nodes | `node_character.py`, imported by `__init__.py` | Hair, age/body/eyes/clothing pools, manual details, character slots, and profile save/load nodes. |
|
||||
| Character utility nodes | `node_character.py`, imported by `__init__.py` | Hair, age/body/eyes/clothing pools, manual details, character slots, and profile save/load nodes. Includes `SxCP Character Age Range`, `SxCP Character Body Pool`, `SxCP Woman Body Pool`, `SxCP Man Body Pool`, `SxCP Eye Color Pool`, and `SxCP Character Clothing`. |
|
||||
| Hardcore position utility nodes | `node_hardcore_position.py`, imported by `__init__.py` | Position-family pool and action/filter gates for hardcore routes. |
|
||||
| Formatter utility nodes | `node_formatter.py`, imported by `__init__.py` | Caption naturalizer, Krea2 formatter, and SDXL formatter node wrappers. |
|
||||
| Insta/OF utility nodes | `node_insta.py`, imported by `__init__.py` | Insta/OF option config and dual prompt-pair node wrappers. |
|
||||
@@ -914,11 +914,15 @@ The script does not import ComfyUI. It parses the repo and prints:
|
||||
- route documentation validation so critical route modules are listed in this
|
||||
map and the architecture plan, and registered in `SMOKE_CASES` by their
|
||||
expected smoke cases.
|
||||
- node documentation validation so every registered ComfyUI display name appears
|
||||
in this route map or the README before the node can silently drift out of
|
||||
user-facing docs.
|
||||
|
||||
Use its output to spot doc drift after adding a new node or pool. If a new node
|
||||
or pool appears there but not in this map, update the relevant route table. The
|
||||
script exits nonzero when JSON pool references, item template axes, critical
|
||||
route docs, or critical route smoke registrations do not resolve.
|
||||
route docs, critical route smoke registrations, or registered node display
|
||||
names do not resolve.
|
||||
|
||||
## Behavioral Smoke Helper
|
||||
|
||||
|
||||
@@ -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",
|
||||
"node documentation validation",
|
||||
),
|
||||
)
|
||||
|
||||
PROMPT_ROW_READ_SCAN_GLOBS: tuple[str, ...] = (
|
||||
@@ -137,6 +141,10 @@ ALLOWED_PROMPT_ROW_READS: set[tuple[str, str]] = {
|
||||
|
||||
ROUTE_POLICY_ACTION_EXCLUSIONS = {"default"}
|
||||
ROUTE_POLICY_POSITION_EXCLUSIONS = {"any"}
|
||||
NODE_DOC_PATHS = (
|
||||
"docs/prompt-pool-routing-map.md",
|
||||
"README.md",
|
||||
)
|
||||
|
||||
|
||||
def _literal_or_none(node: ast.AST) -> Any:
|
||||
@@ -734,6 +742,26 @@ def _routing_doc_errors() -> list[tuple[str, str, str]]:
|
||||
return errors
|
||||
|
||||
|
||||
def _node_documentation_errors(display_names: dict[str, Any]) -> list[tuple[str, str, str]]:
|
||||
doc_parts = []
|
||||
for doc_name in NODE_DOC_PATHS:
|
||||
path = ROOT / doc_name
|
||||
if path.exists():
|
||||
doc_parts.append(path.read_text(encoding="utf-8"))
|
||||
doc_text = "\n".join(doc_parts)
|
||||
errors: list[tuple[str, str, str]] = []
|
||||
if not doc_text:
|
||||
return [("(node docs)", ", ".join(NODE_DOC_PATHS), "no node documentation sources found")]
|
||||
for class_name, display_name in sorted(display_names.items(), key=lambda item: str(item[1])):
|
||||
display_text = str(display_name or "").strip()
|
||||
if not display_text:
|
||||
errors.append((str(class_name), "display name", "registered node has empty display name"))
|
||||
continue
|
||||
if display_text not in doc_text:
|
||||
errors.append((str(class_name), "node documentation", f"missing display name in docs: {display_text}"))
|
||||
return errors
|
||||
|
||||
|
||||
class _PromptRowReadVisitor(ast.NodeVisitor):
|
||||
def __init__(self, path: Path) -> None:
|
||||
self.path = path
|
||||
@@ -1071,6 +1099,13 @@ def main() -> int:
|
||||
return 1
|
||||
print("OK: critical route modules are documented and covered by smoke cases.")
|
||||
|
||||
print("\n# Node Documentation Validation")
|
||||
node_doc_errors = _node_documentation_errors(display)
|
||||
if node_doc_errors:
|
||||
print_table(("Node", "Location", "Issue"), node_doc_errors)
|
||||
return 1
|
||||
print("OK: registered node display names are documented in the route map or README.")
|
||||
|
||||
print("\n# Metadata Prompt Fallback Validation")
|
||||
prompt_row_read_errors = _prompt_row_read_errors()
|
||||
if prompt_row_read_errors:
|
||||
|
||||
Reference in New Issue
Block a user