broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('enabled', 'disable-toggle'): 'disable-pending', ('disable-pending', 'save-success'): 'disabled', ('disable-pending', 'save-failure'): 'disable-error', ('disabled', 'enable-toggle'): 'enable-pending', ('enable-pending', 'save-success'): 'enabled', ('enable-pending', 'save-failure'): 'enable-error', ('disable-error', 'retry-disable'): 'disable-pending', ('enable-error', 'retry-enable'): 'enable-pending', ('disable-pending', 'toggle-back-on'): 'cancel-disable-pending', ('cancel-disable-pending', 'disable-save-completes'): 'disabled'}
    data = json.loads(json.dumps({'channel': 'mentions', 'channels': {'mentions': True, 'security': True}, 'request': 2, 'desired': True})) if payload is None else {**json.loads(json.dumps({'channel': 'mentions', 'channels': {'mentions': True, 'security': True}, 'request': 2, 'desired': True})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event.startswith('save-') and arg.get('request',data['request']) != data['request']: event = 'unknown-event'
            if event in ('disable-toggle','retry-disable'): data['desired'] = False
            if event in ('enable-toggle','retry-enable','toggle-back-on'): data['desired'] = True
            state = table.get((state, event), state)
            if state == 'disabled': data['channels'][data['channel']] = False
            if state == 'enabled': data['channels'][data['channel']] = True
            if state == 'enable-pending' and previous == 'cancel-disable-pending': data['request'] += 1
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('enabled / disable-toggle', solve('enabled', [('disable-toggle', N)], N), 'disable-pending')
check('disable-pending / save-success', solve('disable-pending', [('save-success', N)], N), 'disabled')
check('disable-pending / save-failure', solve('disable-pending', [('save-failure', N)], N), 'disable-error')
check('disabled / enable-toggle', solve('disabled', [('enable-toggle', N)], N), 'enable-pending')
check('enable-pending / save-success', solve('enable-pending', [('save-success', N)], N), 'enabled')
check('enable-pending / save-failure', solve('enable-pending', [('save-failure', N)], N), 'enable-error')
check('disable-error / retry-disable', solve('disable-error', [('retry-disable', N)], N), 'disable-pending')
check('enable-error / retry-enable', solve('enable-error', [('retry-enable', N)], N), 'enable-pending')
check('disable-pending / toggle-back-on', solve('disable-pending', [('toggle-back-on', N)], N), 'cancel-disable-pending')
check('cancel-disable-pending / disable-save-completes', solve('cancel-disable-pending', [('disable-save-completes', N)], N), 'enable-pending')
check('older notification incarnation', solve('enabled', [('disable-toggle', N - 1)], N), 'enabled')
check('future notification incarnation', solve('enabled', [('disable-toggle', N + 1)], N), 'enabled')
check('unknown event is inert', solve('enabled', [('unknown-event', N)], N), 'enabled')
check('empty delivery batch', solve('enabled', [], N), 'enabled')
check('N stale deliveries before current delivery', solve('enabled', [('disable-toggle', N - 1)] * N + [('disable-toggle', N)], N), 'disable-pending')
check('trace disable-toggle then save-success', solve('enabled', [('disable-toggle', N), ('save-success', N)], N), 'disabled')
check('trace disable-toggle then save-failure', solve('enabled', [('disable-toggle', N), ('save-failure', N)], N), 'disable-error')
check('trace disable-toggle then toggle-back-on', solve('enabled', [('disable-toggle', N), ('toggle-back-on', N)], N), 'cancel-disable-pending')
check('trace save-success then enable-toggle', solve('disable-pending', [('save-success', N), ('enable-toggle', N)], N), 'enable-pending')
check('trace save-failure then retry-disable', solve('disable-pending', [('save-failure', N), ('retry-disable', N)], N), 'disable-pending')
check('trace enable-toggle then save-success', solve('disabled', [('enable-toggle', N), ('save-success', N)], N), 'enabled')
check('trace enable-toggle then save-failure', solve('disabled', [('enable-toggle', N), ('save-failure', N)], N), 'enable-error')
check('trace save-success then disable-toggle', solve('enable-pending', [('save-success', N), ('disable-toggle', N)], N), 'disable-pending')
check('trace save-failure then retry-enable', solve('enable-pending', [('save-failure', N), ('retry-enable', N)], N), 'enable-pending')
check('trace retry-disable then save-success', solve('disable-error', [('retry-disable', N), ('save-success', N)], N), 'disabled')
check('trace retry-disable then save-failure', solve('disable-error', [('retry-disable', N), ('save-failure', N)], N), 'disable-error')
check('trace retry-disable then toggle-back-on', solve('disable-error', [('retry-disable', N), ('toggle-back-on', N)], N), 'cancel-disable-pending')
check('trace retry-enable then save-success', solve('enable-error', [('retry-enable', N), ('save-success', N)], N), 'enabled')
check('trace retry-enable then save-failure', solve('enable-error', [('retry-enable', N), ('save-failure', N)], N), 'enable-error')
check('trace toggle-back-on then disable-save-completes', solve('disable-pending', [('toggle-back-on', N), ('disable-save-completes', N)], N), 'enable-pending')
check('trace disable-save-completes then save-success', solve('cancel-disable-pending', [('disable-save-completes', N), ('save-success', N)], N), 'enabled')
check('trace disable-save-completes then save-failure', solve('cancel-disable-pending', [('disable-save-completes', N), ('save-failure', N)], N), 'enable-error')
check('domain state regression 1: channels', solve('disable-pending', [('save-success', N, {'request': 1})], N, {}, 'channels'), {'phase': 'disable-pending', 'value': {'mentions': True, 'security': True}})
check('domain state regression 2: channels', solve('disable-pending', [('save-success', N, {})], N, {}, 'channels'), {'phase': 'disabled', 'value': {'mentions': False, 'security': True}})
check('domain state regression 3: channels', solve('disable-pending', [('save-failure', N, {})], N, {}, 'channels'), {'phase': 'disable-error', 'value': {'mentions': True, 'security': True}})
check('domain state regression 4: desired', solve('disabled', [('enable-toggle', N, {})], N, {'channels': {'mentions': False, 'security': True}}, 'desired'), {'phase': 'enable-pending', 'value': True})
check('domain state regression 5: desired', solve('disable-pending', [('toggle-back-on', N, {})], N, {'desired': False}, 'desired'), {'phase': 'cancel-disable-pending', 'value': True})
check('domain state regression 6: request', solve('cancel-disable-pending', [('disable-save-completes', N, {})], N, {}, 'request'), {'phase': 'enable-pending', 'value': 3})
check('domain state regression 7: channels', solve('enabled', [('disable-toggle', N, {}), ('toggle-back-on', N, {}), ('disable-save-completes', N, {}), ('save-success', N, {'request': 3})], N, {}, 'channels'), {'phase': 'enabled', 'value': {'mentions': True, 'security': True}})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('enabled', 'disable-toggle'): 'disable-pending', ('disable-pending', 'save-success'): 'disabled', ('disable-pending', 'save-failure'): 'disable-error', ('disabled', 'enable-toggle'): 'enable-pending', ('enable-pending', 'save-success'): 'enabled', ('enable-pending', 'save-failure'): 'enable-error', ('disable-error', 'retry-disable'): 'disable-pending', ('enable-error', 'retry-enable'): 'enable-pending', ('disable-pending', 'toggle-back-on'): 'cancel-disable-pending', ('cancel-disable-pending', 'disable-save-completes'): 'enabled'}
    data = json.loads(json.dumps({'channel': 'mentions', 'channels': {'mentions': True, 'security': True}, 'request': 2, 'desired': True})) if payload is None else {**json.loads(json.dumps({'channel': 'mentions', 'channels': {'mentions': True, 'security': True}, 'request': 2, 'desired': True})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event.startswith('save-') and arg.get('request',data['request']) != data['request']: event = 'unknown-event'
            if event in ('disable-toggle','retry-disable'): data['desired'] = False
            if event in ('enable-toggle','retry-enable','toggle-back-on'): data['desired'] = True
            state = table.get((state, event), state)
            if state == 'disabled': data['channels'][data['channel']] = False
            if state == 'enabled': data['channels'][data['channel']] = True
            if state == 'enable-pending' and previous == 'cancel-disable-pending': data['request'] += 1
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('enabled / disable-toggle', solve('enabled', [('disable-toggle', N)], N), 'disable-pending')
check('disable-pending / save-success', solve('disable-pending', [('save-success', N)], N), 'disabled')
check('disable-pending / save-failure', solve('disable-pending', [('save-failure', N)], N), 'disable-error')
check('disabled / enable-toggle', solve('disabled', [('enable-toggle', N)], N), 'enable-pending')
check('enable-pending / save-success', solve('enable-pending', [('save-success', N)], N), 'enabled')
check('enable-pending / save-failure', solve('enable-pending', [('save-failure', N)], N), 'enable-error')
check('disable-error / retry-disable', solve('disable-error', [('retry-disable', N)], N), 'disable-pending')
check('enable-error / retry-enable', solve('enable-error', [('retry-enable', N)], N), 'enable-pending')
check('disable-pending / toggle-back-on', solve('disable-pending', [('toggle-back-on', N)], N), 'cancel-disable-pending')
check('cancel-disable-pending / disable-save-completes', solve('cancel-disable-pending', [('disable-save-completes', N)], N), 'enable-pending')
check('older notification incarnation', solve('enabled', [('disable-toggle', N - 1)], N), 'enabled')
check('future notification incarnation', solve('enabled', [('disable-toggle', N + 1)], N), 'enabled')
check('unknown event is inert', solve('enabled', [('unknown-event', N)], N), 'enabled')
check('empty delivery batch', solve('enabled', [], N), 'enabled')
check('N stale deliveries before current delivery', solve('enabled', [('disable-toggle', N - 1)] * N + [('disable-toggle', N)], N), 'disable-pending')
check('trace disable-toggle then save-success', solve('enabled', [('disable-toggle', N), ('save-success', N)], N), 'disabled')
check('trace disable-toggle then save-failure', solve('enabled', [('disable-toggle', N), ('save-failure', N)], N), 'disable-error')
check('trace disable-toggle then toggle-back-on', solve('enabled', [('disable-toggle', N), ('toggle-back-on', N)], N), 'cancel-disable-pending')
check('trace save-success then enable-toggle', solve('disable-pending', [('save-success', N), ('enable-toggle', N)], N), 'enable-pending')
check('trace save-failure then retry-disable', solve('disable-pending', [('save-failure', N), ('retry-disable', N)], N), 'disable-pending')
check('trace enable-toggle then save-success', solve('disabled', [('enable-toggle', N), ('save-success', N)], N), 'enabled')
check('trace enable-toggle then save-failure', solve('disabled', [('enable-toggle', N), ('save-failure', N)], N), 'enable-error')
check('trace save-success then disable-toggle', solve('enable-pending', [('save-success', N), ('disable-toggle', N)], N), 'disable-pending')
check('trace save-failure then retry-enable', solve('enable-pending', [('save-failure', N), ('retry-enable', N)], N), 'enable-pending')
check('trace retry-disable then save-success', solve('disable-error', [('retry-disable', N), ('save-success', N)], N), 'disabled')
check('trace retry-disable then save-failure', solve('disable-error', [('retry-disable', N), ('save-failure', N)], N), 'disable-error')
check('trace retry-disable then toggle-back-on', solve('disable-error', [('retry-disable', N), ('toggle-back-on', N)], N), 'cancel-disable-pending')
check('trace retry-enable then save-success', solve('enable-error', [('retry-enable', N), ('save-success', N)], N), 'enabled')
check('trace retry-enable then save-failure', solve('enable-error', [('retry-enable', N), ('save-failure', N)], N), 'enable-error')
check('trace toggle-back-on then disable-save-completes', solve('disable-pending', [('toggle-back-on', N), ('disable-save-completes', N)], N), 'enable-pending')
check('trace disable-save-completes then save-success', solve('cancel-disable-pending', [('disable-save-completes', N), ('save-success', N)], N), 'enabled')
check('trace disable-save-completes then save-failure', solve('cancel-disable-pending', [('disable-save-completes', N), ('save-failure', N)], N), 'enable-error')
check('domain state regression 1: channels', solve('disable-pending', [('save-success', N, {'request': 1})], N, {}, 'channels'), {'phase': 'disable-pending', 'value': {'mentions': True, 'security': True}})
check('domain state regression 2: channels', solve('disable-pending', [('save-success', N, {})], N, {}, 'channels'), {'phase': 'disabled', 'value': {'mentions': False, 'security': True}})
check('domain state regression 3: channels', solve('disable-pending', [('save-failure', N, {})], N, {}, 'channels'), {'phase': 'disable-error', 'value': {'mentions': True, 'security': True}})
check('domain state regression 4: desired', solve('disabled', [('enable-toggle', N, {})], N, {'channels': {'mentions': False, 'security': True}}, 'desired'), {'phase': 'enable-pending', 'value': True})
check('domain state regression 5: desired', solve('disable-pending', [('toggle-back-on', N, {})], N, {'desired': False}, 'desired'), {'phase': 'cancel-disable-pending', 'value': True})
check('domain state regression 6: request', solve('cancel-disable-pending', [('disable-save-completes', N, {})], N, {}, 'request'), {'phase': 'enable-pending', 'value': 3})
check('domain state regression 7: channels', solve('enabled', [('disable-toggle', N, {}), ('toggle-back-on', N, {}), ('disable-save-completes', N, {}), ('save-success', N, {'request': 3})], N, {}, 'channels'), {'phase': 'enabled', 'value': {'mentions': True, 'security': True}})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
The disable-save-completes event leaves a notification in disabled instead of enable-pending.

Notification channel preference feedback is a bounded visual-notification workflow with mutable policy state {'channel': 'mentions', 'channels': {'mentions': True, 'security': True}, 'request': 2, 'desired': True}. Its default transition relation is {('enabled', 'disable-toggle'): 'disable-pending', ('disable-pending', 'save-success'): 'disabled', ('disable-pending', 'save-failure'): 'disable-error', ('disabled', 'enable-toggle'): 'enable-pending', ('enable-pending', 'save-success'): 'enabled', ('enable-pending', 'save-failure'): 'enable-error', ('disable-error', 'retry-disable'): 'disable-pending', ('enable-error', 'retry-enable'): 'enable-pending', ('disable-pending', 'toggle-back-on'): 'cancel-disable-pending', ('cancel-disable-pending', 'disable-save-completes'): 'enable-pending'}; domain inputs can suppress or redirect transitions and update policy fields, as specified in solve. Different-generation events are inert. Batches apply in order. Optional observe returns the selected policy field alongside the phase.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'account-b-loading', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

meaning: Fixture-count band, not measured model difficulty

deps: stdlib

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'account-a-visible', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
The switch-to-b event leaves a notification in account-b-loading instead of scrubbing-a.

Notification surface account switch is a bounded visual-notification workflow with mutable policy state {'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False}. Its default transition relation is {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}; domain inputs can suppress or redirect transitions and update policy fields, as specified in solve. Different-generation events are inert. Batches apply in order. Optional observe returns the selected policy field alongside the phase.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-a-visible', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-visible', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
The scrub-complete event leaves a notification in account-a-visible instead of account-b-loading.

Notification surface account switch is a bounded visual-notification workflow with mutable policy state {'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False}. Its default transition relation is {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}; domain inputs can suppress or redirect transitions and update policy fields, as specified in solve. Different-generation events are inert. Batches apply in order. Optional observe returns the selected policy field alongside the phase.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-a-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'switch-error', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
The b-inbox-loaded event leaves a notification in account-a-visible instead of account-b-visible.

Notification surface account switch is a bounded visual-notification workflow with mutable policy state {'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False}. Its default transition relation is {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}; domain inputs can suppress or redirect transitions and update policy fields, as specified in solve. Different-generation events are inert. Batches apply in order. Optional observe returns the selected policy field alongside the phase.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'account-a-visible', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, events, generation, payload=None, observe=None):
    table = {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'account-b-visible', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}
    data = json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})) if payload is None else {**json.loads(json.dumps({'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False})), **json.loads(json.dumps(payload))}
    state = initial
    for delivery in events:
        event, event_generation = delivery[:2]
        arg = delivery[2] if len(delivery) > 2 else {}
        if event_generation == generation:
            previous = state
            if event == 'b-inbox-loaded' and arg.get('owner',data['target']) != data['target']: event = 'unknown-event'
            if event == 'b-inbox-loaded' and not data['scrubbed'] and arg.get('require_scrub',False): event = 'unknown-event'
            if event == 'switch-back-to-a': data['target'] = 'a'
            state = table.get((state, event), state)
            if state.startswith('scrubbing'):
                data['rows'] = []
                data['scrubbed'] = False
            if event == 'scrub-complete': data['scrubbed'] = True
            if state == 'account-b-visible':
                data['owner'] = 'b'
                data['rows'] = arg.get('rows',['b-notice'])[:]
            if state == 'signed-out':
                data['owner'] = None
                data['rows'] = []
    return {'phase': state, 'value': data[observe]} if observe else state
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('account-a-visible / switch-to-b', solve('account-a-visible', [('switch-to-b', N)], N), 'scrubbing-a')
check('scrubbing-a / scrub-complete', solve('scrubbing-a', [('scrub-complete', N)], N), 'account-b-loading')
check('account-b-loading / b-inbox-loaded', solve('account-b-loading', [('b-inbox-loaded', N)], N), 'account-b-visible')
check('account-b-loading / b-inbox-failed', solve('account-b-loading', [('b-inbox-failed', N)], N), 'switch-error')
check('account-b-loading / late-a-response', solve('account-b-loading', [('late-a-response', N)], N), 'account-b-loading')
check('switch-error / retry-b-load', solve('switch-error', [('retry-b-load', N)], N), 'account-b-loading')
check('account-b-visible / sign-out', solve('account-b-visible', [('sign-out', N)], N), 'scrubbing-b')
check('scrubbing-b / scrub-complete', solve('scrubbing-b', [('scrub-complete', N)], N), 'signed-out')
check('signed-out / late-b-response', solve('signed-out', [('late-b-response', N)], N), 'signed-out')
check('scrubbing-a / switch-back-to-a', solve('scrubbing-a', [('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('older notification incarnation', solve('account-a-visible', [('switch-to-b', N - 1)], N), 'account-a-visible')
check('future notification incarnation', solve('account-a-visible', [('switch-to-b', N + 1)], N), 'account-a-visible')
check('unknown event is inert', solve('account-a-visible', [('unknown-event', N)], N), 'account-a-visible')
check('empty delivery batch', solve('account-a-visible', [], N), 'account-a-visible')
check('N stale deliveries before current delivery', solve('account-a-visible', [('switch-to-b', N - 1)] * N + [('switch-to-b', N)], N), 'scrubbing-a')
check('trace switch-to-b then scrub-complete', solve('account-a-visible', [('switch-to-b', N), ('scrub-complete', N)], N), 'account-b-loading')
check('trace switch-to-b then switch-back-to-a', solve('account-a-visible', [('switch-to-b', N), ('switch-back-to-a', N)], N), 'scrubbing-for-a')
check('trace scrub-complete then b-inbox-loaded', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace scrub-complete then b-inbox-failed', solve('scrubbing-a', [('scrub-complete', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace scrub-complete then late-a-response', solve('scrubbing-a', [('scrub-complete', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace b-inbox-loaded then sign-out', solve('account-b-loading', [('b-inbox-loaded', N), ('sign-out', N)], N), 'scrubbing-b')
check('trace b-inbox-failed then retry-b-load', solve('account-b-loading', [('b-inbox-failed', N), ('retry-b-load', N)], N), 'account-b-loading')
check('trace late-a-response then b-inbox-loaded', solve('account-b-loading', [('late-a-response', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace late-a-response then b-inbox-failed', solve('account-b-loading', [('late-a-response', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace late-a-response then late-a-response', solve('account-b-loading', [('late-a-response', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace retry-b-load then b-inbox-loaded', solve('switch-error', [('retry-b-load', N), ('b-inbox-loaded', N)], N), 'account-b-visible')
check('trace retry-b-load then b-inbox-failed', solve('switch-error', [('retry-b-load', N), ('b-inbox-failed', N)], N), 'switch-error')
check('trace retry-b-load then late-a-response', solve('switch-error', [('retry-b-load', N), ('late-a-response', N)], N), 'account-b-loading')
check('trace sign-out then scrub-complete', solve('account-b-visible', [('sign-out', N), ('scrub-complete', N)], N), 'signed-out')
check('trace scrub-complete then late-b-response', solve('scrubbing-b', [('scrub-complete', N), ('late-b-response', N)], N), 'signed-out')
check('trace late-b-response then late-b-response', solve('signed-out', [('late-b-response', N), ('late-b-response', N)], N), 'signed-out')
check('domain state regression 1: rows', solve('account-a-visible', [('switch-to-b', N, {})], N, {}, 'rows'), {'phase': 'scrubbing-a', 'value': []})
check('domain state regression 2: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'owner': 'a'})], N, {}, 'rows'), {'phase': 'account-b-loading', 'value': ['a-notice']})
check('domain state regression 3: owner', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {}, 'owner'), {'phase': 'account-b-loading', 'value': 'a'})
check('domain state regression 4: rows', solve('account-b-loading', [('b-inbox-loaded', N, {'require_scrub': True})], N, {'scrubbed': True, 'rows': []}, 'rows'), {'phase': 'account-b-visible', 'value': ['b-notice']})
check('domain state regression 5: owner', solve('scrubbing-b', [('scrub-complete', N, {})], N, {'owner': 'b'}, 'owner'), {'phase': 'signed-out', 'value': None})
check('domain state regression 6: target', solve('scrubbing-a', [('switch-back-to-a', N, {})], N, {}, 'target'), {'phase': 'scrubbing-for-a', 'value': 'a'})
check('domain state regression 7: rows', solve('account-a-visible', [('switch-to-b', N, {}), ('scrub-complete', N, {}), ('b-inbox-loaded', N, {'require_scrub': True, 'rows': ['b']}), ('sign-out', N, {}), ('scrub-complete', N, {}), ('late-b-response', N, {})], N, {}, 'rows'), {'phase': 'signed-out', 'value': []})
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
The b-inbox-failed event leaves a notification in account-a-visible instead of switch-error.

Notification surface account switch is a bounded visual-notification workflow with mutable policy state {'owner': 'a', 'rows': ['a-notice'], 'target': 'b', 'scrubbed': False}. Its default transition relation is {('account-a-visible', 'switch-to-b'): 'scrubbing-a', ('scrubbing-a', 'scrub-complete'): 'account-b-loading', ('account-b-loading', 'b-inbox-loaded'): 'account-b-visible', ('account-b-loading', 'b-inbox-failed'): 'switch-error', ('account-b-loading', 'late-a-response'): 'account-b-loading', ('switch-error', 'retry-b-load'): 'account-b-loading', ('account-b-visible', 'sign-out'): 'scrubbing-b', ('scrubbing-b', 'scrub-complete'): 'signed-out', ('signed-out', 'late-b-response'): 'signed-out', ('scrubbing-a', 'switch-back-to-a'): 'scrubbing-for-a'}; domain inputs can suppress or redirect transitions and update policy fields, as specified in solve. Different-generation events are inert. Batches apply in order. Optional observe returns the selected policy field alongside the phase.

split: open-access