Repository navigation
Fix/lru eviction and effective fingerprint - #2
Merged
Merged
Conversation
…-Redis suite Follow-up on review findings. Two were correct against what I had written, and the first turned out to be exploitable rather than merely overstated. Rate limiter eviction could reset a throttled window. A throttled client backs off -- correct client behaviour -- which makes it the least-recently-used entry, so plain LRU dropped it first and its limit reset. Measured: 2000 junk actor ids restored a blocked actor to allowed. Windows at or above the limit are now never evicted; when nothing evictable remains a new actor is refused instead (fail-closed) and counted in capacity_refusals. The docstring claimed "active actors are never displaced", which the algorithm did not guarantee; it now describes what it actually does. The policy pin compared file bytes, not behaviour. Hashing the YAML is wrong in both directions: reformatting or editing a comment rejected a behaviourally identical policy, and a code upgrade changing a default left the file untouched so the hash was unchanged while the engine decided differently -- exactly the silent divergence the pin exists to catch. effective_fingerprint hashes the loaded config with defaults resolved and is what the pin checks; policy_fingerprint is kept for provenance. Both on /health and in the new `aetherya policy fingerprint`. Real-Redis integration suite with a service container in CI. The in-memory double cannot demonstrate MULTI/EXEC semantics, serialization between concurrent clients, real TTL, or that separate instances share state. Validated against Redis 6.2.14: under 100 threads a deliberately racy read-then-write granted 92 permits against a limit of 10 where this implementation granted exactly 10, so the test discriminates rather than passing vacuously. Durable audit sinks. A protocol alone did not close the retention gap. HTTPAuditSink (batched, background worker, bounded queue with oldest-drop, retry) and FileAuditSink ship byte-identical lines. /health now reports audit_mirror_ok, errors, dropped, pending and last success, backed by a process-level registry since AuditLogger is built per request -- shipping is best-effort by design, so alerting on those counters is the only thing separating durable audit from a local file that looks durable. Also fixes a deadlock I introduced in the sink: stats() held the stats lock and called healthy(), which took the non-reentrant lock again, while pending() took the queue lock in the opposite order to write(). And close() flushed one batch, losing the tail on shutdown; it now drains. Verification: 1054 unit tests, 100% coverage, mypy strict and ruff clean. 13 integration tests green against real Redis. pipeline p95 0.23ms, chaos p95 1.29ms detection 1.0, security_baseline 0 differences. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The phases were a sketch; this makes them executable, and adds the thing that was actually missing to picture the system working. docs/rollout-phases.md states each phase's posture, the wiring change it needs, what to measure, and exit criteria that can fail — including what to do when shadow mode reports a false-positive rate you cannot accept, and the fact that any policy change restarts the measurement window because the fingerprint moved. examples/agent_loop.py runs an actual loop. The existing example showed the API call but not what an agent is: here the model proposes a tool call, the engine rules on it, and the result or the refusal goes back into the conversation. The --phase flag switches enforcement posture without touching policy or wiring, which is the property that makes shadow-then-tighten safe. The scenario is a prompt injection arriving through tool output, not a malicious model — the agent reads a poisoned document and proposes what it was told to. That is the case the boundary exists for and the model cannot solve alone. Two backends: a deterministic scripted agent (no key, no network, reproducible) and a real OpenAI tool-calling loop. The gating code around them is identical. The OpenAI path is unverified here — no API key was available — and is marked as such in the source. Verification: 1065 tests, 100% coverage, mypy strict and ruff clean. All three phases exercised by tests, including that enforcement tightens monotonically and that refusals are fed back rather than raised. Co-Authored-By: Claude Opus 5 <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.
No description provided.