[BUG] security-guidance: two TestStop tests read review_model.json instead of the session state file

Status Open
Reported on v2.1.289
Maintainer reply None cached
Activity 0 comments · opened Oct 5, 2026
[!NOTE] On behalf of Marvel Tiyjudy, written by Claude Code (Claude Opus 5.5, claude-opus-5-5, reasoning level high).

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In the security-guidance plugin, two tests in plugins/security-guidance/tests/test_repo_resolution.py fail since 2.0.9 (anthropics/claude-plugins-official@d182ca4, "follow the newest Opus instead of a pinned release"):

  • TestStop::test_subagent_stop_reviews_without_consuming_session_state
  • TestStop::test_subagent_stop_findings_do_not_advance_baseline_or_fire_count

The cause is the test helper _read_state (line 236). It opens the first *.json file it finds in SECURITY_WARNINGS_STATE_DIR:

files = [e.path for e in os.scandir(d) if e.name.endswith(".json")]

Since 2.0.9, default_review_model() in hooks/llm.py writes its cache, review_model.json, to the same directory. After a review has run, the directory holds two JSON files. When scandir returns review_model.json first, the test reads the model cache instead of security_warnings_state_<key>.json. NTFS lists entries in name order, so on Windows that happens every time. On other file systems it depends on directory order.

As far as I can tell this is a test-only bug. The production code reads state files by exact name (session_state.get_state_file), and cleanup_old_state_files only touches security_warnings_state_* files.

What Should Happen?

_read_state should read the session state file and ignore other JSON in the state directory. Both tests should pass, as they did on 2.0.8 (6bfd4e0).

Error Messages/Logs

>       assert after["touched_paths"] == before["touched_paths"] != []
E       KeyError: 'touched_paths'
tests\test_repo_resolution.py:460: KeyError

>       assert after.get("baseline_sha") == before.get("baseline_sha")
E       AssertionError: assert None == '8a08a57a6eb1a52452195950afdce8ad0f38461b'
E        +  where <built-in method get of dict object at 0x...> = {'endpoint': 'd2f60d2c7b5509aa', 'model': None, 'at': 1791208946.3105175}.get
tests\test_repo_resolution.py:507: AssertionError
2 failed in 8.27s

The dict in the second failure is the review_model.json cache (endpoint, model, at), not session state.

Steps to Reproduce

  1. git clone https://github.com/anthropics/claude-plugins-official && cd claude-plugins-official && git checkout d182ca4 (current main)
  2. cd plugins/security-guidance
  3. On Windows, run:

``
python -m pytest -q "tests/test_repo_resolution.py::TestStop::test_subagent_stop_reviews_without_consuming_session_state" "tests/test_repo_resolution.py::TestStop::test_subagent_stop_findings_do_not_advance_baseline_or_fire_count"
``

  1. Both fail as above. At 6bfd4e0 (2.0.8) the same command passes both.

Narrowing the helper to the session state files makes both pass at d182ca4 (verified):

 def _read_state(env):
     d = env["SECURITY_WARNINGS_STATE_DIR"]
-    files = [e.path for e in os.scandir(d) if e.name.endswith(".json")]
+    files = [e.path for e in os.scandir(d)
+             if e.name.startswith("security_warnings_state_") and e.name.endswith(".json")]
     assert files, os.listdir(d)

_read_state_or_empty (line 244) has the same .json filter for its emptiness check, so it may want the same narrowing.

Claude Model

Not applicable (test suite, no model call).

Is this a regression?

Yes, this worked in a previous version.

Last Working Version

security-guidance 2.0.8 (anthropics/claude-plugins-official@6bfd4e0)

Claude Code Version

2.1.289 (Claude Code)

Platform

Anthropic API

Operating System

Windows 11 Enterprise (10.0.26200)

Terminal/Shell

Other (Git Bash), Python 3.14.7, pytest 9.1.1

Additional Information

The plugin README asks for the relevant section of ~/.claude/security/log.txt. That doesn't apply here: the failure is in the test suite, not in a live hook run.

Twelve other tests in the suite fail on Windows at both 2.0.8 and 2.0.9 (repo resolution). They are unrelated and not part of this report.

View original on GitHub ↗