Split product analytics from observability in pkg/telemetry¶
- Authors
- Matt Cockayne, Claude Opus 4.8 (AI drafting assistant)
- Date
- 12 July 2026
- Status
- REFRAMED 2026-07-16 (observability core extracted; scope narrowed to the analytics remainder — see §0)
- Builds on
2026-07-10-telemetry-props-decoupling.md,2026-07-07-config-section-adapters-for-extraction.md,2026-06-01-otel-observability.md- Related
2026-07-07-package-extraction-report.md
0. Reframe (2026-07-16) — observability extracted; this spec is now analytics-only¶
This spec was written before the observability extraction and framed an in-tree split as the prerequisite to extracting either concern. That work has since happened by a different route, so the spec is reframed:
- Observability's core is already extracted.
otelcore+logs+metrics+tracingnow live in the standalonegitlab.com/phpboyscout/go/observabilityv0.1.0(Phase 2, spec2026-07-16-observability-module-extraction.md, a full repoint). What remains in rootpkg/telemetryfor observability is only the thin GTB wiring —Setup/SetupFromProps,ObservabilitySettings, and the relocated config-key adapter (observability_otel_config.go) — which consumes the module. That wiring is GTB composition and is not a further module candidate. - The two concerns are already import-separated. Measured on the current tree, the
observability wiring files import no analytics symbol (
Collector,Backend, spill, machine identity, deletion), and vice versa. Thetest/architectureno-cross-import guard this spec proposed (§5.1/§6) is therefore effectively already satisfied — the split-as-prerequisite is done. No separate in-tree-split effort is needed. - Only the analytics concern remains to extract. The live target is root
pkg/telemetry's product-analytics product — theCollector, buffering/spill, deletion, machine identity, data-dir, and the vendor backends — into an analytics module (+posthog/datadogbackend modules). Everything below now reads as the plan for that extraction.
0.1 Analytics is blocked — it is NOT the next extraction¶
The analytics concern's current GTB coupling (production imports), post the
browser/redact/controls extractions:
| Import | Count | Status |
|---|---|---|
pkg/telemetrytypes |
5 | shared types — move with analytics or keep as a shared dep |
pkg/telemetry (self) |
5 | intra-package (posthog/datadog → the Backend interface) |
pkg/http |
4 | blocker — analytics ships events over the hardened HTTP client; needs go/httpclient (Phase 4) or the transport extraction first |
pkg/props |
2 | GTB composition — handled via *FromProps adapters, not a blocker |
pkg/logger |
2 | the *slog.Logger seam |
pkg/osinfo |
1 | blocker — machine identity; needs osinfo extracted or the dep severed |
So analytics extraction is gated behind Phase 4 (go/httpclient) at minimum (so the
backends can dial via go/httpclient instead of pkg/http), plus resolving pkg/osinfo.
It is re-sequenced after the transport stack, not next. The immediate next step in
the extraction program is Phase 3 (go/transportmw) per the
transport-stack plan.
The remainder of this document (the app-metadata/consent seam, redaction-at-ingestion, the backend contract, and the OTel-log-backend-as-analytics-transport classification) stands as the design for the eventual analytics extraction, to be picked up once its blockers clear.
1. Context (historical — pre-reframe)¶
pkg/telemetry grew into two distinct products that happen to share a package:
- Product analytics — GTB's opt-in, informed-consent CLI usage pipeline: the
Collector, event buffering and on-disk spill, redaction at ingestion, machine identity, deletion-request handling, data-directory resolution, and the vendor backends (posthog,datadog, the OTel-log backend). - Observability — service-side, implied-consent OpenTelemetry setup for
long-running GTB services, built on the reusable
otelcore/logs/metrics/tracingsubpackages and exposed to GTB throughObservabilitySettingsand theobservability_config_adapter.go/datadir_config_adapter.goseams.
The 2026-06-01-otel-observability.md spec
deliberately co-located these under one package with two consent models, and
that was the right call at the time. The
package extraction report
now flags the coupling as the blocker for extraction:
Do not extract root telemetry before splitting observability from product analytics.
The 2026-07-10-telemetry-props-decoupling.md
spec severed the last pkg/props type import from root telemetry (relocating
EventType / DeliveryMode into pkg/telemetrytypes) and explicitly scoped
itself to type coupling only, naming this larger split as the follow-on
effort. The config-section-adapters
spec likewise confined root telemetry's config reads to adapter files and named
this split as the remaining architectural work. This spec is that work.
2. Problem statement¶
The two concerns are entangled at the package level in ways that prevent either from being extracted cleanly:
- Shared package, divergent audiences. Analytics serves the vendor (informed consent, off by default); observability serves the operator (implied consent, on when a service opts in). A single import surface forces every consumer of one to link the other.
- Backends bridge both. The vendor backends were designed as analytics sinks but the OTel-log backend blurs the line between an analytics transport and an observability signal.
- Different extraction destinations. The report proposes
gitlab.com/phpboyscout/observabilityfor the OTel subpackages, while product analytics is a lower-priority, heavier-coupling candidate (it importspkg/browser,pkg/controls,pkg/http,pkg/osinfo,pkg/redact). They cannot share an extraction timeline. - Config seams already diverge. Observability config resolves through
ObservabilitySettings+ override-precedence detection; analytics config resolves through consent/source-awareIsSet/Haschecks. These are two different adapter shapes living in one package.
3. Goals¶
- Establish a clean intra-package boundary between the analytics concern and the observability concern, so each can later move to its own module without a second round of boundary surgery.
- Make the observability concern depend only on the OTel subpackages and
explicit options — no dependency on the analytics
Collector, spill, machine identity, or deletion machinery. - Make the analytics concern depend on explicit app-metadata / consent interfaces rather than reaching into observability setup.
- Preserve both existing consent models and every current
telemetry.*config key, precedence, and reload semantic. This is a refactor, not a behaviour change. - Keep the vendor backends as optional adapters attached to the analytics concern, with the OTel-log backend's role explicitly classified.
- Leave GTB wiring (
pkg/propsadapters, data-dir resolution, consent prompts, config persistence) in the GTB composition layer, matching the pattern the config-section-adapters spec established. - Produce a sequencing plan: observability extractable first, analytics later, backends after analytics contracts.
4. Non-goals¶
- Extracting either concern into a module in this spec. This spec delivers
the in-tree split only. The actual
gitlab.com/phpboyscout/observabilitymodule and any analytics module are separate follow-up specs that consume the seams defined here. This keeps the diff reviewable and the risk contained. - Changing consent behaviour, config keys, or the on-disk spill format.
- Reworking the
otelcore/logs/metrics/tracingsubpackages. They are already the clean observability core; this spec organises root telemetry around them, it does not touch them beyond import adjustments. - Touching
pkg/telemetrytypes(already resolved upstream).
5. Design¶
5.1 Two internal concerns, one package (for now)¶
Root pkg/telemetry is reorganised so its files, types, and constructors group
unambiguously under one of two concerns, with no import edges from
observability code to analytics code (and vice versa) inside the package. The
split is by responsibility, established first as file/type grouping and a
boundary guard, so that a later git mv into a module is close to mechanical.
Analytics concern (the Collector product):
telemetry.go,backend.go,backend_otel.go,deletion.go,spill.go,machine*.go,datadir.go,datadir_config_adapter.go- vendor backends under
posthog/,datadog/
Observability concern (service OTel setup):
observability.go,observability_settings.go,observability_config_adapter.go- depends on
otelcore/logs/metrics/tracingonly
5.2 Package layout options¶
Two candidate shapes; the spec review must choose one (see §11 Open Questions):
Option A — subpackage split now. Introduce pkg/telemetry/analytics and
pkg/telemetry/observability (or promote observability to a top-level
pkg/observability) in this spec, moving files immediately. Pro: the module
boundary is real and guard-enforced today; extraction becomes git mv of a
whole directory. Con: a larger, more disruptive diff now, and every downstream
import of pkg/telemetry symbols shifts (pre-1.0, acceptable, but noisy).
Option B — grouping + guard now, physical move at extraction. Keep one
package, enforce the no-cross-import rule with a test/architecture guard over
file-level symbol groups, and defer the directory move to each concern's
extraction spec. Pro: minimal churn now, downstream imports unchanged. Con: the
boundary is convention-plus-test rather than a hard package wall until
extraction.
Given the pre-1.0 posture and that the config-section-adapters spec already proved the "co-located adapter + guard test" approach, Option B is the recommended default, with Option A reserved for the observability half if it extracts imminently.
5.3 Dependency-inverted seams¶
Whichever layout is chosen, the following seams are introduced so neither concern reaches into GTB or into the other:
- App metadata / consent interface for analytics — replaces any remaining
implicit reliance on GTB-owned values (tool name, version, machine ID, data
dir, consent state) with a small explicit interface populated by a GTB
adapter (
*FromProps). Extends thetelemetrytypesdecoupling. - Observability options — observability setup already takes
ObservabilitySettings; confirm it needs nothing from the analytics side and thatObservabilitySettingsFromProps/SetupFromPropsremain the only GTB adapters. - Backend contract — the analytics
Backendinterface stays the injection point forposthog/datadog; classify the OTel-log backend as an analytics transport (not an observability signal) so it travels with the analytics concern.
5.4 Redaction boundary¶
Confirm redaction happens at event ingestion in the analytics concern before any value reaches a backend, so a future standalone analytics module is safe by construction (the report calls this out explicitly). Add a test asserting the ingestion boundary redacts.
6. Testing strategy¶
- Import-boundary guard (
test/architecture): assert no observability symbol/file imports an analytics symbol/file and vice versa, mirroring the existingpkg/props/pkg/loggerguard from the config-section-adapters spec. - Behaviour-preservation tests: existing
telemetry_test.go,observability_test.go,backend_test.go,spill_test.go, and theobservability_e2e_test.gosuites must pass unchanged (aside from import paths) — the split is behaviour-neutral. - Redaction-at-ingestion test as per §5.4.
- Consent-model tests: assert informed-consent (analytics, off by default) and implied-consent (observability, service opt-in) paths are independently exercisable after the split.
- No new BDD scenarios: no user-visible CLI workflow changes. Record in the implementation notes that unit/integration coverage is sufficient.
7. Migration & compatibility¶
- Pre-1.0: import-path churn from a subpackage split (Option A) or symbol moves
is an acceptable minor break; add
docs/migration/notes for any moved exported symbol. - Preserve every
telemetry.*config key, precedence, and reload semantic. pkg/propsretains any aliases needed so downstream tools are unaffected, consistent with the telemetrytypes approach.
8. Sequencing (reframed 2026-07-16)¶
In-tree split + guard + seams— not needed: the concerns are already import-separated (§0).Observability extraction— DONE:go/observabilityv0.1.0 (the OTel subpackages moved; GTB consumes it viaSetupFromProps+ the relocated config adapter).- Transport stack first — Phase 3
go/transportmw, Phase 4go/httpclient+go/grpcclient(per the transport plan). Analytics extraction depends ongo/httpclient(its backends ship events over HTTP), so it waits for Phase 4. - Resolve
pkg/osinfo— extract it or sever the machine-identity dependency. - Analytics contracts (separate spec): finalise the app-metadata/consent
interface (§5.3), classify the OTel-log backend as an analytics transport (§5.3),
confirm redaction-at-ingestion (§5.4), then extract the analytics
Collectorinto an analytics module — diallinggo/httpclient, notpkg/http. - Backend modules (separate spec): move
posthog/datadogas optional adapter modules once analytics contracts are stable, each accepting a standard*http.Client(orgo/httpclient).
9. Future considerations¶
- A shared
gitlab.com/phpboyscout/observabilitycould become the instrumentation dependency for extractedhttp/grpcmodules. - The analytics module may warrant a
gtb--prefixed name if it stays GTB-flavoured (consent UX, deletion requests) rather than fully generic.
10. Implementation phases¶
- Phase 1 — Classify every root-telemetry file/type under exactly one
concern; write the
test/architectureboundary guard (failing first). - Phase 2 — Introduce the app-metadata/consent seam for analytics; move any
residual GTB reach-through behind
*FromPropsadapters. - Phase 3 — Sever cross-concern imports until the guard passes; classify the OTel-log backend; add the redaction-at-ingestion test.
- Phase 4 — (Option A only) physically split into subpackages and update imports; otherwise finalise grouping.
- Phase 5 — Docs: update the telemetry component pages,
doc.go, the extraction report, and add migration notes for any moved symbols.
11. Open questions¶
- Layout: Option A (subpackage split now) vs Option B (grouping + guard, move at extraction). Recommended B; confirm.
- Observability destination: promote to a top-level
pkg/observabilitybefore extraction, or keep it underpkg/telemetry/observabilityuntil the module move? - OTel-log backend classification: analytics transport (travels with the
Collector) or an observability signal? Recommended: analytics transport. - Analytics module naming: generic vs
gtb--prefixed — defer to the analytics extraction spec, or decide now to shape the seam?