From d25572b18ea7994f02fddc8c853193ca876bde4e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 03:55:47 +0000 Subject: [PATCH 1/2] Add a cloud session startup hook and fix a stale SQL test The SessionStart hook installs the web, local API and reminder worker dependencies, starts the container's PostgreSQL and builds a disposable commune_dev database from bootstrap.sql, migrations 0001 to 0023 and after-core.sql. It runs only in Claude Code cloud sessions. atomic_commune_details.sql expected SQLSTATE 28000 for an anonymous commune creation, but create_commune has raised 42501 since migration 0001. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01TsrwEAZzSRz3fzyxfP8zso --- .claude/hooks/session-start.sh | 63 +++++++++++++++++++++ .claude/settings.json | 14 +++++ infra/neon/tests/atomic_commune_details.sql | 2 +- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100755 .claude/hooks/session-start.sh create mode 100644 .claude/settings.json diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh new file mode 100755 index 00000000..18bd37af --- /dev/null +++ b/.claude/hooks/session-start.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Prepares a Claude Code cloud session: installs the web app, local API and +# reminder worker dependencies, starts the container's PostgreSQL and creates a +# disposable `commune_dev` database with every migration applied. +# Cloud sessions only; it does nothing on a local machine. +set -euo pipefail + +if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then + exit 0 +fi + +cd "$CLAUDE_PROJECT_DIR" + +pnpm install --frozen-lockfile --prefer-offline \ + --filter workspace \ + --filter @commune/web \ + --filter @commune/local-api \ + --filter @commune/reminder-worker + +# PostgreSQL is optional: skip the database step if the image has none. +if ! command -v pg_ctlcluster >/dev/null 2>&1; then + echo "PostgreSQL not installed; skipping database setup." >&2 + exit 0 +fi + +cluster=$(pg_lsclusters --no-header | awk 'NR==1 {print $1" "$2}') +if [ -n "$cluster" ]; then + # shellcheck disable=SC2086 + pg_ctlcluster $cluster start 2>/dev/null || true +fi +for _ in $(seq 1 20); do + pg_isready -q -h 127.0.0.1 && break + sleep 0.5 +done + +# A local superuser for disposable test databases. The password is not a +# secret: this cluster only listens on loopback inside the cloud container. +su postgres -c "psql -q -v ON_ERROR_STOP=1" <<'SQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'commune') THEN + CREATE ROLE commune SUPERUSER LOGIN PASSWORD 'commune'; + END IF; +END +$$; +SQL + +export PGHOST=127.0.0.1 PGPORT=5432 PGUSER=commune PGPASSWORD=commune +if [ -n "${CLAUDE_ENV_FILE:-}" ]; then + { + echo "export PGHOST=127.0.0.1 PGPORT=5432 PGUSER=commune PGPASSWORD=commune" + echo "export COMMUNE_DEV_DATABASE_URL=postgres://commune:commune@127.0.0.1:5432/commune_dev" + } >> "$CLAUDE_ENV_FILE" +fi + +# Build commune_dev once, as described in infra/local/README.md. An existing +# database is left untouched; drop it to rebuild from the migrations. +if ! psql -d postgres -Atc "SELECT 1 FROM pg_database WHERE datname = 'commune_dev'" | grep -q 1; then + createdb commune_dev + for file in infra/local/bootstrap.sql $(ls infra/neon/migrations/*.sql | sort) infra/local/after-core.sql; do + psql -d commune_dev -q -v ON_ERROR_STOP=1 -f "$file" >/dev/null + done +fi diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..e06b0338 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,14 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh" + } + ] + } + ] + } +} diff --git a/infra/neon/tests/atomic_commune_details.sql b/infra/neon/tests/atomic_commune_details.sql index 762c412d..88703415 100644 --- a/infra/neon/tests/atomic_commune_details.sql +++ b/infra/neon/tests/atomic_commune_details.sql @@ -161,7 +161,7 @@ BEGIN 'Anonymous', 'household', 'GBP', '', '', '', '' ); RAISE EXCEPTION 'Anonymous commune creation succeeded'; - EXCEPTION WHEN SQLSTATE '28000' THEN NULL; + EXCEPTION WHEN SQLSTATE '42501' THEN NULL; END; END; $$; From 57c1822dbb41a2ec270ea0a175371b2040e515b0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 02:02:46 +0000 Subject: [PATCH 2/2] Keep SQLSTATE 28000 in atomic_commune_details.sql after merging main Since migration 0011 an empty identity raises 28000 before create_commune is reached, so main's expectation is the right one and this branch's older change to 42501 is dropped. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_014ztcMYgKXTHtHy5mLuVbKK --- infra/neon/tests/atomic_commune_details.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/neon/tests/atomic_commune_details.sql b/infra/neon/tests/atomic_commune_details.sql index 88703415..762c412d 100644 --- a/infra/neon/tests/atomic_commune_details.sql +++ b/infra/neon/tests/atomic_commune_details.sql @@ -161,7 +161,7 @@ BEGIN 'Anonymous', 'household', 'GBP', '', '', '', '' ); RAISE EXCEPTION 'Anonymous commune creation succeeded'; - EXCEPTION WHEN SQLSTATE '42501' THEN NULL; + EXCEPTION WHEN SQLSTATE '28000' THEN NULL; END; END; $$;