NB: I suspect the root cause of my problems is that Xcode4 is incorrectly updating the private hashes it uses instead of version numbers to track a data-model version. Probably I accidentally added something, then deleted it, and it changed the hash - my original model was so very simple it was easy to compare by eye, and there were no differences.
Also, more generally, the problem is that Xcode4 still doesn't handle CoreData projects properly - it creates CoreData models as "versioned" by default (big improvement from Xcode3, which was doomed to always create useless models), but it still doesn't do any handling of changes in the model - you must manually remember to update the version BEFORE you save any changes (or else all your project migration will fail, forever, with no way out).
Also, I couldn't find any "correct" solution once things go wrong - just too many missing pieces from Apple's Core Data lib. Basically: NSPersistentDocument is incomplete, unsupported, so far as I can tell.
In the end, I went with the following BRUTAL workaround: instructed everyone with old versions to manually edit the CoreData stores to claim they are running the current version.
This works 100%, because unlike CoreData, my app is written to intelligently correct any obvious missing data on import. It's trivial, but CoreData doesn't allow you to say "yes, I've done this. It's OK. Go ahead and open the file!"
- in old version: "save as XML"
- gave them the new header to copy/paste into top of file. NB: Apple uses hashes instead of version numbers, which makes this look scary, even though it isn't. I don't understand why Apple is ignoring their own version system and using hashes instead?
- in new version: open the updated file
- in new version: "save"
...and because I'm overriding the NSManagedObject method:
-awakeFromInsert
and fixing any incorrect/missing data, the steps 3 and 4 above "automagically" update the data.
I know that the "correct" way would have been to create a mapping model, but it's massive overkill for most situations ... and Apple was refusing to load the old data in the first place. So, Core Data was just refusing to even allow me to correct the data - even though my code was happily doing so.
(all old projects have imported and exported correctly - no problems)
IMHO: CoreData really needs some re-design on Apple's end to fix all the edge-cases they didn't think about first time around.
NB: quick warning: make sure you DON'T change any CoreData variables inside awakeFromFetch - Apple temporarily disables change-tracking before calling that method (which is kind of laughable - it makes the method incompatible with the behaviour of all other "awakeFrom*" methods).
Apple's advice: inside awakeFromFetch, decide what you're going to change, then package it up and use something like this:
[self performSelector:@selector(myAwakeFromFetchFixItemX:) withObject:X afterDelay:0.01];
...just thought I'd add that for anyone trying this - otherwise your import will work fine, but your export will silently fail to include the fixed data!