Cron postflight
The Bubble Shop — free, open-source Claude Code skills & plugins by Bubble Invest. One-command install. We're not developers — we built these to run our own family office. Help us improve them.
npx -y skills add vdk888/bubble-skills --skill cron-postflightAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Cron-fire post-flight scaffolding. Audit-log INSERT, git commit + push, notification send. Replaces inline wrap-up boilerplate across scheduled tasks.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
5.5 KB, as published. Nobody here has run it
Cron post-flight — shared scaffolding
Every scheduled task tends to end with the same 3 chores:
- Write an
audit_logrow recording what happened (actor, action, target, outcome) - Commit + push any repo state mutated this run
- Send a notification with the run summary
This skill provides parametric helpers for all three.
Notification routing comes from env vars so this skill ships clean. Set
OPERATOR_CHAT_IDand a token file path before using Block 3.
When to invoke
In any cron's wrap-up STEP. Each helper is a separate Block — invoke whichever you need; you don't have to use all 3.
Block 1 — Audit log row
# === cron-postflight: audit_log helper ===
# Required env:
# POSTFLIGHT_DB — path to your sqlite DB
# POSTFLIGHT_ACTOR — e.g. 'scout' | 'risk-manager'
# POSTFLIGHT_ACTION — e.g. 'cluster_refresh' | 'heartbeat_complete'
# POSTFLIGHT_TARGET — entity touched, e.g. 'vault/clusters/'
# POSTFLIGHT_DETAILS — pipe-separated key=value summary
# POSTFLIGHT_OUTCOME — 'success' | 'partial' | 'failed'
sqlite3 "${POSTFLIGHT_DB:?must set POSTFLIGHT_DB}" \
"INSERT INTO audit_log (logged_at, actor, action, target, details, outcome)
VALUES (
strftime('%Y-%m-%dT%H:%M:%fZ','now'),
'${POSTFLIGHT_ACTOR:?must set POSTFLIGHT_ACTOR}',
'${POSTFLIGHT_ACTION:?must set POSTFLIGHT_ACTION}',
'${POSTFLIGHT_TARGET:?must set POSTFLIGHT_TARGET}',
'${POSTFLIGHT_DETAILS:-}',
'${POSTFLIGHT_OUTCOME:-success}'
);"
Block 2 — Git commit + push
# === cron-postflight: git commit + push helper ===
# Required env:
# POSTFLIGHT_GIT_WORKDIR — absolute path to repo root
# POSTFLIGHT_GIT_MESSAGE — commit message body (multi-line OK; use $'\n' for literal newlines)
# Optional env:
# POSTFLIGHT_GIT_PATHSPEC — files to add (default: -A); e.g. "vault/clusters/" to scope
# POSTFLIGHT_GIT_PUSH — 'yes' (default) | 'no' (skip remote push)
# POSTFLIGHT_GIT_LANE — if set, refuses to commit changes outside this path
cd "${POSTFLIGHT_GIT_WORKDIR:?must set POSTFLIGHT_GIT_WORKDIR}"
# Pre-check — show what would be committed
echo "[postflight] git status (before commit):"
git status --short
# Lane discipline — if pathspec is restricted, abort if changes leak outside
if [ -n "${POSTFLIGHT_GIT_LANE:-}" ]; then
OFFENDING=$(git status --short | awk '{print $2}' | grep -v "^${POSTFLIGHT_GIT_LANE}" | head -3)
if [ -n "$OFFENDING" ]; then
echo "[postflight] git LANE breach — changes outside ${POSTFLIGHT_GIT_LANE}: $OFFENDING"
echo "[postflight] aborting commit; investigate the lane crossing manually"
exit 1
fi
fi
# Add + commit
git add ${POSTFLIGHT_GIT_PATHSPEC:--A}
if git diff --cached --quiet; then
echo "[postflight] no staged changes — skipping commit (no-op)"
COMMIT_OUTCOME="noop"
else
git commit -m "${POSTFLIGHT_GIT_MESSAGE:?must set POSTFLIGHT_GIT_MESSAGE}"
COMMIT_OUTCOME="committed"
fi
# Push (non-fatal — local commit stands either way)
if [ "${POSTFLIGHT_GIT_PUSH:-yes}" = "yes" ] && [ "$COMMIT_OUTCOME" = "committed" ]; then
git push origin main 2>&1 || echo "[postflight] push failed — local commit intact"
fi
Block 3 — Notification
# === cron-postflight: notification helper ===
# Required env:
# POSTFLIGHT_TG_BODY — message body (string; most chat APIs cap at ~4096 chars per message)
# Optional env:
# OPERATOR_CHAT_ID — target chat id
# TELEGRAM_BOT_TOKEN_FILE — path to a file holding the bot token
BOT_TOKEN=$(cat "${TELEGRAM_BOT_TOKEN_FILE:?set TELEGRAM_BOT_TOKEN_FILE}")
CHAT_ID="${OPERATOR_CHAT_ID:?set OPERATOR_CHAT_ID}"
BODY="${POSTFLIGHT_TG_BODY:?must set POSTFLIGHT_TG_BODY}"
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
--data-urlencode "chat_id=${CHAT_ID}" \
--data-urlencode "text=${BODY}"
Note on Markdown rendering: Block 3 sends as plain text — no parse_mode. Asterisks, underscores, backticks render literally. If you need Markdown rendering, use a JSON-POST pattern with parse_mode: Markdown instead — but then every caller must escape special chars, which is usually more friction than it's worth.
For attachments (chart PNGs etc.), use sendPhoto instead:
BOT_TOKEN=$(cat "$TELEGRAM_BOT_TOKEN_FILE")
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendPhoto" \
-F chat_id="$OPERATOR_CHAT_ID" \
-F photo=@/tmp/brief_chart.png \
-F caption="<one-line caption>"
What the calling cron sees
Each block is independent — they don't communicate via env vars. After each successful block, the calling cron simply continues. After a failed block (e.g. git lane breach), exit code is non-zero — the cron should not assume Block N+1 ran.
Hard invariants
- DB writes only via Block 1: keep a single audit-trail discipline — never INSERT to your DB from elsewhere in the post-flight phase.
- Notification routing: all chat IDs and tokens come from env vars.
- Git push is non-fatal: a failed
git push origin maindoes NOT fail the cron; local commit stands and the next run retries the push. - Lane discipline (Block 2): when
POSTFLIGHT_GIT_LANEis set, refuses to commit anything outside that path — prevents a cron crossing perimeter.