Kalah TDD study · Part 5 of 29 · Methodology

Methodology: analysis and validity

Award scoring, the statistical tests and the threats to validity.

Source runs full-1, rep-1, rep-2, tcp-1-list, rep-2/rebuild, pilot-1, pilot-2, tcp-1-list2 and tcp-1 at tdd-experiment c3dd988

Contents

This page sets out how the study scores its awards, which test answers each question and what the study cannot show. It defines no metric. M, E, C and every other measure are defined on Methodology: measures, and short labels such as CQP and PMD in the Glossary. The test results are on the results pages for the survey, the planned contrasts and consistency.

The medals are on the landing page. unchecked

Award scoring

Each award but best overall places the methods on one value, lower first. places gives competition places, ties sharing the best place. podium keeps the places up to TOP = 3 and award gives them the medals gold, silver and bronze. Best overall ranks every method on the 3 CRITERIA cost_effective, fewest_errors and alignment with stats.ranks, ties sharing their average rank, and sums the ranks. Cheapest and Most consistent design are bronze badges that never count toward it. Checked against awards.py at tdd-experiment c3dd988. verified

Figure 5.1

How awards are scored

How awards are scoredThree lanes. Criteria: method records are scored on each criterion, placed lower first and given a podium of medals. Best overall: the criterion values are ranked, summed and placed. Badges: cost to completion and consistency score C are placed for a bronze badge only.CriteriaBest overallBadgesMethod recordsone per methodCRITERIAcost_effective,fewest_errors,alignmentplacesper criterion, lowerfirstpodiumtop 3: gold, silver,bronzestats.ranksties share theiraverage rankoverallrank sum over CRITERIAplaces and podiumtop 3: gold, silver,bronzeCost to completion, Cnever ranked inCRITERIAplaceslower firstBronze badge onlylimit stated with theaward
  1. Method records. one per method. Lane: Criteria.

    Next: CRITERIA; Cost to completion, C.

  2. CRITERIA. cost_effective, fewest_errors, alignment. Lane: Criteria.

    Next: places (Criteria, places); stats.ranks.

  3. places (Criteria, places). per criterion, lower first. Lane: Criteria.

    Next: podium.

  4. podium. top 3: gold, silver, bronze. Lane: Criteria.
  5. stats.ranks. ties share their average rank. Lane: Best overall.

    Next: overall.

  6. overall. rank sum over CRITERIA. Lane: Best overall.

    Next: places and podium.

  7. places and podium. top 3: gold, silver, bronze. Lane: Best overall.
  8. Cost to completion, C. never ranked in CRITERIA. Lane: Badges.

    Next: places (Badges, badge-places).

  9. places (Badges, badge-places). lower first. Lane: Badges.

    Next: Bronze badge only.

  10. Bronze badge only. limit stated with the award. Lane: Badges.

Each award but best overall places methods on one value. Only the 3 criteria feed the best overall rank sum. verified

Text version: How awards are scored
  1. Method records. one per method. Lane: Criteria.

    Next: CRITERIA; Cost to completion, C.

  2. CRITERIA. cost_effective, fewest_errors, alignment. Lane: Criteria.

    Next: places (Criteria, places); stats.ranks.

  3. places (Criteria, places). per criterion, lower first. Lane: Criteria.

    Next: podium.

  4. podium. top 3: gold, silver, bronze. Lane: Criteria.
  5. stats.ranks. ties share their average rank. Lane: Best overall.

    Next: overall.

  6. overall. rank sum over CRITERIA. Lane: Best overall.

    Next: places and podium.

  7. places and podium. top 3: gold, silver, bronze. Lane: Best overall.
  8. Cost to completion, C. never ranked in CRITERIA. Lane: Badges.

    Next: places (Badges, badge-places).

  9. places (Badges, badge-places). lower first. Lane: Badges.

    Next: Bronze badge only.

  10. Bronze badge only. limit stated with the award. Lane: Badges.

Award rules

Each rule is quoted from awards.py. verified

Counts toward best overall marks the awards whose criterion joins the rank sum, by the criterion to award mapping in the Award column below. unchecked

Award rules
AwardRuleCounts toward best overall
Best overallLowest plain sum of each method's ranks on M, E and alignment, ties sharing their average rank. Cheapest and consistency never count.Is the rank sum
Most cost effectiveLowest M, the cost to completion times 1 minus APFDc-H, among methods with a functional mark of 15/15. M_io leaves output tokens out.Yes
Fewest errorsLowest E, the distinct PMD findings plus adjusted surviving mutants, hidden failures and unseen failures.Yes
Best CQP/PMD alignmentThe blind CQP rating agrees with PMD on every principle and PMD reports no issue.Yes
CheapestLowest cost to completion. A bronze badge that never counts toward best overall.No
Most consistent designLowest consistency score C. A bronze badge that never counts toward best overall.No

The Most consistent design rule carries a consistency limit, which the landing page states with the award. unchecked

awards.py functions and constants

Criterion names and returns, docstrings and constants are quoted verbatim from awards.py at the snapshot sha. verified

The Award column is this page's mapping of each criterion to the award it scores. awards.py does not state it. unchecked

awards.py criteria
CriterionAwardReturns
cost_effectiveMost cost effectiverecord['m'] if record['full'] else None
fewest_errorsFewest errorsrecord['e']
alignmentBest CQP/PMD alignment0 if record['aligned'] else None
awards.py functions
FunctionDocstring
error_countE: distinct PMD findings, adjusted surviving mutants, hidden failures and unseen failures, None when any is unmeasured.
agreesWhether the raters read each principle clean exactly where PMD finds nothing in it.
alignedBoth the blind rating agrees with PMD and PMD reports no issue. False unrated or unlinted.
cost_effectiveNo docstring in awards.py.
fewest_errorsNo docstring in awards.py.
alignmentNo docstring in awards.py.
placesCompetition places, lower first, ties sharing the best place.
podium[(place, record, value)] of the records value scores, lower first, up to place top.
overall{name: rank sum} over CRITERIA, each ranked by stats.ranks so ties share their average rank. A method a criterion leaves out ranks last on it.
consistency_limitNo docstring in awards.py.
tokensNo docstring in awards.py.
award{title, rule, places, hover}, each place (place, medal, name, value text). medal fixes every place's medal.
awardsThe award list. spread is {scores: {name: C}, p, runs, n: {name: builds}, builds, drift: [{name, x, y, p}]} of the replicates pooled over the joined contrasts runs, None without one.
awards.py constants
ConstantValue
TOP3
MEDALSgold, silver, bronze
CLEAN_MEDIAN4

Statistical tests

Each row of the table names a question the study asks, the data it uses, the test as the report names it and the stats.py functions whose docstrings describe that test. The source sentence is quoted from the report section that reports the test. verified

Functions are matched to tests by docstring. The snapshot does not record which function computed each reported number. unchecked

Figure 5.2

Which test answers each question

Which test answers each question6 lanes, one per question. Each runs from the question to the stats.py functions, the multiple-test correction and the page that reports the result.Survey scoresScore intervalsPlanned contrastsConsistencyRatings and codeList reliabilityDo mutation scoresdiffer between thesurvey methods?permutation_chi2,fisher_pairs and holmHolm over every pairof methodsSurvey (full-1)How precise is eachmethod's mutationscore?wilsonNone, descriptive onlySurvey (full-1)Does one replicatedarm differ fromanother?mann_whitney_exact andholmHolm over the plannedpairs of each outcomePlanned contrastsDoes one design varymore between builds?brown_forsythe_permutationand holmHolm over the metricsConsistency acrossrunsDo the ratings tracklines of code and PMD?spearman_trial andholmHolm over thecorrelationsRatingsDo two listers agreeon the test list?weighted_kappa,spearman_trial andjaccardNoneRatings
  1. Do mutation scores differ between the survey methods?. Lane: Survey scores.

    Next: permutation_chi2, fisher_pairs and holm.

  2. permutation_chi2, fisher_pairs and holm. Lane: Survey scores.

    Next: Holm over every pair of methods.

  3. Holm over every pair of methods. Lane: Survey scores.

    Next: Survey (full-1) (Survey scores, at0).

  4. Survey (full-1) (Survey scores, at0). Lane: Survey scores.
  5. How precise is each method's mutation score?. Lane: Score intervals.

    Next: wilson.

  6. wilson. Lane: Score intervals.

    Next: None, descriptive only.

  7. None, descriptive only. Lane: Score intervals.

    Next: Survey (full-1) (Score intervals, at1).

  8. Survey (full-1) (Score intervals, at1). Lane: Score intervals.
  9. Does one replicated arm differ from another?. Lane: Planned contrasts.

    Next: mann_whitney_exact and holm.

  10. mann_whitney_exact and holm. Lane: Planned contrasts.

    Next: Holm over the planned pairs of each outcome.

  11. Holm over the planned pairs of each outcome. Lane: Planned contrasts.

    Next: Planned contrasts.

  12. Planned contrasts. Lane: Planned contrasts.
  13. Does one design vary more between builds?. Lane: Consistency.

    Next: brown_forsythe_permutation and holm.

  14. brown_forsythe_permutation and holm. Lane: Consistency.

    Next: Holm over the metrics.

  15. Holm over the metrics. Lane: Consistency.

    Next: Consistency across runs.

  16. Consistency across runs. Lane: Consistency.
  17. Do the ratings track lines of code and PMD?. Lane: Ratings and code.

    Next: spearman_trial and holm.

  18. spearman_trial and holm. Lane: Ratings and code.

    Next: Holm over the correlations.

  19. Holm over the correlations. Lane: Ratings and code.

    Next: Ratings (Ratings and code, at4).

  20. Ratings (Ratings and code, at4). Lane: Ratings and code.
  21. Do two listers agree on the test list?. Lane: List reliability.

    Next: weighted_kappa, spearman_trial and jaccard.

  22. weighted_kappa, spearman_trial and jaccard. Lane: List reliability.

    Next: None.

  23. None. Lane: List reliability.

    Next: Ratings (List reliability, at5).

  24. Ratings (List reliability, at5). Lane: List reliability.

Connections show the next step in each lane. Clean classes and Rater agreement name no stats.py function in the snapshot, so only the table lists them. unchecked

Text version: Which test answers each question
  1. Do mutation scores differ between the survey methods?. Lane: Survey scores.

    Next: permutation_chi2, fisher_pairs and holm.

  2. permutation_chi2, fisher_pairs and holm. Lane: Survey scores.

    Next: Holm over every pair of methods.

  3. Holm over every pair of methods. Lane: Survey scores.

    Next: Survey (full-1) (Survey scores, at0).

  4. Survey (full-1) (Survey scores, at0). Lane: Survey scores.
  5. How precise is each method's mutation score?. Lane: Score intervals.

    Next: wilson.

  6. wilson. Lane: Score intervals.

    Next: None, descriptive only.

  7. None, descriptive only. Lane: Score intervals.

    Next: Survey (full-1) (Score intervals, at1).

  8. Survey (full-1) (Score intervals, at1). Lane: Score intervals.
  9. Does one replicated arm differ from another?. Lane: Planned contrasts.

    Next: mann_whitney_exact and holm.

  10. mann_whitney_exact and holm. Lane: Planned contrasts.

    Next: Holm over the planned pairs of each outcome.

  11. Holm over the planned pairs of each outcome. Lane: Planned contrasts.

    Next: Planned contrasts.

  12. Planned contrasts. Lane: Planned contrasts.
  13. Does one design vary more between builds?. Lane: Consistency.

    Next: brown_forsythe_permutation and holm.

  14. brown_forsythe_permutation and holm. Lane: Consistency.

    Next: Holm over the metrics.

  15. Holm over the metrics. Lane: Consistency.

    Next: Consistency across runs.

  16. Consistency across runs. Lane: Consistency.
  17. Do the ratings track lines of code and PMD?. Lane: Ratings and code.

    Next: spearman_trial and holm.

  18. spearman_trial and holm. Lane: Ratings and code.

    Next: Holm over the correlations.

  19. Holm over the correlations. Lane: Ratings and code.

    Next: Ratings (Ratings and code, at4).

  20. Ratings (Ratings and code, at4). Lane: Ratings and code.
  21. Do two listers agree on the test list?. Lane: List reliability.

    Next: weighted_kappa, spearman_trial and jaccard.

  22. weighted_kappa, spearman_trial and jaccard. Lane: List reliability.

    Next: None.

  23. None. Lane: List reliability.

    Next: Ratings (List reliability, at5).

  24. Ratings (List reliability, at5). Lane: List reliability.

Where each test is used

Where each test is used
QuestionDataTestFunctionCorrectionReported inSource sentence
Do mutation scores differ between the survey methods?full-1 mutation scores of 11 methodsseeded permutation chi-square; Fisher exact tests with Holm correctionpermutation_chi2, fisher_pairs, holmHolm over every pair of methodsResults: survey (full-1)Over the scores a seeded permutation chi-square gives 63.0 with p below 0.001, and Fisher exact tests with Holm correction separate 11 of the 55 pairs at 0.05.
How precise is each method's mutation score?full-1 killed and compiled mutants per methodWilson 95% intervalwilsonNone, descriptive onlyResults: survey (full-1)Each dot is the share of a method's N compiled mutants its tests killed or timed out, labelled killed/N, and its whisker is the Wilson 95% interval, descriptive only since one suite judges all of a method's mutants.
Do clean methods have more classes?full-1 classes per method, start clones left outexact one-sided permutation testnot stated in the snapshotNone, one testResults: survey (full-1)An exact one-sided permutation test over all 165 ways to pick 3 of the 11 methods gives p = 0.006 for clean methods having this many classes or more.
Does one replicated arm differ from another?rep-1 and rep-2 replicates, 5 per armexact two-sided Mann-Whitney U; Holm correctionmann_whitney_exact, holmHolm over the planned pairs of each outcomeResults: planned contrastsThe planned contrasts of run rep-1, each an exact two-sided Mann-Whitney U over the replicates with Holm correction over the planned pairs of each outcome.
Does one design vary more between builds?rep-1 and rep-2 replicates pooled per methodBrown-Forsythe permutation tests; Holm pbrown_forsythe_permutation, holmHolm over the metricsResults: consistency across runsBrown-Forsythe permutation tests of spread per metric give Holm p 0.339 for unadjusted mutation, 0.613 for classes and 0.518 for cost to completion.
Do the three raters agree?full-1, rep-1 and rep-2 blind ratingsKrippendorff's ordinal alpha; seeded bootstrap 95% intervalnot stated in the snapshotNoneSupporting data: ratingsAlpha is Krippendorff's ordinal alpha over the three rater slots, with a seeded bootstrap 95% interval in brackets.
Do the ratings track lines of code and PMD?finals medians against LOC and PMD violationsSpearman correlations; Holm-adjusted qspearman_trial, holmHolm over the correlationsSupporting data: ratingsLOC and PMD rho are Spearman correlations of the finals medians with lines of code and with distinct PMD violations, with Holm-adjusted q.
Do two listers agree on the test list?tcp-1-list clauses and candidates rated againlinearly weighted kappa; Spearman rho with a seeded bootstrap 95% interval; mean Jaccard overlapweighted_kappa, spearman_trial, jaccardNoneSupporting data: ratingsRisk agreement is a linearly weighted kappa, usage agreement a Spearman rho with a seeded bootstrap 95% interval, and checks agreement the mean Jaccard overlap of the clauses each candidate test checks.

stats.py functions

Docstrings are quoted verbatim from stats.py at the snapshot sha. verified

stats.py functions
FunctionDocstring
wilson(lo, hi) Wilson score interval of k successes in n trials, which stays inside 0 to 1 at k = 0 and k = n.
fisher_exactTwo-sided Fisher exact p of the 2x2 table [[a, b], [c, d]]: the summed probability of every table with the same margins that is no more likely than this one. Integer weights keep ties exact.
holmHolm step-down adjusted p values in the order of ps, each capped at 1 and none below a smaller p's.
fisher_pairs{(i, j): (p, Holm p)} of the Fisher exact test of each pair of rows i < j of a k x 2 table, Holm-adjusted over every pair.
chi2_rowsPearson chi-square of a k x 2 table from each row's successes and size, 0 when a column is empty. An empty row adds nothing.
permutation_chi2(chi-square, p) of the k x 2 table of (successes, failures) rows. p is the share of seeded label shuffles, counting the observed one, whose chi-square reaches the observed.
deviationsEach value's absolute deviation from its group median.
brown_forsytheBrown-Forsythe F: one-way ANOVA F of each value's absolute deviation from its group median.
anova_fOne-way ANOVA F. inf when the values vary between groups only, 0 when they do not vary at all.
brown_forsythe_permutation(F, p) of brown_forsythe. p is the share of seeded shuffles of the pooled deviations from each value's own group median over groups of the same sizes, counting the observed one, whose F reaches the observed. Shuffling raw values would mix the group medians into the spread.
ranks1-based ranks with ties given their average rank.
mann_whitney_exact(U, p) of the exact two-sided Mann-Whitney test. U counts the pairs with x above y, ties as half. p is the share of every split of the pooled average ranks into a group of len(xs) whose rank sum lies at least as far from its mean as the observed, so ties stay exact. The C(n, len(xs)) splits suit small samples only.
weighted_kappaCohen's linearly weighted kappa of two raters' numeric ratings of the same items: 1 minus the mean |x - y| over its chance mean across every pairing of the two raters' ratings. None when chance disagreement is 0.
jaccardShared items over all items of two collections. Two empty ones agree fully and give 1.
spearmanSpearman rho as Pearson r of average ranks, None when either side has no spread.
spearman_testNo docstring in stats.py.
spearman_trial(rho, p, lo, hi, skipped): p is the share of seeded shuffles of ys, counting the observed one, whose |rho| reaches the observed, against the null rho = 0. lo to hi is the seeded percentile bootstrap 95% interval over resampled pairs, skipping the resamples with no spread, which skipped counts. lo and hi are None when no resample has spread. All None when either side has no spread.

Threats to validity

The report states its limits once, kept here word for word. The gaps after them come from the run records and the code docstrings in the snapshot. verified

Limits

What this study cannot show, stated once for the whole page.

Each survey method ran once on one task, so no significance test is possible and every survey gap is a single observation.

Tag: verified.

rep-1 and rep-2 each replicated only 4 methods, 5 times each, on one task with one model, with Classic greedy TDD in both. Their planned contrasts detect only large effects and the consistency comparison is exploratory.

Tag: verified.

full-1, rep-1 and rep-2 are three batches. The cost gap between full-1 and rep-1 is no harness change, since the prompts and launch scripts are identical. It coincides with an agent CLI change from 2.1.295 in full-1 to 2.1.308 in rep-1, checked on one agent transcript of each batch. rep-2 ran on CLI 2.1.309, as its contrast.json runtime records. Every agent in the costs.json files records CLI 2.1.295 in full-1 and 2.1.309 in rep-2, so full-1 and rep-2 differ by the same kind of CLI change.

Tag: verified.

Output tokens per request were 148 in full-1, 605 in rep-1 and 495 in rep-2, pooled over every clone of each run. The survey builds of classic, functions-first and noun-verb each cost less than all 5 of their rep-1 replicates, with median ratios of 1.13 to 1.45. With output tokens left out those ratios fall to 0.91 to 1.09 and only noun-verb's survey build still costs less than all 5 replicates. Cost comparisons of full-1 against rep-1 or rep-2 are not valid on full weights. The survey builds on the replicate figures are captioned as a different batch and no claim pools them. Consistency score C pools the raw costs of rep-1 and rep-2 across CLI builds 2.1.308 and 2.1.309. The drift test of Classic greedy TDD between those runs gives M p 0.310.

Tag: recomputed.

The rep-2 rating accepts none of the eight principles. Minimal Duplication, Modular Structure and Problem Alignment are tentative and Simple Constructs reaches alpha 0.52. So CQP accepted, which sums the three principles accepted on the full-1 finals, has no validity check on the rep-2 finals, and the rep-2 CQP sensitivity check is flagged.

Tag: recomputed.

One rep-2 high audit flag is a minor true positive. Most informative next test replicate rep-2-r09 ran ls ../ from its clone, which listed only the names in the clones dir, _heldout among them. It read no file outside its clone. The other 49 rep-2 high flags were judged false positives.

Tag: verified.

All 5 Held-out TDD replicates of rep-2 were rebuilt on 2026-10-30 at harness commit cc8c8cb, the held-out splitter merge, with the recorded harness delta of sha256 8fde5d73. The rest of the batch ended on 2026-10-29 at harness commit 809589d. So the Held-out TDD against Classic greedy TDD contrast spans two launches.

Tag: verified.

Whether the recorded token usage matches the real thinking volume of each request is unchecked.

Tag: unchecked.

All 11 methods reached the ceiling of 15/15, so the functional mark cannot rank them.

Tag: recomputed.

One model ran every agent, claude-opus-5-5 at medium effort.

Tag: verified.

Wall times were measured while all methods ran in parallel on one machine.

Tag: verified.

Bench times come from one machine and hold only for it.

Tag: verified.

Each rater saw a rebuilt clone whose history names no method and a prompt that names none. Comments inside src and test stay as written, so a comment could still hint at a method. The single review per method that the rating supersedes had no second reviewer.

Tag: verified.

The calibration gate was revised on 2026-10-26 after round-a failed. C5 now checks only problem_alignment on the starter stub (D20). C2 now passes a drop of 1 from a parent median of 3 (D19). The modular mutant was rebuilt as one god class (D21). The stub's round-b medians would fail the original C5, which needed all eight principles at 1 or 2.

Tag: verified.

Mutant counts differ by method, so mutation scores compare only loosely.

Tag: recomputed.

Alterability is not measured. No change case was carried out and the rating leaves it out.

Tag: verified.

Testability in the course sense and cohesion, complexity and coupling are not measured.

Tag: verified.

Weighted tokens are relative prices and state no dollar cost.

Tag: verified.

The 15 harness and review agents that belong to no method are left out of every per-method cost.

Tag: recomputed.

The agent transcripts behind Q9 and Q10 stay outside the repo, so those counts cannot be rechecked from this page.

Tag: verified.

The overall rank sum is descriptive. Alignment uses all eight principles, including those with low or unestimable agreement. E is an additive composite count and does not count unique defects. verified

M = T × (1 − APFDc-H) is a trapezoidal estimate over recorded checkpoints. Checkpoint spacing can miss changes between observations. M_io recomputes the curve without output tokens as a sensitivity check. It does not establish comparability across CLI versions. C pools costs without correcting those differences. verified

Gaps in the records

Each row is derived from facts.json at the snapshot sha, so it changes when the snapshot does. recomputed

rep-2/rebuild rebuilt the 5 Held-out TDD replicates rep-2-r04, rep-2-r08, rep-2-r14, rep-2-r17 and rep-2-r20 at harness commit cc8c8cb, where the rest of rep-2 records 809589d. The rebuild records a harness delta of sha256 8fde5d73. Both record agent CLI 2.1.309.

recomputed

Each run records the sha256 of the methods doc it was built under, shown here by its first 8 characters. 64606950: full-1, pilot-1 and pilot-2. 12f026f6, the methods text these pages quote: rep-1, rep-2, rep-2/rebuild and tcp-1-list2. 1f11dd37: tcp-1-list and tcp-1.

recomputed

7 of 9 runs record no harness commit in args.json: full-1, pilot-1, pilot-2, rep-1, tcp-1-list, tcp-1-list2 and tcp-1. The harness version they ran is not on record.

recomputed

Docstrings state 2 limits of the analysis code. stats.py mann_whitney_exact: “The C(n, len(xs)) splits suit small samples only.” awards.py overall: “A method a criterion leaves out ranks last on it.”

recomputed