Imported from eshu-hq/eshu (
go/internal/runtime/AGENTS.md). Install upstream withnpx skills add eshu-hq/eshu --skill runtime. Copyright stays with the author.
AGENTS.md — internal/runtime guidance for LLM assistants
Read first
go/internal/runtime/README.md— pipeline position, exported surface, env vars, and operational notesgo/internal/runtime/config.go—Configshape andLoadConfig; every binary starts herego/internal/runtime/data_stores.go—LoadGraphBackend,OpenPostgres,OpenNeo4jDriver; the two data-store seams every long-running binary wiresgo/internal/runtime/status_server.goandstatus_mux.go— howNewStatusAdminServeris assembled fromNewStatusAdminMuxandNewAdminMuxgo/internal/app/app.go— how binaries compose runtime helpers through the Application and Lifecycle contractsgo/cmd/reducer/main.go— the canonical caller; showsOpenPostgres,LoadGraphBackend,LoadRetryPolicyConfig, and app.NewHostedWithStatusServer in use togethergo/internal/telemetry/instruments.goandcontract.go— metric and span names before adding new telemetry
Invariants this package enforces
-
Backend validation at startup —
LoadGraphBackendrejects unrecognizedESHU_GRAPH_BACKENDvalues with an error; the binary must exit. Never add a default or fallback for an unrecognized string. (data_stores.go:81–92) -
Both backends use the same Bolt driver —
OpenNeo4jDriverhandles bothGraphBackendNeo4jandGraphBackendNornicDB; the switch atdata_stores.go:290is the only place that gates Bolt connectivity to backend. Do not fork this logic into callers. -
Admin endpoints are not authenticated —
NewAdminMuxmounts/healthz,/readyz,/admin/status,/metrics, and optionally/admin/replay,/admin/refinalize, and/admin/replay-collector-generationswithout authentication. These must be served only on the admin/metrics port, never on the public API port. -
Retry defaults are positive —
LoadRetryPolicyConfigrejectsMaxAttempts ≤ 0orRetryDelay ≤ 0. Do not set either to zero or negative, even for tests; use a short positive duration instead. (retry_policy.go:44–49) -
ConfigureMemoryLimitis a one-shot call — call it once per process after telemetry bootstrap. A second call afterGOMEMLIMITis set in the env is a no-op but logs redundantly. (memlimit.go:40–48) -
NewStatusMetricsServermay return nil — whenConfig.MetricsAddris empty, the function returns(nil, nil). Every caller of this function must check for a nil*HTTPServerbefore calling Start. -
NewPprofServermay return nil and defaults to loopback — whenESHU_PPROF_ADDRis unset or whitespace-only, the function returns(nil, nil)and the binary runs without a profiler endpoint. When a port-only value is supplied (e.g.:6060), the bind host is forced topprofLoopbackHost(pprof.go:19) so a default cannot expose pprof on a routable interface. Explicit hosts including0.0.0.0are preserved for the operator's chosen exposure. Invalid addresses fail at startup, including empty ports, non-numeric ports, and ports outside the 0–65535 range, so configuration mistakes surface in a uniform parse-time error rather than atnet.Listen.
Common changes and how to scope them
-
Add a new env var to
Config→ add the field toConfiginconfig.go, read it inLoadConfig, add validation inConfig.Validate; updatego/internal/runtime/config_test.gowith both a valid and invalid case; updatedocs/public/reference/cli-reference.mdor the docker-compose docs if the var affects the Compose contract; rungo test ./internal/runtime -count=1. -
Add a new admin route to
NewStatusAdminServer→ add a newStatusAdminOptionconstructor following the pattern ofWithRecoveryHandlerandWithPrometheusHandlerinstatus_server.go; wire it inNewStatusAdminMuxusing the option pattern; updateAdminMuxConfigif the route belongs to the shared probe contract; add a test instatus_server_test.go. Do not change theNewAdminMuxsignature unless the route is needed on all admin surfaces. -
Change Postgres pool defaults → update the
defaultPostgresXxxconstants indata_stores.go(lines 16–28); add acompose_defaults_test.goassertion if the change affects Compose env; updatedocs/public/reference/nornicdb-tuning.mdfor any NornicDB-relevant pool change. -
Add a new graph backend → add a
GraphBackendconstant indata_stores.go; add a case inLoadGraphBackend's switch; add a case inOpenNeo4jDriverif it uses Bolt; add a test indata_stores_test.go; update the NornicDB ADR and the embedded-local-backends ADR. Do not addif backend == ...branches outside documented narrow seams. -
Expose pprof from another binary → call
NewPprofServer(os.Getenv)after telemetry/logger setup and before the main blocking work in that binary'smain/run; check for a nil return, thenStart(ctx), log the bound address, anddefer Stop(context.Background()). API, MCP, ingester, reducer, bootstrap-index, workflow-coordinator, and hosted collector binaries follow this pattern. Do not add new env vars; reuseESHU_PPROF_ADDRso operators have one knob. -
Expose a new recovery admin route → add a method to
RecoveryHandlerinrecovery_handler.go; register the route inRecoveryHandler.Mount; add a test inrecovery_handler_test.go. -
Add a new
eshu_runtime_*metric → add the emit call in renderStatusMetrics inmetrics.go; verify the gauge name is not already defined; add the name togo/internal/telemetry/contract.goif it needs a span or dimension key; rungo test ./internal/runtime -count=1.
Failure modes and how to debug
-
Symptom: binary exits with "invalid ESHU_GRAPH_BACKEND" → the env var contains an unrecognized string; check
ESHU_GRAPH_BACKENDin the process environment and the Compose service definition. -
Symptom: binary exits with "set ESHU_FACT_STORE_DSN, ESHU_CONTENT_STORE_DSN, or ESHU_POSTGRES_DSN" → none of the three Postgres DSN env vars are set; check secrets injection in the deployment manifest or
.envfile. -
Symptom:
/readyzreturns 503 → one of the readiness probes failed; the response body names the failing dependency.status_schema: ...means the bounded migration-receipt and core-schema read failed (check schema applied and Postgres connectivity); inspect/admin/statusandeshu_runtime_queue_*gauges for backlog pressure without making/readyzaggregate the queue;postgres: ...meansPingContextfailed (database unreachable or pool exhausted);graph: ...means BoltVerifyConnectivityfailed (graph backend unreachable). Probes are registered viaWithReadinessProbes/ReadinessProbesForDependenciesin each binary's wiring. Liveness (/healthz) stays dependency-free by design. -
Symptom:
/metricsendpoint returning only hand-rolled gauges, OTEL data missing →WithPrometheusHandlerwas not passed toNewStatusAdminServer; check that the binary wiresruntimecfg.WithPrometheusHandler(providers.PrometheusHandler). -
Symptom: container OOM-killed despite low
eshu_dp_gomemlimit_bytes→ConfigureMemoryLimitwas not called orGOMEMLIMITenv var overrides the cgroup-derived limit; check thesourcefield in the startup log entry.
Anti-patterns specific to this package
-
Do not branch on
GraphBackendoutside documented seams — branches onGraphBackendNornicDBbelong only indata_stores.go, the Cypher executor seam ininternal/storage/cypher, and narrow wiring helpers in eachcmd/. Do not add backend branches inside admin handlers, retry policy, or metrics rendering. -
Do not authenticate admin routes here — authentication for admin endpoints is an operator/infrastructure concern (network policy, sidecar proxy). Adding auth logic in
NewAdminMuxwould couple all binaries to a single auth scheme. -
Do not add global singletons —
runtimeis imported by many binaries; package-levelvarstate (not constants) creates cross-binary coupling that breaks isolated tests and multi-binary runs in the same process. -
Do not duplicate pool defaults in callers —
LoadPostgresConfigandConfigurePostgresPoolare the canonical source. Callers that set pool values afterOpenPostgresoverride the shared contract and produce inconsistent behavior across binaries.
What NOT to change without an ADR
LoadGraphBackendaccepted values — adding or removing a valid backend string changes the deployment contract for all binaries; seedocs/public/reference/backend-conformance.md.- Admin route contract (
/healthz,/readyz,/admin/status,/metrics,/admin/replay,/admin/refinalize,/admin/replay-collector-generations) — Kubernetes probes, dashboards, and operator runbooks depend on these paths; path or method changes require coordinated infra updates. RetryPolicyConfigdefaults — all long-running binaries inherit these; changing defaults affects queue drain behavior cluster-wide.Configfield names / env var bindings — CLI and Compose documentation, Helm values, and operator runbooks reference these by name.