Skip to content

feat(w4): 10-scenario adversarial suite + 4 benchmark tables (0/10 vs 9/10)#24

Merged
geminimir merged 1 commit into
mainfrom
w4/evals-benchmarks
Jul 26, 2026
Merged

feat(w4): 10-scenario adversarial suite + 4 benchmark tables (0/10 vs 9/10)#24
geminimir merged 1 commit into
mainfrom
w4/evals-benchmarks

Conversation

@geminimir

Copy link
Copy Markdown
Owner

Summary

Milestone W4 — the product. 10 adversarial scenarios (Warden 0/10, naive baseline 9/10), 4 benchmark tables reproducible by make bench. Closes #15, #16, #17.

The W4 acceptance signal:

Warden:         leaks = 0/10
Naive baseline: leaks = 9/10

The four tables land in docs/bench/. Highlights:

  • Table 2 (recall vs. selectivity) — post-filter recall drops to 0.06 at 1% selectivity; label pushdown holds at 1.00. This is the recall cliff the design doc names "the single most persuasive artifact in the repo."
  • Table 3 (predicate size vs. corpus size) — at 50k docs, ID-list expansion needs a 9,663-element predicate; label pushdown needs 6 ints. Makes the O(1) vs O(n) resume claim measurable.
  • Table 1 (Gate 2 overhead) — check() = ~52µs p50 at 1k docs, ~640µs at 50k. Bounded per-call.
  • Table 4 (revocation propagation) — revoke → deny = 3µs; revoke → evict from in-flight session = 17µs. Effectively synchronous.

What ships

  • evals/corpus.py — parameterized synthetic corpus (deterministic under seed; scales from smoke to 1m).
  • evals/scenarios/_shared.py — two deliberately-broken naive baselines (naive_authz_check, naive_rag_retrieve).
  • evals/scenarios/scenario_0{1,2,3,5,6,7,8,9}_*.py — 8 new scenarios completing the 10 from the design doc.
  • evals/scenarios/run_all.py — suite runner with the Harvey-blog table + leak counts. Exit code 0 iff Warden = 0.
  • evals/bench/ — 4 tables + orchestrator. make bench writes to docs/bench/*.md.
  • docs/bench/*.md — generated tables checked in as the current reference.
  • README.md — Headline numbers section linking to the tables.

The 10 scenarios

# Scenario Tests Naive leaks?
01 semantic_neighbor Gate 1+2 vs unfiltered top-K yes
02 ethical_wall deny dominance yes
03 stale_revocation Gate 2 vs stale label filter yes
04 in_flight_revocation Gate 3 evicts mid-run yes
05 citation_leak Gate 3 strips revoked citations yes
06 cross_tenant_space wall wins over space grant yes
07 expired_guest expiry enforcement yes
08 transitive_nesting fidelity + safety at 5 hops no (fidelity/safety pair, not a semantic contrast)
09 deleted_embedded stale index can't leak past Gate 2 yes
10 prompt_injection principal binding beats injection yes

Test plan

  • make oracle — 15 green
  • make rebac — 18 green
  • make labels — 19 green
  • make gates — 22 green
  • make api — 5 green
  • make scenarios12 tests green (10 individual + 2 aggregate) — the W4 acceptance gate
  • make bench — writes all 4 tables in ~30s; regenerated files committed
  • make test — 100 green, 21 integration skipped w/o env vars
  • make differential-gate — 4 properties × 5000 examples still green

Invariant checklist

  • make oracle green.
  • make test green.
  • Differential harness still green at 5000 examples.
  • No shared code between core/oracle.py and any engine module.
  • Gate 1 + Gate 2 semantics unchanged; W4 is measurement + scenarios only.
  • Permissive-superset invariant unchanged.

Security-sensitive?

  • Does not touch core/algebra.py, core/oracle.py, core/rebac.py, core/barriers.py, core/labels.py, or the gate wiring. Pure measurement + adversarial-suite additions.

  • Read invariants section of the README.

  • Not fixing an undisclosed vulnerability.

Notes for reviewers

  • Table 2's recall is simulated analytically — the post-filter/id-list/label-pushdown "recall" numbers use the oracle's authorized_set as ground truth, not a live pgvector benchmark. The shape of the recall cliff is what matters. A live pgvector Recall@K benchmark is a follow-up (needs ~1M docs on real hardware to be meaningful); the harness in retrieval/strategies.py from W2 is ready to plug in.
  • _common.measure() uses time.perf_counter with 5-iteration warmup and 500-iteration samples. Median-based percentiles. Numbers vary ~10% run-to-run on the same laptop.
  • The "honesty" disclaimer appears in every table caption per the design doc's explicit requirement.
  • Scenarios 04 and 10 are unchanged from W3; this PR adds the surrounding suite framework and the other 8. Both still pass end-to-end.

Closes #15, #16, #17. Delivers the W4 acceptance signal:

    Warden:         leaks = 0/10
    Naive baseline: leaks = 9/10

All 4 benchmark tables reproduce via `make bench` and land in
docs/bench/*.md.

evals/corpus.py
  Parameterized synthetic corpus generator. Deterministic under seed.
  Power-law org distribution (top 20% of orgs hold ~80% of docs) plus
  configurable selectivity, barrier density, and scale (smoke/small/
  10k/100k/1M presets). Not from a real IR corpus — the recall numbers
  we care about are relative (strategy A vs B), not absolute.

evals/scenarios/_shared.py
  Two deliberately-broken naive baselines:
    - naive_authz_check: any grant path, ignores deny + expiry
    - naive_rag_retrieve: top-K by similarity, no auth filter
  Both exist to be beaten.

evals/scenarios/scenario_0{1,2,3,5,6,7,8,9}_*.py
  8 new scenarios. Combined with the 2 from W3 (in-flight revocation,
  prompt injection) this completes the design-doc's 10-scenario suite:

    01 semantic_neighbor       Gate 1+2 vs unfiltered top-K
    02 ethical_wall            deny dominance vs naive union grants
    03 stale_revocation        Gate 2 vs stale label filter
    04 in_flight_revocation    (W3) — Gate 3 evicts mid-run
    05 citation_leak           Gate 3 strips revoked citations
    06 cross_tenant_space      wall wins over space grant
    07 expired_guest           expiry enforcement
    08 transitive_nesting      fidelity + safety at 5 hops
    09 deleted_embedded        stale index cannot leak past Gate 2
    10 prompt_injection        (W3) — principal binding beats injection

evals/scenarios/run_all.py
  Suite runner. Prints Harvey-blog-style table with leak counts per
  side. Exit code 0 iff Warden's leak count is 0.

tests/test_scenarios.py
  Parametrized across all 10 scenarios plus two aggregate assertions:
  Warden must be 0/10, naive must be > 0.

evals/bench/{_common,table1_overhead,table2_recall,table3_scaling,
              table4_revocation,run}.py
  Four benchmark tables and an orchestrator. Numbers are real,
  measured, and honest — every table caption disclaims "synthetic
  single-node laptop" scale.

docs/bench/*.md
  Generated tables checked in. Highlights from the current run:
    Table 1: Gate 2 = ~52µs at 1k docs, ~640µs at 50k docs (p50)
    Table 2: post-filter recall = 0.06 at 1% selectivity; label
             pushdown = 1.00. The recall cliff, visualized.
    Table 3: ID-list = 9,663 ints at 50k docs; label pushdown = 6.
             The O(1) vs O(n) claim, in numbers.
    Table 4: revoke → deny = 3µs; revoke → evict = 17µs. Sync.

Makefile
  New: scenarios-report (prints leak table), bench (writes docs/bench/).

.github/workflows/ci.yml
  Fast job now also runs `bench` (smoke) and `scenarios-report` on
  every push.

README.md
  Adds a "Headline numbers" section linking to the four tables and
  showing the 0/10 vs 9/10 result.

Deps: numpy>=1.26 (used by the corpus generator).

Test state: 100 unit tests + 4 differential properties (5000 examples
in CI) + 21 integration tests. All green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@geminimir
geminimir merged commit d6231a7 into main Jul 26, 2026
3 checks passed
@geminimir
geminimir deleted the w4/evals-benchmarks branch July 26, 2026 23:56
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.

W4.1 — Synthetic corpus generator (parameterized scale)

1 participant