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
78 changes: 78 additions & 0 deletions .claude/scripts/cockpit.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,23 @@ work="$(mktemp -d "${TMPDIR:-/tmp}/cockpit-test.XXXXXX")"
# cockpit-serve.sh in the background -- declared here so the SAME EXIT trap
# cleans it up no matter where in the script a later assertion fails.
server_pid=""
# alias_npm_pid/alias_node_pid: same idea, for the `npm run cockpit` alias
# smoke (section 5b) -- npm wraps the real node server behind npm -> sh ->
# node, so cleanup needs to reach the actual node PID too, not just npm's.
alias_npm_pid=""
alias_node_pid=""
cleanup() {
if [ -n "$server_pid" ]; then
kill "$server_pid" >/dev/null 2>&1 || true
wait "$server_pid" 2>/dev/null || true
fi
if [ -n "$alias_node_pid" ]; then
kill "$alias_node_pid" >/dev/null 2>&1 || true
fi
if [ -n "$alias_npm_pid" ]; then
kill "$alias_npm_pid" >/dev/null 2>&1 || true
wait "$alias_npm_pid" 2>/dev/null || true
fi
rm -rf "$work"
}
trap cleanup EXIT
Expand Down Expand Up @@ -430,6 +442,72 @@ tmp_after=0
for f in $tmp_glob; do [ -e "$f" ] && tmp_after=$((tmp_after + 1)); done
check "cockpit-serve.sh does not leak its temp HTML file after exit (3a-followup fix)" [ "$tmp_after" -eq "$tmp_before" ]

# ---------------------------------------------------------------------------
# 5b. `pnpm cockpit` alias smoke (issue #90 review fix): exercises the ROOT
# package.json's `scripts.cockpit` entry itself -- not cockpit-serve.sh
# directly -- via `npm run cockpit`. npm (not pnpm) is used here
# deliberately: npm ships with node, so it needs no extra toolchain
# (corepack/pnpm) in CI, and it reads the SAME scripts.cockpit entry pnpm
# would resolve, so this validates the alias wiring end-to-end. Same
# offline --fixtures seam and 127.0.0.1-only contract as section 5's
# cockpit-serve.sh smoke, on a fresh free port, against the SAME
# fixtures dir used above.
# ---------------------------------------------------------------------------
# shellcheck source=resolve-roots.sh
. "$script_dir/resolve-roots.sh"

check "root package.json has a cockpit script wired to cockpit-serve.sh" node -e '
const p = require(process.argv[1]);
const s = p.scripts && p.scripts.cockpit;
if (typeof s !== "string" || s.indexOf("cockpit-serve.sh") === -1) {
console.error("scripts.cockpit =", s);
process.exit(1);
}
' "$root/package.json"

alias_port="$(node -e '
const s = require("net").createServer();
s.listen(0, "127.0.0.1", () => { console.log(s.address().port); s.close(); });
')"

# --prefix (rather than a `cd`) so the script'"'"'s own relative path
# (.claude/scripts/cockpit-serve.sh) resolves against the repo root
# regardless of this test'"'"'s own cwd.
alias_log="$work/npm-run-cockpit.log"
: > "$alias_log"
npm --prefix "$root" run cockpit -- "$alias_port" --fixtures "$work/fixtures" >"$alias_log" 2>&1 &
alias_npm_pid=$!

alias_ready=0
for _ in $(seq 1 50); do
grep -q "cockpit serving" "$alias_log" 2>/dev/null && { alias_ready=1; break; }
kill -0 "$alias_npm_pid" 2>/dev/null || break # npm exited early -- stop polling
sleep 0.2
done
check "npm run cockpit (the scripts.cockpit alias) prints a startup line within the readiness timeout" [ "$alias_ready" -eq 1 ]

alias_resp="$(curl -s -o - -w '%{http_code}' "http://127.0.0.1:$alias_port/" 2>/dev/null)"
alias_code="${alias_resp: -3}"
alias_body="${alias_resp%???}"
check "npm run cockpit: GET / returns HTTP 200" [ "$alias_code" = "200" ]
check "npm run cockpit: GET / serves the dashboard (issues section present)" bash -c 'printf "%s" "$1" | grep -qF '"'"'<section id="issues"'"'"'' _ "$alias_body"

# npm wraps the real node server behind npm -> sh -> (bash exec) node, so
# killing only npm's own PID can leave the node process (and the listening
# port) orphaned. Find the actual PID bound to the port and kill it
# directly (lsof, falling back to ss if lsof isn't installed), then npm's
# own PID as a best-effort belt-and-braces cleanup.
if command -v lsof >/dev/null 2>&1; then
alias_node_pid="$(lsof -ti tcp:"$alias_port" 2>/dev/null | head -1)"
elif command -v ss >/dev/null 2>&1; then
alias_node_pid="$(ss -ltnp 2>/dev/null | grep ":$alias_port " | grep -oP 'pid=\K[0-9]+' | head -1)"
fi
[ -n "$alias_node_pid" ] && kill "$alias_node_pid" >/dev/null 2>&1 || true
kill "$alias_npm_pid" >/dev/null 2>&1 || true
wait "$alias_npm_pid" 2>/dev/null || true
alias_node_pid=""
alias_npm_pid=""

# ---------------------------------------------------------------------------
# 6. Worker inspector endpoint (GET /api/worker/<role>/<task>, issue #70):
# event timeline + latest breadcrumbs + live worktree forensics, entirely
Expand Down
4 changes: 4 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ worker worktrees. Run it, then open `.claude/state/cockpit.html`. No server, no
script header for usage (`--fixtures`, `GATES_FILE` override) and `docs/COCKPIT_EVALUATION.md` for the
design rationale.

For a live, auto-refreshing view, `.claude/scripts/cockpit-serve.sh` wraps the same renderer behind a small
HTTP server (127.0.0.1 only, node built-ins only). Run it via `pnpm cockpit` (default port 8090), or
`pnpm cockpit -- <port>` to override the port.

### Concurrent config-write safety
This template fans out to **parallel worktree-isolated workers**, and upstream
[anthropics/claude-code#29217](https://github.com/anthropics/claude-code/issues/29217) reported that
Expand Down
13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "recode",
"version": "0.1.0",
"private": true,
"description": "Orchestrated multi-agent Claude Code harness — root package.json is a thin alias for the cockpit dashboard (no dependencies, node built-ins only).",
"scripts": {
"cockpit": "bash .claude/scripts/cockpit-serve.sh"
},
"engines": {
"node": ">=20"
},
"packageManager": "[email protected]"
}
Loading