CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/coding-policy

General-purpose coding policy for Baruch's AI agents

74

Quality

93%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Medium

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

verify-moderation-cleared.shskills/release/

#!/usr/bin/env bash
# Wait for a published plugin version's moderation to clear, polling the
# registry with exponential backoff. Moderation is a post-publish install
# gate: a freshly published version can be blocked from `tessl install`
# until its moderation state reaches "pass". This is the release
# contract's third conjunct (skills/release/references/release-contract.md) — a green publish run
# plus a registry advance does NOT confirm a release on its own.
#
# Signal (machine-readable JSON, not the `tessl plugin info` human text):
#   tessl api v1/tiles/<workspace>/<tile>/versions/<version>
#     .data.attributes.moderationStatus   ("pass" => cleared)
#     .data.attributes.moderationPassed    (bool; true => cleared)
#     .data.attributes.moderationError     (non-null => blocked)
#
# Backoff: the per-attempt delay starts at BASE_DELAY_SEC and doubles each
# attempt, capped at MAX_DELAY_SEC; the loop stops once cumulative sleep
# would exceed BUDGET_SEC. Constants below; override via the matching
# env vars (used by the test harness to keep runs fast).
#
# Transient fetch retry: a non-zero `tessl api` exit is retried inside the
# backoff loop up to FETCH_RETRY_MAX consecutive times before exiting 2, so
# a momentary CLI/network blip does not abort a wait for a release that
# published fine (issue #304). A successful fetch resets the counter.
#
# Fail-loud-at-budget: a still-pending OR blocked state when the budget is
# exhausted exits 1 — an unconfirmed release is never reported as success.
#
# Usage: verify-moderation-cleared.sh <workspace> <plugin> <version>
# Out:   JSON contract differs by exit code (per rules/script-delegation.md
#        "JSON-producing"):
#          - rc 0/1 (moderation finding): one JSON object on stdout
#              {"ok": bool, "reason": "<human text>",
#               "moderation_status": "<status-or-empty>",
#               "version": "<version>", "attempts": N, "elapsed_seconds": N}
#          - rc 2 (tool-state error): stderr-only diagnostic, stdout empty
#            (or, for the missing-jq guard, a minimal JSON envelope with the
#            same fields and "ok": false). Wrappers MUST parse stdout only
#            when exit code is 0 or 1.
# Exit:  0 moderation cleared; 1 blocked or still-pending at budget
#        exhaustion (fail loud); 2 argument-validation, jq-missing, or a
#        persistent `tessl api` failure (retried FETCH_RETRY_MAX times first).

set -euo pipefail

# --- Tunable constants (script is the source of truth; rules/ci-safety.md
# names the contract and points here rather than restating these). ---
BASE_DELAY_SEC="${VERIFY_MODERATION_BASE_DELAY_SEC:-5}"
MAX_DELAY_SEC="${VERIFY_MODERATION_MAX_DELAY_SEC:-120}"
BUDGET_SEC="${VERIFY_MODERATION_BUDGET_SEC:-600}"
# Consecutive `tessl api` fetch failures tolerated before declaring a
# persistent tool failure (issue #304). A single transient non-zero exit —
# an outdated CLI's version-check blip, a momentary network drop — is retried
# inside the backoff loop rather than aborting the whole moderation wait; a
# genuinely persistent failure still exits 2 fast.
FETCH_RETRY_MAX="${VERIFY_MODERATION_FETCH_RETRY_MAX:-3}"

# Terminal "blocked" moderation states — fast-fail without waiting out the
# budget. Anything not in here and not "pass" is treated as still-pending
# and polled until the budget is exhausted.
BLOCK_STATES="fail failed flagged blocked rejected reject denied"

if ! command -v jq >/dev/null 2>&1; then
  printf '{"ok":false,"reason":"jq is not installed; install with '"'"'brew install jq'"'"' (macOS) or '"'"'apt install jq'"'"' (Debian/Ubuntu) and re-run","moderation_status":"","version":"","attempts":0,"elapsed_seconds":0}\n'
  echo "error: jq is not installed; install with 'brew install jq' (macOS) or 'apt install jq' (Debian/Ubuntu) and re-run" >&2
  exit 2
fi

# `err_file` is script-global, never `local` in main: the EXIT trap fires
# after main returns, when a main-local would be out of scope and this
# handler would silently skip cleanup (verified: the normal-return path
# leaks the tempfile every run; only the exit-from-inside-main paths clean up).
err_file=""

# EXIT-trap cleanup. `return 0` is load-bearing: the trap's final command
# status becomes the script's exit status, so a failing `rm` would rewrite
# this script's verdict (rules/error-handling.md Shell Error Handling).
#
# `if ! rm` rather than a bare `rm`: under `set -e` a failing rm aborts the
# handler before `return 0` runs — reintroducing the exact rewrite the
# handler exists to prevent. An `if` condition suspends `set -e`, so the
# failure is reported instead of escaping.
cleanup_err_file() {
  if [[ -n "${err_file:-}" ]]; then
    if ! rm -f "$err_file"; then
      echo "verify-moderation-cleared.sh: warning: could not remove temp file ${err_file} — remove it by hand" >&2
    fi
  fi
  return 0
}

emit_and_exit() {
  local ok="$1" reason="$2" status="$3" version="$4" attempts="$5" elapsed="$6" rc="$7"
  printf '{"ok":%s,"reason":%s,"moderation_status":%s,"version":%s,"attempts":%s,"elapsed_seconds":%s}\n' \
    "$ok" \
    "$(printf '%s' "$reason"  | jq -Rs .)" \
    "$(printf '%s' "$status"  | jq -Rs .)" \
    "$(printf '%s' "$version" | jq -Rs .)" \
    "$attempts" \
    "$elapsed"
  exit "$rc"
}

validate_positive_int() {
  local name="$1" value="$2"
  if ! [[ "$value" =~ ^[1-9][0-9]*$ ]]; then
    echo "error: ${name} must be a positive integer, got: '${value}'" >&2
    exit 2
  fi
}

is_block_state() {
  local s="$1" b
  for b in $BLOCK_STATES; do
    [[ "$s" == "$b" ]] && return 0
  done
  return 1
}

# Sleep one backoff interval and advance the schedule. Reads and WRITES the
# caller's `delay` and `elapsed` locals via bash dynamic scope (same idiom as
# the script-global `err_file`), so both the pending-poll path and the
# transient-fetch-retry path share one backoff schedule rather than
# duplicating it. Declares no `delay`/`elapsed` local of its own — that would
# shadow the caller's and silently stall the schedule.
advance_backoff() {
  sleep "$delay"
  elapsed=$(( elapsed + delay ))
  delay=$(( delay * 2 ))
  # `if`, not `(( ... )) && delay=...`: below the cap — every attempt until the
  # cap is reached — the arithmetic is false, and as the function's LAST command
  # that false becomes advance_backoff's return status. Both call sites are
  # plain simple commands, so `set -e` killed the script on the first backoff:
  # the wait ended after ONE poll, stdout empty, rc 1 — the code the contract
  # reserves for a real moderation finding (issue #306). The `if` form makes the
  # status structurally 0 instead of resting on a trailing statement.
  if (( delay > MAX_DELAY_SEC )); then
    delay="$MAX_DELAY_SEC"
  fi
}

main() {
  if [[ $# -ne 3 ]]; then
    echo "usage: $0 <workspace> <plugin> <version>" >&2
    exit 2
  fi
  local workspace="$1" tile="$2" version="$3"
  if [[ -z "$workspace" || -z "$tile" || -z "$version" ]]; then
    echo "error: <workspace>, <plugin>, and <version> are all required and must be non-empty — capture the published version from the registry advance confirmed by verify-publish-landed.sh" >&2
    exit 2
  fi

  validate_positive_int "VERIFY_MODERATION_BASE_DELAY_SEC" "$BASE_DELAY_SEC"
  validate_positive_int "VERIFY_MODERATION_MAX_DELAY_SEC" "$MAX_DELAY_SEC"
  validate_positive_int "VERIFY_MODERATION_BUDGET_SEC" "$BUDGET_SEC"
  validate_positive_int "VERIFY_MODERATION_FETCH_RETRY_MAX" "$FETCH_RETRY_MAX"
  if (( BASE_DELAY_SEC > MAX_DELAY_SEC )); then
    echo "error: VERIFY_MODERATION_BASE_DELAY_SEC (${BASE_DELAY_SEC}) must not exceed VERIFY_MODERATION_MAX_DELAY_SEC (${MAX_DELAY_SEC})" >&2
    exit 2
  fi
  if (( BASE_DELAY_SEC > BUDGET_SEC )); then
    echo "error: VERIFY_MODERATION_BASE_DELAY_SEC (${BASE_DELAY_SEC}) must not exceed VERIFY_MODERATION_BUDGET_SEC (${BUDGET_SEC})" >&2
    exit 2
  fi

  local endpoint="v1/tiles/${workspace}/${tile}/versions/${version}"
  local delay="$BASE_DELAY_SEC" elapsed=0 attempts=0 fetch_failures=0
  err_file=$(mktemp) || { echo "error: mktemp failed — cannot run verify-moderation-cleared.sh without writable TMPDIR" >&2; exit 2; }
  # Named handler ending `return 0` — the EXIT trap's final command status
  # becomes the script's exit status, so a failing `rm` would rewrite the
  # moderation verdict into a bare 1 (rules/error-handling.md).
  trap cleanup_err_file EXIT

  while :; do
    attempts=$(( attempts + 1 ))

    # Separate stdout (the JSON body) from stderr so a tessl warning can't
    # poison the parsed payload — same capture discipline as
    # verify-publish-landed.sh.
    # A non-zero `tessl api` exit is retried, not fatal: an outdated CLI's
    # version-check blip or a momentary network drop must not abort a wait
    # for a release that already published (issue #304). Escalate to exit 2
    # only after FETCH_RETRY_MAX CONSECUTIVE failures, or if the budget runs
    # out first — a persistent tool/auth/network fault, not a missing version.
    local body
    if ! body=$(tessl api "$endpoint" 2>"$err_file"); then
      local err; err=$(cat "$err_file")
      fetch_failures=$(( fetch_failures + 1 ))
      if (( fetch_failures >= FETCH_RETRY_MAX )); then
        echo "error: 'tessl api ${endpoint}' failed ${fetch_failures} consecutive time(s): ${err} — verify (1) tessl CLI is installed and on PATH ('command -v tessl'), (2) the workspace/plugin/version slug is correct, (3) you have network access to the registry, then re-run; the version was already confirmed on the registry by verify-publish-landed.sh, so a persistent failure here is a tool/auth/network problem, not a missing version" >&2
        exit 2
      fi
      if (( elapsed + delay > BUDGET_SEC )); then
        echo "error: 'tessl api ${endpoint}' kept failing (${fetch_failures} consecutive) and the ${BUDGET_SEC}s budget is exhausted: ${err} — treat as a tool/auth/network failure, not a moderation verdict; re-run once tessl is reachable" >&2
        exit 2
      fi
      echo "verify-moderation-cleared.sh: warning: 'tessl api ${endpoint}' failed (attempt ${fetch_failures}/${FETCH_RETRY_MAX}), retrying after backoff" >&2
      advance_backoff
      continue
    fi
    fetch_failures=0

    # Validate the payload ONCE, explicitly, before extracting fields.
    # Each extraction previously carried `2>/dev/null || true`, collapsing
    # two different states into the same empty string: a field legitimately
    # absent (what `// empty` is for), and a body that is not JSON at all
    # (a proxy error page, an HTML 502). Both then reached the same
    # "response shape may have changed — update this script's jq paths"
    # diagnostic below. That exit code was right and the message was wrong:
    # it sends the operator to rewrite a parse that works, against a
    # registry that is returning 502s (rules/error-handling.md Actionable
    # Messages).
    # `jq empty`, not `jq -e .`: `-e` sets the exit code from the last
    # OUTPUT's truthiness, so a body that is valid JSON but evaluates to
    # `false` or `null` would exit 1 and be misreported "not valid JSON".
    # `jq empty` parses and produces no output — exit reflects parse
    # validity alone.
    if ! printf '%s' "$body" | jq empty >/dev/null 2>&1; then
      echo "error: 'tessl api ${endpoint}' returned a body that is not valid JSON — the registry may be returning an error page or the endpoint shape changed; inspect it directly with 'tessl api ${endpoint}' before retrying (body was: ${body})" >&2
      exit 2
    fi

    # `(.data.attributes.FIELD)?` parenthesizes the WHOLE path before `?`, so
    # an index error at ANY step is suppressed — `.data` a string, or `.data`
    # an object whose `.attributes` is a string/array (a proxy error page that
    # parsed). A one-level `.attributes?` only guards that step and still
    # crashes when `.attributes` itself is the wrong type. Without the guard
    # the call hard-fails under `set -e` and the script exits on jq's raw
    # error, skipping the shape-changed diagnostic below; with it, a wrong
    # shape yields empty for all three and falls through to that diagnostic.
    # Three separate calls, not a `@tsv` read — tab is IFS-whitespace, so a
    # leading empty field would shift the values.
    local status passed mod_error
    status=$(printf '%s' "$body" | jq -r '(.data.attributes.moderationStatus)? // empty')
    passed=$(printf '%s' "$body" | jq -r '(.data.attributes.moderationPassed)? // empty')
    mod_error=$(printf '%s' "$body" | jq -r '(.data.attributes.moderationError)? // empty')

    # A moderationError alone is a valid (blocked) response, so it counts
    # as a parsed field — without it in the guard, a block-by-error result
    # with no status/passed would wrongly exit 2 instead of the rc 1
    # blocked finding below.
    if [[ -z "$status" && -z "$passed" && -z "$mod_error" ]]; then
      echo "error: could not parse moderation fields from 'tessl api ${endpoint}' (body was: ${body}) — the registry response shape may have changed; inspect it directly and update verify-moderation-cleared.sh's jq paths" >&2
      exit 2
    fi

    # Cleared: explicit pass status or the boolean flag.
    if [[ "$status" == "pass" || "$passed" == "true" ]]; then
      emit_and_exit "true" \
        "moderation cleared for ${workspace}/${tile}@${version} (status=${status:-pass}) after ${attempts} check(s), ${elapsed}s" \
        "$status" "$version" "$attempts" "$elapsed" 0
    fi

    # Blocked: a non-null moderation error or a terminal block status —
    # fail loud immediately rather than waiting out the budget.
    if [[ -n "$mod_error" ]] || is_block_state "$status"; then
      emit_and_exit "false" \
        "moderation BLOCKED for ${workspace}/${tile}@${version} (status=${status:-unknown}${mod_error:+, error=${mod_error}}) — the version is published but install-gated; resolve the moderation finding (see https://tessl.io/registry/${workspace}/${tile}/security) before treating the release as confirmed" \
        "$status" "$version" "$attempts" "$elapsed" 1
    fi

    # Still pending. Stop if the next sleep would exceed the budget.
    if (( elapsed + delay > BUDGET_SEC )); then
      emit_and_exit "false" \
        "moderation still pending for ${workspace}/${tile}@${version} (status=${status:-pending}) after ${attempts} check(s) and ${elapsed}s — budget ${BUDGET_SEC}s exhausted; the release is NOT confirmed. Re-run verify-moderation-cleared.sh ${workspace} ${tile} ${version} to keep waiting, or inspect the registry if it stays pending" \
        "$status" "$version" "$attempts" "$elapsed" 1
    fi

    advance_backoff
  done
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  main "$@"
fi

skills

README.md

tile.json