Ecosystem fleet maintenance: CI pin coherence, version-coupling mechanism, fan-out discipline, and docs alignment¶
- Authors
- Matt Cockayne, Claude Fable 5 (AI drafting assistant)
- Date
- 2026-07-23
- Status
- DRAFT — pending review
- Related
- architectural review (§ ecosystem), config family re-pin (the two HIGH findings + the credentials-diamond re-pin — excluded here)
1. Context¶
The ecosystem review's structural verdict is good: 51 extracted modules plus GTB form a clean five-layer DAG with zero cycles, zero replace directives, and uniform conventions. Its weakness is propagation: every mechanism keeping the fleet coherent — dependency pins, CI component pins, the core↔provider compatibility matrix — is convention enforced by Renovate-plus-human-merges, and propagation stalls wherever release fan-out is large. The two HIGH findings are handled by the re-pin spec above; this spec batches the remaining MEDIUM and LOW findings into one standing fleet-maintenance workstream so they are tracked, sequenced, and closed.
2. Items¶
2.1 CI/CD component pin management — MEDIUM¶
Problem. The fleet's .gitlab-ci.yml component pins span cicd
v0.22.0–v0.27.0, and the pin correlates with repo creation date — set once,
never bumped. Sixteen modules — the chat family, the transport/client stack,
and most L0 leaves — sit at v0.22.0, five minors behind config-vault's
v0.27.0. Lint-rule and Go-toolchain behaviour therefore differ across
siblings, and the convention that Renovate covers component pins is broken.
Direction. Audit Renovate on the v0.22.0 cohort (running at all? component-pin datasource matched? MRs opened but unmerged?), then drive one fleet-wide bump to the current cicd release. Structurally, manage the cicd component version from a single shared Renovate preset the whole fleet extends — one place rather than 52.
Acceptance. Every repo's component pin is the same current cicd version, and a shared preset demonstrably proposes the next bump fleet-wide.
2.2 Version-coupling mechanism — MEDIUM (mechanism portion)¶
Problem. Core↔provider coupling (chat + 3 providers, forge + 4 providers)
currently holds — matching minors as promised — but the mechanism is a
docs-page compatibility matrix plus a floor-only require. Providers hook in
via RegisterProvider(name, factory) with no API-version guard and no upper
bound, so a pre-1.0-legal breaking minor in a core interface breaks
downstream consumers (loud but misattributed), while a semantic contract
change skews silently. The live instance (the forge credentials diamond) is
scoped in the re-pin spec; this item is the durable mechanism only.
Direction. Make each core release mechanically bump its providers'
require in lockstep — a scripted release-checklist or CI step that opens
the provider dep-bump MRs the moment the core tag exists. Additionally
evaluate an exported CoreAPIVersion constant per core (chat, forge)
asserted by providers at registration, turning silent semantic skew into a
loud, correctly-attributed startup error. Upper bound: Q2.
Acceptance. A chat or forge core release cannot reach its tag without the lockstep bump step having run, and a decision on CoreAPIVersion is recorded.
2.3 Release fan-out discipline — MEDIUM¶
Problem. A change to a low-layer module fans out into 12–23 human-gated releaser-pleaser Release MRs: a config-core change adapters must adopt forces ~20–23 releases; a tls change propagates through four sequential waves (httpclient/grpcclient/localca → forge + 4 providers + transport → transport-metrics/openapi → GTB), 12 releases, each wave blocked on the previous tag. This cost is the mechanism behind the two HIGH findings — propagation stops partway — and it will recur on the next tls change.
Direction. Accept the cost consciously: (a) an explicit additive-only discipline for L0/L1 modules (tls, redact, yamldoc, transit — breaking changes there need positive justification, not just pre-1.0 licence); (b) batch lower-layer changes so one wave carries several; © script the wave ("bump dep, wait for tag, next layer") so the human gate is the merge click, not the orchestration. The strategic alternative — one multi-module repo — is the re-pin spec's Q1 (Q3 below).
Acceptance. The conventions doc states the L0/L1 additive-only expectation and links a wave-release script (or documented procedure) exercised once on a real multi-wave release.
2.4 Documentation alignment — MEDIUM + LOW¶
Problem (AGENTS.md — MEDIUM). GTB's AGENTS.md — the primary steering
file for every AI-agent session in the repo — still documents at least eight
extracted or deleted pkg/ packages as if local (verified at current HEAD):
lines 172–190 describe pkg/controls/, pkg/grpc//pkg/http/,
pkg/errorhandling/, pkg/vcs/, and pkg/forms/ (deleted outright); lines
219–235 mandate routing through pkg/browser, pkg/regexutil, pkg/redact,
and pkg/credentials — all now in go/* modules. Agents steered by this
file will look for, import, or "restore" deliberately clean-broken packages.
Problem (module doc comments — LOW). The same drift exists in
extracted-module doc comments, visible on pkg.go.dev: chat/config.go:10
cites pkg/credentials.Retrieve (extracted), chat/client.go:114 cites
pkg/http (deleted), plus similar references in httpclient and transport.
Direction. One docs pass: rewrite the stale AGENTS.md sections to point
at the module paths (gitlab.com/phpboyscout/go/<name>) and current GTB
glue, then sweep the affected modules' doc comments in the same pass.
Acceptance. A grep of AGENTS.md for the extracted package names returns no stale references, and the named module doc comments no longer reference deleted GTB packages at each module's HEAD.
2.5 Housekeeping — LOW¶
- GTB stale pins. GTB requires
chat/chat-openaiv0.1.0 (both v0.1.2 remote) andcontrolsv0.1.0 (v0.1.1 remote) — patch-level only, but shipped bug fixes are not reaching GTB. Direction: routine Renovate merge sweep. Acceptance: GTBgo.modat the latest tags. - Errors-tier documentation. cockroachdb/errors is a direct dep of 28 modules but deliberately absent from 23 dependency-light ones (the entire config family, aferobilly, redact, yamldoc) — the exemption from the stated "cockroachdb/errors everywhere" rule is recorded nowhere. Direction/Acceptance: one conventions-doc line naming the tier.
- yamldoc go directive. yamldoc is the only module declaring
go 1.26.4; the fleet is on 1.26.5. Direction: bump at yamldoc's next release. Acceptance: yamldoc HEAD declares the fleet's directive. - config-vault release. config-vault exists with CI (newest cicd pin, v0.27.0) but has zero remote tags — consumers cannot use it. Direction: release v0.1.0 (naturally rides the re-pin spec's adapter wave 1) or mark the README WIP. Acceptance: a v0.1.0 tag or a README WIP notice; shared with the re-pin sweep — whichever lands first closes it.
3. Scope & release plan¶
This spec spans many repos but is mostly non-code — CI config, Renovate config, docs — and almost none of it cuts releases on its own:
- CI pin bump (2.1) is
ci:— non-releasing everywhere; land fleet-wide without waiting for release waves. - Coupling mechanism (2.2) is release-checklist/CI tooling in chat and
forge cores — non-releasing; CoreAPIVersion, if adopted, is an additive
featriding each core's next scheduled release. - Fan-out discipline (2.3) is documentation plus a script — no releases.
- Docs pass (2.4): AGENTS.md is a
docs:commit in GTB (non-releasing); module doc-comment fixes ride each module's next routine release. - Housekeeping (2.5): the GTB pin sweep is a normal Renovate dep MR; yamldoc's directive and config-vault's first release ride their next natural tags.
Ordering is loose; only 2.1 (audit → bump) and 2.2 (mechanism → next core release) sequence internally. The rest is a parallelisable standing workstream, not a single MR.
4. Open questions¶
- Q1 — shared Renovate preset vs per-repo config. Is one fleet preset
the right home for the cicd component pin and the
go/*group rules, or do per-repo configs stay, with the preset only covering the component pin? The 2.1 audit should surface actual per-repo divergence before deciding. - Q2 — is CoreAPIVersion worth the mechanism? A registration-time assert makes semantic skew loud, but adds a lockstep-maintained constant and fires only at runtime, after MVS has produced the skewed build. Is the scripted require-bump (pure process) sufficient, and is any upper bound expressible given Go modules' floor-only semantics?
- Q3 — multi-module repo consolidation. Whether the 19 config adapters (and by extension other families) should collapse into one multi-module repo with synchronised tagging is the re-pin spec's Q1 — its answer determines whether 2.3's wave scripting is a stopgap or the permanent mechanism. Decide it there; this spec inherits.