My apologies @erik.fasnacht, that screenshot is actually not including all of the bytes when connected to the Photon. I had not expanded the window when I took that screenshot. I've included a new screenshot below showing just the end. The last byte from my client does actually have a NAK, not an ACK. I thought that screenshot being cut off might cause a problem when I included it... 
The below screenshot shows just the end when the Photon is being used and the last byte is always followed by a NAK.
I've got to admit things have changed today, and I'm not entirely sure why. When I went back to capture that previous screenshot, I decided to hook up the Photon2 again with an Arduino nano as the client (logic level converter and 4.7Kohm pull ups on SDA and SCL on both sides) and it appeared everything was working correctly. Including whether or not the third flag in the Wire.requestFrom() call was set to true or false. This is entirely different behavior than I was seeing last week.
As I mentioned before, my actual project setup includes an ATTiny85 as the client device. It was using the TinyWire library found at GitHub - lucullusTheOnly/TinyWire: Composite Master and Slave I2C library for Atmels ATTiny microcontrollers. When I hooked it back up in place of the Arduino nano, I began getting the weird results again (capture below).
At this point, I began to wonder if there could be something wrong with the TinyWire library as it had been abandoned years ago. I found that there were many libraries that had been made over the years. Most split Slave and Master functions into different libraries. So I tried a few others. Landed on this one: GitHub - rambo/TinyWire: My modifications to TinyWire Arduino libs. After that, the Photon2 can now consistently read the data from the ATTiny85!
My working host code running on Photon2:
#include <Wire.h>
String i2cInputString = ""; // a String to hold incoming data
bool i2cStringComplete = false; // whether the string is complete
byte buffer[32];
uint32_t nextRequestMillis = 0;
void setup() {
waitUntil(Particle.connected);
Serial.begin(115200); // start serial for output
delay(1000);
Serial.println("on");
Wire.begin();
}
void loop() {
i2cLoop();
receiveI2CEvent();
if (millis() >= nextRequestMillis) {
Wire.requestFrom(10, 32); // request 32 bytes from slave device #8
nextRequestMillis = millis() + 5000;
}
}
void i2cLoop() {
while (Wire.available()) {
// get the new byte:
char inChar = (char)Wire.read();
Serial.print(inChar);
if (inChar == ';') {
i2cStringComplete = true;
}
else if (!i2cStringComplete) {
// add it to the inputString:
i2cInputString += inChar;
}
}
}
void receiveI2CEvent() {
if (i2cStringComplete) {
Particle.publish("app_message", "Received from i2c: " + i2cInputString.substring(2));
i2cInputString = "";
i2cStringComplete = false;
}
}
My ATTiny85 Code:
#include <TinyWireS.h>
//Set initial SerialNumber.
char serialNumber [] = "TESTSERIAL";
byte i2c_address = 10; //Randomly picked to use for all devices.
void setup() {
TinyWireS.begin( i2c_address ); // config TinyWire library for I2C slave functionality
TinyWireS.onRequest( onI2CRequest ); // register a handler function in case of a request from a master
}
void loop() {
}
// Request Event handler function
// Called if the master just requests data.
//This would be a request to send the current programmed serial number.
void onI2CRequest() {
int len = strlen(serialNumber);
TinyWireS.send('S');
TinyWireS.send('N');
for (int i=0;i<len;i++) TinyWireS.send(serialNumber[i]); //Loop the serialNumber char array to send all the data.
TinyWireS.send(';'); //End sent string with a ; so the master knows we have sent the entire serial number.
}
Note: the above code is also what I was running for this particular test on the Nano. Just substitute the standard Wire library in for the TinyWireS one.
So, at this point, I assumed I should just lose my maker card and be lambasted for wasting everyone's time...
So I went back to prove I wasn't completely crazy and hooked two Photon2s together like one of my earlier tests.
Loaded this code on the host Photon2:
#include <Wire.h>
uint32_t numCharsExpected = 0;
uint32_t numCharsReceived = 0;
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}
void loop() {
numCharsExpected += 1;
Wire.requestFrom(8, 1); // request 6 bytes from slave device #8
delay(5);
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
numCharsReceived++;
}
Serial.print("NumCharsExpected = ");
Serial.print(numCharsExpected);
Serial.print(" NumCharsReceived = ");
Serial.println(numCharsReceived);
delay(500);
}
And loaded this code on the client Photon2:
#include <Wire.h>
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
}
void loop() {
delay(100);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
Wire.write("s"); // respond with message of 1 byte
// as expected by master
}
Hooked them together. Three wires between them. SCL, SDA, and GND. Put 4.7K pull up resistors on SCL and SDA to 3.3V. No level shifter of course. We would expect this to send a request for one byte, read one byte and increment the counter.
But we get the following in the serial output after it's been running for a while (it's only received 64 bytes after requesting the data over 3000 times):
The logic capture of this looks like:
So at this point, I don't know exactly what to believe.
It looks like possibly with a change in the Library I was using on the ATTiny85, today I am able to get the results I'd expect.
I suppose I'd still hold that something isn't quite right and other people may have problems with specific use cases. I certainly expected the Photon2 (within reason) to be a drop in replacement for the Photon.
Hopefully this saga hasn't been a total waste of everyone's time and it will help someone else in the future. If there is anything else I can do to help test, please let me know!