fix: feat: PostToolUse hook detects phase file writes in real-time (eliminates polling latency) (#278)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-19 17:55:06 +00:00
parent ab3efa2402
commit ac04dc29a6
6 changed files with 132 additions and 6 deletions

View file

@ -681,7 +681,7 @@ fi
# =============================================================================
status "creating tmux session: ${SESSION_NAME}"
if ! create_agent_session "${SESSION_NAME}" "${WORKTREE}"; then
if ! create_agent_session "${SESSION_NAME}" "${WORKTREE}" "${PHASE_FILE}"; then
log "ERROR: failed to create agent session"
cleanup_labels
cleanup_worktree

View file

@ -167,6 +167,63 @@ else
fail "needs_human mtime guard: expected 1 notify call, got $NOTIFY_COUNT"
fi
# ── Test 9: PostToolUse hook writes marker on phase file reference ────────
HOOK_SCRIPT="$(dirname "$0")/../lib/hooks/on-phase-change.sh"
MARKER_FILE="/tmp/phase-changed-test-session.marker"
rm -f "$MARKER_FILE"
if [ -x "$HOOK_SCRIPT" ]; then
# Simulate hook input that references the phase file
echo "{\"tool_input\":{\"command\":\"echo PHASE:awaiting_ci > ${PHASE_FILE}\"}}" \
| bash "$HOOK_SCRIPT" "$PHASE_FILE" "$MARKER_FILE"
if [ -f "$MARKER_FILE" ]; then
ok "PostToolUse hook writes marker when phase file referenced"
else
fail "PostToolUse hook did not write marker"
fi
rm -f "$MARKER_FILE"
# Simulate hook input that does NOT reference the phase file
echo "{\"tool_input\":{\"command\":\"echo hello > /tmp/other-file\"}}" \
| bash "$HOOK_SCRIPT" "$PHASE_FILE" "$MARKER_FILE"
if [ ! -f "$MARKER_FILE" ]; then
ok "PostToolUse hook skips marker for unrelated operations"
else
fail "PostToolUse hook wrote marker for unrelated operation (false positive)"
fi
rm -f "$MARKER_FILE"
else
fail "PostToolUse hook script not found or not executable: $HOOK_SCRIPT"
fi
# ── Test 10: phase-changed marker resets mtime guard ─────────────────────
# Simulates monitor_phase_loop behavior: when marker exists, last_mtime
# is reset to 0 so the phase is processed even if mtime hasn't changed.
echo "PHASE:awaiting_ci" > "$PHASE_FILE"
LAST_MTIME=$(stat -c %Y "$PHASE_FILE" 2>/dev/null || echo 0)
PHASE_MTIME="$LAST_MTIME"
# Without marker, mtime guard blocks processing (same mtime)
if [ "$PHASE_MTIME" -le "$LAST_MTIME" ]; then
ok "mtime guard blocks when no marker present (baseline)"
else
fail "mtime guard should block when phase_mtime <= last_mtime"
fi
# Now simulate marker present — reset last_mtime to 0
MARKER_FILE="/tmp/phase-changed-test-session.marker"
date +%s > "$MARKER_FILE"
if [ -f "$MARKER_FILE" ]; then
rm -f "$MARKER_FILE"
LAST_MTIME=0
fi
if [ "$PHASE_MTIME" -gt "$LAST_MTIME" ]; then
ok "phase-changed marker resets mtime guard (phase now processable)"
else
fail "phase-changed marker did not reset mtime guard"
fi
# ── Cleanup ───────────────────────────────────────────────────────────────────
rm -f "$PHASE_FILE"