The Monday Morning Agent

"Kickers are too good now."

Same five numbers. Two pictures.

Drawn to make a point refused

Kickers are better than ever 84% 85% 86% 84.2%85.6% 20242025
The window starts where it helps, and the axis starts at 84. Rules 1 and 2, both broken. The renderer names the rule and draws nothing.

Drawn under the standard rule by rule

Are kickers making more of their field goals? 0% 50% 100% of 1,076of 1,105of 1,107of 1,166of 1,140of 444 202120222023202420252026*
Every season on file, the axis from zero, the count under every point, the title a question, the season in progress in gray. Flat: 85.3, 85.4, 85.8, 84.2, 85.6, and 84.2 so far.

Every NFL field goal in six seasons went through the uprights 84 to 86 percent of the time, and a chart can make that climb a mountain or lie flat, depending on where it starts. The argument this episode is about the long ones, the kicks from 50 yards and beyond that seem to go in every week now, and a claim about a trend needs a picture. A picture is a set of choices: which seasons, where the axis begins, which line gets the bright color. So this episode the agent gets written rules for its charts, and it does not draw. It writes down the question and the command, and a renderer draws the rows the tool printed, the same way every week.

This page is how that is set up, for someone who has never built an agent. The eight rules are the chart standard, a file at the root of the repo that every later episode inherits, as episode 2's dictionary is. New to football? The game, explained has what you need. The room votes during this episode's Q&A on the claim episode 4 audits: the four candidates.

Read the eight rules, one per keypress

The setup in one minute

You hand the agent a claim. It turns the claim into a question and asks one small tool to measure. Since episode 2 the tool will not measure a play until the play is written down in the dictionary, and this episode the dictionary learns a second kind of entry: a kick, from one distance to another. The tool learns a third question, a trend: season by season, how often the defined kick was made, against every other field goal attempt, and how often it was tried per game. Then the new part. The agent writes a chart spec, one small file naming the question and the command, and runs the renderer, which draws the rows under the standard or refuses with the rule named. The ruling cites the entry and the chart.

On the registration page this episode's capability is called consistent visualizations: charts that look the same week to week, from rules written down once, so a reader of episode 5 recognizes episode 3's chart. The standard is quoted in full below. This is episode 3 of an eight-episode O'Reilly live series, and every number on this page is real. The long-kick numbers, and the chart the agent draws from them, arrive after the show on November 9, 2026.

The words on these pages

Every number on the site comes from the same tool, and the tool uses a few words in a fixed way. Here they are, roughly in the order the tool applies them. Wherever one of these words appears on a page with a dotted underline, hovering shows its definition and clicking brings you here.

PlayOne snap in the data, with its one-line description. A search reads every play: 265,845 descriptions from 2021 through the 2026 games of October 25. "Field goal" is in 6,251 of them, so the kick, unlike the tush push, has a name in the data. What the description does not carry on its own is a distance band.
Field goal attempt, the defined kickA play with a field goal attempt and a result on file: made, missed, or blocked, with its distance in yards. A blocked kick counts as a miss. The defined kick is every attempt whose distance falls inside the dictionary entry's range. Six seasons hold 6,038 attempts.
Every other field goal attemptThe attempts outside the entry's distances. It is what the defined kick is measured against. An entry that covers every distance, like field-goal, has an empty comparison group, and the tool says so.
Made, make rateA kick was made when the result is "made." The make rate is made divided by attempts, printed to one decimal place.
Tried per gameThe defined kick's attempts divided by the games kept that season, to two decimals. A rate can hold still while this number moves, which is why the chart carries both.
TrendThe tool's third question: one row per season, with the defined kick's attempts, made, rate, and tries per game, every other attempt's rate, and the gap. A trend runs over every season on file, and the season still being played is marked in progress.
Distance bandThe standard's four bands: under 40 yards, 40 to 49, 50 to 59, and 60 and over. With --bands the tool splits the defined kicks by band, so one distance cannot hide behind the others.
In progressThe tool's flag on the last season on file while it is still being played, with its weeks: a partial season, drawn in gray with a dashed edge and never read as a full year.
Dictionary, entryThe folder dictionary/ at the root of the repo, one JSON file per entry. Since this episode an entry is one of two kinds: a rush (episode 2's) or a kick, with a distance from and a distance to, 18 to 70 yards. field-goal, every distance, was committed before the show; the long kick's entry is written by the agent on air. The agent may add an entry, never change one.
The chart standardThe file charts/STANDARD.md: eight rules for every chart the agent draws, about the window, the axes, the counts, the colors, the title, the source line, what may appear, and the template. Written once, enforced by the renderer, inherited by every later episode.
Chart specThe file the agent writes instead of drawing: charts/NAME.json, with a name, a date, the question as the title, the tool, and the command. Nothing about colors, axes, or labels, because those are the standard's.
Renderercharts/render-chart.mjs. It reads a spec, runs the spec's command itself with --json, and draws the rows that come back as one SVG under the standard. It checks the window and the title, fixes everything else by its template, and refuses a spec that breaks a rule with the rule named. It cannot draw a number the tool did not print.
CiteName the entry and the chart a ruling rests on, by file and date. Every ruling that measures a play carries a "Definition used" line; every ruling that measures a trend carries a "Chart" line. The runner checks both.
FilterA rule that drops plays before anything is counted: which seasons, which weeks, which team is kicking, a field goal attempt with a result and a distance. The tool prints every filter in order with the number of plays left after it. Nothing is dropped that is not printed.
Small sampleThe tool's warning when fewer than 50 kicks match the definition in a season. Then one kick moves that season's rate by 100 divided by the count: at 9 kicks, by 11.1 points.
PolicyThe file written for this agent, analyst.md: a job description that says how to turn a claim into a question, how to use the dictionary and the charts, what to show, and what it may not say.
RulingThe agent's answer, in nine parts: the claim as a question, the definition used, the chart, the five complete seasons, this season, how far apart they are, one sentence of judgment, what the plays cannot tell you, and the exact commands it ran. Under 500 words.

What already existed

None of this was made by the agent. Two pieces are new since episode 2: the standard, and the renderer that enforces it.

Public play-by-playEvery NFL play, published by the nflverse community under a CC BY 4.0 license. 372 columns per play, refreshed during the season.
A cut to 35 columnsEpisode 2 kept 31. This one adds four about kicks: whether the play was a field goal attempt, how it ended, how long it was, and who kicked it. Episode 2's agent could search the descriptions for "field goal" and count them; it could not tell a 30-yard kick from a 60-yard kick, and the argument is about distance. Still no betting column.
One read-only toolquery-plays.mjs, extended for this episode. It answers three kinds of question now: a search, episode 2's comparison, and the trend. It prints every filter in order and the entry it applied, quoted. It changes nothing.
The dictionary, with a second kind of entrydictionary/ holds episode 2's tush-push, untouched, and field-goal, every attempt from 18 to 70 yards, committed before the show as the first kick entry. The entry for the long kick is written by the agent on air.
The chart standardcharts/STANDARD.md, eight rules, version 1, dated October 26, 2026. Quoted in full below. A change is a new version, and every chart carries the version it was drawn under.
The renderercharts/render-chart.mjs, about 200 lines. One spec in, one SVG out, or a refusal with the rule named. It runs the tool itself for the rows.
Claude CodeOne command reads a prompt, calls only the tools it is allowed to call, and exits. There is no server to write.

The standard, in eight rules

As the agent reads it, appended to its policy before every run. Each rule says how it is held: the renderer checks it on every spec, the template fixes it so there is no way to break it, or it holds by how the renderer is built. The same rules, one per keypress, with the pictures.

Every season on fileA trend runs from 2021, the first season on file, through the current season, with no season skipped and no narrowing to a few weeks. The window is not a choice.
renderer checks
Axes start at zeroA rate axis runs from 0 to 100 percent. A count axis starts at 0. No axis is cut to make a flat line look steep.
template
The count under every pointEach rate is drawn with the number of plays behind it, so a rate from 9 kicks does not look like a rate from 900.
template
Two colors, and grayThe broadcast's two lines: yellow for the thing the claim is about, blue for what it is compared with. Gray marks the current season while it is still being played, labeled with its last week on file. No third series, no third color.
template
The title is the questionThe chart's title is the question the agent tested, in words a fan would say, ending in a question mark, with no number in it other than a season's year. A title that states the answer is a caption, not a title.
renderer checks
The source line is the commandUnder the chart, the exact tool command that produced the rows, with the plays and seasons it read, so a reader can rerun it.
template
Nothing the tool did not printNo trend line, no annotation, no second axis, no number typed in by hand. The renderer runs the command and draws what comes back, and that is all it can draw.
by construction
The same template every weekOne shape per question. A trend is a line chart of rates by season over a bar chart of attempts per game, laid out the same way every episode, so a reader of episode 5 recognizes episode 3's chart.
by construction

The spec the renderer refused on air

Before the agent runs, a spec that breaks rule 1 is handed to the renderer by hand: the same question, with the trend starting in 2023. It is the chart crime of picking the window, and it is episode-3/bad-chart.json in the repo.

{
  "name": "bad-chart",
  "question": "Are kickers making more of their long field goals than they used to?",
  "tool": "episode-3/query-plays.mjs",
  "command": "--trend field-goal --seasons 2023-2026"
}

What came back, exit code 65, and no file written:

render-chart: REFUSED by rule 1: the command says --seasons 2023-2026; a trend starts at 2021, the first season on file, and ends at 2026, the current one: --seasons 2021-2026. The window is not a choice. Nothing was drawn. The rules are in charts/STANDARD.md, version 1 (October 26, 2026).

The agent gets the same sentence when it breaks a rule, fixes what the sentence names, and runs the renderer again. A refusal is a sentence, not a crash.

How it fits together

Two kinds of work, kept apart, as in episodes 1 and 2. Counting plays has one right answer, so a small tool does it the same way every time. Deciding what to count is judgment, and since episode 2 the definition lives in a file. What is new is that drawing is taken out of the agent's hands too: it chooses the question and the command, which are judgment, and a renderer makes every other choice the same way every week. The yellow cells are the parts written with care: the policy and the standard by hand, the entry and the spec by the agent.

A claim"Kickers are too good now."
The policyanalyst.md, ten sections, one of them new: "Your charts"
The standardcharts/STANDARD.md, eight rules, appended to the policy before every run
The agentClaude Code, allowed to run exactly two scripts and to write one kind of file in each of two folders
It writes a definition and a spec, then names both in the ruling.
The dictionarydictionary/long-field-goal.json, the entry it writes on air, beside the two already there
The speccharts/NAME.json: the question, the tool, and the command, and nothing about how to draw it
The renderer reads the spec and runs the command itself.
The toolquery-plays.mjs prints the definition, every filter, then one row per season
It reads, and only reads.
The plays265,845 plays, 35 columns, a one-line description each, no betting lines
The rows go to the renderer, which draws them or refuses.
The chartcharts/NAME.svg, under the standard: the window, the axes, the counts, the colors, the title, and the source line, none of them chosen by the agent
One rulingThe entry and the chart it rests on, by file and date; five seasons, this season, and the exact commands it ran, the render included
The runner's checksThe dictionary and the standard appended before the run. The citations required after it: the entry, and for a trend the chart. Any entry or spec that was on file and changed, put back, and the run failed. A new spec the renderer refuses fails the run too.

What one run looks like

  1. The claim goes in as one sentence. The runner adds the dictionary, the standard, and the specs on file to the policy. Before the show the specs read "none."
  2. The agent splits the claim. Whether kickers are "too good" is a judgment about the game people want to watch; the plays cannot settle it. Whether they make more of their long kicks than they did, by enough that the kick is a different play, they can. It searches the descriptions: "field goal" is in thousands, "long field goal" in none.
  3. The tool refuses. The agent asks for the trend of long-field-goal. With no entry of that name, the tool stops with exit code 65 and prints an example kick entry, with a note on each line.
  4. It writes the entry, dictionary/long-field-goal.json: a field goal attempt from 50 yards or more, compared with every other attempt, with what the rules cannot see in its last line: the wind, the surface, the hold, whether a timeout iced the kicker.
  5. Two reads with that entry. The trend over every season on file, then the same trend by distance band, the read it chooses because one rate can hide a change at one distance.
  6. It writes the spec, charts/NAME.json, with the question as its title and the trend command. Then it runs the renderer, which runs that command again and draws the rows, or refuses with the rule named.
  7. One ruling, citing the entry and the chart. The runner checks both citations, checks that the new entry is one the tool can use and the new spec one the renderer accepts, and saves the ruling. The agent can write nothing else.
The ruling and the chart arrive after the show

Monday, November 9, 2026, 8:00 a.m. Pacific. The agent runs on air with the dictionary as it stands, writes the long kick's entry and the chart spec, and rules. This box then holds the ruling as it gave it and the chart as the renderer drew it, both committed to the repo exactly as written. Until then the page carries no long-kick number, so the room sees the answer when the agent does.

The steps on the left are from the rehearsal runs of October 26, 2026, eight claims in parallel into scratch folders: the same path every time, about 30 seconds and seven tool calls. The yellow steps mark where the agent is using judgment; the rest is mechanical.

The policy, in ten sections

The policy is a Markdown file of about 1,700 words, written like a job description. It is episode 2's nine sections with one added, fourth, right after the dictionary: the charts. Three other sections changed, and the table says where.

Job descriptionNot a pundit, no opinions about teams. The person in the room who went back and counted. New: it draws charts, and every chart follows a written standard.
Your one toolThree kinds of question now: a search, a defined rush against every other rush, and a trend of a defined kick, season by season, with --bands for the four distances. The printout is the truth about what was measured. Never open the data file, the dictionary folder, or the charts folder directly.
Your dictionaryTwo kinds of entry, a rush and a kick. Rules 1 to 3 as before: search by name first; use an entry on file unchanged; write one if there is none. Rule 4, new: if the play the claim names is neither a rush nor a kick (a jet sweep, a screen, a draw), say so in the ruling, write no entry, and report no number for it. "A count from the nearest rules is not the play."
Your chartsNew. You do not draw. Write a spec in the format the renderer prints, naming the question and the command, and run the renderer; if it refuses, fix what it names. One chart per trend, cited by file and date. The title is the question, with a question mark and no number in it; the numbers go in the ruling. Name the spec for the question, because a later episode may redraw it.
Turn the claim into a questionWhether kickers are too good is not a data question: say so in one sentence and set it aside. Test the claim underneath, that they make more of their kicks now, especially the long ones. Then run the read that could prove it wrong: the tries per game, or the rate by distance band.
Answer it across the seasonsThe five complete seasons as the baseline, then this season's in-progress row, from one command, so the same filters and the same entry apply to both. Repeat the small-sample and in-progress warnings in your own words.
Show every filterEach number comes with its filters and its entry. End with the exact commands, the search and the render included. Never report a number the tool did not print; rates to one decimal, tries per game to two.
What you may not sayNo predictions, no view on what the rule should be, no advice to a team, a bettor, or a fantasy player, and no claim about why a kick was made or why a coach chose to try.
The output contractOne ruling, under 500 words, in nine parts. The new one is "Chart," right after "Definition used," and it is why the limit went up from 400.
Tools you may useFour lines: the tool, the renderer, writing a new .json file in the dictionary folder, and writing a new .json file in the charts folder. No reading files, no writing anywhere else, no network.

What carries to your own agent

Write the taste down, onceWhere the axis starts, which seasons are in, which line is bright: each is a choice that can make the same numbers say two things. The standard makes each choice once, in a file, with the reason. After that a chart is not a matter of taste, and a reader can check it against the rules.
Let the agent choose the question, not the pixelsThe spec holds what is judgment: which question, which command. Everything else is the template's. A spec cannot carry a color or an axis, so there is no way to be persuaded into a prettier lie.
The renderer runs the tool itselfThe agent never hands the renderer a number. The renderer takes the spec's command, runs it with --json, and draws what comes back. Rule 7, nothing the tool did not print, holds by construction, not by asking nicely.
Refuse with the rule namedA spec that starts the trend in 2023 is refused as rule 1, with the window the rule wants printed in the message. The agent fixes what the message names, the same loop as the dictionary's missing entry. A refusal is a sentence, not a crash.
Check the citation, not the promiseThe runner reads the saved run for every trend the agent measured and every render that succeeded, and checks that the ruling names the chart. Missing, the ruling goes back once; still missing, the run fails. Episode 2's entry check works the same way.
Mark the partial season every timeThe tool flags the season still being played, the renderer draws it gray and dashed with its last week, and the policy makes the agent repeat the warning. A seven-week season is never read as a year, because three places refuse to let it be.

Try it yourself

To run the agent, you need a Mac or Linux machine with Node, git, and the Claude Code CLI signed in. The data is already in the repo. The renderer needs only Node.

# get the kit
git clone https://github.com/ismayc/monday-morning-agent.git
cd monday-morning-agent/episode-3

# the trend of every field goal, by distance band: the entry committed before the show
node ./query-plays.mjs --trend field-goal --seasons 2021-2026 --bands

# a kick with no entry: the tool stops and prints an example kick entry
node ./query-plays.mjs --trend no-such-kick --seasons 2021-2026

# a spec that starts the trend in 2023: the renderer names rule 1 and draws nothing
node ../charts/render-chart.mjs ./bad-chart.json

# the whole agent, into scratch folders so your copy of the real ones stays as it is
ANALYST_DICT_DIR=$(mktemp -d) ANALYST_CHART_DIR=$(mktemp -d) ./run-analyst.sh "Kickers are too good now."

Leave out the two variables and the agent reads and writes the repo's own dictionary/ and charts/. After the show those folders hold the entry and the spec written on air, so the agent reads them and measures with them. In scratch folders it starts from the two entries on file and writes its own.

The repositoryThe kit for each episode, the data, the dictionary, the charts, and the examples from real runs.
The chart standardThe eight rules as a file, with the version and the reasons. The charts folder holds the renderer, the README with the fields of a spec, and, after the show, the spec and the SVG written on air.
The standard, one rule per keypressThe eight rules with a picture for each, and at the end the spec the renderer refused on air.
The dictionaryThe README with both kinds of entry, the rush and the kick, and the rules for adding an entry. Two entries on file before the show.
The policy fileAll ten sections, as the agent reads them. What changed since episode 2, line by line.
The vote for episode 4Four claims pundits make, one page, no numbers. The room picks one during this episode's Q&A, and the fact-checker audits it on November 23.
The game, explainedJust enough football to follow the arguments and the data: a drive, a down, a field goal, and the words the pages use.
The live seriesEight episodes on O'Reilly, every other Monday.

The rest of the series

This is episode 3 of eight. Each episode settles one argument and takes away one piece of human oversight, replacing it with something written down. Here it is the picture: from now on every chart the agent draws follows the standard. Episode 1, "You have to establish the run," showed the tool's filters. Episode 2, "Ban the tush push," gave the agent a dictionary it must cite. All eight arguments are on the series page. Next, on November 23, 2026: the pundit audit, with a second agent that checks the first, on the claim this room votes for.

Appendix: the season sheets

Two pages for after the show, to go over at any time, published once the agent has written its entry on air. Each asks the tool six situations with that entry, one row per season: every game, the regular season, the playoffs, the four distance bands, every field goal at any distance, and the five teams that tried the long kick most. Each carries the chart drawn by the renderer under the standard, and prints a verdict by a rule stated on the page. The first ends with the ruling the agent gave on air.