Coverage for openhcs/core/pipeline/step_attribute_stripper.py: 61.9%

30 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-14 05:57 +0000

1""" 

2Step attribute stripper for OpenHCS. 

3 

4This module provides the StepAttributeStripper class, which is responsible for 

5stripping all attributes from Step instances after planning, ensuring that steps 

6are attribute-less shells that operate solely via the processing context. 

7 

8Doctrinal Clauses: 

9- Clause 12 — Absolute Clean Execution 

10- Clause 66 — Immutability After Construction 

11- Clause 88 — No Inferred Capabilities 

12- Clause 245 — Plan Context Supremacy 

13- Clause 503 — Cognitive Load Transfer 

14""" 

15 

16import logging 

17from typing import Any, Dict, List 

18 

19logger = logging.getLogger(__name__) 

20 

21# Error messages for doctrinal violations 

22ERROR_ATTRIBUTE_DELETION_FAILED = ( 

23 "Clause 66 Violation: Failed to delete attribute '{0}' from step '{1}'. " 

24 "All attributes must be stripped after planning." 

25) 

26 

27ERROR_RESERVED_ATTRIBUTE = ( 

28 "Clause 245 Violation: Step '{0}' has reserved attribute '{1}' that cannot be deleted. " 

29 "This indicates a design flaw in the step implementation." 

30) 

31 

32 

33class StepAttributeStripper: 

34 """ 

35 Planner that strips all attributes from Step instances after planning. 

36 

37 This planner ensures that steps are attribute-less shells that operate 

38 solely via the processing context, with no mutable state or pre-declared 

39 fields accessible during execution. 

40 

41 Key principles: 

42 1. All attributes must be stripped from steps after planning 

43 2. Steps must operate solely via the processing context 

44 3. No mutable state or pre-declared fields can be accessed during execution 

45 4. Execution must be based solely on the plan context 

46 """ 

47 

48 @staticmethod 

49 def strip_step_attributes(steps: List[Any], step_plans: Dict[str, Dict[str, Any]]) -> None: 

50 """ 

51 Strip all attributes from Step instances after planning. 

52 

53 Args: 

54 steps: List of Step instances 

55 step_plans: Dictionary mapping step UIDs to step plans 

56 

57 Raises: 

58 ValueError: If attribute deletion fails 

59 RuntimeError: If a step has reserved attributes that cannot be deleted 

60 """ 

61 if not steps: 61 ↛ 62line 61 didn't jump to line 62 because the condition on line 61 was never true

62 logger.warning("No steps provided to StepAttributeStripper") 

63 return 

64 

65 # Process each step 

66 for step in steps: 

67 # Get step identifier for error messages 

68 step_id = getattr(step, "step_id", str(id(step))) 

69 step_name = getattr(step, "name", f"Step {step_id}") 

70 

71 # Get all attributes 

72 attributes = set(vars(step).keys()) 

73 

74 # Log attributes being stripped 

75 logger.debug(f"Stripping {len(attributes)} attributes from step '{step_name}': {attributes}") 

76 

77 # Delete all attributes 

78 for attr in list(attributes): 

79 try: 

80 delattr(step, attr) 

81 except (AttributeError, TypeError) as e: 

82 # Check if this is a reserved attribute that cannot be deleted 

83 if hasattr(type(step), attr) and not hasattr(type(step), "__slots__"): 

84 # This is likely a class attribute or method, not an instance attribute 

85 logger.debug(f"Skipping class attribute/method '{attr}' on step '{step_name}'") 

86 continue 

87 

88 # If deletion failed for other reasons, raise an error 

89 if hasattr(type(step), "__slots__") and attr in getattr(type(step), "__slots__", []): 

90 raise RuntimeError(ERROR_RESERVED_ATTRIBUTE.format(step_name, attr)) from e 

91 else: 

92 raise ValueError(ERROR_ATTRIBUTE_DELETION_FAILED.format(attr, step_name)) from e 

93 

94 # Verify that all attributes have been stripped 

95 remaining_attrs = set(vars(step).keys()) 

96 if remaining_attrs: 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true

97 raise ValueError( 

98 f"Clause 66 Violation: Step '{step_name}' still has attributes after stripping: {remaining_attrs}. " 

99 f"All attributes must be stripped after planning." 

100 ) 

101 

102 logger.info(f"Successfully stripped all attributes from step '{step_name}'")