how to programmatically introduce grammatical errors in sentences
Generating artificial errors is generally risky in NLP, because it's difficult to make sure that the type and distribution of errors correspond exactly to real human errors. If the artificial errors diverge from real errors and a model is trained based on this data, the model will appear to have very good performance since it will rely on the patterns used to generate the errors. However it might not perform well with real data, and it would be difficult to detect it.
That being said, it's been a problem which has been studied for quite a while so the state of the art should help: Google Scholar gives a lot of references, probably some of these papers provide existing implementations as well. One may notice that the concerns I mentioned above are a recurrent question, with some recent papers analyzing how much artificial errors actually help.