These errors on "ambiguous" types come from GADT type checking, which requires annotations in certain places (-principal is more picky about requiring more annotations; instead sometimes the type-checker makes guesses).
Currently the syntactic forms
let <variable> : <type> = <expr> in <expr>
and
let <pattern> : <type> = <expr> in <expr>
are not desugared in the same way. The first is turned into
let <variable> = (<expr> : <type>) in <expr>
and the second into
let (<pattern> : <type>) = <expr> in <expr>
In the first case (let <variable>), the body of the definition gets the annotation. This is required, in your code, for this body to compile under -principal. In the second case (let <pattern>), the body does not get the annotation, so type-checking fails (under -principal).
You can fix it yourself by adding the annotation on the right-hand-side directly
let (op, idx) = (begin match ... end : int op * int)
in fact it suffices to write (op : int op), 2 in the second clause's right-hand-side.
I don't know whether (let <pattern> : <type> = <expr> in <expr>) could instead be desugared into
(let (<pattern> : <type) = (<expr> : <type>) in <expr>), which would also fix the issue. The specifics of how type information is propagated in the type-checker is a delicate design aspect of the type-checker which may still evolve in the future.
The problem is that within the Op clause, we know the type equality (a = int), but this is not true outside the clause; so when a value that has both types (a op) and (int op) is returned by the clause, the type-checker cannot know which type to give to the outside (a op, or int op?), and it needs an explicit annotation to not make an arbitrary (non-principal) choice.