Atmos Core: Component Type Development
Use this skill when developing Atmos itself — adding or modifying a component type (kind) in the
Go codebase. This is contributor/core guidance, distinct from the user-facing atmos-components skill
(which documents authoring terraform/helmfile/container components in stacks).
Start from docs/developing-component-plugins.md (the component-plugin development guide). The notes
below capture the non-obvious wiring learned while adding the container component type.
The component provider (pkg/component)
A component type is a ComponentProvider (pkg/component/provider.go) registered via init() with
component.Register(...) (pkg/component/registry.go). Reference impls:
pkg/component/ansible/— typed-config built-in style.pkg/component/mock/,pkg/component/custom/— thePlugins-map plugin style.pkg/component/container/— provider +cmd/lifecycle split.
Layout per the guide: config.go (typed Config + parseConfig), <type>.go (provider + init),
executor.go (verb implementations), <type>_test.go (>90% coverage). Wire a blank import into
cmd/root.go so init() runs.
Reusable error sentinels live in errors/errors.go: ErrComponentExecutionFailed,
ErrComponentConfigInvalid, ErrComponentValidationFailed, ErrComponentTypeEmpty.
First-class component config (NOT vars)
Per-instance config that is not arbitrary template data must be first-class top-level sections
(siblings of metadata/env/composition), NOT nested under vars. For container, the config reuses
the workflow container-step structs (schema.ContainerBuildStep/ContainerRunStep/ContainerMount/
ContainerPort in pkg/schema/workflow.go) for consistency. Decode a YAML-derived map[string]any
into those structs with mapstructure using TagName: "yaml" so snake_case keys (build_args,
read_only) map.
The CLI command group (cmd/)
Mirror cmd/ansible/: a base cobra.Command registered through the command registry
(cmd/internal CommandProvider), persistent flags via flags.NewStandardParser() (NEVER
viper.BindEnv/BindPFlag), one thin file per verb dispatching to
component.MustGetProvider(<type>).Execute(&component.ExecutionContext{...}). Wire a blank import into
cmd/root.go.
CRITICAL: the describe/list type whitelist
A new top-level components.<type> is dropped (stack renders {}, "component not found") unless
the type is added to several hardcoded lists. Grep AnsibleSectionName / "ansible" across
internal/exec + pkg/list/extract and mirror every hit:
pkg/config/const.go—XComponentType/XSectionNameconsts.internal/exec/describe_stacks_component_processor.go— thetypeEntrieslist ANDcomponentsSectionHasComponents.internal/exec/describe_stacks.go—getComponentBasePathswitch.internal/exec/describe_component.go— thedetectComponentTypeauto-detect order (a loop over[terraform, helmfile, packer, ansible, container]); a type missing here makesatmos describe component <name>fail even when the lifecycle works.pkg/list/extract/components.go— THREE hardcoded type lists (per-stackextractComponentType×2, uniqueextractUniqueComponentType).
Verify with atmos describe stacks (stack with only the new type must be non-empty) and
atmos describe component <name> -s <stack>.
Inheritance & deep-merge for custom types
Built-in types (terraform/helmfile/packer/ansible) get full inheritance via the processComponent
pipeline. Other types ride the custom-component fallback in
internal/exec/stack_processor_process_stacks.go. That fallback now resolves metadata.inherits and
generic-deep-merges all top-level keys (resolveCustomComponentInheritance), so custom types honor
catalog/abstract defaults. Gotchas:
- Strip
metadata.type/inherits/componentfrom a base before merging, or an abstract base poisons the concrete component (sanitizeBaseForInheritance). - Reject
metadata.type: abstractfor execution and filter it from listings. - Use the native merge (
pkg/merge) — it already incorporates the slice-truncation and permissive-type-mismatch fixes indocs/fixes/2026-03-19-*anddocs/fixes/2026-03-24-*. - Component-level config sections (vars/settings/env/hooks/secrets/...) that need per-section
inheritance still require the section whitelist plumbing (see
docs/errors.md/ the merge helpers).
Schema, docs, tests
- JSON schema:
pkg/datafetcher/schema/atmos/manifest/1.0.json— add<type>_components+<type>_component_manifestdefinitions and thecomponents.<type>property. - Docs:
website/docs/components/components-overview.mdx(Component Types table + directory diagram),website/docs/components/<type>.mdx, andwebsite/docs/cli/commands/<type>/usage.mdx. - Tests: provider unit tests with a mockgen
Runtime/dependency,cmd.NewTestKitfor the command, inheritance + abstract + graceful-empty cases. Regenerate affected--helpgolden snapshots with-regenerate-snapshots(never hand-edit). - Gate:
./custom-gcl run --new-from-rev=origin/<base> ./pkg/component/<type>/... ./internal/exec/....