A resource represents one desired piece of machine state.
Examples:
brew:gitbrew_tap:vwall/kitoutasdf_plugin:rubyasdf_tool_versions:~/.tool-versionscask:ghosttydirectory:~/codecopy:~/.codex/skills/nuxt-practicessymlink:~/.zshrcrepo:~/code/example-projectmacos_default:NSGlobalDomain/AppleShowAllExtensions
Every resource can be checked. Some resources can be applied.
Resource.ID() and Resource.Type() are the canonical runtime identity. The
planner and executor stamp that identity onto plan and apply items rather than
trusting duplicated identity fields returned by resource implementations.
type Resource interface {
ID() string
Type() string
Status(ctx context.Context) (StatusResult, error)
Apply(ctx context.Context) (ApplyResult, error)
}type ResourceState string
const (
StateSatisfied ResourceState = "satisfied"
StateMissing ResourceState = "missing"
StateChanged ResourceState = "changed"
StateFailed ResourceState = "failed"
StateSkipped ResourceState = "skipped"
StateUnknown ResourceState = "unknown"
)
type StatusResult struct {
ResourceID string
Type string
State ResourceState
Message string
Details map[string]string
}type ApplyResult struct {
ResourceID string
Type string
Action string
Changed bool
Message string
Details map[string]string
}Running apply twice should not apply the same change twice.
Status should be able to explain what it found.
Status and dry-run must never modify the system.
Resources should operate on the local machine only in the MVP.
IDs should be stable and human-readable.
Examples:
brew:git
brew_tap:vwall/kitout
asdf_plugin:ruby
asdf_tool_versions:/Users/example/.tool-versions
cask:visual-studio-code
directory:/Users/example/code
copy:/Users/example/.codex/skills/nuxt-practices
symlink:/Users/example/.zshrc
repo:/Users/example/code/example-project
macos_default:NSGlobalDomain/AppleShowAllExtensionsThe planner turns status results into actions.
satisfied -> no-op
missing -> apply
changed -> apply if safe, otherwise warn
failed -> fail
skipped -> skip
unknown -> fail unless explicitly allowedPlanning and execution reports carry a report-level ExecutionError. This
preserves cancellation even when a config produces no resource items; per-item
failures remain reserved for resource-specific problems.
The executor runs actions in order.
MVP execution should be sequential. Parallel execution can come later.
Sequential execution makes output easier to follow and avoids command conflicts, especially with Homebrew.
Do not build a full dependency graph in the MVP.
Use fixed execution order:
- doctor prerequisites
- FileVault requirement
- system prerequisites
- Homebrew taps
- Homebrew packages
- asdf plugins and versions
- asdf
.tool-versionsentries - casks
- directories
- repositories
- copies
- symlinks
- macOS defaults
- firewall security settings
- SSH keys
- login shell
- shell commands
This is enough for a first version.
Dry-run should:
- load config
- validate config
- check statuses
- build a plan
- render intended changes
- make no changes
Dry-run should not:
- add Homebrew taps
- install packages
- clone repositories
- create directories
- copy files
- create symlinks
- update security settings
- install system prerequisites
- generate SSH keys
- run shell commands
- write files
Commands whose output is only diagnostic use platform.WithBoundedOutput at
the individual mutation call. The executable runner retains at most 64 KiB from
the end of each stdout/stderr stream and marks truncated captures in errors.
Verbose streaming still forwards complete output to the configured writers.
The default runner preserves complete output for parsers. Keep inventory/status
reads, ssh-keygen -y (public-key data), and asdf installs (full-log failure
guidance) on this path. Do not apply bounded capture to an entire resource
runner, because its status and apply methods may share that runner.
Recommended exit codes:
0 all satisfied or apply completed successfully
1 status found resources needing attention
2 validation error
3 runtime error
4 partial apply failureCancellation is a runtime error (3). Resource apply failures remain 4, even
when some earlier resources were applied successfully.