@sun-asterisk/impact-analyzer 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,116 @@
1
+ # Impact Analyzer - AI Code Generation Guide
2
+
3
+ ## What This Tool Does
4
+
5
+ Analyzes code changes in TypeScript/JavaScript projects to identify impacts on APIs, databases, and logic dependencies. Generates severity-based reports (LOW/MEDIUM/HIGH/CRITICAL).
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ core/
11
+ ├── change-detector.js # Git diff parsing + AST symbol extraction
12
+ ├── impact-analyzer.js # Main orchestrator
13
+ ├── report-generator.js # Console/markdown/JSON output
14
+ ├── detectors/
15
+ │ ├── endpoint-detector.js # Find affected API endpoints
16
+ │ └── database-detector.js # Detect DB table/field changes
17
+ └── utils/
18
+ ├── ast-parser.js # Parse TS/JS with ts-morph
19
+ ├── method-call-graph.js # Build caller/callee maps
20
+ ├── dependency-graph.js # Track dependencies
21
+ ├── file-utils.js # File operations
22
+ └── git-utils.js # Git operations
23
+ ```
24
+
25
+ ## Core Logic Flow
26
+
27
+ 1. **Detect Changes** → Parse git diff, extract changed symbols
28
+ 2. **Build Call Graph** → Map methods to callers/callees
29
+ 3. **Find Impacts** → Traverse graph upward to find affected endpoints and DB ops
30
+ 4. **Calculate Score** → Score = (Endpoints×10) + (DB×5) + (Callers×3) + (Indirect×1)
31
+ 5. **Generate Report** → Output with severity level
32
+
33
+ ## Code Rules for AI Generation
34
+
35
+ ### Essential Patterns
36
+
37
+ **Import/Export**
38
+ ```javascript
39
+ import { Project } from 'ts-morph';
40
+ export function analyzeImpact(files) { }
41
+ ```
42
+
43
+ **Async Operations**
44
+ ```javascript
45
+ async function detectChanges() {
46
+ const files = await getChangedFiles();
47
+ return await Promise.all(files.map(analyzeFile));
48
+ }
49
+ ```
50
+
51
+ **Error Handling**
52
+ ```javascript
53
+ try {
54
+ await operation();
55
+ } catch (error) {
56
+ console.error('Context:', { file, method }, error);
57
+ }
58
+ ```
59
+
60
+ **Call Graph Traversal**
61
+ ```javascript
62
+ function findAffectedEndpoints(method, callGraph) {
63
+ const callers = traverseUpward(method, callGraph);
64
+ return callers.filter(hasHttpDecorator);
65
+ }
66
+ ```
67
+
68
+ ### Code Quality Rules
69
+
70
+ - **Rule C005** - One function does one thing
71
+ - **Rule C006** - Function names use verb-noun (e.g., `detectEndpoints`, `buildCallGraph`)
72
+ - **Rule C007** - No comments that restate code
73
+ - **Rule C015** - Use domain language (e.g., `endpoint`, `impact`, `severity`)
74
+ - **Rule C031** - Separate validation logic
75
+ - **Rule C033** - Separate data access from business logic
76
+
77
+ ### Naming Conventions
78
+
79
+ - Functions: `camelCase` (e.g., `analyzeImpact`)
80
+ - Classes: `PascalCase` (e.g., `ImpactAnalyzer`)
81
+ - Constants: `UPPER_SNAKE_CASE` (e.g., `SEVERITY_LEVELS`)
82
+ - Files: `kebab-case.js` (e.g., `call-graph.js`)
83
+
84
+ ### Required Patterns
85
+
86
+ **Always use:**
87
+ - ES Modules (`import/export`)
88
+ - `async/await` (never callbacks)
89
+ - `ts-morph` for AST parsing
90
+ - Descriptive variable names from domain
91
+
92
+ **Never do:**
93
+ - Use `new` in business logic (use dependency injection)
94
+ - Log errors without context
95
+ - Mix validation with business logic
96
+ - Return raw strings from API handlers
97
+
98
+ ## CLI Usage
99
+
100
+ ```bash
101
+ # Basic
102
+ npx @sun-asterisk/impact-analyzer --input=src --base=origin/main
103
+
104
+ # With reports
105
+ npx @sun-asterisk/impact-analyzer --input=src --base=origin/main --output=report.md --json=report.json
106
+ ```
107
+
108
+ ## Severity Calculation
109
+
110
+ ```javascript
111
+ score = (endpoints × 10) + (dbTables × 5) + (directCallers × 3) + (indirectCallers × 1)
112
+ if (highRiskLogic) score *= 1.5
113
+ if (dbMigration) score += 20
114
+
115
+ severity = score < 21 ? 'LOW' : score < 51 ? 'MEDIUM' : score < 101 ? 'HIGH' : 'CRITICAL'
116
+ ```
@@ -0,0 +1,91 @@
1
+ # Prompts for GitHub Copilot CLI
2
+
3
+ ## Do You Need Prompts?
4
+
5
+ **Short Answer: NO** - For most tasks, you can directly reference the task file:
6
+
7
+ ```bash
8
+ # Direct approach (RECOMMENDED)
9
+ @task-001-refactor-api-detector.md read this task and implement it
10
+ ```
11
+
12
+ **Why Direct Reference Works:**
13
+ - Task files are already structured for AI (clear goals, code examples, acceptance criteria)
14
+ - GitHub Copilot CLI can read and understand markdown task files directly
15
+ - Less overhead, fewer files to maintain
16
+ - Task files contain all necessary context
17
+
18
+ ## When to Use Prompts?
19
+
20
+ Use prompts ONLY when you need to:
21
+
22
+ 1. **Reuse across multiple tasks** - Common patterns used in many places
23
+ 2. **Add constraints not in task** - Team-specific rules, environment limitations
24
+ 3. **Provide context not in codebase** - External dependencies, business rules
25
+ 4. **Break down complex tasks** - Multi-step workflows that need orchestration
26
+
27
+ ## Current Prompts
28
+
29
+ ### task-001-refactor.prompt.md
30
+
31
+ **Purpose:** Provides step-by-step implementation guide for Task 001 refactoring
32
+
33
+ **Why it exists:**
34
+ - Breaks down large refactor into smaller, sequential steps
35
+ - Adds verification steps after each phase
36
+ - Includes rollback instructions for safety
37
+ - Provides consistent implementation pattern for future refactors
38
+
39
+ **When to use:**
40
+ ```bash
41
+ # Step-by-step guided refactor
42
+ @task-001-refactor.prompt.md implement this refactor step by step
43
+ ```
44
+
45
+ **When NOT to use:**
46
+ ```bash
47
+ # Simple, direct implementation
48
+ @task-001-refactor-api-detector.md implement this task
49
+ ```
50
+
51
+ ## Prompt Guidelines
52
+
53
+ If you create a new prompt, follow these rules:
54
+
55
+ **Keep it simple:**
56
+ - One prompt = One specific purpose
57
+ - Max 100 lines
58
+ - Clear structure with sections
59
+
60
+ **Make it actionable:**
61
+ - Use concrete code examples
62
+ - Provide expected outcomes
63
+ - Include verification commands
64
+
65
+ **Avoid duplication:**
66
+ - Don't repeat what's in task files
67
+ - Don't repeat what's in copilot-instructions.md
68
+ - Reference existing docs instead
69
+
70
+ ## Prompt Template
71
+
72
+ ```markdown
73
+ # Prompt: [Name]
74
+
75
+ ## Purpose
76
+ [Why this prompt exists]
77
+
78
+ ## Context
79
+ [What AI needs to know]
80
+
81
+ ## Implementation Steps
82
+ 1. [Step 1]
83
+ 2. [Step 2]
84
+ ...
85
+
86
+ ## Verification
87
+ [How to verify it worked]
88
+
89
+ ## References
90
+ - [Link to related docs]
91
+ ```
@@ -0,0 +1,241 @@
1
+ # Prompt: Task 001 Refactor Implementation
2
+
3
+ ## Purpose
4
+
5
+ Step-by-step implementation guide for refactoring API detector from modules/ to core/ with performance optimization.
6
+
7
+ ## Why This Prompt Exists
8
+
9
+ While the task file contains all requirements, this prompt:
10
+ 1. **Breaks down into safe phases** - Prevents breaking changes
11
+ 2. **Adds verification steps** - Ensures each phase works before moving on
12
+ 3. **Provides rollback strategy** - Safety net if something goes wrong
13
+ 4. **Sequential execution** - Avoids doing everything at once
14
+
15
+ ## Phase 1: Audit Current Structure
16
+
17
+ **Goal:** Understand what exists before changing it
18
+
19
+ ```bash
20
+ # Find all files in modules/ or related to API detection
21
+ find . -name "*api*" -o -name "*endpoint*" -o -name "*detector*" | grep -v node_modules
22
+
23
+ # Check imports
24
+ grep -r "from.*modules" --include="*.js" | head -20
25
+ ```
26
+
27
+ **Document:**
28
+ - Which files exist?
29
+ - Which files import from modules/?
30
+ - Are there tests?
31
+
32
+ ## Phase 2: Create New Structure (No Breaking Changes)
33
+
34
+ **Goal:** Create new files in core/ without touching old code
35
+
36
+ ```javascript
37
+ // Step 1: Create core/utils/logger.js
38
+ export function createLogger(verbose = false) {
39
+ return {
40
+ info: (msg) => console.log(msg),
41
+ debug: (msg) => verbose && console.log(`[DEBUG] ${msg}`),
42
+ verbose: (component, msg) => verbose && console.log(`[${component}] ${msg}`)
43
+ };
44
+ }
45
+
46
+ // Step 2: Create core/detectors/endpoint-detector.js
47
+ // Copy existing logic, add logger, optimize
48
+ import { createLogger } from '../utils/logger.js';
49
+
50
+ export function findAffectedEndpoints(changedMethods, callGraph, options = {}) {
51
+ const logger = createLogger(options.verbose);
52
+ const endpoints = [];
53
+
54
+ logger.info('🔍 Analyzing API impacts...');
55
+ logger.verbose('EndpointDetector', `Processing ${changedMethods.length} methods`);
56
+
57
+ for (const method of changedMethods) {
58
+ const found = traverseToEndpoint(method, callGraph, logger);
59
+ endpoints.push(...found);
60
+ }
61
+
62
+ logger.info(`📡 Found ${endpoints.length} affected endpoints`);
63
+ return endpoints;
64
+ }
65
+
66
+ function traverseToEndpoint(method, callGraph, logger, visited = new Set(), depth = 0) {
67
+ // Max depth check
68
+ if (depth > 10) {
69
+ logger.verbose('EndpointDetector', `Max depth reached for ${method.name}`);
70
+ return [];
71
+ }
72
+
73
+ // Cycle detection with Set (O(1) lookup)
74
+ if (visited.has(method.name)) return [];
75
+ visited.add(method.name);
76
+
77
+ logger.verbose('EndpointDetector', `Traversing ${method.name} (depth: ${depth})`);
78
+
79
+ // Check if endpoint
80
+ if (isHttpEndpoint(method)) {
81
+ const info = extractEndpointInfo(method);
82
+ logger.verbose('EndpointDetector', `Found endpoint: ${info.method} ${info.path}`);
83
+ return [info];
84
+ }
85
+
86
+ // Traverse callers
87
+ const endpoints = [];
88
+ const callers = callGraph.getCallers(method.name) || [];
89
+
90
+ for (const caller of callers) {
91
+ const found = traverseToEndpoint(caller, callGraph, logger, visited, depth + 1);
92
+ endpoints.push(...found);
93
+ }
94
+
95
+ return endpoints;
96
+ }
97
+
98
+ function isHttpEndpoint(method) {
99
+ if (!method.decorators) return false;
100
+ const httpMethods = ['Get', 'Post', 'Put', 'Delete', 'Patch'];
101
+ return method.decorators.some(d => httpMethods.includes(d.name));
102
+ }
103
+
104
+ function extractEndpointInfo(method) {
105
+ const httpDecorator = method.decorators.find(d =>
106
+ ['Get', 'Post', 'Put', 'Delete', 'Patch'].includes(d.name)
107
+ );
108
+
109
+ const controllerDecorator = method.class?.decorators?.find(d =>
110
+ d.name === 'Controller'
111
+ );
112
+
113
+ const basePath = controllerDecorator?.arguments?.[0] || '';
114
+ const subPath = httpDecorator?.arguments?.[0] || '';
115
+
116
+ return {
117
+ type: 'endpoint',
118
+ method: httpDecorator.name.toUpperCase(),
119
+ path: joinPath(basePath, subPath),
120
+ controller: `${method.class.name}.${method.name}`,
121
+ file: method.file
122
+ };
123
+ }
124
+
125
+ function joinPath(base, sub) {
126
+ const cleanBase = base.replace(/\/$/, '');
127
+ const cleanSub = sub.replace(/^\//, '');
128
+ return cleanSub ? `/${cleanBase}/${cleanSub}`.replace(/\/+/g, '/') : `/${cleanBase}`;
129
+ }
130
+ ```
131
+
132
+ **Verify:**
133
+ ```bash
134
+ # Check files created
135
+ ls -la core/detectors/
136
+ ls -la core/utils/logger.js
137
+
138
+ # Verify no syntax errors
139
+ node -c core/detectors/endpoint-detector.js
140
+ node -c core/utils/logger.js
141
+ ```
142
+
143
+ ## Phase 3: Update Imports (One File at a Time)
144
+
145
+ **Goal:** Switch to new structure gradually
146
+
147
+ ```javascript
148
+ // Find main entry point
149
+ // Example: core/impact-analyzer.js
150
+
151
+ // Old import
152
+ // import { findEndpoints } from '../modules/api-detector.js';
153
+
154
+ // New import
155
+ import { findAffectedEndpoints } from './detectors/endpoint-detector.js';
156
+
157
+ // Update function call
158
+ const endpoints = findAffectedEndpoints(changedMethods, callGraph, { verbose });
159
+ ```
160
+
161
+ **Verify after each change:**
162
+ ```bash
163
+ # Test the tool still works
164
+ node cli.js --input=src --base=origin/main --verbose
165
+
166
+ # Check output is correct
167
+ ```
168
+
169
+ ## Phase 4: Remove Unused Code
170
+
171
+ **Goal:** Clean up old files only after new code is verified
172
+
173
+ ```bash
174
+ # First, verify nothing imports from modules/
175
+ grep -r "from.*modules" --include="*.js" .
176
+
177
+ # If clear, remove old files
178
+ rm -rf modules/ # Or move to backup first
179
+ ```
180
+
181
+ **Safety:** Keep backup before deleting
182
+ ```bash
183
+ mkdir -p .backup
184
+ cp -r modules/ .backup/modules-$(date +%Y%m%d)
185
+ ```
186
+
187
+ ## Phase 5: Performance Verification
188
+
189
+ **Goal:** Ensure optimizations worked
190
+
191
+ ```bash
192
+ # Add timing to verbose output
193
+ # In endpoint-detector.js:
194
+ const startTime = Date.now();
195
+ // ... detection logic
196
+ const duration = Date.now() - startTime;
197
+ logger.verbose('EndpointDetector', `Completed in ${duration}ms`);
198
+ ```
199
+
200
+ **Test with verbose mode:**
201
+ ```bash
202
+ node cli.js --input=src --base=origin/main --verbose
203
+ ```
204
+
205
+ **Check:**
206
+ - Is depth limit working? (should see "Max depth reached" if hit)
207
+ - Are visited nodes tracked? (no infinite loops)
208
+ - Is timing shown? (< 50ms per method is good)
209
+
210
+ ## Rollback Strategy
211
+
212
+ If something breaks:
213
+
214
+ ```bash
215
+ # Restore from backup
216
+ cp -r .backup/modules-YYYYMMDD modules/
217
+
218
+ # Revert imports in git
219
+ git checkout -- core/impact-analyzer.js
220
+
221
+ # Or revert entire change
222
+ git reset --hard HEAD
223
+ ```
224
+
225
+ ## Acceptance Checklist
226
+
227
+ - [ ] Phase 1 complete: Current structure documented
228
+ - [ ] Phase 2 complete: New files created in core/
229
+ - [ ] Phase 3 complete: Imports updated, tool still works
230
+ - [ ] Phase 4 complete: Old code removed
231
+ - [ ] Phase 5 complete: Performance verified
232
+ - [ ] Verbose mode works (`--verbose` flag)
233
+ - [ ] Normal mode works (clean output)
234
+ - [ ] No errors in console
235
+ - [ ] Tests pass (if any)
236
+
237
+ ## References
238
+
239
+ - Task File: `.specify/tasks/task-001-refactor-api-detector.md`
240
+ - API Spec: `.specify/specs/features/api-impact-detection.md`
241
+ - Code Rules: `.github/copilot-instructions.md`
@@ -0,0 +1,222 @@
1
+ # BUG-001: Database Detector Missing SELECT Operations
2
+
3
+ **Bug ID:** BUG-001
4
+ **Status:** ✅ RESOLVED
5
+ **Priority:** HIGH
6
+ **Severity:** Medium
7
+ **Created:** 2025-12-25
8
+ **Updated:** 2025-12-25
9
+ **Resolved:** 2025-12-25
10
+
11
+ ---
12
+
13
+ ## Problem Statement
14
+
15
+ Database detector only catches INSERT/UPDATE/DELETE operations but misses SELECT queries, resulting in incomplete database impact reports.
16
+
17
+ ### Observed Behavior
18
+
19
+ When code changes include SELECT operations, they are **not detected**:
20
+
21
+ ```typescript
22
+ // ❌ Not detected:
23
+ await repository.find({ where: { status: 'active' } });
24
+ await repository.findOne({ where: { id } });
25
+ await queryBuilder.select(['name', 'email']).getMany();
26
+ ```
27
+
28
+ ### Expected Behavior
29
+
30
+ All database operations including SELECT should be detected and reported in the impact analysis.
31
+
32
+ ---
33
+
34
+ ## Root Cause
35
+
36
+ **File:** `core/detectors/database-detector.js` (line ~295)
37
+
38
+ The `typeOrmOps` mapping object only includes write operations (INSERT/UPDATE/DELETE) and does not include read operations (SELECT):
39
+
40
+ ```javascript
41
+ const typeOrmOps = {
42
+ 'insert': 'INSERT',
43
+ 'save': 'INSERT/UPDATE',
44
+ 'create': 'INSERT',
45
+ 'update': 'UPDATE',
46
+ 'merge': 'UPDATE',
47
+ 'delete': 'DELETE',
48
+ 'remove': 'DELETE',
49
+ 'softDelete': 'SOFT_DELETE',
50
+ // ❌ Missing: find, findOne, select, query, getMany, getOne
51
+ };
52
+ ```
53
+
54
+ The `analyzeChangedCodeForDatabaseOps()` method iterates through this map to detect database operations. Since SELECT operations are not in the map, they are skipped.
55
+
56
+ ---
57
+
58
+ ## Fix Strategy
59
+
60
+ ### Approach
61
+
62
+ Add SELECT operation methods to the `typeOrmOps` mapping. This is a **low-risk, non-breaking change** that extends existing detection without modifying the core logic.
63
+
64
+ ### Implementation
65
+
66
+ **Step 1:** Locate the `typeOrmOps` definition in `core/detectors/database-detector.js` (~line 295)
67
+
68
+ **Step 2:** Add SELECT operations:
69
+
70
+ ```javascript
71
+ const typeOrmOps = {
72
+ // Existing operations (keep as-is)
73
+ 'insert': 'INSERT',
74
+ 'save': 'INSERT/UPDATE',
75
+ 'create': 'INSERT',
76
+ 'update': 'UPDATE',
77
+ 'merge': 'UPDATE',
78
+ 'delete': 'DELETE',
79
+ 'remove': 'DELETE',
80
+ 'softDelete': 'SOFT_DELETE',
81
+
82
+ // Add SELECT operations
83
+ 'find': 'SELECT',
84
+ 'findOne': 'SELECT',
85
+ 'findBy': 'SELECT',
86
+ 'findOneBy': 'SELECT',
87
+ 'findAndCount': 'SELECT',
88
+ 'select': 'SELECT',
89
+ 'query': 'SELECT',
90
+ 'getMany': 'SELECT',
91
+ 'getOne': 'SELECT',
92
+ };
93
+ ```
94
+
95
+ **Step 3:** No other changes needed. The existing `analyzeChangedCodeForDatabaseOps()` method will automatically handle SELECT operations.
96
+
97
+ ### Why This Works
98
+
99
+ The method already:
100
+ 1. Iterates through `typeOrmOps` entries
101
+ 2. Detects method calls matching the keys
102
+ 3. Adds operations to the `operations` Set
103
+ 4. Formats output correctly
104
+
105
+ Adding new keys will be processed by the existing logic flow.
106
+
107
+ ---
108
+
109
+ ## Verification
110
+
111
+ ### Test Case
112
+
113
+ ```bash
114
+ # Create a test diff with SELECT operations
115
+ cat > test.diff << 'EOF'
116
+ + await repository.find({ where: { id } });
117
+ + await repository.findOne({ where: { status: 'active' } });
118
+ + const users = await queryBuilder.select(['id', 'name']).getMany();
119
+ EOF
120
+
121
+ # Run detector and verify output contains:
122
+ # - operations: ["SELECT"]
123
+ # - Correct table detection
124
+ ```
125
+
126
+ ### Expected Output
127
+
128
+ ```javascript
129
+ {
130
+ tableName: "users",
131
+ modelName: "UserEntity",
132
+ operations: ["SELECT"], // ✅ Now detected
133
+ fields: ["id", "name", "status"],
134
+ severity: "low" // SELECT is typically low severity
135
+ }
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Impact Assessment
141
+
142
+ ### Risk Level: **LOW** ✅
143
+
144
+ **Why Low Risk:**
145
+ - Only adds new entries to existing map
146
+ - No changes to detection logic
147
+ - No changes to data structures
148
+ - Backward compatible (existing detections unaffected)
149
+
150
+ ### Areas Affected
151
+
152
+ | Component | Change Type | Risk |
153
+ |-----------|-------------|------|
154
+ | `database-detector.js` | Addition only | Low |
155
+ | Report output | Enhanced data | None |
156
+ | Existing detections | None | None |
157
+
158
+ ### Testing Required
159
+
160
+ - [ ] Unit test: Verify SELECT operations are detected
161
+ - [ ] Integration test: Check report includes SELECT in operations array
162
+ - [ ] Regression test: Verify existing INSERT/UPDATE/DELETE still work
163
+
164
+ ---
165
+
166
+ ## Implementation Checklist
167
+
168
+ - [x] Update `typeOrmOps` in `database-detector.js`
169
+ - [x] Test with `find()` operation
170
+ - [x] Test with `findOne()` operation
171
+ - [x] Test with `select()` operation
172
+ - [x] Test with `query()` operation
173
+ - [x] Verify report output shows SELECT
174
+ - [x] Check console output displays correctly
175
+ - [x] Verify JSON output includes SELECT
176
+ - [ ] Update CHANGELOG if needed
177
+ - [x] Mark bug as RESOLVED
178
+
179
+ ---
180
+
181
+ ## References
182
+
183
+ **Code Files:**
184
+ - `core/detectors/database-detector.js` (line ~295)
185
+ - `core/report-generator.js` (displays operations)
186
+
187
+ **Related:**
188
+ - Task 002: `.specify/tasks/task-002-database-detector.md`
189
+ - Spec: `.specify/specs/features/database-impact-detection.md`
190
+
191
+ **TypeORM Documentation:**
192
+ - Repository API: https://typeorm.io/repository-api
193
+ - Select operations: `find`, `findOne`, `findBy`, etc.
194
+
195
+ ---
196
+
197
+ ## Notes
198
+
199
+ - SELECT operations should be marked as lower severity than write operations
200
+ - This fix will make reports more complete and accurate
201
+ - Future enhancement: Track which fields are being selected
202
+
203
+ ---
204
+
205
+ **Estimated Effort:** 5 minutes
206
+ **Actual Effort:** 3 minutes
207
+
208
+ ---
209
+
210
+ ## Resolution Summary
211
+
212
+ **Fixed on:** 2025-12-25
213
+
214
+ **Changes Made:**
215
+ 1. Updated `typeOrmOps` map in `analyzeChangedCodeForDatabaseOps()` method (line 295)
216
+ 2. Updated `typeOrmOps` map in `detectDatabaseOperationFromCall()` method (line 552)
217
+ 3. Added 9 SELECT operation methods: find, findOne, findBy, findOneBy, findAndCount, select, query, getMany, getOne
218
+
219
+ **Files Modified:**
220
+ - `core/detectors/database-detector.js` (2 locations updated)
221
+
222
+ **Result:** SELECT operations are now fully detected and will appear in database impact reports.