shirkevich
03/09/2026, 9:32 PMErik Osterman (Cloud Posse)
03/09/2026, 10:12 PMErik Osterman (Cloud Posse)
03/09/2026, 10:13 PMErik Osterman (Cloud Posse)
03/09/2026, 10:13 PMErik Osterman (Cloud Posse)
03/09/2026, 10:13 PMErik Osterman (Cloud Posse)
03/09/2026, 10:13 PMErik Osterman (Cloud Posse)
03/09/2026, 10:14 PMAndriy Knysh (Cloud Posse)
03/09/2026, 10:17 PMshirkevich
03/09/2026, 10:48 PMErik Osterman (Cloud Posse)
03/10/2026, 2:16 AMatmos terraform ... --use-version=1516
or by setting ATMOS_USE_VERSION=1516 and atmos will automatically download and install it, then execute it.Andriy Knysh (Cloud Posse)
03/10/2026, 4:03 AMmainErik Osterman (Cloud Posse)
03/10/2026, 12:51 PMshirkevich
03/11/2026, 11:17 AMorigin/main is still:
log.Debug("Routing to ExecuteTerraformQuery (multi-component)")
err = e.ExecuteTerraformQuery(&info)
--all, --components, and --query all enter through isMultiComponentExecution and land in ExecuteTerraformQuery, which walks stacks sequentially with no graph.
ExecuteTerraformAll is never called from any command routing. The only references are its own definition, tests, and the PRD doc:
0b67828a4:internal/exec/terraform_all.go (definition)
0b67828a4:internal/exec/terraform_all_simple_test.go (test)
0b67828a4:internal/exec/terraform_all_test.go (test)
0b67828a4:docs/prd/terraform-dependency-order.md (docs)
So ExecuteTerraformAll with its graph construction, dependency ordering, and executeInDependencyOrder loop is effectively dead code from the CLI's perspective -- it shipped as library code with tests but was never wired into the command tree.
is it correct? so 1516 can't be tested yet with real codebase? or am I missing smth?Erik Osterman (Cloud Posse)
03/11/2026, 11:57 AMErik Osterman (Cloud Posse)
03/11/2026, 11:58 AMErik Osterman (Cloud Posse)
03/11/2026, 12:05 PMErik Osterman (Cloud Posse)
03/11/2026, 12:05 PMshirkevich
03/11/2026, 12:20 PMAndriy Knysh (Cloud Posse)
03/11/2026, 1:10 PMshirkevich
03/11/2026, 2:23 PMAndriy Knysh (Cloud Posse)
03/11/2026, 2:29 PMErik Osterman (Cloud Posse)
03/11/2026, 5:59 PMErik Osterman (Cloud Posse)
03/11/2026, 6:00 PMErik Osterman (Cloud Posse)
03/11/2026, 6:00 PMErik Osterman (Cloud Posse)
03/11/2026, 6:01 PMexec package.Erik Osterman (Cloud Posse)
03/11/2026, 6:01 PMErik Osterman (Cloud Posse)
03/11/2026, 6:01 PMshirkevich
03/12/2026, 9:44 AMshirkevich
03/12/2026, 9:55 AMpkg/orchestrator
Generic DAG scheduler over already-built nodes
Owns levels/ready queue, status transitions, blocked/skipped semantics, concurrency limits, aggregate exit-code rules
Knows nothing about Terraform, Helmfile, or Ansible
pkg/process
Generic child-process manager
Owns spawn/stop/wait, stream injection, signal forwarding, graceful shutdown, per-node stdout/stderr sinks
This is the natural destination for the shell_utils.go refactor
pkg/runsummary or pkg/orchestrator/report
Stable result model and JSON artifact
Owns NodeResult, ordering rules, timestamps, overall status, serialization
Can be shared by Terraform first and later by Helmfile/Ansible
internal/terraformrun or pkg/terraformrun
Terraform-specific adapter on top of the generic orchestrator
Owns selector expansion, auth bootstrap, workdir validation, command construction, hook lifecycle, destroy semantics
This is where logic should move out of the current exec path over timeErik Osterman (Cloud Posse)
03/12/2026, 1:40 PMErik Osterman (Cloud Posse)
03/14/2026, 5:13 AMErik Osterman (Cloud Posse)
03/14/2026, 5:14 AMshirkevich
03/14/2026, 9:21 AMErik Osterman (Cloud Posse)
03/15/2026, 3:46 AMshirkevich
03/16/2026, 9:15 AMpackage example
import (
"context"
"io"
"time"
)
// Node is pure scheduling data.
// It does not know how to execute itself.
type Node struct {
ID string // unique DAG node ID: "terraform/tenant1-ue2-dev/vpc"
Type string // "terraform", "packer", "ansible"
Ref any // tool-specific payload prepared earlier
}
// Streams are injected by the orchestrator/process layer.
// This is what lets concurrent nodes avoid shared stdio.
type Streams struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
// TaskSpec is the generic process contract.
// Adapters translate tool-specific nodes into this shape.
type TaskSpec struct {
Command []string
Dir string
Env []string
Interactive bool
}
// Result is the generic execution result returned by the process layer.
type Result struct {
ExitCode int
StartedAt time.Time
FinishedAt time.Time
Err error
}
// Runner owns subprocess lifecycle.
// This is the reusable process package, not Terraform-specific code.
type Runner interface {
Run(ctx context.Context, spec TaskSpec, streams Streams) (*Result, error)
}
// Adapter owns tool-specific execution semantics.
// Scheduler uses adapters; adapters use the process layer.
type Adapter interface {
Prepare(ctx context.Context, node *Node) (TaskSpec, error)
Finalize(ctx context.Context, node *Node, result *Result) error
}
// Registry resolves adapters by node type.
type Registry interface {
MustGet(nodeType string) Adapter
}
// Orchestrator is the generic DAG executor.
// In reality it would also own dependency tracking / ready queue / concurrency.
type Orchestrator struct {
runner Runner
registry Registry
}
// ExecuteNode shows the intended boundary:
// scheduler/orchestrator drives execution,
// adapter translates tool semantics,
// process runner owns subprocess execution.
func (o *Orchestrator) ExecuteNode(ctx context.Context, node *Node, streams Streams) (*Result, error) {
adapter := o.registry.MustGet(node.Type)
// Tool-specific preparation:
// auth/bootstrap, hooks, cwd/env resolution, command construction, etc.
spec, err := adapter.Prepare(ctx, node)
if err != nil {
return nil, err
}
// Generic subprocess lifecycle:
// stream isolation, signal forwarding, wait/kill, exit handling.
result, err := o.runner.Run(ctx, spec, streams)
if err != nil {
return nil, err
}
// Tool-specific completion semantics:
// hooks, store writes, planfile handling, post-processing, etc.
if err := adapter.Finalize(ctx, node, result); err != nil {
return nil, err
}
return result, nil
}
// -----------------------------------------------------------------------------
// Terraform adapter
// -----------------------------------------------------------------------------
type TerraformInfo struct {
Dir string
Env []string
// plus stack/component/auth/workdir/hook metadata
}
type TerraformAdapter struct{}
func (a *TerraformAdapter) Prepare(ctx context.Context, node *Node) (TaskSpec, error) {
info := node.Ref.(*TerraformInfo)
// In real code this is where Terraform-specific prep would happen:
// - validate workdir
// - resolve auth context
// - build atmos/terraform args
// - decide whether interactive mode is allowed
return TaskSpec{
Command: []string{"terraform", "apply", "-auto-approve"},
Dir: info.Dir,
Env: info.Env,
}, nil
}
func (a *TerraformAdapter) Finalize(ctx context.Context, node *Node, result *Result) error {
// In real code:
// - run after-apply hooks
// - update stores
// - publish plan/apply metadata
return nil
}
// -----------------------------------------------------------------------------
// Packer adapter
// -----------------------------------------------------------------------------
type PackerInfo struct {
Dir string
Env []string
}
type PackerAdapter struct{}
func (a *PackerAdapter) Prepare(ctx context.Context, node *Node) (TaskSpec, error) {
info := node.Ref.(*PackerInfo)
return TaskSpec{
Command: []string{"packer", "build", "."},
Dir: info.Dir,
Env: info.Env,
}, nil
}
func (a *PackerAdapter) Finalize(ctx context.Context, node *Node, result *Result) error {
return nil
}
// -----------------------------------------------------------------------------
// Ansible adapter
// -----------------------------------------------------------------------------
type AnsibleInfo struct {
Dir string
Env []string
}
type AnsibleAdapter struct{}
func (a *AnsibleAdapter) Prepare(ctx context.Context, node *Node) (TaskSpec, error) {
info := node.Ref.(*AnsibleInfo)
return TaskSpec{
Command: []string{"ansible-playbook", "site.yml"},
Dir: info.Dir,
Env: info.Env,
}, nil
}
func (a *AnsibleAdapter) Finalize(ctx context.Context, node *Node, result *Result) error {
return nil
}
// -----------------------------------------------------------------------------
// Example nodes
// -----------------------------------------------------------------------------
func ExampleNodes() []*Node {
return []*Node{
{
ID: "terraform/tenant1-ue2-dev/vpc",
Type: "terraform",
Ref: &TerraformInfo{
Dir: "/work/terraform/vpc",
Env: []string{"AWS_PROFILE=dev"},
},
},
{
ID: "packer/tenant1-ue2-dev/ami-builder",
Type: "packer",
Ref: &PackerInfo{
Dir: "/work/packer/ami-builder",
Env: []string{"AWS_PROFILE=dev"},
},
},
{
ID: "ansible/tenant1-ue2-dev/playbook",
Type: "ansible",
Ref: &AnsibleInfo{
Dir: "/work/ansible/playbook",
Env: []string{"ANSIBLE_CONFIG=/work/ansible/ansible.cfg"},
},
},
}
}Erik Osterman (Cloud Posse)
03/16/2026, 12:00 PMshirkevich
03/16/2026, 3:04 PMErik Osterman (Cloud Posse)
03/16/2026, 3:17 PMErik Osterman (Cloud Posse)
03/16/2026, 3:17 PMErik Osterman (Cloud Posse)
03/17/2026, 12:33 PMErik Osterman (Cloud Posse)
03/17/2026, 12:34 PMshirkevich
03/17/2026, 1:08 PMErik Osterman (Cloud Posse)
03/17/2026, 1:15 PM