Files
flow/docs/refactor-plan.md

7.6 KiB

Flow CLI Refactor Status

Based on code review (2026-03-22), architecture discussion, and the current implementation. Spec: docs/superpowers/specs/2026-03-16-flow-architecture-redesign.md

Current State

The action-runtime rewrite is implemented. cli.py is a thin Typer adapter, flow.app owns application orchestration, domain modules keep pure planning and resolution logic, and executor-managed mutations are represented as action plans before they reach runtime adapters.

The old structural problems from the original codebase (duplicated flows, monkeypatching, dead modules, singleton-style runtime access) have been removed from the active command paths. The remaining refactor work is deferred cleanup, not a blocker for the action-centered architecture.


Command Surface

This is the implemented command surface.

flow remote enter <target>              # Host only. SSH+tmux into VM.
flow remote list                        # List configured targets.

flow dev create <name> -i <image>       # VM only. Create+start container.
flow dev attach <name>                  # Attach to container tmux session.
flow dev exec <name> [cmd...]           # Run command in container.
flow dev enter <name>                   # Interactive shell in container.
flow dev list                           # List dev containers.
flow dev stop <name>                    # Stop container.
flow dev rm <name>                      # Remove container.
flow dev respawn <name>                 # Respawn tmux panes.

flow dotfiles init --repo <url>         # Clone dotfiles repo + all module repos.
flow dotfiles link [--profile p]        # Reconcile symlinks (creates, fixes broken, removes stale).
flow dotfiles unlink [packages...]      # Remove managed symlinks.
flow dotfiles status [packages...]      # Show packages, link health, module info.
flow dotfiles edit <package>            # Pull -> $EDITOR -> commit+push.

flow dotfiles repos list                # List ALL managed repos (dotfiles + modules).
flow dotfiles repos status [--repo=x]   # Git status for one or all repos.
flow dotfiles repos pull [--repo=x] [--dry-run]
flow dotfiles repos push [--repo=x] [--dry-run]

flow setup run [profile|--profile p] [--dry-run] [--var KEY=VALUE]
flow setup list                         # List profiles.
flow setup show <profile>              # Show profile plan.

flow packages install [name...] [--profile p] [--dry-run]
flow packages list [--all]              # List packages.
flow packages remove <name...> [--dry-run]

flow projects check [--fetch]           # VM only. Git health across ~/projects.
flow projects fetch                     # Fetch all project remotes.
flow projects summary                   # Quick status overview.

Aliases

  • dotfiles -> dot
  • packages -> package, pkg
  • projects -> project; flow sync -> flow projects check --fetch
  • setup -> bootstrap, provision
  • remote enter -> enter
  • dev attach -> dev connect
  • dev rm -> dev remove
  • dotfiles repos -> dotfiles repo

Global flags

  • --version
  • --quiet / -q
  • --no-color

Commands Removed During Refactor

Removed Reason
dotfiles sync Redundant: repos pull + link
dotfiles relink Redundant: link is idempotent
dotfiles undo Redundant: unlink is the inverse of link
dotfiles clean Folded into link (reconciliation handles broken symlinks)
dotfiles modules list Replaced by dotfiles repos list
dotfiles modules sync Replaced by dotfiles repos pull

Action-Centered Architecture

The runtime boundary is flow.actions.

  • ActionPlan is the unit of execution. It can contain high-level DomainAction entries and direct PrimitiveAction entries.
  • DomainAction records intent from a domain such as dotfiles, packages, repos, remote targets, containers, completion, or setup.
  • expand_actions() converts domain actions into primitive actions. Some domains supply already expanded primitive plans when the service has concrete runtime arguments.
  • PrimitiveAction is the canonical executor input for filesystem, process, git, download, archive, container, and tmux operations.
  • ActionExecutor owns dry-run output, append-only JSONL audit logging, rollback stack management, rollback barriers, and dispatch into SystemRuntime.

App use-cases construct plans and pass them to the executor for action-backed commands. Direct runtime calls are limited to explicit interactive boundaries such as attaching to tmux or entering a container shell. Domain modules stay free of I/O where the current implementation has pure resolution/planning functions.

Rollback is best-effort and explicit. Actions default to rollbackable; external boundaries such as shell commands, remote sessions, and non-reversible git/container operations use barrier or none policies.

Key Design Decision: Modules Are Repos

The _module.yaml files define external git repos that provide content for dotfiles packages. These module repos are not git submodules -- they are regular git clones managed by flow.

The dotfiles repo itself is also a git clone managed by flow. So all managed git repos (the dotfiles repo + every module repo) share the same abstraction and the same commands.

dotfiles repos is the single entry point for all repo operations. There is no separate dotfiles modules subcommand group. The --repo=<name> flag filters to a specific repo when needed (dotfiles repo is named dotfiles, module repos are named by their package, e.g. nvim).


Completed Work

Unified Repos Abstraction

RepoInfo with module_ref is the canonical repo model. _discover_repos() finds the dotfiles repo and module repos, and repos list/status/pull/push iterate that single collection with an optional --repo filter. dotfiles init uses the same pull-or-clone flow.

Command Trimming

Removed redundant dotfiles commands:

  • dotfiles sync: use dotfiles repos pull plus dotfiles link
  • dotfiles relink: dotfiles link is idempotent
  • dotfiles undo: use dotfiles unlink
  • dotfiles clean: broken symlink repair is part of link planning
  • dotfiles modules list/sync: use dotfiles repos list/pull

Feature Completion

  • dotfiles edit: pull -> $EDITOR/$VISUAL -> scoped git add -> commit+push, with --no-commit to skip auto-commit/push.
  • dotfiles status: module info, link health, and package filtering.
  • dotfiles repos list: all managed repos with name, type, local path, and clone status.
  • --no-color: global flag added to cli.py.
  • --dry-run: supported by dotfiles link/unlink, repos pull/push, packages install, setup run, remote enter, and dev create.

Improvements Over Spec

These are correct deviations -- the implementation improved on the spec:

  • adapters/containers.py + adapters/tmux.py extracted as adapters (spec had them inline)
  • core/config_parse.py + core/yaml.py extracted for config parsing
  • SystemRuntime extended with containers, tmux, download, and archive runtime fields
  • flow.actions extracted as the canonical execution layer instead of leaving mutation dispatch in individual app use-cases

CI

The GitHub Actions workflow is split into two jobs:

  • unit: installs dependencies and runs pytest tests/ -v --ignore=tests/e2e
  • e2e: verifies Docker is available, sets FLOW_RUN_E2E=1, and runs pytest tests/e2e/ -v

Optional Future Work

These are optional refinements, not blockers for the action-centered rewrite.

Global --dry-run

If per-command --dry-run becomes a maintenance burden, promote to a global flag in cli.py.