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:
- Environment Isolation: Ticket-based development prevents conflicts
- Build Performance: Parallel execution and intelligent caching
- Testing Coverage: Multi-level testing from unit to full system
- Developer Experience: Hot reloading, structured logging, and debugging tools
- Release Management: Automated versioning and cross-platform distribution
The workflow patterns demonstrate production-ready engineering practices suitable for both individual development and team collaboration.