Mandatory skill for creating and maintaining Helm charts following Lerian conventions. Enforces standardized chart structure, values organization, template patterns, security defaults, and dependency management.
This skill enforces Lerian's Helm chart conventions across all services. Every Helm chart MUST follow these patterns to ensure consistency, security, and operability across the platform.
Reference standards: dev-team/docs/standards/helm/
Executor agent: ring:helm-engineer
| Who | Responsibility |
|---|---|
| This Skill (ring:dev-helm) | Orchestrates the workflow: validates input, dispatches agent, verifies output |
| Agent (ring:helm-engineer) |
| Executes: reads app source, creates chart files, validates with helm lint |
<verify_before_proceed>
REQUIRED INPUT:
- service_name: name of the service
- chart_type: single | multi-component | umbrella
- components: list of component names
OPTIONAL INPUT:
- dependencies: [postgresql, mongodb, rabbitmq, valkey, keda]
- has_worker: true/false
- namespace: target namespace
if any REQUIRED input is missing:
→ STOP and report: "Missing required input: [field]"
MUST create the standard directory structure. See helm/conventions.md for:
-helm suffix rule and exceptions)CHART NAME RULES:
- Default: {service_name}-helm (e.g., reporter-helm, tracer-helm)
- Exception: plugin-access-manager (no -helm suffix)
- Exception: otel-collector-lerian (no -helm suffix)
if service_name is NOT in exception list:
→ chart name = {service_name}-helm
MUST define helper functions per component. See helm/templates.md for:
<cannot_skip> values.yaml MUST follow the exact Lerian structure. </cannot_skip>
See helm/values.md for:
<block_condition> HARD GATE: MUST read the application's .env.example or config.go to extract ALL expected environment variables. Do NOT guess. Missing env vars are the #1 cause of CrashLoopBackOff. </block_condition>
<dispatch_required agent="ring:helm-engineer"> Create/update Helm chart following Lerian conventions. </dispatch_required>
Task:
subagent_type: "ring:helm-engineer"
description: "Create Helm chart for {service_name}"
prompt: |
⛔ MANDATORY: Create Helm chart following Lerian conventions.
## Context
- **Service:** {service_name}
- **Components:** {components}
- **Dependencies:** {dependencies}
- **Chart Type:** {chart_type}
## Required Steps
1. Read application .env.example and config struct
2. Verify health check endpoints in application source
3. Create chart structure per Lerian conventions
4. Map ALL env vars to configmap/secrets
5. Validate with helm lint and helm template
## Required Output
- Standards Verification (FIRST)
- Env Var Coverage table (100% coverage required)
- Validation Results (helm lint MUST pass)
if agent returns env_vars_missing > 0:
→ FAIL: "Chart has missing env vars. Fix before proceeding."
if agent returns helm_lint_status == FAIL:
→ Re-dispatch agent with specific lint errors
if all checks PASS:
→ Proceed to Step 6 (Worker) or Step 7 (Validation)
<verify_before_proceed>
See helm/worker-patterns.md for:
<cannot_skip> ALL checks MUST pass before chart is considered complete. </cannot_skip>
RUN in order:
1. helm lint .
→ MUST pass with 0 failures
2. helm template test .
→ MUST render without errors
3. helm template test . --set keda.enabled=false
→ MUST render without errors (if worker exists)
4. Verify ALL application env vars are covered:
→ Read app's .env.example or config struct
→ Compare with configmap + secrets in values.yaml
→ REPORT any missing vars
5. Verify health check paths:
→ Read app's health endpoint registration code
→ Compare with probe paths in deployment template
→ REPORT any mismatches
CHECK each item:
[ ] Chart.yaml name has -helm suffix (unless exception)
[ ] Image declarations use structured repository/tag/pullPolicy with kindIs guard ([pattern](../../docs/standards/helm/templates.md#image-declaration-pattern-mandatory))
[ ] All values quoted in ConfigMap ({{ $value | quote }})
[ ] No hardcoded credentials in values.yaml (use placeholders)
[ ] Security context: runAsNonRoot: true, drop ALL capabilities
[ ] Service type is ClusterIP (never NodePort or LoadBalancer)
[ ] HPA enabled by default with CPU and memory metrics
[ ] PDB enabled by default
[ ] Probes match actual application health endpoints
[ ] initContainers wait for all infrastructure dependencies
[ ] Secrets support useExistingSecret pattern
[ ] All env vars from app's .env.example are present
[ ] OTEL injection is conditional on ENABLE_TELEMETRY
[ ] AWS IAM sidecar is conditional on aws.rolesAnywhere.enabled
[ ] Ingress disabled by default
| User Says | Your Response |
|---|---|
| "Skip the security context" | "Security context is MANDATORY. All containers MUST run as non-root." |
| "We don't need PDB" | "PDB is REQUIRED for production readiness. Adding it now." |
| "Just use the default health path" | "MUST verify health paths against application code. Wrong paths cause CrashLoopBackOff." |
| "We'll add env vars later" | "Missing env vars are the #1 cause of deployment failures. Reading .env.example now." |
| "No need for existing secret support" | "MANDATORY for production. Teams use external secret managers." |
| Rationalization | Why It's WRONG | Required Action |
|---|---|---|
| "Health probes are the same for all services" | Each service has unique endpoints. Wrong paths = CrashLoopBackOff | MUST read application source code |
| "We can use NodePort for testing" | Lerian convention: always ClusterIP. Ingress handles external access | Set service.type: ClusterIP |
| "Secrets can use default values" | Default passwords in values.yaml are a security risk | Use empty strings or placeholders |
| "One configmap for everything" | Sensitive data MUST be in Secrets, not ConfigMap | Split per ConfigMap vs Secrets rule |
| "The chart works, so it's done" | Must validate against app env vars AND lint AND template render | Run ALL validation steps |
| "initContainers are overkill" | Without dependency checks, pods crash before DB is ready | Add wait-for-dependencies |
| "Just use a flat string for the image" | CI gitops-update can't target the tag independently; breaks automated deploys | Use structured repository/tag/pullPolicy with kindIs guard |
## Helm Chart Report: {service_name}
**Status:** [PASS|FAIL|PARTIAL]
**Chart Type:** [single|multi-component|umbrella]
**Components:** [list]
**Dependencies:** [list]
## Files Created/Modified
| File | Action | Status |
|------|--------|--------|
| Chart.yaml | CREATED | OK |
| values.yaml | CREATED | OK |
| templates/_helpers.tpl | CREATED | OK |
| templates/{component}/deployment.yaml | CREATED | OK |
| ... | ... | ... |
## Env Var Coverage
| Source (.env.example) | In ConfigMap | In Secrets | Status |
|-----------------------|-------------|------------|--------|
| SERVER_PORT | YES | - | OK |
| DB_PASSWORD | - | YES | OK |
| MISSING_VAR | NO | NO | MISSING |
## Validation Results
| Check | Status |
|-------|--------|
| helm lint | PASS/FAIL |
| helm template (default) | PASS/FAIL |
| helm template (no keda) | PASS/FAIL |
| Health paths verified | PASS/FAIL |
| All env vars covered | PASS/FAIL |
| Security context | PASS/FAIL |
| No hardcoded secrets | PASS/FAIL |
## Notes
- [Any deviations from standard with justification]