| name | cross-platform-development |
| description | Essential patterns for cross-platform compatibility including path handling and testing practices |
| argument-hint | |
Cross-Platform Development
⚠️ CRITICAL: All path operations and tests MUST be cross-platform compatible (Linux, macOS, Windows).
Overview
This skill covers essential patterns for writing code that works correctly across all supported platforms. Tests that pass on Linux/macOS may fail on Windows CI if they don't follow these patterns.
Core Rules
- Host paths: Always use
filepath.Join() for path construction (never hardcode "/" or "\")
- Container paths: Always use
path.Join() for paths inside containers (containers are always Unix/Linux)
- Convert relative paths to absolute with
filepath.Abs()
- Never hardcode paths with
~ - use os.UserHomeDir() instead
- In tests, use
filepath.Join() for all path assertions
- Use
t.TempDir() for ALL temporary directories in tests - never hardcode paths
Host Paths vs Container Paths
Host Paths (Use filepath.Join)
Host paths run on the actual operating system (Windows, macOS, or Linux) and must use OS-specific separators:
import "path/filepath"
configDir := filepath.Join(storageDir, ".kaiden")
runtimeDir := filepath.Join(storageDir, "runtimes", "podman")
Container Paths (Use path.Join)
Container paths are always Unix/Linux regardless of the host OS. Podman containers run Linux, so paths inside containers must use forward slashes:
import "path"
containerPath := path.Join("/home", "agent", ".config")
mountPath := path.Join("/workspace", "sources", "pkg")
containerPath := filepath.Join("/home", "agent", ".config")
Example from Podman runtime:
import (
"path"
"path/filepath"
)
hostConfigDir := filepath.Join(storageDir, "runtimes", "podman", "config")
containerWorkspace := path.Join("/workspace", "sources")
containerHome := path.Join("/home", "agent")
Common Test Failures on Windows
Tests often fail on Windows due to hardcoded Unix-style paths. These paths get normalized differently by filepath.Abs() on Windows vs Unix systems.
❌ NEVER Do This in Tests
instance, err := instances.NewInstance(instances.NewInstanceParams{
SourceDir: "/path/to/source",
ConfigDir: "/path/to/config",
})
invalidPath := "/this/path/does/not/exist"
path := dir + "/subdir"
✅ ALWAYS Do This in Tests
sourceDir := t.TempDir()
configDir := t.TempDir()
instance, err := instances.NewInstance(instances.NewInstanceParams{
SourceDir: sourceDir,
ConfigDir: configDir,
})
tempDir := t.TempDir()
notADir := filepath.Join(tempDir, "file")
os.WriteFile(notADir, []byte("test"), 0644)
invalidPath := filepath.Join(notADir, "subdir")
path := filepath.Join(dir, "subdir")
chmod-Based Permission Tests
os.Chmod maps to SetFileAttributes on Windows, which only sets the read-only file
attribute — it does not restrict file creation inside a directory the way Unix ACLs do.
Tests that make a directory read-only to trigger a WriteFile error will pass on Linux/macOS
but silently succeed on Windows (no error returned, test fails with "expected error, got nil").
Guard these tests with a runtime skip:
if runtime.GOOS == "windows" {
t.Skip("chmod-based permission tests do not apply on Windows")
}
if os.Getuid() == 0 {
t.Skip("chmod restrictions do not apply to root")
}
The os.Getuid() == 0 guard is also required: root bypasses Unix permission checks, so the
same test would fail if run as root inside a container. Both guards require "runtime" and
"os" imports.
Pattern summary:
func TestWriteSomething_WriteFileFails(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
t.Skip("chmod-based permission tests do not apply on Windows")
}
if os.Getuid() == 0 {
t.Skip("chmod restrictions do not apply to root")
}
dir := t.TempDir()
if err := os.Chmod(dir, 0500); err != nil {
t.Fatalf("setup chmod: %v", err)
}
t.Cleanup(func() { _ = os.Chmod(dir, 0700) })
}
Production Code Patterns
Host Path Construction
import "path/filepath"
configDir := filepath.Join(sourceDir, ".kaiden")
absPath, err := filepath.Abs(relativePath)
configDir := sourceDir + "/.kaiden"
Container Path Construction
import "path"
workspacePath := path.Join("/workspace", "sources")
agentHome := path.Join("/home", "agent", ".config")
workspacePath := filepath.Join("/workspace", "sources")
User Home Directory
import "path/filepath"
homeDir, err := os.UserHomeDir()
defaultPath := filepath.Join(homeDir, ".kdn")
defaultPath := "~/.kdn"
Test Assertions
import "path/filepath"
expectedPath := filepath.Join(".", "relative", "path")
if result != expectedPath {
t.Errorf("Expected %s, got %s", expectedPath, result)
}
expectedPath := "./relative/path"
Temporary Directories in Tests
tempDir := t.TempDir()
sourcesDir := t.TempDir()
tempDir := "/tmp/test"
Path Handling Best Practices
Converting Relative to Absolute
Always convert relative paths to absolute in command preRun:
import "path/filepath"
func (c *myCmd) preRun(cmd *cobra.Command, args []string) error {
relativePath := args[0]
absPath, err := filepath.Abs(relativePath)
if err != nil {
return fmt.Errorf("failed to resolve path: %w", err)
}
c.path = absPath
return nil
}
Building Nested Paths
Host Paths
import "path/filepath"
baseDir := filepath.Join(storageDir, "runtimes")
runtimeDir := filepath.Join(baseDir, "podman")
configDir := filepath.Join(runtimeDir, "config")
configPath := filepath.Join(storageDir, "runtimes", "podman", "config", "image.json")
configPath := storageDir + "/runtimes/podman/config/image.json"
Container Paths
import "path"
workspaceSources := path.Join("/workspace", "sources", "pkg", "cmd")
agentConfig := path.Join("/home", "agent", ".config", "claude")
workspaceSources := filepath.Join("/workspace", "sources", "pkg", "cmd")
Checking Path Existence
import "path/filepath"
configPath := filepath.Join(configDir, "workspace.json")
if _, err := os.Stat(configPath); err != nil {
if os.IsNotExist(err) {
}
}
Testing Patterns
Creating Test Directories
import "path/filepath"
func TestMyFunction(t *testing.T) {
t.Parallel()
storageDir := t.TempDir()
sourcesDir := t.TempDir()
configDir := t.TempDir()
result, err := MyFunction(sourcesDir, configDir)
}
Testing with Invalid Paths
import "path/filepath"
func TestMyFunction_InvalidPath(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
notADir := filepath.Join(tempDir, "file")
os.WriteFile(notADir, []byte("test"), 0644)
invalidPath := filepath.Join(notADir, "subdir")
_, err := MyFunction(invalidPath)
if err == nil {
t.Fatal("Expected error for invalid path")
}
}
Path Assertions in Tests
import "path/filepath"
func TestMyFunction_ReturnsPath(t *testing.T) {
t.Parallel()
sourceDir := t.TempDir()
result, err := MyFunction(sourceDir)
if err != nil {
t.Fatalf("MyFunction() failed: %v", err)
}
expectedPath := filepath.Join(sourceDir, ".kaiden")
if result != expectedPath {
t.Errorf("Expected %s, got %s", expectedPath, result)
}
}
Common Pitfalls
Hardcoded Separators
path := basedir + "/config/file.json"
path := basedir + "\\config\\file.json"
path := filepath.Join(basedir, "config", "file.json")
containerPath := path.Join("/home", "agent", "config", "file.json")
Home Directory Expansion
import "path/filepath"
defaultPath := "~/.kdn"
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
defaultPath := filepath.Join(homeDir, ".kdn")
Absolute Path Detection
import "path/filepath"
if filepath.IsAbs(path) {
}
if strings.HasPrefix(path, "/") {
}
Path Cleaning
import "path/filepath"
cleanPath := filepath.Clean(userPath)
Summary: When to Use Which Package
| Context | Package | Example |
|---|
| Host paths (files on disk) | path/filepath | filepath.Join(storageDir, "config") |
| Container paths (inside Podman) | path | path.Join("/workspace", "sources") |
| URL paths | path | path.Join("/api", "v1", "users") |
Environment Variables
Handle environment variables in a cross-platform way:
homeDir := os.Getenv("HOME")
if homeDir == "" {
homeDir = os.Getenv("USERPROFILE")
}
homeDir, err := os.UserHomeDir()
File Permissions
Be aware of cross-platform permission differences:
err := os.MkdirAll(dirPath, 0755)
err := os.WriteFile(filePath, data, 0644)
Why This Matters
Tests that pass on Linux/macOS may fail on Windows CI if they use hardcoded Unix paths.
Always use:
t.TempDir() for temporary directories in tests
filepath.Join() for host paths (files on disk)
path.Join() for container paths (inside Podman containers, which are always Linux)
Related Skills
/testing-commands - Command testing patterns
/testing-best-practices - General testing best practices
/working-with-podman-runtime-config - Podman runtime configuration
References
- Go filepath package: Standard library documentation for host paths
- Go path package: Standard library documentation for Unix paths (containers, URLs)
- Cross-platform tests: All
*_test.go files should follow these patterns
- Example:
pkg/instances/manager_test.go, pkg/cmd/init_test.go
- Podman runtime:
pkg/runtime/podman/ for container path examples