π€ Generated by the Agentic Engineer
The mental model
Products grow generic code as a byproduct of solving specific problems. A filesystem helper, a Kubernetes client wrapper, a retry/timer utility, a notification shim β written once for one product, then wanted by the next. Left alone, each product reimplements its own variant and the portfolio ends up maintaining several mediocre copies instead of one mature implementation.
So: when code inside a product turns out to be generic, extract it into a shared Go library, so every product inherits one stable, well-tested implementation rather than its own drift.
This is the horizontal sibling of the maturation ladder in #102. That one asks how far a tool should graduate (bash β Go β CLI). This one asks where a piece of code should live once more than one product wants it. Both are the same underlying question β what is the right home for this code now β on different axes.
The rules that bound it
1. Two independent users, not one anticipated user. Extract when a second product actually needs it, not when one product's author suspects someone might. Speculative shared libraries are how you get an abstraction shaped by exactly one caller, which then fits the second caller badly. The portfolio already applies this test to CI actions and policies; it applies here unchanged.
2. Prefer an existing shared library to a new one β the direct analogue of #102's existing-CLI-first rule, and for the same reason: a new library repo is a new product with a permanent maintenance claim.
3. A shared SERVICE is a much heavier commitment than a shared library, and needs a stronger reason. A library is compiled in and versioned by its consumer. A service needs uptime, deployment, compatibility across versions, and someone accountable when it is down β and it turns a compile-time dependency into a runtime failure mode for every consumer. Reach for a library by default; justify a service explicitly, on a need a library genuinely cannot meet (shared mutable state, a central authority, something that must be one instance).
4. Extraction is behaviour-preserving. It is a refactor: the moving product's tests pass unmodified, and consumers migrate additively and backward-compatibly.
5. A shared library is a first-class portfolio product β own repo, portfolio-map entry, product card, roadmap, health checks, release/versioning discipline. Same standard as everything else.
The gap this addresses β measured
The portfolio already has shared libraries for CI building blocks (actions), cluster policies (kyverno-policies), and agent extensions (agent-skills, agent-plugins). It has no shared Go library at all.
What exists instead (2026-07-27):
- Four separate Go modules:
ksail/v7, platform, go-template, provider-upjet-unifi.
- KSail already exposes 16 public packages under
pkg/: apis, awsconfig, cli, client, envvar, fsutil, k8s, notify, operator, registryauth, runner, strutil, svc, timer, toolgen, webui. Several of those are generic by name and by nature β fsutil, strutil, envvar, timer, runner, notify, k8s, client, registryauth.
So KSail is already acting as the portfolio's de-facto shared Go library, but through a CLI's module path. That has a real cost worth stating plainly: anything importing it takes a dependency on a command-line application's module, inherits its release cadence, and pins to its major version β currently v7, so consumers have already been dragged through six majors driven by CLI concerns, not library concerns.
Meanwhile platform is a separate module carrying its own Go programs (generate-kubescape-exceptions, validate-eks-ci-role-policy, validate-merge-group-heal) that plausibly want exactly those helpers.
This issue does not presume the answer is a new repo. Extracting KSail's genuinely-generic packages into a shared library, versioned independently, is the obvious hypothesis β but rule 2 says the alternatives get considered first, and "KSail keeps them and documents them as a supported public API" is a legitimate outcome if the coupling turns out to be cheaper than the split.
Where the doctrine lands
β οΈ Same constraint as #102: this belongs in the product-engineering skill, authored in devantler-tech/agent-skills. The copy in this repo carries metadata.github-repo: agent-skills and is re-pulled by update-agent-skills, so editing it here is silently reverted.
The consuming deployment's Holistic review & shared-library stewardship section already covers extraction for CI actions, agent skills, and Kyverno policies β it should gain Go libraries and services once the generic change lands.
Suggested children
- State the extraction doctrine and its five rules in the
product-engineering skill, upstream.
- Audit KSail's 16 public packages: for each, is it genuinely generic, does a second product actually want it, and what would importing it cost today?
- Audit
platform's Go programs for helpers they reimplement that already exist in KSail β the concrete two-users evidence.
- From that evidence, decide: extract to a shared library, keep and document as KSail public API, or leave alone. Record the reasoning either way.
Acceptance criteria
Related
The mental model
Products grow generic code as a byproduct of solving specific problems. A filesystem helper, a Kubernetes client wrapper, a retry/timer utility, a notification shim β written once for one product, then wanted by the next. Left alone, each product reimplements its own variant and the portfolio ends up maintaining several mediocre copies instead of one mature implementation.
So: when code inside a product turns out to be generic, extract it into a shared Go library, so every product inherits one stable, well-tested implementation rather than its own drift.
This is the horizontal sibling of the maturation ladder in #102. That one asks how far a tool should graduate (bash β Go β CLI). This one asks where a piece of code should live once more than one product wants it. Both are the same underlying question β what is the right home for this code now β on different axes.
The rules that bound it
1. Two independent users, not one anticipated user. Extract when a second product actually needs it, not when one product's author suspects someone might. Speculative shared libraries are how you get an abstraction shaped by exactly one caller, which then fits the second caller badly. The portfolio already applies this test to CI actions and policies; it applies here unchanged.
2. Prefer an existing shared library to a new one β the direct analogue of #102's existing-CLI-first rule, and for the same reason: a new library repo is a new product with a permanent maintenance claim.
3. A shared SERVICE is a much heavier commitment than a shared library, and needs a stronger reason. A library is compiled in and versioned by its consumer. A service needs uptime, deployment, compatibility across versions, and someone accountable when it is down β and it turns a compile-time dependency into a runtime failure mode for every consumer. Reach for a library by default; justify a service explicitly, on a need a library genuinely cannot meet (shared mutable state, a central authority, something that must be one instance).
4. Extraction is behaviour-preserving. It is a refactor: the moving product's tests pass unmodified, and consumers migrate additively and backward-compatibly.
5. A shared library is a first-class portfolio product β own repo, portfolio-map entry, product card, roadmap, health checks, release/versioning discipline. Same standard as everything else.
The gap this addresses β measured
The portfolio already has shared libraries for CI building blocks (
actions), cluster policies (kyverno-policies), and agent extensions (agent-skills,agent-plugins). It has no shared Go library at all.What exists instead (2026-07-27):
ksail/v7,platform,go-template,provider-upjet-unifi.pkg/:apis, awsconfig, cli, client, envvar, fsutil, k8s, notify, operator, registryauth, runner, strutil, svc, timer, toolgen, webui. Several of those are generic by name and by nature βfsutil,strutil,envvar,timer,runner,notify,k8s,client,registryauth.So KSail is already acting as the portfolio's de-facto shared Go library, but through a CLI's module path. That has a real cost worth stating plainly: anything importing it takes a dependency on a command-line application's module, inherits its release cadence, and pins to its major version β currently v7, so consumers have already been dragged through six majors driven by CLI concerns, not library concerns.
Meanwhile
platformis a separate module carrying its own Go programs (generate-kubescape-exceptions,validate-eks-ci-role-policy,validate-merge-group-heal) that plausibly want exactly those helpers.This issue does not presume the answer is a new repo. Extracting KSail's genuinely-generic packages into a shared library, versioned independently, is the obvious hypothesis β but rule 2 says the alternatives get considered first, and "KSail keeps them and documents them as a supported public API" is a legitimate outcome if the coupling turns out to be cheaper than the split.
Where the doctrine lands
product-engineeringskill, authored indevantler-tech/agent-skills. The copy in this repo carriesmetadata.github-repo: agent-skillsand is re-pulled byupdate-agent-skills, so editing it here is silently reverted.The consuming deployment's Holistic review & shared-library stewardship section already covers extraction for CI actions, agent skills, and Kyverno policies β it should gain Go libraries and services once the generic change lands.
Suggested children
product-engineeringskill, upstream.platform's Go programs for helpers they reimplement that already exist in KSail β the concrete two-users evidence.Acceptance criteria
Related