diff --git a/bash-styleguide b/bash-styleguide new file mode 100644 index 0000000..d34860e --- /dev/null +++ b/bash-styleguide @@ -0,0 +1,852 @@ +# CE OS Script Style Guide — Bash + + + + + +--- + +## Purpose + +This document defines the mandatory style and structural standards for all +Bash scripts produced under the CE OS / Cervello Elettrico umbrella. Every +script — installer, utility, tier component, library, or one-off tool — must +conform to these rules before it is considered fit for review, testing, or +promotion. + +A Python style guide will be produced separately once sufficient CE OS Python +use cases exist to ground it in real experience rather than theory. + +These rules encode hard-won lessons from real hardware deployment across Alpine +aarch64, Alpine armhf, Alpine x86, Debian amd64, and NixOS. They exist because +silent failures, missed cleanups, and untested assumptions have caused real +problems on real machines. Follow them without exception. + +CE OS scripts are also teaching documents. An MUO-level reader — someone +competent but not a professional developer — should be able to follow any +script and understand what it does and why. Clarity is not optional. + +--- + +## Part 1 — Universal Rules + +These rules apply to every Bash script regardless of its role (orchestrator, +subscript, or library). + +### 1.1 Attribution header + +Every script — without exception — carries this attribution in its header: + +``` +Created by John A. Hoeven with the ethical assistance of Claude AI +``` + +This is non-negotiable. It appears in every file, every time. + +### 1.2 Version and status + +Every script header includes version and status: + +``` +Version: v0.0.1 | Status: DEVELOPMENT +``` + +Version tracks follow CE OS conventions: + +| Stage | Format | Increment rule | +|-------------|----------|---------------------------------------| +| Development | `v0.0.x` | Third point on every meaningful edit | +| Testing | `v0.x.x` | Second point on milestones | +| Stable | `vX.Y` | Major/minor on scope/compatibility | + +### 1.3 Update and upgrade first + +Every script that installs or modifies packages must run the OS package +manager's update and upgrade sequence as its very first system-touching +action — before any package installation, before any capability test. + +The exact command depends on the detected OS (see §2.6 and §3.6). No +exceptions. A script that installs packages without first updating the +package index is defective. + +### 1.4 Architecture awareness + +Every script that installs packages must: + +1. Detect the running architecture (`uname -m`) +2. Test that each required package is available for that architecture + before attempting installation +3. Log clearly which packages are unavailable and why +4. Never silently skip a package — always surface the gap to the OHIOD + +### 1.5 Test before, during, and after + +Testing is not optional and not an afterthought. Every script has three +explicit test gates: + +- **Pre-flight** — before any system changes. Checks environment, + dependencies, architecture, disk space, connectivity. All warn-only. + No changes made at this stage. +- **Inline** — after each significant operation. Verifies the operation + succeeded before proceeding. Hard-fail on critical operations; warn-only + on optional components. +- **Post-install** — after all operations complete. Verifies installed + tools, written config files, service state. Hard-fail if required + components are absent. + +Test results are written to the log file with timestamps. A script with +no test gates is incomplete. + +### 1.6 Cleanup before exit + +Every script registers a cleanup handler that runs on all exits — normal, +error, and signal. The cleanup handler must: + +- Remove all temporary files and directories created by the script +- Clear the package manager's download cache +- Remove any lock files created by the script +- Log that cleanup completed + +A script that leaves debris is defective. + +### 1.7 No silent failures + +Every error is logged and surfaced. A script that swallows errors and +reports success is worse than one that fails loudly. If an operation +cannot be completed, say so clearly, log it, and either hard-fail or +warn — never silently skip. + +### 1.8 Credentials + +Never write real credentials in any script, config file, or comment. +Use `[PLACEHOLDER]` format always. This applies to passwords, tokens, +API keys, private keys, and any other secret material. + +### 1.9 Never run as root + +CE OS scripts run as the OHIOD user and invoke `doas` for privileged +operations. Scripts must not require or assume direct root execution. +If a script is accidentally run as root, it should warn and exit. + +### 1.10 Script length limits + +CE OS Bash scripts follow these length limits. Lines are counted as written, +including comments — comments are part of the script, not overhead. + +| Script role | Soft limit | Hard limit | Notes | +|---|---|---|---| +| Subscript | 150 lines | 200 lines | Split or extract at hard limit | +| Library | 150 lines | 200 lines | Split by function group if needed | +| Orchestrator | None | None | Coordinators by nature; no logic to limit | + +**Soft limit (150):** Review the script. Can a function be extracted to a +library? Can a phase be split into a subscript? If yes, do it. If the script +is genuinely cohesive at this length, proceed — but document why in the header. + +**Hard limit (200):** Split or extract. This is the default action. No +argument is needed — just do it. + +**Hard limit override:** In rare cases, keeping a script intact beyond 200 +lines is the correct decision. This is permitted, but must be declared +explicitly in the script header: + +```bash +# Line limit override: +``` + +A silent override is not an override — it is a defect. + +Orchestrators are excluded from all limits. An orchestrator that sources +five libraries and calls twelve subscripts in sequence may be 300 lines of +clean, readable flow control. That is correct. Logic belongs in libraries +and subscripts; the orchestrator reads like a table of contents. + +### 1.11 Scripts as teaching documents + +CE OS scripts are read by people learning the system, not only by people +maintaining it. Write accordingly. + +- **Comments explain intent, not mechanics.** `# Install vim` is noise. + `# vim is the CE OS default editor — required for all tiers` is information. +- **Complex functions belong in libraries.** A 50-line function embedded + in a subscript forces the reader to context-switch mid-flow. Extracted + to a library with a clear header comment, it can be read and understood + in isolation. +- **If a function needs a comment block longer than itself, extract it.** + The comment is telling you the function is too complex to live inline. +- **The orchestrator is the lesson plan.** A reader following an orchestrator + should understand the full install flow without opening a single sourced + file. Each call to a subscript or library function should be self-evident + from its name and any inline comment. +- **Target register: MUO level.** Make It Understandable and Obvious. + A competent but non-professional reader should be able to follow the logic. + If they cannot, the script needs work — not the reader. + +--- + +## Part 2 — Bash Style Guide + +### 2.1 Shebang and script class + +CE OS uses two Bash script classes. Choose the correct one: + +**Class A — System scripts** +OpenRC service scripts, `/etc/local.d/` scripts, bootstrap phases, +anything that runs before CE OS layers are confirmed present. + +```sh +#!/bin/sh +# POSIX sh / ash only. No bash features. +``` + +**Class B — CE OS scripts** +All installer scripts, operational scripts, tier components, user-facing +tools. Everything written for CE OS day-to-day use. + +```bash +#!/usr/bin/env bash +# Bash features permitted. BusyBox tools on PATH. +``` + +When in doubt, write Class B. Use Class A only when you have a specific +reason to require ash compatibility. + +**The bootstrap exception:** The opening lines of an installer that runs +before bash is confirmed must be ash-safe, then hand off: + +```sh +#!/bin/sh +# Bootstrap phase — ash only until bash is confirmed +command -v bash >/dev/null 2>&1 || { + echo "Installing bash..." + doas apk add bash +} +exec bash "$0" "$@" +# Everything below this line is normal CE OS bash +``` + +### 2.2 Full header block + +Every CE OS Bash script carries this header in full: + +```bash +#!/usr/bin/env bash +# Created by John A. Hoeven with the ethical assistance of Claude AI +# --------------------------------------------------------------------------- +# .sh +# /opt/ceos/scripts/.sh +# Version: v0.0.1 | Status: DEVELOPMENT +# --------------------------------------------------------------------------- +# Purpose: One-line description of what this script does. +# Target: Which nodes / contexts this runs on (e.g. Alpine aarch64 Pi 5) +# Entry: How it is invoked (e.g. doas bash phase1.sh, or ./tool.sh --flag) +# Depends: ce-common.sh, ce-test-lib.sh (list sourced libraries) +# --------------------------------------------------------------------------- +# Phases: +# 0 — Pre-flight (environment checks, no system changes) +# 1 — +# 2 — +# N — Cleanup (always runs via trap) +# --------------------------------------------------------------------------- +``` + +All fields are mandatory. Leave none blank. If a field does not apply, +write `N/A` — do not delete the line. + +### 2.3 Strict mode — selectively applied + +Do **not** use `set -e` in CE OS scripts. It causes unpredictable exits +on operations that are allowed to fail (package availability checks, +optional installs, test assertions). CE OS scripts handle errors explicitly. + +Use these instead: + +```bash +set -u # Treat unset variables as errors +set -o pipefail # Propagate pipe failures +``` + +Apply `set -u` and `set -o pipefail` after sourcing libraries. Handle +each potential failure explicitly with conditional checks. + +### 2.4 Variables and naming + +```bash +# Constants — UPPER_SNAKE_CASE +readonly SCRIPT_VERSION="v0.0.1" +readonly SCRIPT_NAME="$(basename "$0")" +readonly LOG_DIR="${HOME}/.local/logs/ceos_installer" +readonly ARCH="$(uname -m)" + +# Local variables — lower_snake_case +local pkg_name="" +local install_result=0 + +# Loop variables — short, descriptive +for pkg in "${packages[@]}"; do ... + +# Arrays — UPPER for constants, lower for mutable +readonly REQUIRED_PKGS=( bash vim tmux ) +available_pkgs=() +``` + +Always quote variable expansions: `"${VAR}"` not `$VAR`. Always. + +### 2.5 Functions + +```bash +# Function declaration style — no function keyword +check_architecture() { + local expected_arch="${1:?check_architecture: expected_arch required}" + local actual_arch + actual_arch="$(uname -m)" + + if [[ "${actual_arch}" != "${expected_arch}" ]]; then + log_warn "Architecture mismatch: expected ${expected_arch}, got ${actual_arch}" + return 1 + fi + + log_info "Architecture confirmed: ${actual_arch}" + return 0 +} +``` + +Rules: +- No `function` keyword — use `name() {` style +- Every function documents its arguments with `${VAR:?message}` or explicit checks +- All functions `return 0` on success, non-zero on failure +- Local variables declared with `local` — never pollute global scope +- Functions are grouped: library sourcing, then utilities, then phases, then main + +### 2.6 Package manager abstraction + +Detect the OS and set package manager variables at script start: + +```bash +detect_package_manager() { + if command -v apk >/dev/null 2>&1; then + PKG_MANAGER="apk" + PKG_UPDATE="apk update" + PKG_UPGRADE="apk upgrade" + PKG_INSTALL="apk add" + PKG_CACHE_CLEAN="rm -rf /var/cache/apk/*" + PKG_QUERY="apk info -e" + elif command -v apt-get >/dev/null 2>&1; then + PKG_MANAGER="apt" + PKG_UPDATE="apt-get update" + PKG_UPGRADE="apt-get upgrade -y" + PKG_INSTALL="apt-get install -y" + PKG_CACHE_CLEAN="apt-get clean && rm -rf /var/lib/apt/lists/*" + PKG_QUERY="dpkg -l" + elif command -v nix-env >/dev/null 2>&1; then + PKG_MANAGER="nix" + PKG_UPDATE="nix-channel --update" + PKG_UPGRADE="nixos-rebuild switch" + PKG_INSTALL="nix-env -iA" + PKG_CACHE_CLEAN="nix-collect-garbage -d" + PKG_QUERY="nix-env -q" + else + log_error "No supported package manager found" + exit 1 + fi + log_info "Package manager: ${PKG_MANAGER}" +} + +# Run update and upgrade BEFORE any package installs +run_package_update() { + log_info "Running package update..." + doas ${PKG_UPDATE} || { log_error "Package update failed"; exit 1; } + log_info "Running package upgrade..." + doas ${PKG_UPGRADE} || { log_error "Package upgrade failed"; exit 1; } + log_info "Package update and upgrade complete" +} +``` + +### 2.7 Architecture-aware package testing + +Before installing any package, test availability for the running architecture. +For Alpine, the search order is: + +1. Alpine stable (main) +2. Alpine community +3. Alpine edge +4. CE APK repo on Forgejo (`CE_APK_REPO` — packages that must be built for CE OS) + +Set the CE repo URL as a constant at the top of any script that may need it: + +```bash +# CE APK repository — packages built by Cervello Elettrico +# Replace [PLACEHOLDER] with the live Forgejo APK repo URL when available +readonly CE_APK_REPO="[PLACEHOLDER]" +``` + +```bash +test_package_available() { + local pkg="${1:?test_package_available: pkg required}" + local arch="${ARCH}" + + case "${PKG_MANAGER}" in + apk) + # Check official Alpine repos (stable, community, edge) first + if apk search -x "${pkg}" 2>/dev/null | grep -q "^${pkg}-"; then + log_info " FOUND (Alpine repo): ${pkg}" + return 0 + fi + + # Fall back to CE APK repo on Forgejo + if [[ -n "${CE_APK_REPO:-}" ]] && \ + curl --silent --max-time 5 \ + "${CE_APK_REPO}/${arch}/APKINDEX.tar.gz" \ + | tar -xzO 2>/dev/null \ + | grep -q "^P:${pkg}$"; then + log_info " FOUND (CE repo): ${pkg}" + return 0 + fi + ;; + apt) + apt-cache show "${pkg}" >/dev/null 2>&1 && return 0 + ;; + nix) + nix-env -qaP "${pkg}" >/dev/null 2>&1 && return 0 + ;; + esac + + log_warn "Package '${pkg}' not available for ${arch} on ${PKG_MANAGER}" + return 1 +} + +install_packages() { + local -n pkg_list="${1:?install_packages: pkg_list required}" + local failed=() + local unavailable=() + + for pkg in "${pkg_list[@]}"; do + if ! test_package_available "${pkg}"; then + unavailable+=( "${pkg}" ) + continue + fi + if ! doas ${PKG_INSTALL} "${pkg}"; then + log_error "Failed to install: ${pkg}" + failed+=( "${pkg}" ) + else + log_info "Installed: ${pkg}" + fi + done + + if [[ ${#unavailable[@]} -gt 0 ]]; then + log_warn "Unavailable for ${ARCH}: ${unavailable[*]}" + log_warn "These packages were skipped. Review CE repo for alternatives." + fi + + if [[ ${#failed[@]} -gt 0 ]]; then + log_error "Installation failures: ${failed[*]}" + return 1 + fi + + return 0 +} +``` + +### 2.8 Logging + +Source `ce-common.sh` for the standard logging functions. If writing a +standalone script that cannot source `ce-common.sh`, implement these +four levels minimally: + +```bash +# Minimal logging — use ce-common.sh log functions when available +_log() { + local level="${1}" + local msg="${2}" + local ts + ts="$(date '+%Y-%m-%d %H:%M:%S')" + printf '[%s] [%s] %s\n' "${ts}" "${level}" "${msg}" | tee -a "${LOG_FILE}" +} +log_info() { _log "INFO " "${1}"; } +log_warn() { _log "WARN " "${1}" >&2; } +log_error() { _log "ERROR" "${1}" >&2; } +log_debug() { [[ "${CE_DEBUG:-0}" == "1" ]] && _log "DEBUG" "${1}"; } +``` + +Log file location: `~/.local/logs/ceos_installer/` (XDG compliant). +Log file name: `-.log`. + +### 2.9 Cleanup trap + +Register the cleanup trap immediately after defining the cleanup function, +before any work begins: + +```bash +# ── Cleanup ──────────────────────────────────────────────────────────────── + +TEMP_DIR="" + +cleanup() { + local exit_code="${?}" + + log_info "Running cleanup..." + + # Remove temp files + if [[ -n "${TEMP_DIR}" && -d "${TEMP_DIR}" ]]; then + rm -rf "${TEMP_DIR}" + log_info "Removed temp dir: ${TEMP_DIR}" + fi + + # Clear package manager cache + case "${PKG_MANAGER:-}" in + apk) doas rm -rf /var/cache/apk/* 2>/dev/null || true ;; + apt) doas apt-get clean 2>/dev/null || true + doas rm -rf /var/lib/apt/lists/* 2>/dev/null || true ;; + nix) : ;; # nix cache managed separately + esac + + log_info "Cleanup complete (exit code: ${exit_code})" +} + +trap cleanup EXIT INT TERM HUP + +# Create temp dir only after trap is registered +TEMP_DIR="$(mktemp -d)" +``` + +### 2.10 Test gates — structure + +```bash +# ── Pre-flight ───────────────────────────────────────────────────────────── + +phase_preflight() { + log_info "=== PRE-FLIGHT ===" + local pass=0 + + # Each check: run_test [warn|fail] + run_test "bash version >= 4" '[[ "${BASH_VERSINFO[0]}" -ge 4 ]]' fail + run_test "running as non-root" '[[ "${EUID}" -ne 0 ]]' fail + run_test "internet reachable" \ + 'curl --silent --max-time 5 --output /dev/null https://cervelloelettrico.it' \ + warn + run_test "architecture detected" '[[ -n "${ARCH}" ]]' fail + run_test "disk space >= 500MB" \ + '[[ $(df / --output=avail | tail -1) -ge 512000 ]]' \ + warn + + log_info "Pre-flight complete" +} + +# ── Inline test (example) ────────────────────────────────────────────────── + +phase_install_vim() { + log_info "=== INSTALLING VIM ===" + + doas ${PKG_INSTALL} vim || { log_error "vim install failed"; return 1; } + + # Inline test — verify immediately after install + run_test "vim installed" 'command -v vim >/dev/null 2>&1' fail + + log_info "vim install verified" +} + +# ── Post-install ─────────────────────────────────────────────────────────── + +phase_verify() { + log_info "=== POST-INSTALL VERIFICATION ===" + + local required_tools=( vim tmux starship bash ) + for tool in "${required_tools[@]}"; do + run_test "${tool} available" "command -v ${tool} >/dev/null 2>&1" fail + done + + log_info "All required tools verified" +} +``` + +The `run_test` function lives in `ce-test-lib.sh`. When writing a +standalone script, implement a minimal version: + +```bash +run_test() { + local desc="${1}" + local cmd="${2}" + local mode="${3:-fail}" # fail | warn + + if eval "${cmd}" >/dev/null 2>&1; then + log_info " PASS: ${desc}" + return 0 + else + if [[ "${mode}" == "fail" ]]; then + log_error " FAIL: ${desc}" + exit 1 + else + log_warn " WARN: ${desc}" + return 1 + fi + fi +} +``` + +### 2.11 User confirmation + +One confirmation prompt before system modifications begin. No more: + +```bash +confirm_proceed() { + local prompt="${1:-Proceed with installation?}" + printf '\n%s [y/N] ' "${prompt}" + read -r response + case "${response}" in + [yY]|[yY][eE][sS]) return 0 ;; + *) log_info "Aborted by Organic Humanoid I/O Device unit ID ${USER}"; exit 0 ;; + esac +} +``` + +### 2.12 POSIX / BusyBox compatibility + +For Class B scripts running on Alpine, prefer BusyBox-safe patterns: + +| Avoid (GNU-only) | Use instead (BusyBox-safe) | +|------------------------|----------------------------------------------------| +| `grep -P` | `grep -E` | +| `sed 's/\w//'` | `sed 's/[[:alnum:]_]//'` | +| `find -printf` | `find ... -exec basename {} \;` or pipe to `awk` | +| `stat -c "%Y" f` | `stat -t f \| awk '{print $12}'` | +| `dd status=progress` | plain `dd`, or `pv` if progress needed | +| `ping` in scripts | `curl --silent --max-time 5 --output /dev/null URL`| + +### 2.13 Script architecture — orchestrators, subscripts, and libraries + +CE OS Bash scripts follow a three-layer architecture. Understanding which +layer a script belongs to determines its structure, its line limits, and +what it is permitted to contain. + +#### The three layers + +**Libraries (`ce-*-lib.sh`)** + +A library is a collection of related functions and constants that other +scripts source. Libraries contain no executable code at the top level — +they are sourced, not run. Running a library directly should produce no +output and no side effects. + +Extract a function to a library when: +- More than one script needs it (reuse) +- The function is complex enough that embedding it disrupts the flow of + the script it lives in (clarity) +- The function requires a comment block longer than itself to be understood + (the comment is telling you it belongs in isolation) + +Naming: `ce--lib.sh` — e.g. `ce-pkg-lib.sh`, `ce-net-lib.sh`, +`ce-test-lib.sh`. + +```bash +#!/usr/bin/env bash +# Created by John A. Hoeven with the ethical assistance of Claude AI +# --------------------------------------------------------------------------- +# ce-pkg-lib.sh +# /opt/ceos/lib/ce-pkg-lib.sh +# Version: v0.0.1 | Status: DEVELOPMENT +# --------------------------------------------------------------------------- +# Purpose: Package manager detection, update, and availability testing. +# Used by: ce-install.sh, ce-base.sh, ce-minimal.sh +# --------------------------------------------------------------------------- +# This file is a library. Source it — do not run it directly. +# --------------------------------------------------------------------------- + +# Guard against double-sourcing +[[ -n "${CE_PKG_LIB_LOADED:-}" ]] && return 0 +readonly CE_PKG_LIB_LOADED=1 + +detect_package_manager() { ... } +test_package_available() { ... } +run_package_update() { ... } +``` + +**Subscripts (`ce-*.sh`)** + +A subscript owns a single phase or a coherent group of related operations. +It accepts arguments, performs its work, and returns a meaningful exit code. +Subscripts may be run directly for testing and debugging — this is a feature, +not a side effect. + +Naming: `ce-.sh` or `ce-.sh` — e.g. `ce-base.sh`, +`ce-minimal.sh`, `ce-audio.sh`. + +Subject to the 150-line soft / 200-line hard limit. If a subscript is +growing beyond this, extract complex functions to a library first, then +consider whether the phase itself should be split. + +**Orchestrators (`ce-install.sh` or similar)** + +An orchestrator coordinates subscripts and libraries. It contains: + +- Sourcing of required libraries +- Calling subscripts in sequence +- Inter-phase safety gates (explicit `[y/N]` confirmation before each tier) +- The master cleanup trap +- Flow control based on subscript exit codes + +An orchestrator contains **no complex logic of its own.** If something +complex needs to happen, it lives in a subscript or library. The orchestrator +calls it. A reader following the orchestrator should understand the complete +flow without opening any sourced file. + +Orchestrators have no line limit. A 300-line orchestrator that sources six +libraries and calls ten subscripts in sequence is correct and expected. + +#### Sourcing libraries + +```bash +# Source libraries — paths relative to script location +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly LIB_DIR="${SCRIPT_DIR}/../lib" + +# shellcheck source=../lib/ce-common-lib.sh +source "${LIB_DIR}/ce-common-lib.sh" || { + echo "ERROR: ce-common-lib.sh not found at ${LIB_DIR}" >&2 + exit 1 +} + +# shellcheck source=../lib/ce-pkg-lib.sh +source "${LIB_DIR}/ce-pkg-lib.sh" || { + echo "ERROR: ce-pkg-lib.sh not found at ${LIB_DIR}" >&2 + exit 1 +} +``` + +Always validate that sourced files exist and loaded correctly. A missing +library is a hard failure — never silently proceed without it. + +#### Directory layout + +``` +/opt/ceos/ +├── lib/ +│ ├── ce-common-lib.sh # Logging, run_test, confirm_proceed +│ ├── ce-pkg-lib.sh # Package manager detection and operations +│ ├── ce-test-lib.sh # Test gate framework +│ └── ce-net-lib.sh # Network checks +├── scripts/ +│ ├── ce-install.sh # Orchestrator +│ ├── ce-base.sh # Tier 0 subscript +│ ├── ce-minimal.sh # Tier 1 subscript +│ └── ce-basic.sh # Tier 2 subscript +``` + +#### Summary table + +| Layer | Naming | Contains | Line limit | +|--------------|--------------------|-----------------------------------|---------------------| +| Library | `ce-*-lib.sh` | Functions and constants only | 150 soft / 200 hard | +| Subscript | `ce-*.sh` | Single phase or domain logic | 150 soft / 200 hard | +| Orchestrator | `ce-install.sh` | Flow, gates, sourcing, trap only | None | + +--- + +## Part 3 — Python Style Guide + +*The Python style guide will be produced as a separate document once +sufficient CE OS Python use cases exist to ground it in real experience. +Do not apply Bash conventions to Python scripts — wait for the dedicated +guide.* + +--- +## Part 4 — Quick Reference + +### 4.1 Mandatory checklist — every Bash script + +Before any script is considered ready for review, verify all of the following: + +``` +[ ] Attribution header present and exact +[ ] Version and Status in header +[ ] Script role declared (orchestrator / subscript / library) +[ ] Line limit override comment present if >200 lines (subscripts and libraries) +[ ] Package manager detected, not assumed +[ ] doas ${PKG_UPDATE} && doas ${PKG_UPGRADE} runs first (if installing packages) +[ ] Architecture detected (uname -m) before any package operations +[ ] Each package tested: Alpine stable → community → edge → CE repo +[ ] CE_APK_REPO constant present if CE repo check is used +[ ] Pre-flight test gate implemented (warn-only, no changes) +[ ] Inline tests after each significant operation +[ ] Post-install verification gate implemented +[ ] Cleanup trap registered before any work begins +[ ] Cleanup removes: temp files, /var/cache/apk/*, lock files +[ ] Cleanup runs on EXIT INT TERM HUP +[ ] No silent failures — every error logged and surfaced +[ ] No set -e — errors handled explicitly +[ ] No credentials in code — [PLACEHOLDER] only +[ ] No direct root execution — doas for privileged ops +[ ] Single confirmation prompt before system changes begin +[ ] Log file written to ~/.local/logs/ceos_installer/ +[ ] Comments explain intent, not mechanics +[ ] Complex functions extracted to library if they disrupt script flow +``` + +### 4.2 Header templates — copy/paste + +**Orchestrator:** +```bash +#!/usr/bin/env bash +# Created by John A. Hoeven with the ethical assistance of Claude AI +# --------------------------------------------------------------------------- +# ce-install.sh +# /opt/ceos/scripts/ce-install.sh +# Version: v0.0.1 | Status: DEVELOPMENT +# Role: Orchestrator +# --------------------------------------------------------------------------- +# Purpose: +# Target: +# Entry: +# Sources: ce-common-lib.sh, ce-pkg-lib.sh, ce-test-lib.sh +# Calls: ce-base.sh, ce-minimal.sh, ce-basic.sh +# --------------------------------------------------------------------------- +# Phases: +# 0 — Pre-flight (environment checks, no system changes) +# 1 — Base (Tier 0) +# 2 — Minimal (Tier 1) +# 3 — Basic (Tier 2) +# N — Cleanup (always runs via trap) +# --------------------------------------------------------------------------- +``` + +**Subscript:** +```bash +#!/usr/bin/env bash +# Created by John A. Hoeven with the ethical assistance of Claude AI +# --------------------------------------------------------------------------- +# ce-base.sh +# /opt/ceos/scripts/ce-base.sh +# Version: v0.0.1 | Status: DEVELOPMENT +# Role: Subscript — Tier 0 Base installation +# --------------------------------------------------------------------------- +# Purpose: +# Target: +# Entry: +# Depends: ce-common-lib.sh, ce-pkg-lib.sh +# --------------------------------------------------------------------------- +``` + +**Library:** +```bash +#!/usr/bin/env bash +# Created by John A. Hoeven with the ethical assistance of Claude AI +# --------------------------------------------------------------------------- +# ce-pkg-lib.sh +# /opt/ceos/lib/ce-pkg-lib.sh +# Version: v0.0.1 | Status: DEVELOPMENT +# Role: Library — Package manager detection and operations +# --------------------------------------------------------------------------- +# Purpose: +# Used by: +# --------------------------------------------------------------------------- +# This file is a library. Source it — do not run it directly. +# --------------------------------------------------------------------------- +``` + +**Line limit override (add to header when applicable):** +```bash +# Line limit override: +``` + +--- + +*Created by John A. Hoeven with the ethical assistance of Claude AI* \ No newline at end of file