Files
development-scripts-3darch/references/taskfile.md
root-at-skicandClaude Opus 5 6c8446cf90 Add layered environment model and taskfile composition rules
Fold two overlapping local skills (scripts-shell, shell-scripts-module)
into this one, keeping only what it did not already cover.

- Add references/environment-model.md: the three-layer .env selector model,
  why set -a dot-sourcing beats export $(grep|xargs), tracked samples, the
  no-re-sourcing rule for module readers, and the deployment boundary
  (repo-presence is not git-tracked is not deployed).
- Extend references/taskfile.md with the one-env-driven-task rule (no
  :default pairs) and composing commands through the task surface.
- Add the lifecycle-hook module type to references/module-patterns.md.
- Keep this skill's _moduleExampleV1_ naming as canonical; the retired
  skills' HL_V1_ and double-underscore dialects were not carried over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 22:45:32 +03:00

3.5 KiB

Taskfile Integration

Exposing module commands through the task runner.

Add a module Taskfile.yml only when its commands should be discoverable through task. A module that is only consumed by other modules does not need one.


1. Module Taskfile.yml

# https://taskfile.dev

version: "3"

tasks:
  initialize-one:
    cmds:
      - |
        ./.scripts/module-example-v1/api/initialize-one.sh {{.CLI_ARGS}}
    silent: true

Rules:

  1. Task names mirror the public api/ command names, unless an alias is deliberate. A task named differently from the script it runs makes task --list output impossible to map back to files.
  2. Forward {{.CLI_ARGS}} whenever the command accepts flags.
  3. silent: true suppresses task's own command echo, so only the script's logger output appears.
  4. Invoke the api/ wrapper — never a lib/ file directly.
  5. The path is repository-root-relative, matching how the scripts source their dependencies.

Arguments pass through after --:

task module-example-v1:initialize-one -- --name demo --dry-run

Without the --, task treats the flags as its own and the script never sees them.

One env-driven task, not a :default pair

Where the repository uses the layered environment model (references/environment-model.md), the env layers already supply every value a task needs. A module therefore needs one task, not a <task> + <task>:default pair that hardcodes what the environment already provides:

tasks:
  initialize-one:
    desc: Initialize using the .env layers (override with --flag=…)
    cmds:
      - ./.scripts/module-example-v1/api/initialize-one.sh {{.CLI_ARGS}}
    silent: true

A :default wrapper duplicates configuration in a second place, and the two drift. Environment-specific behaviour belongs in .env-<env>; one-off overrides belong in flags after --.

Composing commands

When an operator-facing command already exists, prefer calling it through task rather than re-implementing or sourcing its internals, and pass parameters explicitly:

task keycloak:realm-registration-enable-one -- --realm-id="${KEYCLOAK_ROOT_REALM_ID}"

Composing at the task surface keeps the public contract the only contract. Reaching into another module's lib/ couples you to its internals and bypasses its reader and validator.

Do not add a second entrypoint for an operation that already has one unless it carries clear operator value — duplicate entrypoints drift apart and it stops being obvious which is authoritative.


2. Root Taskfile.yml include

includes:
  module-example-v1: ./.scripts/module-example-v1/Taskfile.yml

Rules:

  1. Add a root include only for a module intended as a stable public task namespace. The include name becomes a permanent user-facing prefix; renaming it later breaks every documented command and every CI invocation.
  2. Choose a short, clear namespace. Existing examples include ca, kube, npmrc, repos-local, and repos-local-nodejs.
  3. Do not add includes for experimental or in-progress modules — run them through their api/ path until the interface settles.

3. Verifying

# Every included namespace and task, with descriptions
task --list-all

# Confirm a specific task resolves and forwards arguments
task <module-alias>:<task-name> -- --dry-run

task --list shows only tasks carrying a desc:; task --list-all shows everything. A task missing from --list-all means the root include is absent or its path is wrong.