| name | ring:dev-systemplane-migration |
| description | Migrates Lerian Go services from .env/YAML configuration of operational knobs (log levels, feature flags, rate limits, timeouts) to the lib-systemplane runtime config client โ a hot-reloadable plane using Postgres LISTEN/NOTIFY or MongoDB change streams. Wires the standard `make systemplane-ddl` migration-only provisioning pipeline (generator + manifest + drift guard) โ runtime DDL is forbidden in v1.6.0+. Use when adding hot-reloadable runtime configuration or migrating from v4 systemplane (formerly lib-commons/v5/commons/systemplane). Detects deleted v4 residue (Supervisor, BundleFactory, SYSTEMPLANE_* env vars) and runtime DDL anti-patterns. |
Systemplane Migration (lib-systemplane)
When to use
- User requests systemplane integration for a Go service
- User asks to add hot-reloadable runtime configuration
- Task mentions "systemplane", "runtime config", "hot reload", "LISTEN/NOTIFY config", "admin.Mount"
- User asks to migrate from v4 systemplane to v5
Skip when
- Service is not a Go project
- Task does not involve runtime configuration
- Service has zero hot-reloadable knobs (everything is static env-var-at-startup config)
- Task is documentation-only or non-code
You orchestrate. Agents implement. NEVER use Edit/Write/Bash on Go source files.
All code changes go through Task(subagent_type="ring:backend-engineer-golang").
TDD mandatory for all implementation gates (RED โ GREEN โ REFACTOR).
Systemplane Architecture
Three-step lifecycle:
systemplane.NewPostgres / systemplane.NewMongoDB โ construct client (pass open *sql.DB or *mongo.Client)
client.Register(namespace, key, defaultValue, opts...) โ declare every key BEFORE Start
client.Start(ctx) โ begin listening; Get* for reads, OnChange for reactions
Standards reference: WebFetch https://raw.githubusercontent.com/LerianStudio/lib-systemplane/main/doc.go
Canonical import paths:
| Alias | Import Path | Purpose |
|---|
systemplane | github.com/LerianStudio/lib-systemplane | Client, constructors, options, SchemaSQL() / DefaultSeedSQL() provisioning artifacts |
admin | github.com/LerianStudio/lib-systemplane/admin | HTTP admin routes |
systemplanetest | github.com/LerianStudio/lib-systemplane/systemplanetest | Contract test suite |
Legacy paths are DELETED โ do not use lib-commons/v4/... or lib-commons/v5/commons/systemplane (extracted to its own module), and do not use Supervisor, BundleFactory, ApplyBehavior.
Provisioning is migration-only. lib-systemplane publishes systemplane.SchemaSQL() + systemplane.DefaultSeedSQL() as public artifacts and consumers MUST fold them into the service's own SQL migration pipeline via the make systemplane-ddl generator pattern (Gate 3.5). Runtime DDL provisioning is FORBIDDEN โ least-privilege tenant-manager roles cannot run DDL anyway, and any boot-time runSchema-style hook is a CRITICAL deviation.
Scope: operational knobs only โ values that can mutate in-place (log levels, feature flags, rate limits, timeouts, poll intervals).
NOT for settings requiring resource teardown: DSNs, TLS material, listen addresses โ keep in env vars + restart.
Redaction policies for Register:
| Policy | Admin GET returns | Use for |
|---|
RedactNone (default) | Raw value | Log levels, feature flags, non-sensitive |
RedactMask | Type-aware mask | Low-sensitivity values |
RedactFull | null/omitted | Secrets, tokens, API keys |
Any key storing credentials MUST use RedactFull.
Admin mount requires custom authorizer (admin.WithAuthorizer) โ default is DENY-ALL.
Mandatory agent instruction (include in EVERY dispatch):
WebFetch https://raw.githubusercontent.com/LerianStudio/lib-systemplane/main/doc.go.
Use only canonical github.com/LerianStudio/lib-systemplane import paths. v4 packages and the legacy lib-commons/v5/commons/systemplane path no longer exist.
systemplane is for operational knobs only โ not DSNs, TLS, or listen addresses.
Provisioning is migration-only (Gate 3.5). Wire cmd/generate-systemplane-ddl/ + make systemplane-ddl + check-systemplane-ddl-drift + migrations/systemplane_ddl_manifest.json and a bootstrap seam returning []SystemplaneSeedEntry. NEVER call SchemaSQL() at boot. NEVER hand-edit generated migrations/NNN_systemplane_*.sql. See multi-tenant.md ยง27 "Cold-tenant resolution" for the canonical reference.
TDD: RED โ GREEN โ REFACTOR for every gate.
Related Skills
- [[using-lib-systemplane]] โ adoption sweep + API reference for the lib-systemplane module
- [[using-lib-commons]] โ non-observability lib-commons packages (lifecycle, outbox, tenancy)
- [[using-lib-observability]] โ tracing, metrics, logging, assert, runtime, redaction
- For services running in multi-tenant mode (
MULTI_TENANT_ENABLED=true), the consumer-side pattern (registration shape, no-fallback consumer reads, DI interface, make systemplane-ddl provisioning generator, Manager binding when available in the pinned lib version) is documented in dev-team/docs/standards/golang/multi-tenant.md ยง27 "Systemplane in MT mode โ compliance pattern (MANDATORY)". Load that section โ particularly the "Cold-tenant resolution โ make systemplane-ddl generator" subsection โ in addition to the general systemplane architecture below.
Gate Overview
| Gate | Name | Condition | Agent |
|---|
| 0 | Stack Detection + Compliance Audit | Always | Orchestrator |
| 1 | Codebase Analysis (config focus) | Always | ring:codebase-explorer |
| 1.5 | Implementation Preview | Always | ring:visualize |
| 2 | lib-commons v5 Upgrade + v4 Removal | Skip only if v5 in go.mod AND zero v4 imports | ring:backend-engineer-golang |
| 3 | Client Construction + Key Registration | Always | ring:backend-engineer-golang |
| 3.5 | DDL Provisioning (make systemplane-ddl) | Always โ STANDARD provisioning mechanism | ring:backend-engineer-golang |
| 4 | OnChange Subscriptions | Always unless zero hot-reloadable keys (justify) | ring:backend-engineer-golang |
| 5 | Config Bridge | Skip if no Config struct reads need live values | ring:backend-engineer-golang |
| 6 | Admin HTTP Mount + Authorizer | Skip only if service has no admin surface (justify) | ring:backend-engineer-golang |
| 7 | Wiring + Lifecycle + Backward Compat | Always โ NEVER skippable | ring:backend-engineer-golang |
| 8 | Tests | Always | ring:backend-engineer-golang |
| 9 | Code Review | Always | 9 defaults + triggered specialists in parallel |
| 10 | User Validation | Always | User |
| 11 | Activation Guide | Always | Orchestrator |
Gates execute sequentially. Any existing v4 code = NON-COMPLIANT = gates cannot be skipped.
Gate 0: Stack Detection
Orchestrator executes directly. Three phases:
Phase 1: Stack Detection
grep "lib-commons\|lib-systemplane" go.mod
grep -rn "systemplane" internal/
grep -rn "SYSTEMPLANE_" .
grep "postgresql\|postgres" go.mod
grep "mongodb\|mongo" go.mod
grep -rn "fsnotify\|viper.Watch\|Supervisor\|BundleFactory\|ApplyBehavior" internal/
grep -rn "lib-commons/v5/commons/systemplane\|lib-commons/v4" internal/
ls cmd/generate-systemplane-ddl/
ls migrations/systemplane_ddl_manifest.json
grep -n "systemplane-ddl\|check-systemplane-ddl-drift" Makefile
grep -rn "SystemplaneSeedEntries\|buildSystemplaneRegistrations" internal/bootstrap/
grep -rn "runSchema\|SchemaSQL()\|CREATE TABLE.*systemplane_entries" internal/
Phase 2: Compliance Audit (if systemplane code detected)
- No legacy imports (
lib-commons/v4/..., lib-commons/v5/commons/systemplane)
Register called before Start
OnChange wired for hot-reloadable keys
admin.Mount with admin.WithAuthorizer
- Lifecycle:
client.Start(ctx) registered with commons.Launcher
cmd/generate-systemplane-ddl/ generator present AND migrations/systemplane_ddl_manifest.json committed
make systemplane-ddl + check-systemplane-ddl-drift wired in Makefile and called from check-generated-artifacts
- Bootstrap exposes the seam
SystemplaneSeedEntries() ([]SystemplaneSeedEntry, error) derived from the same buildSystemplaneRegistrations (or equivalent) the client uses at boot
- ZERO runtime DDL โ no
runSchema, no SchemaSQL() at boot, no CREATE TABLE ... systemplane_entries outside migrations/
Phase 3: Non-Canonical Detection
- Any
fsnotify / viper.WatchConfig / envconfig.Watch for runtime config โ MUST replace
- Any v4 sub-packages (
domain/, ports/, registry/, service/, bootstrap/) โ MUST remove
- Any
lib-commons/v5/commons/systemplane imports โ MUST migrate to lib-systemplane
- Runtime DDL provisioning (boot-time
SchemaSQL() execution) โ MUST replace with the make systemplane-ddl migration pipeline (Gate 3.5)
- Hand-written
migrations/NNN_systemplane_*.sql files NOT emitted by the generator โ MUST be re-emitted via make systemplane-ddl (drift-guarded)
Severity Reference
| Severity | Criteria |
|---|
| CRITICAL | Legacy import (lib-commons/v4/... or lib-commons/v5/commons/systemplane); admin.Mount without authorizer; secret with RedactNone; runtime DDL provisioning (boot-time SchemaSQL() or CREATE TABLE systemplane_entries outside migrations/) |
| HIGH | No Register before Start; no OnChange for live key; SYSTEMPLANE_* env vars in code; missing cmd/generate-systemplane-ddl/ generator or systemplane_ddl_manifest.json; make systemplane-ddl not wired into check-generated-artifacts |
| MEDIUM | Missing WithLogger/WithTelemetry; no validator on numeric range; hand-edited generated migrations/NNN_systemplane_*.sql (would fail the drift guard) |
| LOW | Missing WithDescription; inconsistent namespace naming |