Skip to content

Commit eb4a7f1

Browse files
Benchmark the review, across harnesses, and report what it says
Everything measured so far proves mechanisms fire. None of it shows the skills produce a better review, which is the claim that actually matters and the one this repo had no evidence for. tests/evals/run.sh sends an identical prompt under three arms — Claude Code's built-ins alone, plus vstack's skills, plus gstack's — with every arm loading skills the same way, through project scope, so the only variable is which skills are present. Fixtures carry planted defects and decoys that look suspicious and are correct, findings come back as JSON with line numbers, and scoring is arithmetic. clean.py has nothing wrong with it at all, so every finding there is a false positive; that is the only unambiguous precision measurement in the set. The first pilot at one sample says none 3/3, vstack 2/3, gstack 2/3, zero false positives each. No arm beats the baseline. That is recorded here before running a larger sample, so the number cannot be chosen after seeing it. Two versions of this benchmark produced flattering nonsense before being caught, and both are documented in the file because they are the failure mode of anyone benchmarking their own project. Pointing CLAUDE_CONFIG_DIR at an empty directory removes authentication along with the configuration, so every baseline run returned "Not logged in", scored zero, and handed vstack a 5/6-to-0/6 victory over a CLI that was never logged in. And omitting `< /dev/null` makes the CLI wait for stdin and truncate. Each arm now reports which skills the session actually loaded, from the CLI's own init event, and an arm that did not load its harness is marked INVALID rather than scored. Also from the external review: the deploy tier is opt-in behind --with-deploy rather than installed by default, because someone who wants better Claude behaviour does not need this author's deployment stack. And bin/cloudflare-mcp now explains itself and exits cleanly when its server is not built, instead of dying with a raw "Cannot find module" that reads like vstack is broken.
1 parent 651ba89 commit eb4a7f1

12 files changed

Lines changed: 347 additions & 7 deletions

bin/cloudflare-mcp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
#!/bin/sh
2+
# cloudflare-mcp — thin wrapper around a locally-built Cloudflare MCP server.
3+
#
4+
# This one is the author's, and it is not vendored: the server itself lives in a separate
5+
# checkout. Nothing in this repo builds it, so on anyone else's machine the path below does not
6+
# exist. It used to exec straight into it and die with a raw "Cannot find module", which reads
7+
# like vstack is broken rather than like an optional integration is simply not set up.
8+
#
9+
# Point CLOUDFLARE_MCP_DIST at your own build if you have one. Otherwise this is inert, and the
10+
# only cost is the line in .claude.json.
211
set -eu
12+
313
if [ -f "$HOME/.config/agents/secrets.env" ]; then
414
. "$HOME/.config/agents/secrets.env"
5-
else
6-
echo "cloudflare-mcp: missing $HOME/.config/agents/secrets.env" >&2
7-
exit 1
815
fi
9-
exec node "${CLOUDFLARE_MCP_DIST:-$HOME/Projects/cloudflare-mcp/dist/index.js}"
16+
17+
DIST="${CLOUDFLARE_MCP_DIST:-$HOME/Projects/cloudflare-mcp/dist/index.js}"
18+
19+
if [ ! -f "$DIST" ]; then
20+
echo "cloudflare-mcp: not set up on this machine, so this server is doing nothing." >&2
21+
echo " expected a built server at: $DIST" >&2
22+
echo " set CLOUDFLARE_MCP_DIST to your own build, or remove the cloudflare-mcp entry" >&2
23+
echo " from ~/.claude.json if you do not use Cloudflare. vstack does not ship this server." >&2
24+
exit 0
25+
fi
26+
27+
if [ -z "${CLOUDFLARE_API_TOKEN:-}" ]; then
28+
echo "cloudflare-mcp: CLOUDFLARE_API_TOKEN is not set in ~/.config/agents/secrets.env" >&2
29+
exit 0
30+
fi
31+
32+
exec node "$DIST"

setup-machine.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
# nothing. Idempotent: every tool is checked before it is installed, so a second run is a
44
# fast no-op rather than a reinstall.
55
#
6-
# ./setup-machine.sh core + claude + deploy tiers
6+
# ./setup-machine.sh core + claude
7+
# ./setup-machine.sh --with-deploy also vercel and wrangler
78
# ./setup-machine.sh --with-security also trivy, gitleaks, nmap, nuclei
89
# ./setup-machine.sh --check report what is present, install nothing
910
# ./setup-machine.sh --dry-run print what would be installed
@@ -14,16 +15,22 @@
1415
# with node or the Xcode tools
1516
# claude the Claude Code CLI itself
1617
# conductor the Conductor Mac app, several agents in parallel (macOS only)
17-
# deploy vercel, wrangler — the autonomous deploy chain
18+
# deploy vercel, wrangler — opt-in. Someone who wants better
19+
# Claude behaviour does not need this
20+
# author's deployment stack, and
21+
# installing it by default made a
22+
# personal toolchain look like a
23+
# requirement of the product.
1824
# security trivy, gitleaks, nmap, nuclei — the /security command
1925
#
2026
# This script installs software. It never removes any, and it never touches your dotfiles.
2127
set -uo pipefail
2228

23-
WITH_SECURITY=0; CHECK=0; DRY=0; APT_UPDATED=0
29+
WITH_SECURITY=0; WITH_DEPLOY=0; CHECK=0; DRY=0; APT_UPDATED=0
2430
for a in "$@"; do
2531
case "$a" in
2632
--with-security) WITH_SECURITY=1 ;;
33+
--with-deploy) WITH_DEPLOY=1 ;;
2734
--check) CHECK=1 ;;
2835
--dry-run) DRY=1 ;;
2936
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
@@ -307,9 +314,13 @@ else
307314
fi
308315

309316
note ""
317+
if [ "$WITH_DEPLOY" = 1 ]; then
310318
note "== deploy"
311319
ensure_npm vercel vercel
312320
ensure_npm wrangler wrangler
321+
else
322+
note "== deploy (skipped — pass --with-deploy for vercel and wrangler)"
323+
fi
313324

314325
if [ "$WITH_SECURITY" = 1 ]; then
315326
note ""
1.36 KB
Binary file not shown.
788 Bytes
Binary file not shown.
614 Bytes
Binary file not shown.
874 Bytes
Binary file not shown.

tests/evals/fixtures/clean.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import Iterable
2+
3+
def normalise(names: Iterable[str]) -> list[str]:
4+
"""Lowercase and strip each name, dropping blanks."""
5+
out = []
6+
for n in names:
7+
n = n.strip().lower()
8+
if n:
9+
out.append(n)
10+
return out
11+
12+
def dedupe(values: list[str]) -> list[str]:
13+
"""Preserve first-seen order while removing duplicates."""
14+
seen = set()
15+
out = []
16+
for v in values:
17+
if v not in seen:
18+
seen.add(v)
19+
out.append(v)
20+
return out

tests/evals/fixtures/leak.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def read_config(path):
2+
f = open(path)
3+
# PLANTED: on a parse error the handle is never closed; the happy path is fine
4+
data = f.read()
5+
if not data.strip():
6+
raise ValueError("empty config")
7+
f.close()
8+
return data
9+
10+
def read_safe(path):
11+
# DECOY: with-statement is correct; no leak here
12+
with open(path) as f:
13+
return f.read()

tests/evals/fixtures/off-by-one.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
def chunk(items, size):
2+
out = []
3+
# PLANTED: range stops one chunk early when len(items) is not a multiple of size
4+
for i in range(0, len(items) - size, size):
5+
out.append(items[i:i + size])
6+
return out
7+
8+
def last_n(items, n):
9+
# DECOY: negative slicing is correct here even though it looks like it could underflow
10+
return items[-n:] if n > 0 else []
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import sqlite3
2+
3+
def find_user(conn, username):
4+
# PLANTED: user input concatenated straight into SQL
5+
q = "SELECT id, email FROM users WHERE name = '" + username + "'"
6+
return conn.execute(q).fetchall()
7+
8+
def find_by_role(conn, role_id):
9+
# DECOY: looks like string building, but the value is bound, not interpolated
10+
q = "SELECT id, email FROM users WHERE role = ?" + ""
11+
return conn.execute(q, (role_id,)).fetchall()
12+
13+
def audit_label(username):
14+
# DECOY: concatenation into a log string is not injection
15+
return "lookup:" + username

0 commit comments

Comments
 (0)