Skip to main content

HumanLayer Repository - Development Workflow Patterns

Analysis Date: 2025-10-14
Focus: Development processes, build systems, and operational patterns

Development Environment Architecture​

Ticket-Based Development Isolation​

Environment Isolation Strategy (/home/halcasteel/humanlayer/Makefile:89-156):

# Ticket-specific development environments
daemon-ticket: daemon-dev-build
@if [ -z "$(TICKET)" ]; then \
echo "Error: TICKET parameter required (e.g., ENG-1234)"; \
exit 1; \
fi && \
source hack/port-utils.sh && \
ticket_num=$$(extract_ticket_number "$(TICKET)") && \
port=$$(find_available_port "$$ticket_num") && \
echo "Starting daemon for ticket $(TICKET) on port $$port" && \
HUMANLAYER_DATABASE_PATH=~/.humanlayer/daemon-$(TICKET).db \
HUMANLAYER_DAEMON_SOCKET=~/.humanlayer/daemon-$$port.sock \
HUMANLAYER_DAEMON_HTTP_PORT=$$port \
./hld/hld-dev

wui-ticket: wui-dev-build
@if [ -z "$(TICKET)" ]; then \
echo "Error: TICKET parameter required"; \
exit 1; \
fi && \
source hack/port-utils.sh && \
ticket_num=$$(extract_ticket_number "$(TICKET)") && \
daemon_port=$$(find_available_port "$$ticket_num") && \
wui_port=$$((daemon_port + 1000)) && \
HUMANLAYER_DAEMON_HTTP_PORT=$$daemon_port \
HUMANLAYER_WUI_PORT=$$wui_port \
npm run --prefix humanlayer-wui dev

Isolation Benefits:

  • Parallel Development: Multiple developers work on different tickets simultaneously
  • Data Isolation: Each ticket gets its own database file and socket
  • Port Management: Automatic port allocation prevents conflicts
  • State Preservation: Ticket-specific state persists across development sessions

Port Allocation Logic (/home/halcasteel/humanlayer/hack/port-utils.sh:15-45):

extract_ticket_number() {
local ticket="$1"
# Extract numeric part from ticket (ENG-1234 -> 1234)
echo "$ticket" | sed 's/[^0-9]*\([0-9]\+\).*/\1/'
}

find_available_port() {
local base_number="$1"
# Start from 8000 + ticket number
local base_port=$((8000 + base_number))
local port=$base_port

# Find next available port
while lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; do
port=$((port + 1))
# Avoid common service ports
if [ $port -eq 8080 ] || [ $port -eq 9000 ]; then
port=$((port + 1))
fi
done

echo $port
}

Build System Architecture​

Hierarchical Build Strategy​

Root Makefile Orchestration (/home/halcasteel/humanlayer/Makefile:1-45):

# Color-coded output for better developer experience
GREEN := \033[32m
YELLOW := \033[33m
RESET := \033[0m

# Parallel build execution
check: check-header
@$(MAKE) -j4 check-hlyr check-wui check-hld check-claudecode-go

test: test-header
@$(MAKE) -j4 test-hlyr test-wui test-hld test-claudecode-go

# Component-specific build delegation
check-hld:
@echo "$(GREEN)[hld]$(RESET) Running checks..."
@cd hld && $(MAKE) check

build-hld:
@echo "$(GREEN)[hld]$(RESET) Building daemon..."
@cd hld && $(MAKE) build

# Build status aggregation
check-status:
@echo "Build Status Summary:"
@for component in hlyr hld wui claudecode-go; do \
if $(MAKE) -C $$component check >/dev/null 2>&1; then \
echo " ✓ $$component: PASS"; \
else \
echo " ✗ $$component: FAIL"; \
fi \
done

Component-Level Build Systems​

Go Module Build Pattern (/home/halcasteel/humanlayer/hld/Makefile:15-67):

# Go build with version injection
BUILD_version ?= $(shell git describe --tags --always --dirty)
LDFLAGS := -X github.com/humanlayer/humanlayer/hld/internal/version.BuildVersion=$(BUILD_version)

# Development vs production builds
build-dev:
go build -ldflags "$(LDFLAGS)" -o hld-dev ./cmd/hld

build-prod:
go build -ldflags "$(LDFLAGS) -s -w" -o hld ./cmd/hld

# Cross-platform builds
build-darwin-arm64:
GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS) -s -w" -o hld-darwin-arm64 ./cmd/hld

# Test execution with coverage
test:
go test -v -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

# Integration tests (requiring build tag)
test-integration:
go test -v -race -tags=integration ./...

# Static analysis
check:
go vet ./...
golangci-lint run
go mod tidy && git diff --exit-code go.mod go.sum

TypeScript Build Pattern (/home/halcasteel/humanlayer/hlyr/Makefile:8-34):

# Package manager detection
PACKAGE_MANAGER := $(shell which bun >/dev/null 2>&1 && echo bun || echo npm)

# Development setup
dev-setup:
$(PACKAGE_MANAGER) install
$(PACKAGE_MANAGER) run build

# TypeScript compilation with source maps
build:
$(PACKAGE_MANAGER) run build
@echo "✓ TypeScript compilation complete"

# Testing with Vitest
test:
$(PACKAGE_MANAGER) run test

# End-to-end testing
test-e2e:
$(PACKAGE_MANAGER) run test:e2e

# Linting and formatting
check:
$(PACKAGE_MANAGER) run typecheck
$(PACKAGE_MANAGER) run lint
$(PACKAGE_MANAGER) run format:check

Turbo-Powered Monorepo Management​

Turborepo Configuration (/home/halcasteel/humanlayer/turbo.json:4-29):

{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "build/**", ".next/**", "out/**"],
"env": ["NODE_ENV", "BUILD_version"]
},
"test": {
"dependsOn": ["build"],
"inputs": ["src/**", "test/**", "__tests__/**"],
"outputs": ["coverage/**"]
},
"check": {
"dependsOn": ["^build"],
"cache": false
},
"dev": {
"cache": false,
"persistent": true
}
},
"globalDependencies": [
"package.json",
"turbo.json",
"tsconfig.json",
".env*"
],
"remoteCache": {
"signature": true
}
}

Caching Strategy Benefits:

  • Incremental Builds: Only rebuild changed components
  • Dependency Tracking: Automatic invalidation on upstream changes
  • Parallel Execution: CPU utilization across all cores
  • Remote Caching: Share artifacts across team (when configured)

Testing Infrastructure​

Multi-Level Testing Strategy​

Unit Testing (Component Level):

// React component testing with Testing Library
// From /home/halcasteel/humanlayer/humanlayer-wui/src/components/ui/button.test.tsx
describe('Button Component', () => {
test('should render with correct variant classes', () => {
render(<Button variant="destructive">Delete</Button>)
const button = screen.getByRole('button')
expect(button).toHaveClass('bg-background', 'text-destructive')
})

test('should handle click events', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click me</Button>)

fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})

Integration Testing (Service Level):

// Go daemon integration tests
// From /home/halcasteel/humanlayer/hld/daemon/daemon_integration_test.go
func TestDaemonApprovalWorkflow(t *testing.T) {
// Setup isolated test environment
socketPath := testutil.SocketPath(t, "approval_workflow")

d := setupTestDaemon(t, socketPath)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Start daemon in background
go func() {
if err := d.Run(ctx); err != nil && !errors.Is(err, context.Canceled) {
t.Errorf("daemon run failed: %v", err)
}
}()

// Wait for daemon startup
waitForSocket(t, socketPath, 5*time.Second)

// Test complete approval workflow
client := testClient(t, socketPath)

// Create approval
approval, err := client.CreateApproval(rpc.CreateApprovalRequest{
SessionID: "test-session",
ToolName: "edit_file",
ToolInput: map[string]interface{}{"file": "test.go"},
})
require.NoError(t, err)
assert.Equal(t, "pending", approval.Status)

// Send decision
result, err := client.SendDecision(rpc.SendDecisionRequest{
ApprovalID: approval.ID,
Decision: "approve",
})
require.NoError(t, err)
assert.Equal(t, "approved", result.Status)
}

End-to-End Testing (Full System):

// E2E testing with real daemon communication
// From /home/halcasteel/humanlayer/hld/e2e/test-rest-api.ts
describe('Full Workflow E2E Tests', () => {
let daemonProcess: ChildProcess
let daemonClient: DaemonClient

beforeAll(async () => {
// Start real daemon process
const testSocketPath = path.join(os.tmpdir(), 'test-daemon.sock')
daemonProcess = spawn('hld-dev', ['--socket-path', testSocketPath])

// Wait for daemon to be ready
await waitForSocket(testSocketPath, 10000)

daemonClient = new DaemonClient(testSocketPath)
})

afterAll(async () => {
daemonProcess.kill('SIGTERM')
await new Promise(resolve => daemonProcess.on('exit', resolve))
})

test('complete Claude Code integration workflow', async () => {
// Create session
const session = await daemonClient.createSession({
command: 'claude-code --model sonnet-4',
directory: '/tmp/test-project'
})

// Simulate MCP tool call
const approval = await daemonClient.createApproval({
sessionId: session.id,
toolName: 'edit_file',
toolInput: { file: 'src/main.rs', content: 'fn main() {}' }
})

// Auto-approve for testing
await daemonClient.sendDecision({
approvalId: approval.id,
decision: 'approve'
})

// Verify session state
const updatedSession = await daemonClient.getSession(session.id)
expect(updatedSession.status).toBe('running')
})
})

Development Experience Enhancements​

Live Reloading and Hot Module Replacement​

Frontend Development Server (/home/halcasteel/humanlayer/humanlayer-wui/vite.config.ts):

export default defineConfig({
server: {
port: 1420,
host: '0.0.0.0',
strictPort: true,
hmr: {
port: 1421,
host: 'localhost',
},
// Proxy API calls to local daemon during development
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false,
},
},
},
// Hot reload for Tauri development
clearScreen: false,
envPrefix: ['VITE_', 'TAURI_'],
})

Backend Auto-Restart (/home/halcasteel/humanlayer/hld/run-with-logging.sh):

#!/bin/bash

# Development wrapper with auto-restart and logging
LOG_FILE="hld-dev.log"
PID_FILE="hld-dev.pid"

# Kill existing process
if [ -f "$PID_FILE" ]; then
kill $(cat "$PID_FILE") 2>/dev/null
rm "$PID_FILE"
fi

# Start daemon with logging
./hld-dev "$@" 2>&1 | tee "$LOG_FILE" &
echo $! > "$PID_FILE"

# Watch for file changes and restart
fswatch -o . -e ".*\\.go$" | while read num; do
echo "File changes detected, rebuilding..."
make build-dev

# Restart daemon
kill $(cat "$PID_FILE") 2>/dev/null
./hld-dev "$@" 2>&1 | tee -a "$LOG_FILE" &
echo $! > "$PID_FILE"
done

Development Debugging Tools​

Structured Logging Configuration:

// Development logging setup from daemon initialization
func setupLogging(config *config.Config) {
logLevel := logrus.InfoLevel
if config.Debug {
logLevel = logrus.DebugLevel
}

logrus.SetLevel(logLevel)
logrus.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
TimestampFormat: "2006-01-02 15:04:05",
ForceColors: true,
})

// Add request ID middleware for tracing
logrus.AddHook(&RequestIDHook{})
}

// Request correlation for debugging
type RequestIDHook struct{}

func (h *RequestIDHook) Levels() []logrus.Level {
return logrus.AllLevels
}

func (h *RequestIDHook) Fire(entry *logrus.Entry) error {
if requestID := entry.Context.Value("request_id"); requestID != nil {
entry.Data["request_id"] = requestID
}
return nil
}

Build Artifact Management​

Version Management and Release Builds​

Version Injection Strategy:

# Git-based version tagging
BUILD_version ?= $(shell git describe --tags --always --dirty)
BUILD_TIME := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
GIT_COMMIT := $(shell git rev-parse HEAD)

# Inject build information into binaries
LDFLAGS := -X github.com/humanlayer/humanlayer/hld/internal/version.BuildVersion=$(BUILD_version) \
-X github.com/humanlayer/humanlayer/hld/internal/version.BuildTime=$(BUILD_TIME) \
-X github.com/humanlayer/humanlayer/hld/internal/version.GitCommit=$(GIT_COMMIT)

# Nightly build with special configuration
daemon-nightly-build:
cd hld && go build -ldflags "$(LDFLAGS) \
-X github.com/humanlayer/humanlayer/hld/config.DefaultCLICommand=humanlayer-nightly" \
-o hld-nightly ./cmd/hld

Cross-Platform Binary Distribution​

Multi-Platform Build Matrix:

# Platform-specific daemon builds
PLATFORMS := darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64

define build-platform
$(eval OS := $(word 1,$(subst /, ,$1)))
$(eval ARCH := $(word 2,$(subst /, ,$1)))
daemon-$(OS)-$(ARCH):
cd hld && GOOS=$(OS) GOARCH=$(ARCH) go build \
-ldflags "$(LDFLAGS) -s -w" \
-o hld-$(OS)-$(ARCH)$(if $(filter windows,$(OS)),.exe,) \
./cmd/hld
endef

$(foreach platform,$(PLATFORMS),$(eval $(call build-platform,$(platform))))

# Bundle all platforms for release
bundle-all-platforms: $(addprefix daemon-,$(subst /,-,$(PLATFORMS)))
mkdir -p dist/
cp hld/hld-* dist/

Tauri Application Bundling​

Application Bundle Configuration (/home/halcasteel/humanlayer/humanlayer-wui/src-tauri/tauri.conf.json):

{
"bundle": {
"active": true,
"targets": ["dmg", "deb", "nsis"],
"identifier": "dev.humanlayer.codelayer",
"publisher": "HumanLayer",
"icon": ["icons/icon.ico", "icons/icon.png"],
"resources": [
"../hld/hld-darwin-arm64",
"../hld/hld-linux-amd64",
"../hld/hld-windows-amd64.exe"
],
"category": "DeveloperTool",
"shortDescription": "AI Coding Assistant Orchestration"
},
"tauri": {
"allowlist": {
"fs": {
"all": false,
"readFile": true,
"writeFile": true,
"createDir": true,
"scope": ["$APPLOCALDATA", "$HOME/.humanlayer/**"]
},
"process": {
"all": false,
"command": {
"all": false,
"scope": {
"allow": [
{
"name": "hld",
"cmd": "",
"args": true
}
]
}
}
}
}
}
}

Continuous Integration Patterns​

GitHub Actions Workflow Structure​

Matrix Build Strategy (inferred from Makefile patterns):

# .github/workflows/ci.yml (reconstructed from build patterns)
name: CI
on: [push, pull_request]

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go-version: [1.24.0]
node-version: [20.x]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: make setup

- name: Run checks
run: make check

- name: Run tests
run: make test

- name: Build artifacts
run: make build-all

release:
needs: test
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest

steps:
- name: Build release artifacts
run: make bundle-all-platforms

- name: Create GitHub release
uses: softprops/action-gh-release@v1
with:
files: dist/*

Performance Optimization in Development​

Build Performance Monitoring​

Build Time Tracking:

# Build timing and optimization
SHELL := /bin/bash
TIMER_START = $(shell date +%s)

timed-build:
@start_time=$$(date +%s); \
$(MAKE) build; \
end_time=$$(date +%s); \
echo "Build completed in $$((end_time - start_time)) seconds"

# Parallel builds for faster development
quick-check:
@echo "Running parallel checks..."
@$(MAKE) -j$(shell nproc) check-hlyr check-hld check-wui check-claudecode-go

# Cache status reporting
cache-status:
@echo "Turborepo cache status:"
@npx turbo run build --dry-run=json | jq '.tasks[] | {task: .task, cache: .cache}'

This development workflow analysis reveals a sophisticated, developer-friendly environment with strong emphasis on:

  1. Environment Isolation: Ticket-based development prevents conflicts
  2. Build Performance: Parallel execution and intelligent caching
  3. Testing Coverage: Multi-level testing from unit to full system
  4. Developer Experience: Hot reloading, structured logging, and debugging tools
  5. Release Management: Automated versioning and cross-platform distribution

The workflow patterns demonstrate production-ready engineering practices suitable for both individual development and team collaboration.