#!/usr/bin/env bash # tools/today/accept.sh # # Validates the chosen candidate, then atomically swaps it in as # ~/.fai/today/active.yaml. Studio reads that file at startup. set -euo pipefail if [ "${1:-}" = "" ]; then printf 'Usage: %s \n' "$0" >&2 exit 1 fi src="$1" [ -f "$src" ] || { printf 'No such file: %s\n' "$src" >&2; exit 1; } # Schema gate. Block accept if the candidate is missing required # top-level keys or carries the wrong schema version. Studio # would silently fall back, but we want a loud failure here so # the operator knows the proposal was malformed. require_key() { local key="$1" if ! grep -qE "^$key:" "$src"; then printf 'Reject: candidate is missing key `%s`.\n' "$key" >&2 exit 2 fi } if ! grep -qE '^schema:[[:space:]]*today/v1[[:space:]]*$' "$src"; then printf 'Reject: candidate must declare `schema: today/v1`.\n' >&2 exit 2 fi for k in badge_en badge_de title_en title_de body_en body_de icon cta; do require_key "$k" done # Banned-words check (matches both DE and EN bodies/titles). banned='viral|killer|powerful|just[[:space:]]+works|revolutionary|game-changing|seamless|next-generation|cutting-edge|world-class|unprecedented' if grep -qiE "$banned" "$src"; then printf 'Reject: candidate contains banned marketing language.\n' >&2 printf 'Hits:\n' >&2 grep -niE "$banned" "$src" >&2 || true exit 3 fi # Atomic move via copy-then-rename so Studio never reads a torn # half-written file even if it polls during the swap. dst="$HOME/.fai/today/active.yaml" mkdir -p "$(dirname "$dst")" tmp="$(mktemp "${dst}.XXXXXX")" cp "$src" "$tmp" mv "$tmp" "$dst" printf 'Accepted: %s\n → %s\n' "$src" "$dst" printf 'Restart Studio (or wait for the next launch) to see it.\n'