Skip to content

Development Workflow

Guidelines and processes for contributing to Fulcrum.


Guiding Principles

  1. Test-Driven Development: Write tests before implementing functionality
  2. High Code Coverage: Aim for >75% coverage for all modules
  3. User Experience First: Every decision should prioritize user experience
  4. CI-Aware: Use non-interactive commands; use CI=true for watch-mode tools
  5. Conventional Commits: Follow the commit message format strictly
  6. No Secrets in Code: Never commit credentials, API keys, or tokens

Development Setup

Prerequisites

# Go 1.24+
go version

# Node.js 20+
node --version

# Docker (for local infrastructure)
docker --version

# Protocol Buffers (for gRPC)
buf --version

Quick Start

# Clone the repository
git clone https://github.com/fulcrum-io/fulcrum.git
cd fulcrum

# Start local infrastructure
./scripts/start-stack.sh

# Run backend tests
go test ./... -short

# Run dashboard dev server
cd dashboard && npm run dev

Task Workflow

1. Create a Branch

# Feature branch
git checkout -b feat/policy-templates

# Bug fix branch
git checkout -b fix/envelope-timeout

2. Write Failing Tests (Red Phase)

Before implementing, write tests that define expected behavior:

func TestPolicyTemplate_Apply(t *testing.T) {
    template := NewPolicyTemplate("deny-bash")
    policy, err := template.Apply("tenant-123")

    require.NoError(t, err)
    assert.Equal(t, "deny-bash", policy.Name)
    assert.Equal(t, "tenant-123", policy.TenantID)
}

Run tests to confirm they fail:

go test ./internal/policyengine/... -v

3. Implement to Pass Tests (Green Phase)

Write the minimum code to make tests pass:

func (t *PolicyTemplate) Apply(tenantID string) (*Policy, error) {
    return &Policy{
        Name:     t.Name,
        TenantID: tenantID,
    }, nil
}

4. Refactor

With passing tests, improve the code: - Remove duplication - Improve naming - Optimize performance - Add documentation

5. Verify Coverage

# Go coverage
go test ./... -cover -coverprofile=coverage.out
go tool cover -html=coverage.out

# Dashboard coverage
cd dashboard && npm run test:coverage

Target: >75% coverage for new code.


Quality Gates

Before marking any task complete, verify:

  • [ ] All tests pass (go test ./...)
  • [ ] Code coverage meets requirements (>75%)
  • [ ] Code follows style guides (gofmt, eslint)
  • [ ] All public functions are documented
  • [ ] Type safety enforced (Go types, TypeScript types)
  • [ ] No linting errors (golangci-lint run)
  • [ ] No security vulnerabilities
  • [ ] Documentation updated if needed

Commit Guidelines

Message Format

<type>(<scope>): <description>

[optional body]

[optional footer]

Types

Type Description
feat New feature
fix Bug fix
docs Documentation only
style Formatting, no code change
refactor Code change without fix or feature
test Adding missing tests
chore Maintenance tasks

Examples

git commit -m "feat(policy): Add template-based policy creation"
git commit -m "fix(envelope): Correct timeout calculation for long-running tasks"
git commit -m "test(brain): Add unit tests for semantic judge"
git commit -m "docs(api): Update policy endpoint documentation"

Rules

  • NEVER add "Co-Authored-By" lines (handled automatically)
  • No secrets in commits (pre-commit hook will catch these)
  • Keep subject line under 72 characters
  • Use imperative mood ("Add feature" not "Added feature")

Testing Requirements

Unit Testing

func TestEvaluator_EvaluatePolicies(t *testing.T) {
    tests := []struct {
        name    string
        input   *EvalRequest
        want    Decision
        wantErr bool
    }{
        {
            name:  "allows safe request",
            input: &EvalRequest{Tool: "read_file"},
            want:  DecisionAllow,
        },
        {
            name:  "denies bash",
            input: &EvalRequest{Tool: "bash"},
            want:  DecisionDeny,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := evaluator.Evaluate(ctx, tt.input)
            if (err != nil) != tt.wantErr {
                t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got.Decision != tt.want {
                t.Errorf("decision = %v, want %v", got.Decision, tt.want)
            }
        })
    }
}

Integration Testing

  • Use testutil.NewTestDB(t) for database tests
  • Use testcontainers for Redis/NATS
  • Test complete user flows
  • Verify database transactions

Running Tests

# Unit tests only
go test ./... -short

# All tests including integration
go test ./...

# Specific package
go test ./internal/policyengine/... -v

# With race detection
go test ./... -race

# With coverage
go test ./... -cover -coverprofile=coverage.out

Code Review Process

Self-Review Checklist

Before requesting review:

Functionality - [ ] Feature works as specified - [ ] Edge cases handled - [ ] Error messages are user-friendly

Code Quality - [ ] Follows style guide - [ ] DRY principle applied - [ ] Clear variable/function names - [ ] Appropriate comments

Testing - [ ] Unit tests comprehensive - [ ] Integration tests pass - [ ] Coverage adequate (>75%)

Security - [ ] No secrets committed - [ ] Input validation present - [ ] SQL injection prevented

Performance - [ ] Database queries optimized - [ ] No N+1 queries - [ ] Appropriate caching


Definition of Done

A task is complete when:

  1. All code implemented to specification
  2. Unit tests written and passing
  3. Code coverage meets requirements
  4. Code passes linting and static analysis
  5. Documentation complete (if applicable)
  6. Changes committed with proper message
  7. PR approved and merged

Emergency Procedures

Critical Bug in Production

  1. Create hotfix branch from main
  2. Write failing test for bug
  3. Implement minimal fix
  4. Test thoroughly
  5. Deploy immediately
  6. Document incident

Security Incident

  1. Rotate all secrets immediately
  2. Review access logs
  3. Patch vulnerability
  4. Notify affected parties
  5. Document and update procedures

Development Commands Reference

Backend (Go)

# Run all tests
go test ./... -short

# Run with coverage
go test ./... -cover

# Run specific package
go test ./internal/policyengine/...

# Format code
gofmt -w .

# Lint
golangci-lint run

# Generate protobuf
buf generate

Dashboard (TypeScript)

# Development server
npm run dev

# Build
npm run build

# Tests
npm test

# Lint
npm run lint

# Type check
npm run typecheck

Infrastructure

# Start all services
./scripts/start-stack.sh

# Stop all services
./scripts/stop-stack.sh

# View logs
docker compose logs -f

See Also


Document Version: 1.0 Last Updated: January 20, 2026