> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudshipai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Code Style

> Coding conventions and best practices for Station

# Code Style Guide

Station follows standard Go conventions with some project-specific guidelines.

## Go Style

### Official Guidelines

Station follows the official Go style guides:

* [Effective Go](https://go.dev/doc/effective_go)
* [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
* [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md) (supplementary)

### Formatting

```bash theme={null}
# Format code (required before commit)
make fmt

# Or directly
gofmt -s -w .
goimports -w .
```

### Linting

```bash theme={null}
# Run linter
make lint

# Or directly
golangci-lint run
```

The project uses `golangci-lint` with these enabled linters:

* `gofmt` - Format checking
* `goimports` - Import organization
* `govet` - Go vet checks
* `errcheck` - Error handling
* `staticcheck` - Static analysis
* `gosimple` - Simplifications
* `ineffassign` - Unused assignments

## Naming Conventions

### Packages

```go theme={null}
// Good - short, lowercase, no underscores
package services
package repositories
package config

// Bad
package agent_services
package AgentRepository
```

### Functions and Methods

```go theme={null}
// Good - verb-noun, exported if needed
func (s *AgentService) Execute(ctx context.Context, id int64) error
func (r *AgentRepository) GetByID(ctx context.Context, id int64) (*Agent, error)
func parseConfig(path string) (*Config, error)

// Bad
func (s *AgentService) DoExecute(ctx context.Context, id int64) error  // redundant "Do"
func (r *AgentRepository) agent_by_id(id int64) *Agent                  // snake_case
```

### Interfaces

```go theme={null}
// Good - suffix with -er for single method, or describe behavior
type AgentExecutor interface {
    Execute(ctx context.Context, task string) (string, error)
}

type AgentService interface {
    Execute(ctx context.Context, id int64, task string) (string, error)
    Create(ctx context.Context, agent *Agent) (int64, error)
    Update(ctx context.Context, agent *Agent) error
    Delete(ctx context.Context, id int64) error
}

// Bad
type IAgentService interface { }  // Don't prefix with I
```

### Variables

```go theme={null}
// Good
var defaultTimeout = 30 * time.Second
agentID := params.Get("id")
mcpServer := config.MCPServers[0]

// Bad
var DefaultTimeout = 30 * time.Second  // unexported should be lowercase
agent_id := params.Get("id")           // snake_case
```

### Constants

```go theme={null}
// Good
const (
    DefaultMaxSteps    = 5
    DefaultAPIPort     = 8585
    StatusPending      = "pending"
    StatusRunning      = "running"
    StatusCompleted    = "completed"
)

// Bad
const DEFAULT_MAX_STEPS = 5  // SCREAMING_SNAKE_CASE
```

## Code Organization

### File Structure

```go theme={null}
// Order of declarations in a file:
// 1. Package comment (if main file)
// 2. Package declaration
// 3. Imports (grouped: stdlib, external, internal)
// 4. Constants
// 5. Variables
// 6. Types (interfaces, then structs)
// 7. Functions (constructors first, then methods)

package services

import (
    "context"
    "database/sql"
    "fmt"
    "time"

    "github.com/google/uuid"
    "go.opentelemetry.io/otel"

    "github.com/cloudshipai/station/internal/config"
    "github.com/cloudshipai/station/internal/db/repositories"
)

const (
    defaultTimeout = 30 * time.Second
)

var (
    ErrNotFound = errors.New("not found")
)

type AgentService interface {
    Execute(ctx context.Context, id int64, task string) error
}

type agentServiceImpl struct {
    repo   repositories.AgentRepository
    config *config.Config
}

func NewAgentService(repo repositories.AgentRepository, cfg *config.Config) AgentService {
    return &agentServiceImpl{repo: repo, config: cfg}
}

func (s *agentServiceImpl) Execute(ctx context.Context, id int64, task string) error {
    // ...
}
```

### Import Grouping

```go theme={null}
import (
    // Standard library
    "context"
    "fmt"
    "time"

    // External dependencies
    "github.com/labstack/echo/v4"
    "go.opentelemetry.io/otel"

    // Internal packages
    "github.com/cloudshipai/station/internal/config"
    "github.com/cloudshipai/station/internal/services"
)
```

## Error Handling

### Error Wrapping

```go theme={null}
// Good - wrap errors with context
func (s *AgentService) Execute(ctx context.Context, id int64, task string) error {
    agent, err := s.repo.GetByID(ctx, id)
    if err != nil {
        return fmt.Errorf("get agent %d: %w", id, err)
    }

    result, err := s.engine.Execute(ctx, agent, task)
    if err != nil {
        return fmt.Errorf("execute agent %s: %w", agent.Name, err)
    }

    return nil
}

// Bad - losing error context
func (s *AgentService) Execute(ctx context.Context, id int64, task string) error {
    agent, err := s.repo.GetByID(ctx, id)
    if err != nil {
        return err  // No context about where/why
    }
    return nil
}
```

### Custom Errors

```go theme={null}
// Define sentinel errors
var (
    ErrAgentNotFound    = errors.New("agent not found")
    ErrInvalidInput     = errors.New("invalid input")
    ErrExecutionTimeout = errors.New("execution timeout")
)

// Use errors.Is for checking
if errors.Is(err, ErrAgentNotFound) {
    return http.StatusNotFound, nil
}
```

### Don't Ignore Errors

```go theme={null}
// Good
result, err := s.Execute(ctx, task)
if err != nil {
    log.Error("execution failed", "error", err)
    return err
}

// Bad
result, _ := s.Execute(ctx, task)  // Ignoring error!
```

## Context Usage

```go theme={null}
// Always pass context as first parameter
func (s *AgentService) Execute(ctx context.Context, id int64, task string) error {
    // Use context for cancellation
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
    }

    // Pass context to downstream calls
    agent, err := s.repo.GetByID(ctx, id)
    if err != nil {
        return err
    }

    return nil
}
```

## Concurrency

### Channel Patterns

```go theme={null}
// Use buffered channels when appropriate
results := make(chan Result, len(tasks))

// Always close channels from producer
go func() {
    defer close(results)
    for _, task := range tasks {
        results <- process(task)
    }
}()

// Consumer reads until closed
for result := range results {
    handleResult(result)
}
```

### Mutex Usage

```go theme={null}
type SafeCounter struct {
    mu    sync.RWMutex
    count int
}

func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Value() int {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.count
}
```

## Documentation

### Package Comments

```go theme={null}
// Package services provides the core business logic for Station.
// It includes agent execution, MCP management, and environment configuration.
package services
```

### Function Comments

```go theme={null}
// Execute runs the specified agent with the given task.
// It returns the agent's response or an error if execution fails.
//
// The execution respects the context deadline and will return
// context.DeadlineExceeded if the timeout is reached.
func (s *AgentService) Execute(ctx context.Context, id int64, task string) (string, error) {
    // ...
}
```

### Inline Comments

```go theme={null}
func (s *AgentService) Execute(ctx context.Context, id int64, task string) error {
    // Validate agent exists before execution
    agent, err := s.repo.GetByID(ctx, id)
    if err != nil {
        return err
    }

    // Initialize MCP connections for tool access
    // This may take a few seconds for large environments
    mcpManager, err := s.initMCP(ctx, agent.Environment)
    if err != nil {
        return fmt.Errorf("init mcp: %w", err)
    }
    defer mcpManager.Close()

    // Execute with timeout from agent config
    timeout := time.Duration(agent.TimeoutSeconds) * time.Second
    ctx, cancel := context.WithTimeout(ctx, timeout)
    defer cancel()

    return s.engine.Execute(ctx, agent, task)
}
```

## Testing Style

See the [Testing Guide](/contributing/testing) for detailed testing conventions.

```go theme={null}
// Use descriptive test names
func TestAgentService_Execute_ReturnsErrorWhenAgentNotFound(t *testing.T) { }
func TestAgentService_Execute_SuccessfulExecution(t *testing.T) { }
func TestAgentService_Execute_TimeoutExceeded(t *testing.T) { }

// Use table-driven tests
func TestValidateInput(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        wantErr bool
    }{
        {"valid input", "hello", false},
        {"empty input", "", true},
    }
    // ...
}
```

## Pre-commit Checklist

Before committing:

1. **Format code**: `make fmt`
2. **Run linter**: `make lint`
3. **Run tests**: `make test`
4. **Check for issues**: `make check` (runs fmt, lint, test)

## Next Steps

<CardGroup cols={2}>
  <Card title="Development Setup" icon="laptop-code" href="/contributing/development">
    Set up your development environment
  </Card>

  <Card title="Testing" icon="flask" href="/contributing/testing">
    Writing and running tests
  </Card>
</CardGroup>
