EWI Resolution (Snowpark Migration Accelerator)
EWI Pattern
#EWI: <PREFIX><NUMBER> => <INFO>
Supported EWI Prefixes:
| Prefix | Source | Description |
|---|---|---|
SPRKPY |
PySpark/Scala → Snowpark Python | Spark Python conversion issues |
SPRKSCL |
Spark Scala → Snowpark Scala | Spark Scala conversion issues |
PNDSPY |
Pandas → Snowpark Pandas | Pandas conversion issues |
SPRKDBX |
Databricks → Snowsight | Databricks-specific issues |
SPRKCNTPY |
PySpark/Scala → Snowpark Connect Python | Snowpark Connect Python issues |
SPRKCNTSCL |
Spark Scala → Snowpark Connect Scala | Snowpark Connect Scala issues |
SPRKHVSQL |
HiveSQL → Snowflake SQL | Hive SQL conversion issues |
SPRKSPSQL |
SparkSQL → Snowflake SQL | Spark SQL conversion issues |
Output Format
Every time you begin a step, sub-step, or significant action, prefix the message with a timestamp in the format [YYYY-MM-DD HH:MM:SS]. Obtain the current time by running date '+%Y-%m-%d %H:%M:%S' in bash.
Example:
[2026-03-24 14:05:32] Starting Step 1: Scan for EWI markers...
[2026-03-24 14:05:45] Found 12 EWI markers in 3 files
[2026-03-24 14:05:46] Step 1 complete.
Workflow
🚨 CRITICAL — RE-EXECUTION POLICY: Every time this skill is invoked, you MUST execute the full workflow from Step 1 — ask ALL questions, initialize the database, process EWIs, commit, etc. NEVER skip steps or decide that "work is already complete" based on the state of a previous run. The workflow is designed to handle re-execution:
- Step 1, Question 3 asks the user which EWIs to process (
Only pending,Specific EWI code, orAll (reset)). - If a previous run resolved everything,
Only pendingwill find 0 pending EWIs — that is fine, present that to the user and let THEM decide. - If the user selects
All (reset), all EWIs are reset topendingand reprocessed from scratch. - The database, Git, and summary tables all support multiple runs. Each run gets its own
fix_id. - DO NOT inspect the database or Git state to preemptively decide there is nothing to do. Always let the user make that decision through the questions.
⚠️ DO NOT create additional files (like fix_session.json, session.txt, etc.) for tracking session state. All persistence is handled by the SQLite database (sma_storage.sqlite3) via the database module at skills/spark-migration/snowpark-api/scripts/sma_api.py. The fix_id is managed automatically by initialize_database().
Step 1: Locate SMA Output Directory and Configure Options
Pre-configured options: If this skill was invoked from spark-migration and the caller provided pre-configured answers (SMA output directory, EWI comment handling, and EWI scope), skip all questions in this step and use the provided values directly. Proceed to Step 2.
If running standalone (no pre-configured options), ask the following questions:
Ask user for the path to the SMA output directory:
- Use
ask_user_questiontool with type "text" - Question: "Path to SMA output directory (containing Output, Reports, Logs folders)"
- This directory contains:
Output/- Converted .py/.ipynb/.scala filesReports/- Issues.csv or IssuesConnect.csvLogs/- Conversion logs
- Use
Ask user about EWI comment handling:
- Use
ask_user_questiontool - Question: "How should EWI comments be handled after fixing?"
- Options:
- Mark: Keep EWI comments but prefix with
[FIXED]or[NOT-FIXED] - Remove: Delete EWI comments after applying fixes
- Mark: Keep EWI comments but prefix with
- Store the user's choice for use in Step 8
- Use
Ask user which EWIs to process:
- Use
ask_user_questiontool - Question: "Which EWIs should be processed?"
- Options:
- Only pending: Process only EWIs with status 'pending' (default - skips already processed)
- Retry not_auto_resolved: Reset
not_auto_resolvedEWIs back topendingand retry them - Specific EWI code: Process only a specific EWI code (e.g., SPRKPY1002)
- All (reset): Reset all EWIs to 'pending' and reprocess everything
- If "Retry not_auto_resolved" selected:
reset_not_resolved_to_pending(workload_path) - If "Specific EWI code" selected: Ask for the code (e.g., "SPRKPY1002") and filter by that code
- If "All (reset)" selected:
reset_all_to_pending(workload_path)
- Use
Step 2: Initialize Git Repository and Create Branch
IMPORTANT: Use Git to track changes instead of creating a copy. This preserves history and allows easy rollback.
All git operations are handled by deterministic Python functions in sma_api. Import:
from sma_api import git_init_if_needed, git_status, git_stash, git_ensure_branch, git_ensure_ready, git_verify_branches, git_commit, MIGRATION_BRANCH
2.1 Check/Initialize Git Repository
Ensure the git repository exists (no-op if it already does):
result = sma_api.git_init_if_needed("<sma_output_dir>")- If not a repo: atomically runs
git init→git add .→git commit→git branch -M main - If already a repo: returns
{"success": True, "action": "already_initialized"}
⚠️ CRITICAL:
git_init_if_needed()ensures the default branch is namedmainso the original SMA output is always preserved and accessible. Without this, there is no reference branch to diff against or revert to.- If not a repo: atomically runs
If the repo has uncommitted changes (check
sma_api.git_is_clean("<sma_output_dir>")): Ask user viaask_user_question:- Stash changes:
sma_api.git_stash("<sma_output_dir>")(recover later withgit stash pop) - Commit changes: Commit pending changes before proceeding
- Abort: Stop and let user handle manually
- Stash changes:
2.2 Create Feature Branch
result = sma_api.git_ensure_branch("<sma_output_dir>")
Returns: {"success": True, "action": "created"|"switched"|"already_on_branch"}
This deterministically handles all cases: creates sma/migration-process if it doesn't exist, switches to it if it exists but isn't checked out, or confirms if already on it.
Shortcut — Steps 2.1 + 2.2 can be replaced with a single call:
result = sma_api.git_ensure_ready("<sma_output_dir>")
This runs the full sequence: init if needed → stash if dirty → ensure branch. Use this when the user doesn't need to be asked about stashing.
All subsequent file operations (reads, edits, fixes) work directly on files in the SMA output directory on the sma/migration-process branch.
The original code is preserved in the main (or master) branch and can be recovered with git checkout main.
Step 3: Generate Fix ID
The fix_id is automatically generated when initializing the database (Step 4).
You can access it from the response of initialize_database() or via generate_fix_id().
Step 4: Initialize or Load SQLite Database
- Check for
sma_storage.sqlite3in the SMA output directory - If database does NOT exist:
- Create
sma_storage.sqlite3in SMA output directory - Look for CSV file in
Reports/subdirectory (try in order):Issues.csvIssuesConnect.csv
- If neither exists, STOP with error
- Create
Issuestable and import CSV data - Add columns:
status TEXT DEFAULT 'pending',notes TEXT DEFAULT ''
- Create
- If database EXISTS:
- Connect to existing database
- Verify
Issuestable exists - Ensure
statusandnotescolumns exist (add if missing with defaults)
The initialize_database function automatically:
- Creates the
ewi_fixer_resultstable if it doesn't exist - Creates the
ewi_fixer_summarytable if it doesn't exist - Generates a unique
fix_idfor the session - Inserts an initial summary record with
fix_idandstart_time
⚠️ CRITICAL: Call initialize_database() EXACTLY ONCE, at the very beginning of Step 4.
- Each call generates a new
fix_idand creates a new summary record. - The
start_timeis recorded at this moment. If you call it late, the duration will be wrong. - Store the returned
fix_idand reuse it for ALL subsequent operations. - NEVER call
initialize_database()again — not on retry, not on error, not at the end. - Orphan cleanup: Before creating the new summary row, close any previous summary records that have
end_time IS NULLby setting theirend_timeto the current timestamp and all numeric counters to0. This prevents orphaned rows from accumulating across retries or cancelled runs.
Use the initialize_database function from sma_api.py:
# Call ONLY ONCE at the beginning — returns fix_id and database status
result = initialize_database(workload_path=sma_output_dir)
# result contains: {"fix_id": "...", "status": "...", ...}
# Store fix_id for use in subsequent calls
# DO NOT call initialize_database() again during this session
Step 5: Load EWI Context and Reference Documentation
- Query Issues table to get pending EWIs:
ewi_codes = get_pending_ewi_codes(workload_path=sma_output_dir) # Returns: [{'Code': 'SPRKPY1002', 'Description': '...', 'Category': 'ConversionError'}, ...] # ^^^^ ^^^^^^^^^^^ ^^^^^^^^ # Column names are CAPITALIZED (imported from CSV headers) - For each unique EWI code, attempt to load reference markdown:
- Try to read
references/<EWI_CODE>.md - Note whether reference exists, is specific, or is generic template
- CACHE: Read each reference markdown only once per EWI code. Store the content in memory and reuse it for all occurrences of the same code across different files and lines. Do NOT re-read the same
.mdfile multiple times. - Note:
SPRKDBXcodes (Databricks-specific) now have reference files. These typically refer to magic commands (%run,%sql) and widgets that SMA already converted or removed. Mark as[NOT-FIXED]if the code persists in files, or update directly in the DB asnot_auto_resolvedif they did not generate visible#EWI:comments in the code.
- Try to read
- Query file and line details for each code:
ewis = get_ewis_by_code(workload_path=sma_output_dir, code='SPRKPY1002', status='pending') # Returns: [{'FileId': 'databricks\\notebooks\\sample.py', 'Line': 32, # 'Code': 'SPRKPY1002', 'Description': '...', 'Category': '...', 'CellId': '0'}, ...] # ^^^^^^ ^^^^ ^^^^^^ # Column names are CAPITALIZED. Key columns: FileId, Line, Code, Description, Category, CellId
⚠️ CRITICAL - PROCESS ALL FILES:
- MUST process ALL files returned by the queries, not just a subset
- MUST iterate through EVERY file with pending EWIs
- NEVER skip files or stop early
- In most Databricks workloads, the Issues.csv registers files with
.pyextension but the actual files inOutput/are.ipynb. ALWAYS try the.ipynbextension if the.pyfile does not exist, regardless of theCellIdvalue. The conditionCellId > 0is NOT reliable — some notebooks have EWIs atCellId = 0.
⚠️ CRITICAL - PATH NORMALIZATION:
FileIdvalues in the database may use Windows backslashes (e.g.,databricks\notebooks\file.py). ALWAYS normalize paths by replacing\with/before constructing filesystem paths.
Step 6: Build File Paths and Validate
For each EWI in query results:
- Construct full path:
<sma_output_dir>/Output/<FileId>(after normalizing path separators)- Handle path separators (Windows
\→ Unix/) - Example:
FileId='databricks\\notebooks\\sample.py'→Output/databricks/notebooks/sample.py
- Handle path separators (Windows
- Resolve file extension (notebook detection):
- If the file exists at the constructed path → use it directly
- If the file does NOT exist:
- ALWAYS try replacing the extension with
.ipynb(do NOT rely onCellId > 0as the only trigger) - Example:
sample.pynot found → trysample.ipynb - This is the MOST COMMON case in Databricks workloads — SMA registers files as
.pybut the actual output is.ipynb
- ALWAYS try replacing the extension with
- If neither path exists → log as error and continue with next file
- Read the file to analyze:
- Verify EWI comments exist on reported line numbers
- Understand code context around each EWI
- Identify the issue based on EWI description
- Identify file type:
- Python files (.py): Use
editormulti_edittool - Scala files (.scala): Use
editormulti_edittool (same approach as .py files) - Notebooks (.ipynb): Use a Python script to read/modify/write the JSON directly (see Step 8 format warning). Use
CellIdto locate the correct cell
- Python files (.py): Use
Step 7: Present Findings
Present summary to user:
- EWI codes to fix with descriptions and categories
- Files affected per EWI with specific line numbers
- Reference documentation status (exists/missing/incomplete/generic template)
- Proposed fixes based on reference documentation OR AI analysis
Step 8: Apply Fixes (Per File)
⚠️ CRITICAL: NOTEBOOK CELL SOURCE FORMAT
SMA output notebooks store cell['source'] as a single string, NOT as an array. Example:
{"source": "#EWI: SPRKPY1055 => ...\nsource_data = spark.read.parquet(...)"}
When reading: The source is already a string. Just split by \n to get lines:
src = cell['source'] # Already a string
if isinstance(src, list):
src = ''.join(src) # Safety fallback if somehow it's a list
lines = src.split('\n')
When writing back: ALWAYS write as a string, NEVER as a list.
cell['source'] = new_src # String, NOT list(new_src)
⛔ NEVER use list(new_src) — this creates a character array (["#", "E", "W", ...]) which corrupts the notebook. The notebook will appear to render correctly in some viewers but the JSON structure is broken.
⛔ Do NOT use notebook_edit_cell or notebook_actions tools for .ipynb files. These tools may alter the source format. Instead, always use a Python script to edit notebooks:
import json
with open(path) as f:
nb = json.load(f)
# ... modify cell['source'] as string ...
with open(path, 'w') as f:
json.dump(nb, f, indent=1, ensure_ascii=False)
⚠️ BATCH PROCESSING STRATEGY FOR LARGE WORKLOADS
When the workload has many files (>20), use this strategy for efficiency:
- Classify first, fix second. Group all EWI codes into three categories:
- Auto-fixable (already handled by SMA or informational): Mark as
[FIXED]in bulk - Definitively not-fixable (no Snowpark equivalent): Mark as
[NOT-FIXED]in bulk - Needs case-by-case analysis: Process individually
- Auto-fixable (already handled by SMA or informational): Mark as
- Apply bulk marks using inline scripts, not individual file edits. Process all auto-fixable and not-fixable EWIs across all files in a single pass per file type (.py, .ipynb, and .scala separately).
- Only do individual file analysis for the EWIs that genuinely need code-level decisions.
- Do NOT launch background agents for parallel file processing — the overhead of coordination and lack of progress visibility makes them unreliable for this task. Use inline scripts instead.
⚠️ PROGRESS VALIDATION
After processing each batch of files, verify the count of EWIs found matches expectations. If a processing pass finds 0 EWIs in files that the database says should have EWIs, STOP and investigate — this is almost certainly a bug in the parsing logic (e.g., the notebook cell format issue above). A count of 0 EWIs found across multiple files is a red flag, not a success.
CRITICAL: DO NOT SIMPLIFY OR RESTRUCTURE FILES
- Only fix the specific issues EWIs identify
- Preserve all original code structure, logic, test blocks, and functionality
- Do NOT refactor, consolidate, or "clean up" code unless explicitly part of the EWI fix
- Files may contain repetitive or test code that appears redundant - leave it intact
Process files one at a time. For each file:
⚠️ CRITICAL - LINE SHIFT PREVENTION: When a file has multiple EWIs, apply fixes from bottom to top (highest line number first). This ensures that line number changes from edits (adding/removing lines) do not shift the positions of EWIs that haven't been processed yet. If you process top-to-bottom, earlier edits will invalidate the line numbers of later EWIs.
Alternative: Re-read the file after each edit to recalculate line positions before applying the next fix.
8.1 Apply All EWI Fixes for the Current File
⚠️ CRITICAL - MUST ATTEMPT TO FIX EVERY EWI:
- You MUST read the reference markdown AND analyze the code context for EACH EWI before deciding if it can be fixed.
- You MUST attempt to apply a code fix (Case A or Case B) for every EWI. Case C (
[NOT-FIXED]) is ONLY for EWIs where you genuinely cannot determine a fix after reading the reference and analyzing the code. - NEVER bulk-mark all EWIs as
[NOT-FIXED]using sed/regex. Each EWI must be individually analyzed and fixed or explicitly determined to be unfixable. - NEVER skip the fix attempt. Even without a reference markdown, use AI analysis (Case B) to determine and apply a fix.
- A typical workload should have the MAJORITY of EWIs resolved as
auto_resolved, notnot_auto_resolved.
Decision Tree for Each EWI in the file:
Case A: Reference markdown exists and provides complete fix guidance
- Apply fix from reference markdown recommendations
- Handle EWI comment based on user's choice from Step 1:
- If "Remove": Delete the EWI comment line
- If "Mark": Change
#EWI: SPRKPY...to#EWI: [FIXED] SPRKPY...
- Track fix source: "Applied using SPRKPY.md guidance"
Case B: Reference markdown missing OR incomplete/vague OR is generic template
- Analyze code context and EWI description
- Determine fix using AI understanding of:
- PySpark/Scala → Snowpark conversion patterns
- Python/Snowflake best practices
- The specific error/warning from EWI description
- Apply the fix determined by AI
- Handle EWI comment based on user's choice from Step 1:
- If "Remove": Delete the EWI comment line
- If "Mark": Change
#EWI: SPRKPY...to#EWI: [FIXED] SPRKPY...
- Track fix source: "EWI context not complete. Fix: "
Case C: Fix could not be applied (unsupported pattern, etc.)
- Handle EWI comment based on user's choice from Step 1:
- If "Remove": Leave the EWI comment as-is (do not remove unfixed EWIs)
- If "Mark": Change
#EWI: SPRKPY...to#EWI: [NOT-FIXED] SPRKPY...
- Track status as 'failed'
EWI Comment Transformation Examples:
# Original EWI comment:
#EWI: SPRKPY1002 => Element spark.read.format is not supported
# If user chose "Remove" and fix succeeded:
# (comment deleted entirely)
# If user chose "Mark" and fix succeeded:
#EWI: [FIXED] SPRKPY1002 => Element spark.read.format is not supported
# If user chose "Mark" and fix failed:
#EWI: [NOT-FIXED] SPRKPY1002 => Element spark.read.format is not supported
For Python/Scala files (.py, .scala):
- Use
editormulti_edittool to:- Apply fix based on reference markdown OR AI analysis
- Handle EWI comment per user's choice (remove or mark)
- Track which lines were fixed
For Notebooks (.ipynb):
- Use a Python script to edit notebook JSON directly (NOT
notebook_edit_cell):- Read JSON, modify
cell['source']as string, write JSON back - Handle EWI comments per user's choice (remove or mark)
- ALWAYS keep
sourceas string — NEVER convert to list
- Read JSON, modify
8.2 Verify File Compiles
After applying ALL fixes to the current file, verify the code compiles:
For Python files (.py):
- Run:
python3 -m py_compile <file_path>using the Bash tool - Check exit code and any syntax errors
- If compilation fails:
- Read the error message carefully
- Identify the exact line and issue
- Fix the syntax error (e.g., missing brackets, parentheses, quotes)
- Re-run compilation check until it passes
For Scala files (.scala):
- Run:
scalac -Xfatal-warnings <file_path>if scalac is available, otherwise verify syntax by checking for balanced braces, proper imports, and valid Scala constructs - If compilation fails:
- Read the error message carefully
- Fix the syntax error
- Re-run compilation check until it passes
For Notebooks (.ipynb):
- Use
notebook_get_kernel_statusto verify kernel is available - Use
notebook_run_cellwith mode="all" to execute all cells - Check for execution errors
- If execution fails:
- Review error output
- Fix the issues
- Re-run cells until successful
Common syntax errors to watch for:
- Missing closing brackets:
},],) - Unmatched quotes:
",' - Incomplete string replacements that break existing code
- Accidentally removing necessary code during EWI comment removal
- Corrupting notebook JSON by converting
sourcestring to character array (NEVER uselist()on source)
Principle:
- EWI fixes should ONLY add or remove specific problematic code
- Never break existing valid syntax
- If unsure whether code should be removed, KEEP IT and only remove the EWI comment
8.3 Record Results in SQLite (Batch per File)
After verifying compilation, batch insert all results for the current file.
IMPORTANT:
- Accumulate results in a list while processing EWIs in steps 8.1-8.2
- Insert all results for the file in ONE batch call (more efficient)
- Each dict in the list represents ONE EWI (one row per EWI)
affected_filemust be the full absolute path
Workflow:
# 1. Initialize list at start of file processing
file_results = []
# 2. As you fix each EWI, append to the list:
file_results.append({
'ewi_code': 'SPRKPY1002',
'fix_description': 'Replaced spark.read.format("parquet") with session.read.parquet()',
'affected_file': '/full/path/to/Output/data_loader.py',
'affected_lines': '15',
'status': 'success'
})
file_results.append({
'ewi_code': 'SPRKPY1045',
'fix_description': 'Replaced RDD operation with DataFrame equivalent',
'affected_file': '/full/path/to/Output/data_loader.py',
'affected_lines': '23',
'status': 'success'
})
file_results.append({
'ewi_code': 'SPRKPY1091',
'fix_description': 'Unable to fix: Custom UDF pattern not supported',
'affected_file': '/full/path/to/Output/data_loader.py',
'affected_lines': '78',
'status': 'failed'
})
# 3. After verifying compilation, batch insert all results for this file:
batch_insert_fix_results(workload_path=sma_output_dir, results=file_results)
Status Values:
'success'- Fix was applied and file compiles correctly'failed'- Fix could not be applied (unsupported pattern, compilation error persists, etc.)
⚠️ CRITICAL - STATUS CONSISTENCY BETWEEN ewi_fixer_results AND Issues TABLE:
The ewi_fixer_results.status (success/failed) and the Issues.status (auto_resolved/not_auto_resolved) MUST be consistent:
- If the edit tool modified the file for this EWI →
ewi_fixer_results.status = 'success'ANDIssues.status = 'auto_resolved' - If the file was NOT modified for this EWI (unsupported pattern, no fix available) →
ewi_fixer_results.status = 'failed'ANDIssues.status = 'not_auto_resolved' - NEVER set
Issues.status = 'not_auto_resolved'when the fix was actually applied to the file. Even if the fix requires manual review later, if the code was changed, it isauto_resolved.
8.4 Repeat for Next File
Move to the next file and repeat steps 8.1 → 8.2 → 8.3 until all files are processed.
Step 9: Update SQLite Database with Results
For each EWI fixed, update the Issues table:
update_ewi_status_single(
workload_path=sma_output_dir,
code='SPRKPY1002',
file_id='databricks/notebooks/sample.py', # Uses FileId column
line=32, # Uses Line column
status='auto_resolved',
notes='AI Resolved: Fix applied using guidance from SPRKPY1002.md reference documentation.'
)
Notes Format Based on Fix Source:
With complete specific reference markdown:
AI Resolved: Fix applied using guidance from SPRKPY<NUMBER>.md reference documentation.Without complete reference (missing/incomplete/generic template):
AI Resolved: EWI context is not complete or does not exist. The fix found: <describe fix in detail>. Consider updating SPRKPY<NUMBER>.md with this solution.With complete reference but requiring additional context:
AI Resolved: Fix applied using SPRKPY<NUMBER>.md guidance. Additional notes: <special considerations>.
Important: Do NOT delete database rows - only UPDATE status and notes columns.
⚠️ DB vs FILE RECONCILIATION:
The database may have more EWI entries than the #EWI: comments found in files because:
- A single
FileIdcan appear multiple times with differentLinevalues SPRKDBXcodes may not generate visible#EWI:comments in the source code- Duplicate entries may exist when the same code appears on the same line with different descriptions
At the end of processing, update ALL remaining pending entries in the database to either auto_resolved or not_auto_resolved, not only the ones that matched #EWI: comments in the files. Use the EWI code classification (auto-fixable vs not-fixable) to determine the correct status.
Step 10: Update Reference Documentation (When Applicable)
IMPORTANT: Distinguish between generic template markdowns and specific markdowns:
Generic Template Markdowns (DO NOT MODIFY):
- Contain placeholder text like
<element>,<option_name>,<function_name> - Apply to multiple different scenarios
- Examples: SPRKPY1002 (generic "element is not supported")
- Action: Do NOT modify these files - they serve as general templates
Specific Markdowns (CAN BE ENHANCED):
- Reference concrete functions, features, or specific conversion patterns
- Examples: SPRKPY1093 (JDBC connection options), SPRKPY1091 (concat_ws function), SPRKPY1092 (backslash removal)
- Action: Can be updated with additional examples, clarifications, or fix strategies
For Specific Markdowns that need enhancement:
- Update
references/<EWI_CODE>.mdwhen:- You found a fix not documented in the existing markdown
- You found a better or alternative approach to what's already documented
- You encountered a special case or edge case not covered
- You have additional examples that would help future fixes
- Update by adding (not replacing):
- New examples with before/after code
- Alternative solutions section
- Clarifications on edge cases
- Additional fix strategies
- Use this format for alternatives:
## Resolution ### Approach 1: [Original approach] [existing content] ### Approach 2: [Your alternative] [your fix and explanation]
For Missing Markdowns:
Create references/<EWI_CODE>.md with standard structure.
Always document in database:
Even if you don't update markdown files, ALWAYS document the fix in the database notes column.
Step 11: Display Summary and Finalize
⚠️ CRITICAL: You MUST call update_summary_end() before finishing the workflow.
This completes the summary record that was started in Step 4.
# Get final statistics from Issues table
stats = get_summary_stats(workload_path=sma_output_dir)
code_stats = get_ewi_code_stats(workload_path=sma_output_dir)
# Get fix results statistics for this session
fix_stats = get_fix_results_stats(workload_path=sma_output_dir)
fix_results = get_fix_results(workload_path=sma_output_dir)
# Calculate files statistics
files_with_success = set()
files_with_failure = set()
for result in fix_results:
if result['status'] == 'success':
files_with_success.add(result['affected_file'])
else:
files_with_failure.add(result['affected_file'])
total_files_fixed = len(files_with_success)
total_not_auto_resolved_files = len(files_with_failure - files_with_success) # Files with ONLY failures
# ⚠️ REQUIRED: Update summary record with final results
update_summary_end(
workload_path=sma_output_dir,
total_ewis=fix_stats['total'],
auto_resolved_ewis=fix_stats['success'],
not_auto_resolved_ewis=fix_stats['failed'],
total_files_fixed=total_files_fixed,
total_not_auto_resolved_files=total_not_auto_resolved_files,
compilation_errors_fixed=compilation_errors_fixed # Track compilation errors fixed
)
# Display summary to user
print(f"Fix Session ID: {fix_id}")
print(f"Fixes attempted: {fix_stats['total']}")
print(f" - Success: {fix_stats['success']}")
print(f" - Failed: {fix_stats['failed']}")
print(f"Files fixed: {total_files_fixed}")
print(f"Files with no fixes: {total_not_auto_resolved_files}")
print(f"Compilation errors fixed: {compilation_errors_fixed}")
# No db.close() needed — connections are handled per-call
IMPORTANT:
- Always call
update_summary_end()before finishing the workflow - This updates the summary record created in Step 4 with the final results
- Without this call, the summary will only have
fix_idandstart_time
Step 12: Commit Changes to Git
After all fixes are applied and verified, commit the changes to the Git repository using sma_api.
- Commit all changes:
Returns:result = sma_api.git_commit("<sma_output_dir>", """DVP EWI Fixer: <summary> Fix Session ID: <fix_id> Total EWIs processed: <total> - Resolved: <success_count> - Not fixed: <failed_count> Files modified: <files_count> Compilation errors fixed: <compilation_errors_fixed>"""){"success": True, "action": "committed", "commit_hash": "abc1234"}
Example commit message:
DVP EWI Fixer: Resolved 265 EWIs across 41 files
Fix Session ID: c2a3ddad-6981-4769-8341-ce9d77049671
Total EWIs processed: 265
- Resolved: 263
- Not fixed: 2
Files modified: 41
Compilation errors fixed: 3
Note: The original code remains accessible via:
git checkout main # or master
Verify both branches exist and are correct:
result = sma_api.git_verify_branches("<sma_output_dir>")Returns:
{"success": True, "branches": [...], "has_main": True, "has_migration": True}⚠️ CRITICAL: At the end of the process there MUST be exactly two branches:
main— contains the original SMA output with zero modifications (the initial commit)sma/migration-process— contains all EWI fixes applied on top ofmain
If
result["has_main"]isFalse, the process failed. The user needsmainas a clean reference to diff against, revert to, or compare with the fixed version.
Stopping Points
- If SMA output directory not found or invalid
- If neither Issues.csv nor IssuesConnect.csv exists in Reports folder
- If file path cannot be constructed or file doesn't exist
- DO NOT stop if reference markdown is missing - analyze and fix anyway
- DO NOT proceed to database updates if code compilation fails - fix syntax errors first
Database Access Module
All database operations use functions from sma_api.py:
Module location: skills/spark-migration/snowpark-api/scripts/sma_api.py
Import and call functions directly. All functions receive workload_path as their first parameter.
⚠️ CRITICAL — MANDATORY USAGE:
- MUST execute the full workflow from Step 1 every time the skill is invoked — NEVER skip steps or decide "work is already complete"
- MUST use functions from
sma_api.pyfor ALL database operations - NEVER create tables manually with raw SQL
- NEVER improvise table names or schemas
- MUST call
update_summary_end()before finishing the workflow - MUST make Git commit at the end (Step 12) with summary message
- The ONLY valid tables are:
Issues,ewi_fixer_results,ewi_fixer_summary - NEVER create external script files (
/tmp/*.py,fix_script.py,batch_fix.py, etc.) — write all processing logic as inline Python in Bash tool calls - NEVER use
cat >orwriteto create standalone Python scripts for batch processing - NEVER open separate
sqlite3.connect()connections — ALL database operations MUST go throughsma_api.pyfunctions
Function Reference
Initialization:
initialize_database(workload_path)— Create/load database, create tables, generate fix_id, insert initial summary
Issues Table (EWI tracking):
get_pending_ewi_codes(workload_path)— Distinct pending EWI codes. Returns dicts:Code,Description,Categoryget_ewis_by_code(workload_path, code, status)— EWIs for specific code. Returns dicts:FileId,Line,Code,Description,Category,CellIdget_ewis_by_file(workload_path, file_id, status)— EWIs for specific fileupdate_ewi_status_single(workload_path, code, file_id, line, status, notes)— Update single row (matches on Code + FileId + Line)update_ewi_status(workload_path, code, status, notes)— Update all rows for a code (cascading)bulk_update_ewi_status(workload_path, codes, status)— Update multiple codes at oncereset_all_to_pending(workload_path)— Reset ALL EWIs to 'pending'reset_not_resolved_to_pending(workload_path)— Resetnot_auto_resolvedto 'pending'get_summary_stats(workload_path)— Status countsget_ewi_code_stats(workload_path)— Per-code statistics
Fix Results Table (per-EWI tracking):
generate_fix_id(workload_path)— Generate a new fix session IDinsert_fix_result(workload_path, ewi_code, fix_description, affected_file, affected_lines, status)— Record single fixbatch_insert_fix_results(workload_path, results)— Batch insert multiple fix results (recommended)get_fix_results(workload_path, fix_id)— Get fix results for a sessionget_fix_results_stats(workload_path, fix_id)— Get success/failed counts
Summary Table (per-session tracking):
insert_summary_start(workload_path)— Called automatically by initialize_databaseupdate_summary_end(workload_path, total_ewis, auto_resolved_ewis, not_auto_resolved_ewis, total_files_fixed, total_not_auto_resolved_files, compilation_errors_fixed)— Complete summaryget_fix_summary(workload_path, fix_id)— Get summary record
See references/DATABASE_SCHEMA.md for full database documentation.
Output
- Modified
.py,.ipynb, and.scalafiles in the SMA output directory (onsma/migration-processbranch) with EWIs resolved and verified to compile/execute correctly - Git commit with all changes on
sma/migration-processbranch - Original code preserved in
main/masterbranch (recoverable viagit checkout main) - Updated
sma_storage.sqlite3database with:statuscolumn updated to 'auto_resolved' for fixed EWIsnotescolumn populated with fix source and description
- Enhanced or created
references/<EWI_CODE>.mdfiles for specific EWIs (not generic templates) - Summary of changes:
- Number of EWIs resolved per code
- Files modified
- Lines changed per file
- Which EWIs were fixed with/without reference documentation
- Which reference markdowns were updated (if any)
- Compilation/execution verification status for each file
- Database statistics:
- Total EWIs: pending/resolved/not_auto_resolved counts
- Per-code statistics breakdown
Final Summary
MANDATORY: After completing all steps (whether running standalone or invoked from the orchestrator), ALWAYS present this summary table:
EWI Fixer Complete
┌──────────────┬──────────┬──────────────────────────────────────────────────────────┐
│ Step │ Status │ Details │
├──────────────┼──────────┼──────────────────────────────────────────────────────────┤
│ EWI Fixer │ Done │ Fixed N/M EWIs (N resolved, M not auto-resolved) │
└──────────────┴──────────┴──────────────────────────────────────────────────────────┘
Output location: <output>/
Git branches:
• main — original code (unmodified)
• sma/migration-process — EWI fixes applied
Rules:
- Replace
NandMwith actual counts from the fix session - Status is
Done,Skipped, orFailed - If no EWIs were found to fix, show
Skippedwith reason - If fixing failed, show
Failedwith brief error - The git branches section uses
sma_api.git_verify_branches()to confirm both branches exist