Files
development-scripts-3darch/references/module-patterns.md
T
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

75 lines
3.3 KiB
Markdown

# Module Patterns
A decision guide for what kind of module you are building and how it should connect to the
others.
---
## 1. Module type
| Module type | Has `api/` | Has `Taskfile.yml` | Example |
|---|:---:|:---:|---|
| Support-only library | No | No | `loggers` |
| Shared foundation with public commands | Yes | Optional / no | `base` |
| Feature module | Yes | Usually yes | `kube`, `github`, `data-migration` |
| Wrapper around an external CLI | Yes | Usually yes | `helm`, `openshift`, `github` |
| Lifecycle hook | Yes | No | `devbox` (an `on-init-hook`) |
A **lifecycle hook** module is invoked by external tooling at a fixed moment — a devbox init
hook, a container entrypoint, a git hook — rather than by an operator. It keeps `api/` because
the tool calls that path directly, and skips `Taskfile.yml` because nobody runs it by hand.
Its implementation commonly branches per platform (`-install-dependencies-osx.sh`,
`-install-dependencies-linux-ubuntu.sh`), one file per branch, sourced from `--index-api.sh`
like any other implementation file.
Pick the type before creating any file — it decides whether `api/` and `Taskfile.yml` exist at
all, and adding them later means reworking every consumer that has already started sourcing
the module.
A module with no user-facing command is a support library. Do not give it an `api/` directory
speculatively; every file in `api/` is a public interface someone may come to depend on.
---
## 2. Dependency sourcing
| You need | Source |
|---|---|
| Logging | `./.scripts/loggers/lib/--index-api.sh` |
| Shared base functions | `./.scripts/base/lib/--index-api.sh` |
| Another module's exported functions | that module's `lib/--index-api.sh` |
| Your own module's full runtime, from its `api/` wrapper | your `lib/--index.sh` |
The rule underneath: **`--index-api.sh` is the public edge between modules; `--index.sh` is a
module's own private entrypoint.** Sourcing another module's `--index.sh` runs its reader and
validator inside your command — importing its defaults, its required-input checks, and in
`base`'s case its `.env` loading. Symptoms show up as variables nobody set, or a validation
failure naming a module the user never invoked.
---
## 3. Command exposure
Three independent decisions, in increasing order of commitment:
1. **`lib/` only** — internal. Reachable from other modules that source your `--index-api.sh`.
2. **`api/<command>.sh`** — user-facing. Someone can run it directly from the repository root.
3. **Module `Taskfile.yml`** — discoverable through `task --list-all`.
4. **Root `Taskfile.yml` include** — a stable public namespace, effectively permanent.
Escalate only as far as the command genuinely needs. Each step is easy to add later and
disruptive to withdraw, because each one is a promise someone may already be relying on.
---
## 4. Common mistakes
- Putting full implementation logic into `api/*.sh`.
- Adding a `lib/-*.sh` file and forgetting to source it from `lib/--index-api.sh`.
- Parsing CLI flags in implementation files instead of `--env-vars-reader.sh`.
- Printing secrets in logs.
- Sourcing `base/lib/--index.sh` from a library function and unexpectedly loading `.env`.
- Adding root Taskfile includes for experimental modules.
- Giving a support-only library an `api/` directory it does not need.
- Mixing function prefixes inside one module after a rename.