# Workflows Creating a module, changing an existing one, and the checks to run before delivering either. --- ## 1. Creating a new module 1. **Confirm the module name and namespace** — the kebab-case directory name, the `_camelCase_` function prefix, and the `UPPER_SNAKE_` environment prefix all derive from it and must agree. 2. **Inspect nearby modules** for a comparable pattern before designing a new one. Matching an existing module beats inventing a variant. 3. **Copy the scaffold:** ```bash cp -R assets/module-example-v1 .scripts/ ``` 4. **Rename throughout** — every `module-example-v1` path, `MODULE_EXAMPLE_V1_`, and `_moduleExampleV1_`: ```bash cd .scripts/ grep -rl 'module-example-v1\|MODULE_EXAMPLE_V1_\|_moduleExampleV1_' . | while read -r f; do sed -i '' \ -e 's|module-example-v1||g' \ -e 's|MODULE_EXAMPLE_V1_|_|g' \ -e 's|_moduleExampleV1_|__|g' "$f" done grep -rn 'module-example-v1\|MODULE_EXAMPLE_V1_\|_moduleExampleV1_' . || echo "rename complete" ``` (`sed -i ''` is BSD/macOS; use `sed -i` on GNU.) 5. **Set the defaults and flags** in `lib/--env-vars-reader.sh`, using `${VAR:-default}`. 6. **Set the required-input checks** in `lib/--env-vars-validator.sh`. 7. **Write implementation files** as `lib/-.sh`, one function each. 8. **Source every implementation file** from `lib/--index-api.sh`. 9. **Add `api/.sh` wrappers** for user-facing commands only. 10. **Add a module `Taskfile.yml`**, and a root include only if the namespace is stable — `references/taskfile.md`. 11. **Make scripts executable** where they will be invoked directly: `chmod +x .scripts//api/*.sh`. 12. **Validate** — section 3. 13. **Update the repository's script documentation** if it has a memory bank or equivalent. --- ## 2. Changing an existing module 1. **Read `lib/--index.sh` and `lib/--index-api.sh` first.** They show what the module loads and in what order, which determines where a change can safely go. 2. **Decide which layer the change belongs to:** | The change is | It belongs in | |---|---| | A new user-facing command | `api/` + `lib/-.sh` + `--index-api.sh` | | New behaviour in an existing command | that command's `lib/-.sh` | | A new CLI flag or default | `--env-vars-reader.sh` | | A new required input | `--env-vars-validator.sh` | | Task-runner exposure | module `Taskfile.yml`, and the root include if public | 3. **Preserve the existing naming and function prefixes.** Consistency inside a module beats matching a newer convention used elsewhere — mixed prefixes in one module are worse than an old prefix used uniformly. 4. **Add every new `lib/-*.sh` to `--index-api.sh`** as you create it, not afterwards. 5. **Add `api/` wrappers only for user-facing commands**; internal helpers stay in `lib/`. 6. **Validate** — section 3. 7. **Update the script documentation** when public behaviour changes. --- ## 3. Validation checklist - [ ] Public commands are under `api/`; reusable implementation is under `lib/`. - [ ] `api/` wrappers are thin — source `lib/--index.sh`, call one function. - [ ] `lib/--index.sh` sources reader, validator, API index, in that order. - [ ] `lib/--index-api.sh` sources every implementation file the wrappers need. - [ ] New CLI flags are parsed in `--env-vars-reader.sh`, using `${VAR:-default}`. - [ ] Required variables are validated before use. - [ ] Logs use `_loggers_*` helpers, and logging actually produces output. - [ ] No secret values are printed or documented. - [ ] Implementation functions `return` rather than `exit`. - [ ] Taskfile entries invoke `./.scripts//api/.sh {{.CLI_ARGS}}`. - [ ] A root `Taskfile.yml` include was added only if intended. - [ ] `bash -n` passes for every changed script. - [ ] The command was actually run once from the repository root. - [ ] Script documentation is updated when public behaviour changed. --- ## 4. Validation commands ```bash # Syntax check every script in the module find .scripts/ -name '*.sh' -print0 | xargs -0 -n1 bash -n # Every implementation file is wired into the API index. # Note the `--` before the pattern: implementation basenames start with a dash, # which grep would otherwise parse as an option flag and report every file. cd .scripts/ for f in lib/-*.sh; do case "$f" in lib/--*) continue;; esac grep -qF -- "$(basename "$f")" lib/--index-api.sh || echo "NOT INDEXED: $f" done # Task exposure resolves task --list-all # Run it — the only check that proves the wiring works ./.scripts//api/.sh --dry-run ``` `bash -n` proves a file parses. It does **not** prove a function is reachable, that a flag is handled, or that logging is bootstrapped — all three fail silently at runtime while every static check passes. The final run is the one that matters; a `--dry-run` flag exists to make it cheap and safe.