From ed60059e80479ef3c09e765839a20932735d2200 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Thu, 14 May 2026 03:00:16 +0000 Subject: [PATCH] =?UTF-8?q?fix(lint):=20ST1021=20=E2=80=94=20lead=20Jitter?= =?UTF-8?q?edTicker=20docstring=20with=20the=20type=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI run #25838658130 against the Phase 6 commit (8191b1ee) failed the golangci-lint step: internal/scheduler/jitter.go:11:1: ST1021: comment on exported type JitteredTicker should be of the form "JitteredTicker ..." (with optional leading article) (staticcheck) The Phase 6 SCALE-M5 commit led the doc block with the Phase 6 backstory ("Phase 6 SCALE-M5 closure (2026-05-14): bounded-jitter wrapper ...") rather than the type name. Pre-commit verification ran `go test` + `go vet` but not staticcheck — same gap CLAUDE.md already calls out in the "make verify" rule. The lint set in .golangci.yml enables `staticcheck` with `checks: ["all", ...]` which includes ST1021; the project's `gofmt + go vet + go test` trio does NOT include it. Restructured the comment so the first line leads with `JitteredTicker is ...` (godoc-canonical form) and demoted the Phase 6 backstory to a trailing paragraph. Same content, same SLO-preservation explanation, same pre-Phase-6 contrast — just reordered so godoc renders the documentation correctly and staticcheck stays clean. The local-staticcheck-binding-rule from the lockfile-regen and fail-closed-pairing hotfixes applies here too: any future commit that introduces an exported Go symbol must include the symbol name in the first word of its doc block. Adding this to the "pre-commit pattern lessons" list in the audit's Tasks-Deferred table along with the Phase 7 update. Verification: staticcheck -checks all,- \ ./internal/scheduler/... → clean go test ./internal/scheduler/... -count=1 → ok (9.6s) gofmt -l internal/scheduler/jitter.go → clean Closes: CI run 25838658130 lint failure on master@8191b1ee --- internal/scheduler/jitter.go | 38 +++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/internal/scheduler/jitter.go b/internal/scheduler/jitter.go index c2ad9ca..2bb2d5f 100644 --- a/internal/scheduler/jitter.go +++ b/internal/scheduler/jitter.go @@ -8,28 +8,26 @@ import ( "time" ) -// Phase 6 SCALE-M5 closure (2026-05-14): bounded-jitter wrapper -// around time.Timer to spread scheduler-loop tick co-fires. +// JitteredTicker is a bounded-jitter wrapper around time.Timer that +// fires on C once per interval ± jitterPct, with the jitter drawn +// fresh on every tick. The base interval is the same as a bare +// time.NewTicker; only the per-tick envelope changes. This preserves +// every loop's expected SLO (a renewal scan still runs ~once per +// hour) while breaking up the co-fire pattern that bare tickers +// produce when multiple loops share a nominal cadence. // -// Pre-Phase-6 the 15 scheduler loops in scheduler.go each used a -// bare time.NewTicker(interval). When multiple loops share a -// nominal cadence (e.g. several loops on a 1h interval), they -// co-fire at the same wall-clock boundary post-server-start, -// producing visible CPU + DB spikes at every hour boundary. The -// renewal scan + the agent health check + the digest preview all -// firing within milliseconds of each other on a freshly-booted -// server can saturate the connection pool until they complete. +// Stop must be called by the caller (typically via defer) to release +// the goroutine. After Stop, the C channel is closed. // -// JitteredTicker replaces the bare time.NewTicker with a goroutine -// that fires C once per interval ± jitterPct, drawn fresh on every -// tick. The base interval is the same as before; only the per-tick -// envelope changes. This preserves every loop's expected SLO (a -// renewal scan still runs ~once per hour) while breaking up the -// co-fire pattern. -// -// JitteredTicker.Stop() must be called by the caller (typically via -// defer) to release the goroutine. After Stop, the C channel is -// closed. +// Phase 6 SCALE-M5 (2026-05-14) introduced this wrapper. Pre-Phase-6 +// the 15 scheduler loops in scheduler.go each used a bare +// time.NewTicker(interval); when multiple loops shared a nominal +// cadence (e.g. several loops on a 1h interval), they co-fired at +// the same wall-clock boundary post-server-start, producing visible +// CPU + DB spikes at every hour boundary. The renewal scan + the +// agent health check + the digest preview all firing within +// milliseconds of each other on a freshly-booted server could +// saturate the connection pool until they completed. type JitteredTicker struct { // C is the channel a tick fires on. Read this in the loop's // select{} the same way you'd read time.Ticker.C.