What it means is that we did something stupid. I'm not sure how novice you are, so I'll take it from the top.

So, lets say you have a large training set - a large number of randomly chosen example essays, and a score for each essay.
You want to develop a model that will predict/guess scores of other essays, in future.

Your model is going to be evaluated against a set of essays - the test set - which you do not see until after the competition is over (at which point you may no longer make changes to your model).

Now, in an ideal world, we'd be able to take just one look at the training set, and instantly learn the best way there is to predict similar essay scores in future. But, in practice, that's not going to be the case. We, the designers of the ML approach, initially don't know how to build essay grading systems. So we are going to need to do a development process, where we think, then try a particular approach, then see how well it works. And we have to iterate on that process.

So, the key bit there, is that we need to see whether a particular approach is working. We can't check the test-set - we don't get access to that until the end of the competition. So, instead, we take the training set, and partition it up into 2 parts, lets call them A and B. Essays from both A and B are in the training set, as we've just said, hence we know the scores for the essays of both A and B.

We now train our model with essays from A, without it being allowed look at essays from B. Then, afterwards, we have it make predictions/guesses of the scores of essays in B, and see how well it does.

This allows us to evaluate how well our modelling approach is doing.

In practice, we repeat this procedure for many different partitions of the training set, into many different A and Bs.

So, thats cross validation.

The key thing to realize is that if we use any information from B, when training A, its not really a fair evaluation. The model we make from A will have been contaminated with information from B.

Which is simple enough.

The thing is, in practice, before doing any of this, people generally go through a conceptually separate process, of deciding what features their model is going to use - 'Feature Selection'. Some part of this process is intuitive, but there are also algorithms people use, to help them do feature selection.

The wrong way to do Feature Selection, is to run your Feature Selection algorithms on the entire training set, and then afterwards train your model on one partition A of the training set, and test it on separate essays from B.

This is wrong, because the essays from B were seen in the feature selection process. Sometimes that doesn't matter - if you are dealing with large data sizes, etc. But on the Kaggle essay competition, in particular because we had rather a large amount of features (e.g. bag-of-words n-grams, parts-of-speech n-grams), relative to the number of training examples, it certainly did matter.

As a result, we saw much higher scores on the training set, than we would have gotten on the test set.

The right way to do things is to do any automatic feature selection only on the training partition of the training set, A. One way to do this is to build any algorithmic feature selection into your process after the point you partition into A and B, during cross validation. But its very easy, when hacking on a competition solution, over a weekend, to accidentally put things in the wrong order, in the codebase.

For this reason, if you have limited training data, its generally a good idea, at the very start of the process, to take some training data, and put it somewhere completely separate, until you think your algorithm is ready to go, and then use it as a final test. People often call this the validation-set.