To verify that an interface became easier, I need to say what state should remain after a specific action. I separated saving, loading and repeated entry into independent functions that can be exercised in one synthetic package.
The module operates neither a real ERP nor a browser screen. Responses and entry actions are values; the functions return a new state. Recorded examples are local evidence, not substitutes for network, keyboard or accessibility verification.
Separate acknowledgement from current input
Save state tracks draft text, local revision, acknowledged revision, sequence and pending requests. begin_save captures the current draft; save_reply checks request identity and success without changing that draft. A first response arriving after the second success cannot roll back the acknowledged sequence.
Unsent edits remain dirty and failure cannot advance confirmation. Regressing server versions are detected, but storage ordering is not repaired. There is no durable pending queue, authentication, retry service or server conflict resolver. I distinguish these responsibilities so a local state calculation is not mistaken for a complete persistence workflow.
Interpret empty results only after success
Initial and loading are distinct from ready, empty_initial and empty_filtered. Error and denied are not variations of a successful empty search. A failed refresh retains earlier rows as stale; denial removes them. An older generation cannot replace the latest query state. This decides response application without cancelling actual server requests.
Rows retained after changed conditions may belong to the previous query. An adopting interface must preserve and label that basis or decide to hide them. The package handles status names and synthetic identifiers, not localized copy or permission APIs. Retention must never be displayed as evidence that the newest filter succeeded.
Make the intended focus target explicit
order_action accepts input, search, escape, choose, enter and add. Selecting an item targets quantity, invalid input stays at its current target and cancelling search retains the typed word. A new row is added only after a valid current row, preserving earlier values. Explicit row and field targets make movement and retention testable.
The target is not actual DOM focus. Native Tab order, composition-related Enter, search-result arrow navigation, error announcements and focus restoration require a real UI implementation and tests. Quantity and row limits belong to this bounded synthetic model, not to a company’s established requirements. The reusable contract includes decisions that adopters still need to make.
Explore the implementation
Complete independent synthetic implementation
"""Pure synthetic interaction contracts. No network, storage or browser access."""
from copy import deepcopy
def save_state():
return {'draft':'','revision':0,'acked_revision':0,'next':1,'applied_sequence':0,'server_version':0,'pending':{}}
def edit(state,text):
if not isinstance(text,str) or len(text)>500: raise ValueError('text')
out=deepcopy(state);out['draft']=text;out['revision']+=1;return out
def begin_save(state):
out=deepcopy(state);token=out['next'];out['next']+=1
request={'revision':out['revision'],'text':out['draft']}
out['pending'][token]=request
return out,{'sequence':token,**request}
def save_reply(state,sequence,ok,server_version=None):
if type(sequence) is not int or sequence not in state['pending']:raise ValueError('unknown request')
if type(ok) is not bool:raise ValueError('result')
if ok and (type(server_version) is not int or server_version<1):raise ValueError('server version')
out=deepcopy(state);request=out['pending'].pop(sequence)
# Acknowledgement is separate from draft text. A late response cannot replace input.
if ok and sequence>out['applied_sequence']:
if server_version<out['server_version']:raise ValueError('server version regression')
out['applied_sequence']=sequence;out['server_version']=server_version
out['acked_revision']=max(out['acked_revision'],request['revision'])
return out
def save_status(state):
return {'dirty':state['revision']>state['acked_revision'],'pending':len(state['pending']),'server_version':state['server_version']}
def load_state():
return {'generation':0,'status':'initial','rows':[],'filtered':False,'retry':False}
def begin_load(state,filtered=False):
if type(filtered) is not bool:raise ValueError('filtered')
out=deepcopy(state);out.update(generation=out['generation']+1,status='loading',filtered=filtered,retry=False);return out
def load_reply(state,generation,result,rows=None):
if type(generation) is not int or generation<1 or generation>state['generation']:raise ValueError('generation')
if generation!=state['generation']:return deepcopy(state)
if result not in ('ok','denied','error'):raise ValueError('result')
out=deepcopy(state)
if result=='denied':out.update(status='denied',rows=[],retry=False)
elif result=='error':out.update(status='stale' if out['rows'] else 'error',retry=True)
else:
if not isinstance(rows,list) or len(rows)>100 or any(not isinstance(x,str) or len(x)>40 for x in rows):raise ValueError('rows')
out.update(rows=list(rows),status='ready' if rows else ('empty_filtered' if out['filtered'] else 'empty_initial'),retry=False)
return out
def order_state():
return {'rows':[{'item':'','quantity':''}],'row':0,'field':'item','popup':False,'message':''}
def valid(field,value):
if field=='item':return isinstance(value,str) and 1<=len(value.strip())<=40
return isinstance(value,str) and value.isascii() and value.isdecimal() and 1<=int(value)<=99
def order_action(state,action,value=None):
out=deepcopy(state);row=out['rows'][out['row']];field=out['field'];out['message']=''
if action=='input':
if not isinstance(value,str) or len(value)>100:raise ValueError('input')
row[field]=value
elif action=='search':
if field!='item':raise ValueError('search target')
out['popup']=True
elif action=='escape':out['popup']=False
elif action=='choose':
if not out['popup'] or not valid('item',value):raise ValueError('choice')
row['item']=value;out.update(popup=False,field='quantity')
elif action=='enter':
if out['popup']:out['message']='choose_or_close'
elif not valid(field,row[field]):out['message']='invalid_'+field
elif field=='item':out['field']='quantity'
elif out['row']+1<len(out['rows']):out.update(row=out['row']+1,field='item')
else:out['message']='row_complete'
elif action=='add':
if len(out['rows'])>=20 or any(not valid(k,row[k]) for k in ('item','quantity')):out['message']='complete_current_row'
else:out['rows'].append({'item':'','quantity':''});out.update(row=len(out['rows'])-1,field='item',popup=False)
else:raise ValueError('action')
return outInspect three records and seventeen tests
Seventeen local tests cover reversed save responses, unsent changes, failure, repeated replies, empty results, query errors, old generations, cancelled search and invalid quantity. They also passed after extracting the ZIP into a fresh directory. The selector shows late-save, failed-refresh and next-row evidence as readable state rather than a generic success badge.
Functions copy prior state so callers can compare before and after. They are not a storage service validating every possible hostile mutation of the state object. This is a pure model inside a trusted program. State provenance, actual authorization and server consistency still need their own boundaries; passing model tests should not be expanded into an operational guarantee.
Integration connects the model to actual behavior
Map real user actions to function inputs first. An old response handler that still assigns draft text can violate the model even when these tests pass. Connect status copy, actual inputs and server versions deliberately, then exercise delayed responses and real keyboard behavior in the rendered application.
The package includes implementation, tests, runner, fixed records and guidance. It is independent synthetic code with permission to modify and reuse. I share the responsibilities and unverified integration work alongside the functions rather than present a finished interface to copy blindly. That makes the module useful for defining a company’s entry workflow more precisely before implementation.