Skip to content

Foundation: audit actual backend calls separately from replay fixture keys - #298

Merged
CompleteDotTech merged 1 commit into
mainfrom
foundation/request-accounting-audit
Oct 15, 2026
Merged

CompleteDotTech merged 1 commit into
mainfrom
foundation/request-accounting-audit

Conversation

@CompleteDotTech

Copy link
Copy Markdown
Contributor

Refs #80

What this is

Adds an optional AuditedBackend wrapper (src/jev_cookbook/accounting.py) that counts the decide() calls a notebook actually makes separately from the distinct replay-fixture keys those calls resolve to, and rewrites the recipe guard's README-budget check to use it. Nothing existing changes unless a notebook opts in: no backend, recipe, fixture or notebook is modified.

Why: a recipe that decides the same state more than once (repeats, retries, comparison arms) makes more calls than the replay file has entries. The old guard compared a README's stated call count to len(responses.json), which is the distinct-key count, so it could pass while the notebook made a different number of real calls (recipe 36's earlier measurement: 290 calls against 72 distinct states).

  • decide_calls: how many times decide() was invoked (a call that raises counts, with returned: false).
  • distinct_requests: how many unique replay keys those calls had; this is the number a fixture file can match.
  • The ledger carries only the replay key and a returned flag: no responses, contents, credentials or timing. A request that fails replay-key validation never reaches the backend and is not counted.
  • This counts logical decide() calls, not HTTP attempts; it is separate from the live backend's requests_made.
  • tests/test_recipe_guard.py: the README-budget check now requires, for an audited notebook, "makes exactly N calls" to equal decide_calls and the distinct keys to equal the keys in responses.json exactly; an unaudited recipe keeps the old len(responses.json) check, and a malformed audit fails rather than falling back to it.
  • Docs: docs/backends.md, docs/live.md.

Verification

The change is one commit (originally 9b9f73b, cherry-picked onto main f00f56b with one docstring-only conflict resolved: the commit's rewritten bullet and main's noul_proposition_gold_coverage bullet both kept). 8 files, +782/-50, no recipe folder touched.

  • Bare pytest 4919 passed / 46 skipped / 50 deselected / 31 xfailed on 3.10 (no SDK) and on 3.14; tests/test_live_sdk.py 62 passed with the SDK mocked; ruff, hygiene (569 files), render_catalog --check, notebook_ci.py fixtures, fixtures validate --all clean.
  • All 50 published notebooks and the template re-executed offline on 3.14: every one byte-identical to its committed file (no recipe uses AuditedBackend yet, so no printed count moves).

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Opus merge-gate review — PR #298 (Jev-Engineering/cookbook)


Verdict summary

No MUST CHANGE findings. The guard rewrite is not loosened anywhere — I proved this three ways
(identical outcomes over all 50 published recipes + template, an argument over the two code paths,
and 28 adversarial notebook variants). The new AuditedBackend counting is correct, leaks nothing,
uses exactly the same replay_key call shape as ReplayBackend.decide, and the public API change is
purely additive. Docs statements all check out against the code. Six SUGGESTIONS below, none
blocking.


1. The guard rewrite — is anything loosened? (the central question)

1a. Line-by-line reading of the two code paths

tests/test_recipe_guard.py:1235-1268 (new) vs origin/main tests/test_recipe_guard.py:1242-1275.

old (origin/main) new (head)
fixtures present, not audited stored = len(json.loads(responses.json)); assert stated == stored keys = set(json.loads(...)); actual = len(keys); assert stated == actual
fixtures absent, not audited pytest.skip(...) pytest.skip(...) (reworded only)
fixtures present, audited n/a actual = len(ledger), plus set(ledger) == set(fixture keys) exactly
fixtures absent, audited pytest.skip(...) checked against the ledger (new coverage)
audit attempted but malformed n/a hard failure, never falls back (tests/request_audit.py:32-37)

len(dict) → len(set(dict)) is the only arithmetic change on the legacy path, and it is a
no-op here.
ReplayBackend.from_json (src/jev_cookbook/backends.py:280-295) requires a JSON
object and rejects duplicate keys; I verified mechanically that all 50 published
fixtures/responses.json files (plus _template) are JSON objects, so len(d) == len(set(d))
identically. (If one were ever a JSON list of strings, set() would dedupe and this would be a
silent loosening — see SUGGESTION S4.)

Can a recipe now satisfy the guard in a way it could not before? Two ways, both intended and
both strictly evidence-backed:

  1. A replay recipe whose README states N > len(responses.json) used to fail; it can now pass
    by committing a valid audit ledger of N calls. But the ledger must cover exactly the fixture
    inventory (request_audit.py:225-229), so N >= len(responses.json) always holds. The stated
    number can never drop below the old proxy.
    This is the Foundation: no check that a recipe's live request count matches its README, or that no example is decided twice #213 fix.
  2. An audited scripted recipe (no responses.json) is now checked rather than skipped — new
    coverage where there was none.

1b. 50-recipe before/after comparison (the regression proof)

pytest tests/test_recipe_guard.py -rA run on head and on a clean git archive origin/main export,
same interpreter and venv:

head: 747 passed, 43 skipped, 50 deselected, 31 xfailed
main: 747 passed, 43 skipped, 50 deselected, 31 xfailed

Full -rA outcome lists (821 lines each) diff to nothing after normalising (a) the line-number
shift of 7 and (b) the one deliberately reworded skip reason. Per-test for the budget check
specifically: 49 PASSED + 1 SKIPPED on both (recipe 36 is the scripted one with no
responses.json; identical skip on both sides).

tests/recipe_guard_known_failures.py is untouched (git diff --stat origin/main...HEAD -- tests/recipe_guard_known_failures.py is empty) and has no entry for
readme_budget_note_matches_stored_answers on either side.

Also confirmed: the new code calls notebook_cells() and ast.parse() on every code cell of all 50
recipes (the old code never parsed a notebook on this check). I verified all 786 non-blank code
cells across the 50 notebooks + template parse cleanly, so this adds no new error surface. The
deselected -m catalog_audit set is also unchanged (7 failed / 43 passed on both head and main —
pre-existing inbound-link drift, unrelated to this PR and not part of the gating pytest run).

1c. Adversarial scratch variants (28 built and executed against the real guard)

Driven through tests/request_audit.audited_call_count and, end-to-end, through
guard.test_the_live_budget_note_matches_the_stored_answer_count with RECIPES_DIR repointed at a
scratch recipe folder (fixtures + notebook + README written to disk). Script:
/workspace/worktrees/_scratch/review-298a/adversarial.py.

# adversarial variant result
00 honest audited run (3 calls, 2 distinct) returns 3 ✅
01a JEV_REQUEST_AUDIT + nothing rejected "invalid JSON"
01b prefix without trailing space rejected "require exactly the shared audit line"
01c {} rejected "invalid audit fields"
01d null / [] payload rejected "invalid audit fields"
01e truncated JSON rejected "invalid JSON"
01f schema: 2 rejected "unsupported schema"
01g non-canonical spacing (json.dumps default) rejected "not the shared canonical audit"
02 audit line moved to an earlier cell's stdout rejected
03 audit printed in a non-final cell, trailing cell after rejected "final code cell must be…"
04 extra code cell appended after the audit cell rejected
05 audit line in output, no wrapper anywhere in source rejected "import the shared AuditedBackend directly" (does not fall back to the proxy)
06 ledger omits a fixture key rejected "missing=1"
07 ledger has a key not in fixtures rejected "extra=1"
09 decisions routed through a locally built fake backend rejected (inventory: missing=2)
13 duplicate execution_count rejected "not executed in order"
14 out-of-order execution_count rejected
15 execution_count: 0 rejected "committed execution evidence is missing"
R1 audited, README states the true 3 passes ✅
R2 audited, README states 2 (= fixture count, understating) rejected "README states 2, backend audit … is 3"
R3 unaudited, README states 2 (= fixture count) passes (legacy path preserved) ✅
R4 unaudited, README states 3 rejected
R5 malformed audit, README states the fixture count rejected (no fallback) ✅

Three variants were accepted, each for a reason that is explicitly in scope-of-the-check:

  • 08 — stale audit (source edited to 4 calls, committed output still says 3): accepted by the
    unit guard, exactly as its docstring says ("Freshness is enforced by the separate offline notebook
    job"). I verified that delegation is real: tools/check_notebook_fresh.py on a notebook pair
    differing only in the audit line exits 1 with
    STALE: notebook.cells[0].outputs[0].text: committed 'JEV_REQUEST_AUDIT {"decide_calls":3}', fresh '…4…'.
    The per-recipe Notebook (<slug>) job runs it (.github/workflows/notebooks.yml:431). Claim accurate.
  • 10/11 — audited scripted recipe whose real decisions go through a backend helpers.py
    constructs
    : the ledger can be empty/unrelated and is accepted (returns 0 / 1). See S1.
  • 12 — builtins.print overridden, forged 10-call ledger: accepted. See S2. Note the forgery can
    only inflate for a replay recipe (the inventory equality pins the floor), never understate.

1d. Mutation testing (22 mutations, applied in-worktree and reverted; tree verified clean after)

Script /workspace/worktrees/_scratch/review-298a/mutate.sh, run against
tests/test_accounting.py tests/test_request_audit.py + the 50 parametrised budget-check cases
(baseline 103 passed, 1 skipped).

All 8 src/jev_cookbook/accounting.py mutations go RED: dedupe the ledger; never set
returned=True; drop assets from the replay key; don't record the call; swap
distinct_requests for the total; return a live alias instead of a snapshot; drop the assets
kwarg on delegation; stop forwarding attribute writes.

10 of 12 tests/request_audit.py mutations go RED, including: malformed-audit falls back
instead of failing; skip the fixture-inventory comparison; drop the canonical-output comparison;
drop the final-cell shape check; drop execution-count evidence; drop in-order execution; allow extra
backend construction; allow protected rebinding; allow an unshared import.

Both tests/test_recipe_guard.py mutations go RED (assertion always true; audited count ignored).

Two survivors, both analysed in detail (probe.py) — neither is a "test that cannot fail":

  • return len(keys) → return payload["decide_calls"] is an equivalent mutant: line 210 already
    asserts they are equal.
  • Relaxing the replay-key regex (request_audit.py:205-207) survives, because the one test aimed at
    it (replay_key="invalid") is actually caught by the inventory assertion instead. See S3.

2. src/jev_cookbook/accounting.py

Correct

  • Replay-key consistency. accounting.py:68 is replay_key(state, questions, assets=assets) —
    character-for-character the same call as ReplayBackend.decide (backends.py:303), the same
    function, and it therefore picks up canonical_image_format and the image-asset envelope on
    main for free. Verified live: with a real PNG asset the ledger key equals
    replay_key(state, QUESTIONS, assets=assets) and differs from the text-only key
    (tests/test_accounting.py:82-108), and mutation M3 (dropping assets=) goes red.
  • Calls that raise are counted. The entry is appended before delegation
    (accounting.py:69-70) and flipped to returned=True only after (:76), so a raising delegate
    leaves returned: false and still counts. Verified against a real ReplayMiss and a real
    BudgetExceeded (test_accounting.py:56-66, :111-146).
  • Replay-key validation failures are not counted — replay_key raises before the append, so a
    request the wrapper itself rejects never reaches the delegate and never enters the ledger. Matches
    the docstring (accounting.py:22-24) and docs/backends.md.
  • Retries. decide_calls is logical calls; LiveBackend.requests_made is HTTP attempts
    (live.py:606-608 "Attempts sent so far, including retries"). The two are deliberately different
    and docs/live.md says so (see §4).
  • No leakage. The ledger holds only a 64-hex key and a bool. I probed with a state containing
    {"secret": "sk-abc123", "text": "hello"}: neither string (nor any response content, timestamp or
    latency) appears in audit()/audit_json(). audit() returns a fresh snapshot each call —
    mutating the returned list does not affect the next call (test_accounting.py:51-52, mutation M6 red).
  • Attribute forwarding. __setattr__ writes through to the delegate (backend.max_retries = 0
    reaches live.max_retries), and mode/model/recorded_dates are read-through properties, so
    the dynamic live-mode model swap is visible (test_accounting.py:127-138). No property-setter
    shadowing bug: backend.mode = x is forwarded, not silently dropped.
  • Additive API only. __init__.py gains one import and one __all__ entry inserted in correct
    alphabetical position (Answer, AuditedBackend, Backend, …). __all__ goes 34 → 35 names;
    nothing renamed or removed. (__all__ is not globally sorted on main either — pre-existing.)
    No import cycle: full suite green on 3.10, 3.14 and with the live extra.

Observations (non-blocking, detailed as SUGGESTIONS below)

  • Thread safety: index = len(self._calls) then append (accounting.py:69-70) is not atomic,
    unlike LiveBackend, which guards every counter with self._lock. I hammered it (8 threads ×
    200 decide() calls, 5 trials, on both 3.10 and 3.14): no corruption observed under the GIL,
    and no recipe in the repo uses threads or concurrent.futures (grepped all helpers.py and
    all notebooks). So this is latent, not live. → S5.
  • Memo-wrapper ordering genuinely changes the number. → S6.
  • __getattr__ recurses if _backend is unset. → S7.

3. tests/request_audit.py — the evidence reader

Thorough and well-scoped; the module docstring honestly bounds itself ("This is not a general proof
about arbitrary Python or helper implementation behavior", :5). Strong points confirmed by test:

  • attempted (:42-50) triggers on an AuditedBackend name/alias, an audit_json attribute, or
    an audit line in any code cell's stdout — so you cannot write the line and dodge verification
    (variant 05).
  • Exactly one top-level AuditedBackend(get_backend(...)) binding, shared imports only, absolute
    imports only, imports before construction, no wildcard import, no extra backend construction, and
    ~15 distinct rebinding/shadowing routes blocked (Store/Del names, def/class/arg, except as,
    match patterns, attribute writes on protected objects, setattr/delattr).
  • Final cell must be exactly print(backend.audit_json()) by ast.dump equality, exactly one
    audit call, exactly one audit line, and it must be the final cell's entire stdout.
  • The payload is not trusted: both counts are recomputed from the ordered calls list and the
    whole line is re-derived through the shared canonical_json and compared byte-for-byte.

4. Docs accuracy

I checked every sentence of the two doc additions against the code. All true.

docs/backends.md:33-79:

  • "one response per distinct request key" ✓ (ReplayBackend.__init__).
  • "the audit never deduplicates them or changes their state, questions, assets, answers, or replay
    keys" ✓ (identity pass-through asserted in test_accounting.py:105).
  • AuditedBackend(get_backend(fixtures="fixtures/responses.json")) ✓ — get_backend really takes
    fixtures= (backends.py:406-414).
  • "Settings such as max_retries, mode, returned model, recorded dates, and the live recorder's
    attributes remain attached to the wrapped backend" ✓.
  • "A request rejected by the wrapper's replay-key validation never reaches the delegate and is not
    counted" ✓.
  • "backend.audit() returns a fresh dictionary snapshot" ✓.
  • "Extra unused fixtures and missing fixtures both fail this audited route" ✓ (variants 06, 07).
  • "An attempted but malformed audit fails instead of falling back to that proxy" ✓ (variant R5).
  • "The separate fresh-kernel notebook check verifies that committed output is current" ✓ —
    independently verified against tools/check_notebook_fresh.py.
  • "not a measurement of live usage, latency, cost, or model quality" — no Jev quality/latency/cost
    claim anywhere in either doc.
    CONTRIBUTING.md §2 satisfied.
  • Cross-link live.md#budget-guard resolves (docs/live.md:94 ## Budget guard) ✓.

docs/live.md:124-131:

  • "It is separate from requests_made" ✓ (live.py:606).
  • "one logical call can retry several HTTP attempts" ✓ (live.py:711-746: _spend() is called once
    per attempt inside the retry loop).
  • "or spend zero attempts when the budget is already exhausted" ✓ (_spend() raises
    BudgetExceeded before the client call, so _made is not incremented — and
    test_accounting.py:136-143 exercises exactly that: ledger [True, True, False],
    requests_made == 2, decide_calls == 3).
  • Cross-link backends.md#actual-calls-and-distinct-requests resolves ✓.

CONTRIBUTING.md §4 ("budgets are enforced in code"): the PR correctly does not claim
AuditedBackend enforces anything — enforcement stays in LiveBackend._spend, and the docs point
there. What the PR does add is enforcement-in-code of the README's budget sentence, which §4's
"prove it with a test rather than a sentence" asks for. §7's "Nothing is skipped, disabled, or
loosened" holds (§1b).


5. Scope

git diff --raw -M origin/main...HEAD — exactly 8 files, every one mode 100644 → 100644 or
000000 → 100644, no renames, no mode changes
:

:100644 100644 M  docs/backends.md
:100644 100644 M  docs/live.md
:100644 100644 M  src/jev_cookbook/__init__.py
:000000 100644 A  src/jev_cookbook/accounting.py
:000000 100644 A  tests/request_audit.py
:000000 100644 A  tests/test_accounting.py
:100644 100644 M  tests/test_recipe_guard.py
:000000 100644 A  tests/test_request_audit.py

Nothing under recipes/, tools/, .github/, catalog/, and README.md untouched. +782/−50.


MUST CHANGE

None. I would merge this head.


SUGGESTIONS (non-blocking)

S1 — tests/request_audit.py:225 — an audited scripted recipe can under-report if helpers.py
builds its own backend.
The AST checks only read notebook cells (audited_call_count(cells, …));
helpers.py is never scanned, and the constructors set (:116-122) only names
AuditedBackend/get_backend/ReplayBackend/ScriptedBackend/LiveBackend, so
from helpers import build_backend; other = build_backend() is not blocked. For a replay recipe
the set(keys) == fixture_keys equality catches it (my variant 09: missing=2). For a scripted
recipe fixture_keys is None, so the inventory check is skipped and I got the guard to return 0
(variant 10) and 1 for an unrelated key (variant 11) while the real decisions happened elsewhere.
Not a regression — these recipes were skipped before — but it is the one soft spot in the new
coverage. Consider either AST-scanning helpers.py for backend construction on the audited path, or
noting the limitation in the check's docstring next to the existing honest "does not prove arbitrary
helper behavior" sentence.

S2 — tests/request_audit.py:94-160 — builtins.print = … / sys.stdout = … escape the
protected-binding sweep.
print = lambda … is blocked (Name/Store, :134-136), but an
attribute store on an unprotected module name is not: with import builtins; builtins.print = …
in an earlier cell I got the guard to accept a forged 10-call ledger (variant 12). Bounded impact —
for a replay recipe the inventory equality means a forgery can only inflate the stated count, never
push it below the old len(responses.json) floor — and the module docstring already disclaims
general-Python proofs. A one-line hardening would close it: reject ast.Attribute nodes in
Store/Del context whose .attr is in protected (this also catches
jev_cookbook.AuditedBackend = …).

S3 — tests/test_request_audit.py:83 — the replay-key format assertion is not independently
pinned.
Relaxing r"[0-9a-f]{64}" to r".*" in request_audit.py:205-207 leaves the whole suite
green (mutation G9). The replay_key="invalid" case passes via the inventory assertion, not the
format one. Proof (probe.py): with fixture_keys=None the relaxed version returns 2 for
["invalid","invalid"] and 1 for "A"*64 and for "ab", where the shipped code correctly says
"malformed replay key". Shipped behaviour is right; the test just isn't pinning it. Fix: add a
fixture_keys=None parametrisation (or match="malformed replay key") to
test_malformed_or_self_declared_summaries_fail.

S4 — tests/test_recipe_guard.py:1238 — set(json.loads(...)) assumes a JSON object. Today all
51 fixture files are objects, so len(set(d)) == len(d) and the legacy path is byte-identical to
origin/main. But if a responses.json were ever a JSON list of strings, set() would dedupe and
silently lower the required count, whereas the old len() would not. A one-line
assert isinstance(loaded, dict) (or reusing ReplayBackend.from_json's own shape error) would make
that impossible rather than merely unlikely.

S5 — src/jev_cookbook/accounting.py:69-70 — the ledger write is not atomic, and that is not
documented.
index = len(self._calls) followed by self._calls.append(...) can interleave; the
companion LiveBackend guards every counter with self._lock (live.py:656). I could not make it
corrupt under the GIL (8 threads × 200 calls × 5 trials, clean on 3.10 and on 3.14) and no recipe in
the repo uses threads, so nothing is broken today. Given the class is a new public API and the
docstring enumerates its other guarantees so carefully, either take the lock or add "not safe for
concurrent use from several threads" to the docstring / docs/backends.md.

S6 — docs/backends.md:36-37, 45 — wrapper ordering with a memoising wrapper changes the number
and is not mentioned.
Measured (probe2.py), 4 logical calls over 2 distinct states:
Memo(AuditedBackend(raw)) reports decide_calls=2; AuditedBackend(Memo(raw)) reports 4.
request_audit.py:107-115 forces AuditedBackend(get_backend(...)) directly, i.e. the audit sits
innermost, so any memo (recipe 50's MemoisingBackend, helpers.py:124) necessarily sits outside
it and its cache hits become invisible. That is arguably the right number for a live budget, but it
contradicts the doc's "Calling decide twice … makes two logical calls" / "Its count includes
repeated identical requests" read by a recipe author who has a memo. One sentence ("any wrapper
placed outside AuditedBackend hides its cache hits from the ledger; that is what makes the count a
live-budget floor") would settle it before the first adopter.

S7 — src/jev_cookbook/accounting.py:49-50 — __getattr__ recurses when _backend is unset.
copy.copy(backend), copy.deepcopy(backend) and AuditedBackend.__new__(AuditedBackend).mode all
raise RecursionError instead of a clear AttributeError. There is prior art for the same shape in
recipes/50-.../helpers.py:172-175 (with a comment acknowledging the same reasoning), so this is
consistent with the repo, but a two-line guard
(if name in self.__slots__: raise AttributeError(name)) removes a confusing failure mode on a class
that, unlike that helper, uses __slots__.

S8 (cosmetic) — tests/request_audit.py:208 — assert call["returned"] is True is strictly
redundant with the canonical-line comparison at :222, since expected_payload hardcodes
"returned": True (verified: removing it still rejects, just with the less helpful "not the shared
canonical audit" message). Keep it for the better message; no change needed, noted only so the
mutation survivor is not mistaken for a gap.


Checks run, with actual results

Three fresh uv venvs in this worktree, strictly sequential, one heavy job at a time, under
nice -n 10. No live API calls, no credential search, nothing posted to GitHub, ~/.cache/uv not
pruned, no wsl.
All recipe execution was on scratch copies under
/workspace/worktrees/_scratch/review-298a/nbrun/; recipes/ was never touched.

Env A — uv venv --python 3.10 + .[dev] (CPython 3.10.21, no SDK)

check result
ruff check . All checks passed!
ruff format --check . 393 files already formatted
pytest (bare) 4919 passed, 46 skipped, 50 deselected, 31 xfailed in 162.71s
pytest tests/test_recipe_guard.py -rA 747 passed, 43 skipped, 50 deselected, 31 xfailed
same, on origin/main export 747 passed, 43 skipped, 50 deselected, 31 xfailed — outcome lists identical
pytest -m catalog_audit tests/test_recipe_guard.py 7 failed, 43 passed — identical on origin/main (pre-existing, non-gating)
python tools/check_hygiene.py Hygiene check passed: 569 file(s) scanned
python tools/render_catalog.py --check exit 0
python tools/notebook_ci.py fixtures 51 recipe folders, 0 failed
python -m jev_cookbook.fixtures validate --all all valid, exit 0
28 adversarial notebook variants 27/28 as expected; the 3 accepted ones analysed above
22 mutations of accounting.py / request_audit.py / test_recipe_guard.py 20 RED, 2 survivors analysed (1 equivalent mutant, 1 → S3); worktree verified clean after
tools/check_notebook_fresh.py on a stale-audit pair exit 1, STALE: … outputs[0].text — freshness delegation confirmed

Env B — uv venv --python 3.14 + .[dev,ml] -c .github/constraints-notebooks.txt (CPython 3.14.7)

check result
pytest (bare) 4919 passed, 46 skipped, 50 deselected, 31 xfailed in 135.72s
execute scratch copy of 01-sentiment-classification + check_notebook_fresh.py executed exit 0; matches a fresh offline run (2 figures, identical bytes)
36-card-game-action-selection (the scripted one) executed exit 0; matches a fresh offline run (3 figures)
43-failure-recovery-selection executed exit 0; matches a fresh offline run (1 figure)
50-model-comparison-and-calibration (the memoising one) executed exit 0; matches a fresh offline run (2 figures)
thread-race probe, 8×200 calls × 5 trials 1600/1600 each, 0 returned:false entries

Env C — uv venv --python 3.10 + .[dev,live]

check result
pytest tests/test_live_sdk.py 62 passed (SDK mocked; no network)
pytest (full, with live extra) 4982 passed, 44 skipped, 50 deselected, 31 xfailed

(These reproduce the PR body's own numbers — 4919/46/50/31, 62, 569 files — exactly, so the PR's
"Verification" section is honest about what was measured, per CONTRIBUTING.md §2.)

Artefacts: /workspace/worktrees/_scratch/review-298a/{adversarial.py, mutate.sh, probe.py, probe2.py, guard_head.txt, guard_main.txt, o_head.txt, o_main.txt}.


OPUS-REVIEW: APPROVE f8878a1

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.

2 participants