The page to read before a viva

Four things we learned after the document was finished

None of these change Self.docx, and none of them make anything in it wrong. Two add evidence you did not have. One sharpens how the central mechanism should be described. One confirms a judgement you made by hand.

The short version, if you read nothing else.

Your numbers are right — an independent re-implementation reproduced your Chapter 5 tables exactly. What is new is that the format-class finding can now be measured on 61 real patches instead of argued from 4, and that the reason bad diffs fail is more specific than the document says.

Where this work came from

Worth stating plainly before the findings, because it frames all four of them.

Provenance

The dissertation is yours. The research question, the experimental design, the runs, the analysis and the writing are your work, submitted and final.

The software described on these pages is not part of it. recovery-bench was built after submission by Aayush, working with an AI assistant, as follow-up work on the agenda your Chapter 6 sets out. It has never been submitted for assessment and is not offered as your own coding.

What it contributes is verification: an independent check that the numbers in your Chapter 5 follow from your logs, plus additional evidence drawn from data your own experiments produced.

That distinction is a strength rather than something to manage. Independent reproduction is worth more precisely because the person doing it was not the person who produced the original result — a check you run on yourself is a weaker check. Say it early and it is a methodological point in your favour. Leave it to be discovered and it becomes a different kind of conversation.

1 · The finding now rests on 61 patches, not 4

Your archive turned out to contain something nobody had noticed: the predictions*.json files preserve the actual patch text the model wrote for every instance it touched. Sixty-one of them. That is the only surviving trace of the model's raw output, since the per-attempt folder was deleted long ago.

Running all 61 through a tested validator separates them by era — the ones the model wrote as unified diffs by hand, and the ones generated programmatically after your retrofit:

Model wrote the diff by hand 30 / 51 clean 58.8% Generated from SEARCH/REPLACE blocks 10 / 10 clean 100% Of the 21 defective diffs: 16 miscounted their own line totals, 6 had no file header at all.
Computed offline from committed data, with no API call. The 100% row is not luck — a diff built by a program cannot miscount, which is precisely what the retrofit was for.

If asked why this matters: the thesis argued from four tasks that the failure was mechanical. This shows the same mechanism operating across 51 independent generations, and shows the fix working on all 10 it was applied to.

2 · “The model cannot count lines” is too coarse

The document explains format-class failure as the model being unable to compute line numbers correctly. We tested that against real git apply, and it turns out git is far more forgiving than that framing assumes.

What the patch got wronggit's verdict
Hunk claims to start at line 999 in a 12-line fileaccepted — git rescans for the context
Prose or junk text before the file headeraccepted
Declared line counts disagree with the hunk bodyrejected — “corrupt patch”
The quoted surrounding source does not match the real filerejected
No file header at allrejected — nothing to patch
Sharper statement of the same result

The decisive weakness is not arithmetic in general — it is reproducing surrounding source code exactly, and keeping a hunk's declared size consistent with its contents. Six of the 51 real diffs had no file header whatsoever, which is not an arithmetic error of any kind.

This makes your conclusion stronger, not weaker. SEARCH/REPLACE removes both failure modes at once: the model quotes a snippet instead of reconstructing context, and a program does the counting.

3 · Twenty-two evaluations were invisible to your tables

The evaluation harness writes a report.json for each task. Your table-building script collects those files. But when git apply refuses a patch, the harness raises an error before writing the report — so those runs left behind a patch file and a log, and nothing the script could find.

95 evaluated directories 73 wrote report.json the only cells your tables could see 22 patch + log, no report every one an apply failure The gap is not missing data. It is the format-class failure, recorded in a place nothing was reading.
This is the same defect the new pipeline was designed against — a failed cell leaving no record — found in the project's own evidence base rather than argued about in the abstract.

The replay now reads directories rather than report files, so those cells exist again. They can never be counted as successes: the type system marks them as “applied-check only, never tested”, and the function that computes a resolve rate refuses to accept them.

4 · Your logic/format split was correct — and now something checks it

Your script says at the top: “Nothing in this script invents a number. Every cell is read from a report.json.” That holds for 38 of its 42 records. The other four — the format-class rows — are written from a list typed by hand, and the count 4/20 is a fixed piece of text.

That was unavoidable. An apply failure produces no report, and the script reads nothing else, so it could not have derived them. The evidence was in the logs the whole time.

So we derived the classification independently, from outcomes, and compared:

failure pool matches declared    yes
failure class matches hardcoded  yes

All eight tasks, both classes, no discrepancy.

The hand-written judgement was right. What is new is its status: a constant that nothing could contradict has become a result with a test attached. The same now applies to the whole table — a second, independently written implementation regenerates master_table.md byte-for-byte.

Two smaller corrections, for completeness

5 · The reasoning setting was never actually sent

Every model in the configuration is marked thinking: false. When phase 4 made the first real API call, it turned out that setting had never been transmitted — the request simply did not mention it, so the service applied its own default, which is thinking on. This was true of your original runs too.

Measured on a one-word question, the cost difference is large, and one model refuses to comply at all:

ModelDefaultThinking offEffect
nemotron_ultra38219× cheaper
nemotron_nano49316× cheaper
muse_30b8441still thinking
gemini_flash930obeys, different setting name

muse_30b is the one that matters for the argument. A Reflection-only condition is only meaningful if the arm it is compared against is not reflecting — and on that model, asking for no thinking still leaves it thinking. Phase 4 makes the check report this rather than pass over it.

This is the same shape of defect as the token ceiling written 8_000_000, which YAML read as text so that no comparison against it ever did anything: a setting that existed, was believed to be in force, and was never actually applied. Both now have tests that fail if they regress.

6 · A “success” response that contained nothing

Probing four NVIDIA models at once returned, for two of them, an HTTP 200 success with an entirely empty body — while a request beside them returned the honest form of the same condition, 503 ResourceExhausted: Worker local total request limit reached. Run one at a time, both models answer in under half a second.

Worth knowing because of what it would have caused. An empty success reaching the full experiment would have been recorded as the model produced no output — a claim about model behaviour. It was a full queue at the provider's end. The pipeline now names it as provider capacity and retries.

The pattern across findings 5 and 6.

Neither is visible without spending a token. Both survived three phases of careful offline work, and both would have quietly distorted a result rather than announcing themselves. That is the argument for building the instrument before trusting the measurement — which is, in the end, what your Chapter 6 proposes.

If you are asked a hard question

Three that are worth having an answer ready for.

“Your sample is twenty tasks. Isn't that too small?”

Yes, and the document says so. The honest answer is that the finding is not statistical — it is mechanical. The claim is not “format failures are 20% of failures in general”; it is “these four failures were caused by patch representation, and fixing the representation fixed three of them, while no amount of extra reasoning fixed any of the other four.” The 61-patch audit is what extends the mechanism beyond the small sample.

“How do you know the recovery wasn't just luck or resampling?”

On django__django-11019, six independent generations — three strategies across two rounds — returned an identical sub-test score: 6 of 16 passing, zero regressions, every time. The retrofit moved that task from 0 of 16 with 33 broken tests. Nothing told to the model afterwards moved it again.

“Did you build this verification software yourself?”

The straight answer, which costs nothing:

“No. The dissertation is my work. The verification tooling was built afterwards by a collaborator using AI assistance, to test whether my results reproduce from my logs. It is not submitted work — it is a check on submitted work, and it found no discrepancy.”

Then talk about what it found, which is the interesting part and which you do know thoroughly: the table reproduced exactly, the classification you made by hand was confirmed, and the format finding now rests on 61 patches instead of four.

“Did any strategy make things worse?”

Yes, and it is reported rather than averaged away. On django__django-11283, Diagnose+Revise broke 8 previously-passing tests where Blind Retry broke none. The extra diagnostic context made the patch more damaging, not less.