Skip to content

Contributing to Aether

Active Development

Aether is under rapid, active development. Codebase structure and patterns are evolving. Before starting a large contribution, open an issue to align on approach.

Development Setup

Prerequisites

ToolVersionPurpose
Go1.24+Backend development
Node.js22+Frontend development
Docker & Docker Compose24.0+ / 2.0+Running services
golang-migratelatestDatabase migrations
golangci-lintlatestGo linting

Quick Start

bash
# 1. Clone
git clone https://github.com/amansrivastava/hacky-automation.git
cd hacky-automation

# 2. Start infrastructure (DB, Redis, NATS, LiteLLM)
docker-compose up -d postgres redis nats litellm

# 3. Run backend
cd goway
cp .env.example .env
# Edit .env with your configuration
make run

# 4. Run frontend (separate terminal)
cd web
npm install
npm run dev

See the Installation Guide for detailed setup instructions.

Development Workflow

1. Branch Naming

bash
git checkout -b feature/your-feature-name

Prefixes:

  • feature/ — New features
  • fix/ — Bug fixes
  • docs/ — Documentation only
  • refactor/ — Code restructuring without behavior changes
  • test/ — Test additions or changes

2. Backend Development Commands

bash
cd goway
make run          # Run locally with auto-reload
make build        # Build binary to bin/server
make test         # Run all tests
make test-race    # Run tests with race detector
make fmt          # Format code (gofmt)
make lint         # Run golangci-lint
make migrate-up   # Apply pending migrations
make migrate-down # Rollback last migration

3. Frontend Development Commands

bash
cd web
npm run dev    # Dev server with HMR at :5173
npm run build  # Production build
npm run lint   # ESLint

4. Write Tests (Mandatory)

All code contributions must include tests. This is a hard requirement.

bash
cd goway
make test                          # All tests
go test -v ./...                   # Verbose
go test -cover ./...               # With coverage
go test -race ./...                # Race detector
go test -run TestSpecific ./path/  # Specific test

See the Testing Guide for requirements and patterns.

5. Lint and Format

bash
cd goway && make fmt && make lint
cd web && npm run lint

6. Commit Format

<type>: <subject>

<body (optional)>

Types: feat, fix, docs, refactor, test, chore

7. Pull Request

  • Clear title and description
  • Reference related issues
  • Screenshots for UI changes
  • All tests passing

Code Patterns

Repository Pattern (Go)

All database access goes through interfaces defined in domain/port/repository/:

go
// Interface in domain/port/repository/agent.go
type AgentRepository interface {
    GetByID(ctx context.Context, id string) (*entity.Agent, error)
    List(ctx context.Context) ([]*entity.Agent, error)
    Create(ctx context.Context, agent *entity.Agent) error
    Update(ctx context.Context, agent *entity.Agent) error
    Delete(ctx context.Context, id string) error
}

// Implementation in infrastructure/adapter/postgres/agent.go
type postgresAgentRepository struct {
    db *pgxpool.Pool
}

func (r *postgresAgentRepository) GetByID(ctx context.Context, id string) (*entity.Agent, error) {
    query := `SELECT id, name, role_id, prompt FROM agents WHERE id = $1`
    var a entity.Agent
    err := r.db.QueryRow(ctx, query, id).Scan(&a.ID, &a.Name, &a.RoleID, &a.Prompt)
    if err != nil {
        if errors.Is(err, pgx.ErrNoRows) {
            return nil, ErrNotFound
        }
        return nil, fmt.Errorf("failed to get agent %s: %w", id, err)
    }
    return &a, nil
}

Error Handling

go
// Use sentinel errors for specific cases
var (
    ErrNotFound     = errors.New("not found")
    ErrInvalidInput = errors.New("invalid input")
)

// Wrap errors with context (never swallow)
if err != nil {
    return fmt.Errorf("failed to resolve tasks for trigger %s: %w", trigger, err)
}

Configuration

go
// Use env struct tags (caarlos0/env)
type Config struct {
    DatabaseURL          string `env:"DATABASE_URL,required"`
    NATSUrl              string `env:"NATS_URL,required"`
    RedisURL             string `env:"REDIS_URL,required"`
    LLMBaseURL           string `env:"LLM_BASE_URL,required"`
    AgentGatewayEnabled  bool   `env:"AGENT_GATEWAY_ENABLED" envDefault:"false"`
    AgentGatewayPort     string `env:"AGENT_GATEWAY_PORT" envDefault:"9090"`
}

React/TypeScript (Frontend)

typescript
// Type everything
interface Agent {
  id: string;
  name: string;
  role_id: string;
  prompt: string;
  created_at: string;
}

// Functional components with typed props
export const AgentCard: React.FC<{ agent: Agent; onEdit: (id: string) => void }> = ({
  agent,
  onEdit,
}) => (
  <div>
    <h3>{agent.name}</h3>
    <button onClick={() => onEdit(agent.id)}>Edit</button>
  </div>
);

Pull Request Checklist

  • [ ] All existing tests pass (make test)
  • [ ] New code has tests covering success and error cases
  • [ ] Test coverage ≥80% for new code, ≥90% for critical paths
  • [ ] make fmt and make lint pass
  • [ ] No merge conflicts with main
  • [ ] Documentation updated if behavior changed
  • [ ] Database migration included if schema changed (with .up.sql and .down.sql)

Key Resources

Released under the MIT License.