helm-expt Doctrine - delivery transport + quirk testing

A repository document, rendered for the site. View source markdown.

New to cub? Install the cub CLI first. You can pull and render public catalog packages without an account. Commands that save or change ConfigHub data require you to sign in.

Generated at: 2026-07-30T12:38:02.000Z UTC · source: committed helm-expt evidence for this rendered repository document.

_A layer of the helm-expt test map._

UNOFFICIAL/EXPERIMENTAL. Standing principles for how we deliver and test - especially the non-recipe quirks (hooks, lifecycle, CRDs, target facts, lookup). These are doctrine: every proof must follow them, and a proof that skips one is incomplete, not just smaller.

1. Render parity proves the recipe; everything else is a routed quirk

The recipe is the static rendered object set. Anything that does not survive a config-only render - hooks, lifecycle actions, CRD installs, target facts, lookup() - is a routed quirk and must be proven separately. Render parity alone never proves a quirk.

2. Prove quirks through k8s and ConfigHub, every time

A quirk validated only via raw kubectl / Argo / Flux is not proven the way the catalog claims. It must flow through the real product path: cub installer → ConfigHub Units → OCI → a live cluster - and the routed quirk executed + observed on that cluster, receipted. Local-kind-only observation is not enough.

3. OCI is the single transport

ConfigHub publishes the bundle once to OCI; every consumer pulls that same artifact:

Re-rendering locally and kubectl apply-ing bypasses OCI - that is the no-ConfigHub fallback only, never the ConfigHub path. A continuous "OCI push auto-applies" without a controller is just reinventing Argo/Flux; so the no-controller mode is a one-shot pull+apply (or cub's agent).

4. Every hook/quirk check = Argo + Flux + kubectl, always

Not whichever controller a given path happens to use. A hook is only proven once all three delivery paths run it (each sourced from the same OCI bundle, per #3).

5. Hooks: observe → execute → emit; nothing automatic until earned

6. Never silent - every outcome is named

6a. The careless-dev assumption (why lane G exists)

Most real breakage is ordinary, not adversarial - devs break tools through silly choices we never anticipated, again and again. The evidence is the whole argument in one number: Helm catches ~1% of careless decisions at render; ~66% vanish silently (the --set footgun); ~33% leak to the k8s API. So we fuzz random bad decisions at volume and name every outcome - the careless dev, not the skeptic, is the most common breaker, and a tool that silently swallows their mistakes is the failure mode to catch.

6b. The Helm-migrant assumption

Many users will arrive fluent in Helm and type normal Helm flags first: --set, --set-string, -f values.yaml, --values, and image paths expressed as Helm values. cub installer should reject what it does not support, but the error should teach the cub route: declared --inputs, --set-image, named bases, or a base edit. A generic "unknown flag" is safe but opaque.

6c. Default credentials are never silent

Deterministic rendering is good for reviewable diffs, but a deterministic Secret value is not a generated password. If a demo/default base ships a fixed placeholder credential, the row stays watch until the name, warning, and recommended production route make that obvious. A base named static-passwords must not quietly contain fixed shared credentials.

6d. Drift detection must state field coverage

cub-scout compare three-way --dry-from can detect meaningful drift, but field coverage is part of the claim. A lane that catches replica or image drift but misses container environment drift is still useful, but it stays watch until the covered fields are explicit and the missing pod-spec fields are added or refused with a reason.

6e. Server-side apply conflicts must be product-readable

ConfigHub/cub delivery uses server-side apply. That can be safer than Helm's silent overwrite when someone has edited a live field by hand, because the API can report field ownership conflict instead of hiding it. The adoption UX bar is not the raw Kubernetes error. The managed path must show a plain reconcile choice, such as keep live, accept desired, or force with an explicit receipt.

7. Live runs are serial and ephemeral

cub cluster up creates a local kind cluster - one at a time (concurrent clusters starve nodes → false blocks), torn down immediately after (cub cluster down --force), orphan clusters/Spaces cleaned. Honest disposition throughout: watch ≠ pass; render parity ≠ live-ready; promotion-proven ≠ production-proven.

8. Humans read websites; agents read docs; operators read the org

Every load-bearing explanation or check publishes to all three surfaces, in the form each reader actually uses:

A truth that exists on only one surface is not published; it is buried. When the three surfaces disagree, the disagreement is itself a finding to surface, not to paper over.

9. Agents author intent, never YAML; parity decides whether the result may exist

When an AI agent (Pilot or any other) produces a chart variant, the agent's only output is the intent and the switch settings. The chart's own renderer produces the objects. A generated variant is allowed to exist only when the parity gate passes, and the gate has three parts:

A refusal must name the correct route forward, never just the block (the same rule 6b applies to Helm migrants): the receipt says what to do instead in this paradigm - an existing-secret reference, a routed prerequisite, a declared input - so the next attempt is the right one. The proof that this works is the receipt trilogy: the refusal named the existing-secret route, the corrected mapping followed it, and the gate passed with determinism identical (corrected-route-receipt).

Refusal is a first-class outcome with a receipt. The first live refusal is the canonical example: an agent mapped "TLS with auto-generated certs", the render was plausible, and determinism diverged because the chart mints new certificates on every render - exactly the nondeterminism that breaks config-as-data. The variant was refused with the reason named (refused-variant-receipt).

The agent is the author, never the authority. A generated variant becomes an ordinary governed object (versioned, gated, promotable) once accepted; an agent must not continuously mutate a live variant, and every receipt records who mapped the intent (mappedBy). The switch-effect maps follow the same rule: classifications are computed by rendering, never asserted (data/pilot-switch-map/).