We figured it out. When creating certain instructions offline, these special instructions require an arbitrary unique resource. For device instructions (e.g. STREAMOUT, EMAIL, CTRIO...), it is a unique "instruction id" so that a device can be tied to a specific instruction whenever it becomes "busy". Similarly, edge triggered instructions are allocated an "edge bit" to maintain the previous-scan's power flow (in order to detect any leading edge conditions).
Anyway, these various IDs are assigned at DOWNLOAD time, but are maintained ON DISK. However, if you Save To Disk, THEN Write To PLC (and assign these IDs THEN), the Disk version will not have these "new" IDs. Not only that, but the "Write to Disk" toolbar button was never being re-enabled because of the tweaking of the program at download time.
Note that once an instruction's edge bit or instruction id is assigned and saved to disk, it stays with it FOREVER (until the instruction is deleted or modified). This helps for proper runtime edit behavior.
So, the problem occurs because the disk has UNASSIGNED Instruction/Edge IDs, but the PLC has ASSIGNED Instruction/Edge IDs. Even though these parameters are "invisible" to the user, they get flagged as being different, but when you do the user-level comparison, they match. Doh!
Here's how we fixed it:
1. If ANY Instruction IDs or Edge IDs are assigned at download time, we flag the PROGRAM as being MODIFIED, so that the Write to Disk button will be enabled (if it wasn't already)
2. If BEFORE you started your download, the CURRENT PROJECT MATCHED what was on disk (i.e. the Write to Disk button was DISABLED), but then AFTER DOWNLOADING, one of these ID's got assigned, then we will log an event to the Output Window that
Program modified - new Edge/Instruction IDs assigned at download
I know this is clear as mud, but it's some of the special handling that is done by Designer that wasn't properly propogated back to the user level. This special ID handling will ONLY occur if a NEW EDGE/DEVICE instruction was added or if you MODIFIED AN EXISTING EDGE/DEVICE instruction (this is technically a "new instance" of that operation). Any other instruction will not cause this to occur (e.g. non-edge contacts, coils, MATH), basically anything without an edge or without a device.