Add export cleanup candidate inspection#94
Conversation
📝 WalkthroughWalkthroughAdds the Changesinspect_export_cleanup_candidates Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: dependency version conflict. Check your lock file or package.json. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@data/tool-guides/export-guide.md`:
- Line 35: The description for the `inspect_export_cleanup_candidates` function
on line 35 is incomplete and only mentions modifier/constraint targets. Update
the wording to include all types of protected targets that the feature actually
protects: parent-linked targets, Boolean-referenced targets, and
modifier/constraint targets. Expand the description to comprehensively list
these three categories so it accurately reflects the actual behavior and
prevents unsafe cleanup assumptions.
In `@desktop/assets/vipermesh-addon.py`:
- Around line 5242-5256: After parsing the names parameter with parse_names()
and creating requested_set, add validation to verify that all requested target
names actually exist in the scene or context being operated on. If any names in
requested_set cannot be found, return an error indicating which names are
unresolved or missing. This prevents typos from silently being accepted and
leaving intended targets unprotected when performing hide, move_to_collection,
or delete operations.
- Around line 5226-5240: The pattern_matches function currently allows
wildcard-only patterns like "*" to match everything because after stripping
asterisks an empty string matches all content, which is dangerous with delete
operations. In the pattern_matches function, add a validation check after
normalizing the token to reject patterns that consist only of wildcards by
checking if token.strip("*") results in an empty string, and skip those patterns
before executing any matching logic.
- Around line 5258-5274: The loop that builds referenced_object_names iterates
through bpy.data.objects which includes all objects across all scenes in the
.blend file, causing unintended mutations to objects outside the active export
context. Replace the iteration over bpy.data.objects with either
bpy.context.scene.objects to scope to the active scene, or filter the objects
based on the provided names parameter to ensure only the intended objects are
processed. This will prevent the subsequent hide, move_to_collection, and delete
operations from affecting objects in other scenes.
In `@lib/orchestration/tool-filter.ts`:
- Line 209: The keyword regex pattern in the pipeline category is missing
"reference object(s)" keywords, which causes requests about cleaning up
reference objects to miss the correct routing path even though
inspect_export_cleanup_candidates handles them. Add "reference object(s)" (or
"reference objects?") to the keywords regex pattern alongside the existing
cleanup, helper, and construction object phrases to ensure proper
categorization.
In `@public/downloads/vipermesh-addon.py`:
- Around line 5208-5215: The parse_names function currently returns an empty
list for invalid input types (the final return [] statement), which silently
treats malformed names as "no protected targets" and allows mutating actions to
proceed unsafely. Instead of returning an empty list as a fallback for unhandled
input types, raise an exception (such as ValueError or TypeError) with a clear
message indicating that the names value is invalid. This ensures that invalid or
unexpected data types are rejected outright rather than silently bypassing
protection mechanisms.
- Around line 5285-5286: The code at lines 5285-5286 classifies any object with
type CAMERA or LIGHT as a cleanup candidate based solely on its type, which
incorrectly flags legitimate scene cameras and lights. Fix this by removing
CAMERA and LIGHT from the type check in the condition (keep only EMPTY for
type-only matching), and instead add cameras and lights to the reasons list only
when they meet additional criteria such as matching a helper naming pattern or
being marked as hidden.
- Around line 5270-5273: The Boolean modifier collection iteration uses
collection.objects which only includes directly-contained objects and misses
nested cutter/reference objects in child collections. Replace collection.objects
with collection.all_objects in the for loop that iterates over collection_obj to
ensure the full collection hierarchy is evaluated when building the
referenced_object_names set.
- Around line 5292-5293: The hidden candidate detection in the condition
checking include_hidden with obj.hide_viewport and obj.hide_render only
evaluates global visibility properties and misses view-layer-specific hidden
state that can be accessed via hide_get(). Replace the current visibility check
by using visible_get() instead of directly checking hide_viewport and
hide_render properties, since visible_get() comprehensively evaluates all
visibility constraints including view-layer-specific hidden state, collection
visibility, and other settings. Update the condition where
reasons.append("hidden") is executed to leverage visible_get() for complete
hidden candidate detection.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4fbe4a0b-eb1b-4a1a-928a-a508b6aca2cc
📒 Files selected for processing (11)
data/tool-guides/export-guide.mddata/tool-guides/scene-management-guide.mddesktop/assets/vipermesh-addon.pydocs/blender-mcp-capability-inventory.mdlib/ai/agents.tslib/orchestration/prompts/blender-agent-system.mdlib/orchestration/tool-filter.tslib/orchestration/tool-registry.tspublic/downloads/vipermesh-addon.pyscripts/test/test-blender-export-cleanup-candidates.tsscripts/test/test-tool-guide-trigger-coverage.ts
| 2. **Apply all modifiers** (if you want clean geometry) — SubSurf, mirrors, booleans become permanent | ||
| 3. **Set correct origin** — Usually center of geometry or bottom center for grounding | ||
| 4. **Clean up** — Remove unused objects, helper empties, construction geometry | ||
| 4. **Inspect cleanup candidates** (`inspect_export_cleanup_candidates`) — Find helper empties, reference objects, hidden construction geometry, and protected modifier/constraint targets before cleanup |
There was a problem hiding this comment.
Broaden the protected-target wording to match actual behavior.
Line 35 only mentions modifier/constraint targets, but the feature also protects parent-linked and Boolean-referenced targets. Tightening this wording avoids unsafe cleanup assumptions.
Suggested doc fix
-4. **Inspect cleanup candidates** (`inspect_export_cleanup_candidates`) — Find helper empties, reference objects, hidden construction geometry, and protected modifier/constraint targets before cleanup
+4. **Inspect cleanup candidates** (`inspect_export_cleanup_candidates`) — Find helper empties, reference objects, hidden construction geometry, and protected dependency targets (parents, constraints, Boolean references, and explicit export targets) before cleanup📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 4. **Inspect cleanup candidates** (`inspect_export_cleanup_candidates`) — Find helper empties, reference objects, hidden construction geometry, and protected modifier/constraint targets before cleanup | |
| 4. **Inspect cleanup candidates** (`inspect_export_cleanup_candidates`) — Find helper empties, reference objects, hidden construction geometry, and protected dependency targets (parents, constraints, Boolean references, and explicit export targets) before cleanup |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@data/tool-guides/export-guide.md` at line 35, The description for the
`inspect_export_cleanup_candidates` function on line 35 is incomplete and only
mentions modifier/constraint targets. Update the wording to include all types of
protected targets that the feature actually protects: parent-linked targets,
Boolean-referenced targets, and modifier/constraint targets. Expand the
description to comprehensively list these three categories so it accurately
reflects the actual behavior and prevents unsafe cleanup assumptions.
| def pattern_matches(value, patterns): | ||
| normalized = str(value or "").lower() | ||
| for pattern in patterns: | ||
| token = str(pattern).strip().lower() | ||
| if not token: | ||
| continue | ||
| if token.startswith("*") and token.endswith("*") and token.strip("*") in normalized: | ||
| return True | ||
| if token.endswith("*") and normalized.startswith(token[:-1]): | ||
| return True | ||
| if token.startswith("*") and normalized.endswith(token[1:]): | ||
| return True | ||
| if normalized == token or token in normalized: | ||
| return True | ||
| return False |
There was a problem hiding this comment.
Reject wildcard-only helper patterns before matching.
helper_patterns=["*"] currently makes every unprotected object a candidate because an empty string matches every name; with action="delete", that can remove non-helper scene content.
Proposed fix
def pattern_matches(value, patterns):
normalized = str(value or "").lower()
for pattern in patterns:
token = str(pattern).strip().lower()
if not token:
continue
- if token.startswith("*") and token.endswith("*") and token.strip("*") in normalized:
+ wildcard_core = token.strip("*")
+ if "*" in token and not wildcard_core:
+ continue
+ if token.startswith("*") and token.endswith("*") and wildcard_core in normalized:
return True
if token.endswith("*") and normalized.startswith(token[:-1]):
return True🧰 Tools
🪛 Ruff (0.15.17)
[warning] 5226-5226: Missing return type annotation for private function pattern_matches
Add return type annotation: bool
(ANN202)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@desktop/assets/vipermesh-addon.py` around lines 5226 - 5240, The
pattern_matches function currently allows wildcard-only patterns like "*" to
match everything because after stripping asterisks an empty string matches all
content, which is dangerous with delete operations. In the pattern_matches
function, add a validation check after normalizing the token to reject patterns
that consist only of wildcards by checking if token.strip("*") results in an
empty string, and skip those patterns before executing any matching logic.
| requested_names = parse_names(names) | ||
| requested_set = set(requested_names) | ||
| if helper_patterns is None: | ||
| helper_patterns = [ | ||
| "helper", "helpers", "reference", "ref_", "guide", "construction", | ||
| "cutter", "boolean", "temp", "tmp", "proxy", "debug", "draft" | ||
| ] | ||
| elif isinstance(helper_patterns, str): | ||
| helper_patterns = [part.strip() for part in helper_patterns.split(",") if part.strip()] | ||
| elif not isinstance(helper_patterns, (list, tuple)): | ||
| return {"error": "helper_patterns must be an array, a comma-separated string, or omitted"} | ||
|
|
||
| action = str(action or "report").lower().replace("-", "_") | ||
| if action not in {"report", "hide", "move_to_collection", "delete"}: | ||
| return {"error": "action must be report, hide, move_to_collection, or delete"} |
There was a problem hiding this comment.
Fail mutating cleanup when protected target names are missing.
names is the explicit export-target protection list, but unresolved entries are silently accepted. A typo can leave the intended target unprotected for hide, move_to_collection, or delete.
Proposed fix
requested_names = parse_names(names)
requested_set = set(requested_names)
@@
action = str(action or "report").lower().replace("-", "_")
if action not in {"report", "hide", "move_to_collection", "delete"}:
return {"error": "action must be report, hide, move_to_collection, or delete"}
+
+ missing_requested_names = [
+ name for name in requested_names
+ if bpy.data.objects.get(name) is None
+ ]
+ if missing_requested_names and action != "report":
+ return {
+ "error": "Cannot mutate cleanup candidates until all protected target names resolve.",
+ "missing_objects": missing_requested_names,
+ }
@@
"referenced_object_names": sorted(referenced_object_names),
"skipped_protected_count": len(set(skipped_protected)),
+ "missing_objects": missing_requested_names,
"helper_patterns": list(helper_patterns),Also applies to: 5332-5344
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@desktop/assets/vipermesh-addon.py` around lines 5242 - 5256, After parsing
the names parameter with parse_names() and creating requested_set, add
validation to verify that all requested target names actually exist in the scene
or context being operated on. If any names in requested_set cannot be found,
return an error indicating which names are unresolved or missing. This prevents
typos from silently being accepted and leaving intended targets unprotected when
performing hide, move_to_collection, or delete operations.
| referenced_object_names = set() | ||
| for obj in bpy.data.objects: | ||
| if obj.parent: | ||
| referenced_object_names.add(obj.parent.name) | ||
| for constraint in getattr(obj, "constraints", []): | ||
| target = getattr(constraint, "target", None) | ||
| if target: | ||
| referenced_object_names.add(target.name) | ||
| for modifier in getattr(obj, "modifiers", []): | ||
| target = getattr(modifier, "object", None) | ||
| if target: | ||
| referenced_object_names.add(target.name) | ||
| collection = getattr(modifier, "collection", None) | ||
| if collection: | ||
| for collection_obj in collection.objects: | ||
| referenced_object_names.add(collection_obj.name) | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n desktop/assets/vipermesh-addon.py | sed -n '5250,5340p'Repository: Ker102/ViperMesh
Length of output: 5334
🏁 Script executed:
# Check the function context and see where this code is called from
cat -n desktop/assets/vipermesh-addon.py | sed -n '5200,5250p'Repository: Ker102/ViperMesh
Length of output: 2713
🏁 Script executed:
# Check what happens after line 5330 to see the mutations
cat -n desktop/assets/vipermesh-addon.py | sed -n '5310,5350p'Repository: Ker102/ViperMesh
Length of output: 2451
🏁 Script executed:
# Find the function definition and its name
rg "def.*helper.*reference" desktop/assets/vipermesh-addon.py -A 2Repository: Ker102/ViperMesh
Length of output: 42
🏁 Script executed:
# Search for calls to this function
rg "helper.*reference|export.*cleanup" desktop/assets/vipermesh-addon.pyRepository: Ker102/ViperMesh
Length of output: 458
🏁 Script executed:
# Check if this function is part of a class and understand context better
sed -n '5150,5210p' desktop/assets/vipermesh-addon.pyRepository: Ker102/ViperMesh
Length of output: 2348
🏁 Script executed:
# Find where inspect_export_cleanup_candidates is called
rg "inspect_export_cleanup_candidates" desktop/assets/vipermesh-addon.pyRepository: Ker102/ViperMesh
Length of output: 192
🏁 Script executed:
# Check if there are any export-related functions that call this
rg "export_asset_package|validate_export_readiness" desktop/assets/vipermesh-addon.py -B 5 -A 5Repository: Ker102/ViperMesh
Length of output: 4168
🏁 Script executed:
# Search for documentation or comments about scene scope
rg -i "scene|scope|blend" desktop/assets/vipermesh-addon.py | grep -i "cleanup\|helper\|reference" | head -20Repository: Ker102/ViperMesh
Length of output: 101
🏁 Script executed:
# Check if the 'names' parameter is used in the function at all
sed -n '5198,5350p' desktop/assets/vipermesh-addon.py | grep -n "names\|requested_set"Repository: Ker102/ViperMesh
Length of output: 1099
🏁 Script executed:
# Check the validate_export_readiness function to see how it uses names parameter
rg "def validate_export_readiness" -A 50 desktop/assets/vipermesh-addon.py | head -70Repository: Ker102/ViperMesh
Length of output: 2541
🏁 Script executed:
# Check if there are any tests or examples showing usage
rg "inspect_export_cleanup_candidates" -B 5 -A 10 desktop/assets/vipermesh-addon.py | grep -A 10 "example\|test\|usage" -iRepository: Ker102/ViperMesh
Length of output: 42
🏁 Script executed:
# Look for how export_asset_package uses this function in its workflow
sed -n '5500,5650p' desktop/assets/vipermesh-addon.py | head -100Repository: Ker102/ViperMesh
Length of output: 3842
🏁 Script executed:
# Check if there are any scene-specific operations in the codebase
rg "bpy.context.scene" desktop/assets/vipermesh-addon.py | wc -lRepository: Ker102/ViperMesh
Length of output: 63
🏁 Script executed:
# Search for any multi-scene handling or scene switching
rg "bpy.context.window.scene\|bpy.context.scene =" desktop/assets/vipermesh-addon.pyRepository: Ker102/ViperMesh
Length of output: 42
🏁 Script executed:
# Check the README or documentation for context about this function
find desktop -name "README*" -o -name "*.md" | head -5Repository: Ker102/ViperMesh
Length of output: 78
🏁 Script executed:
# Search for any comments about scene scope in the file
rg -B 2 -A 2 "scene\|Scene" desktop/assets/vipermesh-addon.py | grep -A 3 -B 3 "cleanup\|helper\|export"Repository: Ker102/ViperMesh
Length of output: 42
🏁 Script executed:
# Check what export_asset_package does with object selection
rg "def export_asset_package" -A 100 desktop/assets/vipermesh-addon.py | head -120Repository: Ker102/ViperMesh
Length of output: 4854
Scope cleanup candidates to the active export scene or use the provided names parameter.
The scan uses bpy.data.objects, then mutates those candidates with hide, move_to_collection, and delete, so helpers from other scenes in the same .blend can be affected even when the function is called with specific object names. The function accepts a names parameter but does not use it to constrain the scan. Use either bpy.context.scene.objects or filter candidates based on the names parameter to limit scope to the active export context.
Proposed fix (active scene scope)
- referenced_object_names = set()
- for obj in bpy.data.objects:
+ scene_objects = list(bpy.context.scene.objects)
+
+ referenced_object_names = set()
+ for obj in scene_objects:
if obj.parent:
referenced_object_names.add(obj.parent.name)
@@
- for obj in bpy.data.objects:
+ for obj in scene_objects:
reasons = []
if obj.name in protected_names:
skipped_protected.append(obj.name)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@desktop/assets/vipermesh-addon.py` around lines 5258 - 5274, The loop that
builds referenced_object_names iterates through bpy.data.objects which includes
all objects across all scenes in the .blend file, causing unintended mutations
to objects outside the active export context. Replace the iteration over
bpy.data.objects with either bpy.context.scene.objects to scope to the active
scene, or filter the objects based on the provided names parameter to ensure
only the intended objects are processed. This will prevent the subsequent hide,
move_to_collection, and delete operations from affecting objects in other
scenes.
| { keywords: GEOMETRY_KEYWORDS, categories: ["geometry"] }, | ||
| { keywords: /(color|material|texture|paint|shade|metallic)/i, categories: ["materials"] }, | ||
| { keywords: /(uv|unwrap|pack island|export|glb|gltf|fbx|obj|stl|file format|package|texture dependenc|missing image|empty filepath|risky texture)/i, categories: ["pipeline"] }, | ||
| { keywords: /(uv|unwrap|pack island|export|glb|gltf|fbx|obj|stl|file format|package|texture dependenc|missing image|empty filepath|risky texture|cleanup candidates?|helper objects?|construction objects?)/i, categories: ["pipeline"] }, |
There was a problem hiding this comment.
Include “reference object(s)” in pipeline keyword matching.
inspect_export_cleanup_candidates handles reference objects too, but the new keyword regex only adds cleanup/helper/construction phrases. Requests like “clean up reference objects before export” may miss this routing path.
Suggested patch
- { keywords: /(uv|unwrap|pack island|export|glb|gltf|fbx|obj|stl|file format|package|texture dependenc|missing image|empty filepath|risky texture|cleanup candidates?|helper objects?|construction objects?)/i, categories: ["pipeline"] },
+ { keywords: /(uv|unwrap|pack island|export|glb|gltf|fbx|obj|stl|file format|package|texture dependenc|missing image|empty filepath|risky texture|cleanup candidates?|helper objects?|reference objects?|construction objects?)/i, categories: ["pipeline"] },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/orchestration/tool-filter.ts` at line 209, The keyword regex pattern in
the pipeline category is missing "reference object(s)" keywords, which causes
requests about cleaning up reference objects to miss the correct routing path
even though inspect_export_cleanup_candidates handles them. Add "reference
object(s)" (or "reference objects?") to the keywords regex pattern alongside the
existing cleanup, helper, and construction object phrases to ensure proper
categorization.
| def parse_names(value): | ||
| if value is None: | ||
| return [] | ||
| if isinstance(value, str): | ||
| return [part.strip() for part in value.split(",") if part.strip()] | ||
| if isinstance(value, (list, tuple)): | ||
| return [str(name).strip() for name in value if str(name).strip()] | ||
| return [] |
There was a problem hiding this comment.
Reject invalid names instead of treating them as “no protected targets”.
A malformed names value currently becomes [], so mutating actions can proceed without protecting the intended export targets.
🛡️ Proposed fix
def parse_names(value):
if value is None:
return []
if isinstance(value, str):
return [part.strip() for part in value.split(",") if part.strip()]
if isinstance(value, (list, tuple)):
return [str(name).strip() for name in value if str(name).strip()]
- return []
+ raise ValueError("names must be an array of object names, a comma-separated string, or omitted")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def parse_names(value): | |
| if value is None: | |
| return [] | |
| if isinstance(value, str): | |
| return [part.strip() for part in value.split(",") if part.strip()] | |
| if isinstance(value, (list, tuple)): | |
| return [str(name).strip() for name in value if str(name).strip()] | |
| return [] | |
| def parse_names(value): | |
| if value is None: | |
| return [] | |
| if isinstance(value, str): | |
| return [part.strip() for part in value.split(",") if part.strip()] | |
| if isinstance(value, (list, tuple)): | |
| return [str(name).strip() for name in value if str(name).strip()] | |
| raise ValueError("names must be an array of object names, a comma-separated string, or omitted") |
🧰 Tools
🪛 Ruff (0.15.17)
[warning] 5208-5208: Missing return type annotation for private function parse_names
(ANN202)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@public/downloads/vipermesh-addon.py` around lines 5208 - 5215, The
parse_names function currently returns an empty list for invalid input types
(the final return [] statement), which silently treats malformed names as "no
protected targets" and allows mutating actions to proceed unsafely. Instead of
returning an empty list as a fallback for unhandled input types, raise an
exception (such as ValueError or TypeError) with a clear message indicating that
the names value is invalid. This ensures that invalid or unexpected data types
are rejected outright rather than silently bypassing protection mechanisms.
| collection = getattr(modifier, "collection", None) | ||
| if collection: | ||
| for collection_obj in collection.objects: | ||
| referenced_object_names.add(collection_obj.name) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Blender Python API Collection.objects vs Collection.all_objects Boolean modifier collection operand nested collections
💡 Result:
In the Blender Python API, there is a critical distinction between a Collection's objects and its all_objects properties [1][2]: 1. Collection.objects: Returns only the objects directly contained within that specific collection [1][3]. It does not include objects located in nested (child) collections [3]. 2. Collection.all_objects: Returns all objects located within that collection, including those residing in its child and descendant collections [1][3]. Important Iteration Gotcha When iterating over all_objects, you should never modify the collection structure or object properties that trigger a cache rebuild (e.g., changing hide_viewport, hide_select, or deleting objects) while looping directly over the iterator [4]. Doing so will invalidate the memory accessed by the iterator and cause crashes [4]. Always convert the collection to a list or tuple first [4][5]: # Correct approach to avoid crashes for obj in collection.all_objects[:]: obj.hide_viewport = True Boolean Modifier Collection Operand When using the Boolean modifier in 'COLLECTION' mode, you specify a collection as the operand [6][7]. The Boolean modifier operates on the target collection, and it effectively uses the contents of that collection to perform the operation [8]. Using a collection operand allows you to group multiple mesh objects to act as a single boolean source [6][8]. If you need to perform a boolean operation specifically on a subset of objects including nested ones, ensure the collection assigned to the modifier's collection property contains all necessary sub-collections, as the modifier will evaluate the entire tree of the assigned collection [8]. Top Results: [1] https://docs.blender.org/api/master/bpy.types.Collection.html [4] https://docs.blender.org/api/latest/info_gotchas_crashes.html [6] https://docs.blender.org/api/latest/bpy.types.BooleanModifier.html [8] https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/booleans.html [3] https://b3d.interplanety.org/en/selecting-objects-in-a-collection/ [5] https://blender.stackexchange.com/questions/279443/python-script-not-iterating-through-all-objects-in-collections
Citations:
- 1: https://docs.blender.org/api/master/bpy.types.Collection.html
- 2: https://docs.blender.org/api/current/bpy.types.Collection.html
- 3: https://b3d.interplanety.org/en/selecting-objects-in-a-collection/
- 4: https://docs.blender.org/api/latest/info_gotchas_crashes.html
- 5: https://blender.stackexchange.com/questions/279443/python-script-not-iterating-through-all-objects-in-collections
- 6: https://docs.blender.org/api/latest/bpy.types.BooleanModifier.html
- 7: https://docs.blender.org/api/current/bpy.types.BooleanModifier.html
- 8: https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/booleans.html?highlight=boolean
Protect nested Boolean collection operands with recursive collection membership.
The Boolean modifier evaluates the entire tree of its assigned collection. Currently, collection.objects only includes directly-contained objects; nested cutter/reference objects in child collections can be missed during classification, and thus incorrectly mutated or deleted. Use all_objects to include the full collection hierarchy.
Proposed fix
collection = getattr(modifier, "collection", None)
if collection:
- for collection_obj in collection.objects:
+ for collection_obj in getattr(collection, "all_objects", collection.objects):
referenced_object_names.add(collection_obj.name)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| collection = getattr(modifier, "collection", None) | |
| if collection: | |
| for collection_obj in collection.objects: | |
| referenced_object_names.add(collection_obj.name) | |
| collection = getattr(modifier, "collection", None) | |
| if collection: | |
| for collection_obj in getattr(collection, "all_objects", collection.objects): | |
| referenced_object_names.add(collection_obj.name) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@public/downloads/vipermesh-addon.py` around lines 5270 - 5273, The Boolean
modifier collection iteration uses collection.objects which only includes
directly-contained objects and misses nested cutter/reference objects in child
collections. Replace collection.objects with collection.all_objects in the for
loop that iterates over collection_obj to ensure the full collection hierarchy
is evaluated when building the referenced_object_names set.
Source: MCP tools
| if obj.type in {"EMPTY", "CAMERA", "LIGHT"}: | ||
| reasons.append(f"type:{obj.type}") |
There was a problem hiding this comment.
Do not classify every camera and light as cleanup by type alone.
This makes normal scene cameras/lights candidates for hide/move/delete even when they are not helper/reference/construction objects. Restrict type-only matching to EMPTY, or require cameras/lights to match a helper pattern or be hidden.
🛡️ Proposed fix
- if obj.type in {"EMPTY", "CAMERA", "LIGHT"}:
+ if obj.type == "EMPTY":
reasons.append(f"type:{obj.type}")If cameras/lights should still be reported, add them only when another signal applies:
if pattern_matches(obj.name, helper_patterns):
reasons.append("name_pattern")
collection_names = [collection.name for collection in obj.users_collection]
if any(pattern_matches(collection_name, helper_patterns) for collection_name in collection_names):
reasons.append("collection_pattern")
if include_hidden and (obj.hide_viewport or obj.hide_render):
reasons.append("hidden")
+ if obj.type in {"CAMERA", "LIGHT"} and reasons:
+ reasons.append(f"type:{obj.type}")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if obj.type in {"EMPTY", "CAMERA", "LIGHT"}: | |
| reasons.append(f"type:{obj.type}") | |
| if obj.type == "EMPTY": | |
| reasons.append(f"type:{obj.type}") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@public/downloads/vipermesh-addon.py` around lines 5285 - 5286, The code at
lines 5285-5286 classifies any object with type CAMERA or LIGHT as a cleanup
candidate based solely on its type, which incorrectly flags legitimate scene
cameras and lights. Fix this by removing CAMERA and LIGHT from the type check in
the condition (keep only EMPTY for type-only matching), and instead add cameras
and lights to the reasons list only when they meet additional criteria such as
matching a helper naming pattern or being marked as hidden.
| if include_hidden and (obj.hide_viewport or obj.hide_render): | ||
| reasons.append("hidden") |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Blender Python Object.hide_get Object.hide_set hide_viewport hide_render visible_get API
💡 Result:
In the Blender Python API, managing object visibility involves distinct properties and methods on the bpy.types.Object class [1][2]. Key Properties and Methods: hide_viewport: A boolean property that controls whether the object is hidden in the 3D viewport [1][3]. hide_render: A boolean property that controls whether the object is hidden during rendering [1][2]. hide_get(view_layer=None): A method that returns the hidden state of an object for a specific view layer [1][4]. hide_set(state, *, view_layer=None): A method that sets the hidden state of an object for a specific view layer [1][5]. visible_get(view_layer=None, viewport=None): A method that returns whether an object is effectively visible in the 3D viewport, taking into account all relevant visibility settings such as viewport hiding, collection visibility, and local view [2][4]. Important Distinctions: The hide_viewport and hide_render properties are typically used for direct state toggling [1]. Conversely, hide_get and hide_set provide a more robust interface that supports view layer-specific control [1][2]. The visible_get method is the recommended way to check if an object is truly visible to the user in the viewport, as it aggregates the complex criteria (like object constraints, parent visibility, and viewport isolation) that determine final visibility [2][6]. Note: These methods and properties differ from operators like bpy.ops.object.hide_view_set, which are intended for UI-driven interactions and perform actions equivalent to user keypresses [7]. For scripted object manipulation, modifying the properties or using the _set / _get methods directly is the standard approach [1].
Citations:
- 1: https://docs.blender.org/api/main/bpy.types.Object.html
- 2: https://docs.blender.org/api/current/bpy.types.Object.html
- 3: https://docs.blender.org/api/4.4/bpy.types.Object.html
- 4: https://docs.blender.org/api/4.2/bpy.types.Object.html
- 5: https://docs.blender.org/api/3.3/bpy.types.Object.html
- 6: https://docs.blender.org/api/3.0/bpy.types.Object.html
- 7: https://docs.blender.org/api/current/bpy.ops.object.html
Include view-layer-specific hidden state in hidden candidate detection.
The current check only examines hide_viewport and hide_render, which are global visibility settings. Objects can also be hidden per-view-layer via hide_get(), which isn't currently checked. This causes incomplete detection when objects are hidden only in specific view layers. Since the codebase already uses visible_get() for comprehensive visibility checking, consider using it here as well—it accounts for view-layer-specific hidden state, collection visibility, and other constraints.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@public/downloads/vipermesh-addon.py` around lines 5292 - 5293, The hidden
candidate detection in the condition checking include_hidden with
obj.hide_viewport and obj.hide_render only evaluates global visibility
properties and misses view-layer-specific hidden state that can be accessed via
hide_get(). Replace the current visibility check by using visible_get() instead
of directly checking hide_viewport and hide_render properties, since
visible_get() comprehensively evaluates all visibility constraints including
view-layer-specific hidden state, collection visibility, and other settings.
Update the condition where reasons.append("hidden") is executed to leverage
visible_get() for complete hidden candidate detection.
Summary
inspect_export_cleanup_candidatesto report helper/reference/hidden/construction objects before export.report,hide,move_to_collection, ordelete, with report as the default.Validation
npx tsx scripts/test/test-blender-export-cleanup-candidates.tspython -m py_compile desktop/assets/vipermesh-addon.py public/downloads/vipermesh-addon.pynpx tsx scripts/test/test-blender-hierarchy-inspection-tool.tsnpx tsx scripts/test/test-blender-uv-export-validation.tsnpx tsx scripts/test/test-tool-guide-trigger-coverage.tsnpx tsx scripts/test/test-blender-capability-inventory.tsnpx tsc --noEmit --incremental falsenpm run lint(passes; existing baseline-browser-mapping age warning)git diff --check(passes; Windows line-ending warnings only)Summary by CodeRabbit
New Features
Documentation