Synthetic fixtures; deterministic local model. No production system is contacted.
Experiment inputs
Complete-run results
Decision trace
What this experiment demonstrates
The help desk at Belgavi's Book Store may grant at most two goodwill credits per customer per month. Geetha already has one. Agent runs for her cases (email, chat, a retry of a timed-out run, a sub-agent) each read her credit count, think, and call the write tool if the count is below the limit. Run starts are spread by a fixed gap, every run thinks for the same time, and each write transaction takes a fixed number of milliseconds.
The model is deterministic and works in whole milliseconds. Nothing is stored in a database and no model is called; the guards follow the documented behaviour of PostgreSQL row locks, conditional updates and SERIALIZABLE transactions in simplified form.
Run controlled comparisons
Start with the defaults: two runs read one credit at 0 ms and 600 ms, both grant, and Geetha ends with 3 credits, over the limit by 1.
Now change one thing at a time. Choose the version check: still 3 credits, and every version check passed, because inserts have no earlier version. Choose the quota row: 2 credits, and the second run gets limit_reached. Choose the conditional insert: also 2 at the default gap, but set the gap between run starts to 0 and both statements see one credit, so the result is 3 again. With SERIALIZABLE and a gap of 0, the second transaction fails, retries once and then returns limit_reached; set retries to 0 and the agent receives a serialization error instead. Set six runs and no existing credits with no guard: 6 credits, over by 4. With the quota row and four runs starting together, the longest lock wait is 60 ms. Finally turn on the back-office insert with the quota row or the advisory lock: 3 credits, because one write path skips the guard.
Inspect the trace and keep evidence
The table lists each run: when it read, what it saw, when it wrote and what the write tool returned. Start trace, Next step, Show all and the slider control how much of the decision trace is visible; the metrics always describe the whole run. Reset restores the defaults and Export JSON downloads the inputs, metrics and trace.
Limits and production translation
Real databases add lock queues, deadlock detection, predicate locks with false positives and network time; agent runs think for different lengths of time and may read more than once. The model has one table, one customer and fixed timings. What carries over: per-row version checks do not protect a limit over several rows, a single conditional statement narrows the window without closing it, a quota row or SERIALIZABLE with retry closes it, and every write path must use the same guard.