Decisions

ADR-0014: setup:* Namespace as First-Use Setup Standard

Replaces dev:setup and dev:install with a setup:* namespace that models the distinct lifecycle of first-use setup operations independently from the daily developer workflow.

Proposed

Status: Proposed

Date: 2026-04-24

Deciders: Engineering Leadership

Supersedes Decision 4 of: ADR-0013: Taskfile Contract v2


Context

ADR-0013 Decision 4 prescribes dev:setup + dev:install as the clone-to-working entry points. The rationale was:

"dev:install stays useful standalone for 'just update my deps after a pull.'"

This works for repos where pnpm install / uv sync is unconditionally safe to run. It fails for repos with private registry dependencies, where installing without a valid auth token produces an error. In those repos, dev:install is not independently useful — it requires a preceding authentication step.

More broadly, the placement of setup tasks in dev:* creates a semantic mismatch:

Namespace intentActual use
dev:* = daily development lifecycledev:setup and dev:install run once per clone, rarely after
Daily: dev:run, dev:watch, dev:migrateFirst-use: env copy, registry auth, dep installation

Setup is a sequential, phased, rarely-run operation. Daily development commands are stateless and independently invocable. Grouping them in the same namespace obscures both.

transcript-service introduced setup:* organically to solve this. Its Taskfile shows the concrete problem:

setup:registry  →  must run before setup:install
setup:install   →  reads NPM_TOKEN from .env written by setup:registry

dev:install cannot model this dependency. dev:setup calling both internally hides the phases from developers who need to re-run just the registry auth step (e.g., after token expiry).

Why not keep dev:setup + dev:install?

The argument for retaining them is familiarity and backward compatibility. But:

  1. Familiaritytask setup is equally discoverable via task --list. No learning cost.
  2. Backward compatibility — Simple repos not using private registries don't need to migrate. The pattern is additive for them; prescriptive for repos with private deps.

The argument against dev:* placement is architectural: dev:* should describe operations that happen during development, not operations that enable development to begin. The distinction is meaningful to both humans and AI agents reading the Taskfile.


Decision

setup:* replaces dev:setup and dev:install as the prescribed pattern for first-use setup.

Standard tasks

TaskLevelPurpose
setupMUSTFull setup orchestrator: prepare → registry → install. Idempotent. The canonical entry point.
setup:prepareSHOULDCopy .env.example.env if missing.
setup:registrySHOULDAuthenticate to private registry and persist token to .env.
setup:installMUSTInstall project dependencies. Reads credentials from .env.
setup:cleanSHOULDRemove installed dependencies (node_modules/, .venv/). Does not touch dist/ — that is build:clean's responsibility.

The setup task (no suffix)

setup is the flat entry point developers run after cloning. It orchestrates the sub-tasks in sequence:

setup:
  desc: "First-use setup: configure env, authenticate registry, install deps"
  cmds:
    - task: setup:prepare
    - task: setup:registry
    - task: setup:install
    - echo ""
    - echo "Setup complete. Next steps:"
    - echo "  1. Review .env and set any required values"
    - echo "  2. task dev:run    — start the application"
    - echo "  3. task --list     — see all available operations"

dev:setup as optional alias

dev:setup MAY be kept as a convenience alias for repos that want to preserve discoverability under dev:*. If present, it MUST delegate to setup:

dev:setup:
  desc: "Alias for: task setup"
  cmds:
    - task: setup

It is not the canonical entry point. task setup is.

Relationship to deps:login:npm:env

setup:registry and deps:login:npm:env solve the same problem. setup:registry SHOULD be implemented by delegating to deps:login:npm:env rather than duplicating the logic:

setup:registry:
  desc: Obtain CodeArtifact token and persist to .env
  cmds:
    - task: deps:login:npm:env

deps:login:npm:env remains the canonical login recipe. setup:registry is its integration point in the setup flow.

setup:clean vs build:clean

These are distinct operations with different scopes:

TaskRemoves
setup:cleanInstalled dependencies (node_modules/, .venv/)
build:cleanBuild artifacts (dist/)

They may coexist. Running both returns the repo to a post-clone state.

When to use setup:*

Use setup:* when:

  1. The repo has private registry dependencies that require authentication before installation
  2. Setup has discrete phases that developers need to invoke independently (e.g., token rotation without reinstalling deps)

For repos with no private deps and no phased setup requirements, dev:setup + dev:install per ADR-0013 remains acceptable. No forced migration.


Consequences

Positive

  • dev:* is focused on daily operations — no first-use setup tasks polluting the namespace
  • Phases are independently invocable — token rotation, env reset, and install can run separately
  • Dependency between phases is explicitsetup:registry before setup:install is visible in the Taskfile, not hidden inside dev:setup
  • task setup is the clear onboarding entry point — discoverable, memorable, unambiguous

Negative

  • Two valid patterns exist — repos without private deps may use dev:setup/dev:install (ADR-0013); repos with private deps use setup:* (this ADR). Engineers working across repos will encounter both.
  • Migration required for repos with private deps — any repo currently using dev:setup that internally authenticates to a private registry should migrate to setup:* to make the phases explicit.

Migration

  • Repos with no private registry deps: no migration required. dev:setup + dev:install continues to work.
  • Repos with private registry deps: migrate to setup:*. The dev:setup alias is optional but recommended during transition.
  • New repos with private deps MUST use setup:* from the start.

Examples

Basic setup (no private registry)

setup:
  desc: "First-use setup: configure env, install deps"
  cmds:
    - |
      if [ ! -f .env ]; then
        cp .env.example .env
        echo "Created .env from .env.example — review and adjust values."
      fi
    - task: setup:install
    - task: validate:structure
    - echo ""
    - echo "Setup complete. Run: task dev:run"

setup:install:
  desc: Install project dependencies
  cmds:
    - pnpm install  # or: uv sync

setup:clean:
  desc: Remove installed dependencies
  cmds:
    - rm -rf node_modules/

Setup with CodeArtifact (private registry)

setup:
  desc: "First-use setup: configure env, authenticate registry, install deps"
  cmds:
    - task: setup:prepare
    - task: setup:registry
    - task: setup:install
    - echo ""
    - echo "Setup complete. Next steps:"
    - echo "  1. Review .env and set any required values"
    - echo "  2. task dev:run    — start the application"
    - echo "  3. task --list     — see all available operations"

setup:prepare:
  desc: Copy .env.example to .env if not present
  cmds:
    - |
      if [ ! -f .env ]; then
        cp .env.example .env
        echo "Created .env from .env.example — review and adjust values."
      else
        echo ".env already exists, skipping."
      fi

setup:registry:
  desc: Obtain CodeArtifact token and persist to .env
  cmds:
    - task: deps:login:npm:env

setup:install:
  desc: Install project dependencies
  dotenv: ['.env']
  cmds:
    - pnpm install

setup:clean:
  desc: Remove installed dependencies
  cmds:
    - rm -rf node_modules/

References