You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pkg/api/ is the HTTP surface package — routing, middleware, server startup, and route-group wiring. Everything else in it names its role (server.go, routes_*.go, route_group_*.go, middleware_setup.go, startup.go, buildinfo.go, config.go). The single outlier is gpu_utilization_worker.go, a self-contained periodic background worker with its own goroutine, Start()/Stop(), DCGM scraping, snapshot retention/cleanup, and threshold-alert emission.
Concrete evidence that it does not belong in pkg/api:
Zero HTTP surface. The file exposes no http.Handler, no route registration, and no middleware — only NewGPUUtilizationWorker, Start, Stop, collectUtilization, scrapeDCGMPerCluster, collectForReservation, and cleanupOldSnapshots (7 functions total).
Only two intra-package callers.pkg/api/server.go:272 constructs it (server.background.gpuUtilWorker = NewGPUUtilizationWorker(...)) and pkg/api/server_runtime.go:39 types the field. No other file in pkg/api touches it.
Its own dependency graph is broader than pkg/api's. It imports pkg/gpu, pkg/k8s, pkg/models, pkg/notifications, pkg/store, pkg/safego, and pkg/api/metrics. Of those, only pkg/api/metrics is in-package; the other six are cross-cutting domains a worker naturally reaches, but a routing file should not.
Pattern precedent already exists in this repo. The identical shape in pkg/agent was resolved by extraction: PredictionWorker, InsightWorker, DeviceTracker, and MetricsHistory were carved out of pkg/agent/*.go into pkg/agent/workers/ ([architect] refactor: extract background workers to pkg/agent/workers #18271, bead 0075a94d-1ec). The GPU worker is the mirror-image case in pkg/api, with the same size class (413 LOC production + 484 LOC tests, comparable to workers.PredictionWorker).
Test-package confusion. Any change to pkg/api/server.go or route wiring re-triggers pkg/api/gpu_utilization_worker_test.go (484 LOC, DCGM scrape mocks and 90-day retention scenarios) even when nothing about the worker changed, because Go's test unit is the package.
Blast-radius for pkg/api compile errors. Any breakage in the worker's dependency chain (pkg/gpu, pkg/notifications, pkg/store) breaks the whole HTTP package's build, blocking route-wiring iteration.
Discovery friction. New contributors looking for periodic-worker patterns grep pkg/agent/workers/ and pkg/gpu/, not pkg/api/. The precedent set by the prior extraction is silently violated by this one file.
pkg/api/metrics back-reference is the only blocker to a cleaner move to pkg/gpu/. If the metrics counter emission is dependency-injected (WorkerMetrics interface with RecordUtilization, RecordAlert methods, satisfied by pkg/api/metrics), the worker can live in pkg/gpu alongside the scraper it wraps. Without that injection, it stays trapped in pkg/api.
Recommendation
Two-step extraction, either step landable independently:
Step A — cheap move, no cycle risk (recommended first):
Create pkg/api/gpuworker/ package.
Move pkg/api/gpu_utilization_worker.go → pkg/api/gpuworker/worker.go, renaming the exported types from GPUUtilizationWorker/NewGPUUtilizationWorker to gpuworker.Worker/gpuworker.New.
Move pkg/api/gpu_utilization_worker_test.go → pkg/api/gpuworker/worker_test.go (adjust package and any unexported-symbol accesses).
Update pkg/api/server.go:272 and pkg/api/server_runtime.go:39 to use the new package name.
This mirrors the pkg/agent/workers extraction (#18271) exactly and does not touch dependency direction.
Step B — cross-package move (do only after Step A stabilises):
Introduce a Metrics interface in pkg/api/gpuworker covering the two consolemetrics calls the worker makes (grep consolemetrics\. inside the file to enumerate).
Move pkg/api/gpuworker/ → pkg/gpu/utilworker/ with the interface constructor-injected from pkg/api/server.go (which retains the concrete consolemetrics import). This preserves the pkg/api → pkg/gpu direction the pkg/gpu/scraper.go header comment mandates.
No workflow (.github/workflows/) changes required for either step — both stay inside pkg/.
Related historical extractions (for reviewer context):
Architecture Finding
Type: tech-debt / package boundary (background worker in HTTP-wiring package)
Affected area:
pkg/api/gpu_utilization_worker.go(413 LOC) +pkg/api/gpu_utilization_worker_test.go(484 LOC)pkg/api/is the HTTP surface package — routing, middleware, server startup, and route-group wiring. Everything else in it names its role (server.go,routes_*.go,route_group_*.go,middleware_setup.go,startup.go,buildinfo.go,config.go). The single outlier isgpu_utilization_worker.go, a self-contained periodic background worker with its own goroutine,Start()/Stop(), DCGM scraping, snapshot retention/cleanup, and threshold-alert emission.Concrete evidence that it does not belong in
pkg/api:http.Handler, no route registration, and no middleware — onlyNewGPUUtilizationWorker,Start,Stop,collectUtilization,scrapeDCGMPerCluster,collectForReservation, andcleanupOldSnapshots(7 functions total).pkg/api/server.go:272constructs it (server.background.gpuUtilWorker = NewGPUUtilizationWorker(...)) andpkg/api/server_runtime.go:39types the field. No other file inpkg/apitouches it.pkg/api's. It importspkg/gpu,pkg/k8s,pkg/models,pkg/notifications,pkg/store,pkg/safego, andpkg/api/metrics. Of those, onlypkg/api/metricsis in-package; the other six are cross-cutting domains a worker naturally reaches, but a routing file should not.pkg/agentwas resolved by extraction:PredictionWorker,InsightWorker,DeviceTracker, andMetricsHistorywere carved out ofpkg/agent/*.gointopkg/agent/workers/([architect] refactor: extract background workers to pkg/agent/workers #18271, bead0075a94d-1ec). The GPU worker is the mirror-image case inpkg/api, with the same size class (413 LOC production + 484 LOC tests, comparable to workers.PredictionWorker).pkg/gpu/scraper.goexplicitly warns about direction: "extracted from pkg/agent to break the pkg/api → pkg/agent import dependency ([architect] pkg/api directly imports pkg/agent — violates binary independence, extract shared utilities #17131, [architect] Extract DCGM types from pkg/agent to pkg/gpu #17640)". Keeping a worker underpkg/apirisks re-introducing the same directional coupling the prior extraction was designed to prevent.Impact
pkg/api/server.goor route wiring re-triggerspkg/api/gpu_utilization_worker_test.go(484 LOC, DCGM scrape mocks and 90-day retention scenarios) even when nothing about the worker changed, because Go's test unit is the package.pkg/apicompile errors. Any breakage in the worker's dependency chain (pkg/gpu,pkg/notifications,pkg/store) breaks the whole HTTP package's build, blocking route-wiring iteration.pkg/agent/workers/andpkg/gpu/, notpkg/api/. The precedent set by the prior extraction is silently violated by this one file.pkg/api/metricsback-reference is the only blocker to a cleaner move topkg/gpu/. If the metrics counter emission is dependency-injected (WorkerMetricsinterface withRecordUtilization,RecordAlertmethods, satisfied bypkg/api/metrics), the worker can live inpkg/gpualongside the scraper it wraps. Without that injection, it stays trapped inpkg/api.Recommendation
Two-step extraction, either step landable independently:
Step A — cheap move, no cycle risk (recommended first):
pkg/api/gpuworker/package.pkg/api/gpu_utilization_worker.go→pkg/api/gpuworker/worker.go, renaming the exported types fromGPUUtilizationWorker/NewGPUUtilizationWorkertogpuworker.Worker/gpuworker.New.pkg/api/gpu_utilization_worker_test.go→pkg/api/gpuworker/worker_test.go(adjust package and any unexported-symbol accesses).pkg/api/server.go:272andpkg/api/server_runtime.go:39to use the new package name.pkg/agent/workersextraction (#18271) exactly and does not touch dependency direction.Step B — cross-package move (do only after Step A stabilises):
Metricsinterface inpkg/api/gpuworkercovering the twoconsolemetricscalls the worker makes (grepconsolemetrics\.inside the file to enumerate).pkg/api/gpuworker/→pkg/gpu/utilworker/with the interface constructor-injected frompkg/api/server.go(which retains the concreteconsolemetricsimport). This preserves thepkg/api → pkg/gpudirection thepkg/gpu/scraper.goheader comment mandates.No workflow (
.github/workflows/) changes required for either step — both stay insidepkg/.Related historical extractions (for reviewer context):
#18271/ bead0075a94d-1ec:pkg/agent/workers(PredictionWorker, InsightWorker, DeviceTracker, MetricsHistory)#18310/ bead924f0d4a-91e:pkg/agent/tokentracker#18125/ bead0f137940-e25:pkg/agent/updater#17131/#17640:pkg/gpusplit frompkg/agentto breakpkg/api → pkg/agentdirectionFiled by architect agent (ACMM L6 — full mode)
🐝 Hive Agent:
architect| Instance:hosted-kubestellar-console-4vkt| SHA:unknown— hive: agent=architect backend=copilot model=claude-opus-4.7 copilot=1.0.88