FAILURE MAP
← Case archive

FA-42830 / Listener lifecycle / Member archive

Disposed context binding continues to invoke its observer callback · case 05

Disposed context binding continues to invoke its observer callback; the returned lifecycle plan violates the stated transition contract.

Member previewVariant 5 · 3 implementations · 8 checks per implementation

Case contract

Plan an observer callback context frame. The bound receiver is independent from event sender. Explicit None receiver differs from no receiver binding. Context-local values shadow inherited values, including None. An exit restores the saved outer context even after callback failure. Nested callbacks append a trace segment without rewriting the existing path. A disposed binding rejects invocation but still permits frame restoration. A forwarded sender route collapses adjacent duplicate hops only; a later revisit after another sender is meaningful and preserved. Restore only names modified by the callback. Restore their saved values or delete them if previously absent; retain all unmodified current names. Only an exit matching the active top trace span may pop the stack; out-of-order exits are ignored until their span becomes current. A reset token is single-use and context-bound: report used before wrong-context, otherwise permit reset. Restore the nearest remaining receiver frame, scanning nested frames from inner to outer while skipping completed invocation identities. Inputs are finite state snapshots, including transitional and stale states; output fields are the exact independent actions/metadata defined by the corrected expression table. Numeric fixture capacities and counts vary with N. Field definitions, each independently computed from the input snapshot: receiver = next((receiver for ident,receiver in reversed(s['receiver_frames']) if ident not in s['finished_frames']),None); bound_none = s['bound']; locals = dict(s['inherited'],**s['local']); null_shadow = s['local']['x'] if 'x' in s['local'] else s['inherited'].get('x'); restore = s['saved'] if s['exited'] else s['current']; trace = s['spans'][:-1] if s['spans'] and s['spans'][-1]==s['exiting_span'] else list(s['spans']); sender = [x for i,x in enumerate(s['sender_route']) if i==0 or x!=s['sender_route'][i-1]]; invoke = not s['disposed'] and not s['exited']; restore_disposed = 'used' if s['reset_token'] in s['used_tokens'] else ('wrong-context' if s['token_context']!=s['active_context'] else 'reset'); unwind_trace = {k:(s['saved_values'][k] if k in s['modified_names'] else s['current_values'][k]) for k in dict.fromkeys(list(s['current_values'])+list(s['saved_values'])) if (k in s['modified_names'] and k in s['saved_values']) or (k not in s['modified_names'] and k in s['current_values'])}

Why this case matters

Observer implementations need to preserve this state while subscriptions enter, execute, and leave their lifecycle.

One recorded failure

Sample boundary fixture

This sample comes from the broken implementation of a controlled reproducer.

Boundary fixtureActualExpectedOutcome
transition 2{"bound_none": true, "invoke": true, "locals": {"x": "outer", "y": "shared"}, "null_shadow": "outer", "receiver": null, "restore": null, "restore_disposed": "reset", "sender": ["a", "b"], "trace": ["root", "a", "b"], "unwind_trace": {"a": 0}}{"bound_none": true, "invoke": false, "locals": {"x": "outer", "y": "shared"}, "null_shadow": "outer", "receiver": null, "restore": null, "restore_disposed": "reset", "sender": ["a", "b"], "trace": ["root", "a", "b"], "unwind_trace": {"a": 0}}Failed

MEMBER ARCHIVE

The complete case is available to members.

This record includes three runnable implementations, regression fixtures, execution results, and source hashes.

Member access is invitation-based. Sign in with your invited account to inspect the sources.

Sign in to the archive ↗