Skip to content

Fix/lru eviction and effective fingerprint - #3

Merged
nayfly merged 3 commits into
mainfrom
fix/lru-eviction-and-effective-fingerprint
Jul 27, 2026
Merged

nayfly merged 3 commits into
mainfrom
fix/lru-eviction-and-effective-fingerprint

Conversation

@nayfly

@nayfly nayfly commented Jul 27, 2026

Copy link
Copy Markdown
Owner

No description provided.

Robert and others added 3 commits July 27, 2026 16:00
Containerising surfaced that the HTTP API never applied the configured rate
limit. The `rate_limit` section was validated on load and build_rate_limiter had
13 integration tests behind it, but nothing in the API path ever constructed a
limiter -- the configured limit was documentation. AetheryaAPI now builds one
per process and passes it to the pipeline. Verified through the container: 65
requests from one actor against a limit of 60 gave 60 allowed and 5 refused,
window visible in Redis.

mypy then caught that run_pipeline typed rate_limiter as ActorRateLimiter, so
the Redis backend could not have been passed to it at all. Widened to the
RateLimiter protocol.

Dockerfile is two-stage and non-root, with the semantic model baked in at build
time: with require_warm_semantic_model, downloading it on first boot would
either delay readiness or leave the advisory layer silently inactive. The
healthcheck asserts ok && !degraded && policy_fingerprint_match, so a replica
serving under an unintended policy is not marked healthy.

docker-compose runs the decision service plus Redis with the audit chain on a
named volume. Verified intact across a restart (9 events, 0 invalid).

config/policy.docker.yaml carries the distributed backend. The repo default
stays memory because redis fails closed and would refuse every request without a
reachable server -- correct behaviour, wrong default. A test pins the two files
to differ in exactly that field so they cannot drift.

Also validated the Redis integration suite against Redis 7.4.8 (previously only
6.2.14): 13/13.

Verification: 1076 tests, 100% coverage, mypy strict and ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ty gate

The last two pushes failed CI on the same test. examples/agent_loop.py checked
for the openai SDK before checking for the API key, so the error raised depended
on whether openai happened to be installed -- present in a dev environment,
absent from CI's [dev] extra. test_openai_backend_requires_a_key therefore
passed locally and failed in CI. The key is checked first now, which is also the
better ergonomics: a missing key is the common case and you should not need the
SDK installed to be told about it. Both paths are covered, one by blocking the
import.

Diagnosing that surfaced a second, local-only problem: security_gate phase 2
generates its own corpus, but AuditLogger appends by design, so a file left by a
previous run was appended to and counted. The phase then failed on events it did
not create and reported invalid == total, which reads like a real integrity
regression rather than stale state. CI never hits it; a second local run always
does. Phase 2 now starts clean, with a test asserting idempotence.

Verified by running the suite with openai, redis and redislite blocked at import
to reproduce CI's dependency set: 1078 tests, 100% coverage. Also re-ran every
CI job locally -- lint, ruff format, mypy, the four named suites, security
baseline, security gate, chaos, pipeline SLO and the slow semantic tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The phases were documented but not deployable: enforcement lived in each
integration, and "review every hard_deny before advancing" was a grep nobody
would run. Three pieces close that.

enforcement.py makes the posture configuration. The engine always computes a
full decision; enforcement.phase decides how much is enforced, so advancing is a
config change and all three phases exist from day one. apply_enforcement never
mutates the decision -- the audit records what the engine ruled, not what a
partially-enforcing deployment did about it, and conflating those makes phase-1
data worthless.

client.py is the sidecar surface: agent in one process, engine in another, one
HTTP call between them. Fails closed from phase 2 on, because a boundary that
disappears when the network hiccups is not a boundary. Phase 1 is the explicit
exception: it enforces nothing, so an outage must not stop the agent.

rollout_report.py answers the exit criteria from the audit trail and exits
non-zero until ready, so it works as a promotion gate. The hard_deny_reviewed
criterion never auto-passes: the tool can count and list those events, it cannot
judge them, and auto-passing would turn the gate into the formality it exists to
prevent.

Writing its tests found that the report crashed on a malformed audit line or an
empty file, because verify_audit_file raises rather than returning. Both are
findings an operator must see before advancing -- now reported as a failed
chain_intact criterion with the detail.

Dockerfile.slim: 8.7GB -> 330MB. The size was never AETHERYA, it was PyTorch,
pulled in for the advisory semantic layer. That layer is capped below every deny
threshold so it can escalate to a human but never refuse alone, and the
deterministic core does not use it. Verified against the running container: the
slim image hard-denies exactly what the full one does, including the quote-split
evasion r''m -rf /, and reports healthy in 8s instead of ~25s.

client.py is also written to satisfy black and ruff-format simultaneously; CI
runs both and they disagreed on three lines sitting at the length boundary.

Verified end to end against the live stack: phases 1, 2 and 3 over HTTP with the
same agent and only the flag changing, and fail-closed on an unreachable service.

1140 tests, 100% coverage under CI's dependency set, mypy strict and ruff clean,
security_baseline 0 differences, pipeline p95 0.15ms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@nayfly
nayfly merged commit 767f3be into main Jul 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant