AST Injection Issues - Troubleshooting Guide
AST Injection Issues - Troubleshooting Guide
Last Updated: February 28, 2026 Status: Production Reference Audience: CLI developers and users debugging injection failures
Table of Contents
- Understanding AST Injection
- Diagnostic Flowchart
- Common Failure Scenarios
- Syntax Error Debugging
- Architectural Violations
- Debugging Techniques
- Real-World Examples
- Prevention & Best Practices
Understanding AST Injection
What is AST Injection?
AST (Abstract Syntax Tree) injection is the process of programmatically modifying code by:
- Parsing the target file into a syntax tree
- Locating injection points (markers or structural patterns)
- Transforming the AST by adding/modifying nodes
- Generating valid code from the modified AST
- Writing the result back to the file
Why AST vs String Replacement?
# ❌ String replacement (fragile)code = code.replace("router = APIRouter()", "router = APIRouter(\nrouter.include_router(commerce)")# Result: Syntax error (unclosed parenthesis, wrong indentation)
# ✅ AST manipulation (type-safe)tree = ast.parse(code)tree.body.append(ast.Expr( value=ast.Call( func=ast.Attribute(value=ast.Name(id='router'), attr='include_router'), args=[ast.Name(id='commerce_router')], keywords=[] )))# Result: Valid Python syntax guaranteedBenefits:
- Syntax correctness guaranteed
- Preserves formatting and comments (in most cases)
- Idempotent (can detect if already injected)
- Handles edge cases (multiline, type hints, complex nesting)
Tools We Use
- Python: Built-in
astmodule for Python files - ast-grep: For Astro/JavaScript/TypeScript files
- Synvert: For Ruby/Rails files (via SynvertAdapter)
- Fallback: String markers when AST fails
Diagnostic Flowchart
┌─────────────────────────────────────┐│ Does `sscli inject` complete? │└──────────────┬──────────────────────┘ │ ┌─────┴─────┐ NO YES │ │ ├─> See: "Command Failures" │ └─> Are files created in features/? │ ┌───────┴────────┐ NO YES │ │ ├─> See: "Files Not Created" │ └─> Are imports added? │ ┌───────┴────────┐ NO YES │ │ ├─> See: "Import Injection Failed" │ └─> Are routes registered? │ ┌───────┴────────┐ NO YES │ │ ├─> See: "Route Registration Failed" │ └─> Does feature work? │ ┌───────┴────────┐ NO YES │ │ ├─> See: "Runtime Errors" │ └─> ✅ Success!Common Failure Scenarios
1. AST Parsing Failed - Invalid Syntax
Error Message:
AstGrepError: ast-grep is not installed.Install via: brew install ast-grep or cargo install ast-grep-cliOr:
Error: AST parsing failed: invalid syntax (routes.py, line 42)SyntaxError: unexpected indentCause: Target file has syntax errors before injection
Diagnosis:
# Check Python syntaxpython -m py_compile src/ui/routes/api.py
# Check JavaScript/Astro syntaxnpx eslint src/pages/index.astro --no-ignore
# Check Ruby syntaxruby -c config/routes.rbSolution:
# Fix syntax errors first# Common issues:# - Missing colons after function definitions (Python)# - Unclosed brackets/parentheses# - Wrong indentation (tabs/spaces mix)# - Missing semicolons (JavaScript)
# Example: Fix Python indentation# Before (❌):def my_function():return "value" # Wrong indentation
# After (✅):def my_function(): return "value" # Correct indentation
# Retry injection after fixingsscli inject commerce2. Injection Markers Missing
Error Message:
Warning: No injection markers found in src/ui/routes/api.pySkipping import injectionCause: Target file doesn’t contain # SSCLI: markers
Diagnosis:
# Check for markersgrep "# SSCLI:" src/ui/routes/api.pySolution - Add Markers Manually:
from fastapi import APIRouter
# SSCLI:IMPORTS:START# Feature imports will be injected here# SSCLI:IMPORTS:END
router = APIRouter()
# SSCLI:ROUTES:START# Feature route registrations will be injected here# SSCLI:ROUTES:ENDFor Astro Templates:
---// SSCLI:IMPORTS:START// Component imports injected here// SSCLI:IMPORTS:END
const title = "My Page";---
<main> {/* SSCLI:COMPONENTS:START */} {/* Components injected here */} {/* SSCLI:COMPONENTS:END */}</main>For Rails Routes:
Rails.application.routes.draw do # SSCLI:ROUTES:START # Feature routes injected here # SSCLI:ROUTES:END
root to: "home#index"end3. File Not Writable
Error Message:
PermissionError: [Errno 13] Permission denied: 'src/ui/routes/api.py'Diagnosis:
# Check file permissionsls -l src/ui/routes/api.py# Should show: -rw-r--r-- (readable and writable by owner)
# Check if file is open in another programlsof src/ui/routes/api.pySolution:
# Fix permissionschmod 644 src/ui/routes/api.py
# If file is locked by Gitgit status# If showing merge conflicts, resolve them first
# If opened in editor, save and close4. AST Variable Substitution Failed
Error Message:
Warning: Variable placeholder $$$PRICING_COMPONENT not found in targetInjection may be incompleteCause: Mismatch between variable name in rule and in code
Example - ast-grep Rule:
id: astro-inject-pricingfix: | $$$PRICING_COMPONENT # Variable expected herePython Caller:
# ❌ Wrong: Variable name mismatchrunner.inject_rule( target_path=path / "index.astro", rule_name="astro-inject-pricing", content_vars={"PRICING_COMP": "<PricingTiers />"} # Wrong name!)
# ✅ Correct: Variable names matchrunner.inject_rule( target_path=path / "index.astro", rule_name="astro-inject-pricing", content_vars={"PRICING_COMPONENT": "<PricingTiers />"})Diagnosis:
# Check rule file for variable namescat foundry/rules/astro-injections/inject-commerce.yml | grep '\$\$\$'
# Check Python code for content_varsgrep -A 5 "inject_rule.*commerce" foundry/actions/features.py5. Idempotency Check Failure
Symptom: Running injection twice creates duplicate code
Example - Duplicate Route Registration:
# After running `sscli inject commerce` twice:
router = APIRouter()router.include_router(commerce_router, prefix="/commerce")router.include_router(commerce_router, prefix="/commerce") # ❌ Duplicate!Cause: Injection doesn’t check if already applied
Solution - Add Idempotency Check:
def inject_commerce_routes(tree: ast.AST, feature: str) -> bool: """ Inject commerce routes with idempotency check.
Returns: True if injection performed, False if already exists. """ # Check if already injected for node in ast.walk(tree): if isinstance(node, ast.Call): if (hasattr(node.func, 'attr') and node.func.attr == 'include_router' and any(arg.id == 'commerce_router' for arg in node.args if isinstance(arg, ast.Name))): console.print(f"[yellow]⚠ {feature} already injected, skipping[/yellow]") return False
# Not found, proceed with injection registration = ast.Expr( value=ast.Call( func=ast.Attribute( value=ast.Name(id='router', ctx=ast.Load()), attr='include_router', ctx=ast.Load() ), args=[ast.Name(id='commerce_router', ctx=ast.Load())], keywords=[ ast.keyword(arg='prefix', value=ast.Constant(value='/commerce')) ] ) ) tree.body.append(registration) return TrueFor ast-grep: Use pattern matching to detect existing code:
id: astro-verify-commerce-injectionrule: pattern: | <PricingComponent />6. Malformed AST Transformation
Symptom: Generated code has syntax errors after injection
Example - Missing Context:
# ❌ Wrong: Missing ctx parameterast.Name(id='router') # Missing ctx=ast.Load()
# ✅ Correct:ast.Name(id='router', ctx=ast.Load())Example - Missing Keywords in Call:
# ❌ Wrong: Prefix won't workast.Call( func=..., args=[ast.Name(id='commerce_router')], keywords=[] # Missing prefix!)
# ✅ Correct:ast.Call( func=..., args=[ast.Name(id='commerce_router')], keywords=[ ast.keyword(arg='prefix', value=ast.Constant(value='/commerce')) ])Debugging:
# Validate generated code before writingtry: new_code = ast.unparse(tree) ast.parse(new_code) # Validate syntax console.print("[green]✓ Generated code is valid[/green]")except SyntaxError as e: console.print(f"[red]✗ Generated invalid code: {e}[/red]") raiseSyntax Error Debugging
Technique 1: Binary Search
When injection produces syntax errors, narrow down the problem:
# 1. Save a backupcp src/ui/routes/api.py src/ui/routes/api.py.backup
# 2. Run injectionsscli inject commerce
# 3. Check syntaxpython -m py_compile src/ui/routes/api.py
# 4. If error, comparediff src/ui/routes/api.py.backup src/ui/routes/api.py
# 5. Analyze the diff to find the problem lineTechnique 2: AST Dump Inspection
Inspect the AST structure before/after:
import ast
# Before injectionoriginal_code = """router = APIRouter()"""tree_before = ast.parse(original_code)print(ast.dump(tree_before, indent=2))
# After injection# ... perform injection ...
tree_after = ast.parse(modified_code)print(ast.dump(tree_after, indent=2))Technique 3: Incremental Validation
Validate at each step:
def safe_inject_feature(filepath: str, feature: str): """Inject feature with validation at each step.""" try: # Step 1: Parse with open(filepath) as f: code = f.read() tree = ast.parse(code) console.print("[green]✓ Step 1: Parse successful[/green]")
# Step 2: Check if already injected if is_already_injected(tree, feature): console.print(f"[yellow]⚠ {feature} already injected[/yellow]") return console.print("[green]✓ Step 2: Idempotency check passed[/green]")
# Step 3: Transform AST tree = add_feature_imports(tree, feature) console.print("[green]✓ Step 3: Imports added[/green]")
tree = add_feature_registration(tree, feature) console.print("[green]✓ Step 4: Routes added[/green]")
# Step 4: Generate code new_code = ast.unparse(tree) console.print("[green]✓ Step 5: Code generation successful[/green]")
# Step 5: Validate syntax ast.parse(new_code) console.print("[green]✓ Step 6: Syntax validation passed[/green]")
# Step 6: Write back with open(filepath, 'w') as f: f.write(new_code) console.print(f"[green]✓ {feature} injected successfully[/green]")
except SyntaxError as e: console.print(f"[red]✗ Syntax error: {e}[/red]") console.print(f"[red] Line {e.lineno}: {e.text}[/red]") raise InjectionError("Target file has syntax errors") except Exception as e: console.print(f"[red]✗ Injection failed: {str(e)}[/red]") raiseArchitectural Violations
Hexagonal Architecture Rules (Python SaaS)
Core Principle: Dependencies flow inward: UI → Use Cases → Entities
Common Violations:
Violation 1: Entity Depending on Infrastructure
# ❌ WRONG: Entity importing from infrastructurefrom features.commerce.src.infrastructure.stripe_client import StripeClient
class Payment: def process(self): client = StripeClient() # ❌ Entity depends on infrastructure! return client.charge()Fix:
# ✅ CORRECT: Entity remains purefrom decimal import Decimal
class Payment: def __init__(self, amount: Decimal, currency: str): self.amount = amount self.currency = currency self.status = "pending"
def mark_completed(self): self.status = "completed"
# Infrastructure adapter handles external dependencies# File: features/commerce/src/infrastructure/payment_gateway.pyfrom features.commerce.src.entities.payment import Payment
class StripePaymentGateway: def process_payment(self, payment: Payment) -> bool: # Stripe client lives here, not in entity return self._stripe_client.charge(payment.amount)Violation 2: Use Case Depending on UI
# ❌ WRONG: Use case importing from routesfrom src.ui.routes.api import get_current_user # ❌ Wrong direction!
def process_payment(payment_data: dict): user = get_current_user() # ❌ Use case depends on UI!Fix:
# ✅ CORRECT: Pass user as parameterfrom features.commerce.src.entities.payment import Payment
def process_payment(payment_data: dict, user_id: str) -> Payment: """ Process payment for a user.
Args: payment_data: Payment details user_id: ID of user (passed from UI layer) """ payment = Payment(**payment_data) # Business logic here return payment
# UI layer passes user_id# File: src/ui/routes/commerce.py@router.post("/payments")async def create_payment( payment: PaymentRequest, current_user: User = Depends(get_current_user)): result = process_payment( payment.dict(), user_id=current_user.id # ✅ UI provides user_id ) return resultViolation 3: Circular Dependencies
# ❌ WRONG: Circular importfrom features.commerce.src.use_cases.payment import process_payment
def create_order(order_data: dict): payment_result = process_payment(order_data["payment"])
# File: features/commerce/src/use_cases/payment.pyfrom features.commerce.src.use_cases.order import create_order # ❌ Circular!
def process_payment(payment_data: dict): if payment_data["type"] == "order": create_order(payment_data) # ❌ Circular dependency!Fix:
# ✅ CORRECT: Introduce shared interfacefrom abc import ABC, abstractmethod
class PaymentProcessor(ABC): @abstractmethod def process(self, data: dict): pass
# File: features/commerce/src/use_cases/order.pyfrom features.commerce.src.entities.interfaces import PaymentProcessor
def create_order(order_data: dict, payment_processor: PaymentProcessor): payment_processor.process(order_data["payment"]) # ✅ Dependency injected
# File: features/commerce/src/use_cases/payment.pyfrom features.commerce.src.entities.interfaces import PaymentProcessor
class PaymentService(PaymentProcessor): def process(self, data: dict): # Payment logic passDetection Script
def validate_hexagonal_architecture(project_path: Path): """ Validate that injected code follows hexagonal architecture.
Rules: - Entities must not import from use_cases or infrastructure - Use cases must not import from ui - No circular dependencies """ violations = []
# Check entities for entity_file in (project_path / "features" / "*/src/entities").glob("*.py"): content = entity_file.read_text()
# Entities should not import from infrastructure if "from .infrastructure" in content or "from ..infrastructure" in content: violations.append(f"{entity_file}: Entity imports from infrastructure")
# Entities should not import from use_cases if "from .use_cases" in content or "from ..use_cases" in content: violations.append(f"{entity_file}: Entity imports from use_cases")
# Check use_cases for usecase_file in (project_path / "features" / "*/src/use_cases").glob("*.py"): content = usecase_file.read_text()
# Use cases should not import from ui if "from src.ui" in content: violations.append(f"{usecase_file}: Use case imports from UI layer")
if violations: console.print("[red]✗ Architectural violations detected:[/red]") for violation in violations: console.print(f" [red]- {violation}[/red]") return False
console.print("[green]✓ Hexagonal architecture validated[/green]") return TrueDebugging Techniques
1. Verbose Mode / Debug Logging
Enable debug output:
# CLI debug modesscli inject commerce --debug
# Python internal debuggingSSCLI_DEBUG=1 sscli inject commerceIn code:
import logging
logger = logging.getLogger(__name__)logger.setLevel(logging.DEBUG)
def inject_feature(path: Path, feature: str): logger.debug(f"Starting injection for {feature}") logger.debug(f"Target path: {path}")
tree = ast.parse(path.read_text()) logger.debug(f"AST parsed: {len(tree.body)} nodes")
# ... injection logic ...
logger.debug(f"AST modified: {len(tree.body)} nodes")2. Dry Run Mode
Test injection without modifying files:
def inject_feature_dry_run(path: Path, feature: str): """ Simulate injection without writing files. """ console.print(f"[yellow]DRY RUN: Would inject {feature} into {path}[/yellow]")
# Parse and transform tree = ast.parse(path.read_text()) tree_modified = add_feature(tree, feature)
# Generate code new_code = ast.unparse(tree_modified)
# Show diff instead of writing console.print("[cyan]--- Original[/cyan]") console.print(path.read_text()) console.print("[cyan]+++ Modified[/cyan]") console.print(new_code)
console.print("[yellow]⚠ No files modified (dry run)[/yellow]")3. AST Visualization
Visualize the syntax tree:
def visualize_ast(code: str): """Pretty-print AST structure.""" tree = ast.parse(code) print(ast.dump(tree, indent=2))
# Example usagecode = """from fastapi import APIRouter
router = APIRouter()router.include_router(commerce_router, prefix="/commerce")"""
visualize_ast(code)
# Output:# Module(# body=[# ImportFrom(# module='fastapi',# names=[alias(name='APIRouter')],# level=0),# Assign(# targets=[Name(id='router', ctx=Store())],# value=Call(# func=Name(id='APIRouter', ctx=Load()),# args=[],# keywords=[])),# Expr(# value=Call(# func=Attribute(# value=Name(id='router', ctx=Load()),# attr='include_router',# ctx=Load()),# args=[Name(id='commerce_router', ctx=Load())],# keywords=[# keyword(# arg='prefix',# value=Constant(value='/commerce'))]))],# type_ignores=[])4. Incremental Injection
Test injection step-by-step:
# Test 1: Just copy filessscli inject commerce --files-only
# Test 2: Add importssscli inject commerce --imports-only
# Test 3: Add routessscli inject commerce --routes-only
# Test 4: Full injectionsscli inject commerce5. Rollback Testing
Test injection with automatic rollback:
import shutilfrom contextlib import contextmanager
@contextmanagerdef rollback_on_error(target_path: Path): """ Backup file before injection, restore on error. """ backup_path = target_path.with_suffix(target_path.suffix + ".backup")
try: # Backup shutil.copy(target_path, backup_path) console.print(f"[cyan]Backup created: {backup_path}[/cyan]")
yield
# Success - remove backup backup_path.unlink() console.print("[green]✓ Injection successful, backup removed[/green]")
except Exception as e: # Error - restore backup console.print(f"[red]✗ Injection failed: {e}[/red]") shutil.copy(backup_path, target_path) console.print(f"[yellow]⚠ Restored from backup[/yellow]") raise
# Usagewith rollback_on_error(Path("src/ui/routes/api.py")): inject_feature(Path("src/ui/routes/api.py"), "commerce")Real-World Examples
Example 1: Failed Python SaaS Commerce Injection
Initial State:
from fastapi import APIRouter
router = APIRouter()
@router.get("/health")def health_check(): return {"status": "ok"}Command:
sscli inject commerceError:
Error: AST parsing failed: invalid syntax (api.py, line 8)SyntaxError: unexpected indentDiagnosis:
python -m py_compile src/ui/routes/api.py# File "src/ui/routes/api.py", line 8# return {"status": "ok"}# ^# IndentationError: unexpected indentRoot Cause: Mixed tabs and spaces in original file
Resolution:
# Fix indentation (use spaces only)from fastapi import APIRouter
router = APIRouter()
@router.get("/health")def health_check(): return {"status": "ok"} # Fixed: 4 spaces, not tab
# Add markers for injection# SSCLI:IMPORTS:START# SSCLI:IMPORTS:END
# SSCLI:ROUTES:START# SSCLI:ROUTES:ENDRetry:
sscli inject commerce# ✓ Commerce feature injected successfullyExample 2: Astro Theme Injection with Missing Placeholder
Initial State:
:root { --color-primary: #000;}Command:
sscli generate --name my-landing --template static-landing --with-themesError:
Warning: Theme placeholder not found in themes.cssFalling back to append modeRoot Cause: Missing [SS-FEATURE-THEMES-PLACEHOLDER] marker
Resolution:
/* [SS-FEATURE-THEMES-PLACEHOLDER] */
:root { --color-primary: #000;}Retry:
sscli generate --name my-landing --template static-landing --with-themes# ✓ Themes injected at placeholder locationExample 3: Rails Route Injection Idempotency Failure
Initial State:
Rails.application.routes.draw do root to: "home#index"endCommand (run twice):
sscli inject commercesscli inject commerce # Run againResult (before fix):
# File: config/routes.rb (after 2nd injection)Rails.application.routes.draw do root to: "home#index"
# Commerce routes namespace :commerce do resources :payments end
# Commerce routes namespace :commerce do resources :payments # ❌ Duplicate! endendRoot Cause: No idempotency check in Rails injection
Resolution:
# Add detection pattern in Synvert rewriterdef inject_commerce_routes # Check if already exists if_not_exist do within_node type: 'send', receiver: 'Rails.application', message: 'routes' do append """ # Commerce routes namespace :commerce do resources :payments end""" end endendRetry:
sscli inject commerce# ✓ Commerce routes injected
sscli inject commerce# ⚠ Commerce already injected, skippingExample 4: Circular Dependency After Injection
Initial State:
def create_order(order_data: dict): # Order creation logic passAfter Injection:
from features.commerce.src.use_cases.payment import process_payment
def create_order(order_data: dict): payment = process_payment(order_data["payment"])
# File: features/commerce/src/use_cases/payment.pyfrom features.commerce.src.use_cases.order import create_order # ❌ Circular!
def process_payment(payment_data: dict): if payment_data.get("create_order"): create_order(payment_data)Error:
ImportError: cannot import name 'create_order' from partially initialized moduleResolution - Refactor to Use Dependency Injection:
from abc import ABC, abstractmethod
class OrderService(ABC): @abstractmethod def create(self, data: dict): pass
class PaymentService(ABC): @abstractmethod def process(self, data: dict): pass
# File: features/commerce/src/use_cases/order.pyfrom features.commerce.src.entities.interfaces import OrderService, PaymentService
class OrderCreationService(OrderService): def __init__(self, payment_service: PaymentService): self._payment_service = payment_service
def create(self, order_data: dict): payment = self._payment_service.process(order_data["payment"]) # Continue order creation
# File: features/commerce/src/use_cases/payment.pyfrom features.commerce.src.entities.interfaces import PaymentService
class PaymentProcessingService(PaymentService): def process(self, payment_data: dict): # Payment logic (no import of order module) passPrevention & Best Practices
Before Injection
1. Pre-flight Validation Checklist
#!/bin/bashecho "🔍 Pre-Injection Validation"echo "=========================="
# 1. Git status cleanif [[ -n $(git status -s) ]]; then echo "⚠️ Warning: Uncommitted changes detected" echo " Recommend: git commit -am 'pre-injection checkpoint'"fi
# 2. Python syntaxecho "✓ Checking Python syntax..."find src -name "*.py" -exec python -m py_compile {} \; 2>&1 | grep -q "SyntaxError" && { echo "❌ Python syntax errors detected" exit 1}
# 3. JavaScript/Astro syntax (if applicable)if [ -f "package.json" ]; then echo "✓ Checking JavaScript syntax..." npm run lint || echo "⚠️ Linting issues detected"fi
# 4. Disk spaceavailable=$(df -h . | tail -1 | awk '{print $4}')echo "✓ Disk space available: $available"
# 5. Injection markers presentif [ -f "src/ui/routes/api.py" ]; then grep -q "# SSCLI:" src/ui/routes/api.py || { echo "⚠️ Warning: No SSCLI markers found in api.py" echo " Injection may use fallback mechanisms" }fi
echo ""echo "✅ Pre-flight checks complete"echo "Ready to run: sscli inject <feature>"2. Backup Strategy
# Before injection, create backup branchgit checkout -b pre-injection-$(date +%Y%m%d-%H%M%S)git commit -am "Pre-injection snapshot"
# Run injectionsscli inject commerce
# Review changesgit diff pre-injection-*During Injection
3. Use Verbose Mode
# See what's happening in real-timesscli inject commerce --verbose --debug4. Dry Run First
# Test without modifying filessscli inject commerce --dry-run
# Review what would change, then applysscli inject commerceAfter Injection
5. Post-Injection Validation
#!/bin/bashecho "🔍 Post-Injection Validation"echo "==========================="
# 1. Syntax checkecho "✓ Checking syntax..."python -m py_compile src/**/*.py || exit 1
# 2. Import checkecho "✓ Checking imports..."python -c "import src.ui.routes.api" || exit 1
# 3. Test suiteecho "✓ Running tests..."pytest tests/ || exit 1
# 4. Lintecho "✓ Running linters..."ruff check src/ || echo "⚠️ Linting issues"
# 5. Type checkecho "✓ Type checking..."mypy src/ || echo "⚠️ Type errors"
# 6. Architecture validationecho "✓ Validating hexagonal architecture..."python scripts/validate_architecture.py
echo ""echo "✅ Post-injection validation complete"6. Manual Testing
# Start servicesdocker-compose up -d
# Check logsdocker-compose logs backend | grep -i error
# Test new endpointscurl http://localhost:8000/api/commerce/health
# Check OpenAPI docsopen http://localhost:8000/docs7. Code Review
# Review all changesgit diff
# Check only injected filesgit diff --name-only | grep -E '(features|routes)'
# Review changes by typegit diff src/ui/routes/api.py # Route registrationgit diff features/commerce/ # New feature filesContinuous Prevention
8. Add Pre-commit Hook
#!/bin/bash
# Prevent commits with syntax errorsfind src -name "*.py" -exec python -m py_compile {} \; 2>&1 | grep -q "SyntaxError" && { echo "❌ Python syntax errors detected. Commit aborted." exit 1}
# Validate hexagonal architecturepython scripts/validate_architecture.py || { echo "❌ Architecture violations detected. Commit aborted." exit 1}
exit 09. CI/CD Validation
name: Validate Injection
on: [push]
jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.11"
- name: Install dependencies run: | pip install -r requirements.txt pip install ast-grep-cli
- name: Validate syntax run: | find src -name "*.py" -exec python -m py_compile {} \;
- name: Validate architecture run: | python scripts/validate_architecture.py
- name: Run tests run: | pytest tests/ -vDebug Checklist
When injection fails, systematically check:
- CLI Version:
sscli --version(use latest) - Python Version:
python --version(3.10+ required) - Dependencies:
pip list | grep -E "(ast-grep|pyyaml|rich)" - File Syntax:
python -m py_compile <target_file> - Markers Exist:
grep "# SSCLI:" <target_file> - File Writable:
ls -l <target_file>(check permissions) - No Git Conflicts:
git status(no merge conflicts) - Disk Space:
df -h .(sufficient space) - License Valid:
sscli account info - Feature Available:
sscli features list - Idempotency: Already injected? Check file content
Getting Help
If injection still fails after following this guide:
1. Collect Diagnostic Information
# Generate diagnostic reportsscli doctor --verbose > diagnostic.log
# Run injection with debug outputsscli inject commerce --debug > injection.log 2>&1
# Capture system infoecho "OS: $(uname -a)" >> diagnostic.logecho "Python: $(python --version)" >> diagnostic.logecho "CLI: $(sscli --version)" >> diagnostic.log2. Create Minimal Reproduction
# Create minimal failing examplemkdir test-injectioncd test-injectionsscli init --template python-saas --name testsscli inject commerce --debug# Capture exactly what fails3. Report Issue
Create a GitHub issue with:
- Operating System & Python version
- CLI version (
sscli --version) - Command that failed
- Full error output (
injection.log) - Minimal reproduction steps
- Relevant file contents (anonymized)
GitHub: https://github.com/seed-source/stack-cli/issues
4. Emergency Manual Injection
If automated injection completely fails, see:
- Manual Injection Guide (coming soon)
- ADR 002: AST-Based Injection
Related Documentation
- Common Errors Troubleshooting
- Manual Injection Guide (coming soon)
- ADR 002: AST-Based Injection
- AST-Grep Quick Start
- Hexagonal Architecture Guide
Last Updated: February 28, 2026 Maintainer: Seed & Source CLI Team Feedback: Open an issue or submit a PR