Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,9 @@ smaller hosts, where RAM is capped by any container memory limit. On Linux,
available memory includes reclaimable host and cgroup file cache and is bounded
by the remaining budget of each enclosing memory cgroup. Load and memory count
work from every session and user. The load average lags new work, so the gate
admits at most one waiting command every 5 seconds per host rather than
releasing every waiter at once. Windows reports no load
admits at most one waiting command per 5-second window per host, never in
consecutive windows, rather than releasing every waiter at once: a burst of
waiters starts about 10 seconds apart. Windows reports no load
average and macOS counts reclaimable cache as used memory, so those signals are
skipped there.

Expand Down
1 change: 0 additions & 1 deletion quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ ownership-manifest design:

## Required

- [Gate contention measurements](/quest/m0/local-gate.md) - evidence that gated checks stay responsive under shared load
- [Worktree process ownership](/quest/m0/worktree.md) - supervise checkout commands and release checkouts automatically
- [Agent lifecycle integration](/quest/m0/worktree-agents.md) - reclaim completed and suspended checkouts through their harness
- [Agent workflow helpers](/quest/m0/helpers/README.md) - CLI commands for CI waits, PR status, claims, quest moves, and serialized merges
Expand Down
1 change: 0 additions & 1 deletion quest/m0/helpers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,4 @@ commands from Sep-Oct. Session counts in each child come from that window.

## Related

- [Expensive command gate](/quest/m0/local-gate.md) - CPU admission for the checks these helpers wrap
- [Agent lifecycle integration](/quest/m0/worktree-agents.md) - removes the agent-level load-average rules
1 change: 0 additions & 1 deletion quest/m0/helpers/headless-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,4 @@ Recommended shape, open for review:
## Related

- [Agent lifecycle integration](/quest/m0/worktree-agents.md) - removes the skill's load-average launch rules, so neither the skill nor the command polls load
- [Expensive command gate](/quest/m0/local-gate.md) - admits expensive phases inside each agent, so the command needs no load polling
- [Runner](/quest/m0/kix/runner.md) - the kix runner's headless Claude Code path; share the adapter
59 changes: 0 additions & 59 deletions quest/m0/local-gate.md

This file was deleted.

1 change: 0 additions & 1 deletion quest/m0/worktree-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ merge.
## Required

- [Worktree process ownership](/quest/m0/worktree.md) - supervised commands and automatic scratch release
- [Expensive command gate](/quest/m0/local-gate.md) - resource control before removing agent-count throttles

## Related

Expand Down
1 change: 0 additions & 1 deletion quest/m0/worktree.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,4 @@ work, stealing a live lock, or deleting scratch a running process still uses.

## Related

- [Gate contention measurements](/quest/m0/local-gate.md) - independent CPU and memory admission
- [Agent lifecycle integration](/quest/m0/worktree-agents.md) - native agents and workflow adoption
124 changes: 124 additions & 0 deletions scripts/gate-bench.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
# Measure `quest gate` under contention. Run inside `nix develop`.
#
# scripts/gate-bench.sh overhead [runs]
# Admission cost: `quest gate true` with a fresh claim directory, so no
# window is ever taken, against the same call with the gate bypassed.
# scripts/gate-bench.sh gated|ungated <suites> [cpus]
# Start <suites> Worker test suites at once, each in its own detached
# worktree of HEAD, through the gate or bypassing it. `cpus` is a taskset
# list that confines the suites to a few cores, to oversubscribe them
# without saturating a shared host; the gate still sees host-wide load.
#
# Reports failed suites, 5-second test timeouts, admission waits, run times,
# the makespan, and the peak 1-minute load and CPU pressure and least available
# memory while the suites ran. Other work on the host counts too, so record the
# load printed first.
set -euo pipefail

root=$(git -C "$(dirname -- "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)
unset QUEST_GATE_ADMITTED
work=$(mktemp -d "${TMPDIR:-/tmp}/quest-gate-bench.XXXXXX")
sampler=
cleanup() {
[[ -z $sampler ]] || kill "$sampler" 2>/dev/null || true
for tree in "$work"/tree.*; do
[[ ! -d $tree ]] || git -C "$root" worktree remove --force "$tree"
done
rm -rf "$work"
}
trap cleanup EXIT

usage() {
echo "usage: $0 overhead [runs] | $0 gated|ungated <suites> [cpus]" >&2
exit 2
}

now_ms() { echo $((${EPOCHREALTIME/./} / 1000)); }

# Print `<name>_median=` and `<name>_max=` of the remaining arguments.
stats() {
local name=$1
shift
printf '%s\n' "$@" | sort -n | awk -v n="$name" \
'{ v[NR] = $1 } END { printf "%s_median=%d %s_max=%d ", n, v[int((NR + 1) / 2)], n, v[NR] }'
}

overhead() {
local runs=${1:-20} i start bypass=() gated=()
cd "$root"
for ((i = 0; i < runs; i++)); do
start=$(now_ms)
QUEST_GATE_ADMITTED=1 bun run --silent quest gate true
bypass+=($(($(now_ms) - start)))
start=$(now_ms)
XDG_RUNTIME_DIR="$work/claims.$i" bun run --silent quest gate true
gated+=($(($(now_ms) - start)))
done
echo "runs=$runs $(stats bypass_ms "${bypass[@]}")$(stats gated_ms "${gated[@]}")"
}

contend() {
local mode=$1 suites=${2:-} cpus=${3:-} i start pin=() pids=()
[[ $suites =~ ^[1-9][0-9]*$ ]] || usage
[[ -z $cpus ]] || pin=(taskset -c "$cpus")

for ((i = 0; i < suites; i++)); do
git -C "$root" worktree add --quiet --detach "$work/tree.$i" HEAD
(cd "$work/tree.$i" && bun install --frozen-lockfile >/dev/null)
done

echo "mode=$mode suites=$suites cpus=${cpus:-all} host_cpus=$(nproc --all) load_before=$(cut -d' ' -f1-3 /proc/loadavg)"
start=$(now_ms)
(
while :; do
printf '%s %s %s\n' "$(cut -d' ' -f1 /proc/loadavg)" \
"$(sed -n 's/^some avg10=\([0-9.]*\).*/\1/p' /proc/pressure/cpu 2>/dev/null || echo 0)" \
"$(awk '/^MemAvailable:/ { print $2 }' /proc/meminfo)"
sleep 1
done
) >"$work/samples" &
sampler=$!

for ((i = 0; i < suites; i++)); do
(
cd "$work/tree.$i"
begin=$(now_ms)
[[ $mode == gated ]] || export QUEST_GATE_ADMITTED=1
status=0
# shellcheck disable=SC2016 # The admitted command expands it.
"${pin[@]}" timeout 900 bun run --silent quest gate \
bash -c 'echo $((${EPOCHREALTIME/./} / 1000)) >admitted; exec bun run --cwd cloudflare test' \
>log 2>&1 || status=$?
echo "$begin $(cat admitted 2>/dev/null || echo "$begin") $(now_ms) $status" >result
) &
pids+=($!)
done
wait "${pids[@]}"
kill "$sampler"
wait "$sampler" || true
sampler=

local failed=0 timeouts=0 waits=() runs=() last=0 begin admitted end status
for ((i = 0; i < suites; i++)); do
read -r begin admitted end status <"$work/tree.$i/result"
((status == 0)) || failed=$((failed + 1))
timeouts=$((timeouts + $(grep -c 'timed out in [0-9]*ms' "$work/tree.$i/log" || true)))
waits+=($((admitted - begin)))
runs+=($((end - admitted)))
((end < last)) || last=$end
done
printf 'failed_suites=%d/%d test_timeouts=%d %s%s' "$failed" "$suites" "$timeouts" \
"$(stats wait_ms "${waits[@]}")" "$(stats run_ms "${runs[@]}")"
awk -v span=$((last - start)) '$1 > l { l = $1 } $2 > p { p = $2 } NR == 1 || $3 < m { m = $3 }
END { printf "makespan_ms=%d peak_load1=%s peak_cpu_psi10=%s min_mem_available_gib=%.1f\n", span, l, p, m / 2^20 }' \
"$work/samples"
# The tests that failed most often, with how many suites they failed in.
grep -h '^ *FAIL ' "$work"/tree.*/log | sed 's/^ *//' | sort | uniq -c | sort -rn | head -n 10 || true
}

case ${1:-} in
overhead) overhead "${@:2}" ;;
gated | ungated) contend "$@" ;;
*) usage ;;
esac
Loading