# ContextBench AWS harness configuration — EXAMPLE / DEFAULTS.
#
# Usage:  cp config.env.example config.env   (config.env is gitignored)
# then edit config.env. Every variable below uses the ": ${VAR:=default}"
# form, so this file is safe to source AFTER config.env: values you set in
# config.env (or export in your shell) always win; this file only fills gaps.
#
# shellcheck shell=bash
# shellcheck disable=SC2034

# --- AWS ----------------------------------------------------------------
# Named profile for the aws CLI. Leave empty to use the default credential
# chain (env vars / default profile / SSO session).
: "${AWS_PROFILE:=}"
: "${AWS_REGION:=us-east-1}"

# --- Instance -----------------------------------------------------------
: "${INSTANCE_TYPE:=c7a.24xlarge}"     # 96 vCPU / 192 GB, AVX-512+VNNI (Zen 4)
: "${USE_SPOT:=1}"                     # 1 = spot, 0 = on-demand
# Spending is fail-closed. Spot launches require an explicit maximum hourly
# price. Falling back from Spot to on-demand is disabled unless it is opted in,
# and every on-demand start/launch must fit the explicit hourly ceiling.
: "${SPOT_MAX_HOURLY_USD:=0}"
: "${ALLOW_ON_DEMAND_FALLBACK:=0}"
: "${MAX_ON_DEMAND_HOURLY_USD:=0}"
# 01-provision.sh schedules an instance-initiated stop this many hours after
# each start. Increase deliberately for a full-suite run; never use 0.
: "${MAX_INSTANCE_HOURS:=6}"
# Optional availability-zone pin (e.g. us-east-1a). Leave empty on the FIRST
# launch so spot picks the AZ with capacity; once the data volume exists the
# scripts pin to the volume's AZ automatically.
: "${AZ:=}"
: "${ROOT_VOLUME_GB:=100}"             # gp3 root: OS, node/npm, embed-model cache
# Persistent gp3 data volume -> /srv/contextbench. Sizing: EVERY instance
# keeps a full repo clone under results/<run-id>/runs/<slug>/work for the
# life of the run (never cleaned up automatically by the driver): the 1,136-task full
# set is ~300-450GB of clones per run-id, plus evaluator eval-repos clones,
# venv, models, and any earlier rollout stages' runs/ trees on the same
# volume. 1TB covers the gated rollout if you delete old runs/ trees between
# stages (rm -rf /srv/contextbench/results/<old-run-id>/runs); run-remote.sh
# also enforces a free-space floor (DISK_FLOOR_GB, default 150) at start.
: "${DATA_VOLUME_GB:=1000}"

# --- SSH ----------------------------------------------------------------
: "${KEY_NAME:=memtrace-bench}"                     # EC2 key-pair name
: "${SSH_PUBKEY_PATH:=$HOME/.ssh/id_ed25519.pub}"   # imported into EC2 if key pair missing
: "${SSH_KEY_PATH:=$HOME/.ssh/id_ed25519}"          # private key used for ssh/rsync
: "${REMOTE_USER:=ubuntu}"

# --- Benchmark ----------------------------------------------------------
# Dataset: verified (500-task gated set) | full (1,136) | train (450) | test (50)
: "${DATASET:=verified}"
: "${BENCHMARK_LANE:=retrieval}"       # retrieval | agent
# Optional deterministic gates. MANIFEST_LIMIT takes the first N IDs after the
# fixed-seed shuffle. MANIFEST_SOURCE_RUN_ID reuses an earlier run's exact
# manifest for paired before/after measurement. They are mutually exclusive;
# both default to the complete selected dataset.
: "${MANIFEST_LIMIT:=0}"
: "${MANIFEST_SOURCE_RUN_ID:=}"
# Parallel instances. "auto" = min(cores/4, (mem_gb-8)/4), computed on the box.
: "${CONCURRENCY:=auto}"
# Standalone/local adapter default. The current scored AWS policy sets
# LINE_BUDGET=200 explicitly in the gitignored config.env; keep the example at
# 80 for legacy reproduction and never mistake this fallback for run provenance.
: "${LINE_BUDGET:=80}"
: "${SELECTOR_MODEL:=gpt-5}"           # OpenAI model for query planning + selection
: "${AGENT_MODEL:=openai/gpt-5}"       # coding agent model when BENCHMARK_LANE=agent
: "${AGENT_HISTORY_DAYS:=365}"         # lookback relative to each task base commit
# runner.py --selector-mode: "default" | "guarded" (guarded = 120-candidate
# pool, lane/exact floors, multi-file selection). Forwarded to
# parallel_driver.py only when != default, so older adapter trees keep working.
: "${SELECTOR_MODE:=default}"
# Post-selector packing is deliberately opt-in and is only for a fresh next
# run. "off" preserves the existing runner output; "offline-packing-v2"
# applies the sealed counterfactual policy after the raw selector audit exists.
: "${POST_SELECTOR_POLICY:=off}"
: "${RUN_TIMEOUT:=7200}"               # per-instance runner.py timeout (seconds)
# runner.py reads these two straight from os.environ (no argv flag); wired
# through 03-run.sh's INNER env string to run-remote.sh, which also carries
# its own "${VAR:=...}" defaults matching the same locked policy so a run
# started without 03-run.sh (e.g. directly in tmux on the box) still gets
# them. v5 adds BM25 window ranking inside oversized symbols; query v3 cleans
# issue-template noise and restores exact lanes for backticked lowercase names.
: "${CB_SEARCH_LIMIT:=100}"
: "${CB_PACK_POLICY:=v5}"
: "${CB_QUERY_STRATEGY:=v3}"

# --- Memtrace -----------------------------------------------------------
# Install mode:
#   npm    — install the MEMTRACE_VERSION pin from npm (default; reproducible)
#   source — rsync the PRIVATE Rust repo at MEMTRACE_SOURCE_DIR to the box and
#            cargo-build the release binary there (96 cores — fast). Scored
#            runs require a clean tree; the payload is checksummed before
#            transfer and verified remotely. Source travels only laptop ->
#            your own EC2 box.
: "${MEMTRACE_INSTALL_MODE:=npm}"
: "${MEMTRACE_SOURCE_DIR:=/Users/alexholmberg/Desktop/Memtrace/memtrace}"
# Reproducible benchmark runs require a clean Git tree. Set this to 1 only for
# an explicitly diagnostic run; preflight and the source manifest label it.
: "${ALLOW_DIRTY_SOURCE:=0}"

# Pin verified 2026-07-10: npm latest = 0.8.21 and matches the local dev
# slice. (An earlier plan said 0.8.19 — stale.) Ignored in source mode.
: "${MEMTRACE_VERSION:=0.8.21}"

# --- Watchdog -------------------------------------------------------------
# Per-instance watchdog in aws/remote/run-remote.sh: any single runner.py older
# than this gets its descendant tree killed, is logged as outcome=timeout in
# results/<run-id>/watchdog.log, and the hardened driver records a zero-context
# failure stub before continuing. This external guard is stricter than
# RUN_TIMEOUT (the driver's own isolated-process-group timeout).
# Sizing: legitimately slow instances exist (the dev-slice vscode instance
# took ~24 min at concurrency 4; the full 1,136-task set has bigger repos and
# EC2 runs at concurrency ~24), a killed instance retried via --resume runs
# cold and may hit the same wall again. The hardened driver retains the task as
# an explicit zero-context failure record, so an aggressive timeout lowers the
# score instead of silently shrinking its denominator. Keep this well above
# the slowest legit instance; 90 min is the floor for full-set runs.
: "${WATCHDOG_MINUTES:=90}"

# Graph-cache namespace. Per INFRASTRUCTURE.md this must digest everything
# the cache key does not already carry: Memtrace build, parser/ignore policy,
# embedding model + dimension. Bump the trailing -v1 if you change parser or
# ignore policy without a Memtrace version bump. NEVER reuse a cache dir
# across Memtrace versions.
# NOTE: the hardened parallel_driver.py does not yet forward --graph-cache-dir
# / --cache-namespace to runner.py; the run script passes them only if the
# driver advertises the flag (see aws/remote/run-remote.sh and README).
#
# In SOURCE mode the npm version pin is meaningless, so the default namespace
# digests the source HEAD sha instead; a dirty tree additionally digests
# `git diff HEAD` (untracked NEW files are not in that hash — commit or
# `git add -N` them if you rely on cache reuse across dirty builds).
# --no-optional-locks everywhere: MEMTRACE_SOURCE_DIR is being actively
# edited by another agent, and a plain `git status` opportunistically WRITES
# .git/index (racing their git operations with index.lock).
if [ -z "${CACHE_NAMESPACE:-}" ] && [ "${MEMTRACE_INSTALL_MODE}" = "source" ] \
        && git --no-optional-locks -C "${MEMTRACE_SOURCE_DIR}" rev-parse --short=12 HEAD >/dev/null 2>&1; then
    _cb_src_sha="$(git --no-optional-locks -C "${MEMTRACE_SOURCE_DIR}" rev-parse --short=12 HEAD)"
    _cb_dirty_suffix=""
    if [ -n "$(git --no-optional-locks -C "${MEMTRACE_SOURCE_DIR}" status --porcelain 2>/dev/null)" ]; then
        _cb_diff_sha="$(git --no-optional-locks -C "${MEMTRACE_SOURCE_DIR}" diff HEAD 2>/dev/null | shasum -a 256 | cut -c1-8)"
        _cb_dirty_suffix="-dirty${_cb_diff_sha}"
    fi
    CACHE_NAMESPACE="contextbench-src${_cb_src_sha}${_cb_dirty_suffix}-jina-code-768-v2"
    unset _cb_src_sha _cb_dirty_suffix _cb_diff_sha
fi
: "${CACHE_NAMESPACE:=contextbench-mt${MEMTRACE_VERSION}-jina-code-768-v2}"

# --- Naming -------------------------------------------------------------
: "${TAG_PREFIX:=contextbench}"        # Name tag for instance/volume/SG (idempotency key)
: "${TMUX_SESSION:=contextbench}"
: "${REMOTE_ADAPTER_DIR:=/home/${REMOTE_USER}/contextbench-adapter}"
