This guide explains the runtime users actually operate day to day: startup, resume, inspection commands, artifacts, diagnostics, and the current interactive UI surface.
Current supported stack for this guide:
- Python runtime only
- Python backends and JavaScript frontends
- built-in local service wiring for databases, Redis, Supabase, and n8n
- worktree orchestration from
todo/plans/...
The supported runtime path is the Python runtime.
In practice, that means:
- first-run setup happens through the config wizard
- inspection commands show what the runtime will do before startup
- resume is saved-state-aware and live-truth-aware
- diagnostics such as
--doctor,--debug-pack, and--debug-reportare part of the normal surface
The Python runtime expects a repo-local .envctl for normal operational commands.
Behavior:
- if
.envctlexists, runtime loads it - if
.envctlis missing and the command is inspect-only or utility-safe, runtime continues with defaults - if
.envctlis missing and you run a normal operational command interactively, runtime opens the setup wizard and writes.envctl - if
.envctlis missing and there is no interactive TTY, runtime exits with a clear error instead of guessing
Useful commands:
envctl config
envctl show-config --json
printf '%s\n' '{"default_mode":"trees"}' | envctl config --stdin-jsonThe current wizard flow is:
Welcome / SourceDefault ModeComponents- optional
Long-Running Service DirectoriesPortsReview / Save
For the full setup story, see First-Run Wizard.
envctl has two runtime modes:
main: operate on the repository root as one environmenttrees: operate on planned or existing worktrees
Mode selection order:
- explicit CLI mode flags
ENVCTL_DEFAULT_MODEfrom environment or.envctl- built-in default
main
Examples:
envctl --main
envctl --trees
envctl --resume
envctl --planplan always resolves into trees mode.
These commands are safe to run before startup:
envctl --list-commands
envctl --list-targets --json
envctl --list-trees --json
envctl show-config --json
envctl show-state --json
envctl explain-startup --json
envctl install-prompts --cli codex --dry-runWhat they are for:
--list-commands: show the supported runtime command surface--list-targets --json: show discovered projects and targetable scopes--list-trees --json: show discovered worktrees / planning targetsshow-config --json: print effective config source and valuesshow-state --json: print the latest saved runtime stateexplain-startup --json: show what startup would do before anything runsinstall-prompts --cli ...: install built-in AI prompt presets without requiring a startup run
The runtime writes scoped artifacts under:
${RUN_SH_RUNTIME_DIR:-/tmp/envctl-runtime}/python-engine/<scope-id>/
Common files:
run_state.jsonruntime_map.jsonports_manifest.jsonerror_report.jsonevents.jsonlruns/<run-id>/...debug/session-*/...
User-facing meaning:
run_state.jsonpowers resume and state-aware commandsruntime_map.jsonis the quickest machine-readable map of projects, services, ports, and URLserror_report.jsonandevents.jsonlare the first places to look during triageruns/<run-id>/...contains immutable per-run logs, test artifacts, summaries, and debug evidence
On envctl --resume, the runtime:
- loads the latest compatible saved state
- reconciles saved services against live process/listener truth
- optionally restores missing services
- rewrites the state with the reconciled result
Useful inspection:
envctl show-state --json
envctl explain-startup --jsonThere are two different doctor surfaces:
envctl doctor --repo /path: installed-command verification for a target repoenvctl --doctor: runtime diagnostics for the current repo and scope
Examples:
envctl doctor --repo /absolute/path/to/repo
envctl --doctor
envctl --doctor --jsonThe runtime doctor surfaces state, runtime health, pointers, readiness, and recent structured failures.
Start with:
envctl show-config --json
envctl show-state --json
envctl explain-startup --json
envctl --doctor --jsonIf the issue is interactive, timing-related, or hard to reproduce:
ENVCTL_DEBUG_UI_MODE=deep envctl
envctl --debug-pack
envctl --debug-report
envctl --debug-lastThat usually answers:
- what config is active
- what startup decision
envctlis making - what state was saved
- whether doctor already sees pointer or runtime-health problems
- where the latest debug bundle lives
There are two different interactive concerns:
- dashboard backend
- selector implementation
Current behavior:
ENVCTL_UI_BACKEND=autokeeps the legacy interactive dashboard by defaultENVCTL_UI_EXPERIMENTAL_DASHBOARD=1makesautoprefer the Textual dashboard when availableENVCTL_UI_BACKEND=textualexplicitly requests the Textual dashboardENVCTL_UI_BACKEND=legacyexplicitly requests the legacy dashboardENVCTL_UI_BACKEND=non_interactiveforces snapshot-only behavior
Selector behavior is separate:
- dashboard target selectors default to the Textual selector
ENVCTL_UI_SELECTOR_IMPL=planning_styleenables the prompt-toolkit fallback pathENVCTL_UI_SELECTOR_IMPL=legacyis only a compatibility alias and still resolves to the Textual selector path
envctl test --failed reruns only the saved failed tests/files for the selected targets.
Behavior:
- backend reruns use saved exact test identifiers when the runtime could extract them
- frontend reruns use saved failed files
- reruns fail closed if the saved git state is stale
- if the prior full run failed before envctl could derive rerunnable selectors, the rerun path explains that instead of pretending no full run occurred
Useful forms:
envctl test --failed
envctl test --failed --skip-startup --load-stateHeadless start with inspection first:
envctl show-config --json
envctl explain-startup --json
envctl --headless --resumeTree planning flow:
envctl --list-trees --json
envctl --plan
envctl dashboard
envctl logs --all --logs-follow
envctl test --allRuntime triage flow:
envctl show-state --json
envctl --doctor --json
envctl --debug-report