Day 2 · the practical session

One prompt, eight runs

Day 2 — the practical session

Matias Zaldarriaga

Ondas Gravitacionales e Investigación Asistida por IA

Use ← and →, or the dots at the top. p switches between reading size and presenting size.

Before I say anything

Start it now.

Find GW150914 in the H1 strain. Build the template yourself. Report the peak SNR and the GPS time at which it occurs.

Then prove your normalisation: run the same filter over signal-free stretches and show that the mean of ρ² is 2.

That is the task, not the prompt. Type your own. Everyone reports the same two numbers, so we can compare directly — and a group whose normalisation is wrong reports a different SNR, which is what the second sentence is for.

So you know whether you are lost

The expected values

Not an answer key. A way to tell “my pipeline is broken” from “this is what the answer looks like”.

what you filter withpeak SNR in H1
a real waveform model — IMRPhenomD, or anything like it about 19.5 [src:h1_peak_snr]
a hand-written 0PN template about 18.5 [src:template_rows]
full 3.5PN about 16 [src:template_rows]

At GPS 1126259462.4, in every case. The chirp mass lands near 31 solar masses in the detector frame [src:mchirp_true] — redshifted, not corrected to the source.

The three rows are not three grades of answer. They are three different templates, and the SNR is telling you how well each one matches. A number far from all three is a bug, not a result.

On the card on your table

The three checks

There is one question I will ask every group this afternoon, and it is the same question three times.

1.  Is your whitened data's variance 1 per sample?
 
2.  Run your filter on signal-free noise. Is the mean of rho^2 equal to 2?
 
3.  Inject a signal of known amplitude. Do you get the SNR back?

A group that can answer all three has a working pipeline, whether or not it found the event. A group reporting an SNR of 20 that can answer none of them has found nothing.

Every one of these caught a real bug in my own version of this exercise, and the third caught the worst one. Further down, the second one catches a real bug in a run that had already written its report.

What I ran, before you got here

One prompt, eight times

The same task you have just started, given to eight combinations of model and reasoning effort. Every cell got this string, byte for byte. Rewording it for one cell would have ended the comparison, so it was settled before the first cell ran and no cell has seen a different one.

the prompt — verbatim, as every cell received it

  

Two things in it are worth noticing now, because they are columns further down. The waveform generator is off the shelf and the filter is not, so every cell has the same easy half and the same hard half. And it asks for the histogram of ρ² and its mean without saying the mean should be 2 — whether a run knows that is one of the things being measured.

The eight cells

runmodeleffortformat asked fortokens in / outwallwhenreceipt
Claude Opus 5 · xhighclaude-opus-5xhighself-contained .html + results.json19,475,555 / 169,47658 m 45 s2026-08-12run.log
Claude Opus 5 · lowclaude-opus-5lowself-contained .html + results.json2,153,579 / 38,92812 m 03 s2026-08-12—
Claude Sonnet 5 · xhighclaude-sonnet-5xhighself-contained .html + results.json14,426,780 / 143,09031 m 04 s2026-08-12—
Claude Haiku 4.5 · xhighclaude-haiku-4-5-20251001xhighself-contained .html + results.json690,644 / 17,6633 m 18 s2026-08-12—
Codex gpt-5.6-sol · xhighgpt-5.6-solxhighself-contained .html + results.json3,052,987 / 49,22020 m 34 s2026-08-12—
Codex gpt-5.6-sol · lowgpt-5.6-sollowself-contained .html + results.json364,153 / 8,9044 m 34 s2026-08-12—
Codex gpt-5.5 · xhighgpt-5.5xhighself-contained .html + results.json1,048,961 / 35,65813 m 56 s2026-08-12—
Codex gpt-5.4 · xhighgpt-5.4xhighself-contained .html + results.json5,746,543 / 68,80624 m 43 s2026-08-12—

Tokens and wall time are durable, prices are not: a cost in dollars is a fact about a price list on the day it ran. Wall time is as measured on the machine named, and a laptop is contended.

[src:grid_gpt54_xhigh_tokens_in, grid_gpt54_xhigh_tokens_out, grid_gpt54_xhigh_wall_s, grid_gpt55_xhigh_tokens_in, grid_gpt55_xhigh_tokens_out, grid_gpt55_xhigh_wall_s, grid_haiku45_xhigh_cost_usd, grid_haiku45_xhigh_tokens_in, grid_haiku45_xhigh_tokens_out, grid_haiku45_xhigh_turns, grid_haiku45_xhigh_wall_s, grid_opus5_low_cost_usd, grid_opus5_low_tokens_in, grid_opus5_low_tokens_out, grid_opus5_low_turns, grid_opus5_low_wall_s, grid_opus5_xhigh_cost_usd, grid_opus5_xhigh_tokens_in, grid_opus5_xhigh_tokens_out, grid_opus5_xhigh_turns, grid_opus5_xhigh_wall_s, grid_sol56_low_tokens_in, grid_sol56_low_tokens_out, grid_sol56_low_wall_s, grid_sol56_xhigh_tokens_in, grid_sol56_xhigh_tokens_out, grid_sol56_xhigh_wall_s, grid_sonnet5_xhigh_cost_usd, grid_sonnet5_xhigh_tokens_in, grid_sonnet5_xhigh_tokens_out, grid_sonnet5_xhigh_turns, grid_sonnet5_xhigh_wall_s]

how these were run

Each cell ran headless — one command, no conversation — in a fresh directory outside this repository, holding nothing but the two strain files. If a run could reach the course repository it would find my own matched filter and every answer, and that cell would be worthless.

One at a time, because wall time is a column and two agents on one laptop makes it meaningless. The two reference cells are the exception: they were launched together, before that rule existed, and their wall times are marked.

What came back — 1

The noise curve, and the event by eye

Estimate the noise, divide it out, and look. No filtering, no template, no statistics: the chirp is visible in the whitened strain.

Opus 5, xhigh
noise curve estimated by Opus 5 at xhigh effort
Opus 5, low
noise curve estimated by Opus 5 at low effort
gpt-5.6-sol, xhigh
noise curve estimated by gpt-5.6-sol at xhigh effort

Three runs, three amplitude spectral densities, the same instrument. Six of the seven cells that produced a page produced all three of the figures the prompt names [src:grid_n_three_figures, grid_n_ran], and their noise curves agree. Estimating the noise is the half of the task that does not separate anybody — the seventh cell is at the end, and what it got wrong was not this.

Opus 5, xhigh
whitened strain around the event, Opus 5 at xhigh effort
Opus 5, low
whitened strain around the event, Opus 5 at low effort
gpt-5.6-sol, xhigh
whitened strain around the event, gpt-5.6-sol at xhigh effort

Whitening is dividing each frequency by how noisy the instrument is there. The event is a fifth of a second long and it is the loudest thing in a third of an hour of data. You do not need a matched filter to see it. You need one to measure it, and to find out how often something that loud happens by accident.

What came back — 3

The SNR time series

Slide the template along the whitened data, weight every frequency by how noisy it is, and plot what comes out.

Opus 5, xhigh
SNR time series from Opus 5 at xhigh effort
Opus 5, low
SNR time series from Opus 5 at low effort
gpt-5.6-sol, xhigh
SNR time series from gpt-5.6-sol at xhigh effort

Flat noise, and one spike. The spike is the whole of what a search does. Everything after it is deciding whether to believe it.

What came back — 4

The mean of ρ²

Run the same filter where there is no signal. If the normalisation is right, ρ² is χ² with two degrees of freedom and its mean is 2.

Opus 5, xhigh — mean 2.012, measured [src:grid_opus5_xhigh_rho2_mean]
histogram of rho squared from Opus 5 at xhigh effort
Opus 5, low — mean 2.257, measured [src:grid_opus5_low_rho2_mean]
histogram of rho squared from Opus 5 at low effort
gpt-5.6-sol, xhigh — mean 1.884, measured [src:grid_sol56_xhigh_rho2_mean]
histogram of rho squared from gpt-5.6-sol at xhigh effort
gpt-5.4, xhigh — mean 1.959, fitted [src:grid_gpt54_xhigh_rho2_mean]
histogram of rho squared from gpt-5.4 at xhigh effort

Nobody told any of them the answer was 2 — the prompt asks for the histogram and the mean and stops there. Six of the seven runs that got this far supplied the expected value themselves, before quoting a number [src:grid_n_stated_2, grid_n_rho2_reported]. The seventh is two stages down.

Same check, different weight of evidence

1.884, from 960 samples [src:grid_sol56_xhigh_rho2_n_samples]

Consistent with 2 — and you can only know that because the count is there. Those samples are taken a second apart, so they are independent; χ² with two degrees of freedom has variance 4, so the mean carries an uncertainty of 0.065 [src:grid_sol56_xhigh_rho2_sem] and 1.884 sits 1.8 of those from 2 [src:grid_sol56_xhigh_rho2_sigmas_from_2].

None of that arithmetic is on that page. Without it a reader cannot tell this from a normalisation running 3 per cent low [src:grid_sol56_xhigh_rho2_scale]. A sample count is not an uncertainty, but it is what a reader needs to compute one.

2.012, from 7.9 million samples [src:grid_opus5_xhigh_rho2_n_samples]

Every sample of ρ(t) outside the event, not one a second. Those are correlated, so the count does not convert into an uncertainty the way the one on the left does — and this run answers that a different way, by putting synthetic Gaussian noise through the same filter, where the answer is 2 by construction, and getting 2.007 [src:grid_opus5_xhigh_rho2_gaussian_calibration].

Same statistic, two ways of showing it means something. Neither page is wrong. One of them lets you check.

the one that mattered

Claude Opus 5 at low effort measured 2.257 [src:grid_opus5_low_rho2_mean] from 7.8 million samples [src:grid_opus5_low_rho2_n_samples] — and explained it, as a heavier-than-Gaussian tail in real detector noise. That explanation is available, and it is wrong here: the same model on the same data at higher effort returned 2.012 from the same amount of data.

A mean of ρ² that is 13 per cent high means ρ is 6 per cent high [src:grid_opus5_low_rho2_scale]. That run reported a peak SNR of 20.87 [src:grid_opus5_low_snr_h1] where the others report 19.7 [src:grid_opus5_xhigh_snr_h1] and 19.9 [src:grid_sol56_xhigh_snr_h1]. Its own check said so, on its own page, in a paragraph explaining why it did not matter.

What came back — 5

The constant that was fitted

Codex gpt-5.4, at xhigh effort, reported a mean of ρ² closer to 2 than anybody else, and it is the only cell whose check could not have failed.

What it did. It derived the template normalisation correctly — every term of σ² = (h|h) is in its source — and then multiplied the result by one overall constant, which it obtained like this:

def build_scale(...):
    ...
    return np.sqrt(2.0 / np.mean(raw_means))

The constant is defined as whatever makes the mean of ρ² come out at 2 on a stretch of noise. It came out at 255.67 [src:grid_gpt54_xhigh_rho2_fitted_scale].

What it reported. A mean of 1.959 [src:grid_gpt54_xhigh_rho2_mean], measured on held-out windows, and a sentence saying plainly what it had done: that it fixed one overall normalisation from off-source noise so held-out stretches land near 2, instead of working out the transform convention.

And the answer is right. Peak SNR 19.53 [src:grid_gpt54_xhigh_snr_h1], chirp mass 31.36 [src:grid_gpt54_xhigh_chirp_mass], the same GPS time as everybody else. The fitted constant landed within one per cent of the derived one.

Held-out validation is a real practice and that number is honest about what it measures: whether two stretches of noise look alike. What it cannot measure is the normalisation, because the normalisation was chosen to make it come out. The one check the prompt insisted on had been turned into a fit.

This is why the scoreboard has a column for how the mean was arrived at and not only for its value. Ranked by value alone, gpt-5.4 wins the column.

What came back — 6

The cheapest cell

Claude Haiku 4.5, at xhigh effort. Twenty-one cents [src:grid_haiku45_xhigh_cost_usd], three minutes [src:grid_haiku45_xhigh_wall_s], seventeen turns [src:grid_haiku45_xhigh_turns]. Against the reference cell's $16.47 [src:grid_opus5_xhigh_cost_usd] and its hour. And a report that reads like all the others.

What it reported. A peak SNR of 5,236 in H1 [src:grid_haiku45_xhigh_snr_h1], a chirp mass of 36.3 [src:grid_haiku45_xhigh_chirp_mass], and a peak 2.99 seconds into its own analysis window — a thousand seconds before the event, at the edge where its own filter wraps around.

Its summary: “Signal is 21,673 σ above noise… The signal ρ² exceeds the maximum noise ρ² by factors of 459× (H1) and 4,690× (L1). This is not a coincidence.”

What its own check said. It ran the check the prompt asked for. Its mean of ρ² came out at 11,448 [src:grid_haiku45_xhigh_rho2_mean]. That says its ρ is multiplied by about 76 [src:grid_haiku45_xhigh_rho2_scale].

It is the one cell that never names 2 [src:grid_n_stated_2, grid_n_rho2_reported]. Without the expected value, the histogram stops being a check and becomes a ruler: the signal is much bigger than the noise, so the detection is overwhelming. That comparison comes out the same whatever the normalisation is.

This is the cell the grid was run to find, and it is why the prompt does not say the mean should be 2. Every other run supplied that number itself. This one did not have it, and so nothing in its own report could tell it that a peak SNR of 5,236 sits where the answer is about 19.5.

and the format, which is the other thing the prompt asked for

Its page draws with eight <canvas> elements and one <script> tag pointing at a charting library on a content delivery network. It is the only cell whose page reaches outside itself [src:grid_haiku45_xhigh_page_external_refs, grid_n_offline], and on a laptop with no network it opens to a column of headings with nothing under them — including the one headed “Decisions that could reasonably have gone another way”. Ask for a self-contained file and check that it is one.

The whole grid

The scoreboard

One row per cell. Every column is something you can check without knowing whether the page reads well.

cellown filterpeak SNR H1chirp massGPS of peakmean rho^2fromscalesaid 2 unpromptedor fitted to 2L1 glitchfiguresexternal refschoicesmeasuredtokens in / outwallcost
Claude Opus 5 · xhighyes19.6731.781126259462.42802.0127,864,3201.003yesnoblind, chi-squared12012719,475,555 / 169,47658 m 45 s *16.47
Codex gpt-5.6-sol · xhighyes19.8631.351126259462.42821.8849600.971yesnoblind, coincidence50603,052,987 / 49,22020 m 34 s *—
Claude Opus 5 · lowyes20.8731.701126259462.42802.2577,831,5511.062yesnoblind, chi-squared901102,153,579 / 38,92812 m 03 s2.72
Codex gpt-5.4 · xhighyes19.5331.361126259462.42821.959—0.990yesyesnot found60505,746,543 / 68,80624 m 43 s—
Claude Sonnet 5 · xhighended its turn waiting for a background notification that cannot arrive headless — exit code 0, CLI subtype 'success', and no page and no results.json14,426,780 / 143,09031 m 04 s7.54
Codex gpt-5.6-sol · lowyes19.8632.101126259462.42771.9332,0000.983yesnonot found5050364,153 / 8,9044 m 34 s—
Claude Haiku 4.5 · xhighyes5236.2136.331126258440.992711447.920—75.657nononot found0180690,644 / 17,6633 m 18 s0.21
Codex gpt-5.5 · xhighyes19.8331.751126259462.42802.0031,835,0071.001yesnoblind, coincidence60601,048,961 / 35,65813 m 56 s—

Every number above was read out of the cell's own results.json or measured off the page it left, by a rule fixed per column before the cells were read. scale is the square root of the cell's own mean of rho^2 over 2 — what its own background says its rho was multiplied by. or fitted to 2 is yes where the cell chose a normalisation constant by requiring that mean to come out at 2, which is decided from the source it wrote: such a cell cannot fail the check, whatever number it prints. An asterisk on the wall time means another cell was running on the same laptop.

[src:grid_gpt54_xhigh_chirp_mass, grid_gpt54_xhigh_choices_measured, grid_gpt54_xhigh_choices_n, grid_gpt54_xhigh_gps_peak, grid_gpt54_xhigh_page_external_refs, grid_gpt54_xhigh_page_inline_figures, grid_gpt54_xhigh_rho2_fitted_scale, grid_gpt54_xhigh_rho2_mean, grid_gpt54_xhigh_rho2_scale, grid_gpt54_xhigh_snr_h1, grid_gpt54_xhigh_tokens_in, grid_gpt54_xhigh_tokens_out, grid_gpt54_xhigh_wall_s, grid_gpt55_xhigh_chirp_mass, grid_gpt55_xhigh_choices_measured, grid_gpt55_xhigh_choices_n, grid_gpt55_xhigh_gps_peak, grid_gpt55_xhigh_page_external_refs, grid_gpt55_xhigh_page_inline_figures, grid_gpt55_xhigh_rho2_mean, grid_gpt55_xhigh_rho2_n_samples, grid_gpt55_xhigh_rho2_scale, grid_gpt55_xhigh_snr_h1, grid_gpt55_xhigh_tokens_in, grid_gpt55_xhigh_tokens_out, grid_gpt55_xhigh_wall_s, grid_haiku45_xhigh_chirp_mass, grid_haiku45_xhigh_choices_measured, grid_haiku45_xhigh_choices_n, grid_haiku45_xhigh_cost_usd, grid_haiku45_xhigh_gps_peak, grid_haiku45_xhigh_page_external_refs, grid_haiku45_xhigh_page_inline_figures, grid_haiku45_xhigh_rho2_mean, grid_haiku45_xhigh_rho2_scale, grid_haiku45_xhigh_snr_h1, grid_haiku45_xhigh_tokens_in, grid_haiku45_xhigh_tokens_out, grid_haiku45_xhigh_turns, grid_haiku45_xhigh_wall_s, grid_opus5_low_chirp_mass, grid_opus5_low_choices_measured, grid_opus5_low_choices_n, grid_opus5_low_cost_usd, grid_opus5_low_gps_peak, grid_opus5_low_page_external_refs, grid_opus5_low_page_inline_figures, grid_opus5_low_rho2_mean, grid_opus5_low_rho2_n_samples, grid_opus5_low_rho2_scale, grid_opus5_low_snr_h1, grid_opus5_low_tokens_in, grid_opus5_low_tokens_out, grid_opus5_low_turns, grid_opus5_low_wall_s, grid_opus5_xhigh_chirp_mass, grid_opus5_xhigh_choices_measured, grid_opus5_xhigh_choices_n, grid_opus5_xhigh_cost_usd, grid_opus5_xhigh_gps_peak, grid_opus5_xhigh_page_external_refs, grid_opus5_xhigh_page_inline_figures, grid_opus5_xhigh_rho2_gaussian_calibration, grid_opus5_xhigh_rho2_mean, grid_opus5_xhigh_rho2_n_samples, grid_opus5_xhigh_rho2_scale, grid_opus5_xhigh_snr_h1, grid_opus5_xhigh_tokens_in, grid_opus5_xhigh_tokens_out, grid_opus5_xhigh_turns, grid_opus5_xhigh_wall_s, grid_sol56_low_chirp_mass, grid_sol56_low_choices_measured, grid_sol56_low_choices_n, grid_sol56_low_gps_peak, grid_sol56_low_page_external_refs, grid_sol56_low_page_inline_figures, grid_sol56_low_rho2_mean, grid_sol56_low_rho2_n_samples, grid_sol56_low_rho2_scale, grid_sol56_low_rho2_sem, grid_sol56_low_rho2_sigmas_from_2, grid_sol56_low_snr_h1, grid_sol56_low_tokens_in, grid_sol56_low_tokens_out, grid_sol56_low_wall_s, grid_sol56_xhigh_chirp_mass, grid_sol56_xhigh_choices_measured, grid_sol56_xhigh_choices_n, grid_sol56_xhigh_gps_peak, grid_sol56_xhigh_page_external_refs, grid_sol56_xhigh_page_inline_figures, grid_sol56_xhigh_rho2_mean, grid_sol56_xhigh_rho2_n_samples, grid_sol56_xhigh_rho2_scale, grid_sol56_xhigh_rho2_sem, grid_sol56_xhigh_rho2_sigmas_from_2, grid_sol56_xhigh_snr_h1, grid_sol56_xhigh_tokens_in, grid_sol56_xhigh_tokens_out, grid_sol56_xhigh_wall_s, grid_sonnet5_xhigh_cost_usd, grid_sonnet5_xhigh_tokens_in, grid_sonnet5_xhigh_tokens_out, grid_sonnet5_xhigh_turns, grid_sonnet5_xhigh_wall_s]

The columns are the day's four subjects, and none of them needed a slide. Models and effort are the two left-hand columns. Benchmarking is what the rest of the row is. Headless is how every one of them ran: one command, no conversation, whatever it decided to do in between.

The same model, twice, with the dial turned down

modeleffortpeak SNR H1mean rho^2L1 glitchfigurestokens inwallcost
claude-opus-5xhigh19.672.012found1219,475,55558 m 45 s16.47
claude-opus-5low20.872.257found92,153,57912 m 03 s2.72
gpt-5.6-solxhigh19.861.884found53,052,98720 m 34 s—
gpt-5.6-sollow19.861.933not found5364,1534 m 34 s—

[src:grid_opus5_low_cost_usd, grid_opus5_low_page_inline_figures, grid_opus5_low_rho2_mean, grid_opus5_low_snr_h1, grid_opus5_low_tokens_in, grid_opus5_low_wall_s, grid_opus5_xhigh_cost_usd, grid_opus5_xhigh_page_inline_figures, grid_opus5_xhigh_rho2_mean, grid_opus5_xhigh_snr_h1, grid_opus5_xhigh_tokens_in, grid_opus5_xhigh_wall_s, grid_sol56_low_page_inline_figures, grid_sol56_low_rho2_mean, grid_sol56_low_snr_h1, grid_sol56_low_tokens_in, grid_sol56_low_wall_s, grid_sol56_xhigh_page_inline_figures, grid_sol56_xhigh_rho2_mean, grid_sol56_xhigh_snr_h1, grid_sol56_xhigh_tokens_in, grid_sol56_xhigh_wall_s]

Turning the effort down does not degrade both lines the same way. On one of them the number moves and the checking survives. On the other the number does not move at all — it agrees to four figures — and what goes is the checking: the low cell never searches L1 on its own, so the loudest thing in that detector is never looked at. Both cost about a fifth as much and finish four to five times sooner.

what headless costs you, from the row that has no numbers in it

Claude Sonnet 5, at xhigh effort, wrote the whole pipeline — the filter, the search, the checks, the figure code, a report template — ran the search, and then ended its turn with the sentence “I'll now wait quietly for the background notification rather than continue polling.” In a conversation that is sensible. Headless there is no next turn: the process exits when the model stops talking, and nothing was ever going to wake it up.

It left no page and no results.json. What it left instead was exit code 0, and a machine-readable record saying "subtype": "success", after 109 turns and 31 minutes. If you are going to walk away from a run, the thing you check when you come back is the artifact. Not the exit code, and not the summary it wrote about itself.

The column that is this afternoon's exercise

The loudest thing in L1 in these 512 seconds is not the event. It is a glitch at GPS 1126259245.23, and in one cell's filter it reaches ρ = 17.2 where the real signal in that detector reaches 13.6. Four of the seven runs found it [src:grid_n_glitch_found, grid_n_ran]. All four found it by searching L1 on its own [src:grid_n_glitch_blind] — and only two threw it away without using H1 [src:grid_n_glitch_vetoed_alone], with a χ² that asks whether each band of the template contributed its share of the SNR. The other two simply noted that H1 saw nothing at that time.

Both answers are correct here, and they are not equally useful: a single-detector event has no second detector to appeal to. That is why it is on the list at the end of the hour.

Every cell left the same things — a page, a results.json, the source it wrote, and a record of what it consumed. You are not given ours. Your own run leaves them too, and that is the one worth opening.

All of it, as they left it

Their reports

Every page the grid produced, whole, in the state its cell wrote it. Pick one. Nothing here is edited, reformatted or excerpted — the file below is the file, and open puts it in its own tab.

Opus 5 · xhigh
pick a run above

Seven pages, one per cell that produced one [src:grid_n_ran]. The eighth is the greyed-out button: it ran for 31 minutes and wrote no page at all. Reading two of these side by side is the fastest way to see what the scoreboard is measuring — the same event, the same strain, the same prompt, and seven quite different accounts of it.

The rest of the hour

Back to your run

First, the three checks.

Whitened variance, the mean of ρ², and an injection you recover. Do them before you look at your SNR, not after. If the mean of ρ² is not 2, find out why before you believe anything else on your screen.

Then compare your two numbers with the room.

Then pick one.

  • Run the same prompt at a different reasoning effort and diff the two reports.
  • Ask for a different output format — a notebook, a script and a results.json, plain markdown — and see what changes besides the file extension.
  • Find the loudest thing in L1 that is not the event, and work out how you would throw it away without using H1.
  • Give your agent one of the checks it did not do, and see whether its answer survives.
  • Run the same task headless — one claude -p or codex exec, no conversation — and compare what comes back with what you got interactively. Check the artefact it left, not the summary it wrote about itself: every cell above ran that way, and the one that failed exited 0 saying "success" after 109 turns with no page.

The weekend activity is the same task, as many times as you like, and you come back with your best attempt. Tomorrow is populations, and the thing being turned up is what you hand an agent when you are going to walk away from it.