Start with docs/CODING.md for the canonical implementation rules. This file is retained as deeper reference for contributors who need longer examples for naming, imports, error handling, and symlink-safe filesystem work.
- Clarity over cleverness: Write obvious code
- Single Responsibility: Functions do one thing well
- Error handling: Always handle errors, provide context
- Documentation: Export functions/types must have GoDoc comments
- Testing: All new code must have tests
lowercase_with_underscores.gofor regular files*_test.gofor test files- Descriptive names (e.g.,
command_validation.go, notutil.go)
- Short, lowercase, single word
- Examples:
resource,config,install,repo - No underscores or mixed caps
- Exported: PascalCase (e.g.,
ResourceType,LoadCommand) - Unexported: camelCase (e.g.,
resourcePath,loadConfig) - Constants: PascalCase for exported, camelCase for unexported
Resources must follow agentskills.io naming:
- Lowercase alphanumeric + hyphens only within each segment
/is allowed between valid segments for nested names such asapi/deploy- Cannot start/end with hyphen
- No consecutive hyphens
- 1-64 characters per segment
// Valid
"test", "run-coverage", "pdf-processing", "skill-v2", "api/deploy"
// Invalid
"Test", "test_coverage", "-test", "test--cmd", "api//deploy"Group imports in three sections with blank lines:
- Standard library
- External dependencies
- Internal packages
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
"github.com/dynatrace-oss/ai-config-manager/v3/pkg/resource"
)Always wrap errors with context:
if err != nil {
return fmt.Errorf("failed to load command: %w", err)
}Rules:
- Use
%wto wrap errors (preserves error chain) - Provide descriptive context
- Don't panic (except in main/init for fatal errors)
- Check errors immediately
Examples:
// ✅ CORRECT: Wrapped with context
if err != nil {
return fmt.Errorf("failed to load command: %w", err)
}
// ❌ WRONG: No context
if err != nil {
return err
}
// ❌ WRONG: Loses error chain
if err != nil {
return fmt.Errorf("error: %s", err.Error())
}See Architecture Rules - Rule 4 for complete details.
// LoadCommand loads a command resource from a markdown file.
// It validates the file format and parses the YAML frontmatter.
// Returns an error if the file is not a valid command resource.
func LoadCommand(filePath string) (*Resource, error) {
// Implementation
}Rules:
- All exported items must have GoDoc comments
- Start with the item name
- Describe what, not how
- Keep comments up-to-date with code
// ✅ GOOD: Use filepath.Join for cross-platform paths
path := filepath.Join(dir, "commands", "test.md")// ✅ GOOD: Check file existence
if _, err := os.Stat(path); err != nil {
return fmt.Errorf("file does not exist: %w", err)
}// ✅ GOOD: Use defer for cleanup
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()// ✅ GOOD: Set appropriate permissions
os.MkdirAll(dir, 0755) // Directories
os.WriteFile(path, data, 0644) // FilesCRITICAL: Resources can be stored as real files (COPY mode) or symlinks (SYMLINK mode). All code must support both transparently.
entry.IsDir() from os.ReadDir() returns false for symlinks to directories!
// ❌ WRONG: Skips symlinked directories
entries, _ := os.ReadDir(dir)
for _, entry := range entries {
if entry.IsDir() { // Returns false for symlinks!
processDirectory(entry.Name())
}
}// ✅ CORRECT: Follows symlinks
entries, _ := os.ReadDir(dir)
for _, entry := range entries {
path := filepath.Join(dir, entry.Name())
info, err := os.Stat(path) // os.Stat follows symlinks
if err != nil {
continue // Handle broken symlinks gracefully
}
if info.IsDir() {
processDirectory(path) // Works for both real and symlinked dirs
}
}Key Rule: Use os.Stat() to follow symlinks, not entry.IsDir() from os.ReadDir().
Testing Requirement: Every discovery function MUST test both real and symlinked resources.
See Architecture Rules - Rule 5 for complete details.
- ✅ Use
filepath.Join()for paths - ✅ Wrap errors with
fmt.Errorf(..., %w, err) - ✅ Use
os.Stat()for symlink-aware checks - ✅ Group imports in 3 sections
- ✅ Add GoDoc comments to exported items
- ✅ Use descriptive variable names
- ✅ Follow existing patterns in codebase
- ❌ Don't use
entry.IsDir()for discovery - ❌ Don't return raw errors without context
- ❌ Don't use string concatenation for paths
- ❌ Don't panic (except main/init)
- ❌ Don't use
%sor%vfor error wrapping - ❌ Don't skip error checking
- Architecture Guide - System overview and design rules
- Testing Guide - Testing patterns and practices
- CONTRIBUTING.md - Quick start and workflow
For real-world examples, see:
pkg/resource/- Resource loading patternspkg/discovery/- Directory traversal with symlink supportpkg/repo/manager.go- Error wrapping examples