See PHI1535. If you:
- have task custom field X which only exists on task subtype S; and
- properly create an object of type S via transactions in the API; and
- also specify a value for field X.
You get a validation error that "X" is not a valid field. This is technically true -- the task isn't a task of subtype S yet -- but not helpful or desirable.
This is a general problem with how transactions are validated and applied. There is a similar existing precedent around "type" transactions for objects like AlmanacService. The AlmanacServiceEditEngine->newEditableObjectFromConduit() method has some code to pull "type" transactions out of the transaction list and apply them first.
We could imagine an incremental approach instead, where transactions are applied one at a time and then followup transactions are validated. This probably leads to the most correct outcome conceptually, but it means we have to start doing work before we can validate things, and sometimes work is hard to undo. For example, we could hit a case where transaction 1 creates some child object, then transaction 2 fails for a trivial reason. We'd need to be able to revert transaction 1 to get back into the right state. This is not practical today and not always possible in the general case.
It may also not be possible, broadly. If an object has both a "type" and a "subtype", both transactions may need to apply before anything can really validate (including the "type" and "subtype" transactions themselves). It's also more friendly for users if transactions can apply in any order, and consistent with the timeline UI which reorders transactions in "importance" order.
So, for now, I think the best path is to continue special-casing these interactions until that stops working. I think the relevant rules here are just:
- Subtypes need to process before normal transactions.
- Subtype transactions will then become no-op transactions, but should be preserved in the transaction record and act as though they are not no-op.
- (Possibly, changing subtypes may need to invalidate other object caches relate to custom fields / existing subtypes.)
These rules are likely easy to implement narrowly so that things technically work, and we can cross the bridge of complex transaction dependencies when we come to it.