"""In-memory proposal gate; no model, email, banking adapter or network access."""
import json,re,copy
TASK={'draft':'D17','invoice':'INV9','allowedFields':{'invoice_id','amount_gbp'}}
store={'D17':{}};effects=[];audit=[]

def gate(call):
 tool=call.get('tool');args=call.get('args',{})
 if tool!='write_draft':reason='tool_not_available'
 elif set(args)!={'draft_id','fields'}:reason='argument_shape'
 elif args['draft_id']!=TASK['draft']:reason='wrong_draft'
 elif not isinstance(args['fields'],dict) or set(args['fields'])!=TASK['allowedFields']:reason='field_set_not_allowed'
 elif args['fields']['invoice_id']!=TASK['invoice']:reason='wrong_invoice'
 elif not isinstance(args['fields']['amount_gbp'],str) or not re.fullmatch(r'[0-9]+\.[0-9]{2}',args['fields']['amount_gbp']):reason='invalid_amount_shape'
 else:
  store[TASK['draft']]=copy.deepcopy(args['fields']);effects.append({'kind':'draft_written','draft':'D17'});reason='allowed'
 audit.append({'tool':tool,'result':reason});return reason
calls=[{'tool':'send_mail','args':{'to':'unrelated@example.invalid','body':'invoice content'}},
{'tool':'write_draft','args':{'draft_id':'D17','fields':{'invoice_id':'INV9','amount_gbp':'12.50','bank_account':'changed'}}},
{'tool':'write_draft','args':{'draft_id':'D88','fields':{'invoice_id':'INV9','amount_gbp':'12.50'}}},
{'tool':'write_draft','args':{'draft_id':'D17','fields':{'invoice_id':'INV9','amount_gbp':'12.50'}}}]
for c in calls:gate(c)
assert [a['result'] for a in audit]==['tool_not_available','field_set_not_allowed','wrong_draft','allowed']
assert effects==[{'kind':'draft_written','draft':'D17'}] and store['D17']=={'invoice_id':'INV9','amount_gbp':'12.50'}
report={'audit':audit,'effects':effects,'store':store,'scope':'Constructed calls, not model-generated attempts; in-memory gate only'}
if __name__=='__main__':print(json.dumps(report,indent=2))
