Skip to content

Commit 53d4fa6

Browse files
fix: use GOOSE_GTWALL_FILE (#1)
* Replace GOOSE_SERVER__SECRET_KEY with GOOSE_GTWALL_FILE The goose wrapper now creates the wall file and exports its path via GOOSE_GTWALL_FILE, which all delegates inherit automatically. This replaces the dependency on GOOSE_SERVER__SECRET_KEY (a server implementation detail) with an explicit, purpose-built env var that follows the same pattern as GOOSE_MOIM_MESSAGE_FILE for telepathy. Wall filenames use PID + RANDOM for uniqueness (wall-<pid>-<rand>.log). Positions directories are derived from the wall path rather than a separate naming scheme. Dashboard extracts and sanitizes WALL_ID from the wall filename for screen session and portfile scoping. AGENT_SESSION_ID was considered but rejected: delegates get their own value, so it cannot scope a shared wall file. * Fix exec trap, unanchored sed, and replace vs removeprefix - goose: drop exec so EXIT trap fires and cleans up wall + telepathy - gtwall: anchor sed in --list to strip only the wall- prefix - goosetown-ui: use removeprefix instead of replace for wall-id derivation
1 parent e4fcd42 commit 53d4fa6

8 files changed

Lines changed: 88 additions & 52 deletions

File tree

dashboard

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22
# dashboard — Launch goosetown dashboard. Idempotent. Port on stdout, noise on stderr.
33
#
4-
# Multi-instance safe: each goose instance (identified by GOOSE_SERVER__SECRET_KEY)
4+
# Multi-instance safe: each goose instance (identified by GOOSE_GTWALL_FILE)
55
# gets its own dashboard. Instances never interfere with each other — no shared screen
66
# names, no global pkill, no "stale session" replacement.
77
set -euo pipefail
@@ -19,11 +19,13 @@ info() { echo "$*" >&2; }
1919
command -v uv &>/dev/null || die "uv not found. Install: curl -LsSf https://astral.sh/uv/install.sh | sh"
2020

2121
# ── Instance identity ───────────────────────────────────────────────────────
22-
# GOOSE_SERVER__SECRET_KEY is a UUID unique per goose instance. The first 8 chars
23-
# (WALL_ID) already scope wall files and position dirs — we reuse it to scope
24-
# screen sessions and portfiles so multiple dashboards can coexist.
25-
[[ -n "${GOOSE_SERVER__SECRET_KEY:-}" ]] || die2 "GOOSE_SERVER__SECRET_KEY not set"
26-
WALL_ID="${GOOSE_SERVER__SECRET_KEY:0:8}"
22+
# GOOSE_GTWALL_FILE points to the wall file for this goose instance. Extract the
23+
# WALL_ID from the filename to scope screen sessions and portfiles so multiple
24+
# dashboards can coexist.
25+
[[ -n "${GOOSE_GTWALL_FILE:-}" ]] || die2 "GOOSE_GTWALL_FILE not set"
26+
# Sanitize to safe chars for screen session names and portfiles (must match gtwall's rules)
27+
WALL_ID=$(basename "$GOOSE_GTWALL_FILE" .log | sed 's/^wall-//' | tr -cd 'A-Za-z0-9_-')
28+
[[ -n "$WALL_ID" ]] || die2 "Could not derive WALL_ID from GOOSE_GTWALL_FILE"
2729
SCREEN_NAME="goosetown-ui-${WALL_ID}"
2830
PORTFILE="${WALLS_DIR}/dashboard-${WALL_ID}.port"
2931

@@ -58,10 +60,8 @@ resolve_session() {
5860

5961
# ── Wall resolution ─────────────────────────────────────────────────────────
6062
resolve_wall() {
61-
local wall_file="${WALLS_DIR}/wall-${WALL_ID}.log"
62-
mkdir -p "$WALLS_DIR"
63-
touch "$wall_file"
64-
echo "$wall_file"
63+
# Use GOOSE_GTWALL_FILE directly - it's already set by the goose wrapper
64+
echo "$GOOSE_GTWALL_FILE"
6565
}
6666

6767
# ── Port helpers ────────────────────────────────────────────────────────────
@@ -197,7 +197,7 @@ Usage: ./dashboard [--stop|--status|--open|--help]
197197
--help This message
198198
199199
Multiple goose instances get separate dashboards (ports 4242-4300).
200-
Each instance is scoped by GOOSE_SERVER__SECRET_KEY.
200+
Each instance is scoped by GOOSE_GTWALL_FILE.
201201
202202
Exit codes: 0=ok, 1=error/not-running, 2=config-error, 3=port-exhaustion, 4=startup-timeout
203203
EOF

goose

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,22 @@ touch "$TELEPATHY_FILE"
2222
# Export for tom extension - orchestrator and all delegates will see this
2323
export GOOSE_MOIM_MESSAGE_FILE="$TELEPATHY_FILE"
2424

25+
# Generate unique wall file for this session
26+
WALLS_DIR="${HOME}/.goosetown/walls"
27+
mkdir -p "$WALLS_DIR"
28+
WALL_FILE="${WALLS_DIR}/wall-$$-${RANDOM}.log"
29+
touch "$WALL_FILE"
30+
export GOOSE_GTWALL_FILE="$WALL_FILE"
31+
2532
# Cleanup on exit
2633
cleanup() {
2734
rm -f "$TELEPATHY_FILE"
35+
rm -f "$WALL_FILE"
36+
rm -rf "${WALL_FILE%.log}.positions"
2837
}
2938
trap cleanup EXIT
3039

3140
echo "🦆 Goosetown telepathy enabled: $TELEPATHY_FILE" >&2
41+
echo "🦆 Goosetown wall enabled: $WALL_FILE" >&2
3242

33-
exec "$GOOSE_BIN" "$@"
43+
"$GOOSE_BIN" "$@"

goose_gui

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,24 @@ touch "$TELEPATHY_FILE"
2121
# Export for tom extension
2222
export GOOSE_MOIM_MESSAGE_FILE="$TELEPATHY_FILE"
2323

24+
# Generate unique wall file for this session
25+
WALLS_DIR="${HOME}/.goosetown/walls"
26+
mkdir -p "$WALLS_DIR"
27+
WALL_FILE="${WALLS_DIR}/wall-$$-${RANDOM}.log"
28+
touch "$WALL_FILE"
29+
export GOOSE_GTWALL_FILE="$WALL_FILE"
30+
2431
# Cleanup on exit
2532
cleanup() {
2633
rm -f "$TELEPATHY_FILE"
27-
echo "Telepathy file cleaned up." >&2
34+
rm -f "$WALL_FILE"
35+
rm -rf "${WALL_FILE%.log}.positions"
36+
echo "Telepathy and wall files cleaned up." >&2
2837
}
2938
trap cleanup EXIT
3039

3140
echo "🦆 Goosetown GUI telepathy enabled: $TELEPATHY_FILE" >&2
41+
echo "🦆 Goosetown GUI wall enabled: $WALL_FILE" >&2
3242
echo "Keep this terminal open. Press Ctrl+C when done to clean up." >&2
3343

3444
# Launch GUI with -n to force new instance (ensures env var is picked up)

gtwall

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,26 @@
1212
WALL_DIR="${GTWALL_DIR:-${HOME}/.goosetown}"
1313
WALLS_DIR="${WALL_DIR}/walls"
1414

15-
# Use GOOSE_SERVER__SECRET_KEY as session ID - it's unique per goose session
16-
# and shared by orchestrator + all delegates automatically
17-
if [[ -n "${GOOSE_SERVER__SECRET_KEY:-}" ]]; then
18-
# Use first 8 chars of the UUID for readability
19-
SESSION_ID="${GOOSE_SERVER__SECRET_KEY:0:8}"
15+
# Use GOOSE_GTWALL_FILE if set, otherwise fallback to default wall
16+
if [[ -n "${GOOSE_GTWALL_FILE:-}" ]]; then
17+
WALL_FILE="$GOOSE_GTWALL_FILE"
2018
else
21-
# Fallback for non-goose usage
22-
SESSION_ID="default"
19+
# Fallback for non-goose usage (manual invocation)
20+
mkdir -p "$WALLS_DIR"
21+
WALL_FILE="${WALLS_DIR}/wall-default.log"
2322
fi
2423

24+
# Derive positions directory from wall file path
25+
POS_DIR="${WALL_FILE%.log}.positions"
26+
27+
# Extract display-friendly session ID from wall filename for help/status text
28+
SESSION_ID=$(basename "$WALL_FILE" .log | sed 's/^wall-//')
2529
# Sanitize SESSION_ID to safe filesystem characters (alphanumeric, dash, underscore)
2630
SESSION_ID=$(printf '%s' "$SESSION_ID" | tr -cd 'A-Za-z0-9_-')
2731
if [[ -z "$SESSION_ID" ]]; then
2832
SESSION_ID="default"
2933
fi
3034

31-
WALL_FILE="${WALLS_DIR}/wall-${SESSION_ID}.log"
32-
POS_DIR="${WALLS_DIR}/positions-${SESSION_ID}"
33-
3435
# Lock management - track if we hold the lock for cleanup
3536
LOCKFILE_HELD=""
3637

@@ -247,7 +248,11 @@ ID Rules:
247248
248249
Session:
249250
EOF
250-
printf ' Session ID: %s (from GOOSE_SERVER__SECRET_KEY)\n' "$SESSION_ID"
251+
if [[ -n "${GOOSE_GTWALL_FILE:-}" ]]; then
252+
printf ' Session ID: %s (from GOOSE_GTWALL_FILE)\n' "$SESSION_ID"
253+
else
254+
printf ' Session ID: %s (default - no GOOSE_GTWALL_FILE set)\n' "$SESSION_ID"
255+
fi
251256
printf ' Wall: %s\n' "$WALL_FILE"
252257
printf ' Positions: %s/\n' "$POS_DIR"
253258
}
@@ -353,7 +358,7 @@ USAGE
353358
shopt -s nullglob
354359
for f in "${WALLS_DIR}"/wall-*.log; do
355360
if [[ -f "$f" ]]; then
356-
session=$(basename "$f" .log | sed 's/wall-//')
361+
session=$(basename "$f" .log | sed 's/^wall-//')
357362
lines=$(wc -l < "$f" | tr -d ' ')
358363
if [[ "$session" == "$SESSION_ID" ]]; then
359364
printf ' %s: %s messages (current)\n' "$session" "$lines"

scripts/goosetown-ui

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,18 @@ def find_parent_session(cur) -> str | None:
8080

8181

8282
def find_wall_file(session_key: str | None = None) -> str | None:
83-
if session_key is None:
84-
secret = os.environ.get("GOOSE_SERVER__SECRET_KEY", "")
85-
session_key = secret[:8] if secret else None
83+
# Check GOOSE_GTWALL_FILE env var first
84+
gtwall_file = os.environ.get("GOOSE_GTWALL_FILE", "")
85+
if gtwall_file and os.path.isfile(gtwall_file):
86+
return gtwall_file
87+
88+
# Fallback: use session_key if provided
8689
if session_key:
8790
path = os.path.join(WALLS_DIR, f"wall-{session_key}.log")
8891
if os.path.isfile(path):
8992
return path
93+
94+
# Last resort: newest wall file
9095
wall_files = glob.glob(os.path.join(WALLS_DIR, "wall-*.log"))
9196
return max(wall_files, key=os.path.getmtime) if wall_files else None
9297

@@ -509,7 +514,7 @@ def main():
509514
wall_file = args.wall or find_wall_file()
510515
wall_id = ""
511516
if wall_file:
512-
wall_id = Path(wall_file).stem.replace("wall-", "")
517+
wall_id = Path(wall_file).stem.removeprefix("wall-")
513518
print(f"📜 Wall file: {wall_file}", file=sys.stderr)
514519
else:
515520
print("⚠️ No wall file found — will poll for it", file=sys.stderr)

tests/test_dashboard.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ set -uo pipefail
66
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
77
DASHBOARD="$SCRIPT_DIR/../dashboard"
88

9-
export GOOSE_SERVER__SECRET_KEY="dash${$}${RANDOM}"
9+
# Create unique wall file for this test session
10+
WALLS_DIR="$HOME/.goosetown/walls"
11+
mkdir -p "$WALLS_DIR"
12+
WALL_FILE="${WALLS_DIR}/wall-test-dash-${$}-${RANDOM}.log"
13+
touch "$WALL_FILE"
14+
export GOOSE_GTWALL_FILE="$WALL_FILE"
1015

1116
passed=0
1217
failed=0
@@ -18,6 +23,8 @@ skip() { echo " ⊘ $1 (skipped: $2)"; ((++skipped)) || true; }
1823

1924
cleanup() {
2025
"$DASHBOARD" --stop >/dev/null 2>&1 || true
26+
rm -f "$WALL_FILE" 2>/dev/null || true
27+
rm -rf "${WALL_FILE%.log}.positions" 2>/dev/null || true
2128
}
2229
trap cleanup EXIT
2330

tests/test_gtwall.sh

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ set -uo pipefail
66
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
77
GTWALL="$SCRIPT_DIR/../gtwall"
88

9-
# Use unique session key - PID + RANDOM for uniqueness
10-
export GOOSE_SERVER__SECRET_KEY="gt${$}${RANDOM}"
9+
# Create unique wall file for this test session
10+
WALLS_DIR="$HOME/.goosetown/walls"
11+
mkdir -p "$WALLS_DIR"
12+
WALL_FILE="${WALLS_DIR}/wall-test-${$}-${RANDOM}.log"
13+
touch "$WALL_FILE"
14+
export GOOSE_GTWALL_FILE="$WALL_FILE"
1115

1216
passed=0
1317
failed=0
@@ -16,9 +20,8 @@ pass() { echo " ✓ $1"; ((++passed)) || true; }
1620
fail() { echo "$1: $2" >&2; ((++failed)) || true; }
1721

1822
cleanup() {
19-
local wall_id="${GOOSE_SERVER__SECRET_KEY:0:8}"
20-
rm -rf "$HOME/.goosetown/walls/wall-${wall_id}.log"* 2>/dev/null || true
21-
rm -rf "$HOME/.goosetown/walls/positions-${wall_id}" 2>/dev/null || true
23+
rm -f "$WALL_FILE" 2>/dev/null || true
24+
rm -rf "${WALL_FILE%.log}.positions" 2>/dev/null || true
2225
}
2326
trap cleanup EXIT
2427

@@ -84,16 +87,17 @@ test_clear() {
8487

8588
test_session_isolation() {
8689
echo "test_session_isolation"
87-
# Keys must differ in first 8 chars
88-
local key_a="isola${$}A"
89-
local key_b="isolb${$}B"
90+
# Create separate wall files for isolation test
91+
local wall_a="${WALLS_DIR}/wall-test-isola-${$}.log"
92+
local wall_b="${WALLS_DIR}/wall-test-isolb-${$}.log"
93+
touch "$wall_a" "$wall_b"
9094

91-
GOOSE_SERVER__SECRET_KEY="$key_a" "$GTWALL" writer "secret-A" >/dev/null
92-
GOOSE_SERVER__SECRET_KEY="$key_b" "$GTWALL" writer "secret-B" >/dev/null
95+
GOOSE_GTWALL_FILE="$wall_a" "$GTWALL" writer "secret-A" >/dev/null
96+
GOOSE_GTWALL_FILE="$wall_b" "$GTWALL" writer "secret-B" >/dev/null
9397

9498
local outputA outputB
95-
outputA=$(GOOSE_SERVER__SECRET_KEY="$key_a" "$GTWALL" reader 2>/dev/null || echo "")
96-
outputB=$(GOOSE_SERVER__SECRET_KEY="$key_b" "$GTWALL" reader 2>/dev/null || echo "")
99+
outputA=$(GOOSE_GTWALL_FILE="$wall_a" "$GTWALL" reader 2>/dev/null || echo "")
100+
outputB=$(GOOSE_GTWALL_FILE="$wall_b" "$GTWALL" reader 2>/dev/null || echo "")
97101

98102
# Each should only see its own
99103
if [[ "$outputA" != *"secret-B"* && "$outputB" != *"secret-A"* ]]; then
@@ -103,22 +107,17 @@ test_session_isolation() {
103107
fi
104108

105109
# Cleanup isolation test walls
106-
rm -rf "$HOME/.goosetown/walls/wall-${key_a:0:8}.log"* 2>/dev/null || true
107-
rm -rf "$HOME/.goosetown/walls/wall-${key_b:0:8}.log"* 2>/dev/null || true
108-
rm -rf "$HOME/.goosetown/walls/positions-${key_a:0:8}" 2>/dev/null || true
109-
rm -rf "$HOME/.goosetown/walls/positions-${key_b:0:8}" 2>/dev/null || true
110+
rm -f "$wall_a" "$wall_b" 2>/dev/null || true
111+
rm -rf "${wall_a%.log}.positions" "${wall_b%.log}.positions" 2>/dev/null || true
110112
}
111113

112114
test_stale_lock_cleanup() {
113115
echo "test_stale_lock_cleanup"
114116
"$GTWALL" --clear >/dev/null 2>&1 || true
115117

116-
# Get correct lock path: ${WALLS_DIR}/wall-${SESSION_ID}.log.lock
117-
local wall_id="${GOOSE_SERVER__SECRET_KEY:0:8}"
118-
local walls_dir="$HOME/.goosetown/walls"
119-
local lock_dir="${walls_dir}/wall-${wall_id}.log.lock"
118+
# Get correct lock path from GOOSE_GTWALL_FILE
119+
local lock_dir="${WALL_FILE}.lock"
120120

121-
mkdir -p "$walls_dir"
122121
mkdir -p "$lock_dir"
123122

124123
# Create stale lock:

ui/js/components.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ function renderBulletin(st) {
221221
? html`<div class="bulletin-empty">
222222
${
223223
messages.length === 0
224-
? 'Waiting for wall… (start a goose session or check GOOSE_SERVER__SECRET_KEY)'
224+
? 'Waiting for wall… (start a goose session or check GOOSE_GTWALL_FILE)'
225225
: 'No messages match current filters.'
226226
}
227227
</div>`

0 commit comments

Comments
 (0)