fix(framework): per-session ticket files unblock parallel sessions [#439]
The ticket gate required the single .crush/active-ticket file, so two
concurrent sessions overwrote each other's ticket mid-work. The gate now
accepts any non-empty .crush/active-ticket* file; each session keeps its
own (e.g. active-ticket-plant, active-ticket-core). Also prune .crush/
session scratch from both shellcheck scanners so one session's throwaway
probe scripts cannot block the other session's commits. Documented in
AGENTS.md Task Tracking.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -160,6 +160,10 @@ for a full audit or `--fast` for pre-commit speed. Bypass with `--no-verify`
|
|||||||
The Crush hook (`hooks/ticket-gate.sh`) blocks modifying operations
|
The Crush hook (`hooks/ticket-gate.sh`) blocks modifying operations
|
||||||
until this file exists. If no ticket exists, CREATE ONE FIRST via
|
until this file exists. If no ticket exists, CREATE ONE FIRST via
|
||||||
redmine-cli, then set it. Clear when done: `> .crush/active-ticket`.
|
redmine-cli, then set it. Clear when done: `> .crush/active-ticket`.
|
||||||
|
**Parallel sessions:** each session uses its OWN suffixed file (e.g.
|
||||||
|
`.crush/active-ticket-plant`, `.crush/active-ticket-core`) so concurrent
|
||||||
|
sessions never clobber each other's ticket; any non-empty
|
||||||
|
`.crush/active-ticket*` satisfies the gate.
|
||||||
- **WORKING.md** is the only in-repo task tracker — a scratchpad for the current
|
- **WORKING.md** is the only in-repo task tracker — a scratchpad for the current
|
||||||
session. The pre-commit hook blocks commits while any task remains unchecked.
|
session. The pre-commit hook blocks commits while any task remains unchecked.
|
||||||
- **Parallel sessions:** when two sessions share this repo (e.g. physical plant
|
- **Parallel sessions:** when two sessions share this repo (e.g. physical plant
|
||||||
|
|||||||
+12
-7
@@ -9,7 +9,6 @@
|
|||||||
# run audits, check status, etc.).
|
# run audits, check status, etc.).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
TICKET_FILE="${CRUSH_PROJECT_DIR}/.crush/active-ticket"
|
|
||||||
TOOL="${CRUSH_TOOL_NAME:-}"
|
TOOL="${CRUSH_TOOL_NAME:-}"
|
||||||
CMD="${CRUSH_TOOL_INPUT_COMMAND:-}"
|
CMD="${CRUSH_TOOL_INPUT_COMMAND:-}"
|
||||||
|
|
||||||
@@ -54,12 +53,18 @@ case "$FILE_PATH" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Check for active ticket
|
# Check for active ticket. Parallel sessions each use their OWN file:
|
||||||
if [ -f "$TICKET_FILE" ] && [ -s "$TICKET_FILE" ]; then
|
# .crush/active-ticket (default / single session)
|
||||||
TICKET=$(cat "$TICKET_FILE")
|
# .crush/active-ticket-<name> (suffixed per session; any non-empty one
|
||||||
printf '{"context":"Active ticket: %s"}\n' "$TICKET"
|
# satisfies the gate — sessions never fight
|
||||||
exit 0
|
# over one file)
|
||||||
fi
|
GATE_OK=0
|
||||||
|
for f in "$CRUSH_PROJECT_DIR"/.crush/active-ticket*; do
|
||||||
|
[ -f "$f" ] && [ -s "$f" ] || continue
|
||||||
|
GATE_OK=1
|
||||||
|
printf '{"context":"Active ticket(s): %s -> %s"}\n' "$(basename "$f")" "$(cat "$f")"
|
||||||
|
done
|
||||||
|
[ "$GATE_OK" -eq 1 ] && exit 0
|
||||||
|
|
||||||
# No active ticket — block
|
# No active ticket — block
|
||||||
cat >&2 <<'MSG'
|
cat >&2 <<'MSG'
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ TODAY="$(date +%Y-%m-%d)"
|
|||||||
# Runs in Docker so the host stays clean (no native shellcheck required).
|
# Runs in Docker so the host stays clean (no native shellcheck required).
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
$RULE_VERBOSE && log_step "Shell scripts (shellcheck)"
|
$RULE_VERBOSE && log_step "Shell scripts (shellcheck)"
|
||||||
mapfile -d '' SH_FILES < <(find . -path ./.git -prune -o -path ./.tmp -prune -o -path ./vendor -prune -o -path ./archive -prune -o -path ./node_modules -prune -o \( -name '*.sh' -o -name '*.bash' \) -print0 2>/dev/null)
|
mapfile -d '' SH_FILES < <(find . -path ./.git -prune -o -path ./.tmp -prune -o -path ./.crush -prune -o -path ./vendor -prune -o -path ./archive -prune -o -path ./node_modules -prune -o \( -name '*.sh' -o -name '*.bash' \) -print0 2>/dev/null)
|
||||||
if [ "${#SH_FILES[@]}" -gt 0 ]; then
|
if [ "${#SH_FILES[@]}" -gt 0 ]; then
|
||||||
if have shellcheck; then
|
if have shellcheck; then
|
||||||
if shellcheck "${SH_FILES[@]}" >/dev/null 2>&1; then
|
if shellcheck "${SH_FILES[@]}" >/dev/null 2>&1; then
|
||||||
|
|||||||
+2
-1
@@ -36,7 +36,8 @@ mapfile -t FILES < <(
|
|||||||
*) echo "$f_abs" ;;
|
*) echo "$f_abs" ;;
|
||||||
esac
|
esac
|
||||||
done < <(find "$ROOT" -type f -name '*.sh' \
|
done < <(find "$ROOT" -type f -name '*.sh' \
|
||||||
-not -path '*/.git/*' -not -path "$ROOT/vendor/*")
|
-not -path '*/.git/*' -not -path "$ROOT/vendor/*" \
|
||||||
|
-not -path "$ROOT/.crush/*")
|
||||||
fi
|
fi
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user