Individual passing functions can still combine into a broken workflow. An earlier approval may survive an edit, or a failed save may be presented as current confirmed data.
I assembled exact copies of four published synthetic modules and ran seven local cross-workflow checks. They use temporary SQLite and fixed values, not a company database or an operational ERP.
Choose facts that must survive failure
The targets are relationships rather than button counts. An edited document must not execute under an earlier approval. Replaying a request must not change history or quantity twice. An injected transaction failure must leave the read model, history and queued follow-up rows at their earlier state. These scenarios start from facts a person needs to trust.
They are not a claim to cover every ERP feature. Screens, real APIs, operational authorization and external delivery are absent. Actors are trusted synthetic contexts and notification checks inspect only local outbox rows. Passing bounded scenarios and proving a whole service complete are different conclusions, so the exercised boundaries are recorded explicitly.
Check a write through another read path
The duplicate-request test does not stop at execute returning success. It reads the temporary database again, projects that document into a query snapshot and compares exported rows and quantity summary. It also checks that only one history event exists. Another module’s business result supplies evidence beyond repeating the writer’s own status.
A scope test confirms that another company cannot read or change the document and that the original quantity remains unchanged. An empty unauthorized read does not mean a legitimate business total is zero. The real service must distinguish denied access from confirmed absence. The scenario names preserve that meaning instead of allowing a convenient empty array to imply a financial fact.
Change interaction and data-version boundaries too
The converted proposal’s schema must be blocked by an older release contract. Applying a reviewed proposal after its base revision changes must fail. Feeding that failed save into the interaction model must retain the person’s newer draft and dirty flag. A fixed diagnostic code records the conflict without including the draft text.
Another check supplies an earlier cursor to a new snapshot with changed contents. Continuation fails while old and new summaries retain their own quantities. These are reproducible synthetic value changes, not a load test creating real concurrent users or delayed network traffic. The evidence is useful precisely because its scope is stated rather than generalized beyond the checks.
Publish the exact test, not only its count
Place the test below beside atomic_workflow.py, snapshot_query.py, interaction_flow.py and versioned_contracts.py from the four published packages, then run it with Python. It uses the standard library and creates a temporary synthetic database that is cleaned up afterward. It accepts no operational database path.
The local report records SHA-256 values for each source and the test file. All seven checks passed in this run. The code below is that complete test rather than a decorative success counter. Reproducible source evidence is retained without claiming browser verification or actual company workflow completion.
Explore the implementation
Complete synthetic tests connecting four published modules
"""Place the four published core Python files beside this test. Synthetic only."""
import unittest,tempfile
from pathlib import Path
from atomic_workflow import WorkflowLab,Actor,command
from snapshot_query import make_snapshot,summary,page,export_rows
from interaction_flow import save_state,edit,begin_save,save_reply,save_status
from versioned_contracts import migration_preview,compatibility,proposal,review,apply_reviewed,error_record
class CrossWorkflowTests(unittest.TestCase):
def setUp(self):
self.temp=tempfile.TemporaryDirectory();self.lab=WorkflowLab(Path(self.temp.name)/'synthetic.sqlite')
self.staff=Actor('A','USER_A','staff');self.reviewer=Actor('A','REVIEWER','coordinator')
def tearDown(self):self.temp.cleanup()
def snapshot(self):
rows=self.lab.read(self.staff)
return make_snapshot([{'company_id':r['company_id'],'id':r['id'],'tick':0,'quantity':r['quantity'],'status':'draft'} for r in rows],{'company_id':'A'},rows[0]['version'])
def test_approval_does_not_survive_a_content_edit(self):
self.assertTrue(self.lab.execute(self.staff,'submit',command('submit',0))['ok'])
self.assertTrue(self.lab.execute(self.reviewer,'approve',command('approve',1))['ok'])
self.assertTrue(self.lab.execute(self.staff,'change',command('edit',2,quantity=7))['ok'])
result=self.lab.execute(self.reviewer,'execute',command('execute',3))
self.assertEqual(result['reason'],'not_approved');self.assertEqual(summary(self.snapshot())['quantity'],7)
def test_duplicate_request_changes_export_once(self):
cmd=command('edit',0,quantity=8);self.lab.execute(self.staff,'same',cmd);self.assertTrue(self.lab.execute(self.staff,'same',cmd)['replayed'])
result=self.snapshot();self.assertEqual(summary(result)['quantity'],8);self.assertEqual(len(export_rows(result)),1);self.assertEqual(len(self.lab.history(self.staff)),1)
def test_failed_transaction_leaves_read_and_side_effects_unchanged(self):
before=self.snapshot()
with self.assertRaises(RuntimeError):self.lab.execute(self.staff,'fail',command('edit',0,quantity=9),fail_before_commit=True)
self.assertEqual(self.snapshot().fingerprint,before.fingerprint);self.assertEqual(self.lab.history(self.staff),[]);self.assertEqual(self.lab.outbox(self.reviewer),[])
def test_scope_denial_is_not_a_zero_business_total(self):
self.assertEqual(self.lab.read(Actor('B','USER_A','staff')),[])
self.assertFalse(self.lab.execute(Actor('B','USER_A','staff'),'cross',command('edit',0,quantity=9))['ok'])
self.assertEqual(summary(self.snapshot())['quantity'],6)
def test_data_preview_changes_code_compatibility(self):
original={'schema':1,'rows':[{'id':'A','qty':6}]};preview=migration_preview(original)
self.assertFalse(preview['applied']);self.assertEqual(original['schema'],1)
self.assertEqual(compatibility({'release':'old','reads':[1],'writes':1},preview['proposed']['schema'])['mode'],'blocked')
def test_review_conflict_does_not_erase_a_new_draft(self):
state={'company_id':'A','revision':1,'quantity':6,'memo':''};p=proposal(state,{'quantity':7},'agent');receipt=review(state,p,'human','reviewer')
ui=edit(save_state(),'earlier');ui,request=begin_save(ui);ui=edit(ui,'newer unsent draft')
with self.assertRaises(ValueError):apply_reviewed({**state,'revision':2},p,receipt)
ui=save_reply(ui,request['sequence'],False)
self.assertEqual(ui['draft'],'newer unsent draft');self.assertTrue(save_status(ui)['dirty'])
self.assertEqual(error_record('VERSION_CONFLICT','save','trace_1')['code'],'VERSION_CONFLICT')
def test_changed_snapshot_rejects_earlier_cursor(self):
rows=[{'company_id':'A','id':'A','tick':1,'quantity':1,'status':'draft'},{'company_id':'A','id':'B','tick':2,'quantity':2,'status':'draft'}]
first=make_snapshot(rows,{'company_id':'A'},1);cursor=page(first,1)['next_cursor'];rows[1]['quantity']=3;second=make_snapshot(rows,{'company_id':'A'},2)
with self.assertRaises(ValueError):page(second,1,cursor)
self.assertEqual(summary(first)['quantity'],3);self.assertEqual(summary(second)['quantity'],4)
if __name__=='__main__':unittest.main()Grow the checks with changing responsibilities
When a feature changes, identify the relationship its failure could break and add a scenario for that relationship. Checking approval against reading or review against draft preservation is often more useful than reproducing every implementation line in a test. These seven cases are the current evidence, not a permanent cap on future coverage.
A real service may also need API authorization, file retention, rendered interaction and reconciliation of external deliveries. Unperformed checks should remain visible. The linked modules and this test are independent synthetic examples. I share which relationships were verified and what still needs verification rather than offer an unsupported blanket assurance after a change.