ACQUIRE · INTERLEAVE · LOOK UP · VERIFY
Dependency injection: where does this handle point?
Actual shared references, mutable tenant context and returned records.
Action ledger
Exact object evidence
Follow a reference, not a scope label
Requests A and B acquire JavaScript client objects from the selected provider. Each acquired handle points to a specific object identity, shown as client-one or client-two in the diagram and exact evidence. The fictional data store contains one alpha report with value twelve and one beta report with value eighty-seven. A read performs a lookup through the actual acquired client. A cross-tenant return is recorded only when the returned record owner differs from the requesting tenant. It is not a flag chosen in advance from a scenario name. Saved snapshots are independent copies, so changing an object later does not rewrite the earlier history.
Compare four provider lifetimes
Provider selects shared mutable, per-tenant, per-request or stateless shared. Shared mutable returns one object and overwrites its owner whenever a request acquires it. Per-tenant retains one object for each tenant and returns that same tenant object on later acquisitions. Per-request constructs a new object per acquisition, even if both callers have the same tenant; releasing that handle removes the request-scoped object in this fixture. Stateless shared has one object but stores no tenant owner: its read receives the authenticated request tenant as an explicit argument. This makes clear that sharing an object is not itself a vulnerability. Sharing mutable request-specific context is the demonstrated problem.
Control ordering and the lookup boundary
Schedule chooses overlapping acquisition or serial requests. The overlap preset acquires A, acquires B, reads A, reads B, then releases both handles. The serial preset finishes A before B acquires anything. Second request tenant sets B to beta or alpha. A always belongs to alpha. Enforce owner guard checks the effective lookup tenant against the authenticated request tenant before reading the fictional store. A mismatched owner is blocked, not silently repaired. Changing a configuration control rebuilds and pauses the trace. Restore defaults selects the unsafe mutable overlap example with distinct tenants and the guard disabled.
Make the failure visible before the read
Restore defaults and choose Next action twice. Both request arrows point to client-one, and its current owner is beta. The earlier acquisition snapshot still shows alpha. Advance once more: A receives the beta report through the same client identity it acquired earlier. Enable the owner guard and inspect the corresponding action; A is blocked while B still reads its own record. Switch to per-tenant or per-request and compare object counts and arrows. Switch to stateless shared: there is still only one object, but A receives alpha and B receives beta. Serial mutable access does not expose this interleaving failure; that absence is not a general concurrency safety proof.
Construct your own interleaving
Manual action buttons acquire, read or release A and B at the currently inspected snapshot. They discard later actions and append the chosen action, creating a new replayable branch. Restart rewinds that current branch; Load selected schedule discards the custom sequence and restores the selected preset. Try Acquire A, Acquire B, Acquire A, Read B from a restarted trace under shared mutable: the last acquisition rebinds the shared object to alpha, so now B receives alpha data. Reading without a handle is blocked. Releasing an unacquired handle is a recorded no-op. The custom trace is bounded to sixty actions; load a preset to clear a full trace.
Inspect time without confusing it with concurrency
Play maps the selected action sequence onto sixty inspection seconds. Pause holds the current snapshot; Next action advances one; Restart rewinds; Show final result completes the inspection. Speed controls half, normal or double pace, and Inspect time scrubs snapshots. These are discrete ordered operations, not real asynchronous tasks or a thread scheduler. Read results and the action ledger provide a textual alternative to arrows; Exact object evidence exposes all request-to-client identities, owners and results. Both records are fictional. No Java injection container, database connection, credentials, network request or production authorization service is created. The guard assumes trustworthy tenant identity; the experiment does not authenticate a user, model nested scopes, dispose real resources or prove arbitrary provider code race-free.