AD

advanced-lokstra-validate-consistency

Ensures application integrity through static and runtime consistency checks.

Install

mkdir -p .claude/skills/advanced-lokstra-validate-consistency && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10553" && unzip -o skill.zip -d .claude/skills/advanced-lokstra-validate-consistency && rm skill.zip

Installs to .claude/skills/advanced-lokstra-validate-consistency

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Validate application consistency - circular dependencies, schema validation, config checks, annotation validation, and service registration. Use after all code is implemented to identify issues before deployment.
212 charsno explicit “when” trigger
Advanced

Key capabilities

  • Detect circular dependencies
  • Validate schema against migrations
  • Check configuration completeness
  • Verify annotation correctness
  • Validate service registration

How it works

Performs static analysis on annotations and imports, runtime resolution checks, and database schema verification.

Inputs & outputs

You give it
Lokstra application source code
You get back
Validation report and exit code

When to use advanced-lokstra-validate-consistency

  • Validate configuration completeness
  • Check for circular service dependencies
  • Verify database schema against migrations
  • Run pre-deployment consistency checks

About this skill

Advanced: Validate Consistency

Overview

This skill provides comprehensive validation tools for Lokstra applications to ensure:

  • Code quality and dependency correctness
  • Configuration completeness and validity
  • Database schema consistency
  • Annotation correctness
  • Service registration and injection validity

Validation Categories:

  1. Static Analysis - Runs without starting the app (annotations, imports, config files)
  2. Runtime Validation - Runs at application startup (service resolution, DI)
  3. Database Validation - Requires database connection (schema, migrations)

When to Use

Use this skill when:

  • Before merging code to production branch
  • Checking for circular dependencies between services
  • Validating configuration completeness
  • Ensuring database schema matches migrations
  • Detecting configuration mismatches
  • Pre-deployment validation
  • CI/CD pipeline integration

Prerequisites:

  • ✅ All code implemented (Phase 1-2 complete)
  • ✅ Configuration finalized (config.yaml, configs/*.yaml)
  • ✅ Database migrations created
  • ✅ Ready for deployment

Quick Validation Commands

# 1. Compile-time check (catches most errors)
go build ./...

# 2. Run with --generate-only (validates annotations without running server)
go run . --generate-only

# 3. Run all tests
go test ./... -v

# 4. Run specific validation scripts
go run scripts/validate_config.go
go run scripts/validate_deps.go
go run scripts/validate_annotations.go

# 5. Full pre-deployment check
bash scripts/pre_deploy_check.sh

1. Annotation Validation

Validate @Handler, @Service, @Route, @Inject Annotations

Lokstra generates code from annotations. Invalid annotations cause runtime failures.

File: scripts/validate_annotations.go

package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

type ValidationError struct {
	File    string
	Line    int
	Message string
}

var (
	handlerPattern = regexp.MustCompile(`@Handler\s+(?:name\s*=\s*"([^"]+)")?`)
	servicePattern = regexp.MustCompile(`@Service\s+"([^"]+)"`)
	routePattern   = regexp.MustCompile(`@Route\s+"(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^"]+)"`)
	injectPattern  = regexp.MustCompile(`@Inject\s+"([^"]+)"`)
)

func main() {
	root := "./modules"
	errors := validateAnnotations(root)

	if len(errors) > 0 {
		fmt.Println("❌ ANNOTATION VALIDATION ERRORS:")
		for _, err := range errors {
			fmt.Printf("  %s:%d - %s\n", err.File, err.Line, err.Message)
		}
		os.Exit(1)
	}

	fmt.Println("✅ All annotations are valid")
}

func validateAnnotations(root string) []ValidationError {
	var errors []ValidationError
	handlerNames := make(map[string]string) // name -> file
	serviceNames := make(map[string]string) // name -> file

	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") {
			return nil
		}

		// Skip generated files
		if strings.HasSuffix(path, "_lokstra_gen.go") {
			return nil
		}

		fset := token.NewFileSet()
		f, parseErr := parser.ParseFile(fset, path, nil, parser.ParseComments)
		if parseErr != nil {
			errors = append(errors, ValidationError{
				File:    path,
				Line:    0,
				Message: fmt.Sprintf("Parse error: %v", parseErr),
			})
			return nil
		}

		// Extract comments and validate annotations
		for _, cg := range f.Comments {
			for _, c := range cg.List {
				line := fset.Position(c.Pos()).Line
				text := c.Text

				// Validate @Handler
				if strings.Contains(text, "@Handler") {
					if match := handlerPattern.FindStringSubmatch(text); match != nil {
						name := match[1]
						if name == "" {
							errors = append(errors, ValidationError{
								File:    path,
								Line:    line,
								Message: "@Handler missing required 'name' parameter",
							})
						} else if existing, exists := handlerNames[name]; exists {
							errors = append(errors, ValidationError{
								File:    path,
								Line:    line,
								Message: fmt.Sprintf("Duplicate @Handler name '%s' (already in %s)", name, existing),
							})
						} else {
							handlerNames[name] = path
						}
					}
				}

				// Validate @Service
				if strings.Contains(text, "@Service") {
					if match := servicePattern.FindStringSubmatch(text); match != nil {
						name := match[1]
						if existing, exists := serviceNames[name]; exists {
							errors = append(errors, ValidationError{
								File:    path,
								Line:    line,
								Message: fmt.Sprintf("Duplicate @Service name '%s' (already in %s)", name, existing),
							})
						} else {
							serviceNames[name] = path
						}
					}
				}

				// Validate @Route
				if strings.Contains(text, "@Route") {
					if !routePattern.MatchString(text) {
						// Check for common mistakes
						if strings.Contains(text, `@Route "`) {
							errors = append(errors, ValidationError{
								File:    path,
								Line:    line,
								Message: "@Route format should be: @Route \"METHOD /path\" (e.g., @Route \"GET /users\")",
							})
						}
					}
				}

				// Validate @Inject
				if strings.Contains(text, "@Inject") {
					if match := injectPattern.FindStringSubmatch(text); match != nil {
						value := match[1]
						// Check for empty inject
						if strings.TrimSpace(value) == "" {
							errors = append(errors, ValidationError{
								File:    path,
								Line:    line,
								Message: "@Inject value cannot be empty",
							})
						}
					}
				}
			}
		}

		return nil
	})

	return errors
}

Run with: go run scripts/validate_annotations.go


2. Circular Dependency Detection

Module-Level Dependencies

Lokstra uses DDD bounded contexts (modules). Cross-module dependencies should be unidirectional.

File: scripts/validate_deps.go

package main

import (
	"fmt"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"strings"
)

type Dependency struct {
	From string
	To   string
}

func main() {
	root := "./modules"
	deps := findDependencies(root)
	cycles := findCycles(deps)

	if len(cycles) > 0 {
		fmt.Println("❌ CIRCULAR DEPENDENCIES DETECTED:")
		for _, cycle := range cycles {
			fmt.Println("  ", strings.Join(cycle, " -> "))
		}
		fmt.Println("")
		fmt.Println("💡 Solutions:")
		fmt.Println("  1. Extract shared types to modules/shared/domain/")
		fmt.Println("  2. Use interfaces for cross-module communication")
		fmt.Println("  3. Use event-driven patterns for decoupling")
		os.Exit(1)
	}

	fmt.Println("✅ No circular dependencies found")
}

func findDependencies(root string) []Dependency {
	var deps []Dependency

	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") {
			return nil
		}

		fset := token.NewFileSet()
		f, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
		if err != nil {
			return nil
		}

		module := extractModuleName(path)
		if module == "" || module == "shared" {
			return nil // Skip shared module
		}

		for _, imp := range f.Imports {
			importPath := strings.Trim(imp.Path.Value, "\"")
			if strings.Contains(importPath, "/modules/") {
				importedModule := extractModuleFromPath(importPath)
				if importedModule != "" && importedModule != module && importedModule != "shared" {
					deps = append(deps, Dependency{
						From: module,
						To:   importedModule,
					})
				}
			}
		}
		return nil
	})

	return deps
}

func extractModuleName(path string) string {
	parts := strings.Split(filepath.ToSlash(path), "/")
	for i, part := range parts {
		if part == "modules" && i+1 < len(parts) {
			return parts[i+1]
		}
	}
	return ""
}

func extractModuleFromPath(importPath string) string {
	parts := strings.Split(importPath, "/")
	for i, part := range parts {
		if part == "modules" && i+1 < len(parts) {
			return parts[i+1]
		}
	}
	return ""
}

func findCycles(deps []Dependency) [][]string {
	// Build adjacency list
	graph := make(map[string]map[string]bool)
	for _, dep := range deps {
		if graph[dep.From] == nil {
			graph[dep.From] = make(map[string]bool)
		}
		graph[dep.From][dep.To] = true
	}

	// DFS to find cycles
	var cycles [][]string
	visited := make(map[string]int) // 0=unvisited, 1=in-progress, 2=done

	var dfs func(node string, path []string) bool
	dfs = func(node string, path []string) bool {
		if visited[node] == 1 {
			// Found cycle - extract cycle from path
			cycleStart := -1
			for i, n := range path {
				if n == node {
					cycleStart = i
					break
				}
			}
			if cycleStart >= 0 {
				cycle := append(path[cycleStart:], node)
				cycles = append(cycles, cycle)
			}
			return true
		}
		if visited[node] == 2 {
			return false
		}

		visited[node] = 1
		path = append(path, node)

		for neighbor := range graph[node] {
			dfs(neighbor, path)
		}

		visited[node] = 2
		return false
	}

	for module := range graph {
		if visited[module] == 0 {
			dfs(module, nil)
		}
	}

	return cycles
}

3. Configuration Validation

Validate config.yaml and configs/*.yaml

File: scripts/validate_config.go

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v3"
)

type ConfigValidationError struct {
	File    string
	Path    string
	Message string
}

func main() {
	errors := []ConfigValidationError{}

	// Load all config files
	configFiles := []string{"config.yaml"}
	if entries, err := os.ReadDir("configs"); err == nil {
		for _, entry := range entries {
			if !entry.IsDir() && (filepath.Ext(entry.Name()) == ".yaml" || filepath.Ext(entry.Name()) == ".yml") {
				configFiles = append(configFiles, filepath.Join("configs", entry.Name()))
			}
		}
	}

	merged := make(map[string]any)

	// Parse and merge all configs
	for _, file := range configFiles {
		data, err := os.ReadFile(file)
		if err != nil {
			if file == "config.yaml" {
				errors = append(errors, ConfigValidationError{
					File:    file,
					Message: fmt.Sprintf("Cannot read config file: %v", err),
				})
			}
			c

---

*Content truncated.*

When not to use it

  • When database migrations are missing

Prerequisites

Code implementedConfiguration finalizedDatabase migrations created

Limitations

  • Requires database connection for schema validation

How it compares

Automates pre-deployment consistency checks that typically require manual verification.

Compared to similar skills

advanced-lokstra-validate-consistency side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
advanced-lokstra-validate-consistency (this skill)06moReviewAdvanced
run-tests15moReviewIntermediate
dbx-regenerate16moReviewIntermediate
litestream06moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry