fix(session-start): time-based dedup as defense in depth (v6.1.5)#49
Merged
Merged
Conversation
The per-session_id:source_kind dedup added in v6.1.2 only catches the
startup-class re-fire case. Codex was observed firing SessionStart 9+
times in rapid succession near session limits (after a user "resume and
wrap up" instruction) with rotating session_ids and source values that
our existing dedup didn't anticipate. The user's transcript showed 9
identical short-pointer payloads stacked back-to-back.
Added a 5-second time-window dedup that runs before the session-id
dedup. If any SessionStart payload was emitted in the last 5 seconds,
the new fire short-circuits silently regardless of payload shape --
session_id, source, agent_id, or absence of all three.
Window is intentionally short (5s) so legitimate user-driven compacts
spaced minutes apart still emit their resumption context. The observed
bug was 9 fires inside ~1 second; even 1-second window would catch it.
Implementation:
- Add LAST_EMIT_FILE in STATE_DIR.
- Before any state-mutating work, check file mtime; if within window,
exit 0.
- Touch LAST_EMIT_FILE after emit_additional_context.
- mtime check uses both BSD (stat -f %m) and GNU (stat -c %Y) forms.
Tests:
- Added test_session_start_time_dedup_suppresses_rapid_refires that
fires 4 SessionStart events in rapid succession with rotating
session_ids and a mix of startup/compact sources, asserts exactly
one emits.
- Fixed test_session_start_json and
test_wrapper_suppresses_unavailable_c_utf8_locale_noise to use
isolated tmpdir cwds so the per-cwd state dir doesn't poison
subsequent SessionStart tests via the new time dedup.
Version bump 6.1.4 -> 6.1.5 across all four manifests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
User reported seeing 9 identical SessionStart hook payloads stacked back-to-back in Codex after telling the model to "resume and wrap up" near the session limit. The per-
session_id:source_kinddedup added in v6.1.2 only catches startup-class re-fires when the host populatessession_idconsistently — Codex's resume/wrap-up lifecycle apparently fires SessionStart multiple times with rotating IDs and source values our existing dedup doesn't anticipate.hooks/session-start: added a 5-second time-window dedup that runs first. If any SessionStart payload was emitted in the last 5 seconds, the new fire short-circuits regardless of payload shape (session_id, source, agent_id, or absence of all three). Defense in depth on top of the existing per-key dedup.session-start-last-emit); both BSDstat -f %mand GNUstat -c %Ypaths covered.Test plan
test_session_start_time_dedup_suppresses_rapid_refires: fires 4 SessionStart events with rotating session_ids + mixed startup/compact sources within 1s, asserts exactly one emits.test_session_start_jsonandtest_wrapper_suppresses_unavailable_c_utf8_locale_noisenow use isolated tmpdir cwds so the per-cwd state dir doesn't poison subsequent SessionStart tests via the new time dedup.tests/hook-contracts.sh— 51 assertions PASStests/version-check.sh— manifests agree on 6.1.5sleep 6→ emits normally.Why not just dedup harder on session_id
Tried that. Codex appears to rotate
session_idbetween SessionStart fires in some lifecycle events, so per-session dedup misses the rapid-fire case. Time dedup is cheap, correct for the observed symptom, and doesn't depend on the host's choice of payload field names.🤖 Generated with Claude Code