|
| 1 | +# AGENTS.md — codebase map for AI agents |
| 2 | + |
| 3 | +Read this first. Orientation map for `ubgo/shutdown` so a fresh agent knows what every part does and where to change things, without reading every file. |
| 4 | + |
| 5 | +## What this repo is |
| 6 | + |
| 7 | +`ubgo/shutdown` is a **phased, parallel-within-phase, observable graceful-shutdown manager** for Go services. You register named handlers (and "actors" — long-running goroutines) into ordered phases; on a signal (or a programmatic trigger) the `Manager` runs each phase in order, handlers within a phase in parallel, with per-handler timeouts, an overall budget, a watchdog hard-exit, force-exit on a second signal, error aggregation, and observer hooks for telemetry. The core has **zero third-party dependencies**; framework/telemetry integrations live in `contrib/`. See `README.md` for the pitch, `doc.go` for the package overview. |
| 8 | + |
| 9 | +## Modules |
| 10 | + |
| 11 | +| Path | Module | Role | Deps | |
| 12 | +|---|---|---|---| |
| 13 | +| `.` | `github.com/ubgo/shutdown` | Core manager. | stdlib only (a `zero-dep-check` CI gate enforces this) | |
| 14 | +| `contrib/shutdown-{nethttp,gin,chi,echo,fiber}` | each own module | HTTP-server shutdown adapters. | the target framework | |
| 15 | +| `contrib/shutdown-{otel,prom,zap}` | each own module | Observer/Logger adapters for OpenTelemetry, Prometheus, zap. | the target lib | |
| 16 | + |
| 17 | +Go 1.24. Each contrib is a separate module so the core stays dependency-free. |
| 18 | + |
| 19 | +## Core files — what each owns |
| 20 | + |
| 21 | +| File | Responsibility | |
| 22 | +|---|---| |
| 23 | +| `doc.go` | Package overview godoc. | |
| 24 | +| `types.go` | `HandlerFunc`, `Phase` (+ the predefined phase constants), `ErrorPolicy`, `Logger` interface, `Observer`, `RunFunc`/`InterruptFunc`. | |
| 25 | +| `manager.go` | `Manager` — the central coordinator: `New`, `Register`, `Subscribe`, `Listen` (signal-driven), `Shutdown` (programmatic), `OnSignal`. | |
| 26 | +| `register.go` | `Register`/`RegisterOption` (`WithPhase`, `WithTimeout`, …) and the internal `registration`. | |
| 27 | +| `runner.go` | Phase execution: parallel-within-phase by default, serial when opted in; per-handler ctx = min(handler timeout, remaining budget); error aggregation. | |
| 28 | +| `actor.go` | "Actors" — register a long-running `RunFunc` + `InterruptFunc` pair that the manager interrupts and waits for, unified into the same phase machinery. | |
| 29 | +| `watchdog.go` | The hard-exit safety net: after budget + grace, `os.Exit` with the stuck-handler names logged. | |
| 30 | +| `observer.go` | Observer fan-out (`OnSignal`/`OnPhaseStart`/`OnHandlerEnd`/`OnComplete`, …) consumed by `contrib/shutdown-otel`/`-prom`. | |
| 31 | +| `options.go` | `Option` / `config` — `WithBudget`, `WithLogger`, `WithSignals`, `WithForceOnSecondSignal`, `WithExitOnComplete`, `WithErrorPolicy`, `WithSerial`. | |
| 32 | +| `logger.go` | The minimal `Logger` interface's noop default (real loggers plug in via `WithLogger` / `contrib/shutdown-zap`). | |
| 33 | + |
| 34 | +## The flow to understand |
| 35 | + |
| 36 | +`manager.go:Listen` (or `Shutdown`) → sort registrations into phase buckets (`bucketsByPhase`) → for each phase ascending: fire `OnPhaseStart`, run handlers (`runner.go:runPhase`, parallel unless `WithSerial`), aggregate errors per `ErrorPolicy`, fire `OnPhaseEnd` → `OnComplete` with `errors.Join`. A second signal force-exits; the `watchdog` hard-exits if the budget is blown. Phases run logs/flush **last** by phase number so earlier errors are recorded. |
| 37 | + |
| 38 | +## Conventions |
| 39 | + |
| 40 | +- **Zero third-party deps in core** — the `zero-dep-check` task/CI gate fails if `go.mod` gains a non-stdlib require. Framework/telemetry code goes in `contrib/`. |
| 41 | +- **Race detector mandatory**; high coverage. `task ci` runs everything. |
| 42 | +- **No panics in libraries** — handlers' panics are recovered into a `PanicError` and surfaced in the aggregate. |
| 43 | +- Comments explain *why*, not *what*. |
| 44 | + |
| 45 | +## Running |
| 46 | + |
| 47 | +```sh |
| 48 | +task ci # fmt + vet + race tests + coverage + zero-dep-check |
| 49 | +task test:race |
| 50 | +task zero-dep-check |
| 51 | +``` |
| 52 | + |
| 53 | +## Where to look for X |
| 54 | + |
| 55 | +- "Add a phase / change ordering" → `types.go` (phase constants) + `WithPhase` in `register.go`. |
| 56 | +- "Wire it to my HTTP framework" → `contrib/shutdown-<framework>`. |
| 57 | +- "Emit shutdown telemetry" → `observer.go` + `contrib/shutdown-otel` / `-prom`. |
| 58 | +- "Force-exit / watchdog behavior" → `manager.go` (`runShutdownWithForceWatch`) + `watchdog.go`. |
0 commit comments