The sweep (main's item 1): 199 tracked text files, 783 lines. The founder's full name, first name and possessive become "the founder" (sentence starts capitalised); the lowercase operating-system user name in WSL paths and commands becomes <user>; the second owner login becomes "the second owner login"; the three earlier businesses and the two other brands become "the other business", "the earlier entity", "the earlier business" and "another brand"; the Chrome profile rule names the igneum.network profile, not the profile's label. The standing commit login igneum-labs is not a founder term here: the fresh-repository step renames it in the history (docs/plans/history-rewrite.md, tools/repo/fresh-repo.sh). The patterns never appear in plain text in the tree (a plaintext list would be the hit): tools/ci/founder-strings.b64 (perl regex, tab, a sample per row) is read by tools/ci/founder-strings-check.sh (every tracked text file, perl, known-failed first: the self-test plants each row's sample in a fixture and the hit must name the file), by tools/community/discord-hooks.mjs (the guard's founder and business rows; the test takes its fixtures from the samples) and by tools/repo/fresh-repo.sh (the business names of the rewrite rules). site/forbidden-strings.txt carries the same patterns as b64: lines, decoded case-insensitive by site/scrub.mjs and tools/ci/launch-gates-check.mjs (whose fixture now plants an encoded made-up name). The check runs in the gate's tree checks on every merge. Not in this commit, by main's word: the 105 commit messages and 40 personal-identity commits that need the history rewrite (listed, not run), and the secrets found by gitleaks over the history (reported with owners). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
89 lines
5.5 KiB
Bash
Executable file
89 lines
5.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# The capacity controller on igneum-build-1 (infra/build-server/capacity). Runs the job queue in priority order, one job
|
|
# at a time, each for a bounded slice; a 5 s poll of /srv/builds/_locks SIGSTOPs the running job the instant any build
|
|
# slot or the measure hold is taken and SIGCONTs it when they clear. The layer never takes a build slot and never writes
|
|
# under /srv/builds/<worktree>. Started as user build by igneum-capacity.service (Nice 19, chrt -i 0 wrapper, CPUQuota
|
|
# leaving 8 threads free). Killed by the night battery's start and restarted after (the service's ExecStartPre/StopPost).
|
|
#
|
|
# run.sh the controller loop (systemd runs this)
|
|
# run.sh --once one pass through the weighted sequence, then exit (for a manual check)
|
|
# run.sh --dry-run call every job's --dry-run in priority order and exit
|
|
#
|
|
# The weighted sequence (CAP_SEQUENCE) gives the earlier, higher-priority jobs more turns. Default:
|
|
# pow-fuzz pow-fuzz pow-fuzz sync-fuzz sync-fuzz sim-sweeps model-sweeps clippy-audit
|
|
# Each turn runs one job for CAP_SLICE_S (default 1800 s) through run_slice, which backgrounds the job in its own
|
|
# process group and pauses/resumes it with the lock poll.
|
|
set -uo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; export CAP_JOB=run
|
|
. "$HERE/../capacity/lib.sh" 2>/dev/null || . "$HERE/lib.sh"
|
|
|
|
SLICE_S="${CAP_SLICE_S:-1800}"
|
|
POLL_S="${CAP_POLL_S:-5}"
|
|
SEQUENCE="${CAP_SEQUENCE:-pow-fuzz pow-fuzz pow-fuzz sync-fuzz sync-fuzz sim-sweeps model-sweeps clippy-audit}"
|
|
JOBS_DIR="$HERE/jobs"
|
|
|
|
"$NODE_BIN" "$SUMMARY" init 2>/dev/null || true
|
|
|
|
if [ "${1:-}" = --dry-run ]; then
|
|
for j in pow-fuzz sync-fuzz sim-sweeps model-sweeps clippy-audit; do
|
|
cap_say "dry-run $j"; bash "$JOBS_DIR/$j.sh" --dry-run || cap_say "$j dry-run returned $?"
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
# run_slice <job>: background the job in its own process group, poll the locks, STOP while a build or measure holds,
|
|
# CONT when clear, enforce the slice as a wall clock, reap at the end. The job writes its own summary; the controller
|
|
# updates the top-level state and current_job.
|
|
run_slice() {
|
|
local job="$1" log="$CAP_LOG/$job.slice.log"; mkdir -p "$CAP_LOG"
|
|
cap_say "slice start: $job (${SLICE_S}s)"
|
|
# own process group via setsid so STOP/CONT reach the whole cargo/python tree
|
|
setsid bash "$JOBS_DIR/$job.sh" --slice-s "$SLICE_S" > "$log" 2>&1 &
|
|
local pid=$! pgid; pgid=$(ps -o pgid= -p "$pid" 2>/dev/null | tr -d ' '); [ -n "$pgid" ] || pgid="$pid"
|
|
local paused=0 end=$(( $(date +%s) + SLICE_S + 120 )) # a 2 min grace over the slice for the job's own teardown
|
|
while kill -0 "$pid" 2>/dev/null; do
|
|
if cap_build_active; then
|
|
if [ "$paused" = 0 ]; then kill -STOP -"$pgid" 2>/dev/null && paused=1; local why; why=$(cap_hold_reason); cap_say "pause $job ($why)"; printf '{"state":"paused","current_job":%s,"paused_reason":%s,"slice_s":%s}' "$(cap_json_str "$job")" "$(cap_json_str "$why")" "$SLICE_S" | cap_controller_summary; fi
|
|
else
|
|
if [ "$paused" = 1 ]; then kill -CONT -"$pgid" 2>/dev/null; paused=0; cap_say "resume $job"; printf '{"state":"running","current_job":%s,"paused_reason":null,"slice_s":%s}' "$(cap_json_str "$job")" "$SLICE_S" | cap_controller_summary; fi
|
|
fi
|
|
# a safety stop so a wedged job cannot hold the turn forever; a paused job's clock does not advance it past end+grace
|
|
if [ "$paused" = 0 ] && [ "$(date +%s)" -ge "$end" ]; then cap_say "slice over time, stopping $job"; kill -INT -"$pgid" 2>/dev/null; sleep 3; kill -KILL -"$pgid" 2>/dev/null; break; fi
|
|
sleep "$POLL_S"
|
|
done
|
|
[ "$paused" = 1 ] && kill -CONT -"$pgid" 2>/dev/null
|
|
wait "$pid" 2>/dev/null; local rc=$?
|
|
cap_say "slice end: $job rc $rc"
|
|
}
|
|
|
|
once() {
|
|
for job in $SEQUENCE; do
|
|
# No mining on any Hetzner box, ever (the founder through main, 7 October 2026; Hetzner's policies forbid it): a job that would start a
|
|
# miner or a GPU worker is refused here, whatever SEQUENCE says. Nodes, builds, tests, benchmarks and CPU proving only.
|
|
if grep -qE 'igneum-miner[[:space:]]+mine|igneum-worker-(cuda|opencl)|igneum-app.*--mine|cargo run.*-p[[:space:]]+igneum-miner' "$JOBS_DIR/$job.sh" 2>/dev/null; then
|
|
echo "capacity: REFUSED job $job: it would start a miner or a GPU worker; no mining on a Hetzner box (infra/build-server/README.md)" >&2; continue
|
|
fi
|
|
[ -f "$JOBS_DIR/$job.sh" ] || { cap_say "no job $job"; continue; }
|
|
# wait out a running build before starting a slice (do not even launch during a build)
|
|
while cap_build_active; do
|
|
printf '{"state":"waiting","current_job":null,"paused_reason":%s,"slice_s":%s}' "$(cap_json_str "$(cap_hold_reason)")" "$SLICE_S" | cap_controller_summary
|
|
sleep "$POLL_S"
|
|
done
|
|
printf '{"state":"running","current_job":%s,"paused_reason":null,"slice_s":%s,"host":%s}' "$(cap_json_str "$job")" "$SLICE_S" "$(cap_json_str "$(hostname)")" | cap_controller_summary
|
|
run_slice "$job"
|
|
done
|
|
}
|
|
|
|
cap_sync_checkout >/dev/null 2>&1 || cap_say "initial checkout had trouble (jobs will retry)"
|
|
if [ "${1:-}" = --once ]; then once; printf '{"state":"idle","current_job":null,"paused_reason":null}' | cap_controller_summary; exit 0; fi
|
|
|
|
trap 'cap_say "controller stopping"; printf "{\"state\":\"stopped\",\"current_job\":null}" | cap_controller_summary; exit 0' INT TERM
|
|
cap_say "controller start (sequence: $SEQUENCE; slice ${SLICE_S}s; poll ${POLL_S}s)"
|
|
cycle=0
|
|
while true; do
|
|
cycle=$((cycle + 1))
|
|
# refresh the checkout at the top of each cycle (cheap when nothing moved); yields if a build is active
|
|
while cap_build_active; do sleep "$POLL_S"; done
|
|
cap_sync_checkout >/dev/null 2>&1 || true
|
|
once
|
|
done
|