garrytan / garrytan/gstack

Two defects in `_SESSIONS` concurrent-session detection — value never echoed + marker TTL doesn't reflect 'currently active'

Open
#1,651 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## Context

Built a CLAUDE.md-level concurrent-agent detection rule on top of gstack's `_SESSIONS` mechanism (so a session-start step can force the operator into a separate git worktree when another Claude Code agent is active in the same repo). Two defects surfaced during the ship + dogfooding.

## Defect A — `_SESSIONS` is computed but never echoed

Every skill preamble does:

```bash
mkdir -p ~/.gstack/sessions
touch ~/.gstack/sessions/"$PPID"
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
```

The variable is computed but only consumed by an internal check inside the preamble. The preamble echoes many other state values to stdout for the calling agent to read (`BRANCH:`, `PROACTIVE:`, `PROACTIVE_PROMPTED:`, `SKILL_PREFIX:`, `REPO_MODE:`, `LAKE_INTRO:`, `TELEMETRY:`, `TEL_PROMPTED:`, `EXPLAIN_LEVEL:`, `QUESTION_TUNING:`, `ARTIFACTS_SYNC:`, `LEARNINGS:`, `HAS_ROUTING:`, `ROUTING_DECLINED:`, `VENDORED_GSTACK:`, `MODEL_OVERLAY:`, `CHECKPOINT_MODE:`, `CHECKPOINT_PUSH:`) — but never `_SESSIONS:`. Any CLAUDE.md rule that tells the agent to *"read the preamble's session count"* has no value to read.

**Cost:** my v1 CLAUDE.md rule said exactly that and shipped non-functional. Caught on post-ship audit when I re-read my own /ship preamble output adversarially. Worked around by inlining the bash command (`find ~/.gstack/sessions -mmin -120 -type f | wc -l`) into the rule itself.

**Fix shape (cheap):** add `echo "SESSIONS: $_SESSIONS"` to the preamble's existing echo block. ~1 line. Backwards-compatible.

## Defect B — Marker TTL doesn't reflect "currently active"

The `touch ~/.gstack/sessions/$PPID` runs once per skill preamble invocation. The `find -mmin -120` finds markers touched within 2h. A long-running Claude Code session that doesn't invoke a gstack skill for >2h has its marker mtime stay frozen; eventually it falls outside the window and the session reports `SESSIONS=0` despite being active.

**Reproduced live in my own session post-ship:**

```
$ ls -la ~/.gstack/sessions/
-rw-r--r-- 1 user staff 0 May 21 12:28 65834 ← last skill invocation 10h+ ago

$ find ~/.gstack/sessions -mmin -120 -type f
(empty) ← marker outside the 2h window

$ # but my Claude Code session is still active and editing files
```

If two long-running sessions hit this state simultaneously, both register `SESSIONS=0` and both "proceed normally" while actively colliding — the exact failure mode the marker was designed to prevent.

**Fix shapes (pick one):**

1. **Refresh the marker via a `PreToolUse` hook** (gstack already uses these heavily). `touch ~/.gstack/sessions/$PPID` on every tool call. Marker mtime tracks "actively using tools," not "ran a skill in the last 2h."
2. Shorter TTL + periodic refresh hook. More moving parts.
3. Switch to a different detection signal entirely (e.g., `ps`). Platform-dependent, brittle.

Recommendation: option 1. Reuses existing hook infrastructure, minimal added complexity, gives accurate "is this session active" signal.

## Why this matters beyond my repo

Any gstack consumer building concurrent-agent safety on top of `_SESSIONS` will hit both gaps. Filing now so the design conversation happens once at the right layer instead of every consumer rolling their own workaround.

## Reference

Full forensic detail (with the multi-month context of the worktree-warfare failure mode this detection was meant to prevent) lives in my local repo at: [`docs/upstream-issues/gstack-sessions-detection-gaps.md`](https://github.com/Nomo-AI-Labs/nomo-ai/blob/main/docs/upstream-issues/gstack-sessions-detection-gaps.md) (will be on `main` once PR Nomo-AI-Labs/nomo-ai#2843 merges).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.