fix: P1 test state-file PID mismatch + P2 session key eviction

P1 (cubic-dev-ai): Test process PID differs from spawned hook PID,
so test was seeding/clearing wrong state file. Fix: pass fixed
CLAUDE_SESSION_ID='gateguard-test-session' to spawned hooks.

P2 (cubic-dev-ai): Pruning checked array could evict __bash_session__
and other session keys, causing gates to re-fire mid-session. Fix:
preserve __prefixed keys during pruning, only evict file-path entries.

9/9 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
seto
2026-04-13 15:40:13 +09:00
parent 6ed1c643e7
commit 67256194a0
2 changed files with 10 additions and 5 deletions

View File

@@ -59,9 +59,12 @@ function loadState() {
function saveState(state) {
try {
state.last_active = Date.now();
// Prune checked list if it exceeds the cap
// Prune checked list if it exceeds the cap.
// Preserve session keys (__prefixed) so gates like __bash_session__ don't re-fire.
if (state.checked.length > MAX_CHECKED_ENTRIES) {
state.checked = state.checked.slice(-MAX_CHECKED_ENTRIES);
const sessionKeys = state.checked.filter(k => k.startsWith('__'));
const fileKeys = state.checked.filter(k => !k.startsWith('__'));
state.checked = [...sessionKeys, ...fileKeys.slice(-(MAX_CHECKED_ENTRIES - sessionKeys.length))];
}
fs.mkdirSync(STATE_DIR, { recursive: true });
// Atomic write: temp file + rename prevents partial reads

View File

@@ -9,9 +9,9 @@ const { spawnSync } = require('child_process');
const runner = path.join(__dirname, '..', '..', 'scripts', 'hooks', 'run-with-flags.js');
const stateDir = process.env.GATEGUARD_STATE_DIR || path.join(process.env.HOME || process.env.USERPROFILE || '/tmp', '.gateguard-test-' + process.pid);
// State files are per-session. In tests, falls back to pid-based name.
const sessionId = process.env.CLAUDE_SESSION_ID || process.env.ECC_SESSION_ID || `pid-${process.ppid || process.pid}`;
const stateFile = path.join(stateDir, `state-${sessionId.replace(/[^a-zA-Z0-9_-]/g, '_')}.json`);
// Use a fixed session ID so test process and spawned hook process share the same state file
const TEST_SESSION_ID = 'gateguard-test-session';
const stateFile = path.join(stateDir, `state-${TEST_SESSION_ID}.json`);
function test(name, fn) {
try {
@@ -60,6 +60,7 @@ function runHook(input, env = {}) {
...process.env,
ECC_HOOK_PROFILE: 'standard',
GATEGUARD_STATE_DIR: stateDir,
CLAUDE_SESSION_ID: TEST_SESSION_ID,
...env
},
timeout: 15000,
@@ -87,6 +88,7 @@ function runBashHook(input, env = {}) {
...process.env,
ECC_HOOK_PROFILE: 'standard',
GATEGUARD_STATE_DIR: stateDir,
CLAUDE_SESSION_ID: TEST_SESSION_ID,
...env
},
timeout: 15000,