| name | analyze_soc_violations |
| description | Analyze the transpiler codebase (especially renderer) for Separation of Concerns violations, document them with impact assessment, solutions, and create an action plan for refactoring. Note: Do not worry about deprecated features or backward compatibility; refactoring can be aggressive and breaking changes are acceptable. |
Skill metadata
- Name: SoC Violations Analyzer
- Intent: Identify architectural violations, assess their impact, and create a prioritized refactoring roadmap
- Primary output:
docs_help_dev/soc-violations.md with comprehensive analysis and action plan
- Focus areas: Renderer (primary), Planner-Renderer boundary, Schema Provider usage
- Key Assumption: No backward compatibility required; deprecated features can be ignored, allowing for more radical refactoring without legacy constraints.
Activation / When to use
Invoke this skill when you need to:
- Audit the codebase for Separation of Concerns violations
- Create a refactoring roadmap for architectural improvements
- Assess technical debt related to SoC violations
- Plan architectural improvements before major features
Required Reading Before Analysis
The skill must read these documents first:
-
docs_help_dev/how-to-respect-soc.md
- Decision framework for SoC
- The Adjacency List Thought Experiment
- Red flags and patterns
-
docs_help_dev/02-architecture.md
- Phase responsibilities
- Design principles
- File responsibilities
-
.claude/skills/fix_transpiler_bugs/SKILL.md
- Known case studies
- Architectural lessons
Analysis Methodology
Phase 1: Pattern Detection
Red Flags to Look For:
In Renderer (src/gsql2rsql/renderer/ — sql_renderer.py, join_renderer.py, recursive_cte_renderer.py, procedural_bfs_renderer.py, expression_renderer.py):
if op.direction == RelationshipDirection.BOTH:
if relationship_type in ["KNOWS", "FRIENDS"]:
if self._undirected_strategy == "union_edges":
if edge_strategy == EdgeAccessStrategy.EDGE_LIST:
if node.label == "Person" and relationship == "KNOWS":
Note: As of ADR-009, all db_schema calls have been migrated out of renderers into sql_enrichment.py. The renderer now reads from EnrichedPlanData (immutable frozen dataclasses). If you find a new db_schema call in a renderer, it should be migrated to enrichment.
In Planner (src/gsql2rsql/planner/logical_plan.py):
sql_fragment = "SELECT * FROM ..."
op.sql_hint = sql_fragment
if self.dialect == "databricks":
Phase 2: Impact Assessment
For each violation found, assess:
-
Severity (Critical / High / Medium / Low):
- Critical: Blocks major features or storage model changes
- High: Significant coupling, hard to extend
- Medium: Code smell, increases maintenance cost
- Low: Minor issue, easy workaround
-
Impact Radius:
- How many files/modules affected?
- How many features depend on this code?
- What breaks if we change storage model?
-
Technical Debt:
- How much harder does this make future changes?
- Does it prevent planned features?
Phase 3: Solution Design
For each violation, propose:
- Ideal Solution: Perfect SoC following architecture
- Pragmatic Solution: If ideal is blocked by SQL constraints
- Intermediate Steps: Can we improve incrementally?
Phase 4: Prioritization
Rank violations by:
- Impact × Difficulty matrix
- Feature dependencies (blocking planned work?)
- Refactoring risk (how much can break?)
Output Document Structure
Create docs_help_dev/soc-violations.md with:
# Separation of Concerns Violations: Analysis & Remediation Plan
**Analysis Date**: [DATE]
**Analyzer**: [WHO/WHAT]
**Codebase Version**: [COMMIT/VERSION]
---
## Executive Summary
- Total violations found: [N]
- Critical: [N] | High: [N] | Medium: [N] | Low: [N]
- Estimated refactoring effort: [PERSON-DAYS]
- Priority 1 violations blocking: [FEATURES/CHANGES]
---
## Violation Categories
### 1. Renderer Making Semantic Decisions
**Pattern**: Renderer checks semantic intent and decides SQL structure
**Instances Found**: [N]
#### V1.1: Undirected VLP with EdgeAccessStrategy
**Location**: `src/gsql2rsql/renderer/recursive_cte_renderer.py`
**Code**:
```python
is_undirected = op.direction == RelationshipDirection.BOTH
edge_strategy = self._graph_def.get_edge_access_strategy()
needs_union = is_undirected and edge_strategy == EdgeAccessStrategy.EDGE_LIST
if needs_union:
# Generate UNION ALL
Why This Violates SoC:
- Renderer decides "undirected + edge_list = UNION ALL"
- This is a semantic decision based on storage model
- Blocks: Switching to adjacency lists without touching renderer
Severity: HIGH
Impact Radius:
- Files: renderer modules (
recursive_cte_renderer.py, join_renderer.py)
- Features: All VLP undirected queries
- Storage models: EDGE_LIST vs ADJACENCY_BIDIRECTIONAL
Current Workarounds: EdgeAccessStrategy abstraction (partial fix)
Ideal Solution:
if rel.direction == BOTH and edge_strategy == EDGE_LIST:
return BidirectionalRecursiveTraversalOperator(...)
if isinstance(op, BidirectionalRecursiveTraversalOperator):
return self._render_with_internal_union(op)
Difficulty: MEDIUM
- Requires: New operator type, planner changes, renderer simplification
- Risk: Medium (tests cover behavior well)
- Estimated effort: 4-6 hours
SQL Constraint: WITH RECURSIVE structure requires UNION ALL inside CTE
Pragmatic Status: DOCUMENTED, abstraction added, future refactoring planned
2. [Other Violation Category]
[Continue pattern for each category...]
Architectural Debt Hotspots
Hotspot 1: Renderer Undirected Logic
Lines of Code: ~200 lines across multiple methods
Violation Count: 3-5 instances
Total Effort to Fix: 8-12 hours
Methods Affected:
_render_recursive_cte() in recursive_cte_renderer.py: EdgeAccessStrategy check
_render_join() in join_renderer.py: JoinKeyPairType.EITHER_AS_SOURCE/SINK handling
_should_use_undirected_union_optimization() in join_renderer.py: Semantic decision helper
Refactoring Approach:
- Create
BidirectionalRecursiveTraversalOperator
- Move logic to planner's
_build_variable_length_path()
- Simplify renderer to type-check operators
- Update tests (should pass without changes)
Action Plan
Phase 1: Quick Wins (Low-Hanging Fruit)
Estimated Effort: 2-4 hours
Risk: Low
Phase 2: Medium Priority (Architectural Improvements)
Estimated Effort: 8-12 hours
Risk: Medium
Phase 3: Major Refactoring (Requires Design Review)
Estimated Effort: 16-24 hours
Risk: High
Phase 4: Future Considerations
Blocked By: Feature requirements, design decisions
Testing Strategy
For each refactoring:
-
Pre-Refactoring:
- Run full test suite, capture coverage
- Document current SQL output for key queries
- Identify integration test gaps
-
During Refactoring:
- TDD: Write tests for new operators/abstractions first
- Incremental changes with test runs
- SQL diff validation (before/after should match semantically)
-
Post-Refactoring:
- Full regression suite (PySpark + unit tests)
- Performance benchmarks (ensure no degradation)
- Manual smoke testing of edge cases
Risk Assessment
High-Risk Refactorings
V1.1: BidirectionalRecursiveTraversalOperator
- Risk: Breaking VLP queries in production
- Mitigation: Feature flag, extensive testing, gradual rollout
- Rollback Plan: Keep old code path behind flag for 2 releases
V1.3: WITH RECURSIVE redesign
- Risk: Fundamental change to operator semantics
- Mitigation: Prototype in separate branch, design review, RFC
- Decision: Requires stakeholder buy-in
Medium-Risk Refactorings
V1.2: JoinKeyPairType consolidation
- Risk: Breaking single-hop undirected queries
- Mitigation: Comprehensive test coverage, SQL diff validation
Success Metrics
Track progress with:
- Violation Count: Decrease from [N] to target
- Test Coverage: Maintain >90% during refactoring
- Code Complexity: Reduce cyclomatic complexity in renderer
- Maintainability Index: Improve from [X] to [Y]
Definition of Done:
- ✅ All Critical and High severity violations resolved
- ✅ Documented pragmatic compromises for Medium/Low
- ✅ No test regressions
- ✅ Updated architecture docs reflect changes
- ✅ Team review and sign-off
References
Maintenance
Review Frequency: Quarterly or before major features
Owner: Architecture team
Next Review: [DATE + 3 months]
---
## Analysis Process
### Step 1: Code Discovery
```bash
# Find renderer files
find src/gsql2rsql/renderer -name "*.py"
# Find planner files
find src/gsql2rsql/planner -name "*.py"
# Search for red flag patterns
grep -r "direction ==" src/gsql2rsql/renderer/
grep -r "get_edge_access_strategy" src/gsql2rsql/
grep -r "isinstance.*Operator" src/gsql2rsql/renderer/
Step 2: Pattern Analysis
For each file, look for:
- Conditional logic on semantic fields (direction, relationship types)
- Schema queries for business logic (not just structure)
- Storage model decisions (EDGE_LIST vs ADJACENCY)
- Hardcoded SQL patterns that should be operator-driven
- Dual-renderer inconsistencies — VLP bugs may exist in
recursive_cte_renderer.py but not in procedural_bfs_renderer.py (or vice versa). Both consume the same EnrichedRecursiveOp and should apply the same SoC rules
Step 3: Trace Impact
For each violation:
Step 4: Solution Design
Use the Adjacency List Thought Experiment:
- Assume edges are now stored bidirectionally
- What code needs to change?
- Is it in the renderer? → VIOLATION
- Should it be in planner? → SOLUTION
Deliverables
-
docs_help_dev/soc-violations.md
- Comprehensive violation catalog
- Impact assessment
- Solution designs
- Action plan with effort estimates
-
Executive Summary (in conversation)
- Top 3 critical violations
- Quick wins (< 4 hours each)
- Recommended priority order
-
Optional: Refactoring Tickets (if requested)
- GitHub issues for each Phase 1-2 item
- Acceptance criteria
- Test requirements
Example Analysis Output
Found 12 SoC violations in renderer:
- 3 Critical (blocking adjacency list support)
- 4 High (significant coupling)
- 3 Medium (code smells)
- 2 Low (minor issues)
Top Critical Violations:
1. V1.1: Undirected VLP EdgeAccessStrategy check
→ Solution: BidirectionalRecursiveTraversalOperator
→ Effort: 6 hours, Risk: Medium
2. V1.2: JoinKeyPairType semantic handling
→ Solution: Move to planner join creation
→ Effort: 4 hours, Risk: Medium
3. V2.1: Edge property access patterns
→ Solution: PropertyAccessStrategy abstraction
→ Effort: 8 hours, Risk: High
Quick Wins (Phase 1):
- V3.2: Extract SQL constants (1 hour)
- V5.1: Move dialect checks (2 hours)
Recommended Next Steps:
1. Review findings with team
2. Prioritize based on upcoming features
3. Start with Quick Wins to build momentum
4. Schedule design review for V1.1 refactoring
Quality Checks
Before finalizing the document:
Verification Commands
After any refactoring from this analysis:
uv run pyright src/gsql2rsql
uv run mypy src/gsql2rsql
uv run pytest tests/ -n 4 -q
uv run pyright src/gsql2rsql && uv run mypy src/gsql2rsql && uv run pytest tests/ -n 4 -q
Final Instruction
After reading the required docs, systematically:
- Search codebase for red flag patterns
- Document each violation with location, severity, impact
- Design solutions (ideal + pragmatic)
- Create prioritized action plan
- Output comprehensive
soc-violations.md
- Provide executive summary to user
Focus on renderer but include planner-renderer boundary violations.