I am still trying to understand what actually happened.
Okay, I'll try to help.
What I have read is that BTCNuggets, a miner that was still running v2 software, mined a block B'(N) that was valid by v2 criteria but invalid by v3 criteria.
Yes. The software doing the block version upgrade to V3 was watching and had counted 950 of the last 1000 blocks having contained headers that said "block version=3". Once that happened, any further blocks whose header said "block version = 2" were supposed to be rejected.
And at precisely that moment, at absolutely the first block where a "version = 2" block ought to have been rejected, BTCNuggets mined a block whose blockchain header said "block version = 2."
Somehow F2Pool, a miner that was running v3 software, received the header of this block and successfully mined a block B'(N+1) that was valid under v3 rules (empty, in fact) except for having an invalid parent. As luck would have it, they also mined another (empty) block B'(N+2) with B'(N+1) as parent. Then AntPool, who was running v3 software too, mined B'(N+3) on top of B'(N+2). And so on; this was the 'bad' branch.
Slight edit here: F2Pool and AntPool were not running correct software. They had taken correct software and then deliberately modified it to make it wrong. They stripped out the functionality of actually checking the block they were building on, because time spent checking a block for validity is not time spent mining, and - ASSUMING the block you've received is correct - spending more time mining makes you more money. So F2Pool took this "version= 2" block from BTCNuggets, failed to check it, and built a "version = 3" block as the next block in the chain. This version-3 block was not valid because it was built on a block that ought to've been rejected, but they'd never checked. F2Pool and Antpool then built further on this new invalid block, never actually checking the original block from BTCNuggets. If they had checked that block, they'd have known that the blocks they were building were invalid because that parent/grandparent/etc block originally built by BTCNuggets was invalid by the v3 rules.
When they did this, they built a chain that every full node checking the block chain and running correct software for block-version-3 would immediately reject no matter how long it got.
Meanwhile, other miners running v3 software either did not see block B'(N), or saw it and detected that it was not v3-valid; so they ignored it and eventually mined a block B''(N) that was v3-valid. Several v3 miners solved additional v3-valid blocks B''(N+1), B''(N+2), etc. on top B''(N). This was the 'good' branch.
Yes. Miners that had not deliberately introduced this bug into their software, simply ignored the bad chain because its blocks are invalid, and continued to do as they always do - to work to extend the longest valid chain. Likewise all v3-enabled clients were also ignoring the bad chain and would not regard a transaction as "real" unless it were included in a valid chain.
For a while, the bad branch was ahead of the good one, in terms of total work. Fortunately the devs and other bitcoin hackers were watching the blockchain to monitor the onset of BIP66, spotted the problem immediately, alerted F2Pool and AntPool that they were mining a v3-invalid chain, and sent out an alert recommending a 30-confirmation (5 hour) wait for important transactions.
Regardless of how far ahead the bad branch had gotten, nobody running correct V3-checking software would ever have been using its blocks as a criterion of whether a transaction were successful, because those were invalid blocks. The bad fork could still be adding blocks today, and people running up-to-date software would be in absolutely no danger of being fooled. As far as a full node is concerned, your transaction is not confirmed until it is in a VALID block.
The only reason people were in danger of thinking a transaction was confirmed when it in fact was not, is because a lot of them are subject to exactly the same bug that F2Pool and AntPool deliberately added to their otherwise-correct code - they are not fully checking blocks for validity.
In some cases this is because they are running lightweight phone clients or SPV wallets. Some of those check
most of the requirements for validity, but there is one condition that they cannot check without downloading the entire block chain, and that is that in order to be valid a block must be built on a valid parent block.
In other cases this is because they are using web wallets at places like blockchain.io, and have no idea whether or not the people holding their keys are checking the validity of blocks according to the v.3 rules. - in which case it's best to assume that they are not, otherwise you're going to get "confirmed" transactions which later disappear.
Is this a correct summary of the events?
Approximately.
* How many blocks were actually mined on the bad branch (starting form B'(N)) before it was abandoned?
Six. Assuming nobody decides to mine another one.
* Were F2Pool, AntPool, and the other pools that mined on the bad chain running modified versions of the v3 core? If so, what were the modifications?
Yes. They had deliberately introduced a bug into their code to avoid "wasting" time checking the validity of blocks.
* Did F2Pool realize that the BIP66 rules had already started to apply when they received B'(N) from BTCNuggets? (Say, perhaps it had counted only 949 v3 blocks, or had its counter reset recently, etc.)
Perhaps they knew BIP66 had gone into effect, but they weren't checking validity, so clearly they didn't care.
* How did F2Pool get the header of the invalid block B'(N): directly from BTCNuggets, from a v2-running relay node, or from a v3-running relay node?
Pretty sure it doesn't matter; given what they'd done to their mining software it would have done the same thing regardless.
* In the standard version of the v3 core software, do miners get the whole parent node and verify its validity?
Yes, absolutely.
* Could there be an off-by-one bug in the v3 core software, that would have resulted in F2Pool verifying B'(N) with pre-BIP66 rules instead of BIP66 rules? (Note that a v3-valid node *can* have a v3-invalid parent, if the parent was mined before BIP66 went into effect.)
Nope, I checked. There is no off-by-one error in the functionality that they cut out of their client.
* If F2Pool knew that BIP66 had kicked in, and got B'(N) from a v2-running miner or node: why was it still accepting transactions, blocks, and block headers from v2-running players, without checking whether such data was v3-valid?
Because of not being willing to spend one-tenth of a second checking a block when that tenth of a second could be spent mining.
* If a miner receives a block B(M), how many blocks B(M), B(M-1), B(M-2) etc. should it validate on its own before daring to mine B(M+1) on top of it?
ALL OF THEM. The blocks between the current block and the genesis block. When you receive one block you need to check one block. And if you keep doing so, then at any given moment you're going to have complete proof of the validity of EVERY BLOCK from genesis to the current block.
* Suppose that a miner receives block B(M) and starts validating it, while in parallel mining a new block B(M+1) on top of it; and happens to solve B(M+1) before the validation of B(M) is complete. Should it broadcast B(M+1), or wait until the verification of B(M) is complete?
Clearly not at issue here; in more than one case, the previous block was more than ten seconds old before the next block came out, meaning there is absolutely no way that any attempt to check it was made. Any such check would have failed WELL before then.