For local activity, the input doesn't contribute to history size but the output does, is this expected?

If the workflow executes a local activity giving it say 10 MB data, it’s not recorded in the history and everything’s fine. I can’t see any inputs to that local activity in temporal UI too. However if that local activity returns data, it’s recorded in history (or at-least the temporal ui shows it AND it affects the history size). So if I return 10 MB from local activity, the workflow errors out saying max gRPC limit is 4 MBs.

Is this expected and can I rely on this (ie. pass unbounded-in-size data to local activity is fine as long as it doesn’t return anything big back to the workflow)?

Yes, this is expected. In some SDKs you can enable logging of local activity inputs. This might be useful for troubleshooting but counts towards all the limits.