---
title: "Chapter 05: Fast Feedback and Automatic Shells"
description: "Use targeted tasks for fast feedback and configure direnv with an explicit trust boundary."
type: "lesson"
order: 5
chapter: "02-services-and-contracts"
duration: "45–90 minutes"
---

# Chapter 05: Fast Feedback and Automatic Shells

## Objective

Use targeted tasks for fast feedback and configure direnv with an explicit trust boundary.

Your tutor should stop at each gate and wait for your evidence before continuing.

## Concept

Different feedback has different costs. A formatter is quick; a database test needs a running service. Expose both through a small vocabulary so an agent can iterate cheaply and still finish with full validation.

Our `check:all` task depends on format, lint, and unit tasks. Those tasks only read files, so they can safely run concurrently. A formatter that rewrites files must not race with tests reading them. A database integration task also needs readiness and cleanup; simply naming it `check:integration` does not start PostgreSQL.

direnv automates entering an environment when you change directories. Its approval is a trust decision: `.envrc` is executable shell code. It belongs outside the project’s self-bootstrapping paradox—the tool must be available in your ordinary shell before it can activate the project.

## Concept check

**Q1.** Why is `ruff format --check` suitable for a parallel check graph, while `ruff format` needs care?

**Q2.** Does adding direnv to project packages install your ordinary shell’s direnv hook?

<details>
<summary>Concept answer key — attempt first</summary>

## Answer key (concept)

**Q1.** The first checks without rewriting; the second mutates files. **Pass criteria:** explain a read/write race.

**Q2.** No; bootstrap direnv and its shell hook separately. **Pass criteria:** identify the activation dependency.

</details>

## Gate: Run targeted tasks

```bash
devenv tasks run check:unit
devenv tasks run check:all
devenv test
```

**Pass:** the unit task runs the suite, the aggregate includes all three declared checks, and the final command also runs the database contract after Chapter 04. Compare elapsed times. Tasks use `after` to name prerequisites; consult the task documentation before changing execution modes.

Optional advanced wiring can attach tasks before `devenv:enterTest`. The lab keeps `enterTest` explicit so service lifecycle and the final gate remain easy to audit.

Current devenv also offers native auto activation through `devenv hook`; the upstream guide recommends that route for many workflows. This lesson uses the still-supported direnv integration to make its explicit approval boundary visible. Choose one activation mechanism.

## Gate: Install and verify direnv

```bash
nix profile install nixpkgs#direnv
direnv version
```

For zsh on the Mac, add the following once to `~/.zshrc`:

```bash
eval "$(direnv hook zsh)"
```

For Bash, use the Bash hook in the appropriate interactive startup file. Open a new terminal and verify `direnv version` before creating `.envrc`.

## Gate: Review the integration

Use the `.envrc` generated by your installed devenv, if present. Otherwise create this documented integration:

```bash
watch_file devenv.nix
watch_file devenv.yaml
watch_file devenv.lock
eval "$(devenv direnvrc)"
use devenv
```

Read it and the referenced project files, then run:

```bash
direnv allow
python -c 'import sys; print(sys.executable)'
```

**Pass:** the project Python is selected when entering the directory. Leave the directory and verify that direnv unloads its changes. A tool may still exist outside the project if it was installed independently; unloading does not uninstall it.

## Gate: Change a dependency deliberately

Add `pkgs.tree` to the package list. Re-enter or reload the shell, run `tree --version`, review the Nix diff, and commit it. No agent should repair an undeclared project dependency by silently changing your host.

## Known failure modes

- `.envrc` is not automatically trustworthy because it is in Git.
- A task named `check:all` can omit integration tests; the documented final gate remains `devenv test`.
- Modified-file caching can hide environment-dependent failures. Keep acceptance checks uncached until their inputs are well defined.
- Auto activation can be convenient for a human; CI should invoke commands explicitly.

## Check your understanding

**Q3.** What is the final gate after a targeted test passes?

**Q4.** What should you inspect before approving an unfamiliar `.envrc`?

<details>
<summary>Answer key — attempt every question first</summary>

## Answer key

**Q3.** `devenv test`, followed by review. **Pass criteria:** distinguish fast iteration from completion.

**Q4.** Its commands and referenced scripts/configuration. **Pass criteria:** recognize code execution at directory entry.

</details>

## References

- [Tasks and dependencies](https://devenv.sh/tasks/)
- [direnv integration](https://devenv.sh/integrations/direnv/)
- [direnv shell hooks](https://direnv.net/docs/hook.html)

---

[← Chapter 04](/nix-agent-lab/02-services-and-contracts/04-real-services/) · [Next: Chapter 06 →](/nix-agent-lab/02-services-and-contracts/06-specs-and-agent-memory/)
