perf(tools): use load_config_readonly on the approval guard path - #22
Open
spfcraze wants to merge 1 commit into
Open
perf(tools): use load_config_readonly on the approval guard path#22spfcraze wants to merge 1 commit into
spfcraze wants to merge 1 commit into
Conversation
The terminal-command guard path loaded config 2-3x per invocation via load_config(), which pays a defensive deepcopy of the entire config on every call (~356us of the ~376us warm-cache cost measured on a real config.yaml). All six swapped call sites were audited read-only — every caller takes scalar reads or iterates the returned structures; none mutate (the save path at save_permanent_allowlist keeps load_config) — so they now use load_config_readonly(), the API built for exactly this (precedent: NousResearch#74211, NousResearch#74322; the one unsafe-site lesson from NousResearch#56085's salvage is covered by the mutation audit and a cache-integrity test). Measured (real config.yaml, warm cache): load_config 376.0us -> load_config_readonly 19.9us (18.9x); full guard pass check_all_command_guards('ls -la','local') 930.7us -> 241.8us (3.85x). Tests: new test_approval_config_readonly.py drives the real functions against a temp HERMES_HOME — readonly call counts per function, a no-deepcopy pin for the full guard pass, and cache-identity/integrity checks. Existing test mocks retargeted from load_config to load_config_readonly (same injection intent). Note: 6 test_approval_mode_parity failures are pre-existing ordering flakes — identical with the change stashed on clean main.
spfcraze
force-pushed
the
fix/approval-config-readonly
branch
from
August 1, 2026 14:26
de264d3 to
fbbe935
Compare
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.
What does this PR do?
The terminal-command guard path loaded config 2-3x per invocation via load_config(), which pays a defensive deepcopy of the whole config on every call. Swap the six read-only call sites in tools/approval.py and tools/tirith_security.py to load_config_readonly() — the API built for exactly this (precedent: NousResearch#74211, NousResearch#74322). Every swapped site was mutation-audited: all callers take scalar reads or iterate; none mutate the returned dict or nested structures. The save path (save_permanent_allowlist) keeps load_config.
Related Issue
No direct issue — discovered via code review and reproduced live (see below).
Related PRs reviewed during the duplicate check (none covers this change):
Changes Made
fix/approval-config-readonly— 5 file(s) changed vs base:tests/tools/test_approval.pytests/tools/test_approval_config_readonly.pytests/tools/test_cron_approval_mode.pytools/approval.pytools/tirith_security.pytools/approval.py: 5 sites swapped (permanent-allowlist load, _get_approval_config, _get_cron_approval_mode, two tirith fail-open probes); tools/tirith_security.py: 1 site (_load_security_config). tests/tools/test_approval_config_readonly.py (new): drives the real functions against a temp HERMES_HOME — readonly call counts per function, a no-deepcopy pin for a full guard pass, cache identity + integrity after guard runs. Existing mocks in test_approval.py (4) and test_cron_approval_mode.py (12) retargeted to load_config_readonly, same injection intent. Mutation audit in the commit message covers the NousResearch#56085 unsafe-site lesson.
How to Test
Measured on a real config.yaml with warm cache (repo venv, median of 7x2000 calls): load_config 376.0us vs load_config_readonly 19.9us per call (18.9x). End-to-end guard pass check_all_command_guards('ls -la','local'): 930.7us -> 241.8us (3.85x) — paid per terminal command, including in yolo mode.
Validation completed (recorded by prp):
tests/tools/test_approval_config_readonly.py.tests/tools/: branch 5147 passed / 32 failed vs baseline 5144 passed / 32 failed — zero branch-only failures.Logs
Sabotage verification output: