Handover: config migration (config-section-adapters)¶
This note hands off the refactor/config-section-adapters branch (MR !200) to a
follow-up session. It records what is done, the patterns to reuse, and
the remaining config-migration work.
Status¶
- slog-first logging (
0116-slog-first-extraction-seams, APPROVED): COMPLETE.logger.Loggeris now a*slog.Loggermirror, and every extraction-candidate package (chat,controls,config,http,grpc,gateway,telemetry+datadog/posthog,errorhandling) has been migrated offpkg/loggerto*slog.Logger. Only GTB adapter files (*config_adapter.go,*FromProps) and the composition layer (pkg/cmd,pkg/setup,pkg/props,pkg/docs,pkg/utils) still importpkg/logger. - config-section-adapters (
0115-config-section-adapters-for-extraction, IN PROGRESS): first wave DONE (chat,tls,http,grpc,gateway,telemetry/otelcore,vcs+ providers +repo). Later waves remain (below).
Patterns to reuse¶
Logger boundary¶
- Packages accept
*slog.Logger; a nil logger resolves toslog.New(slog.DiscardHandler)(quiet by default, the app wires logging). - GTB adapters convert
props.Logger(thelogger.Loggerinterface) to*slog.Loggerwithlogger.ToSlog(props.Logger)at the boundary.ToSlogis nil-safe, returns a*slog.Loggerunchanged, else builds one over the logger'sHandler()(so buffer capture is preserved in tests). - Runtime level/format live only in the GTB root via
logger.SetLevel/logger.SetFormatterhelpers (the optionalLeveller/Reformatterinterfaces). A plain*slog.Loggerno-ops for them. - Tests: wrap logger args in
logger.ToSlog(...); replace custom capture loggers withlogger.NewCaptureHandler()+slog.New(capture)and assert oncapture.Records()/Messages()/Contains().
Config typed-struct adapters¶
- Extracted packages own typed config structs (
mapstructuretags). GTB'sconfig_adapter.godoes the config reads:config.UnmarshalSection[T](one shot) orconfig.ObserveSection[T](long-lived, reload-aware), merges package defaults, validates, resolves credentials. - Choose the mechanism by shape (see the spec's §12 note):
UnmarshalSectionfor self-contained sections (chat,http/grpcserver settings).- Narrow lookup interface (
release.Config/vcs.TokenConfig) for provider registries with dynamic, provider-specific keys (vcs). Do NOT force typed decode there; it already imports zero GTB packages. - Manual
IsSet-override for shared↔transport/signal fallback (tls,telemetry/otelcore). - Composition across multiple prefixes (
gateway). config_adapter.gois the GTB adapter file that will NOT travel with the extracted module. Keep allpkg/config+pkg/propscoupling there; package cores stay clean.
Remaining config-migration work¶
Per config-section-adapters §8 (later waves) and §9 Phase 6:
- Packages that currently need no config. Add typed structs only if/when
GTB introduces config-driven behaviour (deferred, not speculative):
authn(API-key/JWT/mTLS config in the transports),output.Config(deferred, output resolves format from the--outputflag, not a config section; adding it now would be dead surface),browser,workspace,forms,changelog,redact,regexutil,openapi,errorhandlingHelpConfig. - Root
pkg/telemetry. Thepkg/propstype coupling is now DONE (0117-telemetry-props-decoupling, IMPLEMENTED):EventType/DeliveryMode+ constants moved to the dependency-freepkg/telemetrytypesleaf;pkg/propskeeps them as type aliases (zero downstream break);pkg/telemetrycore no longer importspkg/props. Still remaining before extraction: the larger product-analytics/consent ↔ observability split (see the extraction report's telemetry section): a separate effort, not a config/props-type concern. - Phase 6, automated import-boundary check: DONE.
test/architecture/boundary_test.gois now a table-driven guard overpkg/propsandpkg/logger, covering all first-wave cores pluspkg/telemetry. No separate CI job is needed,go test ./...(the cicdgo-testMR component) already runstest/architecture; the earlierjust ciworry was moot. - Open questions still deferred: Q1
SectionInConfig(not shipped:SectionExists+IsSetcover the telemetry-consent case); Q5 the extractedpkg/configmodule name (defer to the config-extraction spec). vcs/releasereceived a registry-signature narrowing rather than a full typed adapter (judged adequate since its providers own the typed settings): revisit if/when extracting the releases module.
Gotchas & conventions¶
- Mockery churn:
mockery/just mocksregenerates the wholemocks/tree with import-order churn. After an interface change, regenerate then keep ONLY the affected package's mocks (git checkout/git cleanthe rest). - slog
int→int64: slog normalises integer attributes toint64; tests asserting on captured integer key/values must useassert.EqualValues. contextcheck: request/RPC logging usesl.Log(ctx, level, msg): thread the request/RPC context through, nevercontext.Background().- Adding imports: prefer
goimports -w; ad-hocsedinsertion can misorder the group or leave a stray blank line (rungofmt -wafter). internal/agenttest:TestGoBuildTool_SuccessOnTrivialModulefails witherror obtaining VCS status: exit status 128, an environmental Go build-stamp failure in this sandbox, pre-existing onmainand unrelated to this work. Exclude it:go test $(go list ./... | grep -v internal/agent).
Verification¶
go build ./...
go test $(go list ./... | grep -v internal/agent)
golangci-lint run ./...
go test ./test/architecture/... # props import-boundary guard