{"id":"a7ad7997-56e6-4e31-8ee9-6b5b1868f7a4","entityType":"agent","slug":"clawhub-psyb0t-servicepack","name":"servicepack","canonicalUrl":"https://www.xpersona.co/agent/clawhub-psyb0t-servicepack","canonicalPath":"/agent/clawhub-psyb0t-servicepack","generatedAt":"2026-10-10T07:47:37.429Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":null},"description":"Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.7K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17fq93tmpky791n7516jcn08n83sfn2:servicepack","sourceUrl":"https://clawhub.ai/psyb0t/servicepack","homepage":"https://clawhub.ai/psyb0t/skills/servicepack","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/psyb0t/servicepack","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/psyb0t/skills/servicepack","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":65,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"servicepack technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":null},"stars":null,"forks":null,"downloads":1729,"packageName":null,"latestVersion":"1.9.4","tractionLabel":"1.7K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T03:33:02.177Z","lastCrawledAt":"2026-10-10T03:33:02.177Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T03:33:02.177Z","lastVerifiedAt":null,"highlights":[{"version":"1.9.4","createdAt":"2026-10-10T00:49:15.196Z","changelog":"- Removed the skill card documentation file. - Updated references/setup.md. - Cleaned up documentation by removing redundant files.","fileCount":4,"zipByteSize":8657},{"version":"1.9.3","createdAt":"2026-09-21T10:56:08.424Z","changelog":"- Removed the skill-card.md file to streamline the repo. - No functionality or command changes in this release. - Documentation, permissions, and usage remain unchanged.","fileCount":4,"zipByteSize":8841},{"version":"1.9.2","createdAt":"2026-08-21T06:22:43.654Z","changelog":"- Removed obsolete skill-card.md file. - Updated SKILL.md file content. - `make audit` shell permission removed. - Minor metadata/permissions clean-up.","fileCount":4,"zipByteSize":8913},{"version":"1.9.1","createdAt":"2026-08-21T02:29:16.918Z","changelog":"- Removed the file: skill-card.md - No other changes to source, permissions, or functionality.","fileCount":4,"zipByteSize":8851},{"version":"1.9.0","createdAt":"2026-08-21T01:55:50.052Z","changelog":"- Removed the skill-card.md file. - Updated SKILL.md, with content and permission details unchanged. - No user-facing functional changes; documentation and metadata maintenance only.","fileCount":4,"zipByteSize":8848},{"version":"1.8.1","createdAt":"2026-08-20T10:40:24.435Z","changelog":"- Removed the skill-card.md file. - No other changes.","fileCount":4,"zipByteSize":8888},{"version":"1.7.2","createdAt":"2026-08-20T08:54:49.216Z","changelog":"## servicepack 1.7.2 Changelog - Removed the file `skill-card.md` from the project. - No new features or code changes introduced; this is a cleanup/maintenance release.","fileCount":4,"zipByteSize":8802},{"version":"1.7.1","createdAt":"2026-08-20T05:49:05.619Z","changelog":"- No changes detected in this version; documentation and interface remain the same. - Version bump only, with no file or feature modifications.","fileCount":4,"zipByteSize":8887}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17fq93tmpky791n7516jcn08n83sfn2:servicepack","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17fq93tmpky791n7516jcn08n83sfn2:servicepack` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/psyb0t/servicepack before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T07:47:37.425Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-psyb0t-servicepack/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":null},"readme":"Skill: servicepack\n\nOwner: psyb0t\n\nSummary: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\n\nTags: latest:1.9.4\n\nVersion history:\n\nv1.9.4 | 2026-10-10T00:49:15.196Z | auto\n\n- Removed the skill card documentation file.\n- Updated references/setup.md.\n- Cleaned up documentation by removing redundant files.\n\nv1.9.3 | 2026-09-21T10:56:08.424Z | auto\n\n- Removed the skill-card.md file to streamline the repo.\n- No functionality or command changes in this release.\n- Documentation, permissions, and usage remain unchanged.\n\nv1.9.2 | 2026-08-21T06:22:43.654Z | auto\n\n- Removed obsolete skill-card.md file.\n- Updated SKILL.md file content.\n- `make audit` shell permission removed.\n- Minor metadata/permissions clean-up.\n\nv1.9.1 | 2026-08-21T02:29:16.918Z | auto\n\n- Removed the file: skill-card.md\n- No other changes to source, permissions, or functionality.\n\nv1.9.0 | 2026-08-21T01:55:50.052Z | auto\n\n- Removed the skill-card.md file.\n- Updated SKILL.md, with content and permission details unchanged. \n- No user-facing functional changes; documentation and metadata maintenance only.\n\nv1.8.1 | 2026-08-20T10:40:24.435Z | auto\n\n- Removed the skill-card.md file.  \n- No other changes.\n\nv1.7.2 | 2026-08-20T08:54:49.216Z | auto\n\n## servicepack 1.7.2 Changelog\n\n- Removed the file `skill-card.md` from the project.\n- No new features or code changes introduced; this is a cleanup/maintenance release.\n\nv1.7.1 | 2026-08-20T05:49:05.619Z | auto\n\n- No changes detected in this version; documentation and interface remain the same.\n- Version bump only, with no file or feature modifications.\n\nv1.7.0 | 2026-08-20T05:42:00.477Z | auto\n\n- Removed the skill's skill-card.md file.\n- No user-facing or functional changes; documentation file cleanup only.\n\nv1.6.4 | 2026-08-17T18:36:04.804Z | auto\n\n- Removed the file: skill-card.md\n- No other changes to functionality or documentation.\n\nv1.6.1 | 2026-08-17T17:14:43.381Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or permissions.\n- Documentation remains unchanged.\n\nv1.5.0 | 2026-08-15T23:07:14.025Z | auto\n\n- Removed the skill documentation file skill-card.md.\n- No user-facing feature or API changes. This update is documentation cleanup only.\n\nv1.4.0 | 2026-08-14T14:14:24.873Z | auto\n\n## servicepack 1.4.0\n\n- Removed the skill-card.md file.\n- No other user-facing changes in this version.\n\nv1.3.4 | 2026-08-14T13:43:47.970Z | auto\n\n- Removed the skill-card.md file.\n- No functional or behavioral changes to the skill; documentation and implementation remain unchanged.\n\nv1.3.3 | 2026-08-14T13:01:38.065Z | auto\n\n## servicepack 1.3.3\n\n- Removed the file `skill-card.md`.\n- No other user-visible changes.\n\nv1.3.2 | 2026-08-14T11:44:40.634Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or user-facing features.\n\nv1.3.1 | 2026-08-14T02:06:49.089Z | auto\n\n- Added new make commands to permissions: dev-image, test-integration, test-coverage, format, and audit.\n- Now requires Docker instead of Go in the environment for full functionality.\n- Homepage description and import path updated to clarify intended usage for both local debugging and mono-/multi-binary deployments.\n- Removed some read permissions and added/adjusted write and shell permissions to match new build/test workflow.\n- Minor documentation edits for clarity.\n\nv1.3.0 | 2026-08-14T01:38:26.429Z | auto\n\nservicepack 1.3.0\n\n- Switched logging and context management from slog/slogconf to ctxscope for service scaffolding and usage.\n- Updated example service code to use ctxscope logging patterns and structured context.\n- Improved configuration struct example to use `default` tag and demonstrate ctxscope integration.\n- Removed outdated skill-card.md documentation.\n- Updated references/setup.md with latest usage patterns.\n\nv1.2.23 | 2026-08-08T21:25:09.861Z | auto\n\n- Updated the example service scaffolded by `make service` to use panic(\"TODO: Implement ...\") in the Run() method rather than blocking on `<-ctx.Done()`.\n- Minor doc fixes and copyedits in SKILL.md for clarity and consistency.\n- Removed the unused/obsolete `skill-card.md` file.\n\nv1.2.22 | 2026-08-08T15:05:50.664Z | auto\n\n- Removed the skill-card.md file.\n- No functional or user-facing changes to the skill itself.\n\nv1.2.21 | 2026-08-08T09:56:40.963Z | auto\n\n- Minor update to documentation for clarity and completeness.\n- The logging mention in the description now includes both \"slogging/slogconf\".\n- Removed the file skill-card.md.\n- No changes to code or functionality.\n\nv1.2.20 | 2026-08-06T12:39:09.877Z | auto\n\n- Removed the file: skill-card.md\n- No other user-facing changes in this version.\n\nv1.2.19 | 2026-08-06T11:23:24.575Z | auto\n\n- Removed the skill-card.md file.\n- No functional or behavioral changes to the skill.\n\nv1.2.18 | 2026-08-06T10:20:38.282Z | auto\n\n- Removed the skill card documentation file (skill-card.md).\n- No changes to code, interfaces, or core functionality.\n\nv1.2.17 | 2026-08-06T03:17:17.083Z | auto\n\n- Removed the skill-card.md file, consolidating documentation in SKILL.md.\n- No functional or behavior changes. All existing features and usage remain the same.\n\nv1.2.16 | 2026-08-06T02:44:57.446Z | auto\n\n- Documented a key clarification: implementing `Dependent` only orders the launch of services, not their readiness; true ordered readiness requires combining `Dependent` with `ReadyNotifier`.\n- Added an explanation of the interaction between dependency launch order and readiness gating, making service startup behavior clearer.\n- Removed outdated `skill-card.md` file.\n- No functional or permission changes; this update is purely to improve documentation and understanding.\n\nv1.2.15 | 2026-08-01T20:25:12.884Z | auto\n\n- Removed the file skill-card.md.\n- No changes to features or functionality.\n\nv1.2.14 | 2026-07-27T23:34:23.354Z | auto\n\n- Removed the file: skill-card.md.\n- No other user-facing changes.\n\nv1.2.13 | 2026-07-27T22:51:57.394Z | auto\n\n- Removed the sample file skill-card.md.  \n- No changes to core functionality.  \n- All usage instructions and framework documentation remain unchanged.\n\nv1.2.12 | 2026-07-27T07:42:08.238Z | auto\n\n- Removed sample file: skill-card.md\n- No user-facing functionality changes; documentation and core skill behavior remain unchanged.\n\nv1.2.11 | 2026-07-27T06:11:12.891Z | auto\n\n- Removed the skill documentation file skill-card.md.\n- No changes to functionality or user-facing features.\n\nv1.2.10 | 2026-07-27T05:55:58.413Z | auto\n\nNo user-visible changes in this release.\n\n- Version bump to 1.2.10 with no file or documentation changes detected.\n\nv1.2.9 | 2026-07-27T05:21:57.110Z | auto\n\n# servicepack 1.2.9 Changelog\n\n- Removed the skill-card.md file.\n- No other functionality, permissions, or documentation changes detected in this version.\n\nv1.2.8 | 2026-07-26T15:51:44.372Z | auto\n\n- Removed the documentation file skill-card.md.\n- No changes to functionality or configuration.\n\nv1.2.7 | 2026-07-25T23:44:33.222Z | auto\n\n- Expanded documentation in SKILL.md with detailed usage, lifecycle hooks, CLI subcommands, and advanced framework concepts.\n- Clear guidelines on when to use (and not use) servicepack for Go service/daemon development.\n- Step-by-step quick start instructions and example service scaffold shown for easy onboarding.\n- Security and safety section outlines framework boundaries and safe usage.\n- Documentation of all supported Service interfaces (Retryable, AllowedFailure, Dependent, etc.) and project structure.\n- Enhanced instructions for customization, logging, and config handling.\n\nArchive index:\n\nArchive v1.9.4: 4 files, 8657 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (1873b), SKILL.md (10169b), _meta.json (130b)\n\nFile v1.9.4:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, `docs/`, and `tests/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.9.4:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.9.4\",\n  \"publishedAt\": 1791593355196\n}\n\nFile v1.9.4:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.9`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.9.4:skill-card.md\n\n## Description:\n\nGuides developers in building Go services with the clone-and-own servicepack template, including concurrent workers, startup dependencies, retries, and graceful shutdown.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers use this skill to scaffold and maintain Go applications with multiple long-running services, from local development through deployment as one binary or separate services.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The documented make own step rewrites project files and resets Git history in the clone.\n\nMitigation: Run it only in a fresh clone after confirming the target module path.\n\nRisk: Build and update commands can change project files or run tools against sensitive code.\n\nMitigation: Review Make targets before running them; keep custom service code outside framework-owned files that updates overwrite.\n\n## Reference(s):\n\n- [servicepack setup guide](references/setup.md)\n- [servicepack project homepage](https://github.com/psyb0t/servicepack)\n- [servicepack ClawHub release](https://clawhub.ai/psyb0t/skills/servicepack)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Code, Shell commands, Configuration instructions]\n\n**Output Format:** [Markdown with Go and shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guidance covers project setup, service implementations, and build commands.]\n\n## Skill Version(s):\n\n1.9.4 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.9.3: 4 files, 8841 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2351b), SKILL.md (10169b), _meta.json (130b)\n\nFile v1.9.3:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, `docs/`, and `tests/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.9.3:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.9.3\",\n  \"publishedAt\": 1789988168424\n}\n\nFile v1.9.3:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.4`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.9.3:skill-card.md\n\n## Description:\n\nServicepack helps agents guide developers through creating Go services from the psyb0t/servicepack template, including service scaffolding, dependency ordering, readiness gating, retries, lifecycle hooks, logging, configuration, build, test, and Docker-backed workflows.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to build or modify Go servicepack projects that run related services together locally and can later be deployed as one binary or split into separate microservices.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The setup workflow can rewrite module files and reset git history when running the ownership command in a cloned template repository.\n\nMitigation: Run ownership setup only in a fresh clone or intended project workspace, and review the setup notes before executing commands.\n\nRisk: Docker-backed build and development targets may pull or build local development images.\n\nMitigation: Use an environment where Docker image builds are expected and allowed, and review generated commands before running them.\n\nRisk: Framework-owned files can be overwritten by servicepack update workflows.\n\nMitigation: Keep custom behavior in documented extension points such as service files, lifecycle hooks, and custom command files.\n\n## Reference(s):\n\n- [Setup reference](references/setup.md)\n- [ClawHub skill page](https://clawhub.ai/psyb0t/skills/servicepack)\n- [Project homepage](https://github.com/psyb0t/servicepack)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with inline Go code and bash command blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include generated service scaffolding guidance, Make target commands, configuration notes, and file-editing recommendations.]\n\n## Skill Version(s):\n\n1.9.3 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.9.2: 4 files, 8913 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2502b), SKILL.md (10169b), _meta.json (130b)\n\nFile v1.9.2:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, `docs/`, and `tests/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.9.2:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.9.2\",\n  \"publishedAt\": 1787293363654\n}\n\nFile v1.9.2:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.4`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.9.2:skill-card.md\n\n## Description:\n\nBuild a Go service on psyb0t/servicepack, a clone-and-own Go service framework for related services that need concurrent execution, dependency-ordered startup, retries, readiness gates, service-specific CLI commands, structured logging, and graceful shutdown.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to start or extend Go service projects based on the servicepack template. It guides them through cloning, taking ownership of the template, adding services, configuring lifecycle behavior, running Docker-backed build/test workflows, and understanding framework boundaries.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Template ownership commands can rewrite project files and reinitialize Git history in the working clone.\n\nMitigation: Run ownership or development commands only on a fresh clone or after making a backup.\n\nRisk: Docker-backed Make targets can execute build scripts, Makefiles, Dockerfiles, and environment-dependent behavior from the cloned project.\n\nMitigation: Review or pin the upstream commit and inspect Makefiles, Dockerfiles, scripts, and Docker-exposed environment variables before running them.\n\nRisk: Framework-owned files may be overwritten by update workflows, which can discard manual edits in those paths.\n\nMitigation: Keep custom behavior in service directories, lifecycle hooks, and documented extension files rather than editing framework-owned files.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/psyb0t/skills/servicepack)\n- [servicepack repository homepage](https://github.com/psyb0t/servicepack)\n- [Setup reference](references/setup.md)\n\n## Skill Output:\n\n**Output Type(s):** [guidance, markdown, code, shell commands, configuration]\n\n**Output Format:** [Markdown guidance with inline Go, shell, and configuration examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Assumes a Docker-capable Go project workspace and may propose Make targets that modify the current clone.]\n\n## Skill Version(s):\n\n1.9.2 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.9.1: 4 files, 8851 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2365b), SKILL.md (10188b), _meta.json (130b)\n\nFile v1.9.1:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\n    - \"make audit\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, `docs/`, and `tests/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.9.1:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.9.1\",\n  \"publishedAt\": 1787279356918\n}\n\nFile v1.9.1:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.4`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.9.1:skill-card.md\n\n## Description:\n\nservicepack helps developers build Go services from a clone-and-own framework with concurrent service management, dependency-aware startup, retries, readiness gating, CLI commands, structured logging, and graceful shutdown.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to scaffold and maintain Go servicepack applications, add services, regenerate service registration, configure lifecycle hooks, and run build, test, lint, and audit workflows.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The clone-and-own workflow rewrites project identity and reinitializes Git history for the clone.\n\nMitigation: Run make own only in a fresh clone at the start of adoption and keep an external backup or remote copy of any work that must be preserved.\n\nRisk: Docker-backed make targets and framework update commands can modify local project files.\n\nMitigation: Review the relevant Makefile targets and current working tree before running build, generation, or update commands.\n\nRisk: Service code added by the user may introduce runtime exposure such as HTTP, gRPC, database, or credential handling surfaces.\n\nMitigation: Review the generated and custom service code, manage service environment variables explicitly, and run the documented test, lint, and audit targets before deployment.\n\n## Reference(s):\n\n- [servicepack ClawHub page](https://clawhub.ai/psyb0t/skills/servicepack)\n- [servicepack repository](https://github.com/psyb0t/servicepack)\n- [Setup](references/setup.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with Go code examples and shell command blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires Docker for the documented development, build, generation, lint, and test workflows.]\n\n## Skill Version(s):\n\n1.9.1 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.9.0: 4 files, 8848 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2453b), SKILL.md (10188b), _meta.json (130b)\n\nFile v1.9.0:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\n    - \"make audit\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, `docs/`, and `tests/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.9.0:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.9.0\",\n  \"publishedAt\": 1787277350052\n}\n\nFile v1.9.0:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.4`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.9.0:skill-card.md\n\n## Description:\n\nBuild Go service projects from the psyb0t/servicepack template with guidance for service scaffolding, registration, lifecycle hooks, dependency ordering, retries, readiness gating, logging, configuration, testing, and build commands.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill when starting or maintaining a servicepack-based Go service, especially when they need concurrent service management, ordered startup, readiness gates, retry behavior, graceful shutdown, and project-specific build or test commands.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The template conversion command rewrites module files and resets git history in the cloned repository.\n\nMitigation: Run make own only once at the start of a fresh or disposable clone, and back up any work that must be preserved before running template-conversion commands.\n\nRisk: Framework-owned files can be overwritten by servicepack update workflows.\n\nMitigation: Keep custom behavior in service directories, cmd/init.go, cmd/commands.go, docs, tests, and local project files rather than editing framework-owned paths.\n\nRisk: Docker is required for normal dependency, generation, build, lint, and test workflows.\n\nMitigation: Confirm Docker is installed and available before relying on the skill's recommended servicepack Make targets.\n\n## Reference(s):\n\n- [ClawHub servicepack skill page](https://clawhub.ai/psyb0t/skills/servicepack)\n- [servicepack project homepage](https://github.com/psyb0t/servicepack)\n- [Setup reference](references/setup.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with inline Go examples, shell commands, configuration notes, and file-path recommendations]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires Docker for normal servicepack build, dependency, generation, lint, and test workflows.]\n\n## Skill Version(s):\n\n1.9.0 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.8.1: 4 files, 8888 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2519b), SKILL.md (10165b), _meta.json (130b)\n\nFile v1.8.1:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\n    - \"make audit\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.8.1:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.8.1\",\n  \"publishedAt\": 1787222424435\n}\n\nFile v1.8.1:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.4`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.8.1:skill-card.md\n\n## Description:\n\nservicepack helps agents guide developers through creating Go services from the psyb0t/servicepack clone-and-own template, including service scaffolding, lifecycle hooks, dependency ordering, retries, readiness gates, logging, configuration, and Make/Docker workflows.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill when starting or maintaining a Go servicepack project that runs related workers and services with coordinated startup, retries, readiness signaling, per-service CLI commands, and graceful shutdown.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: `make own` rewrites the module, removes the existing Git history in the clone, and creates a new initial commit.\n\nMitigation: Run `make own` only once in a fresh clone and confirm the target module path before execution.\n\nRisk: The skill guides agents toward Docker-backed Make targets that execute project build, generation, lint, test, and audit workflows.\n\nMitigation: Review the upstream template and Make targets before allowing those commands, and run them in an appropriate development environment with Docker available.\n\nRisk: The framework has no runtime service surface by itself, but generated services can introduce HTTP, gRPC, database, or credential-handling behavior.\n\nMitigation: Review the service code, configuration variables, and exposed endpoints added by the developer before deployment.\n\n## Reference(s):\n\n- [servicepack Skill on ClawHub](https://clawhub.ai/psyb0t/skills/servicepack)\n- [servicepack Publisher Profile](https://clawhub.ai/user/psyb0t)\n- [Setup Reference](references/setup.md)\n- [servicepack Project Homepage](https://github.com/psyb0t/servicepack)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Markdown, Code, Shell commands, Configuration]\n\n**Output Format:** [Markdown with Go and shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include file-edit guidance, Make targets, Docker-backed commands, and service configuration steps.]\n\n## Skill Version(s):\n\n1.8.1 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.7.2: 4 files, 8802 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2255b), SKILL.md (10165b), _meta.json (130b)\n\nFile v1.7.2:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\n    - \"make audit\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating between services in the same process.\n- You want per-service CLI subcommands (`./app <service> migrate`) alongside the long-running `./app run`.\n- You're adding a new service to a repo that already has `servicepack.version`, `Makefile.servicepack`, or `internal/pkg/service-manager/` present.\n\n## When NOT to use\n\n- You need a single, simple `main()` with no concurrent workers — plain Go is less ceremony.\n- You're building an HTTP API only, no background workers — reach for `aichteeteapee` directly in a plain `main.go`; servicepack's value is the multi-service supervisor, not routing.\n- You want a library to import into an EXISTING app without restructuring around `cmd/`, `internal/app/`, `internal/pkg/services/`. servicepack expects to own your project's top-level shape.\n\n## Quick start\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker\n```\n\n`make service` scaffolds `internal/pkg/services/my-worker/my-worker.go`:\n\n```go\npackage myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nEdit the `Run()` body, then `make service-registration` regenerates `internal/pkg/services/services.gen.go` (auto-discovers every `Service` implementation via `gofindimpl`). Build and run:\n\n```bash\nmake build\n./build/yourproject run\n```\n\n## The Service interface\n\nEvery service implements:\n\n```go\ntype Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}\n```\n\n`Run()` listens for `ctx.Done()` and returns cleanly on cancellation; a non-nil return from `Run()` stops all services (unless the service is an `AllowedFailure`, see below). `Stop()` runs during shutdown for cleanup.\n\n## Optional interfaces — opt into extra behavior\n\nA service can implement any combination of these on top of `Service`:\n\n```go\n// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}\n```\n\nDependencies on services not present in the current process (e.g. another microservice) are skipped with a debug log, not an error — cyclic dependencies within the process ARE rejected at startup.\n\n**`Dependent` alone orders the LAUNCH, not the readiness.** A service that does not implement `ReadyNotifier` is treated as ready the moment its goroutine is launched, so its dependents are started right after — possibly before its `Run` body has executed a single line. If a dependent genuinely must not start until the dependency is accepting work (a DB accepting connections, a listener bound), the dependency has to implement `ReadyNotifier` and close its channel when it is actually up. Combining `Dependent` with `ReadyNotifier` is what turns \"started in the right order\" into \"started only once the dependency works\".\n\n## Lifecycle hooks — customize without touching framework files\n\n`cmd/init.go` is yours; it's never overwritten by `make servicepack-update`. Register hooks on the `App` singleton:\n\n```go\n// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}\n```\n\nHooks run sequentially in registration order; multiple hooks are allowed.\n\n## Custom CLI commands\n\n`cmd/commands.go` is also yours — add standalone cobra commands separate from per-service `Commander` commands:\n\n```go\n// cmd/commands.go\npackage main\n\nimport \"github.com/spf13/cobra\"\n\nfunc commands() []*cobra.Command {\n\treturn []*cobra.Command{\n\t\t{\n\t\t\tUse:   \"seed\",\n\t\t\tShort: \"Seed the database\",\n\t\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\t\t// your logic\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\n## Logging and config\n\n- Logging is `ctxscope` over `log/slog`, with `github.com/psyb0t/slogging/slogconf` wiring the default handler. Add extra `slog.Handler`s (Loki, Datadog, etc.) in `cmd/init.go`; set durable identity fields with `ctxscope.Set(ctx, ...)`, then log through `ctxscope.GetLogger(ctx)`.\n- Config is `github.com/psyb0t/gonfiguration` — struct tags (`env:\"MYWORKER_VALUE\"`), `gonfiguration.Parse(&cfg)`, `gonfiguration.SetDefaults(map[string]any{...})`. Never `os.Getenv` directly.\n- Errors are wrapped with `github.com/psyb0t/ctxerrors` (`ctxerrors.Wrap(err, \"doing X\")`) for file/line/function context.\n\n## Framework boundaries — never hand-edit these\n\n`internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, `cmd/main.go`, `Makefile.servicepack`, `scripts/make/servicepack/`, `Dockerfile.servicepack*`, `servicepack.version` are all overwritten by `make servicepack-update`. Customize behavior through the lifecycle hooks above, not by patching these files. Everything under `internal/pkg/services/`, plus `Makefile`, `Dockerfile`, `Dockerfile.dev`, `cmd/init.go`, `cmd/commands.go`, is yours and never touched by updates.\n\n## Filtering which services run\n\n```bash\nexport SERVICES_ENABLED=\"my-worker,another-service\"   # comma-separated; unset/empty = all\n./build/yourproject run\n```\n\n## Further reading\n\n`references/setup.md` has the install/module details, Docker/toolchain requirements, and a fuller worked example with `Retryable` + `Dependent` + `ReadyNotifier` combined.\n\nFile v1.7.2:_meta.json\n\n{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.7.2\",\n  \"publishedAt\": 1787216089216\n}\n\nFile v1.7.2:references/setup.md\n\n# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.4`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `example-migrator`, `example-optional`, `example-flaky`, `example-crasher`, `example-nested/http`, `example-nested/grpc`) so you can see retries, dependencies, allowed failures, readiness gating, and a crash-everything failure in action before committing to `make own`.\n\n## Environment variables the framework itself reads\n\n```bash\nLOG_LEVEL=debug                 # debug, info, warn, error\nLOG_FORMAT=json                 # json, text\nLOG_ADD_SOURCE=true              # include file:line in log records\nENV=dev                          # dev, prod (default: prod) — via goenv\nRUNNER_SHUTDOWNTIMEOUT=10s        # graceful shutdown deadline (default: 10s)\nSERVICES_ENABLED=svc1,svc2        # comma-separated allowlist; empty/unset = run all\n```\n\nYour own services define their own env vars via `gonfiguration` struct tags — see the worked example below.\n\n## Fuller worked example — retryable, dependent, ready-notifying service\n\nScaffolded with `make service NAME=example-db`, then hand-extended past the generated skeleton to show the three most commonly combined optional interfaces:\n\n```go\n// internal/pkg/services/example-db/example_db.go\npackage exampledb\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"example-db\"\n\ntype Config struct {\n\tDSN string `env:\"EXAMPLEDB_DSN\"`\n}\n\ntype ExampleDB struct {\n\tconfig  Config\n\treadyCh chan struct{}\n}\n\nfunc New() (*ExampleDB, error) {\n\tcfg := Config{}\n\n\tgonfiguration.SetDefaults(map[string]any{\n\t\t\"EXAMPLEDB_DSN\": \"postgres://localhost/example\",\n\t})\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"failed to parse example-db config\")\n\t}\n\n\treturn &ExampleDB{\n\t\tconfig:  cfg,\n\t\treadyCh: make(chan struct{}),\n\t}, nil\n}\n\nfunc (s *ExampleDB) Name() string {\n\treturn ServiceName\n}\n\n// MaxRetries + RetryDelay satisfy Retryable — restart up to 2 times,\n// waiting 2s between attempts, before giving up.\nfunc (s *ExampleDB) MaxRetries() int {\n\treturn 2\n}\n\nfunc (s *ExampleDB) RetryDelay() time.Duration {\n\treturn 2 * time.Second\n}\n\n// Ready satisfies ReadyNotifier — dependents wait on this channel\n// before their own Run() is started.\nfunc (s *ExampleDB) Ready() <-chan struct{} {\n\treturn s.readyCh\n}\n\nfunc (s *ExampleDB) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\n\t// connect, migrate, whatever \"actually ready\" means for you\n\tclose(s.readyCh)\n\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleDB) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\n```go\n// internal/pkg/services/example-api/example_api.go\npackage exampleapi\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxscope\"\n)\n\nconst ServiceName = \"example-api\"\n\ntype ExampleAPI struct{}\n\nfunc New() (*ExampleAPI, error) {\n\treturn &ExampleAPI{}, nil\n}\n\nfunc (s *ExampleAPI) Name() string {\n\treturn ServiceName\n}\n\n// Dependencies satisfies Dependent — example-api waits for\n// example-db to signal ready before its Run() starts.\nfunc (s *ExampleAPI) Dependencies() []string {\n\treturn []string{\"example-db\"}\n}\n\nfunc (s *ExampleAPI) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(ctx).Info(\"starting service\")\n\t<-ctx.Done()\n\n\treturn nil\n}\n\nfunc (s *ExampleAPI) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}\n```\n\nAfter adding files, regenerate discovery and build:\n\n```bash\nmake service-registration\nmake build\n./build/yourproject run\n```\n\n`example-db` starts first (no deps), signals ready, then `example-api` starts. If `example-db.Run()` returns an error, it retries twice with a 2s delay before propagating and stopping the whole process (it does not implement `AllowedFailure`).\n\nFile v1.7.2:skill-card.md\n\n## Description:\n\nBuild Go services from the psyb0t/servicepack clone-and-own template with concurrent service management, dependency-aware startup, retries, readiness gating, CLI commands, structured logging, and graceful shutdown.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to start or maintain Go servicepack projects, scaffold services, regenerate service registration, and apply service lifecycle patterns for local debugging or deployment as one binary or split microservices.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The template ownership step intentionally rewrites module files and resets Git history in the clone.\n\nMitigation: Run the ownership step only once in a fresh clone intended for the new project.\n\nRisk: Generated projects inherit the runtime, network, database, and secret-management exposure of the services the user writes.\n\nMitigation: Review and secure service-specific HTTP, gRPC, database, and environment configuration before deployment.\n\nRisk: Framework-owned files can be overwritten by servicepack update workflows.\n\nMitigation: Keep custom behavior in documented service directories, cmd/init.go, and cmd/commands.go rather than editing framework-owned files.\n\n## Reference(s):\n\n- [servicepack ClawHub page](https://clawhub.ai/psyb0t/skills/servicepack)\n- [servicepack GitHub repository](https://github.com/psyb0t/servicepack)\n- [Setup](references/setup.md)\n\n## Skill Output:\n\n**Output Type(s):** [guidance, markdown, code, shell commands, configuration]\n\n**Output Format:** [Markdown guidance with Go code snippets and shell commands]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires Docker for normal dependency, generation, build, lint, and test workflows.]\n\n## Skill Version(s):\n\n1.7.2 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.7.1: 4 files, 8887 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2529b), SKILL.md (10165b), _meta.json (130b)\n\nFile v1.7.1:SKILL.md\n\n---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\n    - \"make audit\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"F\n\nArchive v1.7.0: 4 files, 8897 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2486b), SKILL.md (10165b), _meta.json (130b)\n\nArchive v1.6.4: 4 files, 8919 bytes\n\nFiles: references/setup.md (7078b), skill-card.md (2552b), SKILL.md (10165b), _meta.json (130b)","readmeExcerpt":"Skill: servicepack Owner: psyb0t Summary: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a go get library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander)","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"git clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\nmake service NAME=my-worker"},{"language":"go","snippet":"package myworker\n\nimport (\n\t\"context\"\n\n\t\"github.com/psyb0t/ctxerrors\"\n\t\"github.com/psyb0t/ctxscope\"\n\t\"github.com/psyb0t/gonfiguration\"\n)\n\nconst ServiceName = \"my-worker\"\n\ntype Config struct {\n\tValue string `env:\"MYWORKER_VALUE\" default:\"default-value\"`\n}\n\ntype MyWorker struct {\n\tconfig Config\n}\n\nfunc New() (*MyWorker, error) {\n\tcfg := Config{}\n\n\tif err := gonfiguration.Parse(&cfg); err != nil {\n\t\treturn nil, ctxerrors.Wrap(err, \"parse my-worker config\")\n\t}\n\n\treturn &MyWorker{config: cfg}, nil\n}\n\nfunc (s *MyWorker) Name() string {\n\treturn ServiceName\n}\n\nfunc (s *MyWorker) Run(ctx context.Context) error {\n\tctx = ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tlogger := ctxscope.GetLogger(ctx)\n\tlogger.Info(\"starting service\")\n\n\t<-ctx.Done()\n\tlogger.Info(\"service context cancelled\")\n\n\treturn nil\n}\n\nfunc (s *MyWorker) Stop(ctx context.Context) error {\n\tserviceCtx := ctxscope.Set(ctx, ctxscope.Attr(\"service\", ServiceName))\n\tctxscope.GetLogger(serviceCtx).Info(\"stopping service\")\n\n\treturn nil\n}"},{"language":"bash","snippet":"make build\n./build/yourproject run"},{"language":"go","snippet":"type Service interface {\n\tName() string\n\tRun(ctx context.Context) error\n\tStop(ctx context.Context) error\n}"},{"language":"go","snippet":"// Retryable — service gets restarted on failure, up to MaxRetries times,\n// waiting RetryDelay between attempts.\ntype Retryable interface {\n\tMaxRetries() int\n\tRetryDelay() time.Duration\n}\n\n// AllowedFailure — service can die (even after exhausting retries)\n// without killing the rest of the process.\ntype AllowedFailure interface {\n\tIsAllowedFailure() bool\n}\n\n// Dependent — service manager topologically sorts start order;\n// services with no deps start first.\ntype Dependent interface {\n\tDependencies() []string // names of other services in this process\n}\n\n// ReadyNotifier — service manager waits for this channel to close\n// before starting anything that depends on this service.\ntype ReadyNotifier interface {\n\tReady() <-chan struct{}\n}\n\n// Commander — exposes CLI subcommands under the service's own\n// namespace: ./app <servicename> <subcommand>. Only that service\n// gets instantiated when its command runs.\ntype Commander interface {\n\tCommands() []*cobra.Command\n}"},{"language":"go","snippet":"// cmd/init.go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/yourname/yourproject/internal/app\"\n)\n\nfunc init() {\n\tapp.GetInstance().OnPreRun(func(ctx context.Context) {\n\t\t// runs before any service starts\n\t})\n\n\tapp.GetInstance().OnPostStop(func(ctx context.Context) {\n\t\t// runs after all services have stopped\n\t})\n}"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: servicepack\ndescription: Build a Go service on psyb0t/servicepack — clone-and-own framework (not a `go get` library) providing a Service interface (Name/Run/Stop), a singleton ServiceManager that runs services concurrently with dependency-ordered topological start, automatic retry (Retryable), non-fatal failures (AllowedFailure), readiness gating (ReadyNotifier), per-service CLI subcommands (Commander), an App singleton with OnPreRun/OnPostStop lifecycle hooks, gofindimpl-based service auto-discovery codegen, ctxscope/slogging structured logging, and a graceful-shutdown Runner. Import path github.com/psyb0t/servicepack. Use when the user wants related Go services debugged together locally, then deployed as one binary or split into separate microservices, with retry/dependency/readiness semantics.\nhomepage: https://github.com/psyb0t/servicepack\nuser-invocable: true\npermissions:\n  filesystem:\n    read:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefile*\"\n      - \"README.md\"\n    write:\n      - \"internal/pkg/services/**\"\n      - \"cmd/init.go\"\n      - \"cmd/commands.go\"\n  shell:\n    - \"make dev-image\"\n    - \"make service NAME=*\"\n    - \"make service-registration\"\n    - \"make build\"\n    - \"make test\"\n    - \"make test-integration\"\n    - \"make test-coverage\"\n    - \"make lint\"\n    - \"make format\"\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    requires:\n      bins:\n        - docker\n---\n\n# servicepack — build a Go service on the framework\n\n`servicepack` runs your Go services concurrently without you hand-rolling a supervisor loop. It is NOT a package you `go get` into an existing project — it's a **template repo you clone and make your own**, then you add services under `internal/pkg/services/`. This skill teaches you to build a service with it, not to run it as a standalone server (there's nothing to run until you write a service).\n\n## Security & safety\n\nservicepack is source code you compile into your own binary — it has no runtime surface of its own, no network listener, no daemon to secure. Once cloned it's just Go files in your repo; whatever surface your SERVICE exposes (HTTP, gRPC, DB connections) is on you, same as any Go code you'd write by hand. The only things worth flagging:\n\n- `make own MODNAME=...` rewrites `go.mod`, nukes `.git`, and re-inits — irreversible on the clone, run it once at the start.\n- `internal/app/`, `internal/pkg/service-manager/`, `pkg/runner/`, and `cmd/main.go` are framework-owned files that `make servicepack-update` overwrites — never hand-edit them (see \"Framework boundaries\" below).\n- No secrets, tokens, or credentials live in the framework itself. Your services' env vars are your own to manage (`gonfiguration`, not `os.Getenv`).\n\n## When to use\n\n- Starting a new Go service/daemon that needs to run one or more long-lived workers concurrently, with clean shutdown on SIGINT/SIGTERM.\n- You need retry-on-failure, non-fatal (\"allowed failure\") services, dependency-ordered startup, or readiness gating bet"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn79dhvmpjng4rp2jjk8k0v5xx80ccbk\",\n  \"slug\": \"servicepack\",\n  \"version\": \"1.9.4\",\n  \"publishedAt\": 1791593355196\n}"},{"path":"references/setup.md","content":"# Setup\n\n## This is a template, not a `go get` dependency\n\nThere is no `go get github.com/psyb0t/servicepack` step. `internal/`, `cmd/main.go`, and `pkg/runner/` are not designed to be imported into an existing module — they're the skeleton of YOUR module. You clone the repo, rewrite the module path, and build on top.\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake own MODNAME=github.com/yourname/yourproject\n```\n\n`make own` (irreversible on this clone):\n\n- nukes `.git`, runs `git init` fresh on `main`\n- rewrites the module name everywhere (`go.mod` + every import)\n- replaces `README.md` with a stub for your project name\n- runs the Docker-backed dependency and registration targets\n- creates the initial commit\n\nYour binary name is derived from `go.mod`'s module name (last path segment) at build time — no separate app-name config.\n\n## Go and Docker requirements\n\n`go.mod` declares Go `1.26.9`. The supported Make targets run that project\ntoolchain in the development/build Docker images, so `make own` does not reject\nan older host Go installation. Docker must be available for normal dependency,\ngeneration, build, lint, and test work. Keep the `go.mod` declaration aligned\nwith the framework version you are updating to; do not treat an arbitrary host\nGo version as the project contract.\n\n## Module path / import path\n\nFramework code imports as `github.com/psyb0t/servicepack/...` before you run `make own`; afterwards every import is rewritten to your module path, e.g. `github.com/yourname/yourproject/internal/app`.\n\nCore framework packages you'll reference directly:\n\n| Package | Purpose |\n|---|---|\n| `<your-module>/internal/app` | `App` singleton — `GetInstance()`, `OnPreRun`, `OnPostStop` |\n| `<your-module>/internal/pkg/service-manager` (import alias `servicemanager`) | `Service`/`Retryable`/`AllowedFailure`/`Dependent`/`ReadyNotifier`/`Commander` interfaces, `GetInstance()` |\n| `<your-module>/pkg/runner` | `runner.RunContext(ctx, runnable)` — signal handling + graceful shutdown with a caller-supplied parent context; `runner.Run(runnable)` remains the background-context compatibility helper |\n| `<your-module>/internal/pkg/services` | generated `services.Init()` (via `services.gen.go`) |\n\nThird-party deps pulled in by the framework itself (already in `go.mod`, vendored):\n\n- `github.com/psyb0t/ctxerrors` — error wrapping with file/line/function capture\n- `github.com/psyb0t/ctxscope` — contextual structured logging\n- `github.com/psyb0t/goenv` — `dev`/`prod` environment detection\n- `github.com/psyb0t/gonfiguration` — env-var config parsing via struct tags\n- `github.com/psyb0t/slogging` — `log/slog` handler wiring (`slogging/slogconf`)\n- `github.com/spf13/cobra` — CLI command tree\n\n## Skip the clone entirely — try it in Docker first\n\n```bash\ngit clone https://github.com/psyb0t/servicepack\ncd servicepack\nmake run-dev\n```\n\nBuilds a dev image and runs the shipped example services (`hello-world`, `example-database`, `example-api`, `exam"},{"path":"skill-card.md","content":"## Description:\n\nGuides developers in building Go services with the clone-and-own servicepack template, including concurrent workers, startup dependencies, retries, and graceful shutdown.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[psyb0t](https://clawhub.ai/user/psyb0t)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers use this skill to scaffold and maintain Go applications with multiple long-running services, from local development through deployment as one binary or separate services.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The documented make own step rewrites project files and resets Git history in the clone.\n\nMitigation: Run it only in a fresh clone after confirming the target module path.\n\nRisk: Build and update commands can change project files or run tools against sensitive code.\n\nMitigation: Review Make targets before running them; keep custom service code outside framework-owned files that updates overwrite.\n\n## Reference(s):\n\n- [servicepack setup guide](references/setup.md)\n- [servicepack project homepage](https://github.com/psyb0t/servicepack)\n- [servicepack ClawHub release](https://clawhub.ai/psyb0t/skills/servicepack)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Code, Shell commands, Configuration instructions]\n\n**Output Format:** [Markdown with Go and shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guidance covers project setup, service implementations, and build commands.]\n\n## Skill Version(s):\n\n1.9.4 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1638,"uniquenessScore":43,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T03:33:02.177Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T07:47:37.429Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}