4.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
dappco.re/go/core/process is the process management framework for CoreGO. It handles process execution (spawn, monitor, stream, kill), daemon lifecycle (PID files, health checks, graceful shutdown, registry), and pipeline orchestration (parallel, sequential, or DAG-ordered multi-process runs). All process events broadcast via Core IPC actions.
Commands
core go test # Run all tests
core go test --run Name # Single test
core go fmt # Format
core go lint # Lint
core go vet # Vet
Architecture
The package has three layers, all in the root process package (plus a exec subpackage):
Layer 1: Process Execution (service.go, process.go)
Service is a Core service (*core.ServiceRuntime[Options]) that manages all Process instances. It spawns subprocesses, pipes stdout/stderr through goroutines, captures output to a RingBuffer, and broadcasts IPC actions (ActionProcessStarted, ActionProcessOutput, ActionProcessExited, ActionProcessKilled — defined in actions.go).
The legacy global singleton API (process_global.go) was removed in favor of
explicit Core service registration.
Layer 2: Daemon Lifecycle (daemon.go, pidfile.go, health.go, registry.go)
Daemon composes three independent components into a lifecycle manager:
- PIDFile (pidfile.go) — single-instance enforcement via PID file with stale-PID detection (
Signal(0)) - HealthServer (health.go) — HTTP
/health(liveness) and/ready(readiness) endpoints - Registry (registry.go) — tracks running daemons as JSON files in
~/.core/daemons/, auto-prunes dead PIDs onList()
Daemon.Start() acquires PID → starts health → registers. Daemon.Run(ctx) blocks until context cancellation, then Stop() reverses in order (unregister → release PID → stop health).
Layer 3: Pipeline Orchestration (runner.go)
Runner executes multiple RunSpecs with three modes: RunAll (DAG-aware, parallelizes independent specs in waves), RunSequential (stops on first failure), RunParallel (ignores dependencies). Dependency failures cascade via Skipped status unless AllowFailure is set.
exec Subpackage (exec/)
Builder-pattern wrapper around os/exec with structured logging via a pluggable Logger interface. NopLogger is the default. Separate from the main package — no Core/IPC integration, just a convenience wrapper.
Key Patterns
- Core integration:
Serviceembeds*core.ServiceRuntime[Options]and usess.Core().ACTION(...)to broadcast typed action messages. Tests create a Core instance viaframework.New(framework.WithService(Register)). - Output capture: All process output goes through a fixed-size
RingBuffer(default 1MB). Oldest data is silently overwritten when full. SetRunOptions.DisableCaptureto skip buffering for long-running processes where output is only streamed via IPC. - Process lifecycle: Status transitions are
StatusPending → StatusRunning → StatusExited|StatusFailed|StatusKilled. Thedonechannel closes on exit; use<-proc.Done()orproc.Wait(). - Detach / process group isolation: Set
RunOptions.Detach = trueto run the subprocess in its own process group (Setpgid). Detached processes usecontext.Background()so they survive parent context cancellation and parent death. - Graceful shutdown:
Service.OnShutdownkills all running processes.Daemon.Stop()performs ordered teardown: sets health to not-ready → shuts down health server → releases PID file → unregisters from registry.DaemonOptions.ShutdownTimeout(default 30 s) bounds the shutdown context. - Auto-registration: Pass a
RegistryandRegistryEntryinDaemonOptionsto automatically register the daemon onStart()and unregister onStop(). - PID liveness checks: Both
PIDFileandRegistryuseproc.Signal(syscall.Signal(0))to check if a PID is alive before trusting stored state. - Error handling: All errors MUST use
core.E(), neverfmt.Errorforerrors.New. Sentinel errors are package-level vars created withcore.E("", "message", nil).
Dependencies
dappco.re/go/core— Core DI framework, IPC actions,ServiceRuntimedappco.re/go/core/io— Filesystem abstraction (coreio.Local) used by PIDFile and Registrygithub.com/stretchr/testify— test assertions (require/assert)
Testing
- Tests spawn real subprocesses (typically
echo,sleep,cat). Use short timeouts to avoid hanging. - Test helper
newTestService(t)in service_test.go creates a Core instance with a small buffer (1024 bytes) and returns both theServiceandCore. - Uses
github.com/stretchr/testifywithrequirefor setup andassertfor checks.