Master context document for AI coding agents contributing to this repository. Every agent MUST read this file before starting work.
tfenv is a Terraform version manager written in Bash, modelled after rbenv.
~2.5k LOC across bin/, lib/, libexec/, test/, and share/.
- Language: Bash (no shellcheck — deliberate choice; see ADR-0003)
- Default branch:
master - Current release: v3.2.0 (April 2026)
- Maintainer: Mike Peachey / Jaz (@Zordrak)
- Minimal dependencies: bash, curl, grep/ggrep, sort, unzip
- License: MIT
bin/ Entry points (terraform shim, tfenv command)
lib/ Shared libraries sourced by multiple scripts
libexec/ Subcommands (tfenv-install, tfenv-use, etc.)
test/ Integration tests (download real Terraform binaries)
share/ Static assets (HashiCorp PGP keys)
.github/ CI workflows, agent definitions, issue templates
agents/ Agent definition files (.agent.md)
instructions/ Auto-loaded coding standards
ISSUE_TEMPLATE/ Structured issue forms
docs/
adr/ Architecture Decision Records
Every libexec/ script contains its own boilerplate for resolving
TFENV_ROOT and sourcing helpers. This is intentional — each script
must be executable in isolation. Do NOT refactor this into a shared loader.
See ADR-0002.
ALL code changes MUST happen in a git worktree under .worktrees/. The main
working tree is shared by all agents and the human — modifying it directly
risks conflicts. .worktrees/ is gitignored.
# Create a worktree for your work
git worktree add .worktrees/fix-123 -b fix/123-description master
# Do all work inside the worktree
cd .worktrees/fix-123
# Clean up when done (after PR is created)
cd /path/to/main/tree
git worktree remove .worktrees/fix-123Exception: Read-only operations (searching, reading files, running tests) may use the main working tree.
- Run the test suite before committing
- Read the diff before pushing
- Check your own work before calling it done
- Fix problems when you find them — do not defer
The project deliberately has minimal dependencies: bash, curl, grep/ggrep, sort, and unzip. Do NOT add new external tools or packages without explicit approval from the maintainer.
The project does not use ShellCheck. This is a deliberate architectural decision. Do NOT add shellcheck directives, configs, or CI steps. See ADR-0003.
- Never commit directly to
master. Always use a feature branch. - Branch naming:
fix/<short-description>for bugs,feat/<short-description>for features,chore/<short-description>for maintenance. Reference the issue number where applicable (e.g.fix/406-unbound-requested-variable). - One logical change per branch/PR. Do not bundle unrelated fixes.
- PRs target
master. There is no develop or staging branch. - Merge strategy: The repo uses merge commits (not squash). Do not force-push or rebase shared branches.
Freeform — no conventional commits standard is enforced. Be descriptive.
Reference issue numbers where applicable (e.g. Fix #406: ...).
Historical examples:
Fix realpath not available on macOSCope with different line endings in .terraform-versionReduce duplication, and add safety
Before starting work on any issue, an agent MUST claim it:
- Add the
agent:in-progresslabel to the issue - Post a comment:
Claimed by <agent-name>. Working on this. - If the label is already present, the issue is claimed — do NOT work on it
This acts as a distributed lock preventing duplicate effort when multiple agents run concurrently.
When work is complete (PR created), remove agent:in-progress and add
agent:review-requested.
# Claim an issue
gh issue edit NNN --add-label 'agent:in-progress'
gh issue comment NNN --body 'Claimed by <agent-name>. Working on this.'
# Release after PR created
gh issue edit NNN --remove-label 'agent:in-progress' --add-label 'agent:review-requested'The gh CLI is the primary and load-bearing interface to GitHub for all
agents. Use gh api repos/tfutils/tfenv/... for operations not covered by
high-level gh commands.
The GitHub MCP server may supplement gh where proven reliable, but must
never be on the critical path.
Each work type is owned by a specific agent. If you receive a request that belongs to a different agent, say so and stop.
| Work Type | Owning Agent | Description |
|---|---|---|
| Autonomous delivery orchestration | developer |
Assess board, dispatch specialists |
| Bug hunting and auditing | bug-finder |
Find defects, file structured issues |
| Bug fixing | bug-fixer |
Implement fixes with tests |
| Feature design and specification | feature-designer |
Write detailed specs as issues |
| Feature implementation | feature-implementer |
Build from specs with tests |
| Code review | reviewer |
First-pass structured PR review |
| Architecture and design decisions | architect |
ADRs, decomposition, trade-off analysis |
| Documentation quality | documenter |
Cross-doc consistency, link integrity |
| Delivery metrics and reporting | evaluator |
Compute metrics from gh data |
| Board management and triage | pm |
Organise backlog, recommend priorities |
| Release management | releaser |
CHANGELOG, tags, GitHub releases |
Integration tests that download and install real Terraform binaries from HashiCorp. They are not fast, they are not unit tests, and they require network access.
# Install dependencies (macOS only — installs ggrep via Homebrew)
./test/install_deps.sh
# Run all test suites
./test/run.sh
# Run a specific test suite
./test/run.sh test_install_and_use.shtest/test_common.shprovides setup and helperstest/run.shdiscovers and runs alltest_*files- Helper functions:
error_and_proceed(log failure, continue),check_active_version,check_installed_version,check_default_version,cleanup - Do not use
error_and_die— it does not exist in the test context
test/run.sh ends with exit 0 regardless of test failures. It logs
failures but the exit code does not reflect them. This means CI may not
catch test regressions. Be aware and check test output manually.
| Trigger | Platforms |
|---|---|
| Pull request | macos-latest, ubuntu-latest |
| Push to master | ubuntu-24.04, ubuntu-22.04, macos-14, macos-13, windows-2025, windows-2022 |
CI also builds the Dockerfile and validates it on Ubuntu runners.
Releases are manual and infrequent. The releaser agent handles the
judgement-intensive parts but always requires human confirmation.
- Accumulate changes on
master - Update
CHANGELOG.mdwith a new version section using the format:Categories:## X.Y.Z (Month Day, Year) * CATEGORY: Description (Contributor Name <email>)BREAKING CHANGE,NEW FEATURE,FIX,MAJOR THANKS - Create an annotated tag:
git tag -a vX.Y.Z -m "tfenv vX.Y.Z" - Push the tag:
git push origin vX.Y.Z - Create a GitHub Release from the tag
- Update
Dockerfileif it hardcodes the version
Agents must not create releases or tags without explicit instruction from the maintainer.
Issues use a namespaced label system:
| Namespace | Labels | Purpose |
|---|---|---|
type: |
bug, feature, chore, question |
What kind of work |
severity: |
critical, high, medium, low |
Impact of bugs |
priority: |
critical, high, medium, low |
When to address |
complexity: |
trivial, small, medium, large |
Effort estimate |
confidence: |
confirmed, probable, speculative |
Bug certainty |
category: |
install, use, list, uninstall, version-resolution, verification, platform, logging |
Affected area |
agent: |
in-progress, review-requested |
Agent workflow state |
- Do not run
git pushtomasteror any remote branch without explicit approval from Jaz - Do not create GitHub releases or tags without explicit instruction
- Do not close or lock issues — only the maintainer triages
- Do not refactor the standalone boilerplate in
libexec/scripts - Do not add shellcheck directives, configs, or CI steps
- Do not add new dependencies without discussion
- Do not modify
.github/workflows/without explicit approval — CI changes affect all platforms and have outsized blast radius - Do not merge PRs — create them and leave for human review
- Do not modify Terraform structure (if any) without explicit approval from Jaz — content changes are fine, structural changes are not
- Do not write to
/tmpor/dev/null— use.tmp/in the workspace root for temporary files - Do not remove tool permissions from agent definitions — granted
permissions (
tools:in frontmatter) are the maintainer's decision. Agents may recommend changes but must not unilaterally reduce access.
- Run the test suite locally and verify all tests pass. Do not rely solely on CI — the exit code bug means CI may report green on failure.
- Read the diff carefully. Bash quoting and operator precedence bugs are the most common class of defect in this codebase.
- Test on at least one platform. If you only have Linux, note that
in the PR. macOS and Windows have different
readlink,grep, andsedbehaviours. - Do not modify the test runner exit code bug as a drive-by fix in an unrelated PR. It should be its own tracked change.
These are the most frequent sources of bugs. Check for them in every change:
- Shell operator precedence:
cmd1 || cmd2 | cmd3meanscmd1 || (cmd2 | cmd3), not(cmd1 || cmd2) | cmd3. - Unquoted variables: Always quote
"${var}"in arguments, conditions, and assignments. Unquoted variables cause word-splitting. - Double-quoted traps:
trap "rm ${var}" EXITexpands at definition time and is vulnerable to word-splitting. Use functions or single quotes. $@in for-loops: Always quote:for arg in "$@".- Regex anchoring:
^1.1matches1.10.xbecause.is a regex wildcard. Use^1\.1\.for exact prefix matching. - Cross-platform differences: macOS uses BSD
sed/grep/readlink. GNU extensions may not be available. The project installsggrepon macOS but uses the systemsedandreadlink. set -uo pipefail: All scripts run with strict mode. Undefined variables cause immediate crashes. Use${var:-default}for optional variables.- Carriage returns:
.terraform-versionfiles from Windows/WSL may contain\r. Always strip withtr -d '\r'after reading.
| Document | Purpose |
|---|---|
| .github/copilot-instructions.md | Copilot configuration, auto-loading |
| .github/instructions/bash.instructions.md | Bash coding standards (auto-loaded for **/*.sh) |
| SECURITY.md | Vulnerability reporting policy |
| docs/adr/ | Architecture Decision Records |
| CHANGELOG.md | Release history |