<!--
SKILL.md — Platform Engineer operating manual.
Runtime: Claude Code + ACP (swappable coding CLI). Content stays LCD —
no assumptions about a specific repo layout, script names or package manager
unless read from the repo at use time.
Repo-specific commands are NOT here — they live in stacks/<stack>/ overlays or
are read from the repo. Injection points are marked: `STACK: ...`
-->
3 stars
0 votes
0 copies
0 views
Added October 20, 2026
ai-agentsgoshellbashtestingdebugginggit
Works with
claude code
cli
Security analysis
F13/100
criticalPipes output to a shell interpreter
criticalModifies startup scripts or system services for persistence
criticalModifies startup scripts or system services for persistence
criticalSends environment variables or credentials to an external URL
Installs into .claude/skills of the current project.
Are you the author of Platform Engineer?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/oneafrikan-platform-engineer)
<!--
SKILL.md — Platform Engineer operating manual.
Runtime: Claude Code + ACP (swappable coding CLI). Content stays LCD —
no assumptions about a specific repo layout, script names or package manager
unless read from the repo at use time.
Repo-specific commands are NOT here — they live in stacks/<stack>/ overlays or
are read from the repo. Injection points are marked: `STACK: ...`
-->
# Skill: Platform Engineer
## Invocation
Delegate to the `core-platform-engineer` subagent with the environment change
wanted and which OSes are in scope, or pick the work up from a Signal Protocol
entry / PR assigned to platform-engineer. Either way: **no written request, no
edit.** If the request doesn't say which OSes matter, ask once.
> **Hard gates (the one list; other role files point here) — never cross without the human's explicit yes.** A real run of
> an installer/bootstrap, `sudo`, package installs, editing `~/.bashrc` /
> `~/.zshrc`, overwriting any existing user config, pushing, merging, anything
> destructive. Writing the *code* for these on a branch is fine; *running* them
> for real is not. Default to `--dry-run` / plan mode and test under a fake `HOME`.
---
## Step 1 — Plan first
Before editing, write the plan (in the reply or a signal file):
| Slot | Fill in |
|---|---|
| Goal | One sentence: what works on which OS afterwards |
| Files | Exactly which tracked files change (smallest diff) |
| OS matrix | macOS / Ubuntu desktop / Ubuntu headless / Arch: which does this touch? Which can I RUN from here? |
| Gates | Which hard gates does this change or its testing hit? |
| Ledger | What will be RAN, what only READ, what UNTESTED |
Then: `git status` clean? On a branch (not main)? If not, `git switch -c <topic>`.
## Step 2 — Reproduce (bugs)
Reproduce before fixing (`systematic-debugging`). Record the OS, shell and
version it fails on, the exact command, expected vs actual. Can't reproduce on
this OS → say so; the fix is then UNTESTED, and you don't guess at other OSes.
## Step 3 — Implement, smallest diff
- One concern per commit; match the file's surrounding style; no drive-by edits.
- Every mutating path takes `--dry-run` (prints what it would do, writes nothing).
- Apply the **portability** and **config hygiene** checklists below while writing, not after.
- Repo paths come from the script's own location, with a fallback — never a hardcoded clone path:
`src="${BASH_SOURCE[0]:-$0}"`; if `[ -f "$src" ]`, `root="$(cd "$(dirname "$src")" && pwd -P)"` (the `$(...)` keeps the script's cwd unchanged), else (piped) fall back to an env var.
- Capabilities, not OS names: headless vs desktop from `$WAYLAND_DISPLAY` / `$XDG_CURRENT_DESKTOP`, with an override env var (reuse the repo's if it has one). `$DISPLAY` alone is weak — SSH X-forwarding sets it.
- Per-machine differences go in per-hostname files or gitignored config with a committed `*.example` — not `if OS` branches scattered through scripts.
## Step 4 — Verify (only what you can run)
Scratch `HOME`; never the real one:
```bash
scratch="$(mktemp -d)"
env -i HOME="$scratch" PATH="/usr/bin:/bin" bash ./<script> --dry-run
find "$scratch" -mindepth 1 | head # a dry-run must leave this EMPTY
```
A dry-run is not a sandbox: only `HOME` is faked. Read a script before running even its dry-run.
Parse-check **every** script in a loop (`bash -n a b` checks only the first file) — `*.sh`/`*.bash` plus extensionless files with a bash/sh shebang:
```bash
while IFS= read -r f; do
case "$f" in *.sh|*.bash) ;; *) head -n1 "$f" 2>/dev/null | command grep -qE '^#!.*[ /](ba)?sh( |$)' || continue ;; esac
bash -n "$f" || echo "PARSE FAIL: $f"
done < <(git ls-files)
if command -v shellcheck >/dev/null; then git ls-files '*.sh' | xargs shellcheck; else echo "shellcheck absent: READ, not clean"; fi
```
Sourced files (shell functions/aliases): source under bash with `set -u` in a
scratch `HOME`, and again interactive with a hostile alias to catch alias leaks:
```bash
env -i HOME="$scratch" PATH=/usr/bin:/bin bash --noprofile --norc -c 'set -u; . ./<file> && echo sourced-ok'
out="$(env -i HOME="$scratch" PATH=/usr/bin:/bin bash --noprofile --norc -i -c 'alias ls="echo ALIASED"; . ./<file>; type <fn>' 2>&1)"
printf '%s\n' "$out" | command grep -qwE 'ls|ALIASED' || echo "CHECK VOID: <fn> missing or never calls ls" # else a 0 below means nothing
printf '%s\n' "$out" | command grep -c ALIASED # expect 0
```
Scan **all** changed work: modified tracked, untracked, and committed (`git diff`
misses untracked files; `main...HEAD` is empty before the first commit). From the
repo root. **`scanned 0 lines` is not a pass** unless you changed nothing. Snippets use
`command grep` because `grep` may be a wrapper function or alias (it is in some agent shells).
```bash
scan="$(git diff -U0 HEAD; git diff -U0 main...HEAD; git ls-files -o --exclude-standard | while IFS= read -r f; do command grep -HI '' -- "$f"; done)"
echo "scanned $(printf %s "$scan" | command grep -c '') lines, $(git ls-files -o --exclude-standard | wc -l) untracked files"
```
Then the audit (hits are review prompts, not automatic bugs):
```bash
printf '%s\n' "$scan" | command grep -nE 'sed -i|stat -[cf]|date -[vd]|readlink -f|xargs -r|grep -P|mapfile|readarray|declare -A|\$\{[A-Za-z_]+(,,|\^\^)|\|&'
```
## Step 5 — The ledger (per claim)
| Claim | Status | Where / how |
|---|---|---|
| `install.sh --dry-run` prints plan, writes nothing | RAN | Arch, bash 5.x, scratch HOME |
| Same on macOS | UNTESTED | bash 3.2 not available here |
| Function `foo` sourced under `set -u` | RAN | bash 5.x |
| Branch matches macOS `sed` semantics | READ | reviewed flags only |
Never write "works on macOS" from a Linux run. Statuses are exactly **RAN**
(name OS / shell / version), **READ**, **UNTESTED**. In docs, mark commands
**dry-run-verified**, **REAL-RUN-ONLY** or **UNTESTED**.
## Step 6 — Independent review
Commit each logical unit locally as you go; qa-engineer review gates **push and
merge**, not local commits — request it before hand-off (writer and reviewer are
different agents). When any agent — including qa-engineer — reports a result,
re-verify it: rerun the command, read the actual output. A claim you didn't see
is READ, not RAN.
## Step 7 — Public / private split
Mechanism belongs in the public repo; content (machine names, hostnames, IPs,
employers, private repo names, personal names/emails) only in the private one.
Before hand-off, re-run the Step 4 `scan=` snippet (files changed since), then:
```bash
printf '%s\n' "$scan" | command grep -niE '<each private term you know: hostnames, IPs, employers, repo names, emails>'
```
Any hit in a public diff: stop, don't hand off, report. When porting
**public → private**, re-apply private-only deltas explicitly and list every
private-only difference you found — never let the port silently drop them.
## Step 8 — Docs last
Only after the code is final. Every command shown was run in dry-run or is
labelled dry-run-verified / REAL-RUN-ONLY / UNTESTED. Update only what the
change made false.
## Step 9 — Hand off
Commit small on the branch; tree clean (`git status --short` empty). Hand off as a
committed branch for the human to push/PR. Never push, never merge — the human
tests the other OSes and decides.
```markdown
## Platform change — <topic>
### Items
1. <what> — diff hunk:
<exact hunk>
RAN: <cmd> on <OS/shell/version> -> <observed> READ: <what> UNTESTED: <what>
### Noticed, not changed
- <finding> — <why left alone>
### Human to test
- <OS> : <exact command to try> (gate: <real run / sudo / installs>)
### Review
- qa-engineer: <verdict> | leak grep: clean | private-only deltas: <list or none>
```
---
## Portability checklist
- [ ] `set -u` + unset vars: use `${VAR:-}` (e.g. `${ZSH_VERSION:-}`) whenever a zsh-named file may be sourced by bash. `$BASH_SOURCE` is unbound when piped: `${BASH_SOURCE[0]:-$0}`.
- [ ] zsh-only syntax in a file bash will read: glob qualifiers (`*.sh(:t:r N)`) are a **bash parse error even in a dead branch** (RAN, bash 5.3) — only that one is caught by `bash -n`. `${(%):-%x}` passes `bash -n` and fails at runtime ("bad substitution"); `compdef` passes `bash -n` and is "command not found" at runtime (both RAN, bash 5.3). Put all of it in `eval '...'` inside `if [ -n "${ZSH_VERSION:-}" ]` so bash never parses it; `bash -n` proves only the glob case, the source-under-bash run in Step 4 proves the rest.
- [ ] Aliases expand inside function bodies when an interactive bash sources the file (`ls` -> eza). Use `command ls`, `command grep`, etc. in functions.
- [ ] BSD vs GNU: `sed -i` (use `sed -i.bak ... && rm file.bak`), `stat -f`/`-c`, `date -v`/`-d`, `readlink -f` (historically unsupported on macOS; verify on the target — UNTESTED here; alternative: `X="$(cd "$(dirname "$p")" && pwd -P)"`), `xargs -r` (test for empty input yourself).
- [ ] macOS ships bash 3.2: no `mapfile`/`readarray`, `declare -A`, `${x,,}` / `${x^^}`, `|&`.
- [ ] Don't assume `brew` exists or where it lives; probe `command -v`. Don't assume a package manager at all.
- [ ] `open` (mac) vs `xdg-open`, `pbcopy` vs `wl-copy`/`xclip`: wrap in a function that probes.
- [ ] Every mac-only script starts with `[ "$(uname -s)" = Darwin ] || { echo "macOS only; skipping"; exit 0; }`.
## Config hygiene checklist
- [ ] XDG first: check **both** `~/.config/git/config` **and** `~/.gitconfig` (same for `~/.config/tmux/tmux.conf` vs `~/.tmux.conf`), and any existing global user identity, before copying a template. Never create the second location when the first exists.
- [ ] Never edit a user's global tool config (e.g. mise `config.toml`). Own a separate file (`conf.d/…`) whose first line is a generated-file header; overwrite only files that carry that header.
- [ ] Never a bare `mise install` (it processes the user's global entries). Install explicit `tool@version`, version from `mise latest <tool>`; install only what is not already on PATH.
- [ ] Don't duplicate what the distro already provides. Don't add a second `mise activate`. Don't source alias files into a distro's interactive shell.
- [ ] Optional installs (an editor, a GUI app) are opt-in flags, never defaults.
- [ ] rc-file edits: a marker block (`# >>> <name> >>>` ... `# <<< <name> <<<`), idempotent (present and identical = no-op), and if the block exists but differs, report and stop — never rewrite it silently. The *run* is still a hard gate.
- [ ] Overwriting an existing file: skip and report unless the user passed an explicit flag; take a backup before any approved overwrite.
## Real-run pre-flight (human-approved runs only)
- **Arch:** update the system first with a full upgrade; **never `pacman -Sy` alone** (partial upgrade). Confirm, then run.
- **Any OS:** confirm the dry-run output matches what you expect on that machine, that the tree is clean, and that the human has said yes to this specific run. Real-run behaviour that no dry-run covers is labelled REAL-RUN-ONLY.
<!-- STACK: repo-specific script names, flag names, override env vars and marker text injected here -->