Files
gstack/bin/gstack-brain-enqueue
T
garrytan cd7485b0b4 W1: guarded migration of every executable state-root site; uninstall deletes only ~/.gstack
Bins, careful/freeze hooks, setup, upgrade migrations, browse/src, design,
ios-qa daemon, lib and scripts resolve the state root through
bin/gstack-state-root.sh (bash) or lib/state-root.ts (TS). Bins source the
twin and stop with a reinstall message when it is missing; hooks source it
and never spawn gstack-paths. browse/src/config.ts and lib/cso/state.ts
delegate to resolveStateRoot. Analytics writers and readers move together
so the usage log stays one file. gstack-uninstall deletes state only at
~/.gstack, refuses (exit 2) when it resolves to /, $HOME or an ancestor,
the checkout or the git root, and leaves any other resolved root in place
with the removal command. Fixtures that copy single bins now copy the twin.
2026-09-30 06:38:50 +00:00

67 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# gstack-brain-enqueue — write a path record into the GBrain sync spool.
#
# Usage:
# gstack-brain-enqueue <file-path>
#
# Called by writer scripts (gstack-learnings-log, gstack-timeline-log, etc.)
# after their local write. Fire-and-forget; failures are silent (never blocks
# the writer). The spool is drained by `gstack-brain-sync --once` invoked from
# the preamble at skill START and END boundaries.
#
# No-op when:
# - artifacts_sync_mode is off (the default)
# - ~/.gstack/.git doesn't exist (feature not initialized)
# - <file-path> matches a line in ~/.gstack/.brain-skip.txt
#
# Env:
# GSTACK_HOME — override ~/.gstack state directory (aligns with writers).
# Tests use GSTACK_HOME=/tmp/test-$$ for isolation.
#
# Concurrency: maildir-style spool — one FILE per record under
# .brain-queue.d/, created via tmp-file + atomic rename. Writer and drainer
# never share an inode, so there is no append/rewrite race by construction
# (the legacy single-file .brain-queue.jsonl append could race the drain's
# rewrite). Filenames are <epoch>-<pid>-<uniq>.json, so a sorted listing is
# chronological.
# No `-e` — writer shims rely on this never failing loudly.
set -uo pipefail
FILE="${1:-}"
[ -z "$FILE" ] && exit 0
. "$(dirname "$0")/gstack-state-root.sh" 2>/dev/null || { echo "$0: cannot resolve the gstack state root: $(dirname "$0")/gstack-state-root.sh is missing. fix: reinstall with ./setup or /gstack-upgrade (docs/state-root.md)" >&2; exit 1; }
gstack_state_root_select
GSTACK_HOME="$_gstack_sr_root"
SPOOL="$GSTACK_HOME/.brain-queue.d"
SKIP_FILE="$GSTACK_HOME/.brain-skip.txt"
# Fast exits: no git repo, no sync.
[ ! -d "$GSTACK_HOME/.git" ] && exit 0
# Check sync mode. off → silent no-op.
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
MODE=$("$SCRIPT_DIR/gstack-config" get artifacts_sync_mode 2>/dev/null || echo off)
[ "$MODE" = "off" ] && exit 0
# User-maintained skip list (for secret-scan false positives).
if [ -f "$SKIP_FILE" ]; then
if grep -Fxq "$FILE" "$SKIP_FILE" 2>/dev/null; then
exit 0
fi
fi
# JSON-escape the file path (backslash + quotes only; paths shouldn't have other specials).
ESC_FILE=$(printf '%s' "$FILE" | sed 's/\\/\\\\/g; s/"/\\"/g')
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "")
# One spool file per record: tmp write + atomic rename. Any failure exits 0
# silently (fire-and-forget contract), cleaning up the tmp file.
mkdir -p "$SPOOL" 2>/dev/null || exit 0
TMP="$SPOOL/.tmp-$$-$RANDOM"
printf '{"file":"%s","ts":"%s"}\n' "$ESC_FILE" "$TS" > "$TMP" 2>/dev/null || { rm -f "$TMP" 2>/dev/null; exit 0; }
mv -f "$TMP" "$SPOOL/$(date +%s)-$$-$RANDOM.json" 2>/dev/null || rm -f "$TMP" 2>/dev/null
exit 0