| """ |
| Novel-trap eval cases: traps the model CANNOT know from training data, because the |
| rules are invented (fictional APIs / systems with arbitrary, non-derivable behavior). |
| |
| The whole point: a frontier model can reason its way out of a KNOWN trap (Kahan, |
| N+1, races) because that knowledge is in its weights. It CANNOT reason its way out |
| of an ARBITRARY rule it has never seen. If skill files have any value for frontier |
| models, it must show up HERE — where the only way to know the trap is to be told. |
| |
| Design contract: |
| - The system is fictional and the failing behavior is ARBITRARY (not derivable). |
| - The OBVIOUS approach (what any sensible engineer/model would write) is wrong |
| in this fictional system for a reason only the skill reveals. |
| - The skill's gotcha states the rule WITHOUT just handing over the final code. |
| - trap_desc / correct_desc let the blind judge classify the primary recommendation. |
| - leak_terms: the skill must not contain the literal task answer. |
| |
| These mimic the REAL value case: a proprietary codebase with undocumented behavior. |
| """ |
| from distractor_eval import DistractorCase |
|
|
| CASES = [ |
| |
| DistractorCase( |
| name="zthread_fence_before_commit", |
| session_extraction={ |
| "themes": ["zthrumdb driver", "transactions"], |
| "approaches_tried": [ |
| {"approach": "call conn.begin(); ...writes...; conn.commit() as normal", |
| "why_it_failed": "in zthrumdb, commit() silently no-ops unless conn.fence() is called first; writes were lost with no error"}, |
| ], |
| "dead_ends": [{"position": 0.4, "what_happened": |
| "the standard begin/commit pattern returned success but the rows were never persisted; zthrumdb requires a fence() barrier before commit() or commit is ignored"}], |
| "breakthroughs": [{"position": 0.85, "what_worked": |
| "call conn.fence() immediately before conn.commit(); without the fence, zthrumdb buffers and silently drops the transaction"}], |
| "gotchas": [ |
| "In the zthrumdb driver, conn.commit() silently no-ops (returns success, persists nothing) unless conn.fence() is called immediately before it. Always fence() right before commit() or the transaction is dropped with no error.", |
| ], |
| "sentiment_arc": {"start": "confident", "end": "baffled"}, |
| }, |
| task_prompt=("Using the zthrumdb Python driver, write a function that opens a connection, " |
| "inserts three rows in a transaction, and durably commits them. Show the code."), |
| trap_signature=[], correct_signature=[], |
| leak_terms=["write a function that opens a connection, inserts three rows"], |
| trap_desc="Standard begin/insert/commit without calling conn.fence() before conn.commit().", |
| correct_desc="Calls conn.fence() immediately before conn.commit() so zthrumdb actually persists the transaction.", |
| ), |
|
|
| |
| |
| DistractorCase( |
| name="qbucket_read_resets", |
| session_extraction={ |
| "themes": ["qbucket limiter", "rate limiting"], |
| "approaches_tried": [ |
| {"approach": "check limiter.remaining > 0 before each call, sleep when it hits 0", |
| "why_it_failed": "reading .remaining in qbucket RESETS the window as a side effect, so polling it in a loop made the limit never actually apply"}, |
| ], |
| "dead_ends": [{"position": 0.45, "what_happened": |
| "polling limiter.remaining to decide whether to wait accidentally kept resetting the bucket, so the rate limit was never enforced and we got banned"}], |
| "breakthroughs": [{"position": 0.85, "what_worked": |
| "read limiter.remaining exactly once per window and cache it locally; never poll it in a loop, because each read resets the qbucket window"}], |
| "gotchas": [ |
| "In qbucket, reading limiter.remaining has a SIDE EFFECT: it resets the rate-limit window. Never poll .remaining in a loop to decide whether to wait; read it at most once per window and track remaining count locally, or you disable your own rate limiting.", |
| ], |
| "sentiment_arc": {"start": "confident", "end": "alarmed"}, |
| }, |
| task_prompt=("Using the qbucket rate limiter, write a loop that makes 1000 API calls while " |
| "respecting the limiter so you never exceed the allowed rate. Show the code."), |
| trap_signature=[], correct_signature=[], |
| leak_terms=["write a loop that makes 1000 API calls"], |
| trap_desc="Polling limiter.remaining inside the loop to decide whether to wait.", |
| correct_desc="Reads limiter.remaining at most once per window and tracks the count locally, never polling it in the loop, because reading resets the qbucket window.", |
| ), |
|
|
| |
| DistractorCase( |
| name="flazon_list_tag_reversal", |
| session_extraction={ |
| "themes": ["flazon serializer", "serialization"], |
| "approaches_tried": [ |
| {"approach": "flazon.dump(my_list) then flazon.load(...) round-trip as usual", |
| "why_it_failed": "flazon deserializes untagged lists in REVERSE order; round-tripping silently reversed the data unless the list was wrapped with flazon.ordered()"}, |
| ], |
| "dead_ends": [{"position": 0.5, "what_happened": |
| "round-tripping a list through flazon silently reversed its order; flazon requires flazon.ordered(list) on dump or it restores elements last-to-first"}], |
| "breakthroughs": [{"position": 0.85, "what_worked": |
| "wrap lists in flazon.ordered() before dump; untagged lists come back reversed because flazon stores them as a LIFO stack internally"}], |
| "gotchas": [ |
| "flazon serializes untagged lists as a LIFO stack, so flazon.load reverses their order on the way back. Always wrap lists with flazon.ordered() before flazon.dump, or your data round-trips reversed with no error.", |
| ], |
| "sentiment_arc": {"start": "confident", "end": "confused"}, |
| }, |
| task_prompt=("Using the flazon serializer, write code that serializes a list of 5 user records " |
| "to bytes and deserializes them back, preserving their original order. Show the code."), |
| trap_signature=[], correct_signature=[], |
| leak_terms=["serializes a list of 5 user records"], |
| trap_desc="Plain flazon.dump(list) / flazon.load without wrapping the list in flazon.ordered().", |
| correct_desc="Wraps the list in flazon.ordered() before flazon.dump so it round-trips in the original order instead of reversed.", |
| ), |
| ] |
|
|