Exercise 2: A Second Example Reproducing the Call-Graph Blind Spot — Possible Solution ==================================================================== THE NEW FUNCTION, DESIGNED TO REPRODUCE THE SAME GAP ------------------------------ def cancel_order_and_notify_user(order, user): order['status'] = 'cancelled' # direct Order data mutation, NO function call validate_user_email(user) # calls a User-group function return f"Order cancelled, notifying {user['id']}" This is deliberately the MIRROR IMAGE of this chapter's own update_user_loyalty_points: that function called an Order-group function but mutated User data directly; this one calls a User-group function but mutates Order data (order['status']) directly. Same gap, opposite direction. VERIFYING THE CALLS-ONLY METHOD MISCLASSIFIES IT ------------------------------ calls made: {'validate_user_email'} calls-only: touches_order = False | touches_user = True calls-only classification: pure User The calls-only method sees only the call to validate_user_email() and concludes this function belongs entirely to the User group - missing the direct order['status'] mutation completely, exactly like it missed update_user_loyalty_points' direct user['points'] mutation. VERIFYING THE COMBINED METHOD CORRECTS IT ------------------------------ combined: touches_order = True | touches_user = True combined classification: BOUNDARY Adding the direct-data-access check (searching for order[ / order.get() in the source) correctly reclassifies this function as a genuine boundary function, matching this chapter's own combined method exactly. WHY THIS CONFIRMS THE GAP IS A GENERAL PROPERTY OF THE METHOD, NOT A ONE-OFF ------------------------------ This chapter's own example (update_user_loyalty_points) showed the calls-only method failing in one specific direction (Order call + User data mutation). This exercise shows the identical failure mode in the OPPOSITE direction (User call + Order data mutation) - proving the blind spot is a structural property of measuring calls alone, applicable to any function that mutates a domain's data without going through that domain's own functions, regardless of which domain is "the one it calls into" versus "the one it touches directly." WHY THIS WORKS AS AN ANSWER ------------------------------ The new function is deliberately constructed as a mirror image of this chapter's own known gap, and both the calls-only misclassification and the combined method's correction are verified directly, confirming the gap generalizes rather than being specific to one particular function.